]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
fix crash when collapsing ert with cursor inside
[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                 UpdatableInset * inset = cur.inset().asUpdatableInset();
432                 lyxerr << "inset: " << inset << endl;
433                 if (!inset)
434                         break;
435
436                 InsetBase::Code code = inset->lyxCode();
437                 switch (code) {
438                         case InsetBase::TABULAR_CODE:
439                                 enable = cmd.argument == "tabular";
440                                 break;
441                         case InsetBase::ERT_CODE:
442                                 enable = cmd.argument == "ert";
443                                 break;
444                         case InsetBase::FLOAT_CODE:
445                                 enable = cmd.argument == "float";
446                                 break;
447                         case InsetBase::WRAP_CODE:
448                                 enable = cmd.argument == "wrap";
449                                 break;
450                         case InsetBase::NOTE_CODE:
451                                 enable = cmd.argument == "note";
452                                 break;
453                         case InsetBase::BRANCH_CODE:
454                                 enable = cmd.argument == "branch";
455                                 break;
456                         case InsetBase::BOX_CODE:
457                                 enable = cmd.argument == "box";
458                                 break;
459                         default:
460                                 break;
461                 }
462                 break;
463         }
464
465         case LFUN_INSET_APPLY: {
466                 string const name = cmd.getArg(0);
467                 InsetBase * inset = owner->getDialogs().getOpenInset(name);
468                 if (inset) {
469                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
470                         FuncStatus fs;
471                         bool const success = inset->getStatus(cur, fr, fs);
472                         // Every inset is supposed to handle this
473                         BOOST_ASSERT(success);
474                         flag |= fs;
475                 } else {
476                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
477                         flag |= getStatus(fr);
478                 }
479                 enable = flag.enabled();
480                 break;
481         }
482
483         case LFUN_DIALOG_SHOW: {
484                 string const name = cmd.getArg(0);
485                 if (!buf)
486                         enable = name == "aboutlyx"
487                                 || name == "file"
488                                 || name == "forks"
489                                 || name == "prefs"
490                                 || name == "texinfo";
491                 else if (name == "print")
492                         enable = Exporter::IsExportable(*buf, "dvi")
493                                 && lyxrc.print_command != "none";
494                 else if (name == "character" || name == "mathpanel")
495                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
496                 else if (name == "latexlog")
497                         enable = IsFileReadable(buf->getLogName().second);
498 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
499                 else if (name == "spellchecker")
500                         enable = false;
501 #endif
502                 else if (name == "vclog")
503                         enable = buf->lyxvc().inUse();
504                 break;
505         }
506
507         case LFUN_DIALOG_SHOW_NEW_INSET:
508                 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
509                 break;
510
511         case LFUN_DIALOG_UPDATE: {
512                 string const name = cmd.getArg(0);
513                 if (!buf)
514                         enable = name == "prefs";
515                 break;
516         }
517
518         // this one is difficult to get right. As a half-baked
519         // solution, we consider only the first action of the sequence
520         case LFUN_SEQUENCE: {
521                 // argument contains ';'-terminated commands
522                 string const firstcmd = token(cmd.argument, ';', 0);
523                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
524                 func.origin = cmd.origin;
525                 flag = getStatus(func);
526         }
527
528         case LFUN_MENUNEW:
529         case LFUN_MENUNEWTMPLT:
530         case LFUN_WORDFINDFORWARD:
531         case LFUN_WORDFINDBACKWARD:
532         case LFUN_PREFIX:
533         case LFUN_EXEC_COMMAND:
534         case LFUN_CANCEL:
535         case LFUN_META_FAKE:
536         case LFUN_CLOSEBUFFER:
537         case LFUN_MENUWRITE:
538         case LFUN_WRITEAS:
539         case LFUN_UPDATE:
540         case LFUN_PREVIEW:
541         case LFUN_IMPORT:
542         case LFUN_QUIT:
543         case LFUN_TOCVIEW:
544         case LFUN_AUTOSAVE:
545         case LFUN_RECONFIGURE:
546         case LFUN_HELP_OPEN:
547         case LFUN_FILE_NEW:
548         case LFUN_FILE_OPEN:
549         case LFUN_DROP_LAYOUTS_CHOICE:
550         case LFUN_MENU_OPEN_BY_NAME:
551         case LFUN_GETNAME:
552         case LFUN_NOTIFY:
553         case LFUN_GOTOFILEROW:
554         case LFUN_DIALOG_SHOW_NEXT_INSET:
555         case LFUN_DIALOG_HIDE:
556         case LFUN_DIALOG_DISCONNECT_INSET:
557         case LFUN_CHILDOPEN:
558         case LFUN_TOGGLECURSORFOLLOW:
559         case LFUN_KMAP_OFF:
560         case LFUN_KMAP_PRIM:
561         case LFUN_KMAP_SEC:
562         case LFUN_KMAP_TOGGLE:
563         case LFUN_REPEAT:
564         case LFUN_EXPORT_CUSTOM:
565         case LFUN_PRINT:
566         case LFUN_SAVEPREFERENCES:
567         case LFUN_SCREEN_FONT_UPDATE:
568         case LFUN_SET_COLOR:
569         case LFUN_MESSAGE:
570         case LFUN_EXTERNAL_EDIT:
571         case LFUN_GRAPHICS_EDIT:
572         case LFUN_ALL_INSETS_TOGGLE:
573         case LFUN_LANGUAGE_BUFFER:
574         case LFUN_TEXTCLASS_APPLY:
575         case LFUN_TEXTCLASS_LOAD:
576         case LFUN_SAVE_AS_DEFAULT:
577         case LFUN_BUFFERPARAMS_APPLY:
578         case LFUN_LYXRC_APPLY:
579         case LFUN_NEXTBUFFER:
580         case LFUN_PREVIOUSBUFFER:
581                 // these are handled in our dispatch()
582                 break;
583
584         default:
585
586                 if (!::getStatus(cur, cmd, flag))
587                         flag = view()->getStatus(cmd);
588         }
589
590         if (!enable)
591                 flag.enabled(false);
592
593         // Can we use a readonly buffer?
594         if (buf && buf->isReadonly()
595             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
596             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
597                 flag.message(N_("Document is read-only"));
598                 flag.enabled(false);
599         }
600
601         // the default error message if we disable the command
602         if (!flag.enabled() && flag.message().empty())
603                 flag.message(N_("Command disabled"));
604
605         return flag;
606 }
607
608
609 namespace {
610
611 bool ensureBufferClean(BufferView * bv)
612 {
613         Buffer & buf = *bv->buffer();
614         if (buf.isClean())
615                 return true;
616
617         string const file = MakeDisplayPath(buf.fileName(), 30);
618         string text = bformat(_("The document %1$s has unsaved "
619                                 "changes.\n\nDo you want to save "
620                                 "the document?"), file);
621         int const ret = Alert::prompt(_("Save changed document?"),
622                                       text, 0, 1, _("&Save"),
623                                       _("&Cancel"));
624
625         if (ret == 0)
626                 bv->owner()->dispatch(FuncRequest(LFUN_MENUWRITE));
627
628         return buf.isClean();
629 }
630
631
632 void showPrintError(string const & name)
633 {
634         string str = bformat(_("Could not print the document %1$s.\n"
635                                "Check that your printer is set up correctly."),
636                              MakeDisplayPath(name, 50));
637         Alert::error(_("Print document failed"), str);
638 }
639
640
641 void loadTextclass(string const & name)
642 {
643         std::pair<bool, lyx::textclass_type> const tc_pair =
644                 textclasslist.NumberOfClass(name);
645
646         if (!tc_pair.first) {
647                 lyxerr << "Document class \"" << name
648                        << "\" does not exist."
649                        << std::endl;
650                 return;
651         }
652
653         lyx::textclass_type const tc = tc_pair.second;
654
655         if (!textclasslist[tc].load()) {
656                 string s = bformat(_("The document could not be converted\n"
657                                      "into the document class %1$s."),
658                                    textclasslist[tc].name());
659                 Alert::error(_("Could not change class"), s);
660         }
661 }
662
663
664 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
665
666 } //namespace anon
667
668
669 void LyXFunc::dispatch(FuncRequest const & cmd)
670 {
671         BOOST_ASSERT(view());
672         string const argument = cmd.argument;
673         kb_action const action = cmd.action;
674
675         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
676         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
677
678         // we have not done anything wrong yet.
679         errorstat = false;
680         dispatch_buffer.erase();
681
682         bool update = false;
683
684         FuncStatus const flag = getStatus(cmd);
685         if (!flag.enabled()) {
686                 // We cannot use this function here
687                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
688                        << lyxaction.getActionName(action)
689                        << " [" << action << "] is disabled at this location"
690                        << endl;
691                 setErrorMessage(flag.message());
692         } else {
693
694                 if (view()->available())
695                         view()->hideCursor();
696
697                 switch (action) {
698
699                 case LFUN_WORDFINDFORWARD:
700                 case LFUN_WORDFINDBACKWARD: {
701                         static string last_search;
702                         string searched_string;
703
704                         if (!argument.empty()) {
705                                 last_search = argument;
706                                 searched_string = argument;
707                         } else {
708                                 searched_string = last_search;
709                         }
710
711                         if (searched_string.empty())
712                                 break;
713
714                         bool const fw = action == LFUN_WORDFINDFORWARD;
715                         string const data =
716                                 lyx::find::find2string(searched_string, true, false, fw);
717                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
718                         break;
719                 }
720
721                 case LFUN_PREFIX:
722                         owner->message(keyseq.printOptions());
723                         break;
724
725                 case LFUN_EXEC_COMMAND:
726                         owner->getToolbars().display("minibuffer", true);
727                         owner->focus_command_buffer();
728                         break;
729
730                 case LFUN_CANCEL:
731                         keyseq.reset();
732                         meta_fake_bit = key_modifier::none;
733                         if (view()->available())
734                                 // cancel any selection
735                                 dispatch(FuncRequest(LFUN_MARK_OFF));
736                         setMessage(N_("Cancel"));
737                         break;
738
739                 case LFUN_META_FAKE:
740                         meta_fake_bit = key_modifier::alt;
741                         setMessage(keyseq.print());
742                         break;
743
744                 case LFUN_READ_ONLY_TOGGLE:
745                         if (owner->buffer()->lyxvc().inUse())
746                                 owner->buffer()->lyxvc().toggleReadOnly();
747                         else
748                                 owner->buffer()->setReadonly(
749                                         !owner->buffer()->isReadonly());
750                         break;
751
752                 // --- Menus -----------------------------------------------
753                 case LFUN_MENUNEW:
754                         menuNew(argument, false);
755                         break;
756
757                 case LFUN_MENUNEWTMPLT:
758                         menuNew(argument, true);
759                         break;
760
761                 case LFUN_CLOSEBUFFER:
762                         closeBuffer();
763                         break;
764
765                 case LFUN_MENUWRITE:
766                         if (!owner->buffer()->isUnnamed()) {
767                                 string const str = bformat(_("Saving document %1$s..."),
768                                          MakeDisplayPath(owner->buffer()->fileName()));
769                                 owner->message(str);
770                                 MenuWrite(owner->buffer());
771                                 owner->message(str + _(" done."));
772                         } else
773                                 WriteAs(owner->buffer());
774                         break;
775
776                 case LFUN_WRITEAS:
777                         WriteAs(owner->buffer(), argument);
778                         break;
779
780                 case LFUN_MENURELOAD: {
781                         string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
782                         string text = bformat(_("Any changes will be lost. Are you sure "
783                                 "you want to revert to the saved version of the document %1$s?"), file);
784                         int const ret = Alert::prompt(_("Revert to saved document?"),
785                                 text, 0, 1, _("&Revert"), _("&Cancel"));
786
787                         if (ret == 0)
788                                 view()->reload();
789                         break;
790                 }
791
792                 case LFUN_UPDATE:
793                         Exporter::Export(owner->buffer(), argument, true);
794                         view()->showErrorList(BufferFormat(*owner->buffer()));
795                         break;
796
797                 case LFUN_PREVIEW:
798                         Exporter::Preview(owner->buffer(), argument);
799                         view()->showErrorList(BufferFormat(*owner->buffer()));
800                         break;
801
802                 case LFUN_BUILDPROG:
803                         Exporter::Export(owner->buffer(), "program", true);
804                         view()->showErrorList(_("Build"));
805                         break;
806
807                 case LFUN_RUNCHKTEX:
808                         owner->buffer()->runChktex();
809                         view()->showErrorList(_("ChkTeX"));
810                         break;
811
812                 case LFUN_EXPORT:
813                         if (argument == "custom")
814                                 owner->getDialogs().show("sendto");
815                         else {
816                                 Exporter::Export(owner->buffer(), argument, false);
817                                 view()->showErrorList(BufferFormat(*owner->buffer()));
818                         }
819                         break;
820
821                 case LFUN_EXPORT_CUSTOM: {
822                         string format_name;
823                         string command = split(argument, format_name, ' ');
824                         Format const * format = formats.getFormat(format_name);
825                         if (!format) {
826                                 lyxerr << "Format \"" << format_name
827                                        << "\" not recognized!"
828                                        << std::endl;
829                                 break;
830                         }
831
832                         Buffer * buffer = owner->buffer();
833
834                         // The name of the file created by the conversion process
835                         string filename;
836
837                         // Output to filename
838                         if (format->name() == "lyx") {
839                                 string const latexname =
840                                         buffer->getLatexName(false);
841                                 filename = ChangeExtension(latexname,
842                                                            format->extension());
843                                 filename = AddName(buffer->temppath(), filename);
844
845                                 if (!buffer->writeFile(filename))
846                                         break;
847
848                         } else {
849                                 Exporter::Export(buffer, format_name, true,
850                                                  filename);
851                         }
852
853                         // Substitute $$FName for filename
854                         if (!contains(command, "$$FName"))
855                                 command = "( " + command + " ) < $$FName";
856                         command = subst(command, "$$FName", filename);
857
858                         // Execute the command in the background
859                         Systemcall call;
860                         call.startscript(Systemcall::DontWait, command);
861                         break;
862                 }
863
864                 case LFUN_PRINT: {
865                         string target;
866                         string target_name;
867                         string command = split(split(argument, target, ' '),
868                                                target_name, ' ');
869
870                         if (target.empty()
871                             || target_name.empty()
872                             || command.empty()) {
873                                 lyxerr << "Unable to parse \""
874                                        << argument << '"' << std::endl;
875                                 break;
876                         }
877                         if (target != "printer" && target != "file") {
878                                 lyxerr << "Unrecognized target \""
879                                        << target << '"' << std::endl;
880                                 break;
881                         }
882
883                         Buffer * buffer = owner->buffer();
884
885                         if (!Exporter::Export(buffer, "dvi", true)) {
886                                 showPrintError(buffer->fileName());
887                                 break;
888                         }
889
890                         // Push directory path.
891                         string const path = buffer->temppath();
892                         Path p(path);
893
894                         // there are three cases here:
895                         // 1. we print to a file
896                         // 2. we print directly to a printer
897                         // 3. we print using a spool command (print to file first)
898                         Systemcall one;
899                         int res = 0;
900                         string const dviname =
901                                 ChangeExtension(buffer->getLatexName(true),
902                                                 "dvi");
903
904                         if (target == "printer") {
905                                 if (!lyxrc.print_spool_command.empty()) {
906                                         // case 3: print using a spool
907                                         string const psname =
908                                                 ChangeExtension(dviname,".ps");
909                                         command += lyxrc.print_to_file
910                                                 + QuoteName(psname)
911                                                 + ' '
912                                                 + QuoteName(dviname);
913
914                                         string command2 =
915                                                 lyxrc.print_spool_command +' ';
916                                         if (target_name != "default") {
917                                                 command2 += lyxrc.print_spool_printerprefix
918                                                         + target_name
919                                                         + ' ';
920                                         }
921                                         command2 += QuoteName(psname);
922                                         // First run dvips.
923                                         // If successful, then spool command
924                                         res = one.startscript(
925                                                 Systemcall::Wait,
926                                                 command);
927
928                                         if (res == 0)
929                                                 res = one.startscript(
930                                                         Systemcall::DontWait,
931                                                         command2);
932                                 } else {
933                                         // case 2: print directly to a printer
934                                         res = one.startscript(
935                                                 Systemcall::DontWait,
936                                                 command + QuoteName(dviname));
937                                 }
938
939                         } else {
940                                 // case 1: print to a file
941                                 command += lyxrc.print_to_file
942                                         + QuoteName(MakeAbsPath(target_name,
943                                                                 path))
944                                         + ' '
945                                         + QuoteName(dviname);
946                                 res = one.startscript(Systemcall::DontWait,
947                                                       command);
948                         }
949
950                         if (res != 0)
951                                 showPrintError(buffer->fileName());
952                         break;
953                 }
954
955                 case LFUN_IMPORT:
956                         doImport(argument);
957                         break;
958
959                 case LFUN_QUIT:
960                         QuitLyX();
961                         break;
962
963                 case LFUN_TOCVIEW: {
964                         InsetCommandParams p("tableofcontents");
965                         string const data = InsetCommandMailer::params2string("toc", p);
966                         owner->getDialogs().show("toc", data, 0);
967                         break;
968                 }
969
970                 case LFUN_AUTOSAVE:
971                         AutoSave(view());
972                         break;
973
974                 case LFUN_RECONFIGURE:
975                         Reconfigure(view());
976                         break;
977
978                 case LFUN_HELP_OPEN: {
979                         string const arg = argument;
980                         if (arg.empty()) {
981                                 setErrorMessage(N_("Missing argument"));
982                                 break;
983                         }
984                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
985                         if (fname.empty()) {
986                                 lyxerr << "LyX: unable to find documentation file `"
987                                                          << arg << "'. Bad installation?" << endl;
988                                 break;
989                         }
990                         owner->message(bformat(_("Opening help file %1$s..."),
991                                 MakeDisplayPath(fname)));
992                         view()->loadLyXFile(fname, false);
993                         break;
994                 }
995
996                 // --- version control -------------------------------
997                 case LFUN_VC_REGISTER:
998                         if (!ensureBufferClean(view()))
999                                 break;
1000                         if (!owner->buffer()->lyxvc().inUse()) {
1001                                 owner->buffer()->lyxvc().registrer();
1002                                 view()->reload();
1003                         }
1004                         break;
1005
1006                 case LFUN_VC_CHECKIN:
1007                         if (!ensureBufferClean(view()))
1008                                 break;
1009                         if (owner->buffer()->lyxvc().inUse()
1010                                         && !owner->buffer()->isReadonly()) {
1011                                 owner->buffer()->lyxvc().checkIn();
1012                                 view()->reload();
1013                         }
1014                         break;
1015
1016                 case LFUN_VC_CHECKOUT:
1017                         if (!ensureBufferClean(view()))
1018                                 break;
1019                         if (owner->buffer()->lyxvc().inUse()
1020                                         && owner->buffer()->isReadonly()) {
1021                                 owner->buffer()->lyxvc().checkOut();
1022                                 view()->reload();
1023                         }
1024                         break;
1025
1026                 case LFUN_VC_REVERT:
1027                         owner->buffer()->lyxvc().revert();
1028                         view()->reload();
1029                         break;
1030
1031                 case LFUN_VC_UNDO:
1032                         owner->buffer()->lyxvc().undoLast();
1033                         view()->reload();
1034                         break;
1035
1036                 // --- buffers ----------------------------------------
1037                 case LFUN_SWITCHBUFFER:
1038                         view()->setBuffer(bufferlist.getBuffer(argument));
1039                         break;
1040
1041                 case LFUN_NEXTBUFFER:
1042                         view()->setBuffer(bufferlist.next(view()->buffer()));
1043                         break;
1044
1045                 case LFUN_PREVIOUSBUFFER:
1046                         view()->setBuffer(bufferlist.previous(view()->buffer()));
1047                         break;
1048
1049                 case LFUN_FILE_NEW:
1050                         NewFile(view(), argument);
1051                         break;
1052
1053                 case LFUN_FILE_OPEN:
1054                         open(argument);
1055                         break;
1056
1057                 case LFUN_DROP_LAYOUTS_CHOICE:
1058                         owner->getToolbars().openLayoutList();
1059                         break;
1060
1061                 case LFUN_MENU_OPEN_BY_NAME:
1062                         owner->getMenubar().openByName(argument);
1063                         break;
1064
1065                 // --- lyxserver commands ----------------------------
1066                 case LFUN_GETNAME:
1067                         setMessage(owner->buffer()->fileName());
1068                         lyxerr[Debug::INFO] << "FNAME["
1069                                                          << owner->buffer()->fileName()
1070                                                          << "] " << endl;
1071                         break;
1072
1073                 case LFUN_NOTIFY:
1074                         dispatch_buffer = keyseq.print();
1075                         lyxserver->notifyClient(dispatch_buffer);
1076                         break;
1077
1078                 case LFUN_GOTOFILEROW: {
1079                         string file_name;
1080                         int row;
1081                         istringstream is(argument);
1082                         is >> file_name >> row;
1083                         if (prefixIs(file_name, package().temp_dir())) {
1084                                 // Needed by inverse dvi search. If it is a file
1085                                 // in tmpdir, call the apropriated function
1086                                 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1087                         } else {
1088                                 // Must replace extension of the file to be .lyx
1089                                 // and get full path
1090                                 string const s = ChangeExtension(file_name, ".lyx");
1091                                 // Either change buffer or load the file
1092                                 if (bufferlist.exists(s)) {
1093                                         view()->setBuffer(bufferlist.getBuffer(s));
1094                                 } else {
1095                                         view()->loadLyXFile(s);
1096                                 }
1097                         }
1098
1099                         view()->setCursorFromRow(row);
1100
1101                         view()->center();
1102                         // see BufferView_pimpl::center()
1103                         view()->updateScrollbar();
1104                         break;
1105                 }
1106
1107                 case LFUN_DIALOG_SHOW: {
1108                         string const name = cmd.getArg(0);
1109                         string data = trim(cmd.argument.substr(name.size()));
1110
1111                         if (name == "character") {
1112                                 data = freefont2string();
1113                                 if (!data.empty())
1114                                         owner->getDialogs().show("character", data);
1115                         }
1116
1117                         else if (name == "latexlog") {
1118                                 pair<Buffer::LogType, string> const logfile =
1119                                         owner->buffer()->getLogName();
1120                                 switch (logfile.first) {
1121                                 case Buffer::latexlog:
1122                                         data = "latex ";
1123                                         break;
1124                                 case Buffer::buildlog:
1125                                         data = "literate ";
1126                                         break;
1127                                 }
1128                                 data += logfile.second;
1129                                 owner->getDialogs().show("log", data);
1130                         }
1131                         else if (name == "vclog") {
1132                                 string const data = "vc " +
1133                                         owner->buffer()->lyxvc().getLogFile();
1134                                 owner->getDialogs().show("log", data);
1135                         }
1136                         else
1137                                 owner->getDialogs().show(name, data);
1138                         break;
1139                 }
1140
1141                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1142                         string const name = cmd.getArg(0);
1143                         string data = trim(cmd.argument.substr(name.size()));
1144                         if (name == "bibitem" ||
1145                             name == "bibtex" ||
1146                             name == "include" ||
1147                             name == "index" ||
1148                             name == "label" ||
1149                             name == "ref" ||
1150                             name == "toc" ||
1151                             name == "url") {
1152                                 InsetCommandParams p(name);
1153                                 data = InsetCommandMailer::params2string(name, p);
1154                         } else if (name == "box") {
1155                                 // \c data == "Boxed" || "Frameless" etc
1156                                 InsetBoxParams p(data);
1157                                 data = InsetBoxMailer::params2string(p);
1158                         } else if (name == "branch") {
1159                                 InsetBranchParams p;
1160                                 data = InsetBranchMailer::params2string(p);
1161                         } else if (name == "citation") {
1162                                 InsetCommandParams p("cite");
1163                                 data = InsetCommandMailer::params2string(name, p);
1164                         } else if (name == "ert") {
1165                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1166                         } else if (name == "external") {
1167                                 InsetExternalParams p;
1168                                 Buffer const & buffer = *owner->buffer();
1169                                 data = InsetExternalMailer::params2string(p, buffer);
1170                         } else if (name == "float") {
1171                                 InsetFloatParams p;
1172                                 data = InsetFloatMailer::params2string(p);
1173                         } else if (name == "graphics") {
1174                                 InsetGraphicsParams p;
1175                                 Buffer const & buffer = *owner->buffer();
1176                                 data = InsetGraphicsMailer::params2string(p, buffer);
1177                         } else if (name == "note") {
1178                                 InsetNoteParams p;
1179                                 data = InsetNoteMailer::params2string(p);
1180                         } else if (name == "vspace") {
1181                                 VSpace space;
1182                                 data = InsetVSpaceMailer::params2string(space);
1183                         } else if (name == "wrap") {
1184                                 InsetWrapParams p;
1185                                 data = InsetWrapMailer::params2string(p);
1186                         }
1187                         owner->getDialogs().show(name, data, 0);
1188                         break;
1189                 }
1190
1191                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1192                         break;
1193
1194                 case LFUN_DIALOG_UPDATE: {
1195                         string const & name = argument;
1196                         // Can only update a dialog connected to an existing inset
1197                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1198                         if (inset) {
1199                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1200                                 inset->dispatch(view()->cursor(), fr);
1201                         } else if (name == "paragraph") {
1202                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1203                         } else if (name == "prefs") {
1204                                 owner->getDialogs().update(name, string());
1205                         }
1206                         break;
1207                 }
1208
1209                 case LFUN_DIALOG_HIDE:
1210                         Dialogs::hide(argument, 0);
1211                         break;
1212
1213                 case LFUN_DIALOG_DISCONNECT_INSET:
1214                         owner->getDialogs().disconnect(argument);
1215                         break;
1216
1217                 case LFUN_CHILDOPEN: {
1218                         string const filename =
1219                                 MakeAbsPath(argument, owner->buffer()->filePath());
1220                         setMessage(N_("Opening child document ") +
1221                                          MakeDisplayPath(filename) + "...");
1222                         view()->savePosition(0);
1223                         string const parentfilename = owner->buffer()->fileName();
1224                         if (bufferlist.exists(filename))
1225                                 view()->setBuffer(bufferlist.getBuffer(filename));
1226                         else
1227                                 view()->loadLyXFile(filename);
1228                         // Set the parent name of the child document.
1229                         // This makes insertion of citations and references in the child work,
1230                         // when the target is in the parent or another child document.
1231                         owner->buffer()->setParentName(parentfilename);
1232                         break;
1233                 }
1234
1235                 case LFUN_TOGGLECURSORFOLLOW:
1236                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1237                         break;
1238
1239                 case LFUN_KMAP_OFF:
1240                         owner->getIntl().KeyMapOn(false);
1241                         break;
1242
1243                 case LFUN_KMAP_PRIM:
1244                         owner->getIntl().KeyMapPrim();
1245                         break;
1246
1247                 case LFUN_KMAP_SEC:
1248                         owner->getIntl().KeyMapSec();
1249                         break;
1250
1251                 case LFUN_KMAP_TOGGLE:
1252                         owner->getIntl().ToggleKeyMap();
1253                         break;
1254
1255                 case LFUN_REPEAT: {
1256                         // repeat command
1257                         string countstr;
1258                         string rest = split(argument, countstr, ' ');
1259                         istringstream is(countstr);
1260                         int count = 0;
1261                         is >> count;
1262                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1263                         for (int i = 0; i < count; ++i)
1264                                 dispatch(lyxaction.lookupFunc(rest));
1265                         break;
1266                 }
1267
1268                 case LFUN_SEQUENCE: {
1269                         // argument contains ';'-terminated commands
1270                         string arg = argument;
1271                         while (!arg.empty()) {
1272                                 string first;
1273                                 arg = split(arg, first, ';');
1274                                 FuncRequest func(lyxaction.lookupFunc(first));
1275                                 func.origin = cmd.origin;
1276                                 dispatch(func);
1277                         }
1278                         break;
1279                 }
1280
1281                 case LFUN_SAVEPREFERENCES: {
1282                         Path p(package().user_support());
1283                         lyxrc.write("preferences", false);
1284                         break;
1285                 }
1286
1287                 case LFUN_SCREEN_FONT_UPDATE:
1288                         // handle the screen font changes.
1289                         lyxrc.set_font_norm_type();
1290                         lyx_gui::update_fonts();
1291                         // All visible buffers will need resize
1292                         view()->resize();
1293                         break;
1294
1295                 case LFUN_SET_COLOR: {
1296                         string lyx_name;
1297                         string const x11_name = split(argument, lyx_name, ' ');
1298                         if (lyx_name.empty() || x11_name.empty()) {
1299                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1300                                                         " <x11_name>"));
1301                                 break;
1302                         }
1303
1304                         bool const graphicsbg_changed =
1305                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1306                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1307
1308                         if (!lcolor.setColor(lyx_name, x11_name)) {
1309                                 setErrorMessage(
1310                                         bformat(_("Set-color \"%1$s\" failed "
1311                                                                 "- color is undefined or "
1312                                                                 "may not be redefined"), lyx_name));
1313                                 break;
1314                         }
1315
1316                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1317
1318                         if (graphicsbg_changed) {
1319 #ifdef WITH_WARNINGS
1320 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1321 #endif
1322 #if 0
1323                                 lyx::graphics::GCache::get().changeDisplay(true);
1324 #endif
1325                         }
1326                         break;
1327                 }
1328
1329                 case LFUN_MESSAGE:
1330                         owner->message(argument);
1331                         break;
1332
1333                 case LFUN_TOOLTIPS_TOGGLE:
1334                         owner->getDialogs().toggleTooltips();
1335                         break;
1336
1337                 case LFUN_EXTERNAL_EDIT: {
1338                         FuncRequest fr(action, argument);
1339                         InsetExternal().dispatch(view()->cursor(), fr);
1340                         break;
1341                 }
1342
1343                 case LFUN_GRAPHICS_EDIT: {
1344                         FuncRequest fr(action, argument);
1345                         InsetGraphics().dispatch(view()->cursor(), fr);
1346                         break;
1347                 }
1348
1349                 case LFUN_INSET_APPLY: {
1350                         string const name = cmd.getArg(0);
1351                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1352                         if (inset) {
1353                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1354                                 inset->dispatch(view()->cursor(), fr);
1355                         } else {
1356                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1357                                 dispatch(fr);
1358                         }
1359                         // ideally, the update flag should be set by the insets,
1360                         // but this is not possible currently
1361                         update = true;
1362                         break;
1363                 }
1364
1365                 case LFUN_ALL_INSETS_TOGGLE: {
1366                         string action;
1367                         string const name = split(argument, action, ' ');
1368                         InsetBase::Code const inset_code =
1369                                 InsetBase::translate(name);
1370
1371                         LCursor & cur = view()->cursor();
1372                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1373
1374                         InsetBase & inset = owner->buffer()->inset();
1375                         InsetIterator it  = inset_iterator_begin(inset);
1376                         InsetIterator const end = inset_iterator_end(inset);
1377                         for (; it != end; ++it) {
1378                                 if (inset_code == InsetBase::NO_CODE
1379                                     || inset_code == it->lyxCode())
1380                                         it->dispatch(cur, fr);
1381                         }
1382                         break;
1383                 }
1384
1385                 case LFUN_LANGUAGE_BUFFER: {
1386                         Buffer & buffer = *owner->buffer();
1387                         Language const * oldL = buffer.params().language;
1388                         Language const * newL = languages.getLanguage(argument);
1389                         if (!newL || oldL == newL)
1390                                 break;
1391
1392                         if (oldL->RightToLeft() == newL->RightToLeft()
1393                             && !buffer.isMultiLingual())
1394                                 buffer.changeLanguage(oldL, newL);
1395                         else
1396                                 buffer.updateDocLang(newL);
1397                         break;
1398                 }
1399
1400                 case LFUN_SAVE_AS_DEFAULT: {
1401                         string const fname =
1402                                 AddName(AddPath(package().user_support(), "templates/"),
1403                                         "defaults.lyx");
1404                         Buffer defaults(fname);
1405
1406                         istringstream ss(argument);
1407                         LyXLex lex(0,0);
1408                         lex.setStream(ss);
1409                         int const unknown_tokens = defaults.readHeader(lex);
1410
1411                         if (unknown_tokens != 0) {
1412                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1413                                        << unknown_tokens << " unknown token"
1414                                        << (unknown_tokens == 1 ? "" : "s")
1415                                        << endl;
1416                         }
1417
1418                         if (defaults.writeFile(defaults.fileName()))
1419                                 setMessage(_("Document defaults saved in ")
1420                                            + MakeDisplayPath(fname));
1421                         else
1422                                 setErrorMessage(_("Unable to save document defaults"));
1423                         break;
1424                 }
1425
1426                 case LFUN_BUFFERPARAMS_APPLY: {
1427                         biblio::CiteEngine const engine =
1428                                 owner->buffer()->params().cite_engine;
1429
1430                         istringstream ss(argument);
1431                         LyXLex lex(0,0);
1432                         lex.setStream(ss);
1433                         int const unknown_tokens =
1434                                 owner->buffer()->readHeader(lex);
1435
1436                         if (unknown_tokens != 0) {
1437                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1438                                        << unknown_tokens << " unknown token"
1439                                        << (unknown_tokens == 1 ? "" : "s")
1440                                        << endl;
1441                         }
1442                         if (engine == owner->buffer()->params().cite_engine)
1443                                 break;
1444
1445                         LCursor & cur = view()->cursor();
1446                         FuncRequest fr(LFUN_INSET_REFRESH);
1447
1448                         InsetBase & inset = owner->buffer()->inset();
1449                         InsetIterator it  = inset_iterator_begin(inset);
1450                         InsetIterator const end = inset_iterator_end(inset);
1451                         for (; it != end; ++it)
1452                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1453                                         it->dispatch(cur, fr);
1454                         break;
1455                 }
1456
1457                 case LFUN_TEXTCLASS_APPLY: {
1458                         Buffer * buffer = owner->buffer();
1459
1460                         lyx::textclass_type const old_class =
1461                                 buffer->params().textclass;
1462
1463                         loadTextclass(argument);
1464
1465                         std::pair<bool, lyx::textclass_type> const tc_pair =
1466                                 textclasslist.NumberOfClass(argument);
1467
1468                         if (!tc_pair.first)
1469                                 break;
1470
1471                         lyx::textclass_type const new_class = tc_pair.second;
1472                         if (old_class == new_class)
1473                                 // nothing to do
1474                                 break;
1475
1476                         owner->message(_("Converting document to new document class..."));
1477                         ErrorList el;
1478                         lyx::cap::SwitchBetweenClasses(
1479                                 old_class, new_class,
1480                                 buffer->paragraphs(), el);
1481
1482                         bufferErrors(*buffer, el);
1483                         view()->showErrorList(_("Class switch"));
1484                         updateCounters(*buffer);
1485                         update = true;
1486                         break;
1487                 }
1488
1489                 case LFUN_TEXTCLASS_LOAD:
1490                         loadTextclass(argument);
1491                         break;
1492
1493                 case LFUN_LYXRC_APPLY: {
1494                         LyXRC const lyxrc_orig = lyxrc;
1495
1496                         istringstream ss(argument);
1497                         bool const success = lyxrc.read(ss) == 0;
1498
1499                         if (!success) {
1500                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1501                                        << "Unable to read lyxrc data"
1502                                        << endl;
1503                                 break;
1504                         }
1505
1506                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1507                         break;
1508                 }
1509
1510                 default: {
1511                         view()->cursor().dispatch(cmd);
1512                         update |= view()->cursor().result().update();
1513                         if (!view()->cursor().result().dispatched()) {
1514                                 update |= view()->dispatch(cmd);
1515                         }
1516
1517                         break;
1518                 }
1519                 }
1520
1521                 BOOST_ASSERT(view());
1522                 if (view()->available()) {
1523                         // Redraw screen unless explicitly told otherwise.
1524                         // This also initializes the position cache for all insets
1525                         // in (at least partially) visible top-level paragraphs.
1526                         view()->update(true, update);
1527
1528                         // if we executed a mutating lfun, mark the buffer as dirty
1529                         // FIXME: Why not use flag.enabled() but call getStatus again?
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_TEX_ALLOWS_SPACES:
1985         case LyXRC::RC_UIFILE:
1986         case LyXRC::RC_USER_EMAIL:
1987         case LyXRC::RC_USER_NAME:
1988         case LyXRC::RC_USETEMPDIR:
1989         case LyXRC::RC_USE_ALT_LANG:
1990         case LyXRC::RC_USE_ESC_CHARS:
1991         case LyXRC::RC_USE_INP_ENC:
1992         case LyXRC::RC_USE_PERS_DICT:
1993         case LyXRC::RC_USE_SPELL_LIB:
1994         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1995         case LyXRC::RC_VIEWER:
1996         case LyXRC::RC_WHEEL_JUMP:
1997         case LyXRC::RC_LAST:
1998                 break;
1999         }
2000 }
2001
2002 } // namespace anon