3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
8 * Full author contact details are available in file CREDITS.
15 #include "BufferParams.h"
17 #include "ServerSocket.h"
19 #include "frontends/alert.h" //to be removed?
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/Systemcall.h"
28 // FIXME: Q_WS_MACX is not available, it's in Qt
29 #ifdef USE_MACOSX_PACKAGING
30 #include "support/linkback/LinkBackProxy.h"
34 using namespace lyx::support
;
38 namespace Alert
= frontend::Alert
;
39 namespace os
= support::os
;
43 string
const token_from_format("$$i");
44 string
const token_path_format("$$p");
45 string
const token_socket_format("$$a");
48 class FormatNamesEqual
: public unary_function
<Format
, bool> {
50 FormatNamesEqual(string
const & name
)
52 bool operator()(Format
const & f
) const
54 return f
.name() == name_
;
61 class FormatExtensionsEqual
: public unary_function
<Format
, bool> {
63 FormatExtensionsEqual(string
const & extension
)
64 : extension_(extension
) {}
65 bool operator()(Format
const & f
) const
67 return f
.extension() == extension_
;
75 bool operator<(Format
const & a
, Format
const & b
)
77 // use the compare_ascii_no_case instead of compare_no_case,
78 // because in turkish, 'i' is not the lowercase version of 'I',
79 // and thus turkish locale breaks parsing of tags.
81 return compare_ascii_no_case(a
.prettyname(), b
.prettyname()) < 0;
85 Format::Format(string
const & n
, string
const & e
, string
const & p
,
86 string
const & s
, string
const & v
, string
const & ed
,
88 : name_(n
), extension_(e
), prettyname_(p
), shortcut_(s
), viewer_(v
),
89 editor_(ed
), flags_(flags
)
93 bool Format::dummy() const
95 return extension().empty();
99 bool Format::isChildFormat() const
103 return isdigit(name_
[name_
.length() - 1]);
107 string
const Format::parentFormat() const
109 return name_
.substr(0, name_
.length() - 1);
113 // This method should return a reference, and throw an exception
114 // if the format named name cannot be found (Lgb)
115 Format
const * Formats::getFormat(string
const & name
) const
117 FormatList::const_iterator cit
=
118 find_if(formatlist
.begin(), formatlist
.end(),
119 FormatNamesEqual(name
));
120 if (cit
!= formatlist
.end())
127 string
Formats::getFormatFromFile(FileName
const & filename
) const
129 if (filename
.empty())
132 string
const format
= filename
.guessFormatFromContents();
136 // try to find a format from the file extension.
137 string
const ext
= getExtension(filename
.absFilename());
139 // this is ambigous if two formats have the same extension,
140 // but better than nothing
141 Formats::const_iterator cit
=
142 find_if(formatlist
.begin(), formatlist
.end(),
143 FormatExtensionsEqual(ext
));
144 if (cit
!= formats
.end()) {
145 LYXERR(Debug::GRAPHICS
, "\twill guess format from file extension: "
146 << ext
<< " -> " << cit
->name());
154 static string
fixCommand(string
const & cmd
, string
const & ext
,
155 os::auto_open_mode mode
)
157 // configure.py says we do not want a viewer/editor
161 // Does the OS manage this format?
162 if (os::canAutoOpenFile(ext
, mode
))
165 // if configure.py found nothing, clear the command
166 if (token(cmd
, ' ', 0) == "auto")
169 // use the command found by configure.py
174 void Formats::setAutoOpen()
176 FormatList::iterator fit
= formatlist
.begin();
177 FormatList::iterator
const fend
= formatlist
.end();
178 for ( ; fit
!= fend
; ++fit
) {
179 fit
->setViewer(fixCommand(fit
->viewer(), fit
->extension(), os::VIEW
));
180 fit
->setEditor(fixCommand(fit
->editor(), fit
->extension(), os::EDIT
));
185 int Formats::getNumber(string
const & name
) const
187 FormatList::const_iterator cit
=
188 find_if(formatlist
.begin(), formatlist
.end(),
189 FormatNamesEqual(name
));
190 if (cit
!= formatlist
.end())
191 return distance(formatlist
.begin(), cit
);
197 void Formats::add(string
const & name
)
199 if (!getFormat(name
))
200 add(name
, name
, name
, string(), string(), string(),
205 void Formats::add(string
const & name
, string
const & extension
,
206 string
const & prettyname
, string
const & shortcut
,
207 string
const & viewer
, string
const & editor
,
210 FormatList::iterator it
=
211 find_if(formatlist
.begin(), formatlist
.end(),
212 FormatNamesEqual(name
));
213 if (it
== formatlist
.end())
214 formatlist
.push_back(Format(name
, extension
, prettyname
,
215 shortcut
, viewer
, editor
, flags
));
217 *it
= Format(name
, extension
, prettyname
, shortcut
, viewer
,
222 void Formats::erase(string
const & name
)
224 FormatList::iterator it
=
225 find_if(formatlist
.begin(), formatlist
.end(),
226 FormatNamesEqual(name
));
227 if (it
!= formatlist
.end())
228 formatlist
.erase(it
);
234 std::sort(formatlist
.begin(), formatlist
.end());
238 void Formats::setViewer(string
const & name
, string
const & command
)
241 FormatList::iterator it
=
242 find_if(formatlist
.begin(), formatlist
.end(),
243 FormatNamesEqual(name
));
244 if (it
!= formatlist
.end())
245 it
->setViewer(command
);
249 bool Formats::view(Buffer
const & buffer
, FileName
const & filename
,
250 string
const & format_name
) const
252 if (filename
.empty() || !filename
.exists()) {
253 Alert::error(_("Cannot view file"),
254 bformat(_("File does not exist: %1$s"),
255 from_utf8(filename
.absFilename())));
259 Format
const * format
= getFormat(format_name
);
260 if (format
&& format
->viewer().empty() &&
261 format
->isChildFormat())
262 format
= getFormat(format
->parentFormat());
263 if (!format
|| format
->viewer().empty()) {
264 // FIXME: I believe this is the wrong place to show alerts, it should be done
265 // by the caller (this should be "utility" code)
266 Alert::error(_("Cannot view file"),
267 bformat(_("No information for viewing %1$s"),
268 prettyName(format_name
)));
272 if (format
->viewer() == "auto") {
273 if (os::autoOpenFile(filename
.absFilename(), os::VIEW
))
276 Alert::error(_("Cannot view file"),
277 bformat(_("Auto-view file %1$s failed"),
278 from_utf8(filename
.absFilename())));
283 string command
= libScriptSearch(format
->viewer());
285 if (format_name
== "dvi" &&
286 !lyxrc
.view_dvi_paper_option
.empty()) {
287 string paper_size
= buffer
.params().paperSizeName(BufferParams::XDVI
);
288 if (!paper_size
.empty()) {
289 command
+= ' ' + lyxrc
.view_dvi_paper_option
;
290 command
+= ' ' + paper_size
;
291 if (buffer
.params().orientation
== ORIENTATION_LANDSCAPE
&&
292 buffer
.params().papersize
!= PAPER_CUSTOM
)
297 if (!contains(command
, token_from_format
))
298 command
+= ' ' + token_from_format
;
300 command
= subst(command
, token_from_format
, quoteName(filename
.toFilesystemEncoding()));
301 command
= subst(command
, token_path_format
, quoteName(onlyPath(filename
.toFilesystemEncoding())));
302 command
= subst(command
, token_socket_format
, quoteName(theServerSocket().address()));
303 LYXERR(Debug::FILES
, "Executing command: " << command
);
304 // FIXME UNICODE utf8 can be wrong for files
305 buffer
.message(_("Executing command: ") + from_utf8(command
));
308 int const res
= one
.startscript(Systemcall::DontWait
, command
);
311 Alert::error(_("Cannot view file"),
312 bformat(_("An error occurred whilst running %1$s"),
313 makeDisplayPath(command
, 50)));
320 bool Formats::edit(Buffer
const & buffer
, FileName
const & filename
,
321 string
const & format_name
) const
323 if (filename
.empty() || !filename
.exists()) {
324 Alert::error(_("Cannot edit file"),
325 bformat(_("File does not exist: %1$s"),
326 from_utf8(filename
.absFilename())));
330 // LinkBack files look like PDF, but have the .linkback extension
331 string
const ext
= getExtension(filename
.absFilename());
332 if (format_name
== "pdf" && ext
== "linkback") {
333 #ifdef USE_MACOSX_PACKAGING
334 return editLinkBackFile(filename
.absFilename().c_str());
336 Alert::error(_("Cannot edit file"),
337 _("LinkBack files can only be edited on Apple Mac OSX."));
339 #endif // USE_MACOSX_PACKAGING
342 Format
const * format
= getFormat(format_name
);
343 if (format
&& format
->editor().empty() &&
344 format
->isChildFormat())
345 format
= getFormat(format
->parentFormat());
346 if (!format
|| format
->editor().empty()) {
347 // FIXME: I believe this is the wrong place to show alerts, it should
348 // be done by the caller (this should be "utility" code)
349 Alert::error(_("Cannot edit file"),
350 bformat(_("No information for editing %1$s"),
351 prettyName(format_name
)));
356 if (format
->editor() == "auto") {
357 if (os::autoOpenFile(filename
.absFilename(), os::EDIT
))
360 Alert::error(_("Cannot edit file"),
361 bformat(_("Auto-edit file %1$s failed"),
362 from_utf8(filename
.absFilename())));
367 string command
= format
->editor();
369 if (!contains(command
, token_from_format
))
370 command
+= ' ' + token_from_format
;
372 command
= subst(command
, token_from_format
, quoteName(filename
.toFilesystemEncoding()));
373 command
= subst(command
, token_path_format
, quoteName(onlyPath(filename
.toFilesystemEncoding())));
374 command
= subst(command
, token_socket_format
, quoteName(theServerSocket().address()));
375 LYXERR(Debug::FILES
, "Executing command: " << command
);
376 // FIXME UNICODE utf8 can be wrong for files
377 buffer
.message(_("Executing command: ") + from_utf8(command
));
380 int const res
= one
.startscript(Systemcall::DontWait
, command
);
383 Alert::error(_("Cannot edit file"),
384 bformat(_("An error occurred whilst running %1$s"),
385 makeDisplayPath(command
, 50)));
392 docstring
const Formats::prettyName(string
const & name
) const
394 Format
const * format
= getFormat(name
);
396 return from_utf8(format
->prettyname());
398 return from_utf8(name
);
402 string
const Formats::extension(string
const & name
) const
404 Format
const * format
= getFormat(name
);
406 return format
->extension();
416 Formats system_formats
;