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