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