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