]> git.lyx.org Git - lyx.git/blob - src/Format.cpp
make frontend::Application a bit slimmer
[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 "debug.h"
18 #include "gettext.h"
19 #include "ServerSocket.h"
20
21 #include "frontends/alert.h" //to be removed?
22
23 #include "support/filetools.h"
24 #include "support/lstrings.h"
25 #include "support/os.h"
26 #include "support/Systemcall.h"
27
28 using std::find_if;
29 using std::string;
30 using std::distance;
31
32
33 namespace lyx {
34
35 using support::absolutePath;
36 using support::bformat;
37 using support::compare_ascii_no_case;
38 using support::contains;
39 using support::FileName;
40 using support::libScriptSearch;
41 using support::makeDisplayPath;
42 using support::onlyPath;
43 using support::quoteName;
44 using support::subst;
45 using support::Systemcall;
46 using support::token;
47
48 namespace Alert = frontend::Alert;
49 namespace os = support::os;
50
51 namespace {
52
53 string const token_from_format("$$i");
54 string const token_path_format("$$p");
55 string const token_socket_format("$$a");
56
57
58 class FormatNamesEqual : public std::unary_function<Format, bool> {
59 public:
60         FormatNamesEqual(string const & name)
61                 : name_(name) {}
62         bool operator()(Format const & f) const
63         {
64                 return f.name() == name_;
65         }
66 private:
67         string name_;
68 };
69
70
71 class FormatExtensionsEqual : public std::unary_function<Format, bool> {
72 public:
73         FormatExtensionsEqual(string const & extension)
74                 : extension_(extension) {}
75         bool operator()(Format const & f) const
76         {
77                 return f.extension() == extension_;
78         }
79 private:
80         string extension_;
81 };
82
83 } //namespace anon
84
85 bool operator<(Format const & a, Format const & b)
86 {
87         // use the compare_ascii_no_case instead of compare_no_case,
88         // because in turkish, 'i' is not the lowercase version of 'I',
89         // and thus turkish locale breaks parsing of tags.
90
91         return compare_ascii_no_case(a.prettyname(), b.prettyname()) < 0;
92 }
93
94
95 Format::Format(string const & n, string const & e, string const & p,
96                string const & s, string const & v, string const & ed,
97                int flags)
98         : name_(n), extension_(e), prettyname_(p), shortcut_(s), viewer_(v),
99           editor_(ed), flags_(flags)
100 {}
101
102
103 bool Format::dummy() const
104 {
105         return extension().empty();
106 }
107
108
109 bool Format::isChildFormat() const
110 {
111         if (name_.empty())
112                 return false;
113         return isdigit(name_[name_.length() - 1]);
114 }
115
116
117 string const Format::parentFormat() const
118 {
119         return name_.substr(0, name_.length() - 1);
120 }
121
122
123 // This method should return a reference, and throw an exception
124 // if the format named name cannot be found (Lgb)
125 Format const * Formats::getFormat(string const & name) const
126 {
127         FormatList::const_iterator cit =
128                 find_if(formatlist.begin(), formatlist.end(),
129                         FormatNamesEqual(name));
130         if (cit != formatlist.end())
131                 return &(*cit);
132         else
133                 return 0;
134 }
135
136
137 string Formats::getFormatFromFile(FileName const & filename) const
138 {
139         if (filename.empty())
140                 return string();
141
142         string const format = filename.guessFormatFromContents();
143         if (!format.empty())
144                 return format;
145
146         // try to find a format from the file extension.
147         string const ext = support::getExtension(filename.absFilename());
148         if (!ext.empty()) {
149                 // this is ambigous if two formats have the same extension,
150                 // but better than nothing
151                 Formats::const_iterator cit =
152                         find_if(formatlist.begin(), formatlist.end(),
153                                 FormatExtensionsEqual(ext));
154                 if (cit != formats.end()) {
155                         LYXERR(Debug::GRAPHICS, "\twill guess format from file extension: "
156                                 << ext << " -> " << cit->name());
157                         return cit->name();
158                 }
159         }
160         return string();
161 }
162
163
164 static string fixCommand(string const & cmd, string const & ext,
165                   os::auto_open_mode mode)
166 {
167         // configure.py says we do not want a viewer/editor
168         if (cmd.empty())
169                 return cmd;
170
171         // Does the OS manage this format?
172         if (os::canAutoOpenFile(ext, mode))
173                 return "auto";
174
175         // if configure.py found nothing, clear the command
176         if (token(cmd, ' ', 0) == "auto")
177                 return string();
178
179         // use the command found by configure.py
180         return cmd;
181 }
182
183
184 void Formats::setAutoOpen()
185 {
186         FormatList::iterator fit = formatlist.begin();
187         FormatList::iterator const fend = formatlist.end();
188         for ( ; fit != fend ; ++fit) {
189                 fit->setViewer(fixCommand(fit->viewer(), fit->extension(), os::VIEW));
190                 fit->setEditor(fixCommand(fit->editor(), fit->extension(), os::EDIT));
191         }
192 }
193
194
195 int Formats::getNumber(string const & name) const
196 {
197         FormatList::const_iterator cit =
198                 find_if(formatlist.begin(), formatlist.end(),
199                         FormatNamesEqual(name));
200         if (cit != formatlist.end())
201                 return distance(formatlist.begin(), cit);
202         else
203                 return -1;
204 }
205
206
207 void Formats::add(string const & name)
208 {
209         if (!getFormat(name))
210                 add(name, name, name, string(), string(), string(),
211                     Format::document);
212 }
213
214
215 void Formats::add(string const & name, string const & extension,
216                   string const & prettyname, string const & shortcut,
217                   string const & viewer, string const & editor,
218                   int flags)
219 {
220         FormatList::iterator it =
221                 find_if(formatlist.begin(), formatlist.end(),
222                         FormatNamesEqual(name));
223         if (it == formatlist.end())
224                 formatlist.push_back(Format(name, extension, prettyname,
225                                             shortcut, viewer, editor, flags));
226         else
227                 *it = Format(name, extension, prettyname, shortcut, viewer,
228                              editor, flags);
229 }
230
231
232 void Formats::erase(string const & name)
233 {
234         FormatList::iterator it =
235                 find_if(formatlist.begin(), formatlist.end(),
236                         FormatNamesEqual(name));
237         if (it != formatlist.end())
238                 formatlist.erase(it);
239 }
240
241
242 void Formats::sort()
243 {
244         std::sort(formatlist.begin(), formatlist.end());
245 }
246
247
248 void Formats::setViewer(string const & name, string const & command)
249 {
250         add(name);
251         FormatList::iterator it =
252                 find_if(formatlist.begin(), formatlist.end(),
253                         FormatNamesEqual(name));
254         if (it != formatlist.end())
255                 it->setViewer(command);
256 }
257
258
259 bool Formats::view(Buffer const & buffer, FileName const & filename,
260                    string const & format_name) const
261 {
262         if (filename.empty() || !filename.exists()) {
263                 Alert::error(_("Cannot view file"),
264                         bformat(_("File does not exist: %1$s"),
265                                 from_utf8(filename.absFilename())));
266                 return false;
267         }
268
269         Format const * format = getFormat(format_name);
270         if (format && format->viewer().empty() &&
271             format->isChildFormat())
272                 format = getFormat(format->parentFormat());
273         if (!format || format->viewer().empty()) {
274 // FIXME: I believe this is the wrong place to show alerts, it should be done
275 // by the caller (this should be "utility" code)
276                 Alert::error(_("Cannot view file"),
277                         bformat(_("No information for viewing %1$s"),
278                                 prettyName(format_name)));
279                 return false;
280         }
281         // viewer is 'auto'
282         if (format->viewer() == "auto") {
283                 if (os::autoOpenFile(filename.absFilename(), os::VIEW))
284                         return true;
285                 else {
286                         Alert::error(_("Cannot view file"),
287                                 bformat(_("Auto-view file %1$s failed"),
288                                         from_utf8(filename.absFilename())));
289                         return false;
290                 }
291         }
292
293         string command = libScriptSearch(format->viewer());
294
295         if (format_name == "dvi" &&
296             !lyxrc.view_dvi_paper_option.empty()) {
297                 command += ' ' + lyxrc.view_dvi_paper_option;
298                 string paper_size = buffer.params().paperSizeName();
299                 if (paper_size == "letter")
300                         paper_size = "us";
301                 command += ' ' + paper_size;
302                 if (buffer.params().orientation == ORIENTATION_LANDSCAPE)
303                         command += 'r';
304         }
305
306         if (!contains(command, token_from_format))
307                 command += ' ' + token_from_format;
308
309         command = subst(command, token_from_format, quoteName(filename.toFilesystemEncoding()));
310         command = subst(command, token_path_format, quoteName(onlyPath(filename.toFilesystemEncoding())));
311         command = subst(command, token_socket_format, quoteName(theServerSocket().address()));
312         LYXERR(Debug::FILES, "Executing command: " << command);
313         // FIXME UNICODE utf8 can be wrong for files
314         buffer.message(_("Executing command: ") + from_utf8(command));
315
316         Systemcall one;
317         int const res = one.startscript(Systemcall::DontWait, command);
318
319         if (res) {
320                 Alert::error(_("Cannot view file"),
321                              bformat(_("An error occurred whilst running %1$s"),
322                                makeDisplayPath(command, 50)));
323                 return false;
324         }
325         return true;
326 }
327
328
329 bool Formats::edit(Buffer const & buffer, FileName const & filename,
330                          string const & format_name) const
331 {
332         if (filename.empty() || !filename.exists()) {
333                 Alert::error(_("Cannot edit file"),
334                         bformat(_("File does not exist: %1$s"),
335                                 from_utf8(filename.absFilename())));
336                 return false;
337         }
338
339         Format const * format = getFormat(format_name);
340         if (format && format->editor().empty() &&
341             format->isChildFormat())
342                 format = getFormat(format->parentFormat());
343         if (!format || format->editor().empty()) {
344 // FIXME: I believe this is the wrong place to show alerts, it should
345 // be done by the caller (this should be "utility" code)
346                 Alert::error(_("Cannot edit file"),
347                         bformat(_("No information for editing %1$s"),
348                                 prettyName(format_name)));
349                 return false;
350         }
351         // editor is 'auto'
352         if (format->editor() == "auto") {
353                 if (os::autoOpenFile(filename.absFilename(), os::EDIT))
354                         return true;
355                 else {
356                         Alert::error(_("Cannot edit file"),
357                                 bformat(_("Auto-edit file %1$s failed"),
358                                         from_utf8(filename.absFilename())));
359                         return false;
360                 }
361         }
362
363         string command = format->editor();
364
365         if (!contains(command, token_from_format))
366                 command += ' ' + token_from_format;
367
368         command = subst(command, token_from_format, quoteName(filename.toFilesystemEncoding()));
369         command = subst(command, token_path_format, quoteName(onlyPath(filename.toFilesystemEncoding())));
370         command = subst(command, token_socket_format, quoteName(theServerSocket().address()));
371         LYXERR(Debug::FILES, "Executing command: " << command);
372         // FIXME UNICODE utf8 can be wrong for files
373         buffer.message(_("Executing command: ") + from_utf8(command));
374
375         Systemcall one;
376         int const res = one.startscript(Systemcall::DontWait, command);
377
378         if (res) {
379                 Alert::error(_("Cannot edit file"),
380                              bformat(_("An error occurred whilst running %1$s"),
381                                makeDisplayPath(command, 50)));
382                 return false;
383         }
384         return true;
385 }
386
387
388 docstring const Formats::prettyName(string const & name) const
389 {
390         Format const * format = getFormat(name);
391         if (format)
392                 return from_utf8(format->prettyname());
393         else
394                 return from_utf8(name);
395 }
396
397
398 string const Formats::extension(string const & name) const
399 {
400         Format const * format = getFormat(name);
401         if (format)
402                 return format->extension();
403         else
404                 return name;
405 }
406
407
408
409
410 Formats formats;
411
412 Formats system_formats;
413
414
415 } // namespace lyx