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