]> git.lyx.org Git - lyx.git/blob - src/Format.cpp
4219174df7f5089786c3b3cf60bceaf2e66adf24
[lyx.git] / src / Format.cpp
1 /**
2  * \file Format.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Dekel Tsur
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "Format.h"
14 #include "Buffer.h"
15 #include "BufferParams.h"
16 #include "LyXRC.h"
17 #include "ServerSocket.h"
18
19 #include "frontends/alert.h" //to be removed?
20
21 #include "support/debug.h"
22 #include "support/filetools.h"
23 #include "support/gettext.h"
24 #include "support/lstrings.h"
25 #include "support/os.h"
26 #include "support/Path.h"
27 #include "support/Systemcall.h"
28 #include "support/textutils.h"
29 #include "support/Translator.h"
30
31 #include <algorithm>
32
33 // FIXME: Q_WS_MACX is not available, it's in Qt
34 #ifdef USE_MACOSX_PACKAGING
35 #include "support/linkback/LinkBackProxy.h"
36 #endif
37
38 using namespace std;
39 using namespace lyx::support;
40
41 namespace lyx {
42
43 namespace Alert = frontend::Alert;
44 namespace os = support::os;
45
46 namespace {
47
48 string const token_from_format("$$i");
49 string const token_path_format("$$p");
50 string const token_socket_format("$$a");
51
52
53 class FormatNamesEqual : public unary_function<Format, bool> {
54 public:
55         FormatNamesEqual(string const & name)
56                 : name_(name) {}
57         bool operator()(Format const & f) const
58         {
59                 return f.name() == name_;
60         }
61 private:
62         string name_;
63 };
64
65
66 class FormatExtensionsEqual : public unary_function<Format, bool> {
67 public:
68         FormatExtensionsEqual(string const & extension)
69                 : extension_(extension) {}
70         bool operator()(Format const & f) const
71         {
72                 return f.hasExtension(extension_);
73         }
74 private:
75         string extension_;
76 };
77
78 } //namespace anon
79
80
81 bool operator<(Format const & a, Format const & b)
82 {
83         // use the compare_ascii_no_case instead of compare_no_case,
84         // because in turkish, 'i' is not the lowercase version of 'I',
85         // and thus turkish locale breaks parsing of tags.
86
87         return compare_ascii_no_case(a.prettyname(), b.prettyname()) < 0;
88 }
89
90
91 Format::Format(string const & n, string const & e, string const & p,
92                string const & s, string const & v, string const & ed,
93                int flags)
94         : name_(n), extensions_(e), prettyname_(p), shortcut_(s), viewer_(v),
95           editor_(ed), flags_(flags)
96 {
97         extension_list_ = getVectorFromString(e, ",");
98 }
99
100
101 bool Format::dummy() const
102 {
103         return extension().empty();
104 }
105
106
107 bool Format::hasExtension(string const & e) const
108 {
109         return (find(extension_list_.begin(), extension_list_.end(), e)
110                 != extension_list_.end());
111 }
112
113
114 bool Format::isChildFormat() const
115 {
116         if (name_.empty())
117                 return false;
118         return isDigitASCII(name_[name_.length() - 1]);
119 }
120
121
122 string const Format::parentFormat() const
123 {
124         return name_.substr(0, name_.length() - 1);
125 }
126
127
128 void Format::setExtensions(string const & e)
129 {
130         extensions_ = e;
131         extension_list_ = getVectorFromString(e, ",");
132 }
133
134
135 // This method should return a reference, and throw an exception
136 // if the format named name cannot be found (Lgb)
137 Format const * Formats::getFormat(string const & name) const
138 {
139         FormatList::const_iterator cit =
140                 find_if(formatlist.begin(), formatlist.end(),
141                         FormatNamesEqual(name));
142         if (cit != formatlist.end())
143                 return &(*cit);
144         else
145                 return 0;
146 }
147
148
149 string Formats::getFormatFromFile(FileName const & filename) const
150 {
151         if (filename.empty())
152                 return string();
153
154         string const format = filename.guessFormatFromContents();
155         if (!format.empty())
156                 return format;
157
158         // try to find a format from the file extension.
159         string const ext = getExtension(filename.absFileName());
160         if (!ext.empty()) {
161                 // this is ambigous if two formats have the same extension,
162                 // but better than nothing
163                 Formats::const_iterator cit =
164                         find_if(formatlist.begin(), formatlist.end(),
165                                 FormatExtensionsEqual(ext));
166                 if (cit != formats.end()) {
167                         LYXERR(Debug::GRAPHICS, "\twill guess format from file extension: "
168                                 << ext << " -> " << cit->name());
169                         return cit->name();
170                 }
171         }
172         return string();
173 }
174
175
176 static string fixCommand(string const & cmd, string const & ext,
177                   os::auto_open_mode mode)
178 {
179         // configure.py says we do not want a viewer/editor
180         if (cmd.empty())
181                 return cmd;
182
183         // Does the OS manage this format?
184         if (os::canAutoOpenFile(ext, mode))
185                 return "auto";
186
187         // if configure.py found nothing, clear the command
188         if (token(cmd, ' ', 0) == "auto")
189                 return string();
190
191         // use the command found by configure.py
192         return cmd;
193 }
194
195
196 void Formats::setAutoOpen()
197 {
198         FormatList::iterator fit = formatlist.begin();
199         FormatList::iterator const fend = formatlist.end();
200         for ( ; fit != fend ; ++fit) {
201                 fit->setViewer(fixCommand(fit->viewer(), fit->extension(), os::VIEW));
202                 fit->setEditor(fixCommand(fit->editor(), fit->extension(), os::EDIT));
203         }
204 }
205
206
207 int Formats::getNumber(string const & name) const
208 {
209         FormatList::const_iterator cit =
210                 find_if(formatlist.begin(), formatlist.end(),
211                         FormatNamesEqual(name));
212         if (cit != formatlist.end())
213                 return distance(formatlist.begin(), cit);
214         else
215                 return -1;
216 }
217
218
219 void Formats::add(string const & name)
220 {
221         if (!getFormat(name))
222                 add(name, name, name, string(), string(), string(),
223                     Format::document);
224 }
225
226
227 void Formats::add(string const & name, string const & extensions,
228                   string const & prettyname, string const & shortcut,
229                   string const & viewer, string const & editor,
230                   int flags)
231 {
232         FormatList::iterator it =
233                 find_if(formatlist.begin(), formatlist.end(),
234                         FormatNamesEqual(name));
235         if (it == formatlist.end())
236                 formatlist.push_back(Format(name, extensions, prettyname,
237                                             shortcut, viewer, editor, flags));
238         else
239                 *it = Format(name, extensions, prettyname, shortcut, viewer,
240                              editor, flags);
241 }
242
243
244 void Formats::erase(string const & name)
245 {
246         FormatList::iterator it =
247                 find_if(formatlist.begin(), formatlist.end(),
248                         FormatNamesEqual(name));
249         if (it != formatlist.end())
250                 formatlist.erase(it);
251 }
252
253
254 void Formats::sort()
255 {
256         std::sort(formatlist.begin(), formatlist.end());
257 }
258
259
260 void Formats::setViewer(string const & name, string const & command)
261 {
262         add(name);
263         FormatList::iterator it =
264                 find_if(formatlist.begin(), formatlist.end(),
265                         FormatNamesEqual(name));
266         if (it != formatlist.end())
267                 it->setViewer(command);
268 }
269
270
271 void Formats::setEditor(string const & name, string const & command)
272 {
273         add(name);
274         FormatList::iterator it =
275                 find_if(formatlist.begin(), formatlist.end(),
276                         FormatNamesEqual(name));
277         if (it != formatlist.end())
278                 it->setEditor(command);
279 }
280
281
282 bool Formats::view(Buffer const & buffer, FileName const & filename,
283                    string const & format_name) const
284 {
285         if (filename.empty() || !filename.exists()) {
286                 Alert::error(_("Cannot view file"),
287                         bformat(_("File does not exist: %1$s"),
288                                 from_utf8(filename.absFileName())));
289                 return false;
290         }
291
292         Format const * format = getFormat(format_name);
293         if (format && format->viewer().empty() &&
294             format->isChildFormat())
295                 format = getFormat(format->parentFormat());
296         if (!format || format->viewer().empty()) {
297 // FIXME: I believe this is the wrong place to show alerts, it should be done
298 // by the caller (this should be "utility" code)
299                 Alert::error(_("Cannot view file"),
300                         bformat(_("No information for viewing %1$s"),
301                                 prettyName(format_name)));
302                 return false;
303         }
304         // viewer is 'auto'
305         if (format->viewer() == "auto") {
306                 if (os::autoOpenFile(filename.absFileName(), os::VIEW, buffer.filePath()))
307                         return true;
308                 else {
309                         Alert::error(_("Cannot view file"),
310                                 bformat(_("Auto-view file %1$s failed"),
311                                         from_utf8(filename.absFileName())));
312                         return false;
313                 }
314         }
315
316         string command = libScriptSearch(format->viewer());
317
318         if (format_name == "dvi" &&
319             !lyxrc.view_dvi_paper_option.empty()) {
320                 string paper_size = buffer.params().paperSizeName(BufferParams::XDVI);
321                 if (!paper_size.empty()) {
322                         command += ' ' + lyxrc.view_dvi_paper_option;
323                         command += ' ' + paper_size;
324                         if (buffer.params().orientation == ORIENTATION_LANDSCAPE &&
325                             buffer.params().papersize != PAPER_CUSTOM)
326                                 command += 'r';
327                 }
328         }
329
330         if (!contains(command, token_from_format))
331                 command += ' ' + token_from_format;
332
333         command = subst(command, token_from_format, quoteName(onlyFileName(filename.toFilesystemEncoding())));
334         command = subst(command, token_path_format, quoteName(onlyPath(filename.toFilesystemEncoding())));
335         command = subst(command, token_socket_format, quoteName(theServerSocket().address()));
336         LYXERR(Debug::FILES, "Executing command: " << command);
337         // FIXME UNICODE utf8 can be wrong for files
338         buffer.message(_("Executing command: ") + from_utf8(command));
339
340         PathChanger p(filename.onlyPath());
341         Systemcall one;
342         one.startscript(Systemcall::DontWait, command, buffer.filePath());
343
344         // we can't report any sort of error, since we aren't waiting
345         return true;
346 }
347
348
349 bool Formats::edit(Buffer const & buffer, FileName const & filename,
350                          string const & format_name) const
351 {
352         if (filename.empty() || !filename.exists()) {
353                 Alert::error(_("Cannot edit file"),
354                         bformat(_("File does not exist: %1$s"),
355                                 from_utf8(filename.absFileName())));
356                 return false;
357         }
358
359         // LinkBack files look like PDF, but have the .linkback extension
360         string const ext = getExtension(filename.absFileName());
361         if (format_name == "pdf" && ext == "linkback") {
362 #ifdef USE_MACOSX_PACKAGING
363                 return editLinkBackFile(filename.absFileName().c_str());
364 #else
365                 Alert::error(_("Cannot edit file"),
366                              _("LinkBack files can only be edited on Apple Mac OSX."));
367                 return false;
368 #endif // USE_MACOSX_PACKAGING
369         }
370
371         Format const * format = getFormat(format_name);
372         if (format && format->editor().empty() &&
373             format->isChildFormat())
374                 format = getFormat(format->parentFormat());
375         if (!format || format->editor().empty()) {
376 // FIXME: I believe this is the wrong place to show alerts, it should
377 // be done by the caller (this should be "utility" code)
378                 Alert::error(_("Cannot edit file"),
379                         bformat(_("No information for editing %1$s"),
380                                 prettyName(format_name)));
381                 return false;
382         }
383
384         // editor is 'auto'
385         if (format->editor() == "auto") {
386                 if (os::autoOpenFile(filename.absFileName(), os::EDIT, buffer.filePath()))
387                         return true;
388                 else {
389                         Alert::error(_("Cannot edit file"),
390                                 bformat(_("Auto-edit file %1$s failed"),
391                                         from_utf8(filename.absFileName())));
392                         return false;
393                 }
394         }
395
396         string command = format->editor();
397
398         if (!contains(command, token_from_format))
399                 command += ' ' + token_from_format;
400
401         command = subst(command, token_from_format, quoteName(filename.toFilesystemEncoding()));
402         command = subst(command, token_path_format, quoteName(onlyPath(filename.toFilesystemEncoding())));
403         command = subst(command, token_socket_format, quoteName(theServerSocket().address()));
404         LYXERR(Debug::FILES, "Executing command: " << command);
405         // FIXME UNICODE utf8 can be wrong for files
406         buffer.message(_("Executing command: ") + from_utf8(command));
407
408         Systemcall one;
409         one.startscript(Systemcall::DontWait, command, buffer.filePath());
410
411         // we can't report any sort of error, since we aren't waiting
412         return true;
413 }
414
415
416 docstring const Formats::prettyName(string const & name) const
417 {
418         Format const * format = getFormat(name);
419         if (format)
420                 return from_utf8(format->prettyname());
421         else
422                 return from_utf8(name);
423 }
424
425
426 string const Formats::extension(string const & name) const
427 {
428         Format const * format = getFormat(name);
429         if (format)
430                 return format->extension();
431         else
432                 return name;
433 }
434
435
436 string const Formats::extensions(string const & name) const
437 {
438         Format const * format = getFormat(name);
439         if (format)
440                 return format->extensions();
441         else
442                 return name;
443 }
444
445
446 namespace {
447 typedef Translator<OutputParams::FLAVOR, string> FlavorTranslator;
448
449 FlavorTranslator initFlavorTranslator()
450 {
451         FlavorTranslator f(OutputParams::LATEX, "latex");
452         f.addPair(OutputParams::DVILUATEX, "dviluatex");
453         f.addPair(OutputParams::LUATEX, "luatex");
454         f.addPair(OutputParams::PDFLATEX, "pdflatex");
455         f.addPair(OutputParams::XETEX, "xetex");
456         f.addPair(OutputParams::XML, "docbook-xml");
457         f.addPair(OutputParams::HTML, "xhtml");
458         f.addPair(OutputParams::TEXT, "text");
459         return f;
460 }
461
462
463 FlavorTranslator const & flavorTranslator()
464 {
465         static FlavorTranslator translator = initFlavorTranslator();
466         return translator;
467 }
468 }
469
470
471 std::string flavor2format(OutputParams::FLAVOR flavor)
472 {
473         return flavorTranslator().find(flavor);
474 }
475
476
477 /* Not currently needed, but I'll leave the code in case it is.
478 OutputParams::FLAVOR format2flavor(std::string fmt)
479 {
480         return flavorTranslator().find(fmt);
481 } */
482
483 Formats formats;
484
485 Formats system_formats;
486
487
488 } // namespace lyx