]> git.lyx.org Git - lyx.git/blob - src/frontends/controllers/frontend_helpers.cpp
Fixed some lines that were too long. It compiled afterwards.
[lyx.git] / src / frontends / controllers / frontend_helpers.cpp
1 /**
2  * \file frontend_helpers.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Angus Leeming
7  * \author Herbert Voß
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "frontend_helpers.h"
15
16 #include "Buffer.h"
17 #include "BufferParams.h"
18 #include "Color.h"
19 #include "debug.h"
20 #include "gettext.h"
21 #include "Language.h"
22 #include "Length.h"
23
24 #include "frontends/FileDialog.h"
25 #include "frontends/alert.h"
26
27 #include "support/filetools.h"
28 #include "support/lstrings.h"
29 #include "support/Package.h"
30 #include "support/filetools.h"
31 #include "support/lstrings.h"
32 #include "support/lyxalgo.h"
33 #include "support/os.h"
34 #include "support/Package.h"
35 #include "support/Path.h"
36 #include "support/Systemcall.h"
37
38 #include <boost/cregex.hpp>
39 #include <boost/regex.hpp>
40
41 #include <algorithm>
42 #include <fstream>
43
44 using std::string;
45 using std::vector;
46 using std::pair;
47 using std::endl;
48
49 namespace lyx {
50
51 using support::ascii_lowercase;
52 using support::bformat;
53 using support::compare_ascii_no_case;
54 using support::contains;
55 using support::getVectorFromString;
56 using support::ltrim;
57 using support::prefixIs;
58 using support::rtrim;
59 using support::split;
60 using support::subst;
61 using support::token;
62 using support::trim;
63
64 namespace biblio {
65
66 namespace {
67
68 vector<string> const init_possible_cite_commands()
69 {
70         char const * const pos[] = {
71                 "cite",
72                 "citet", "citep", "citealt", "citealp",
73                 "citeauthor", "citeyear", "citeyearpar",
74                 "citet*", "citep*", "citealt*", "citealp*", "citeauthor*",
75                 "Citet",  "Citep",  "Citealt",  "Citealp",  "Citeauthor",
76                 "Citet*", "Citep*", "Citealt*", "Citealp*", "Citeauthor*",
77                 "fullcite",
78                 "footcite", "footcitet", "footcitep", "footcitealt",
79                 "footcitealp", "footciteauthor", "footciteyear",
80                 "footciteyearpar",
81                 "citefield",
82                 "citetitle",
83                 "cite*"
84         };
85         size_t const size_pos = sizeof(pos) / sizeof(pos[0]);
86
87         return vector<string>(pos, pos + size_pos);
88 }
89
90
91 vector<string> const & possible_cite_commands()
92 {
93         static vector<string> const pos = init_possible_cite_commands();
94         return pos;
95 }
96
97
98 bool is_possible_cite_command(string const & input)
99 {
100         vector<string> const & possibles = possible_cite_commands();
101         vector<string>::const_iterator const end = possibles.end();
102         return std::find(possibles.begin(), end, input) != end;
103 }
104
105
106 string const default_cite_command(CiteEngine engine)
107 {
108         string str;
109         switch (engine) {
110         case ENGINE_BASIC:
111                 str = "cite";
112                 break;
113         case ENGINE_NATBIB_AUTHORYEAR:
114                 str = "citet";
115                 break;
116         case ENGINE_NATBIB_NUMERICAL:
117                 str = "citep";
118                 break;
119         case ENGINE_JURABIB:
120                 str = "cite";
121                 break;
122         }
123         return str;
124 }
125
126
127 static const docstring TheBibliographyRef(from_ascii("TheBibliographyRef"));
128
129 } // namespace anon
130
131
132 string const asValidLatexCommand(string const & input,
133                                  CiteEngine const engine)
134 {
135         string const default_str = default_cite_command(engine);
136         if (!is_possible_cite_command(input))
137                 return default_str;
138
139         string output;
140         switch (engine) {
141         case ENGINE_BASIC:
142                 output = default_str;
143                 break;
144
145         case ENGINE_NATBIB_AUTHORYEAR:
146         case ENGINE_NATBIB_NUMERICAL:
147                 if (input == "cite" || input == "citefield" ||
148                     input == "citetitle" || input == "cite*")
149                         output = default_str;
150                 else if (prefixIs(input, "foot"))
151                         output = input.substr(4);
152                 else
153                         output = input;
154                 break;
155
156         case ENGINE_JURABIB: {
157                 // Jurabib does not support the 'uppercase' natbib style.
158                 if (input[0] == 'C')
159                         output = string(1, 'c') + input.substr(1);
160                 else
161                         output = input;
162
163                 // Jurabib does not support the 'full' natbib style.
164                 string::size_type const n = output.size() - 1;
165                 if (output != "cite*" && output[n] == '*')
166                         output = output.substr(0, n);
167
168                 break;
169         }
170         }
171
172         return output;
173 }
174
175
176 docstring const familyName(docstring const & name)
177 {
178         if (name.empty())
179                 return docstring();
180
181         // Very simple parser
182         docstring fname = name;
183
184         // possible authorname combinations are:
185         // "Surname, FirstName"
186         // "Surname, F."
187         // "FirstName Surname"
188         // "F. Surname"
189         docstring::size_type idx = fname.find(',');
190         if (idx != docstring::npos)
191                 return ltrim(fname.substr(0, idx));
192         idx = fname.rfind('.');
193         if (idx != docstring::npos && idx + 1 < fname.size())
194                 fname = ltrim(fname.substr(idx + 1));
195         // test if we have a LaTeX Space in front
196         if (fname[0] == '\\')
197                 return fname.substr(2);
198
199         return rtrim(fname);
200 }
201
202
203 docstring const getAbbreviatedAuthor(InfoMap const & map, string const & key)
204 {
205         BOOST_ASSERT(!map.empty());
206
207         InfoMap::const_iterator it = map.find(key);
208         if (it == map.end())
209                 return docstring();
210         docstring const & data = it->second;
211
212         // Is the entry a BibTeX one or one from lyx-layout "bibliography"?
213         docstring::size_type const pos = data.find(TheBibliographyRef);
214         if (pos != docstring::npos) {
215                 if (pos <= 2) {
216                         return docstring();
217                 }
218
219                 docstring const opt = trim(data.substr(0, pos - 1));
220                 if (opt.empty())
221                         return docstring();
222
223                 docstring authors;
224                 split(opt, authors, '(');
225                 return authors;
226         }
227
228         docstring author = parseBibTeX(data, "author");
229
230         if (author.empty())
231                 author = parseBibTeX(data, "editor");
232
233         if (author.empty()) {
234                 author = parseBibTeX(data, "key");
235                 if (author.empty())
236                         // FIXME UNICODE
237                         return from_utf8(key);
238                 return author;
239         }
240
241         vector<docstring> const authors = getVectorFromString(author, from_ascii(" and "));
242         if (authors.empty())
243                 return author;
244
245         if (authors.size() == 2)
246                 return bformat(_("%1$s and %2$s"),
247                         familyName(authors[0]), familyName(authors[1]));
248
249         if (authors.size() > 2)
250                 return bformat(_("%1$s et al."), familyName(authors[0]));
251
252         return familyName(authors[0]);
253 }
254
255
256 docstring const getYear(InfoMap const & map, string const & key)
257 {
258         BOOST_ASSERT(!map.empty());
259
260         InfoMap::const_iterator it = map.find(key);
261         if (it == map.end())
262                 return docstring();
263         docstring const & data = it->second;
264
265         // Is the entry a BibTeX one or one from lyx-layout "bibliography"?
266         docstring::size_type const pos = data.find(TheBibliographyRef);
267         if (pos != docstring::npos) {
268                 if (pos <= 2) {
269                         return docstring();
270                 }
271
272                 docstring const opt =
273                         trim(data.substr(0, pos - 1));
274                 if (opt.empty())
275                         return docstring();
276
277                 docstring authors;
278                 docstring const tmp = split(opt, authors, '(');
279                 docstring year;
280                 split(tmp, year, ')');
281                 return year;
282
283         }
284
285         docstring year = parseBibTeX(data, "year");
286         if (year.empty())
287                 year = _("No year");
288
289         return year;
290 }
291
292
293 namespace {
294
295 // A functor for use with std::sort, leading to case insensitive sorting
296 class compareNoCase: public std::binary_function<string, string, bool>
297 {
298 public:
299         bool operator()(string const & s1, string const & s2) const {
300                 return compare_ascii_no_case(s1, s2) < 0;
301         }
302 };
303
304 } // namespace anon
305
306
307 vector<string> const getKeys(InfoMap const & map)
308 {
309         vector<string> bibkeys;
310         InfoMap::const_iterator it  = map.begin();
311         InfoMap::const_iterator end = map.end();
312         for (; it != end; ++it) {
313                 bibkeys.push_back(it->first);
314         }
315
316         std::sort(bibkeys.begin(), bibkeys.end(), compareNoCase());
317         return bibkeys;
318 }
319
320
321 docstring const getInfo(InfoMap const & map, string const & key)
322 {
323         BOOST_ASSERT(!map.empty());
324
325         InfoMap::const_iterator it = map.find(key);
326         if (it == map.end())
327                 return docstring();
328         docstring const & data = it->second;
329
330         // is the entry a BibTeX one or one from lyx-layout "bibliography"?
331         docstring::size_type const pos = data.find(TheBibliographyRef);
332         if (pos != docstring::npos) {
333                 docstring::size_type const pos2 = pos + TheBibliographyRef.size();
334                 docstring const info = trim(data.substr(pos2));
335                 return info;
336         }
337
338         // Search for all possible "required" keys
339         docstring author = parseBibTeX(data, "author");
340         if (author.empty())
341                 author = parseBibTeX(data, "editor");
342
343         docstring year      = parseBibTeX(data, "year");
344         docstring title     = parseBibTeX(data, "title");
345         docstring booktitle = parseBibTeX(data, "booktitle");
346         docstring chapter   = parseBibTeX(data, "chapter");
347         docstring number    = parseBibTeX(data, "number");
348         docstring volume    = parseBibTeX(data, "volume");
349         docstring pages     = parseBibTeX(data, "pages");
350         docstring annote    = parseBibTeX(data, "annote");
351         docstring media     = parseBibTeX(data, "journal");
352         if (media.empty())
353                 media = parseBibTeX(data, "publisher");
354         if (media.empty())
355                 media = parseBibTeX(data, "school");
356         if (media.empty())
357                 media = parseBibTeX(data, "institution");
358
359         odocstringstream result;
360         if (!author.empty())
361                 result << author << ", ";
362         if (!title.empty())
363                 result << title;
364         if (!booktitle.empty())
365                 result << ", in " << booktitle;
366         if (!chapter.empty())
367                 result << ", Ch. " << chapter;
368         if (!media.empty())
369                 result << ", " << media;
370         if (!volume.empty())
371                 result << ", vol. " << volume;
372         if (!number.empty())
373                 result << ", no. " << number;
374         if (!pages.empty())
375                 result << ", pp. " << pages;
376         if (!year.empty())
377                 result << ", " << year;
378         if (!annote.empty())
379                 result << "\n\n" << annote;
380
381         docstring const result_str = rtrim(result.str());
382         if (!result_str.empty())
383                 return result_str;
384
385         // This should never happen (or at least be very unusual!)
386         return data;
387 }
388
389
390 namespace {
391
392 // Escape special chars.
393 // All characters are literals except: '.|*?+(){}[]^$\'
394 // These characters are literals when preceded by a "\", which is done here
395 // @todo: This function should be moved to support, and then the test in tests
396 //        should be moved there as well.
397 string const escape_special_chars(string const & expr)
398 {
399         // Search for all chars '.|*?+(){}[^$]\'
400         // Note that '[' and '\' must be escaped.
401         // This is a limitation of boost::regex, but all other chars in BREs
402         // are assumed literal.
403         boost::regex reg("[].|*?+(){}^$\\[\\\\]");
404
405         // $& is a perl-like expression that expands to all
406         // of the current match
407         // The '$' must be prefixed with the escape character '\' for
408         // boost to treat it as a literal.
409         // Thus, to prefix a matched expression with '\', we use:
410         return boost::regex_replace(expr, reg, "\\\\$&");
411 }
412
413
414 // A functor for use with std::find_if, used to ascertain whether a
415 // data entry matches the required regex_
416 // @throws: boost::regex_error if the supplied regex pattern is not valid
417 // @todo: This function should be moved to support.
418 class RegexMatch : public std::unary_function<string, bool>
419 {
420 public:
421         // re and icase are used to construct an instance of boost::RegEx.
422         // if icase is true, then matching is insensitive to case
423         RegexMatch(InfoMap const & m, string const & re, bool icase)
424                 : map_(m), regex_(re, icase) {}
425
426         bool operator()(string const & key) const {
427                 // the data searched is the key + its associated BibTeX/biblio
428                 // fields
429                 string data = key;
430                 InfoMap::const_iterator info = map_.find(key);
431                 if (info != map_.end())
432                         // FIXME UNICODE
433                         data += ' ' + to_utf8(info->second);
434
435                 // Attempts to find a match for the current RE
436                 // somewhere in data.
437                 return boost::regex_search(data, regex_);
438         }
439 private:
440         InfoMap const map_;
441         mutable boost::regex regex_;
442 };
443
444 } // namespace anon
445
446
447 vector<string>::const_iterator
448 searchKeys(InfoMap const & theMap,
449            vector<string> const & keys,
450            string const & search_expr,
451            vector<string>::const_iterator start,
452            Search type,
453            Direction dir,
454            bool caseSensitive)
455 {
456         // Preliminary checks
457         if (start < keys.begin() || start >= keys.end())
458                 return keys.end();
459
460         string expr = trim(search_expr);
461         if (expr.empty())
462                 return keys.end();
463
464         if (type == SIMPLE)
465                 // We must escape special chars in the search_expr so that
466                 // it is treated as a simple string by boost::regex.
467                 expr = escape_special_chars(expr);
468
469         try {
470                 // Build the functor that will be passed to find_if.
471                 RegexMatch const match(theMap, expr, !caseSensitive);
472
473                 // Search the vector of 'keys' from 'start' for one
474                 // that matches the predicate 'match'. Searching can
475                 // be forward or backward from start.
476                 if (dir == FORWARD)
477                         return std::find_if(start, keys.end(), match);
478
479                 vector<string>::const_reverse_iterator rit(start);
480                 vector<string>::const_reverse_iterator rend = keys.rend();
481                 rit = std::find_if(rit, rend, match);
482
483                 if (rit == rend)
484                         return keys.end();
485                 // This is correct and always safe.
486                 // (See Meyer's Effective STL, Item 28.)
487                 return (++rit).base();
488         }
489         catch (boost::regex_error &) {
490                 return keys.end();
491         }
492 }
493
494
495 docstring const parseBibTeX(docstring data, string const & findkey)
496 {
497         // at first we delete all characters right of '%' and
498         // replace tabs through a space and remove leading spaces
499         // we read the data line by line so that the \n are
500         // ignored, too.
501         docstring data_;
502         int Entries = 0;
503         docstring dummy = token(data,'\n', Entries);
504         while (!dummy.empty()) {
505                 // no tabs
506                 dummy = subst(dummy, '\t', ' ');
507                 // no leading spaces
508                 dummy = ltrim(dummy);
509                 // ignore lines with a beginning '%' or ignore all right of %
510                 docstring::size_type const idx =
511                         dummy.empty() ? docstring::npos : dummy.find('%');
512                 if (idx != docstring::npos)
513                         // Check if this is really a comment or just "\%"
514                         if (idx == 0 || dummy[idx - 1] != '\\')
515                                 dummy.erase(idx, docstring::npos);
516                         else
517                                 //  This is "\%", so just erase the '\'
518                                 dummy.erase(idx - 1, 1);
519                 // do we have a new token or a new line of
520                 // the same one? In the first case we ignore
521                 // the \n and in the second we replace it
522                 // with a space
523                 if (!dummy.empty()) {
524                         if (!contains(dummy, '='))
525                                 data_ += ' ' + dummy;
526                         else
527                                 data_ += dummy;
528                 }
529                 dummy = token(data, '\n', ++Entries);
530         }
531
532         // replace double commas with "" for easy scanning
533         data = subst(data_, from_ascii(",,"), from_ascii("\"\""));
534
535         // unlikely!
536         if (data.empty())
537                 return docstring();
538
539         // now get only the important line of the bibtex entry.
540         // all entries are devided by ',' except the last one.
541         data += ',';
542         // now we have same behaviour for all entries because the last one
543         // is "blah ... }"
544         Entries = 0;
545         bool found = false;
546         // parsing of title and booktitle is different from the
547         // others, because booktitle contains title
548         do {
549                 dummy = token(data, ',', Entries++);
550                 if (!dummy.empty()) {
551                         found = contains(ascii_lowercase(dummy), from_ascii(findkey));
552                         if (findkey == "title" &&
553                             contains(ascii_lowercase(dummy), from_ascii("booktitle")))
554                                 found = false;
555                 }
556         } while (!found && !dummy.empty());
557         if (dummy.empty())
558                 // no such keyword
559                 return docstring();
560
561         // we are not sure, if we get all, because "key= "blah, blah" is
562         // allowed.
563         // Therefore we read all until the next "=" character, which follows a
564         // new keyword
565         docstring keyvalue = dummy;
566         dummy = token(data, ',', Entries++);
567         while (!contains(dummy, '=') && !dummy.empty()) {
568                 keyvalue += ',' + dummy;
569                 dummy = token(data, ',', Entries++);
570         }
571
572         // replace double "" with originals ,, (two commas)
573         // leaving us with the all-important line
574         data = subst(keyvalue, from_ascii("\"\""), from_ascii(",,"));
575
576         // Clean-up.
577         // 1. Spaces
578         data = rtrim(data);
579         // 2. if there is no opening '{' then a closing '{' is probably cruft.
580         if (!contains(data, '{'))
581                 data = rtrim(data, "}");
582         // happens, when last keyword
583         docstring::size_type const idx =
584                 !data.empty() ? data.find('=') : docstring::npos;
585
586         if (idx == docstring::npos)
587                 return docstring();
588
589         data = trim(data.substr(idx));
590
591         // a valid entry?
592         if (data.length() < 2 || data[0] != '=')
593                 return docstring();
594         else {
595                 // delete '=' and the following spaces
596                 data = ltrim(data, " =");
597                 if (data.length() < 2) {
598                         // not long enough to find delimiters
599                         return data;
600                 } else {
601                         docstring::size_type keypos = 1;
602                         char_type enclosing;
603                         if (data[0] == '{') {
604                                 enclosing = '}';
605                         } else if (data[0] == '"') {
606                                 enclosing = '"';
607                         } else {
608                                 // no {} and no "", pure data but with a
609                                 // possible ',' at the end
610                                 return rtrim(data, ",");
611                         }
612                         docstring tmp = data.substr(keypos);
613                         while (tmp.find('{') != docstring::npos &&
614                                tmp.find('}') != docstring::npos &&
615                                tmp.find('{') < tmp.find('}') &&
616                                tmp.find('{') < tmp.find(enclosing)) {
617
618                                 keypos += tmp.find('{') + 1;
619                                 tmp = data.substr(keypos);
620                                 keypos += tmp.find('}') + 1;
621                                 tmp = data.substr(keypos);
622                         }
623                         if (tmp.find(enclosing) == docstring::npos)
624                                 return data;
625                         else {
626                                 keypos += tmp.find(enclosing);
627                                 return data.substr(1, keypos - 1);
628                         }
629                 }
630         }
631 }
632
633
634 namespace {
635
636
637 char const * const citeCommands[] = {
638         "cite", "citet", "citep", "citealt", "citealp", "citeauthor",
639         "citeyear", "citeyearpar" };
640
641 unsigned int const nCiteCommands =
642         sizeof(citeCommands) / sizeof(char *);
643
644 CiteStyle const citeStyles[] = {
645         CITE, CITET, CITEP, CITEALT, CITEALP,
646         CITEAUTHOR, CITEYEAR, CITEYEARPAR };
647
648 unsigned int const nCiteStyles =
649         sizeof(citeStyles) / sizeof(CiteStyle);
650
651 CiteStyle const citeStylesFull[] = {
652         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
653
654 unsigned int const nCiteStylesFull =
655         sizeof(citeStylesFull) / sizeof(CiteStyle);
656
657 CiteStyle const citeStylesUCase[] = {
658         CITET, CITEP, CITEALT, CITEALP, CITEAUTHOR };
659
660 unsigned int const nCiteStylesUCase =
661         sizeof(citeStylesUCase) / sizeof(CiteStyle);
662
663 } // namespace anon
664
665
666 CitationStyle::CitationStyle(string const & command)
667         : style(CITE), full(false), forceUCase(false)
668 {
669         if (command.empty())
670                 return;
671
672         string cmd = command;
673         if (cmd[0] == 'C') {
674                 forceUCase = true;
675                 cmd[0] = 'c';
676         }
677
678         string::size_type const n = cmd.size() - 1;
679         if (cmd != "cite" && cmd[n] == '*') {
680                 full = true;
681                 cmd = cmd.substr(0,n);
682         }
683
684         char const * const * const last = citeCommands + nCiteCommands;
685         char const * const * const ptr = std::find(citeCommands, last, cmd);
686
687         if (ptr != last) {
688                 size_t idx = ptr - citeCommands;
689                 style = citeStyles[idx];
690         }
691 }
692
693
694 string const CitationStyle::asLatexStr() const
695 {
696         string cite = citeCommands[style];
697         if (full) {
698                 CiteStyle const * last = citeStylesFull + nCiteStylesFull;
699                 if (std::find(citeStylesFull, last, style) != last)
700                         cite += '*';
701         }
702
703         if (forceUCase) {
704                 CiteStyle const * last = citeStylesUCase + nCiteStylesUCase;
705                 if (std::find(citeStylesUCase, last, style) != last)
706                         cite[0] = 'C';
707         }
708
709         return cite;
710 }
711
712
713 vector<CiteStyle> const getCiteStyles(CiteEngine const engine)
714 {
715         unsigned int nStyles = 0;
716         unsigned int start = 0;
717
718         switch (engine) {
719         case ENGINE_BASIC:
720                 nStyles = 1;
721                 start = 0;
722                 break;
723         case ENGINE_NATBIB_AUTHORYEAR:
724         case ENGINE_NATBIB_NUMERICAL:
725                 nStyles = nCiteStyles - 1;
726                 start = 1;
727                 break;
728         case ENGINE_JURABIB:
729                 nStyles = nCiteStyles;
730                 start = 0;
731                 break;
732         }
733
734         typedef vector<CiteStyle> cite_vec;
735
736         cite_vec styles(nStyles);
737         cite_vec::size_type i = 0;
738         int j = start;
739         for (; i != styles.size(); ++i, ++j)
740                 styles[i] = citeStyles[j];
741
742         return styles;
743 }
744
745
746 vector<docstring> const
747 getNumericalStrings(string const & key,
748                     InfoMap const & map, vector<CiteStyle> const & styles)
749 {
750         if (map.empty())
751                 return vector<docstring>();
752
753         docstring const author = getAbbreviatedAuthor(map, key);
754         docstring const year   = getYear(map, key);
755         if (author.empty() || year.empty())
756                 return vector<docstring>();
757
758         vector<docstring> vec(styles.size());
759         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
760                 docstring str;
761
762                 switch (styles[i]) {
763                 case CITE:
764                 case CITEP:
765                         str = from_ascii("[#ID]");
766                         break;
767
768                 case CITET:
769                         str = author + " [#ID]";
770                         break;
771
772                 case CITEALT:
773                         str = author + " #ID";
774                         break;
775
776                 case CITEALP:
777                         str = from_ascii("#ID");
778                         break;
779
780                 case CITEAUTHOR:
781                         str = author;
782                         break;
783
784                 case CITEYEAR:
785                         str = year;
786                         break;
787
788                 case CITEYEARPAR:
789                         str = '(' + year + ')';
790                         break;
791                 }
792
793                 vec[i] = str;
794         }
795
796         return vec;
797 }
798
799
800 vector<docstring> const
801 getAuthorYearStrings(string const & key,
802                     InfoMap const & map, vector<CiteStyle> const & styles)
803 {
804         if (map.empty())
805                 return vector<docstring>();
806
807         docstring const author = getAbbreviatedAuthor(map, key);
808         docstring const year   = getYear(map, key);
809         if (author.empty() || year.empty())
810                 return vector<docstring>();
811
812         vector<docstring> vec(styles.size());
813         for (vector<docstring>::size_type i = 0; i != vec.size(); ++i) {
814                 docstring str;
815
816                 switch (styles[i]) {
817                 case CITE:
818                         // jurabib only: Author/Annotator
819                         // (i.e. the "before" field, 2nd opt arg)
820                         str = author + "/<" + _("before") + '>';
821                         break;
822
823                 case CITET:
824                         str = author + " (" + year + ')';
825                         break;
826
827                 case CITEP:
828                         str = '(' + author + ", " + year + ')';
829                         break;
830
831                 case CITEALT:
832                         str = author + ' ' + year ;
833                         break;
834
835                 case CITEALP:
836                         str = author + ", " + year ;
837                         break;
838
839                 case CITEAUTHOR:
840                         str = author;
841                         break;
842
843                 case CITEYEAR:
844                         str = year;
845                         break;
846
847                 case CITEYEARPAR:
848                         str = '(' + year + ')';
849                         break;
850                 }
851
852                 vec[i] = str;
853         }
854
855         return vec;
856 }
857
858 } // namespace biblio
859
860 namespace frontend {
861
862 vector<FamilyPair> const getFamilyData()
863 {
864         vector<FamilyPair> family(5);
865
866         FamilyPair pr;
867
868         pr.first = _("No change");
869         pr.second = Font::IGNORE_FAMILY;
870         family[0] = pr;
871
872         pr.first = _("Roman");
873         pr.second = Font::ROMAN_FAMILY;
874         family[1] = pr;
875
876         pr.first = _("Sans Serif");
877         pr.second = Font::SANS_FAMILY;
878         family[2] = pr;
879
880         pr.first = _("Typewriter");
881         pr.second = Font::TYPEWRITER_FAMILY;
882         family[3] = pr;
883
884         pr.first = _("Reset");
885         pr.second = Font::INHERIT_FAMILY;
886         family[4] = pr;
887
888         return family;
889 }
890
891
892 vector<SeriesPair> const getSeriesData()
893 {
894         vector<SeriesPair> series(4);
895
896         SeriesPair pr;
897
898         pr.first = _("No change");
899         pr.second = Font::IGNORE_SERIES;
900         series[0] = pr;
901
902         pr.first = _("Medium");
903         pr.second = Font::MEDIUM_SERIES;
904         series[1] = pr;
905
906         pr.first = _("Bold");
907         pr.second = Font::BOLD_SERIES;
908         series[2] = pr;
909
910         pr.first = _("Reset");
911         pr.second = Font::INHERIT_SERIES;
912         series[3] = pr;
913
914         return series;
915 }
916
917
918 vector<ShapePair> const getShapeData()
919 {
920         vector<ShapePair> shape(6);
921
922         ShapePair pr;
923
924         pr.first = _("No change");
925         pr.second = Font::IGNORE_SHAPE;
926         shape[0] = pr;
927
928         pr.first = _("Upright");
929         pr.second = Font::UP_SHAPE;
930         shape[1] = pr;
931
932         pr.first = _("Italic");
933         pr.second = Font::ITALIC_SHAPE;
934         shape[2] = pr;
935
936         pr.first = _("Slanted");
937         pr.second = Font::SLANTED_SHAPE;
938         shape[3] = pr;
939
940         pr.first = _("Small Caps");
941         pr.second = Font::SMALLCAPS_SHAPE;
942         shape[4] = pr;
943
944         pr.first = _("Reset");
945         pr.second = Font::INHERIT_SHAPE;
946         shape[5] = pr;
947
948         return shape;
949 }
950
951
952 vector<SizePair> const getSizeData()
953 {
954         vector<SizePair> size(14);
955
956         SizePair pr;
957
958         pr.first = _("No change");
959         pr.second = Font::IGNORE_SIZE;
960         size[0] = pr;
961
962         pr.first = _("Tiny");
963         pr.second = Font::SIZE_TINY;
964         size[1] = pr;
965
966         pr.first = _("Smallest");
967         pr.second = Font::SIZE_SCRIPT;
968         size[2] = pr;
969
970         pr.first = _("Smaller");
971         pr.second = Font::SIZE_FOOTNOTE;
972         size[3] = pr;
973
974         pr.first = _("Small");
975         pr.second = Font::SIZE_SMALL;
976         size[4] = pr;
977
978         pr.first = _("Normal");
979         pr.second = Font::SIZE_NORMAL;
980         size[5] = pr;
981
982         pr.first = _("Large");
983         pr.second = Font::SIZE_LARGE;
984         size[6] = pr;
985
986         pr.first = _("Larger");
987         pr.second = Font::SIZE_LARGER;
988         size[7] = pr;
989
990         pr.first = _("Largest");
991         pr.second = Font::SIZE_LARGEST;
992         size[8] = pr;
993
994         pr.first = _("Huge");
995         pr.second = Font::SIZE_HUGE;
996         size[9] = pr;
997
998         pr.first = _("Huger");
999         pr.second = Font::SIZE_HUGER;
1000         size[10] = pr;
1001
1002         pr.first = _("Increase");
1003         pr.second = Font::INCREASE_SIZE;
1004         size[11] = pr;
1005
1006         pr.first = _("Decrease");
1007         pr.second = Font::DECREASE_SIZE;
1008         size[12] = pr;
1009
1010         pr.first = _("Reset");
1011         pr.second = Font::INHERIT_SIZE;
1012         size[13] = pr;
1013
1014         return size;
1015 }
1016
1017
1018 vector<BarPair> const getBarData()
1019 {
1020         vector<BarPair> bar(5);
1021
1022         BarPair pr;
1023
1024         pr.first = _("No change");
1025         pr.second = IGNORE;
1026         bar[0] = pr;
1027
1028         pr.first = _("Emph");
1029         pr.second = EMPH_TOGGLE;
1030         bar[1] = pr;
1031
1032         pr.first = _("Underbar");
1033         pr.second = UNDERBAR_TOGGLE;
1034         bar[2] = pr;
1035
1036         pr.first = _("Noun");
1037         pr.second = NOUN_TOGGLE;
1038         bar[3] = pr;
1039
1040         pr.first = _("Reset");
1041         pr.second = INHERIT;
1042         bar[4] = pr;
1043
1044         return bar;
1045 }
1046
1047
1048 vector<ColorPair> const getColorData()
1049 {
1050         vector<ColorPair> color(11);
1051
1052         ColorPair pr;
1053
1054         pr.first = _("No change");
1055         pr.second = Color::ignore;
1056         color[0] = pr;
1057
1058         pr.first = _("No color");
1059         pr.second = Color::none;
1060         color[1] = pr;
1061
1062         pr.first = _("Black");
1063         pr.second = Color::black;
1064         color[2] = pr;
1065
1066         pr.first = _("White");
1067         pr.second = Color::white;
1068         color[3] = pr;
1069
1070         pr.first = _("Red");
1071         pr.second = Color::red;
1072         color[4] = pr;
1073
1074         pr.first = _("Green");
1075         pr.second = Color::green;
1076         color[5] = pr;
1077
1078         pr.first = _("Blue");
1079         pr.second = Color::blue;
1080         color[6] = pr;
1081
1082         pr.first = _("Cyan");
1083         pr.second = Color::cyan;
1084         color[7] = pr;
1085
1086         pr.first = _("Magenta");
1087         pr.second = Color::magenta;
1088         color[8] = pr;
1089
1090         pr.first = _("Yellow");
1091         pr.second = Color::yellow;
1092         color[9] = pr;
1093
1094         pr.first = _("Reset");
1095         pr.second = Color::inherit;
1096         color[10] = pr;
1097
1098         return color;
1099 }
1100
1101
1102
1103 namespace {
1104
1105 class Sorter
1106         : public std::binary_function<LanguagePair,
1107                                       LanguagePair, bool>
1108 {
1109 public:
1110         bool operator()(LanguagePair const & lhs,
1111                         LanguagePair const & rhs) const {
1112                 return lhs.first < rhs.first;
1113         }
1114 };
1115
1116
1117 class ColorSorter
1118 {
1119 public:
1120         bool operator()(Color::color const & lhs,
1121                         Color::color const & rhs) const {
1122                 return lcolor.getGUIName(lhs) < lcolor.getGUIName(rhs);
1123         }
1124 };
1125
1126 } // namespace anon
1127
1128
1129 vector<LanguagePair> const getLanguageData(bool character_dlg)
1130 {
1131         vector<LanguagePair>::size_type const size = character_dlg ?
1132                 languages.size() + 2 : languages.size();
1133
1134         vector<LanguagePair> langs(size);
1135
1136         if (character_dlg) {
1137                 langs[0].first = _("No change");
1138                 langs[0].second = "ignore";
1139                 langs[1].first = _("Reset");
1140                 langs[1].second = "reset";
1141         }
1142
1143         vector<string>::size_type i = character_dlg ? 2 : 0;
1144         for (Languages::const_iterator cit = languages.begin();
1145              cit != languages.end(); ++cit) {
1146                 langs[i].first  = _(cit->second.display());
1147                 langs[i].second = cit->second.lang();
1148                 ++i;
1149         }
1150
1151         // Don't sort "ignore" and "reset"
1152         vector<LanguagePair>::iterator begin = character_dlg ?
1153                 langs.begin() + 2 : langs.begin();
1154
1155         std::sort(begin, langs.end(), Sorter());
1156
1157         return langs;
1158 }
1159
1160
1161 vector<Color_color> const getSortedColors(vector<Color_color> colors)
1162 {
1163         // sort the colors
1164         std::sort(colors.begin(), colors.end(), ColorSorter());
1165         return colors;
1166 }
1167
1168 } // namespace frontend
1169
1170 using support::addName;
1171 using support::FileFilterList;
1172 using support::getExtension;
1173 using support::libFileSearch;
1174 using support::makeAbsPath;
1175 using support::makeRelPath;
1176 using support::onlyFilename;
1177 using support::onlyPath;
1178 using support::package;
1179 using support::prefixIs;
1180 using support::removeExtension;
1181
1182 namespace frontend {
1183
1184
1185 docstring const browseFile(docstring const & filename,
1186                         docstring const & title,
1187                         FileFilterList const & filters,
1188                         bool save,
1189                         pair<docstring,docstring> const & dir1,
1190                         pair<docstring,docstring> const & dir2)
1191 {
1192         docstring lastPath = from_ascii(".");
1193         if (!filename.empty())
1194                 lastPath = from_utf8(onlyPath(to_utf8(filename)));
1195
1196         FileDialog fileDlg(title, LFUN_SELECT_FILE_SYNC, dir1, dir2);
1197
1198         FileDialog::Result result;
1199
1200         if (save)
1201                 result = fileDlg.save(lastPath, filters,
1202                                       from_utf8(onlyFilename(to_utf8(filename))));
1203         else
1204                 result = fileDlg.open(lastPath, filters,
1205                                       from_utf8(onlyFilename(to_utf8(filename))));
1206
1207         return result.second;
1208 }
1209
1210
1211 docstring const browseRelFile(docstring const & filename,
1212                            docstring const & refpath,
1213                            docstring const & title,
1214                            FileFilterList const & filters,
1215                            bool save,
1216                            pair<docstring,docstring> const & dir1,
1217                            pair<docstring,docstring> const & dir2)
1218 {
1219         docstring const fname = from_utf8(makeAbsPath(
1220                 to_utf8(filename), to_utf8(refpath)).absFilename());
1221
1222         docstring const outname = browseFile(fname, title, filters, save,
1223                                           dir1, dir2);
1224         docstring const reloutname = makeRelPath(outname, refpath);
1225         if (prefixIs(reloutname, from_ascii("../")))
1226                 return outname;
1227         else
1228                 return reloutname;
1229 }
1230
1231
1232
1233 docstring const browseLibFile(docstring const & dir,
1234                            docstring const & name,
1235                            docstring const & ext,
1236                            docstring const & title,
1237                            FileFilterList const & filters)
1238 {
1239         // FIXME UNICODE
1240         pair<docstring, docstring> const dir1(_("System files|#S#s"),
1241                                        from_utf8(addName(package().system_support().absFilename(), to_utf8(dir))));
1242
1243         pair<docstring, docstring> const dir2(_("User files|#U#u"),
1244                                        from_utf8(addName(package().user_support().absFilename(), to_utf8(dir))));
1245
1246         docstring const result = browseFile(from_utf8(
1247                 libFileSearch(to_utf8(dir), to_utf8(name), to_utf8(ext)).absFilename()),
1248                 title, filters, false, dir1, dir2);
1249
1250         // remove the extension if it is the default one
1251         docstring noextresult;
1252         if (from_utf8(getExtension(to_utf8(result))) == ext)
1253                 noextresult = from_utf8(removeExtension(to_utf8(result)));
1254         else
1255                 noextresult = result;
1256
1257         // remove the directory, if it is the default one
1258         docstring const file = from_utf8(onlyFilename(to_utf8(noextresult)));
1259         if (from_utf8(libFileSearch(to_utf8(dir), to_utf8(file), to_utf8(ext)).absFilename()) == result)
1260                 return file;
1261         else
1262                 return noextresult;
1263 }
1264
1265
1266 docstring const browseDir(docstring const & pathname,
1267                        docstring const & title,
1268                        pair<docstring,docstring> const & dir1,
1269                        pair<docstring,docstring> const & dir2)
1270 {
1271         docstring lastPath = from_ascii(".");
1272         if (!pathname.empty())
1273                 lastPath = from_utf8(onlyPath(to_utf8(pathname)));
1274
1275         FileDialog fileDlg(title, LFUN_SELECT_FILE_SYNC, dir1, dir2);
1276
1277         FileDialog::Result const result =
1278                 fileDlg.opendir(lastPath, from_utf8(onlyFilename(to_utf8(pathname))));
1279
1280         return result.second;
1281 }
1282
1283
1284 vector<docstring> const getLatexUnits()
1285 {
1286         vector<docstring> units;
1287         int i = 0;
1288         char const * str = stringFromUnit(i);
1289         for (; str != 0; ++i, str = stringFromUnit(i))
1290                 units.push_back(from_ascii(str));
1291
1292         return units;
1293 }
1294
1295 } // namespace frontend
1296
1297
1298 using support::bformat;
1299 using support::contains;
1300 using support::FileName;
1301 using support::getExtension;
1302 using support::getFileContents;
1303 using support::getVectorFromString;
1304 using support::libFileSearch;
1305 using support::onlyFilename;
1306 using support::package;
1307 using support::quoteName;
1308 using support::split;
1309 using support::Systemcall;
1310 using support::token;
1311
1312 namespace frontend {
1313
1314 void rescanTexStyles()
1315 {
1316         // Run rescan in user lyx directory
1317         support::Path p(package().user_support());
1318         FileName const command = libFileSearch("scripts", "TeXFiles.py");
1319         Systemcall one;
1320         int const status = one.startscript(Systemcall::Wait,
1321                         lyx::support::os::python() + ' ' +
1322                         quoteName(command.toFilesystemEncoding()));
1323         if (status == 0)
1324                 return;
1325         // FIXME UNICODE
1326         Alert::error(_("Could not update TeX information"),
1327                      bformat(_("The script `%s' failed."), lyx::from_utf8(command.absFilename())));
1328 }
1329
1330
1331 void texhash()
1332 {
1333         // Run texhash in user lyx directory
1334         support::Path p(package().user_support());
1335
1336         //path to texhash through system
1337         Systemcall one;
1338         one.startscript(Systemcall::Wait,"texhash");
1339 }
1340
1341
1342 void getTexFileList(string const & filename, std::vector<string> & list)
1343 {
1344         list.clear();
1345         FileName const file = libFileSearch("", filename);
1346         if (file.empty())
1347                 return;
1348
1349         list = getVectorFromString(getFileContents(file), "\n");
1350
1351         // Normalise paths like /foo//bar ==> /foo/bar
1352         boost::RegEx regex("/{2,}");
1353         std::vector<string>::iterator it  = list.begin();
1354         std::vector<string>::iterator end = list.end();
1355         for (; it != end; ++it) {
1356                 *it = regex.Merge((*it), "/");
1357         }
1358
1359         // remove empty items and duplicates
1360         list.erase(std::remove(list.begin(), list.end(), ""), list.end());
1361         eliminate_duplicates(list);
1362 }
1363
1364
1365 string const getListOfOptions(string const & classname, string const & type)
1366 {
1367         FileName const filename(getTexFileFromList(classname, type));
1368         if (filename.empty())
1369                 return string();
1370         string optionList = string();
1371         std::ifstream is(filename.toFilesystemEncoding().c_str());
1372         while (is) {
1373                 string s;
1374                 is >> s;
1375                 if (contains(s,"DeclareOption")) {
1376                         s = s.substr(s.find("DeclareOption"));
1377                         s = split(s,'{');               // cut front
1378                         s = token(s,'}',0);             // cut end
1379                         optionList += (s + '\n');
1380                 }
1381         }
1382         return optionList;
1383 }
1384
1385
1386 string const getTexFileFromList(string const & file,
1387                             string const & type)
1388 {
1389         string file_ = file;
1390         // do we need to add the suffix?
1391         if (!(getExtension(file) == type))
1392                 file_ += '.' + type;
1393
1394         lyxerr << "Searching for file " << file_ << endl;
1395
1396         string lstfile;
1397         if (type == "cls")
1398                 lstfile = "clsFiles.lst";
1399         else if (type == "sty")
1400                 lstfile = "styFiles.lst";
1401         else if (type == "bst")
1402                 lstfile = "bstFiles.lst";
1403         else if (type == "bib")
1404                 lstfile = "bibFiles.lst";
1405         FileName const abslstfile = libFileSearch(string(), lstfile);
1406         if (abslstfile.empty()) {
1407                 lyxerr << "File `'" << lstfile << "' not found." << endl;
1408                 return string();
1409         }
1410         string const allClasses = getFileContents(abslstfile);
1411         int entries = 0;
1412         string classfile = token(allClasses, '\n', entries);
1413         int count = 0;
1414         while ((!contains(classfile, file) ||
1415                 (onlyFilename(classfile) != file)) &&
1416                 (++count < 1000)) {
1417                 classfile = token(allClasses, '\n', ++entries);
1418         }
1419
1420         // now we have filename with full path
1421         lyxerr << "with full path: " << classfile << endl;
1422
1423         return classfile;
1424 }
1425
1426 } // namespace frontend
1427 } // namespace lyx