]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
Next step of true unicode filenames: Use support::FileName instead of
[lyx.git] / src / lyxfunc.C
1 /**
2  * \file lyxfunc.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "lyxfunc.h"
23
24 #include "BranchList.h"
25 #include "buffer.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
30 #include "cursor.h"
31 #include "CutAndPaste.h"
32 #include "debug.h"
33 #include "dispatchresult.h"
34 #include "encoding.h"
35 #include "errorlist.h"
36 #include "exporter.h"
37 #include "format.h"
38 #include "funcrequest.h"
39 #include "FuncStatus.h"
40 #include "gettext.h"
41 #include "importer.h"
42 #include "insetiterator.h"
43 #include "intl.h"
44 #include "kbmap.h"
45 #include "language.h"
46 #include "LColor.h"
47 #include "session.h"
48 #include "lyx_main.h"
49 #include "lyx_cb.h"
50 #include "LyXAction.h"
51 #include "lyxfind.h"
52 #include "lyxlex.h"
53 #include "lyxrc.h"
54 #include "lyxrow.h"
55 #include "lyxserver.h"
56 #include "lyxtextclasslist.h"
57 #include "lyxvc.h"
58 #include "paragraph.h"
59 #include "pariterator.h"
60 #include "ParagraphParameters.h"
61 #include "undo.h"
62
63 #include "insets/insetbox.h"
64 #include "insets/insetbranch.h"
65 #include "insets/insetcommand.h"
66 #include "insets/insetert.h"
67 #include "insets/insetexternal.h"
68 #include "insets/insetfloat.h"
69 #include "insets/insetgraphics.h"
70 #include "insets/insetinclude.h"
71 #include "insets/insetnote.h"
72 #include "insets/insettabular.h"
73 #include "insets/insetvspace.h"
74 #include "insets/insetwrap.h"
75
76 #include "frontends/Application.h"
77 #include "frontends/Alert.h"
78 #include "frontends/Dialogs.h"
79 #include "frontends/FileDialog.h"
80 #include "frontends/FontLoader.h"
81 #include "frontends/Gui.h"
82 #include "frontends/LyXKeySym.h"
83 #include "frontends/LyXView.h"
84 #include "frontends/Menubar.h"
85 #include "frontends/Toolbars.h"
86
87 #include "support/environment.h"
88 #include "support/filefilterlist.h"
89 #include "support/filetools.h"
90 #include "support/forkedcontr.h"
91 #include "support/fs_extras.h"
92 #include "support/lstrings.h"
93 #include "support/path.h"
94 #include "support/package.h"
95 #include "support/systemcall.h"
96 #include "support/convert.h"
97 #include "support/os.h"
98
99 #include <boost/current_function.hpp>
100 #include <boost/filesystem/operations.hpp>
101
102 #include <sstream>
103
104
105 namespace lyx {
106
107 using bv_funcs::freefont2string;
108
109 using support::absolutePath;
110 using support::addName;
111 using support::addPath;
112 using support::bformat;
113 using support::changeExtension;
114 using support::contains;
115 using support::FileFilterList;
116 using support::FileName;
117 using support::fileSearch;
118 using support::ForkedcallsController;
119 using support::i18nLibFileSearch;
120 using support::isDirWriteable;
121 using support::isFileReadable;
122 using support::isStrInt;
123 using support::makeAbsPath;
124 using support::makeDisplayPath;
125 using support::package;
126 using support::quoteName;
127 using support::rtrim;
128 using support::split;
129 using support::subst;
130 using support::Systemcall;
131 using support::token;
132 using support::trim;
133 using support::prefixIs;
134
135 using std::endl;
136 using std::make_pair;
137 using std::pair;
138 using std::string;
139 using std::istringstream;
140 using std::ostringstream;
141
142 namespace Alert = frontend::Alert;
143 namespace fs = boost::filesystem;
144
145
146 // (alkis)
147 extern tex_accent_struct get_accent(kb_action action);
148
149
150 namespace {
151
152 bool getLocalStatus(LCursor cursor,
153                FuncRequest const & cmd, FuncStatus & status)
154 {
155         // Try to fix cursor in case it is broken.
156         cursor.fixIfBroken();
157
158         // This is, of course, a mess. Better create a new doc iterator and use
159         // this in Inset::getStatus. This might require an additional
160         // BufferView * arg, though (which should be avoided)
161         //LCursor safe = *this;
162         bool res = false;
163         for ( ; cursor.depth(); cursor.pop()) {
164                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
165                 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
166                 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
167                 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
168
169                 // The inset's getStatus() will return 'true' if it made
170                 // a definitive decision on whether it want to handle the
171                 // request or not. The result of this decision is put into
172                 // the 'status' parameter.
173                 if (cursor.inset().getStatus(cursor, cmd, status)) {
174                         res = true;
175                         break;
176                 }
177         }
178         return res;
179 }
180
181
182 /** Return the change status at cursor position, taking in account the
183  * status at each level of the document iterator (a table in a deleted
184  * footnote is deleted).
185  * When \param outer is true, the top slice is not looked at.
186  */
187 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
188 {
189         size_t const depth = dit.depth() - (outer ? 1 : 0);
190
191         for (size_t i = 0 ; i < depth ; ++i) {
192                 CursorSlice const & slice = dit[i];
193                 if (!slice.inset().inMathed()
194                     && slice.pos() < slice.paragraph().size()) {
195                         Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
196                         if (ch != Change::UNCHANGED)
197                                 return ch;
198                 }
199         }
200         return Change::UNCHANGED;
201 }
202
203 }
204
205 LyXFunc::LyXFunc()
206         : lyx_view_(0),
207         encoded_last_key(0),
208         meta_fake_bit(key_modifier::none)
209 {
210 }
211
212
213 void LyXFunc::initKeySequences(kb_keymap * kb)
214 {
215         keyseq.reset(new kb_sequence(kb, kb));
216         cancel_meta_seq.reset(new kb_sequence(kb, kb));
217 }
218
219
220 void LyXFunc::setLyXView(LyXView * lv)
221 {
222         lyx_view_ = lv;
223 }
224
225
226 void LyXFunc::handleKeyFunc(kb_action action)
227 {
228         char_type c = encoded_last_key;
229
230         if (keyseq->length())
231                 c = 0;
232
233         lyx_view_->view()->getIntl().getTransManager().deadkey(
234                 c, get_accent(action).accent, view()->getLyXText(), view()->cursor());
235         // Need to clear, in case the minibuffer calls these
236         // actions
237         keyseq->clear();
238         // copied verbatim from do_accent_char
239         view()->cursor().resetAnchor();
240         view()->update();
241 }
242
243
244 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
245 {
246         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
247
248         // Do nothing if we have nothing (JMarc)
249         if (!keysym->isOK()) {
250                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
251                                    << endl;
252                 return;
253         }
254
255         if (keysym->isModifier()) {
256                 lyxerr[Debug::KEY] << "isModifier true" << endl;
257                 return;
258         }
259
260         //Encoding const * encoding = view()->cursor().getEncoding();
261         //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
262         size_t encoded_last_key = keysym->getUCSEncoded();
263
264         // Do a one-deep top-level lookup for
265         // cancel and meta-fake keys. RVDK_PATCH_5
266         cancel_meta_seq->reset();
267
268         FuncRequest func = cancel_meta_seq->addkey(keysym, state);
269         lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
270                            << " action first set to [" << func.action << ']'
271                            << endl;
272
273         // When not cancel or meta-fake, do the normal lookup.
274         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
275         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
276         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
277                 // remove Caps Lock and Mod2 as a modifiers
278                 func = keyseq->addkey(keysym, (state | meta_fake_bit));
279                 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
280                                    << "action now set to ["
281                                    << func.action << ']' << endl;
282         }
283
284         // Dont remove this unless you know what you are doing.
285         meta_fake_bit = key_modifier::none;
286
287         // Can this happen now ?
288         if (func.action == LFUN_NOACTION) {
289                 func = FuncRequest(LFUN_COMMAND_PREFIX);
290         }
291
292         if (lyxerr.debugging(Debug::KEY)) {
293                 lyxerr << BOOST_CURRENT_FUNCTION
294                        << " Key [action="
295                        << func.action << "]["
296                        << keyseq->print() << ']'
297                        << endl;
298         }
299
300         // already here we know if it any point in going further
301         // why not return already here if action == -1 and
302         // num_bytes == 0? (Lgb)
303
304         if (keyseq->length() > 1) {
305                 lyx_view_->message(from_utf8(keyseq->print()));
306         }
307
308
309         // Maybe user can only reach the key via holding down shift.
310         // Let's see. But only if shift is the only modifier
311         if (func.action == LFUN_UNKNOWN_ACTION &&
312             state == key_modifier::shift) {
313                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
314                 func = keyseq->addkey(keysym, key_modifier::none);
315                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
316         }
317
318         if (func.action == LFUN_UNKNOWN_ACTION) {
319                 // Hmm, we didn't match any of the keysequences. See
320                 // if it's normal insertable text not already covered
321                 // by a binding
322                 if (keysym->isText() && keyseq->length() == 1) {
323                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
324                         func = FuncRequest(LFUN_SELF_INSERT,
325                                            FuncRequest::KEYBOARD);
326                 } else {
327                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
328                         lyx_view_->message(_("Unknown function."));
329                         return;
330                 }
331         }
332
333         if (func.action == LFUN_SELF_INSERT) {
334                 if (encoded_last_key != 0) {
335                         docstring const arg(1, encoded_last_key);
336                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
337                                              FuncRequest::KEYBOARD));
338                         lyxerr[Debug::KEY]
339                                 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
340                 }
341         } else {
342                 dispatch(func);
343         }
344 }
345
346
347 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
348 {
349         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
350         FuncStatus flag;
351
352         if (cmd.action == LFUN_LYX_QUIT) {
353                 flag.message(from_utf8(N_("Exiting")));
354                 flag.enabled(true);
355                 return flag;
356         } else if (cmd.action == LFUN_BOOKMARK_GOTO) {
357                 // bookmarks can be valid even if there is no opened buffer
358                 flag.enabled(LyX::ref().session().bookmarks().isValid(convert<unsigned int>(to_utf8(cmd.argument()))));
359                 return flag;
360         } else if (cmd.action == LFUN_BOOKMARK_CLEAR) {
361                 flag.enabled(LyX::ref().session().bookmarks().size() > 0);
362                 return flag;
363         } else if (cmd.action == LFUN_TOOLBAR_TOGGLE_STATE) {
364                 ToolbarBackend::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
365                 if (!(flags & ToolbarBackend::AUTO))
366                         flag.setOnOff(flags & ToolbarBackend::ON);
367                 return flag;
368         }
369
370         LCursor & cur = view()->cursor();
371
372         /* In LyX/Mac, when a dialog is open, the menus of the
373            application can still be accessed without giving focus to
374            the main window. In this case, we want to disable the menu
375            entries that are buffer-related.
376
377            Note that this code is not perfect, as bug 1941 attests:
378            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
379         */
380         Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
381         if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
382                 buf = 0;
383
384         if (cmd.action == LFUN_NOACTION) {
385                 flag.message(from_utf8(N_("Nothing to do")));
386                 flag.enabled(false);
387                 return flag;
388         }
389
390         switch (cmd.action) {
391         case LFUN_UNKNOWN_ACTION:
392 #ifndef HAVE_LIBAIKSAURUS
393         case LFUN_THESAURUS_ENTRY:
394 #endif
395                 flag.unknown(true);
396                 flag.enabled(false);
397                 break;
398
399         default:
400                 break;
401         }
402
403         if (flag.unknown()) {
404                 flag.message(from_utf8(N_("Unknown action")));
405                 return flag;
406         }
407
408         if (!flag.enabled()) {
409                 if (flag.message().empty())
410                         flag.message(from_utf8(N_("Command disabled")));
411                 return flag;
412         }
413
414         // Check whether we need a buffer
415         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
416                 // no, exit directly
417                 flag.message(from_utf8(N_("Command not allowed with"
418                                     "out any document open")));
419                 flag.enabled(false);
420                 return flag;
421         }
422
423         // I would really like to avoid having this switch and rather try to
424         // encode this in the function itself.
425         // -- And I'd rather let an inset decide which LFUNs it is willing
426         // to handle (Andre')
427         bool enable = true;
428         switch (cmd.action) {
429         case LFUN_BUFFER_TOGGLE_READ_ONLY:
430                 flag.setOnOff(buf->isReadonly());
431                 break;
432
433         case LFUN_BUFFER_SWITCH:
434                 // toggle on the current buffer, but do not toggle off
435                 // the other ones (is that a good idea?)
436                 if (to_utf8(cmd.argument()) == buf->fileName())
437                         flag.setOnOff(true);
438                 break;
439
440         case LFUN_BUFFER_EXPORT:
441                 enable = cmd.argument() == "custom"
442                         || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
443                 break;
444
445         case LFUN_BUFFER_CHKTEX:
446                 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
447                 break;
448
449         case LFUN_BUILD_PROGRAM:
450                 enable = Exporter::isExportable(*buf, "program");
451                 break;
452
453         case LFUN_LAYOUT_TABULAR:
454                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
455                 break;
456
457         case LFUN_LAYOUT:
458         case LFUN_LAYOUT_PARAGRAPH:
459                 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
460                 break;
461
462         case LFUN_VC_REGISTER:
463                 enable = !buf->lyxvc().inUse();
464                 break;
465         case LFUN_VC_CHECK_IN:
466                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
467                 break;
468         case LFUN_VC_CHECK_OUT:
469                 enable = buf->lyxvc().inUse() && buf->isReadonly();
470                 break;
471         case LFUN_VC_REVERT:
472         case LFUN_VC_UNDO_LAST:
473                 enable = buf->lyxvc().inUse();
474                 break;
475         case LFUN_BUFFER_RELOAD:
476                 enable = !buf->isUnnamed() && !buf->isClean();
477                 break;
478
479         case LFUN_INSET_SETTINGS: {
480                 enable = false;
481                 if (!cur)
482                         break;
483                 InsetBase::Code code = cur.inset().lyxCode();
484                 switch (code) {
485                         case InsetBase::TABULAR_CODE:
486                                 enable = cmd.argument() == "tabular";
487                                 break;
488                         case InsetBase::ERT_CODE:
489                                 enable = cmd.argument() == "ert";
490                                 break;
491                         case InsetBase::FLOAT_CODE:
492                                 enable = cmd.argument() == "float";
493                                 break;
494                         case InsetBase::WRAP_CODE:
495                                 enable = cmd.argument() == "wrap";
496                                 break;
497                         case InsetBase::NOTE_CODE:
498                                 enable = cmd.argument() == "note";
499                                 break;
500                         case InsetBase::BRANCH_CODE:
501                                 enable = cmd.argument() == "branch";
502                                 break;
503                         case InsetBase::BOX_CODE:
504                                 enable = cmd.argument() == "box";
505                                 break;
506                         default:
507                                 break;
508                 }
509                 break;
510         }
511
512         case LFUN_INSET_APPLY: {
513                 string const name = cmd.getArg(0);
514                 InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
515                 if (inset) {
516                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
517                         FuncStatus fs;
518                         bool const success = inset->getStatus(cur, fr, fs);
519                         // Every inset is supposed to handle this
520                         BOOST_ASSERT(success);
521                         flag |= fs;
522                 } else {
523                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
524                         flag |= getStatus(fr);
525                 }
526                 enable = flag.enabled();
527                 break;
528         }
529
530         case LFUN_DIALOG_SHOW: {
531                 string const name = cmd.getArg(0);
532                 if (!buf)
533                         enable = name == "aboutlyx"
534                                 || name == "file"
535                                 || name == "forks"
536                                 || name == "prefs"
537                                 || name == "texinfo";
538                 else if (name == "print")
539                         enable = Exporter::isExportable(*buf, "dvi")
540                                 && lyxrc.print_command != "none";
541                 else if (name == "character" || name == "mathpanel")
542                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
543                 else if (name == "latexlog")
544                         enable = isFileReadable(FileName(buf->getLogName().second));
545 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
546                 else if (name == "spellchecker")
547                         enable = false;
548 #endif
549                 else if (name == "vclog")
550                         enable = buf->lyxvc().inUse();
551                 else if (name == "view-source")
552                         enable = buf;
553                 break;
554         }
555
556         case LFUN_DIALOG_SHOW_NEW_INSET:
557                 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
558                 break;
559
560         case LFUN_DIALOG_UPDATE: {
561                 string const name = cmd.getArg(0);
562                 if (!buf)
563                         enable = name == "prefs";
564                 break;
565         }
566
567         case LFUN_CITATION_INSERT: {
568                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
569                 enable = getStatus(fr).enabled();
570                 break;
571         }
572
573         case LFUN_BUFFER_WRITE: {
574                 enable = view()->buffer()->isUnnamed()
575                         || !view()->buffer()->isClean();
576                 break;
577         }
578
579
580         // this one is difficult to get right. As a half-baked
581         // solution, we consider only the first action of the sequence
582         case LFUN_COMMAND_SEQUENCE: {
583                 // argument contains ';'-terminated commands
584                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
585                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
586                 func.origin = cmd.origin;
587                 flag = getStatus(func);
588         }
589
590         case LFUN_BUFFER_NEW:
591         case LFUN_BUFFER_NEW_TEMPLATE:
592         case LFUN_WORD_FIND_FORWARD:
593         case LFUN_WORD_FIND_BACKWARD:
594         case LFUN_COMMAND_PREFIX:
595         case LFUN_COMMAND_EXECUTE:
596         case LFUN_CANCEL:
597         case LFUN_META_PREFIX:
598         case LFUN_BUFFER_CLOSE:
599         case LFUN_BUFFER_WRITE_AS:
600         case LFUN_BUFFER_UPDATE:
601         case LFUN_BUFFER_VIEW:
602         case LFUN_BUFFER_IMPORT:
603         case LFUN_TOC_VIEW:
604         case LFUN_BUFFER_AUTO_SAVE:
605         case LFUN_RECONFIGURE:
606         case LFUN_HELP_OPEN:
607         case LFUN_FILE_NEW:
608         case LFUN_FILE_OPEN:
609         case LFUN_DROP_LAYOUTS_CHOICE:
610         case LFUN_MENU_OPEN:
611         case LFUN_SERVER_GET_NAME:
612         case LFUN_SERVER_NOTIFY:
613         case LFUN_SERVER_GOTO_FILE_ROW:
614         case LFUN_DIALOG_HIDE:
615         case LFUN_DIALOG_DISCONNECT_INSET:
616         case LFUN_BUFFER_CHILD_OPEN:
617         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
618         case LFUN_KEYMAP_OFF:
619         case LFUN_KEYMAP_PRIMARY:
620         case LFUN_KEYMAP_SECONDARY:
621         case LFUN_KEYMAP_TOGGLE:
622         case LFUN_REPEAT:
623         case LFUN_BUFFER_EXPORT_CUSTOM:
624         case LFUN_BUFFER_PRINT:
625         case LFUN_PREFERENCES_SAVE:
626         case LFUN_SCREEN_FONT_UPDATE:
627         case LFUN_SET_COLOR:
628         case LFUN_MESSAGE:
629         case LFUN_EXTERNAL_EDIT:
630         case LFUN_GRAPHICS_EDIT:
631         case LFUN_ALL_INSETS_TOGGLE:
632         case LFUN_BUFFER_LANGUAGE:
633         case LFUN_TEXTCLASS_APPLY:
634         case LFUN_TEXTCLASS_LOAD:
635         case LFUN_BUFFER_SAVE_AS_DEFAULT:
636         case LFUN_BUFFER_PARAMS_APPLY:
637         case LFUN_LYXRC_APPLY:
638         case LFUN_BUFFER_NEXT:
639         case LFUN_BUFFER_PREVIOUS:
640         case LFUN_WINDOW_NEW:
641         case LFUN_WINDOW_CLOSE:
642                 // these are handled in our dispatch()
643                 break;
644
645         default:
646                 if (!getLocalStatus(cur, cmd, flag))
647                         flag = view()->getStatus(cmd);
648         }
649
650         if (!enable)
651                 flag.enabled(false);
652
653         // Can we use a readonly buffer?
654         if (buf && buf->isReadonly()
655             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
656             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
657                 flag.message(from_utf8(N_("Document is read-only")));
658                 flag.enabled(false);
659         }
660
661         // Are we in a DELETED change-tracking region?
662         if (buf && lookupChangeType(cur, true) == Change::DELETED
663             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
664             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
665                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
666                 flag.enabled(false);
667         }
668
669         // the default error message if we disable the command
670         if (!flag.enabled() && flag.message().empty())
671                 flag.message(from_utf8(N_("Command disabled")));
672
673         return flag;
674 }
675
676
677 bool LyXFunc::ensureBufferClean(BufferView * bv)
678 {
679         Buffer & buf = *bv->buffer();
680         if (buf.isClean())
681                 return true;
682
683         docstring const file = makeDisplayPath(buf.fileName(), 30);
684         docstring text = bformat(_("The document %1$s has unsaved "
685                                              "changes.\n\nDo you want to save "
686                                              "the document?"), file);
687         int const ret = Alert::prompt(_("Save changed document?"),
688                                       text, 0, 1, _("&Save"),
689                                       _("&Cancel"));
690
691         if (ret == 0)
692                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
693
694         return buf.isClean();
695 }
696
697
698 namespace {
699
700 void showPrintError(string const & name)
701 {
702         docstring str = bformat(_("Could not print the document %1$s.\n"
703                                             "Check that your printer is set up correctly."),
704                              makeDisplayPath(name, 50));
705         Alert::error(_("Print document failed"), str);
706 }
707
708
709 void loadTextclass(string const & name)
710 {
711         std::pair<bool, textclass_type> const tc_pair =
712                 textclasslist.numberOfClass(name);
713
714         if (!tc_pair.first) {
715                 lyxerr << "Document class \"" << name
716                        << "\" does not exist."
717                        << std::endl;
718                 return;
719         }
720
721         textclass_type const tc = tc_pair.second;
722
723         if (!textclasslist[tc].load()) {
724                 docstring s = bformat(_("The document could not be converted\n"
725                                                   "into the document class %1$s."),
726                                    from_utf8(textclasslist[tc].name()));
727                 Alert::error(_("Could not change class"), s);
728         }
729 }
730
731
732 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
733
734 } //namespace anon
735
736
737 void LyXFunc::dispatch(FuncRequest const & cmd)
738 {
739         string const argument = to_utf8(cmd.argument());
740         kb_action const action = cmd.action;
741
742         lyxerr[Debug::ACTION] << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
743         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
744
745         // we have not done anything wrong yet.
746         errorstat = false;
747         dispatch_buffer.erase();
748
749         // redraw the screen at the end (first of the two drawing steps).
750         //This is done unless explicitely requested otherwise
751         Update::flags updateFlags = Update::FitCursor;
752
753         FuncStatus const flag = getStatus(cmd);
754         if (!flag.enabled()) {
755                 // We cannot use this function here
756                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
757                        << lyxaction.getActionName(action)
758                        << " [" << action << "] is disabled at this location"
759                        << endl;
760                 setErrorMessage(flag.message());
761         } else {
762                 switch (action) {
763
764                 case LFUN_WORD_FIND_FORWARD:
765                 case LFUN_WORD_FIND_BACKWARD: {
766                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
767                         static string last_search;
768                         string searched_string;
769
770                         if (!argument.empty()) {
771                                 last_search = argument;
772                                 searched_string = argument;
773                         } else {
774                                 searched_string = last_search;
775                         }
776
777                         if (searched_string.empty())
778                                 break;
779
780                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
781                         string const data =
782                                 find2string(searched_string, true, false, fw);
783                         find(view(), FuncRequest(LFUN_WORD_FIND, data));
784                         break;
785                 }
786
787                 case LFUN_COMMAND_PREFIX:
788                         BOOST_ASSERT(lyx_view_);
789                         lyx_view_->message(from_utf8(keyseq->printOptions()));
790                         break;
791
792                 case LFUN_COMMAND_EXECUTE:
793                         BOOST_ASSERT(lyx_view_);
794                         lyx_view_->getToolbars().display("minibuffer", true);
795                         lyx_view_->focus_command_buffer();
796                         break;
797
798                 case LFUN_CANCEL:
799                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
800                         keyseq->reset();
801                         meta_fake_bit = key_modifier::none;
802                         if (view()->buffer())
803                                 // cancel any selection
804                                 dispatch(FuncRequest(LFUN_MARK_OFF));
805                         setMessage(_("Cancel"));
806                         break;
807
808                 case LFUN_META_PREFIX:
809                         meta_fake_bit = key_modifier::alt;
810                         setMessage(from_utf8(keyseq->print()));
811                         break;
812
813                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
814                         BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
815                         if (lyx_view_->buffer()->lyxvc().inUse())
816                                 lyx_view_->buffer()->lyxvc().toggleReadOnly();
817                         else
818                                 lyx_view_->buffer()->setReadonly(
819                                         !lyx_view_->buffer()->isReadonly());
820                         break;
821
822                 // --- Menus -----------------------------------------------
823                 case LFUN_BUFFER_NEW:
824                         menuNew(argument, false);
825                         break;
826
827                 case LFUN_BUFFER_NEW_TEMPLATE:
828                         menuNew(argument, true);
829                         break;
830
831                 case LFUN_BUFFER_CLOSE:
832                         closeBuffer();
833                         view()->update();
834                         break;
835
836                 case LFUN_BUFFER_WRITE:
837                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
838                         if (!lyx_view_->buffer()->isUnnamed()) {
839                                 docstring const str = bformat(_("Saving document %1$s..."),
840                                          makeDisplayPath(lyx_view_->buffer()->fileName()));
841                                 lyx_view_->message(str);
842                                 menuWrite(lyx_view_->buffer());
843                                 lyx_view_->message(str + _(" done."));
844                         } else
845                                 writeAs(lyx_view_->buffer());
846                         updateFlags = Update::None;
847                         break;
848
849                 case LFUN_BUFFER_WRITE_AS:
850                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
851                         writeAs(lyx_view_->buffer(), argument);
852                         updateFlags = Update::None;
853                         break;
854
855                 case LFUN_BUFFER_RELOAD: {
856                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
857                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
858                         docstring text = bformat(_("Any changes will be lost. Are you sure "
859                                                              "you want to revert to the saved version of the document %1$s?"), file);
860                         int const ret = Alert::prompt(_("Revert to saved document?"),
861                                 text, 0, 1, _("&Revert"), _("&Cancel"));
862
863                         if (ret == 0)
864                                 view()->reload();
865                         break;
866                 }
867
868                 case LFUN_BUFFER_UPDATE:
869                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
870                         Exporter::Export(lyx_view_->buffer(), argument, true);
871                         break;
872
873                 case LFUN_BUFFER_VIEW:
874                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
875                         Exporter::preview(lyx_view_->buffer(), argument);
876                         break;
877
878                 case LFUN_BUILD_PROGRAM:
879                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
880                         Exporter::Export(lyx_view_->buffer(), "program", true);
881                         break;
882
883                 case LFUN_BUFFER_CHKTEX:
884                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
885                         lyx_view_->buffer()->runChktex();
886                         break;
887
888                 case LFUN_BUFFER_EXPORT:
889                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
890                         if (argument == "custom")
891                                 lyx_view_->getDialogs().show("sendto");
892                         else {
893                                 Exporter::Export(lyx_view_->buffer(), argument, false);
894                         }
895                         break;
896
897                 case LFUN_BUFFER_EXPORT_CUSTOM: {
898                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
899                         string format_name;
900                         string command = split(argument, format_name, ' ');
901                         Format const * format = formats.getFormat(format_name);
902                         if (!format) {
903                                 lyxerr << "Format \"" << format_name
904                                        << "\" not recognized!"
905                                        << std::endl;
906                                 break;
907                         }
908
909                         Buffer * buffer = lyx_view_->buffer();
910
911                         // The name of the file created by the conversion process
912                         string filename;
913
914                         // Output to filename
915                         if (format->name() == "lyx") {
916                                 string const latexname =
917                                         buffer->getLatexName(false);
918                                 filename = changeExtension(latexname,
919                                                            format->extension());
920                                 filename = addName(buffer->temppath(), filename);
921
922                                 if (!buffer->writeFile(filename))
923                                         break;
924
925                         } else {
926                                 Exporter::Export(buffer, format_name, true, filename);
927                         }
928
929                         // Substitute $$FName for filename
930                         if (!contains(command, "$$FName"))
931                                 command = "( " + command + " ) < $$FName";
932                         command = subst(command, "$$FName", filename);
933
934                         // Execute the command in the background
935                         Systemcall call;
936                         call.startscript(Systemcall::DontWait, command);
937                         break;
938                 }
939
940                 case LFUN_BUFFER_PRINT: {
941                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
942                         string target;
943                         string target_name;
944                         string command = split(split(argument, target, ' '),
945                                                target_name, ' ');
946
947                         if (target.empty()
948                             || target_name.empty()
949                             || command.empty()) {
950                                 lyxerr << "Unable to parse \""
951                                        << argument << '"' << std::endl;
952                                 break;
953                         }
954                         if (target != "printer" && target != "file") {
955                                 lyxerr << "Unrecognized target \""
956                                        << target << '"' << std::endl;
957                                 break;
958                         }
959
960                         Buffer * buffer = lyx_view_->buffer();
961
962                         if (!Exporter::Export(buffer, "dvi", true)) {
963                                 showPrintError(buffer->fileName());
964                                 break;
965                         }
966
967                         // Push directory path.
968                         string const path = buffer->temppath();
969                         support::Path p(path);
970
971                         // there are three cases here:
972                         // 1. we print to a file
973                         // 2. we print directly to a printer
974                         // 3. we print using a spool command (print to file first)
975                         Systemcall one;
976                         int res = 0;
977                         string const dviname =
978                                 changeExtension(buffer->getLatexName(true),
979                                                 "dvi");
980
981                         if (target == "printer") {
982                                 if (!lyxrc.print_spool_command.empty()) {
983                                         // case 3: print using a spool
984                                         string const psname =
985                                                 changeExtension(dviname,".ps");
986                                         command += lyxrc.print_to_file
987                                                 + quoteName(psname)
988                                                 + ' '
989                                                 + quoteName(dviname);
990
991                                         string command2 =
992                                                 lyxrc.print_spool_command +' ';
993                                         if (target_name != "default") {
994                                                 command2 += lyxrc.print_spool_printerprefix
995                                                         + target_name
996                                                         + ' ';
997                                         }
998                                         command2 += quoteName(psname);
999                                         // First run dvips.
1000                                         // If successful, then spool command
1001                                         res = one.startscript(
1002                                                 Systemcall::Wait,
1003                                                 command);
1004
1005                                         if (res == 0)
1006                                                 res = one.startscript(
1007                                                         Systemcall::DontWait,
1008                                                         command2);
1009                                 } else {
1010                                         // case 2: print directly to a printer
1011                                         res = one.startscript(
1012                                                 Systemcall::DontWait,
1013                                                 command + quoteName(dviname));
1014                                 }
1015
1016                         } else {
1017                                 // case 1: print to a file
1018                                 command += lyxrc.print_to_file
1019                                         + quoteName(makeAbsPath(target_name,
1020                                                                 path))
1021                                         + ' '
1022                                         + quoteName(dviname);
1023                                 res = one.startscript(Systemcall::DontWait,
1024                                                       command);
1025                         }
1026
1027                         if (res != 0)
1028                                 showPrintError(buffer->fileName());
1029                         break;
1030                 }
1031
1032                 case LFUN_BUFFER_IMPORT:
1033                         doImport(argument);
1034                         break;
1035
1036                 case LFUN_LYX_QUIT:
1037                         if (argument == "closeOnly") {
1038                                 if (!theApp->gui().closeAll())
1039                                         break;
1040                                 lyx_view_ = 0;
1041                         }
1042
1043                         // FIXME: this code needs to be transfered somewhere else
1044                         // as lyx_view_ will most certainly be null and a same buffer
1045                         // might be visible in more than one LyXView.
1046                         if (lyx_view_ && lyx_view_->view()->buffer()) {
1047                                 // save cursor Position for opened files to .lyx/session
1048                                 LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
1049                                         boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
1050                         }
1051
1052                         LyX::ref().quit();
1053                         break;
1054
1055                 case LFUN_TOC_VIEW: {
1056                         BOOST_ASSERT(lyx_view_);
1057                         InsetCommandParams p("tableofcontents");
1058                         string const data = InsetCommandMailer::params2string("toc", p);
1059                         lyx_view_->getDialogs().show("toc", data, 0);
1060                         break;
1061                 }
1062
1063                 case LFUN_BUFFER_AUTO_SAVE:
1064                         autoSave(view());
1065                         break;
1066
1067                 case LFUN_RECONFIGURE:
1068                         BOOST_ASSERT(lyx_view_);
1069                         reconfigure(*lyx_view_);
1070                         break;
1071
1072                 case LFUN_HELP_OPEN: {
1073                         BOOST_ASSERT(lyx_view_);
1074                         string const arg = argument;
1075                         if (arg.empty()) {
1076                                 setErrorMessage(_("Missing argument"));
1077                                 break;
1078                         }
1079                         string const fname = i18nLibFileSearch("doc", arg, "lyx").absFilename();
1080                         if (fname.empty()) {
1081                                 lyxerr << "LyX: unable to find documentation file `"
1082                                                          << arg << "'. Bad installation?" << endl;
1083                                 break;
1084                         }
1085                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
1086                                 makeDisplayPath(fname)));
1087                         lyx_view_->loadLyXFile(fname, false);
1088                         break;
1089                 }
1090
1091                 // --- version control -------------------------------
1092                 case LFUN_VC_REGISTER:
1093                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1094                         if (!ensureBufferClean(view()))
1095                                 break;
1096                         if (!lyx_view_->buffer()->lyxvc().inUse()) {
1097                                 lyx_view_->buffer()->lyxvc().registrer();
1098                                 view()->reload();
1099                         }
1100                         break;
1101
1102                 case LFUN_VC_CHECK_IN:
1103                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1104                         if (!ensureBufferClean(view()))
1105                                 break;
1106                         if (lyx_view_->buffer()->lyxvc().inUse()
1107                                         && !lyx_view_->buffer()->isReadonly()) {
1108                                 lyx_view_->buffer()->lyxvc().checkIn();
1109                                 view()->reload();
1110                         }
1111                         break;
1112
1113                 case LFUN_VC_CHECK_OUT:
1114                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1115                         if (!ensureBufferClean(view()))
1116                                 break;
1117                         if (lyx_view_->buffer()->lyxvc().inUse()
1118                                         && lyx_view_->buffer()->isReadonly()) {
1119                                 lyx_view_->buffer()->lyxvc().checkOut();
1120                                 view()->reload();
1121                         }
1122                         break;
1123
1124                 case LFUN_VC_REVERT:
1125                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1126                         lyx_view_->buffer()->lyxvc().revert();
1127                         view()->reload();
1128                         break;
1129
1130                 case LFUN_VC_UNDO_LAST:
1131                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1132                         lyx_view_->buffer()->lyxvc().undoLast();
1133                         view()->reload();
1134                         break;
1135
1136                 // --- buffers ----------------------------------------
1137                 case LFUN_BUFFER_SWITCH:
1138                         BOOST_ASSERT(lyx_view_);
1139                         lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1140                         break;
1141
1142                 case LFUN_BUFFER_NEXT:
1143                         BOOST_ASSERT(lyx_view_);
1144                         lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1145                         break;
1146
1147                 case LFUN_BUFFER_PREVIOUS:
1148                         BOOST_ASSERT(lyx_view_);
1149                         lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1150                         break;
1151
1152                 case LFUN_FILE_NEW:
1153                         BOOST_ASSERT(lyx_view_);
1154                         newFile(view(), argument);
1155                         break;
1156
1157                 case LFUN_FILE_OPEN:
1158                         BOOST_ASSERT(lyx_view_);
1159                         open(argument);
1160                         break;
1161
1162                 case LFUN_DROP_LAYOUTS_CHOICE:
1163                         BOOST_ASSERT(lyx_view_);
1164                         lyx_view_->getToolbars().openLayoutList();
1165                         break;
1166
1167                 case LFUN_MENU_OPEN:
1168                         BOOST_ASSERT(lyx_view_);
1169                         lyx_view_->getMenubar().openByName(from_utf8(argument));
1170                         break;
1171
1172                 // --- lyxserver commands ----------------------------
1173                 case LFUN_SERVER_GET_NAME:
1174                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1175                         setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1176                         lyxerr[Debug::INFO] << "FNAME["
1177                                                          << lyx_view_->buffer()->fileName()
1178                                                          << "] " << endl;
1179                         break;
1180
1181                 case LFUN_SERVER_NOTIFY:
1182                         dispatch_buffer = from_utf8(keyseq->print());
1183                         theLyXServer().notifyClient(to_utf8(dispatch_buffer));
1184                         break;
1185
1186                 case LFUN_SERVER_GOTO_FILE_ROW: {
1187                         BOOST_ASSERT(lyx_view_);
1188                         string file_name;
1189                         int row;
1190                         istringstream is(argument);
1191                         is >> file_name >> row;
1192                         if (prefixIs(file_name, package().temp_dir())) {
1193                                 // Needed by inverse dvi search. If it is a file
1194                                 // in tmpdir, call the apropriated function
1195                                 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1196                         } else {
1197                                 // Must replace extension of the file to be .lyx
1198                                 // and get full path
1199                                 string const s = changeExtension(file_name, ".lyx");
1200                                 // Either change buffer or load the file
1201                                 if (theBufferList().exists(s)) {
1202                                         lyx_view_->setBuffer(theBufferList().getBuffer(s));
1203                                 } else {
1204                                         lyx_view_->loadLyXFile(s);
1205                                 }
1206                         }
1207
1208                         view()->setCursorFromRow(row);
1209
1210                         view()->center();
1211                         // see BufferView::center()
1212                         break;
1213                 }
1214
1215                 case LFUN_DIALOG_SHOW: {
1216                         BOOST_ASSERT(lyx_view_);
1217                         string const name = cmd.getArg(0);
1218                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1219
1220                         if (name == "character") {
1221                                 data = freefont2string();
1222                                 if (!data.empty())
1223                                         lyx_view_->getDialogs().show("character", data);
1224                         } else if (name == "latexlog") {
1225                                 pair<Buffer::LogType, string> const logfile =
1226                                         lyx_view_->buffer()->getLogName();
1227                                 switch (logfile.first) {
1228                                 case Buffer::latexlog:
1229                                         data = "latex ";
1230                                         break;
1231                                 case Buffer::buildlog:
1232                                         data = "literate ";
1233                                         break;
1234                                 }
1235                                 data += LyXLex::quoteString(logfile.second);
1236                                 lyx_view_->getDialogs().show("log", data);
1237                         } else if (name == "vclog") {
1238                                 string const data = "vc " +
1239                                         LyXLex::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1240                                 lyx_view_->getDialogs().show("log", data);
1241                         } else
1242                                 lyx_view_->getDialogs().show(name, data);
1243                         break;
1244                 }
1245
1246                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1247                         BOOST_ASSERT(lyx_view_);
1248                         string const name = cmd.getArg(0);
1249                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1250                         if (name == "bibitem" ||
1251                             name == "bibtex" ||
1252                             name == "index" ||
1253                             name == "label" ||
1254                             name == "nomenclature" ||
1255                             name == "ref" ||
1256                             name == "toc" ||
1257                             name == "url") {
1258                                 InsetCommandParams p(name);
1259                                 data = InsetCommandMailer::params2string(name, p);
1260                         } else if (name == "include") {
1261                                 InsetCommandParams p(data);
1262                                 data = InsetIncludeMailer::params2string(p);
1263                         } else if (name == "box") {
1264                                 // \c data == "Boxed" || "Frameless" etc
1265                                 InsetBoxParams p(data);
1266                                 data = InsetBoxMailer::params2string(p);
1267                         } else if (name == "branch") {
1268                                 InsetBranchParams p;
1269                                 data = InsetBranchMailer::params2string(p);
1270                         } else if (name == "citation") {
1271                                 InsetCommandParams p("cite");
1272                                 data = InsetCommandMailer::params2string(name, p);
1273                         } else if (name == "ert") {
1274                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1275                         } else if (name == "external") {
1276                                 InsetExternalParams p;
1277                                 Buffer const & buffer = *lyx_view_->buffer();
1278                                 data = InsetExternalMailer::params2string(p, buffer);
1279                         } else if (name == "float") {
1280                                 InsetFloatParams p;
1281                                 data = InsetFloatMailer::params2string(p);
1282                         } else if (name == "graphics") {
1283                                 InsetGraphicsParams p;
1284                                 Buffer const & buffer = *lyx_view_->buffer();
1285                                 data = InsetGraphicsMailer::params2string(p, buffer);
1286                         } else if (name == "note") {
1287                                 InsetNoteParams p;
1288                                 data = InsetNoteMailer::params2string(p);
1289                         } else if (name == "vspace") {
1290                                 VSpace space;
1291                                 data = InsetVSpaceMailer::params2string(space);
1292                         } else if (name == "wrap") {
1293                                 InsetWrapParams p;
1294                                 data = InsetWrapMailer::params2string(p);
1295                         }
1296                         lyx_view_->getDialogs().show(name, data, 0);
1297                         break;
1298                 }
1299
1300                 case LFUN_DIALOG_UPDATE: {
1301                         BOOST_ASSERT(lyx_view_);
1302                         string const & name = argument;
1303                         // Can only update a dialog connected to an existing inset
1304                         InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1305                         if (inset) {
1306                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1307                                 inset->dispatch(view()->cursor(), fr);
1308                         } else if (name == "paragraph") {
1309                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1310                         } else if (name == "prefs") {
1311                                 lyx_view_->getDialogs().update(name, string());
1312                         }
1313                         break;
1314                 }
1315
1316                 case LFUN_DIALOG_HIDE:
1317                         Dialogs::hide(argument, 0);
1318                         break;
1319
1320                 case LFUN_DIALOG_DISCONNECT_INSET:
1321                         BOOST_ASSERT(lyx_view_);
1322                         lyx_view_->getDialogs().disconnect(argument);
1323                         break;
1324
1325
1326                 case LFUN_CITATION_INSERT: {
1327                         BOOST_ASSERT(lyx_view_);
1328                         if (!argument.empty()) {
1329                                 // we can have one optional argument, delimited by '|'
1330                                 // citation-insert <key>|<text_before>
1331                                 // this should be enhanced to also support text_after
1332                                 // and citation style
1333                                 string arg = argument;
1334                                 string opt1;
1335                                 if (contains(argument, "|")) {
1336                                         arg = token(argument, '|', 0);
1337                                         opt1 = '[' + token(argument, '|', 1) + ']';
1338                                 }
1339                                 std::ostringstream os;
1340                                 os << "citation LatexCommand\n"
1341                                    << "\\cite" << opt1 << "{" << arg << "}\n"
1342                                    << "\\end_inset";
1343                                 FuncRequest fr(LFUN_INSET_INSERT, os.str());
1344                                 dispatch(fr);
1345                         } else
1346                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "citation"));
1347                         break;
1348                 }
1349
1350                 case LFUN_BUFFER_CHILD_OPEN: {
1351                         BOOST_ASSERT(lyx_view_);
1352                         string const filename =
1353                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1354                         // FIXME Should use bformat
1355                         setMessage(_("Opening child document ") +
1356                                          makeDisplayPath(filename) + "...");
1357                         view()->saveBookmark(false);
1358                         string const parentfilename = lyx_view_->buffer()->fileName();
1359                         if (theBufferList().exists(filename))
1360                                 lyx_view_->setBuffer(theBufferList().getBuffer(filename));
1361                         else
1362                                 lyx_view_->loadLyXFile(filename);
1363                         // Set the parent name of the child document.
1364                         // This makes insertion of citations and references in the child work,
1365                         // when the target is in the parent or another child document.
1366                         lyx_view_->buffer()->setParentName(parentfilename);
1367                         break;
1368                 }
1369
1370                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1371                         BOOST_ASSERT(lyx_view_);
1372                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1373                         break;
1374
1375                 case LFUN_KEYMAP_OFF:
1376                         BOOST_ASSERT(lyx_view_);
1377                         lyx_view_->view()->getIntl().keyMapOn(false);
1378                         break;
1379
1380                 case LFUN_KEYMAP_PRIMARY:
1381                         BOOST_ASSERT(lyx_view_);
1382                         lyx_view_->view()->getIntl().keyMapPrim();
1383                         break;
1384
1385                 case LFUN_KEYMAP_SECONDARY:
1386                         BOOST_ASSERT(lyx_view_);
1387                         lyx_view_->view()->getIntl().keyMapSec();
1388                         break;
1389
1390                 case LFUN_KEYMAP_TOGGLE:
1391                         BOOST_ASSERT(lyx_view_);
1392                         lyx_view_->view()->getIntl().toggleKeyMap();
1393                         break;
1394
1395                 case LFUN_REPEAT: {
1396                         // repeat command
1397                         string countstr;
1398                         string rest = split(argument, countstr, ' ');
1399                         istringstream is(countstr);
1400                         int count = 0;
1401                         is >> count;
1402                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1403                         for (int i = 0; i < count; ++i)
1404                                 dispatch(lyxaction.lookupFunc(rest));
1405                         break;
1406                 }
1407
1408                 case LFUN_COMMAND_SEQUENCE: {
1409                         // argument contains ';'-terminated commands
1410                         string arg = argument;
1411                         while (!arg.empty()) {
1412                                 string first;
1413                                 arg = split(arg, first, ';');
1414                                 FuncRequest func(lyxaction.lookupFunc(first));
1415                                 func.origin = cmd.origin;
1416                                 dispatch(func);
1417                         }
1418                         break;
1419                 }
1420
1421                 case LFUN_PREFERENCES_SAVE: {
1422                         support::Path p(package().user_support());
1423                         lyxrc.write("preferences", false);
1424                         break;
1425                 }
1426
1427                 case LFUN_SCREEN_FONT_UPDATE:
1428                         BOOST_ASSERT(lyx_view_);
1429                         // handle the screen font changes.
1430                         lyxrc.set_font_norm_type();
1431                         theFontLoader().update();
1432                         // All visible buffers will need resize
1433                         view()->resize();
1434                         break;
1435
1436                 case LFUN_SET_COLOR: {
1437                         string lyx_name;
1438                         string const x11_name = split(argument, lyx_name, ' ');
1439                         if (lyx_name.empty() || x11_name.empty()) {
1440                                 setErrorMessage(_("Syntax: set-color <lyx_name>"
1441                                                         " <x11_name>"));
1442                                 break;
1443                         }
1444
1445                         bool const graphicsbg_changed =
1446                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1447                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1448
1449                         if (!lcolor.setColor(lyx_name, x11_name)) {
1450                                 setErrorMessage(
1451                                                 bformat(_("Set-color \"%1$s\" failed "
1452                                                                        "- color is undefined or "
1453                                                                        "may not be redefined"),
1454                                                                            from_utf8(lyx_name)));
1455                                 break;
1456                         }
1457
1458                         theApp->updateColor(lcolor.getFromLyXName(lyx_name));
1459
1460                         if (graphicsbg_changed) {
1461 #ifdef WITH_WARNINGS
1462 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1463 #endif
1464 #if 0
1465                                 graphics::GCache::get().changeDisplay(true);
1466 #endif
1467                         }
1468                         break;
1469                 }
1470
1471                 case LFUN_MESSAGE:
1472                         BOOST_ASSERT(lyx_view_);
1473                         lyx_view_->message(from_utf8(argument));
1474                         break;
1475
1476                 case LFUN_EXTERNAL_EDIT: {
1477                         BOOST_ASSERT(lyx_view_);
1478                         FuncRequest fr(action, argument);
1479                         InsetExternal().dispatch(view()->cursor(), fr);
1480                         break;
1481                 }
1482
1483                 case LFUN_GRAPHICS_EDIT: {
1484                         FuncRequest fr(action, argument);
1485                         InsetGraphics().dispatch(view()->cursor(), fr);
1486                         break;
1487                 }
1488
1489                 case LFUN_INSET_APPLY: {
1490                         BOOST_ASSERT(lyx_view_);
1491                         string const name = cmd.getArg(0);
1492                         InsetBase * inset = lyx_view_->getDialogs().getOpenInset(name);
1493                         if (inset) {
1494                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1495                                 inset->dispatch(view()->cursor(), fr);
1496                         } else {
1497                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1498                                 dispatch(fr);
1499                         }
1500                         // ideally, the update flag should be set by the insets,
1501                         // but this is not possible currently
1502                         updateFlags = Update::Force | Update::FitCursor;
1503                         break;
1504                 }
1505
1506                 case LFUN_ALL_INSETS_TOGGLE: {
1507                         BOOST_ASSERT(lyx_view_);
1508                         string action;
1509                         string const name = split(argument, action, ' ');
1510                         InsetBase::Code const inset_code =
1511                                 InsetBase::translate(name);
1512
1513                         LCursor & cur = view()->cursor();
1514                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1515
1516                         InsetBase & inset = lyx_view_->buffer()->inset();
1517                         InsetIterator it  = inset_iterator_begin(inset);
1518                         InsetIterator const end = inset_iterator_end(inset);
1519                         for (; it != end; ++it) {
1520                                 if (inset_code == InsetBase::NO_CODE
1521                                     || inset_code == it->lyxCode()) {
1522                                         LCursor tmpcur = cur;
1523                                         tmpcur.pushLeft(*it);
1524                                         it->dispatch(tmpcur, fr);
1525                                 }
1526                         }
1527                         updateFlags = Update::Force | Update::FitCursor;
1528                         break;
1529                 }
1530
1531                 case LFUN_BUFFER_LANGUAGE: {
1532                         BOOST_ASSERT(lyx_view_);
1533                         Buffer & buffer = *lyx_view_->buffer();
1534                         Language const * oldL = buffer.params().language;
1535                         Language const * newL = languages.getLanguage(argument);
1536                         if (!newL || oldL == newL)
1537                                 break;
1538
1539                         if (oldL->rightToLeft() == newL->rightToLeft()
1540                             && !buffer.isMultiLingual())
1541                                 buffer.changeLanguage(oldL, newL);
1542                         else
1543                                 buffer.updateDocLang(newL);
1544                         break;
1545                 }
1546
1547                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1548                         string const fname =
1549                                 addName(addPath(package().user_support(), "templates/"),
1550                                         "defaults.lyx");
1551                         Buffer defaults(fname);
1552
1553                         istringstream ss(argument);
1554                         LyXLex lex(0,0);
1555                         lex.setStream(ss);
1556                         int const unknown_tokens = defaults.readHeader(lex);
1557
1558                         if (unknown_tokens != 0) {
1559                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1560                                        << unknown_tokens << " unknown token"
1561                                        << (unknown_tokens == 1 ? "" : "s")
1562                                        << endl;
1563                         }
1564
1565                         if (defaults.writeFile(defaults.fileName()))
1566                                 // FIXME Should use bformat
1567                                 setMessage(_("Document defaults saved in ")
1568                                            + makeDisplayPath(fname));
1569                         else
1570                                 setErrorMessage(_("Unable to save document defaults"));
1571                         break;
1572                 }
1573
1574                 case LFUN_BUFFER_PARAMS_APPLY: {
1575                         BOOST_ASSERT(lyx_view_);
1576                         biblio::CiteEngine const engine =
1577                                 lyx_view_->buffer()->params().cite_engine;
1578
1579                         istringstream ss(argument);
1580                         LyXLex lex(0,0);
1581                         lex.setStream(ss);
1582                         int const unknown_tokens =
1583                                 lyx_view_->buffer()->readHeader(lex);
1584
1585                         if (unknown_tokens != 0) {
1586                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1587                                        << unknown_tokens << " unknown token"
1588                                        << (unknown_tokens == 1 ? "" : "s")
1589                                        << endl;
1590                         }
1591                         if (engine == lyx_view_->buffer()->params().cite_engine)
1592                                 break;
1593
1594                         LCursor & cur = view()->cursor();
1595                         FuncRequest fr(LFUN_INSET_REFRESH);
1596
1597                         InsetBase & inset = lyx_view_->buffer()->inset();
1598                         InsetIterator it  = inset_iterator_begin(inset);
1599                         InsetIterator const end = inset_iterator_end(inset);
1600                         for (; it != end; ++it)
1601                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1602                                         it->dispatch(cur, fr);
1603                         break;
1604                 }
1605
1606                 case LFUN_TEXTCLASS_APPLY: {
1607                         BOOST_ASSERT(lyx_view_);
1608                         Buffer * buffer = lyx_view_->buffer();
1609
1610                         textclass_type const old_class =
1611                                 buffer->params().textclass;
1612
1613                         loadTextclass(argument);
1614
1615                         std::pair<bool, textclass_type> const tc_pair =
1616                                 textclasslist.numberOfClass(argument);
1617
1618                         if (!tc_pair.first)
1619                                 break;
1620
1621                         textclass_type const new_class = tc_pair.second;
1622                         if (old_class == new_class)
1623                                 // nothing to do
1624                                 break;
1625
1626                         lyx_view_->message(_("Converting document to new document class..."));
1627                         recordUndoFullDocument(view());
1628                         buffer->params().textclass = new_class;
1629                         StableDocIterator backcur(view()->cursor());
1630                         ErrorList & el = buffer->errorList("Class Switch");
1631                         cap::switchBetweenClasses(
1632                                 old_class, new_class,
1633                                 static_cast<InsetText &>(buffer->inset()), el);
1634
1635                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1636
1637                         buffer->errors("Class Switch");
1638                         updateLabels(*buffer);
1639                         updateFlags = Update::Force | Update::FitCursor;
1640                         break;
1641                 }
1642
1643                 case LFUN_TEXTCLASS_LOAD:
1644                         loadTextclass(argument);
1645                         break;
1646
1647                 case LFUN_LYXRC_APPLY: {
1648                         LyXRC const lyxrc_orig = lyxrc;
1649
1650                         istringstream ss(argument);
1651                         bool const success = lyxrc.read(ss) == 0;
1652
1653                         if (!success) {
1654                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1655                                        << "Unable to read lyxrc data"
1656                                        << endl;
1657                                 break;
1658                         }
1659
1660                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1661                         break;
1662                 }
1663
1664                 case LFUN_WINDOW_NEW:
1665                         LyX::ref().newLyXView();
1666                         break;
1667
1668                 case LFUN_WINDOW_CLOSE:
1669                         BOOST_ASSERT(lyx_view_);
1670                         BOOST_ASSERT(theApp);
1671                         lyx_view_->close();
1672                         // We return here because lyx_view does not exists anymore.
1673                         return;
1674
1675                 case LFUN_BOOKMARK_GOTO: {
1676                         BOOST_ASSERT(lyx_view_);
1677                         unsigned int idx = convert<unsigned int>(to_utf8(cmd.argument()));
1678                         BookmarksSection::Bookmark const bm = LyX::ref().session().bookmarks().bookmark(idx);
1679                         BOOST_ASSERT(!bm.filename.empty());
1680                         // if the file is not opened, open it.
1681                         if (!theBufferList().exists(bm.filename))
1682                                 dispatch(FuncRequest(LFUN_FILE_OPEN, bm.filename));
1683                         // open may fail, so we need to test it again
1684                         if (theBufferList().exists(bm.filename)) {
1685                                 // if the current buffer is not that one, switch to it.
1686                                 if (lyx_view_->buffer()->fileName() != bm.filename)
1687                                         dispatch(FuncRequest(LFUN_BUFFER_SWITCH, bm.filename));
1688                                 // BOOST_ASSERT(lyx_view_->buffer()->fileName() != bm.filename);
1689                                 view()->moveToPosition(bm.par_id, bm.par_pos);
1690                         } 
1691                         break;
1692                 }
1693
1694                 case LFUN_BOOKMARK_CLEAR:
1695                         LyX::ref().session().bookmarks().clear();
1696                         break;
1697
1698                 case LFUN_TOOLBAR_TOGGLE_STATE:
1699                         lyx_view_->toggleToolbarState(argument);
1700                         break;
1701
1702                 default: {
1703                         BOOST_ASSERT(lyx_view_);
1704                         view()->cursor().dispatch(cmd);
1705                         updateFlags = view()->cursor().result().update();
1706                         if (!view()->cursor().result().dispatched())
1707                                 if (view()->dispatch(cmd)) 
1708                                         updateFlags = Update::Force | Update::FitCursor;
1709                         break;
1710                 }
1711                 }
1712
1713                 if (lyx_view_ && view()->buffer()) {
1714                         // BufferView::update() updates the ViewMetricsInfo and
1715                         // also initializes the position cache for all insets in
1716                         // (at least partially) visible top-level paragraphs.
1717                         std::pair<bool, bool> needSecondUpdate 
1718                                 = view()->update(updateFlags);
1719
1720                         // Redraw screen unless explicitly told otherwise.
1721                         if (needSecondUpdate.first)
1722                                 // Buffer::changed() signals that a repaint is needed.
1723                                 // The frontend (WorkArea) knows which area to repaint
1724                                 // thanks to the ViewMetricsInfo updated above.
1725                                 view()->buffer()->changed();
1726
1727                         lyx_view_->updateStatusBar();
1728
1729                         // if we executed a mutating lfun, mark the buffer as dirty
1730                         if (flag.enabled()
1731                             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1732                             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1733                                 view()->buffer()->markDirty();
1734
1735                         if (view()->cursor().inTexted()) {
1736                                 lyx_view_->updateLayoutChoice();
1737                         }
1738                 }
1739         }
1740         if (!quitting) {
1741                 lyx_view_->updateMenubar();
1742                 lyx_view_->updateToolbars();
1743                 sendDispatchMessage(getMessage(), cmd);
1744         }
1745 }
1746
1747
1748 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1749 {
1750         const bool verbose = (cmd.origin == FuncRequest::MENU
1751                               || cmd.origin == FuncRequest::TOOLBAR
1752                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1753
1754         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1755                 lyxerr[Debug::ACTION] << "dispatch msg is " << to_utf8(msg) << endl;
1756                 if (!msg.empty())
1757                         lyx_view_->message(msg);
1758                 return;
1759         }
1760
1761         docstring dispatch_msg = msg;
1762         if (!dispatch_msg.empty())
1763                 dispatch_msg += ' ';
1764
1765         string comname = lyxaction.getActionName(cmd.action);
1766
1767         bool argsadded = false;
1768
1769         if (!cmd.argument().empty()) {
1770                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1771                         comname += ' ' + to_utf8(cmd.argument());
1772                         argsadded = true;
1773                 }
1774         }
1775
1776         string const shortcuts = theTopLevelKeymap().printbindings(cmd);
1777
1778         if (!shortcuts.empty())
1779                 comname += ": " + shortcuts;
1780         else if (!argsadded && !cmd.argument().empty())
1781                 comname += ' ' + to_utf8(cmd.argument());
1782
1783         if (!comname.empty()) {
1784                 comname = rtrim(comname);
1785                 dispatch_msg += from_utf8('(' + rtrim(comname) + ')');
1786         }
1787
1788         lyxerr[Debug::ACTION] << "verbose dispatch msg "
1789                 << to_utf8(dispatch_msg) << endl;
1790         if (!dispatch_msg.empty())
1791                 lyx_view_->message(dispatch_msg);
1792 }
1793
1794
1795 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1796 {
1797         string initpath = lyxrc.document_path;
1798         string filename(name);
1799
1800         if (view()->buffer()) {
1801                 string const trypath = lyx_view_->buffer()->filePath();
1802                 // If directory is writeable, use this as default.
1803                 if (isDirWriteable(trypath))
1804                         initpath = trypath;
1805         }
1806
1807         static int newfile_number;
1808
1809         if (filename.empty()) {
1810                 filename = addName(lyxrc.document_path,
1811                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1812                 while (theBufferList().exists(filename) || fs::is_readable(filename)) {
1813                         ++newfile_number;
1814                         filename = addName(lyxrc.document_path,
1815                                            "newfile" +  convert<string>(newfile_number) +
1816                                     ".lyx");
1817                 }
1818         }
1819
1820         // The template stuff
1821         string templname;
1822         if (fromTemplate) {
1823                 FileDialog fileDlg(_("Select template file"),
1824                         LFUN_SELECT_FILE_SYNC,
1825                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1826                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1827
1828                 FileDialog::Result result =
1829                         fileDlg.open(from_utf8(lyxrc.template_path),
1830                                      FileFilterList(_("LyX Documents (*.lyx)")),
1831                                      docstring());
1832
1833                 if (result.first == FileDialog::Later)
1834                         return;
1835                 if (result.second.empty())
1836                         return;
1837                 templname = to_utf8(result.second);
1838         }
1839
1840         Buffer * const b = newFile(filename, templname, !name.empty());
1841         if (b)
1842                 lyx_view_->setBuffer(b);
1843 }
1844
1845
1846 void LyXFunc::open(string const & fname)
1847 {
1848         string initpath = lyxrc.document_path;
1849
1850         if (view()->buffer()) {
1851                 string const trypath = lyx_view_->buffer()->filePath();
1852                 // If directory is writeable, use this as default.
1853                 if (isDirWriteable(trypath))
1854                         initpath = trypath;
1855         }
1856
1857         string filename;
1858
1859         if (fname.empty()) {
1860                 FileDialog fileDlg(_("Select document to open"),
1861                         LFUN_FILE_OPEN,
1862                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1863                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support(), "examples"))));
1864
1865                 FileDialog::Result result =
1866                         fileDlg.open(from_utf8(initpath),
1867                                      FileFilterList(_("LyX Documents (*.lyx)")),
1868                                      docstring());
1869
1870                 if (result.first == FileDialog::Later)
1871                         return;
1872
1873                 filename = to_utf8(result.second);
1874
1875                 // check selected filename
1876                 if (filename.empty()) {
1877                         lyx_view_->message(_("Canceled."));
1878                         return;
1879                 }
1880         } else
1881                 filename = fname;
1882
1883         // get absolute path of file and add ".lyx" to the filename if
1884         // necessary
1885         string const fullpath = fileSearch(string(), filename, "lyx").absFilename();
1886         if (!fullpath.empty()) {
1887                 filename = fullpath;
1888         }
1889
1890         docstring const disp_fn = makeDisplayPath(filename);
1891
1892         // if the file doesn't exist, let the user create one
1893         if (!fs::exists(filename)) {
1894                 // the user specifically chose this name. Believe him.
1895                 Buffer * const b = newFile(filename, string(), true);
1896                 if (b)
1897                         lyx_view_->setBuffer(b);
1898                 return;
1899         }
1900
1901         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1902
1903         docstring str2;
1904         if (lyx_view_->loadLyXFile(filename)) {
1905                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1906         } else {
1907                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1908         }
1909         lyx_view_->message(str2);
1910 }
1911
1912
1913 void LyXFunc::doImport(string const & argument)
1914 {
1915         string format;
1916         string filename = split(argument, format, ' ');
1917
1918         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1919                             << " file: " << filename << endl;
1920
1921         // need user interaction
1922         if (filename.empty()) {
1923                 string initpath = lyxrc.document_path;
1924
1925                 if (view()->buffer()) {
1926                         string const trypath = lyx_view_->buffer()->filePath();
1927                         // If directory is writeable, use this as default.
1928                         if (isDirWriteable(trypath))
1929                                 initpath = trypath;
1930                 }
1931
1932                 docstring const text = bformat(_("Select %1$s file to import"),
1933                         formats.prettyName(format));
1934
1935                 FileDialog fileDlg(text,
1936                         LFUN_BUFFER_IMPORT,
1937                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1938                         make_pair(_("Examples|#E#e"),
1939                                   from_utf8(addPath(package().system_support(), "examples"))));
1940
1941                 docstring filter = formats.prettyName(format);
1942                 filter += " (*.";
1943                 // FIXME UNICODE
1944                 filter += from_utf8(formats.extension(format));
1945                 filter += ')';
1946
1947                 FileDialog::Result result =
1948                         fileDlg.open(from_utf8(initpath),
1949                                      FileFilterList(filter),
1950                                      docstring());
1951
1952                 if (result.first == FileDialog::Later)
1953                         return;
1954
1955                 filename = to_utf8(result.second);
1956
1957                 // check selected filename
1958                 if (filename.empty())
1959                         lyx_view_->message(_("Canceled."));
1960         }
1961
1962         if (filename.empty())
1963                 return;
1964
1965         // get absolute path of file
1966         filename = makeAbsPath(filename);
1967
1968         string const lyxfile = changeExtension(filename, ".lyx");
1969
1970         // Check if the document already is open
1971         if (use_gui && theBufferList().exists(lyxfile)) {
1972                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile), true)) {
1973                         lyx_view_->message(_("Canceled."));
1974                         return;
1975                 }
1976         }
1977
1978         // if the file exists already, and we didn't do
1979         // -i lyx thefile.lyx, warn
1980         if (fs::exists(lyxfile) && filename != lyxfile) {
1981                 docstring const file = makeDisplayPath(lyxfile, 30);
1982
1983                 docstring text = bformat(_("The document %1$s already exists.\n\n"
1984                                                      "Do you want to over-write that document?"), file);
1985                 int const ret = Alert::prompt(_("Over-write document?"),
1986                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1987
1988                 if (ret == 1) {
1989                         lyx_view_->message(_("Canceled."));
1990                         return;
1991                 }
1992         }
1993
1994         ErrorList errorList;
1995         Importer::Import(lyx_view_, filename, format, errorList);
1996         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
1997 }
1998
1999
2000 void LyXFunc::closeBuffer()
2001 {
2002         // save current cursor position
2003         LyX::ref().session().lastFilePos().save(lyx_view_->buffer()->fileName(),
2004                 boost::tie(view()->cursor().pit(), view()->cursor().pos()) );
2005         if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2006                 if (theBufferList().empty()) {
2007                         // need this otherwise SEGV may occur while
2008                         // trying to set variables that don't exist
2009                         // since there's no current buffer
2010                         lyx_view_->getDialogs().hideBufferDependent();
2011                 } else {
2012                         lyx_view_->setBuffer(theBufferList().first());
2013                 }
2014         }
2015 }
2016
2017
2018 // Each "lyx_view_" should have it's own message method. lyxview and
2019 // the minibuffer would use the minibuffer, but lyxserver would
2020 // send an ERROR signal to its client.  Alejandro 970603
2021 // This function is bit problematic when it comes to NLS, to make the
2022 // lyx servers client be language indepenent we must not translate
2023 // strings sent to this func.
2024 void LyXFunc::setErrorMessage(docstring const & m) const
2025 {
2026         dispatch_buffer = m;
2027         errorstat = true;
2028 }
2029
2030
2031 void LyXFunc::setMessage(docstring const & m) const
2032 {
2033         dispatch_buffer = m;
2034 }
2035
2036
2037 string const LyXFunc::viewStatusMessage()
2038 {
2039         // When meta-fake key is pressed, show the key sequence so far + "M-".
2040         if (wasMetaKey())
2041                 return keyseq->print() + "M-";
2042
2043         // Else, when a non-complete key sequence is pressed,
2044         // show the available options.
2045         if (keyseq->length() > 0 && !keyseq->deleted())
2046                 return keyseq->printOptions();
2047
2048         if (!view()->buffer())
2049                 return to_utf8(_("Welcome to LyX!"));
2050
2051         return view()->cursor().currentState();
2052 }
2053
2054
2055 BufferView * LyXFunc::view() const
2056 {
2057         BOOST_ASSERT(lyx_view_);
2058         return lyx_view_->view();
2059 }
2060
2061
2062 bool LyXFunc::wasMetaKey() const
2063 {
2064         return (meta_fake_bit != key_modifier::none);
2065 }
2066
2067
2068 namespace {
2069
2070 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2071 {
2072         // Why the switch you might ask. It is a trick to ensure that all
2073         // the elements in the LyXRCTags enum is handled. As you can see
2074         // there are no breaks at all. So it is just a huge fall-through.
2075         // The nice thing is that we will get a warning from the compiler
2076         // if we forget an element.
2077         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2078         switch (tag) {
2079         case LyXRC::RC_ACCEPT_COMPOUND:
2080         case LyXRC::RC_ALT_LANG:
2081         case LyXRC::RC_ASCIIROFF_COMMAND:
2082         case LyXRC::RC_ASCII_LINELEN:
2083         case LyXRC::RC_AUTOREGIONDELETE:
2084         case LyXRC::RC_AUTORESET_OPTIONS:
2085         case LyXRC::RC_AUTOSAVE:
2086         case LyXRC::RC_AUTO_NUMBER:
2087         case LyXRC::RC_BACKUPDIR_PATH:
2088         case LyXRC::RC_BIBTEX_COMMAND:
2089         case LyXRC::RC_BINDFILE:
2090         case LyXRC::RC_CHECKLASTFILES:
2091         case LyXRC::RC_USELASTFILEPOS:
2092         case LyXRC::RC_LOADSESSION:
2093         case LyXRC::RC_CHKTEX_COMMAND:
2094         case LyXRC::RC_CONVERTER:
2095         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2096         case LyXRC::RC_COPIER:
2097         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2098         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2099         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2100         case LyXRC::RC_DATE_INSERT_FORMAT:
2101         case LyXRC::RC_DEFAULT_LANGUAGE:
2102         case LyXRC::RC_DEFAULT_PAPERSIZE:
2103         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2104         case LyXRC::RC_DISPLAY_GRAPHICS:
2105         case LyXRC::RC_DOCUMENTPATH:
2106                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2107                         if (fs::exists(lyxrc_new.document_path) &&
2108                             fs::is_directory(lyxrc_new.document_path)) {
2109                                 support::package().document_dir() = lyxrc.document_path;
2110                         }
2111                 }
2112         case LyXRC::RC_ESC_CHARS:
2113         case LyXRC::RC_FONT_ENCODING:
2114         case LyXRC::RC_FORMAT:
2115         case LyXRC::RC_INDEX_COMMAND:
2116         case LyXRC::RC_INPUT:
2117         case LyXRC::RC_KBMAP:
2118         case LyXRC::RC_KBMAP_PRIMARY:
2119         case LyXRC::RC_KBMAP_SECONDARY:
2120         case LyXRC::RC_LABEL_INIT_LENGTH:
2121         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2122         case LyXRC::RC_LANGUAGE_AUTO_END:
2123         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2124         case LyXRC::RC_LANGUAGE_COMMAND_END:
2125         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2126         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2127         case LyXRC::RC_LANGUAGE_PACKAGE:
2128         case LyXRC::RC_LANGUAGE_USE_BABEL:
2129         case LyXRC::RC_MAKE_BACKUP:
2130         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2131         case LyXRC::RC_NUMLASTFILES:
2132         case LyXRC::RC_PATH_PREFIX:
2133                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2134                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2135                 }
2136         case LyXRC::RC_PERS_DICT:
2137         case LyXRC::RC_POPUP_BOLD_FONT:
2138         case LyXRC::RC_POPUP_FONT_ENCODING:
2139         case LyXRC::RC_POPUP_NORMAL_FONT:
2140         case LyXRC::RC_PREVIEW:
2141         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2142         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2143         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2144         case LyXRC::RC_PRINTCOPIESFLAG:
2145         case LyXRC::RC_PRINTER:
2146         case LyXRC::RC_PRINTEVENPAGEFLAG:
2147         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2148         case LyXRC::RC_PRINTFILEEXTENSION:
2149         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2150         case LyXRC::RC_PRINTODDPAGEFLAG:
2151         case LyXRC::RC_PRINTPAGERANGEFLAG:
2152         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2153         case LyXRC::RC_PRINTPAPERFLAG:
2154         case LyXRC::RC_PRINTREVERSEFLAG:
2155         case LyXRC::RC_PRINTSPOOL_COMMAND:
2156         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2157         case LyXRC::RC_PRINTTOFILE:
2158         case LyXRC::RC_PRINTTOPRINTER:
2159         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2160         case LyXRC::RC_PRINT_COMMAND:
2161         case LyXRC::RC_RTL_SUPPORT:
2162         case LyXRC::RC_SCREEN_DPI:
2163         case LyXRC::RC_SCREEN_FONT_ENCODING:
2164         case LyXRC::RC_SCREEN_FONT_ROMAN:
2165         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2166         case LyXRC::RC_SCREEN_FONT_SANS:
2167         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2168         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2169         case LyXRC::RC_SCREEN_FONT_SIZES:
2170         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2171         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2172         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2173         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2174         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2175         case LyXRC::RC_SCREEN_ZOOM:
2176         case LyXRC::RC_SERVERPIPE:
2177         case LyXRC::RC_SET_COLOR:
2178         case LyXRC::RC_SHOW_BANNER:
2179         case LyXRC::RC_SPELL_COMMAND:
2180         case LyXRC::RC_TEMPDIRPATH:
2181         case LyXRC::RC_TEMPLATEPATH:
2182         case LyXRC::RC_TEX_ALLOWS_SPACES:
2183         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2184                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2185                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2186                 }
2187         case LyXRC::RC_UIFILE:
2188         case LyXRC::RC_USER_EMAIL:
2189         case LyXRC::RC_USER_NAME:
2190         case LyXRC::RC_USETEMPDIR:
2191         case LyXRC::RC_USE_ALT_LANG:
2192         case LyXRC::RC_USE_CONVERTER_CACHE:
2193         case LyXRC::RC_USE_ESC_CHARS:
2194         case LyXRC::RC_USE_INP_ENC:
2195         case LyXRC::RC_USE_PERS_DICT:
2196         case LyXRC::RC_USE_SPELL_LIB:
2197         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2198         case LyXRC::RC_VIEWER:
2199         case LyXRC::RC_LAST:
2200                 break;
2201         }
2202 }
2203
2204 } // namespace anon
2205
2206
2207 } // namespace lyx