]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
ff4133515488eec3bd9815817670cf758baf0fe1
[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/insetinclude.h"
68 #include "insets/insetnote.h"
69 #include "insets/insettabular.h"
70 #include "insets/insetvspace.h"
71 #include "insets/insetwrap.h"
72
73 #include "frontends/Alert.h"
74 #include "frontends/Dialogs.h"
75 #include "frontends/FileDialog.h"
76 #include "frontends/lyx_gui.h"
77 #include "frontends/LyXKeySym.h"
78 #include "frontends/LyXView.h"
79 #include "frontends/Menubar.h"
80 #include "frontends/Toolbars.h"
81
82 #include "support/environment.h"
83 #include "support/filefilterlist.h"
84 #include "support/filetools.h"
85 #include "support/forkedcontr.h"
86 #include "support/fs_extras.h"
87 #include "support/lstrings.h"
88 #include "support/path.h"
89 #include "support/package.h"
90 #include "support/systemcall.h"
91 #include "support/convert.h"
92 #include "support/os.h"
93
94 #include <boost/current_function.hpp>
95 #include <boost/filesystem/operations.hpp>
96
97 #include <sstream>
98
99 using bv_funcs::freefont2string;
100
101 using lyx::support::AbsolutePath;
102 using lyx::support::AddName;
103 using lyx::support::AddPath;
104 using lyx::support::bformat;
105 using lyx::support::ChangeExtension;
106 using lyx::support::contains;
107 using lyx::support::FileFilterList;
108 using lyx::support::FileSearch;
109 using lyx::support::ForkedcallsController;
110 using lyx::support::i18nLibFileSearch;
111 using lyx::support::IsDirWriteable;
112 using lyx::support::IsFileReadable;
113 using lyx::support::isStrInt;
114 using lyx::support::MakeAbsPath;
115 using lyx::support::MakeDisplayPath;
116 using lyx::support::package;
117 using lyx::support::Path;
118 using lyx::support::QuoteName;
119 using lyx::support::rtrim;
120 using lyx::support::split;
121 using lyx::support::subst;
122 using lyx::support::Systemcall;
123 using lyx::support::token;
124 using lyx::support::trim;
125 using lyx::support::prefixIs;
126
127 using std::endl;
128 using std::make_pair;
129 using std::pair;
130 using std::string;
131 using std::istringstream;
132
133 namespace biblio = lyx::biblio;
134 namespace fs = boost::filesystem;
135
136
137 extern BufferList bufferlist;
138 extern LyXServer * lyxserver;
139
140 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
141
142 // (alkis)
143 extern tex_accent_struct get_accent(kb_action action);
144
145
146 namespace {
147
148 bool getStatus(LCursor cursor,
149                FuncRequest const & cmd, FuncStatus & status)
150 {
151         // Try to fix cursor in case it is broken.
152         cursor.fixIfBroken();
153
154         // This is, of course, a mess. Better create a new doc iterator and use
155         // this in Inset::getStatus. This might require an additional
156         // BufferView * arg, though (which should be avoided)
157         //LCursor safe = *this;
158         bool res = false;
159         for ( ; cursor.depth(); cursor.pop()) {
160                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
161                 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
162                 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
163                 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
164
165                 // The inset's getStatus() will return 'true' if it made
166                 // a definitive decision on whether it want to handle the
167                 // request or not. The result of this decision is put into
168                 // the 'status' parameter.
169                 if (cursor.inset().getStatus(cursor, cmd, status)) {
170                         res = true;
171                         break;
172                 }
173         }
174         return res;
175 }
176
177 }
178
179 LyXFunc::LyXFunc(LyXView * lv)
180         : owner(lv),
181         encoded_last_key(0),
182         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
183         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
184         meta_fake_bit(key_modifier::none)
185 {
186 }
187
188
189 void LyXFunc::handleKeyFunc(kb_action action)
190 {
191         char c = encoded_last_key;
192
193         if (keyseq.length()) {
194                 c = 0;
195         }
196
197         owner->getIntl().getTransManager()
198                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
199         // Need to clear, in case the minibuffer calls these
200         // actions
201         keyseq.clear();
202         // copied verbatim from do_accent_char
203         view()->cursor().resetAnchor();
204         view()->update();
205 }
206
207
208 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
209 {
210         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
211
212         // Do nothing if we have nothing (JMarc)
213         if (!keysym->isOK()) {
214                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
215                                    << endl;
216                 return;
217         }
218
219         if (keysym->isModifier()) {
220                 lyxerr[Debug::KEY] << "isModifier true" << endl;
221                 return;
222         }
223
224         Encoding const * encoding = view()->cursor().getEncoding();
225
226         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
227
228         // Do a one-deep top-level lookup for
229         // cancel and meta-fake keys. RVDK_PATCH_5
230         cancel_meta_seq.reset();
231
232         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
233         lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
234                            << " action first set to [" << func.action << ']'
235                            << endl;
236
237         // When not cancel or meta-fake, do the normal lookup.
238         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
239         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
240         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
241                 // remove Caps Lock and Mod2 as a modifiers
242                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
243                 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
244                                    << "action now set to ["
245                                    << func.action << ']' << endl;
246         }
247
248         // Dont remove this unless you know what you are doing.
249         meta_fake_bit = key_modifier::none;
250
251         // Can this happen now ?
252         if (func.action == LFUN_NOACTION) {
253                 func = FuncRequest(LFUN_PREFIX);
254         }
255
256         if (lyxerr.debugging(Debug::KEY)) {
257                 lyxerr << BOOST_CURRENT_FUNCTION
258                        << " Key [action="
259                        << func.action << "]["
260                        << keyseq.print() << ']'
261                        << endl;
262         }
263
264         // already here we know if it any point in going further
265         // why not return already here if action == -1 and
266         // num_bytes == 0? (Lgb)
267
268         if (keyseq.length() > 1) {
269                 owner->message(keyseq.print());
270         }
271
272
273         // Maybe user can only reach the key via holding down shift.
274         // Let's see. But only if shift is the only modifier
275         if (func.action == LFUN_UNKNOWN_ACTION &&
276             state == key_modifier::shift) {
277                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
278                 func = keyseq.addkey(keysym, key_modifier::none);
279                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
280         }
281
282         if (func.action == LFUN_UNKNOWN_ACTION) {
283                 // Hmm, we didn't match any of the keysequences. See
284                 // if it's normal insertable text not already covered
285                 // by a binding
286                 if (keysym->isText() && keyseq.length() == 1) {
287                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
288                         func = FuncRequest(LFUN_SELFINSERT);
289                 } else {
290                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
291                         owner->message(_("Unknown function."));
292                         return;
293                 }
294         }
295
296         if (func.action == LFUN_SELFINSERT) {
297                 if (encoded_last_key != 0) {
298                         string const arg(1, encoded_last_key);
299                         dispatch(FuncRequest(LFUN_SELFINSERT, arg));
300                         lyxerr[Debug::KEY]
301                                 << "SelfInsert arg[`" << arg << "']" << endl;
302                 }
303         } else {
304                 dispatch(func);
305         }
306 }
307
308
309 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
310 {
311         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
312         FuncStatus flag;
313         LCursor & cur = view()->cursor();
314
315         /* In LyX/Mac, when a dialog is open, the menus of the
316            application can still be accessed without giving focus to
317            the main window. In this case, we want to disable the menu
318            entries that are buffer-related.
319         */
320         Buffer * buf;
321         if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
322                 buf = 0;
323         else
324                 buf = owner->buffer();
325
326         if (cmd.action == LFUN_NOACTION) {
327                 flag.message(N_("Nothing to do"));
328                 flag.enabled(false);
329                 return flag;
330         }
331
332         switch (cmd.action) {
333         case LFUN_UNKNOWN_ACTION:
334 #ifndef HAVE_LIBAIKSAURUS
335         case LFUN_THESAURUS_ENTRY:
336 #endif
337                 flag.unknown(true);
338                 flag.enabled(false);
339                 break;
340         default:
341                 flag |= lyx_gui::getStatus(cmd);
342         }
343
344         if (flag.unknown()) {
345                 flag.message(N_("Unknown action"));
346                 return flag;
347         }
348
349         if (!flag.enabled()) {
350                 if (flag.message().empty())
351                         flag.message(N_("Command disabled"));
352                 return flag;
353         }
354
355         // Check whether we need a buffer
356         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
357                 // no, exit directly
358                 flag.message(N_("Command not allowed with"
359                                     "out any document open"));
360                 flag.enabled(false);
361                 return flag;
362         }
363
364         // I would really like to avoid having this switch and rather try to
365         // encode this in the function itself.
366         // -- And I'd rather let an inset decide which LFUNs it is willing
367         // to handle (Andre')
368         bool enable = true;
369         switch (cmd.action) {
370         case LFUN_TOOLTIPS_TOGGLE:
371                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
372                 break;
373
374         case LFUN_READ_ONLY_TOGGLE:
375                 flag.setOnOff(buf->isReadonly());
376                 break;
377
378         case LFUN_SWITCHBUFFER:
379                 // toggle on the current buffer, but do not toggle off
380                 // the other ones (is that a good idea?)
381                 if (cmd.argument == buf->fileName())
382                         flag.setOnOff(true);
383                 break;
384
385         case LFUN_EXPORT:
386                 enable = cmd.argument == "custom"
387                         || Exporter::IsExportable(*buf, cmd.argument);
388                 break;
389         case LFUN_CUT:
390         case LFUN_COPY:
391                 enable = cur.selection();
392                 break;
393
394         case LFUN_RUNCHKTEX:
395                 enable = buf->isLatex() && lyxrc.chktex_command != "none";
396                 break;
397
398         case LFUN_BUILDPROG:
399                 enable = Exporter::IsExportable(*buf, "program");
400                 break;
401
402         case LFUN_LAYOUT_TABULAR:
403                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
404                 break;
405
406         case LFUN_LAYOUT:
407         case LFUN_LAYOUT_PARAGRAPH:
408                 enable = !cur.inset().forceDefaultParagraphs(&cur.inset());
409                 break;
410
411         case LFUN_VC_REGISTER:
412                 enable = !buf->lyxvc().inUse();
413                 break;
414         case LFUN_VC_CHECKIN:
415                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
416                 break;
417         case LFUN_VC_CHECKOUT:
418                 enable = buf->lyxvc().inUse() && buf->isReadonly();
419                 break;
420         case LFUN_VC_REVERT:
421         case LFUN_VC_UNDO:
422                 enable = buf->lyxvc().inUse();
423                 break;
424         case LFUN_MENURELOAD:
425                 enable = !buf->isUnnamed() && !buf->isClean();
426                 break;
427
428         case LFUN_INSET_SETTINGS: {
429                 enable = false;
430                 if (!cur)
431                         break;
432                 InsetBase::Code code = cur.inset().lyxCode();
433                 switch (code) {
434                         case InsetBase::TABULAR_CODE:
435                                 enable = cmd.argument == "tabular";
436                                 break;
437                         case InsetBase::ERT_CODE:
438                                 enable = cmd.argument == "ert";
439                                 break;
440                         case InsetBase::FLOAT_CODE:
441                                 enable = cmd.argument == "float";
442                                 break;
443                         case InsetBase::WRAP_CODE:
444                                 enable = cmd.argument == "wrap";
445                                 break;
446                         case InsetBase::NOTE_CODE:
447                                 enable = cmd.argument == "note";
448                                 break;
449                         case InsetBase::BRANCH_CODE:
450                                 enable = cmd.argument == "branch";
451                                 break;
452                         case InsetBase::BOX_CODE:
453                                 enable = cmd.argument == "box";
454                                 break;
455                         default:
456                                 break;
457                 }
458                 break;
459         }
460
461         case LFUN_INSET_APPLY: {
462                 string const name = cmd.getArg(0);
463                 InsetBase * inset = owner->getDialogs().getOpenInset(name);
464                 if (inset) {
465                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
466                         FuncStatus fs;
467                         bool const success = inset->getStatus(cur, fr, fs);
468                         // Every inset is supposed to handle this
469                         BOOST_ASSERT(success);
470                         flag |= fs;
471                 } else {
472                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument);
473                         flag |= getStatus(fr);
474                 }
475                 enable = flag.enabled();
476                 break;
477         }
478
479         case LFUN_DIALOG_SHOW: {
480                 string const name = cmd.getArg(0);
481                 if (!buf)
482                         enable = name == "aboutlyx"
483                                 || name == "file"
484                                 || name == "forks"
485                                 || name == "prefs"
486                                 || name == "texinfo";
487                 else if (name == "print")
488                         enable = Exporter::IsExportable(*buf, "dvi")
489                                 && lyxrc.print_command != "none";
490                 else if (name == "character" || name == "mathpanel")
491                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
492                 else if (name == "latexlog")
493                         enable = IsFileReadable(buf->getLogName().second);
494 #if !defined (USE_ASPELL) && !defined (USE_ISPELL) && !defined (USE_PSPELL)
495                 else if (name == "spellchecker")
496                         enable = false;
497 #endif
498                 else if (name == "vclog")
499                         enable = buf->lyxvc().inUse();
500                 break;
501         }
502
503         case LFUN_DIALOG_SHOW_NEW_INSET:
504                 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
505                 break;
506
507         case LFUN_DIALOG_UPDATE: {
508                 string const name = cmd.getArg(0);
509                 if (!buf)
510                         enable = name == "prefs";
511                 break;
512         }
513
514         // this one is difficult to get right. As a half-baked
515         // solution, we consider only the first action of the sequence
516         case LFUN_SEQUENCE: {
517                 // argument contains ';'-terminated commands
518                 string const firstcmd = token(cmd.argument, ';', 0);
519                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
520                 func.origin = cmd.origin;
521                 flag = getStatus(func);
522         }
523
524         case LFUN_MENUNEW:
525         case LFUN_MENUNEWTMPLT:
526         case LFUN_WORDFINDFORWARD:
527         case LFUN_WORDFINDBACKWARD:
528         case LFUN_PREFIX:
529         case LFUN_EXEC_COMMAND:
530         case LFUN_CANCEL:
531         case LFUN_META_FAKE:
532         case LFUN_CLOSEBUFFER:
533         case LFUN_MENUWRITE:
534         case LFUN_WRITEAS:
535         case LFUN_UPDATE:
536         case LFUN_PREVIEW:
537         case LFUN_IMPORT:
538         case LFUN_QUIT:
539         case LFUN_TOCVIEW:
540         case LFUN_AUTOSAVE:
541         case LFUN_RECONFIGURE:
542         case LFUN_HELP_OPEN:
543         case LFUN_FILE_NEW:
544         case LFUN_FILE_OPEN:
545         case LFUN_DROP_LAYOUTS_CHOICE:
546         case LFUN_MENU_OPEN_BY_NAME:
547         case LFUN_GETNAME:
548         case LFUN_NOTIFY:
549         case LFUN_GOTOFILEROW:
550         case LFUN_DIALOG_SHOW_NEXT_INSET:
551         case LFUN_DIALOG_HIDE:
552         case LFUN_DIALOG_DISCONNECT_INSET:
553         case LFUN_CHILDOPEN:
554         case LFUN_TOGGLECURSORFOLLOW:
555         case LFUN_KMAP_OFF:
556         case LFUN_KMAP_PRIM:
557         case LFUN_KMAP_SEC:
558         case LFUN_KMAP_TOGGLE:
559         case LFUN_REPEAT:
560         case LFUN_EXPORT_CUSTOM:
561         case LFUN_PRINT:
562         case LFUN_SAVEPREFERENCES:
563         case LFUN_SCREEN_FONT_UPDATE:
564         case LFUN_SET_COLOR:
565         case LFUN_MESSAGE:
566         case LFUN_EXTERNAL_EDIT:
567         case LFUN_GRAPHICS_EDIT:
568         case LFUN_ALL_INSETS_TOGGLE:
569         case LFUN_LANGUAGE_BUFFER:
570         case LFUN_TEXTCLASS_APPLY:
571         case LFUN_TEXTCLASS_LOAD:
572         case LFUN_SAVE_AS_DEFAULT:
573         case LFUN_BUFFERPARAMS_APPLY:
574         case LFUN_LYXRC_APPLY:
575         case LFUN_NEXTBUFFER:
576         case LFUN_PREVIOUSBUFFER:
577                 // these are handled in our dispatch()
578                 break;
579
580         default:
581
582                 if (!::getStatus(cur, cmd, flag))
583                         flag = view()->getStatus(cmd);
584         }
585
586         if (!enable)
587                 flag.enabled(false);
588
589         // Can we use a readonly buffer?
590         if (buf && buf->isReadonly()
591             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
592             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
593                 flag.message(N_("Document is read-only"));
594                 flag.enabled(false);
595         }
596
597         // the default error message if we disable the command
598         if (!flag.enabled() && flag.message().empty())
599                 flag.message(N_("Command disabled"));
600
601         return flag;
602 }
603
604
605 namespace {
606
607 bool ensureBufferClean(BufferView * bv)
608 {
609         Buffer & buf = *bv->buffer();
610         if (buf.isClean())
611                 return true;
612
613         string const file = MakeDisplayPath(buf.fileName(), 30);
614         string text = bformat(_("The document %1$s has unsaved "
615                                 "changes.\n\nDo you want to save "
616                                 "the document?"), file);
617         int const ret = Alert::prompt(_("Save changed document?"),
618                                       text, 0, 1, _("&Save"),
619                                       _("&Cancel"));
620
621         if (ret == 0)
622                 bv->owner()->dispatch(FuncRequest(LFUN_MENUWRITE));
623
624         return buf.isClean();
625 }
626
627
628 void showPrintError(string const & name)
629 {
630         string str = bformat(_("Could not print the document %1$s.\n"
631                                "Check that your printer is set up correctly."),
632                              MakeDisplayPath(name, 50));
633         Alert::error(_("Print document failed"), str);
634 }
635
636
637 void loadTextclass(string const & name)
638 {
639         std::pair<bool, lyx::textclass_type> const tc_pair =
640                 textclasslist.NumberOfClass(name);
641
642         if (!tc_pair.first) {
643                 lyxerr << "Document class \"" << name
644                        << "\" does not exist."
645                        << std::endl;
646                 return;
647         }
648
649         lyx::textclass_type const tc = tc_pair.second;
650
651         if (!textclasslist[tc].load()) {
652                 string s = bformat(_("The document could not be converted\n"
653                                      "into the document class %1$s."),
654                                    textclasslist[tc].name());
655                 Alert::error(_("Could not change class"), s);
656         }
657 }
658
659
660 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
661
662 } //namespace anon
663
664
665 void LyXFunc::dispatch(FuncRequest const & cmd)
666 {
667         BOOST_ASSERT(view());
668         string const argument = cmd.argument;
669         kb_action const action = cmd.action;
670
671         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
672         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
673
674         // we have not done anything wrong yet.
675         errorstat = false;
676         dispatch_buffer.erase();
677
678         bool update = false;
679
680         FuncStatus const flag = getStatus(cmd);
681         if (!flag.enabled()) {
682                 // We cannot use this function here
683                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
684                        << lyxaction.getActionName(action)
685                        << " [" << action << "] is disabled at this location"
686                        << endl;
687                 setErrorMessage(flag.message());
688         } else {
689
690                 if (view()->available())
691                         view()->hideCursor();
692
693                 switch (action) {
694
695                 case LFUN_WORDFINDFORWARD:
696                 case LFUN_WORDFINDBACKWARD: {
697                         static string last_search;
698                         string searched_string;
699
700                         if (!argument.empty()) {
701                                 last_search = argument;
702                                 searched_string = argument;
703                         } else {
704                                 searched_string = last_search;
705                         }
706
707                         if (searched_string.empty())
708                                 break;
709
710                         bool const fw = action == LFUN_WORDFINDFORWARD;
711                         string const data =
712                                 lyx::find::find2string(searched_string, true, false, fw);
713                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
714                         break;
715                 }
716
717                 case LFUN_PREFIX:
718                         owner->message(keyseq.printOptions());
719                         break;
720
721                 case LFUN_EXEC_COMMAND:
722                         owner->getToolbars().display("minibuffer", true);
723                         owner->focus_command_buffer();
724                         break;
725
726                 case LFUN_CANCEL:
727                         keyseq.reset();
728                         meta_fake_bit = key_modifier::none;
729                         if (view()->available())
730                                 // cancel any selection
731                                 dispatch(FuncRequest(LFUN_MARK_OFF));
732                         setMessage(N_("Cancel"));
733                         break;
734
735                 case LFUN_META_FAKE:
736                         meta_fake_bit = key_modifier::alt;
737                         setMessage(keyseq.print());
738                         break;
739
740                 case LFUN_READ_ONLY_TOGGLE:
741                         if (owner->buffer()->lyxvc().inUse())
742                                 owner->buffer()->lyxvc().toggleReadOnly();
743                         else
744                                 owner->buffer()->setReadonly(
745                                         !owner->buffer()->isReadonly());
746                         break;
747
748                 // --- Menus -----------------------------------------------
749                 case LFUN_MENUNEW:
750                         menuNew(argument, false);
751                         break;
752
753                 case LFUN_MENUNEWTMPLT:
754                         menuNew(argument, true);
755                         break;
756
757                 case LFUN_CLOSEBUFFER:
758                         closeBuffer();
759                         break;
760
761                 case LFUN_MENUWRITE:
762                         if (!owner->buffer()->isUnnamed()) {
763                                 string const str = bformat(_("Saving document %1$s..."),
764                                          MakeDisplayPath(owner->buffer()->fileName()));
765                                 owner->message(str);
766                                 MenuWrite(owner->buffer());
767                                 owner->message(str + _(" done."));
768                         } else
769                                 WriteAs(owner->buffer());
770                         break;
771
772                 case LFUN_WRITEAS:
773                         WriteAs(owner->buffer(), argument);
774                         break;
775
776                 case LFUN_MENURELOAD: {
777                         string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
778                         string text = bformat(_("Any changes will be lost. Are you sure "
779                                 "you want to revert to the saved version of the document %1$s?"), file);
780                         int const ret = Alert::prompt(_("Revert to saved document?"),
781                                 text, 0, 1, _("&Revert"), _("&Cancel"));
782
783                         if (ret == 0)
784                                 view()->reload();
785                         break;
786                 }
787
788                 case LFUN_UPDATE:
789                         Exporter::Export(owner->buffer(), argument, true);
790                         view()->showErrorList(BufferFormat(*owner->buffer()));
791                         break;
792
793                 case LFUN_PREVIEW:
794                         Exporter::Preview(owner->buffer(), argument);
795                         view()->showErrorList(BufferFormat(*owner->buffer()));
796                         break;
797
798                 case LFUN_BUILDPROG:
799                         Exporter::Export(owner->buffer(), "program", true);
800                         view()->showErrorList(_("Build"));
801                         break;
802
803                 case LFUN_RUNCHKTEX:
804                         owner->buffer()->runChktex();
805                         view()->showErrorList(_("ChkTeX"));
806                         break;
807
808                 case LFUN_EXPORT:
809                         if (argument == "custom")
810                                 owner->getDialogs().show("sendto");
811                         else {
812                                 Exporter::Export(owner->buffer(), argument, false);
813                                 view()->showErrorList(BufferFormat(*owner->buffer()));
814                         }
815                         break;
816
817                 case LFUN_EXPORT_CUSTOM: {
818                         string format_name;
819                         string command = split(argument, format_name, ' ');
820                         Format const * format = formats.getFormat(format_name);
821                         if (!format) {
822                                 lyxerr << "Format \"" << format_name
823                                        << "\" not recognized!"
824                                        << std::endl;
825                                 break;
826                         }
827
828                         Buffer * buffer = owner->buffer();
829
830                         // The name of the file created by the conversion process
831                         string filename;
832
833                         // Output to filename
834                         if (format->name() == "lyx") {
835                                 string const latexname =
836                                         buffer->getLatexName(false);
837                                 filename = ChangeExtension(latexname,
838                                                            format->extension());
839                                 filename = AddName(buffer->temppath(), filename);
840
841                                 if (!buffer->writeFile(filename))
842                                         break;
843
844                         } else {
845                                 Exporter::Export(buffer, format_name, true,
846                                                  filename);
847                         }
848
849                         // Substitute $$FName for filename
850                         if (!contains(command, "$$FName"))
851                                 command = "( " + command + " ) < $$FName";
852                         command = subst(command, "$$FName", filename);
853
854                         // Execute the command in the background
855                         Systemcall call;
856                         call.startscript(Systemcall::DontWait, command);
857                         break;
858                 }
859
860                 case LFUN_PRINT: {
861                         string target;
862                         string target_name;
863                         string command = split(split(argument, target, ' '),
864                                                target_name, ' ');
865
866                         if (target.empty()
867                             || target_name.empty()
868                             || command.empty()) {
869                                 lyxerr << "Unable to parse \""
870                                        << argument << '"' << std::endl;
871                                 break;
872                         }
873                         if (target != "printer" && target != "file") {
874                                 lyxerr << "Unrecognized target \""
875                                        << target << '"' << std::endl;
876                                 break;
877                         }
878
879                         Buffer * buffer = owner->buffer();
880
881                         if (!Exporter::Export(buffer, "dvi", true)) {
882                                 showPrintError(buffer->fileName());
883                                 break;
884                         }
885
886                         // Push directory path.
887                         string const path = buffer->temppath();
888                         Path p(path);
889
890                         // there are three cases here:
891                         // 1. we print to a file
892                         // 2. we print directly to a printer
893                         // 3. we print using a spool command (print to file first)
894                         Systemcall one;
895                         int res = 0;
896                         string const dviname =
897                                 ChangeExtension(buffer->getLatexName(true),
898                                                 "dvi");
899
900                         if (target == "printer") {
901                                 if (!lyxrc.print_spool_command.empty()) {
902                                         // case 3: print using a spool
903                                         string const psname =
904                                                 ChangeExtension(dviname,".ps");
905                                         command += lyxrc.print_to_file
906                                                 + QuoteName(psname)
907                                                 + ' '
908                                                 + QuoteName(dviname);
909
910                                         string command2 =
911                                                 lyxrc.print_spool_command +' ';
912                                         if (target_name != "default") {
913                                                 command2 += lyxrc.print_spool_printerprefix
914                                                         + target_name
915                                                         + ' ';
916                                         }
917                                         command2 += QuoteName(psname);
918                                         // First run dvips.
919                                         // If successful, then spool command
920                                         res = one.startscript(
921                                                 Systemcall::Wait,
922                                                 command);
923
924                                         if (res == 0)
925                                                 res = one.startscript(
926                                                         Systemcall::DontWait,
927                                                         command2);
928                                 } else {
929                                         // case 2: print directly to a printer
930                                         res = one.startscript(
931                                                 Systemcall::DontWait,
932                                                 command + QuoteName(dviname));
933                                 }
934
935                         } else {
936                                 // case 1: print to a file
937                                 command += lyxrc.print_to_file
938                                         + QuoteName(MakeAbsPath(target_name,
939                                                                 path))
940                                         + ' '
941                                         + QuoteName(dviname);
942                                 res = one.startscript(Systemcall::DontWait,
943                                                       command);
944                         }
945
946                         if (res != 0)
947                                 showPrintError(buffer->fileName());
948                         break;
949                 }
950
951                 case LFUN_IMPORT:
952                         doImport(argument);
953                         break;
954
955                 case LFUN_QUIT:
956                         QuitLyX(argument == "force");
957                         break;
958
959                 case LFUN_TOCVIEW: {
960                         InsetCommandParams p("tableofcontents");
961                         string const data = InsetCommandMailer::params2string("toc", p);
962                         owner->getDialogs().show("toc", data, 0);
963                         break;
964                 }
965
966                 case LFUN_AUTOSAVE:
967                         AutoSave(view());
968                         break;
969
970                 case LFUN_RECONFIGURE:
971                         Reconfigure(view());
972                         break;
973
974                 case LFUN_HELP_OPEN: {
975                         string const arg = argument;
976                         if (arg.empty()) {
977                                 setErrorMessage(N_("Missing argument"));
978                                 break;
979                         }
980                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
981                         if (fname.empty()) {
982                                 lyxerr << "LyX: unable to find documentation file `"
983                                                          << arg << "'. Bad installation?" << endl;
984                                 break;
985                         }
986                         owner->message(bformat(_("Opening help file %1$s..."),
987                                 MakeDisplayPath(fname)));
988                         view()->loadLyXFile(fname, false);
989                         break;
990                 }
991
992                 // --- version control -------------------------------
993                 case LFUN_VC_REGISTER:
994                         if (!ensureBufferClean(view()))
995                                 break;
996                         if (!owner->buffer()->lyxvc().inUse()) {
997                                 owner->buffer()->lyxvc().registrer();
998                                 view()->reload();
999                         }
1000                         break;
1001
1002                 case LFUN_VC_CHECKIN:
1003                         if (!ensureBufferClean(view()))
1004                                 break;
1005                         if (owner->buffer()->lyxvc().inUse()
1006                                         && !owner->buffer()->isReadonly()) {
1007                                 owner->buffer()->lyxvc().checkIn();
1008                                 view()->reload();
1009                         }
1010                         break;
1011
1012                 case LFUN_VC_CHECKOUT:
1013                         if (!ensureBufferClean(view()))
1014                                 break;
1015                         if (owner->buffer()->lyxvc().inUse()
1016                                         && owner->buffer()->isReadonly()) {
1017                                 owner->buffer()->lyxvc().checkOut();
1018                                 view()->reload();
1019                         }
1020                         break;
1021
1022                 case LFUN_VC_REVERT:
1023                         owner->buffer()->lyxvc().revert();
1024                         view()->reload();
1025                         break;
1026
1027                 case LFUN_VC_UNDO:
1028                         owner->buffer()->lyxvc().undoLast();
1029                         view()->reload();
1030                         break;
1031
1032                 // --- buffers ----------------------------------------
1033                 case LFUN_SWITCHBUFFER:
1034                         view()->setBuffer(bufferlist.getBuffer(argument));
1035                         break;
1036
1037                 case LFUN_NEXTBUFFER:
1038                         view()->setBuffer(bufferlist.next(view()->buffer()));
1039                         break;
1040
1041                 case LFUN_PREVIOUSBUFFER:
1042                         view()->setBuffer(bufferlist.previous(view()->buffer()));
1043                         break;
1044
1045                 case LFUN_FILE_NEW:
1046                         NewFile(view(), argument);
1047                         break;
1048
1049                 case LFUN_FILE_OPEN:
1050                         open(argument);
1051                         break;
1052
1053                 case LFUN_DROP_LAYOUTS_CHOICE:
1054                         owner->getToolbars().openLayoutList();
1055                         break;
1056
1057                 case LFUN_MENU_OPEN_BY_NAME:
1058                         owner->getMenubar().openByName(argument);
1059                         break;
1060
1061                 // --- lyxserver commands ----------------------------
1062                 case LFUN_GETNAME:
1063                         setMessage(owner->buffer()->fileName());
1064                         lyxerr[Debug::INFO] << "FNAME["
1065                                                          << owner->buffer()->fileName()
1066                                                          << "] " << endl;
1067                         break;
1068
1069                 case LFUN_NOTIFY:
1070                         dispatch_buffer = keyseq.print();
1071                         lyxserver->notifyClient(dispatch_buffer);
1072                         break;
1073
1074                 case LFUN_GOTOFILEROW: {
1075                         string file_name;
1076                         int row;
1077                         istringstream is(argument);
1078                         is >> file_name >> row;
1079                         if (prefixIs(file_name, package().temp_dir())) {
1080                                 // Needed by inverse dvi search. If it is a file
1081                                 // in tmpdir, call the apropriated function
1082                                 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1083                         } else {
1084                                 // Must replace extension of the file to be .lyx
1085                                 // and get full path
1086                                 string const s = ChangeExtension(file_name, ".lyx");
1087                                 // Either change buffer or load the file
1088                                 if (bufferlist.exists(s)) {
1089                                         view()->setBuffer(bufferlist.getBuffer(s));
1090                                 } else {
1091                                         view()->loadLyXFile(s);
1092                                 }
1093                         }
1094
1095                         view()->setCursorFromRow(row);
1096
1097                         view()->center();
1098                         // see BufferView_pimpl::center()
1099                         view()->updateScrollbar();
1100                         break;
1101                 }
1102
1103                 case LFUN_DIALOG_SHOW: {
1104                         string const name = cmd.getArg(0);
1105                         string data = trim(cmd.argument.substr(name.size()));
1106
1107                         if (name == "character") {
1108                                 data = freefont2string();
1109                                 if (!data.empty())
1110                                         owner->getDialogs().show("character", data);
1111                         }
1112
1113                         else if (name == "latexlog") {
1114                                 pair<Buffer::LogType, string> const logfile =
1115                                         owner->buffer()->getLogName();
1116                                 switch (logfile.first) {
1117                                 case Buffer::latexlog:
1118                                         data = "latex ";
1119                                         break;
1120                                 case Buffer::buildlog:
1121                                         data = "literate ";
1122                                         break;
1123                                 }
1124                                 data += logfile.second;
1125                                 owner->getDialogs().show("log", data);
1126                         }
1127                         else if (name == "vclog") {
1128                                 string const data = "vc " +
1129                                         owner->buffer()->lyxvc().getLogFile();
1130                                 owner->getDialogs().show("log", data);
1131                         }
1132                         else
1133                                 owner->getDialogs().show(name, data);
1134                         break;
1135                 }
1136
1137                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1138                         string const name = cmd.getArg(0);
1139                         string data = trim(cmd.argument.substr(name.size()));
1140                         if (name == "bibitem" ||
1141                             name == "bibtex" ||
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 == "include") {
1150                                 InsetCommandParams p(data);
1151                                 data = InsetIncludeMailer::params2string(p);
1152                         } else if (name == "box") {
1153                                 // \c data == "Boxed" || "Frameless" etc
1154                                 InsetBoxParams p(data);
1155                                 data = InsetBoxMailer::params2string(p);
1156                         } else if (name == "branch") {
1157                                 InsetBranchParams p;
1158                                 data = InsetBranchMailer::params2string(p);
1159                         } else if (name == "citation") {
1160                                 InsetCommandParams p("cite");
1161                                 data = InsetCommandMailer::params2string(name, p);
1162                         } else if (name == "ert") {
1163                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1164                         } else if (name == "external") {
1165                                 InsetExternalParams p;
1166                                 Buffer const & buffer = *owner->buffer();
1167                                 data = InsetExternalMailer::params2string(p, buffer);
1168                         } else if (name == "float") {
1169                                 InsetFloatParams p;
1170                                 data = InsetFloatMailer::params2string(p);
1171                         } else if (name == "graphics") {
1172                                 InsetGraphicsParams p;
1173                                 Buffer const & buffer = *owner->buffer();
1174                                 data = InsetGraphicsMailer::params2string(p, buffer);
1175                         } else if (name == "note") {
1176                                 InsetNoteParams p;
1177                                 data = InsetNoteMailer::params2string(p);
1178                         } else if (name == "vspace") {
1179                                 VSpace space;
1180                                 data = InsetVSpaceMailer::params2string(space);
1181                         } else if (name == "wrap") {
1182                                 InsetWrapParams p;
1183                                 data = InsetWrapMailer::params2string(p);
1184                         }
1185                         owner->getDialogs().show(name, data, 0);
1186                         break;
1187                 }
1188
1189                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1190                         break;
1191
1192                 case LFUN_DIALOG_UPDATE: {
1193                         string const & name = argument;
1194                         // Can only update a dialog connected to an existing inset
1195                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1196                         if (inset) {
1197                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1198                                 inset->dispatch(view()->cursor(), fr);
1199                         } else if (name == "paragraph") {
1200                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1201                         } else if (name == "prefs") {
1202                                 owner->getDialogs().update(name, string());
1203                         }
1204                         break;
1205                 }
1206
1207                 case LFUN_DIALOG_HIDE:
1208                         Dialogs::hide(argument, 0);
1209                         break;
1210
1211                 case LFUN_DIALOG_DISCONNECT_INSET:
1212                         owner->getDialogs().disconnect(argument);
1213                         break;
1214
1215                 case LFUN_CHILDOPEN: {
1216                         string const filename =
1217                                 MakeAbsPath(argument, owner->buffer()->filePath());
1218                         setMessage(N_("Opening child document ") +
1219                                          MakeDisplayPath(filename) + "...");
1220                         view()->savePosition(0);
1221                         string const parentfilename = owner->buffer()->fileName();
1222                         if (bufferlist.exists(filename))
1223                                 view()->setBuffer(bufferlist.getBuffer(filename));
1224                         else
1225                                 view()->loadLyXFile(filename);
1226                         // Set the parent name of the child document.
1227                         // This makes insertion of citations and references in the child work,
1228                         // when the target is in the parent or another child document.
1229                         owner->buffer()->setParentName(parentfilename);
1230                         break;
1231                 }
1232
1233                 case LFUN_TOGGLECURSORFOLLOW:
1234                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1235                         break;
1236
1237                 case LFUN_KMAP_OFF:
1238                         owner->getIntl().KeyMapOn(false);
1239                         break;
1240
1241                 case LFUN_KMAP_PRIM:
1242                         owner->getIntl().KeyMapPrim();
1243                         break;
1244
1245                 case LFUN_KMAP_SEC:
1246                         owner->getIntl().KeyMapSec();
1247                         break;
1248
1249                 case LFUN_KMAP_TOGGLE:
1250                         owner->getIntl().ToggleKeyMap();
1251                         break;
1252
1253                 case LFUN_REPEAT: {
1254                         // repeat command
1255                         string countstr;
1256                         string rest = split(argument, countstr, ' ');
1257                         istringstream is(countstr);
1258                         int count = 0;
1259                         is >> count;
1260                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1261                         for (int i = 0; i < count; ++i)
1262                                 dispatch(lyxaction.lookupFunc(rest));
1263                         break;
1264                 }
1265
1266                 case LFUN_SEQUENCE: {
1267                         // argument contains ';'-terminated commands
1268                         string arg = argument;
1269                         while (!arg.empty()) {
1270                                 string first;
1271                                 arg = split(arg, first, ';');
1272                                 FuncRequest func(lyxaction.lookupFunc(first));
1273                                 func.origin = cmd.origin;
1274                                 dispatch(func);
1275                         }
1276                         break;
1277                 }
1278
1279                 case LFUN_SAVEPREFERENCES: {
1280                         Path p(package().user_support());
1281                         lyxrc.write("preferences", false);
1282                         break;
1283                 }
1284
1285                 case LFUN_SCREEN_FONT_UPDATE:
1286                         // handle the screen font changes.
1287                         lyxrc.set_font_norm_type();
1288                         lyx_gui::update_fonts();
1289                         // All visible buffers will need resize
1290                         view()->resize();
1291                         break;
1292
1293                 case LFUN_SET_COLOR: {
1294                         string lyx_name;
1295                         string const x11_name = split(argument, lyx_name, ' ');
1296                         if (lyx_name.empty() || x11_name.empty()) {
1297                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1298                                                         " <x11_name>"));
1299                                 break;
1300                         }
1301
1302                         bool const graphicsbg_changed =
1303                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1304                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1305
1306                         if (!lcolor.setColor(lyx_name, x11_name)) {
1307                                 setErrorMessage(
1308                                         bformat(_("Set-color \"%1$s\" failed "
1309                                                                 "- color is undefined or "
1310                                                                 "may not be redefined"), lyx_name));
1311                                 break;
1312                         }
1313
1314                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1315
1316                         if (graphicsbg_changed) {
1317 #ifdef WITH_WARNINGS
1318 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1319 #endif
1320 #if 0
1321                                 lyx::graphics::GCache::get().changeDisplay(true);
1322 #endif
1323                         }
1324                         break;
1325                 }
1326
1327                 case LFUN_MESSAGE:
1328                         owner->message(argument);
1329                         break;
1330
1331                 case LFUN_TOOLTIPS_TOGGLE:
1332                         owner->getDialogs().toggleTooltips();
1333                         break;
1334
1335                 case LFUN_EXTERNAL_EDIT: {
1336                         FuncRequest fr(action, argument);
1337                         InsetExternal().dispatch(view()->cursor(), fr);
1338                         break;
1339                 }
1340
1341                 case LFUN_GRAPHICS_EDIT: {
1342                         FuncRequest fr(action, argument);
1343                         InsetGraphics().dispatch(view()->cursor(), fr);
1344                         break;
1345                 }
1346
1347                 case LFUN_INSET_APPLY: {
1348                         string const name = cmd.getArg(0);
1349                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1350                         if (inset) {
1351                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1352                                 inset->dispatch(view()->cursor(), fr);
1353                         } else {
1354                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1355                                 dispatch(fr);
1356                         }
1357                         // ideally, the update flag should be set by the insets,
1358                         // but this is not possible currently
1359                         update = true;
1360                         break;
1361                 }
1362
1363                 case LFUN_ALL_INSETS_TOGGLE: {
1364                         string action;
1365                         string const name = split(argument, action, ' ');
1366                         InsetBase::Code const inset_code =
1367                                 InsetBase::translate(name);
1368
1369                         LCursor & cur = view()->cursor();
1370                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1371
1372                         InsetBase & inset = owner->buffer()->inset();
1373                         InsetIterator it  = inset_iterator_begin(inset);
1374                         InsetIterator const end = inset_iterator_end(inset);
1375                         for (; it != end; ++it) {
1376                                 if (inset_code == InsetBase::NO_CODE
1377                                     || inset_code == it->lyxCode())
1378                                         it->dispatch(cur, fr);
1379                         }
1380                         break;
1381                 }
1382
1383                 case LFUN_LANGUAGE_BUFFER: {
1384                         Buffer & buffer = *owner->buffer();
1385                         Language const * oldL = buffer.params().language;
1386                         Language const * newL = languages.getLanguage(argument);
1387                         if (!newL || oldL == newL)
1388                                 break;
1389
1390                         if (oldL->RightToLeft() == newL->RightToLeft()
1391                             && !buffer.isMultiLingual())
1392                                 buffer.changeLanguage(oldL, newL);
1393                         else
1394                                 buffer.updateDocLang(newL);
1395                         break;
1396                 }
1397
1398                 case LFUN_SAVE_AS_DEFAULT: {
1399                         string const fname =
1400                                 AddName(AddPath(package().user_support(), "templates/"),
1401                                         "defaults.lyx");
1402                         Buffer defaults(fname);
1403
1404                         istringstream ss(argument);
1405                         LyXLex lex(0,0);
1406                         lex.setStream(ss);
1407                         int const unknown_tokens = defaults.readHeader(lex);
1408
1409                         if (unknown_tokens != 0) {
1410                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1411                                        << unknown_tokens << " unknown token"
1412                                        << (unknown_tokens == 1 ? "" : "s")
1413                                        << endl;
1414                         }
1415
1416                         if (defaults.writeFile(defaults.fileName()))
1417                                 setMessage(_("Document defaults saved in ")
1418                                            + MakeDisplayPath(fname));
1419                         else
1420                                 setErrorMessage(_("Unable to save document defaults"));
1421                         break;
1422                 }
1423
1424                 case LFUN_BUFFERPARAMS_APPLY: {
1425                         biblio::CiteEngine const engine =
1426                                 owner->buffer()->params().cite_engine;
1427
1428                         istringstream ss(argument);
1429                         LyXLex lex(0,0);
1430                         lex.setStream(ss);
1431                         int const unknown_tokens =
1432                                 owner->buffer()->readHeader(lex);
1433
1434                         if (unknown_tokens != 0) {
1435                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1436                                        << unknown_tokens << " unknown token"
1437                                        << (unknown_tokens == 1 ? "" : "s")
1438                                        << endl;
1439                         }
1440                         if (engine == owner->buffer()->params().cite_engine)
1441                                 break;
1442
1443                         LCursor & cur = view()->cursor();
1444                         FuncRequest fr(LFUN_INSET_REFRESH);
1445
1446                         InsetBase & inset = owner->buffer()->inset();
1447                         InsetIterator it  = inset_iterator_begin(inset);
1448                         InsetIterator const end = inset_iterator_end(inset);
1449                         for (; it != end; ++it)
1450                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1451                                         it->dispatch(cur, fr);
1452                         break;
1453                 }
1454
1455                 case LFUN_TEXTCLASS_APPLY: {
1456                         recordUndoFullDocument(view());
1457                         Buffer * buffer = owner->buffer();
1458
1459                         lyx::textclass_type const old_class =
1460                                 buffer->params().textclass;
1461
1462                         loadTextclass(argument);
1463
1464                         std::pair<bool, lyx::textclass_type> const tc_pair =
1465                                 textclasslist.NumberOfClass(argument);
1466
1467                         if (!tc_pair.first)
1468                                 break;
1469
1470                         lyx::textclass_type const new_class = tc_pair.second;
1471                         if (old_class == new_class)
1472                                 // nothing to do
1473                                 break;
1474
1475                         owner->message(_("Converting document to new document class..."));
1476                         StableDocIterator backcur(view()->cursor());
1477                         ErrorList el;
1478                         lyx::cap::SwitchBetweenClasses(
1479                                 old_class, new_class,
1480                                 buffer->paragraphs(), el);
1481
1482                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1483                         bufferErrors(*buffer, el);
1484                         view()->showErrorList(_("Class switch"));
1485                         updateCounters(*buffer);
1486                         update = true;
1487                         break;
1488                 }
1489
1490                 case LFUN_TEXTCLASS_LOAD:
1491                         loadTextclass(argument);
1492                         break;
1493
1494                 case LFUN_LYXRC_APPLY: {
1495                         LyXRC const lyxrc_orig = lyxrc;
1496
1497                         istringstream ss(argument);
1498                         bool const success = lyxrc.read(ss) == 0;
1499
1500                         if (!success) {
1501                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1502                                        << "Unable to read lyxrc data"
1503                                        << endl;
1504                                 break;
1505                         }
1506
1507                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1508                         break;
1509                 }
1510
1511                 default: {
1512                         view()->cursor().dispatch(cmd);
1513                         update |= view()->cursor().result().update();
1514                         if (!view()->cursor().result().dispatched())
1515                                 update |= view()->dispatch(cmd);
1516                         break;
1517                 }
1518                 }
1519
1520                 if (view()->available()) {
1521                         // Redraw screen unless explicitly told otherwise.
1522                         // This also initializes the position cache for all insets
1523                         // in (at least partially) visible top-level paragraphs.
1524                         if (update)
1525                                 view()->update(Update::FitCursor | Update::Force);
1526                         else
1527                                 view()->update(Update::FitCursor);
1528
1529                         // if we executed a mutating lfun, mark the buffer as dirty
1530                         // FIXME: Why not use flag.enabled() but call getStatus again?
1531                         if (getStatus(cmd).enabled()
1532                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1533                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1534                                 view()->buffer()->markDirty();
1535                 }
1536
1537                 if (view()->cursor().inTexted()) {
1538                         view()->owner()->updateLayoutChoice();
1539                 }
1540         }
1541         sendDispatchMessage(_(getMessage()), cmd);
1542 }
1543
1544
1545 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1546 {
1547         owner->updateMenubar();
1548         owner->updateToolbars();
1549
1550         const bool verbose = (cmd.origin == FuncRequest::UI
1551                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1552
1553         if (cmd.action == LFUN_SELFINSERT || !verbose) {
1554                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1555                 if (!msg.empty())
1556                         owner->message(msg);
1557                 return;
1558         }
1559
1560         string dispatch_msg = msg;
1561         if (!dispatch_msg.empty())
1562                 dispatch_msg += ' ';
1563
1564         string comname = lyxaction.getActionName(cmd.action);
1565
1566         bool argsadded = false;
1567
1568         if (!cmd.argument.empty()) {
1569                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1570                         comname += ' ' + cmd.argument;
1571                         argsadded = true;
1572                 }
1573         }
1574
1575         string const shortcuts = toplevel_keymap->printbindings(cmd);
1576
1577         if (!shortcuts.empty())
1578                 comname += ": " + shortcuts;
1579         else if (!argsadded && !cmd.argument.empty())
1580                 comname += ' ' + cmd.argument;
1581
1582         if (!comname.empty()) {
1583                 comname = rtrim(comname);
1584                 dispatch_msg += '(' + rtrim(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