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