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