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