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