]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
fix bug 1415; fix annoying typo
[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 "debug.h"
32 #include "encoding.h"
33 #include "exporter.h"
34 #include "format.h"
35 #include "funcrequest.h"
36 #include "gettext.h"
37 #include "importer.h"
38 #include "intl.h"
39 #include "iterators.h"
40 #include "kbmap.h"
41 #include "LColor.h"
42 #include "lyx_cb.h"
43 #include "LyXAction.h"
44 #include "lyxfind.h"
45 #include "lyxrc.h"
46 #include "lyxrow.h"
47 #include "lyxserver.h"
48 #include "lyxvc.h"
49 #include "paragraph.h"
50 #include "ParagraphParameters.h"
51 #include "undo.h"
52
53 #include "insets/insetcommand.h"
54 #include "insets/insetexternal.h"
55 #include "insets/insettabular.h"
56
57 #include "mathed/math_cursor.h"
58
59 #include "frontends/Alert.h"
60 #include "frontends/Dialogs.h"
61 #include "frontends/FileDialog.h"
62 #include "frontends/lyx_gui.h"
63 #include "frontends/LyXKeySym.h"
64 #include "frontends/LyXView.h"
65 #include "frontends/Menubar.h"
66 #include "frontends/Toolbar.h"
67
68 #include "support/FileInfo.h"
69 #include "support/filetools.h"
70 #include "support/forkedcontr.h"
71 #include "support/path.h"
72 #include "support/path_defines.h"
73 #include "support/tostr.h"
74 #include "support/std_sstream.h"
75 #include "support/os.h"
76
77 using bv_funcs::apply_freefont;
78 using bv_funcs::changeDepth;
79 using bv_funcs::currentState;
80 using bv_funcs::DEC_DEPTH;
81 using bv_funcs::freefont2string;
82 using bv_funcs::INC_DEPTH;
83 using bv_funcs::update_and_apply_freefont;
84
85 using lyx::support::AddName;
86 using lyx::support::AddPath;
87 using lyx::support::bformat;
88 using lyx::support::ChangeExtension;
89 using lyx::support::FileInfo;
90 using lyx::support::FileSearch;
91 using lyx::support::ForkedcallsController;
92 using lyx::support::i18nLibFileSearch;
93 using lyx::support::IsDirWriteable;
94 using lyx::support::IsFileReadable;
95 using lyx::support::isStrInt;
96 using lyx::support::MakeAbsPath;
97 using lyx::support::MakeDisplayPath;
98 using lyx::support::Path;
99 using lyx::support::rtrim;
100 using lyx::support::split;
101 using lyx::support::strToInt;
102 using lyx::support::strToUnsignedInt;
103 using lyx::support::system_lyxdir;
104 using lyx::support::token;
105 using lyx::support::trim;
106 using lyx::support::user_lyxdir;
107 using lyx::support::prefixIs;
108 using lyx::support::os::getTmpDir;
109
110 using std::endl;
111 using std::make_pair;
112 using std::string;
113 using std::istringstream;
114
115
116 extern BufferList bufferlist;
117 extern LyXServer * lyxserver;
118 extern bool selection_possible;
119
120 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
121
122 // (alkis)
123 extern tex_accent_struct get_accent(kb_action action);
124
125 extern void ShowLatexLog();
126
127
128 LyXFunc::LyXFunc(LyXView * o)
129         : owner(o),
130         encoded_last_key(0),
131         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
132         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
133         meta_fake_bit(key_modifier::none)
134 {
135 }
136
137
138 void LyXFunc::moveCursorUpdate()
139 {
140         LyXText * lt = view()->text;
141         if (lt->selection.mark())
142                 lt->setSelection();
143         view()->update();
144         view()->switchKeyMap();
145 }
146
147
148 void LyXFunc::handleKeyFunc(kb_action action)
149 {
150         char c = encoded_last_key;
151
152         if (keyseq.length()) {
153                 c = 0;
154         }
155
156         owner->getIntl().getTransManager()
157                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
158         // Need to clear, in case the minibuffer calls these
159         // actions
160         keyseq.clear();
161         // copied verbatim from do_accent_char
162         view()->update();
163         view()->getLyXText()->selection.cursor = view()->getLyXText()->cursor;
164 }
165
166
167 void LyXFunc::processKeySym(LyXKeySymPtr keysym,
168                             key_modifier::state state)
169 {
170         string argument;
171
172         if (lyxerr.debugging(Debug::KEY)) {
173                 lyxerr << "KeySym is "
174                        << keysym->getSymbolName()
175                        << endl;
176         }
177
178         // Do nothing if we have nothing (JMarc)
179         if (!keysym->isOK()) {
180                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
181                                    << endl;
182                 return;
183         }
184
185         if (keysym->isModifier()) {
186                 lyxerr[Debug::KEY] << "isModifier true" << endl;
187                 return;
188         }
189
190         Encoding const * encoding = view()->getEncoding();
191
192         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
193
194         // Do a one-deep top-level lookup for
195         // cancel and meta-fake keys. RVDK_PATCH_5
196         cancel_meta_seq.reset();
197
198         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
199         lyxerr[Debug::KEY] << "action first set to [" << func.action << ']' << endl;
200
201         // When not cancel or meta-fake, do the normal lookup.
202         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
203         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
204         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
205                 // remove Caps Lock and Mod2 as a modifiers
206                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
207                 lyxerr[Debug::KEY] << "action now set to ["
208                         << func.action << ']' << endl;
209         }
210
211         // Dont remove this unless you know what you are doing.
212         meta_fake_bit = key_modifier::none;
213
214         // can this happen now ?
215         if (func.action == LFUN_NOACTION) {
216                 func = FuncRequest(LFUN_PREFIX);
217         }
218
219         if (lyxerr.debugging(Debug::KEY)) {
220                 lyxerr << "Key [action="
221                        << func.action << "]["
222                        << keyseq.print() << ']'
223                        << endl;
224         }
225
226         // already here we know if it any point in going further
227         // why not return already here if action == -1 and
228         // num_bytes == 0? (Lgb)
229
230         if (keyseq.length() > 1) {
231                 owner->message(keyseq.print());
232         }
233
234
235         // Maybe user can only reach the key via holding down shift.
236         // Let's see. But only if shift is the only modifier
237         if (func.action == LFUN_UNKNOWN_ACTION &&
238             state == key_modifier::shift) {
239                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
240                 func = keyseq.addkey(keysym, key_modifier::none);
241                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
242         }
243
244         if (func.action == LFUN_UNKNOWN_ACTION) {
245                 // Hmm, we didn't match any of the keysequences. See
246                 // if it's normal insertable text not already covered
247                 // by a binding
248                 if (keysym->isText() && keyseq.length() == 1) {
249                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
250                         func = FuncRequest(LFUN_SELFINSERT);
251                 } else {
252                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
253                         owner->message(_("Unknown function."));
254                         return;
255                 }
256         }
257
258         if (func.action == LFUN_SELFINSERT) {
259                 if (encoded_last_key != 0) {
260                         string arg;
261                         arg += encoded_last_key;
262
263                         dispatch(FuncRequest(view(), LFUN_SELFINSERT, arg));
264
265                         lyxerr[Debug::KEY] << "SelfInsert arg[`"
266                                    << argument << "']" << endl;
267                 }
268         } else {
269                 dispatch(func);
270         }
271 }
272
273
274 FuncStatus LyXFunc::getStatus(FuncRequest const & ev) const
275 {
276         FuncStatus flag;
277         Buffer * buf = owner->buffer();
278
279         if (ev.action == LFUN_NOACTION) {
280                 setStatusMessage(N_("Nothing to do"));
281                 return flag.disabled(true);
282         }
283
284         switch (ev.action) {
285         case LFUN_UNKNOWN_ACTION:
286 #ifndef HAVE_LIBAIKSAURUS
287         case LFUN_THESAURUS_ENTRY:
288 #endif
289                 flag.unknown(true);
290                 flag.disabled(true);
291                 break;
292         default:
293                 flag |= lyx_gui::getStatus(ev);
294         }
295
296         if (flag.unknown()) {
297                 setStatusMessage(N_("Unknown action"));
298                 return flag;
299         }
300
301         // the default error message if we disable the command
302         setStatusMessage(N_("Command disabled"));
303
304         // Check whether we need a buffer
305         if (!lyxaction.funcHasFlag(ev.action, LyXAction::NoBuffer)) {
306                 // Yes we need a buffer, do we have one?
307                 if (buf) {
308                         // yes
309                         // Can we use a readonly buffer?
310                         if (buf->isReadonly() &&
311                             !lyxaction.funcHasFlag(ev.action,
312                                                    LyXAction::ReadOnly)) {
313                                 // no
314                                 setStatusMessage(N_("Document is read-only"));
315                                 flag.disabled(true);
316                         }
317                 } else {
318                         // no
319                         setStatusMessage(N_("Command not allowed with"
320                                             "out any document open"));
321                         return flag.disabled(true);
322                 }
323         }
324
325         UpdatableInset * tli = view()->theLockingInset();
326
327         // I would really like to avoid having this switch and rather try to
328         // encode this in the function itself.
329         bool disable = false;
330         switch (ev.action) {
331         case LFUN_EXPORT:
332                 disable = ev.argument != "custom"
333                         && !Exporter::IsExportable(*buf, ev.argument);
334                 break;
335         case LFUN_UNDO:
336                 disable = buf->undostack().empty();
337                 break;
338         case LFUN_REDO:
339                 disable = buf->redostack().empty();
340                 break;
341         case LFUN_CUT:
342         case LFUN_COPY:
343                 if (tli) {
344                         UpdatableInset * in = tli;
345                         if (in->lyxCode() != InsetOld::TABULAR_CODE) {
346                                 in = tli->getFirstLockingInsetOfType(InsetOld::TABULAR_CODE);
347                         }
348                         if (in && static_cast<InsetTabular*>(in)->hasSelection()) {
349                                 disable = false;
350                                 break;
351                         }
352                 }
353                 disable = !mathcursor && !view()->getLyXText()->selection.set();
354                 break;
355         case LFUN_RUNCHKTEX:
356                 disable = !buf->isLatex() || lyxrc.chktex_command == "none";
357                 break;
358         case LFUN_BUILDPROG:
359                 disable = !Exporter::IsExportable(*buf, "program");
360                 break;
361
362         case LFUN_LAYOUT_TABULAR:
363                 disable = !tli
364                         || (tli->lyxCode() != InsetOld::TABULAR_CODE
365                             && !tli->getFirstLockingInsetOfType(InsetOld::TABULAR_CODE));
366                 break;
367
368         case LFUN_DEPTH_MIN:
369                 disable = !changeDepth(view(), view()->getLyXText(), DEC_DEPTH, true);
370                 break;
371
372         case LFUN_DEPTH_PLUS:
373                 disable = !changeDepth(view(), view()->getLyXText(), INC_DEPTH, true);
374                 break;
375
376         case LFUN_LAYOUT:
377         case LFUN_LAYOUT_PARAGRAPH: {
378                 InsetOld * inset = view()->getLyXText()->cursorPar()->inInset();
379                 disable = inset && inset->forceDefaultParagraphs(inset);
380                 break;
381         }
382
383         case LFUN_INSET_OPTARG:
384                 disable = (view()->getLyXText()->cursorPar()->layout()->optionalargs == 0);
385                 break;
386
387         case LFUN_TABULAR_FEATURE:
388                 if (mathcursor) {
389 #if 0
390                         // FIXME: check temporarily disabled
391                         // valign code
392                         char align = mathcursor->valign();
393                         if (align == '\0') {
394                                 disable = true;
395                                 break;
396                         }
397                         if (ev.argument.empty()) {
398                                 flag.clear();
399                                 break;
400                         }
401                         if (!contains("tcb", ev.argument[0])) {
402                                 disable = true;
403                                 break;
404                         }
405                         flag.setOnOff(ev.argument[0] == align);
406                 } else {
407                         disable = true;
408
409                         char align = mathcursor->halign();
410                         if (align == '\0') {
411                                 disable = true;
412                                 break;
413                         }
414                         if (ev.argument.empty()) {
415                                 flag.clear();
416                                 break;
417                         }
418                         if (!contains("lcr", ev.argument[0])) {
419                                 disable = true;
420                                 break;
421                         }
422                         flag.setOnOff(ev.argument[0] == align);
423 #endif
424
425                         disable = !mathcursor->halign();
426                         break;
427                 }
428
429                 if (tli) {
430                         FuncStatus ret;
431                         //ret.disabled(true);
432                         if (tli->lyxCode() == InsetOld::TABULAR_CODE) {
433                                 ret = static_cast<InsetTabular *>(tli)
434                                         ->getStatus(ev.argument);
435                                 flag |= ret;
436                                 disable = false;
437                         } else if (tli->getFirstLockingInsetOfType(InsetOld::TABULAR_CODE)) {
438                                 ret = static_cast<InsetTabular *>
439                                         (tli->getFirstLockingInsetOfType(InsetOld::TABULAR_CODE))
440                                         ->getStatus(ev.argument);
441                                 flag |= ret;
442                                 disable = false;
443                         } else {
444                                 disable = true;
445                         }
446                 } else {
447                         static InsetTabular inset(*owner->buffer(), 1, 1);
448                         FuncStatus ret;
449
450                         disable = true;
451                         ret = inset.getStatus(ev.argument);
452                         if (ret.onoff(true) || ret.onoff(false))
453                                 flag.setOnOff(false);
454                 }
455                 break;
456
457         case LFUN_VC_REGISTER:
458                 disable = buf->lyxvc().inUse();
459                 break;
460         case LFUN_VC_CHECKIN:
461                 disable = !buf->lyxvc().inUse() || buf->isReadonly();
462                 break;
463         case LFUN_VC_CHECKOUT:
464                 disable = !buf->lyxvc().inUse() || !buf->isReadonly();
465                 break;
466         case LFUN_VC_REVERT:
467         case LFUN_VC_UNDO:
468                 disable = !buf->lyxvc().inUse();
469                 break;
470         case LFUN_MENURELOAD:
471                 disable = buf->isUnnamed() || buf->isClean();
472                 break;
473         case LFUN_BOOKMARK_GOTO:
474                 disable =  !view()->
475                         isSavedPosition(strToUnsignedInt(ev.argument));
476                 break;
477         case LFUN_MERGE_CHANGES:
478         case LFUN_ACCEPT_CHANGE:
479         case LFUN_REJECT_CHANGE:
480         case LFUN_ACCEPT_ALL_CHANGES:
481         case LFUN_REJECT_ALL_CHANGES:
482                 disable = !buf->params().tracking_changes;
483                 break;
484         case LFUN_INSET_TOGGLE: {
485                 LyXText * lt = view()->getLyXText();
486                 disable = !(isEditableInset(lt->getInset())
487                             || (lt->inset_owner
488                                 && lt->inset_owner->owner()
489                                 && lt->inset_owner->owner()->isOpen()));
490                 break;
491         }
492
493         case LFUN_INSET_SETTINGS: {
494                 disable = true;
495                 UpdatableInset * inset = view()->theLockingInset();
496
497                 if (!inset)
498                         break;
499
500                 // get the innermost inset
501                 inset = inset->getLockingInset();
502
503                 // jump back to owner if an InsetText, so
504                 // we get back to the InsetTabular or whatever
505                 if (inset->lyxCode() == InsetOld::TEXT_CODE)
506                         inset = inset->owner();
507
508                 InsetOld::Code code = inset->lyxCode();
509                 switch (code) {
510                         case InsetOld::TABULAR_CODE:
511                                 disable = ev.argument != "tabular";
512                                 break;
513                         case InsetOld::ERT_CODE:
514                                 disable = ev.argument != "ert";
515                                 break;
516                         case InsetOld::FLOAT_CODE:
517                                 disable = ev.argument != "float";
518                                 break;
519                         case InsetOld::MINIPAGE_CODE:
520                                 disable = ev.argument != "minipage";
521                                 break;
522                         case InsetOld::WRAP_CODE:
523                                 disable = ev.argument != "wrap";
524                                 break;
525                         case InsetOld::NOTE_CODE:
526                                 disable = ev.argument != "note";
527                                 break;
528                         case InsetOld::BRANCH_CODE:
529                                 disable = ev.argument != "branch";
530                                 break;
531                         case InsetOld::BOX_CODE:
532                                 disable = ev.argument != "box";
533                                 break;
534                         default:
535                                 break;
536                 }
537                 break;
538         }
539
540         case LFUN_MATH_MUTATE:
541                 if (mathcursor)
542                         //flag.setOnOff(mathcursor->formula()->hullType() == ev.argument);
543                         flag.setOnOff(false);
544                 else
545                         disable = true;
546                 break;
547
548         // we just need to be in math mode to enable that
549         case LFUN_MATH_SIZE:
550         case LFUN_MATH_SPACE:
551         case LFUN_MATH_LIMITS:
552         case LFUN_MATH_NONUMBER:
553         case LFUN_MATH_NUMBER:
554         case LFUN_MATH_EXTERN:
555                 disable = !mathcursor;
556                 break;
557
558         case LFUN_DIALOG_SHOW: {
559                 string const name = ev.getArg(0);
560                 if (!buf) {
561                         disable = !(name == "aboutlyx" ||
562                                     name == "file" ||
563                                     name == "forks" ||
564                                     name == "preferences" ||
565                                     name == "texinfo");
566                         break;
567                 }
568
569                 if (name == "print") {
570                         disable = !Exporter::IsExportable(*buf, "dvi") ||
571                                 lyxrc.print_command == "none";
572                 } else if (name == "character") {
573                         UpdatableInset * tli = view()->theLockingInset();
574                         disable = tli && tli->lyxCode() == InsetOld::ERT_CODE;
575                 } else if (name == "vclog") {
576                         disable = !buf->lyxvc().inUse();
577                 } else if (name == "latexlog") {
578                         disable = !IsFileReadable(buf->getLogName().second);
579                 }
580                 break;
581         }
582
583         default:
584                 break;
585         }
586
587         // the functions which insert insets
588         InsetOld::Code code = InsetOld::NO_CODE;
589         switch (ev.action) {
590         case LFUN_DIALOG_SHOW_NEW_INSET:
591                 if (ev.argument == "bibitem")
592                         code = InsetOld::BIBITEM_CODE;
593                 else if (ev.argument == "bibtex")
594                         code = InsetOld::BIBTEX_CODE;
595                 else if (ev.argument == "citation")
596                         code = InsetOld::CITE_CODE;
597                 else if (ev.argument == "ert")
598                         code = InsetOld::ERT_CODE;
599                 else if (ev.argument == "external")
600                         code = InsetOld::EXTERNAL_CODE;
601                 else if (ev.argument == "float")
602                         code = InsetOld::FLOAT_CODE;
603                 else if (ev.argument == "graphics")
604                         code = InsetOld::GRAPHICS_CODE;
605                 else if (ev.argument == "include")
606                         code = InsetOld::INCLUDE_CODE;
607                 else if (ev.argument == "index")
608                         code = InsetOld::INDEX_CODE;
609                 else if (ev.argument == "label")
610                         code = InsetOld::LABEL_CODE;
611                 else if (ev.argument == "minipage")
612                         code = InsetOld::MINIPAGE_CODE;
613                 else if (ev.argument == "ref")
614                         code = InsetOld::REF_CODE;
615                 else if (ev.argument == "toc")
616                         code = InsetOld::TOC_CODE;
617                 else if (ev.argument == "url")
618                         code = InsetOld::URL_CODE;
619                 else if (ev.argument == "wrap")
620                         code = InsetOld::WRAP_CODE;
621                 break;
622
623         case LFUN_INSET_ERT:
624                 code = InsetOld::ERT_CODE;
625                 break;
626         case LFUN_INSET_FOOTNOTE:
627                 code = InsetOld::FOOT_CODE;
628                 break;
629         case LFUN_TABULAR_INSERT:
630                 code = InsetOld::TABULAR_CODE;
631                 break;
632         case LFUN_INSET_MARGINAL:
633                 code = InsetOld::MARGIN_CODE;
634                 break;
635         case LFUN_INSET_MINIPAGE:
636                 code = InsetOld::MINIPAGE_CODE;
637                 break;
638         case LFUN_INSET_FLOAT:
639         case LFUN_INSET_WIDE_FLOAT:
640                 code = InsetOld::FLOAT_CODE;
641                 break;
642         case LFUN_INSET_WRAP:
643                 code = InsetOld::WRAP_CODE;
644                 break;
645         case LFUN_FLOAT_LIST:
646                 code = InsetOld::FLOAT_LIST_CODE;
647                 break;
648 #if 0
649         case LFUN_INSET_LIST:
650                 code = InsetOld::LIST_CODE;
651                 break;
652         case LFUN_INSET_THEOREM:
653                 code = InsetOld::THEOREM_CODE;
654                 break;
655 #endif
656         case LFUN_INSET_CAPTION:
657                 code = InsetOld::CAPTION_CODE;
658                 break;
659         case LFUN_INSERT_NOTE:
660                 code = InsetOld::NOTE_CODE;
661                 break;
662         case LFUN_INSERT_BOX:
663                 code = InsetOld::BOX_CODE;
664                 break;
665         case LFUN_INSERT_BRANCH:
666                 code = InsetOld::BRANCH_CODE;
667                 if (buf->params().branchlist().empty())
668                         disable = true;
669                 break;
670         case LFUN_INSERT_LABEL:
671                 code = InsetOld::LABEL_CODE;
672                 break;
673         case LFUN_INSET_OPTARG:
674                 code = InsetOld::OPTARG_CODE;
675                 break;
676         case LFUN_ENVIRONMENT_INSERT:
677                 code = InsetOld::MINIPAGE_CODE;
678                 break;
679         case LFUN_INDEX_INSERT:
680                 code = InsetOld::INDEX_CODE;
681                 break;
682         case LFUN_INDEX_PRINT:
683                 code = InsetOld::INDEX_PRINT_CODE;
684                 break;
685         case LFUN_TOC_INSERT:
686                 code = InsetOld::TOC_CODE;
687                 break;
688         case LFUN_HTMLURL:
689         case LFUN_URL:
690                 code = InsetOld::URL_CODE;
691                 break;
692         case LFUN_QUOTE:
693                 // always allow this, since we will inset a raw quote
694                 // if an inset is not allowed.
695                 break;
696         case LFUN_HYPHENATION:
697         case LFUN_LIGATURE_BREAK:
698         case LFUN_HFILL:
699         case LFUN_MENU_SEPARATOR:
700         case LFUN_LDOTS:
701         case LFUN_END_OF_SENTENCE:
702                 code = InsetOld::SPECIALCHAR_CODE;
703                 break;
704         case LFUN_SPACE_INSERT:
705                 // slight hack: we know this is allowed in math mode
706                 if (!mathcursor)
707                         code = InsetOld::SPACE_CODE;
708                 break;
709         case LFUN_INSET_DIALOG_SHOW: {
710                         LyXText * lt = view()->getLyXText();
711                         InsetOld * inset = lt->getInset();
712                         disable = !inset;
713                         if (!disable) {
714                                 code = inset->lyxCode();
715                                 if (!(code == InsetOld::INCLUDE_CODE
716                                         || code == InsetOld::BIBTEX_CODE
717                                         || code == InsetOld::FLOAT_LIST_CODE
718                                         || code == InsetOld::TOC_CODE))
719                                         disable = true;
720                         }
721                 }
722                 break;
723         default:
724                 break;
725         }
726         if (code != InsetOld::NO_CODE && tli && !tli->insetAllowed(code))
727                 disable = true;
728
729         if (disable)
730                 flag.disabled(true);
731
732         // A few general toggles
733         switch (ev.action) {
734         case LFUN_TOOLTIPS_TOGGLE:
735                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
736                 break;
737
738         case LFUN_READ_ONLY_TOGGLE:
739                 flag.setOnOff(buf->isReadonly());
740                 break;
741         case LFUN_APPENDIX:
742                 flag.setOnOff(view()->getLyXText()->cursorPar()->params().startOfAppendix());
743                 break;
744         case LFUN_SWITCHBUFFER:
745                 // toggle on the current buffer, but do not toggle off
746                 // the other ones (is that a good idea?)
747                 if (ev.argument == buf->fileName())
748                         flag.setOnOff(true);
749                 break;
750         case LFUN_TRACK_CHANGES:
751                 flag.setOnOff(buf->params().tracking_changes);
752                 break;
753         default:
754                 break;
755         }
756
757         // the font related toggles
758         if (!mathcursor) {
759                 LyXFont const & font = view()->getLyXText()->real_current_font;
760                 switch (ev.action) {
761                 case LFUN_EMPH:
762                         flag.setOnOff(font.emph() == LyXFont::ON);
763                         break;
764                 case LFUN_NOUN:
765                         flag.setOnOff(font.noun() == LyXFont::ON);
766                         break;
767                 case LFUN_BOLD:
768                         flag.setOnOff(font.series() == LyXFont::BOLD_SERIES);
769                         break;
770                 case LFUN_SANS:
771                         flag.setOnOff(font.family() == LyXFont::SANS_FAMILY);
772                         break;
773                 case LFUN_ROMAN:
774                         flag.setOnOff(font.family() == LyXFont::ROMAN_FAMILY);
775                         break;
776                 case LFUN_CODE:
777                         flag.setOnOff(font.family() == LyXFont::TYPEWRITER_FAMILY);
778                         break;
779                 default:
780                         break;
781                 }
782         } else {
783                 string tc = mathcursor->getLastCode();
784                 switch (ev.action) {
785                 case LFUN_BOLD:
786                         flag.setOnOff(tc == "mathbf");
787                         break;
788                 case LFUN_SANS:
789                         flag.setOnOff(tc == "mathsf");
790                         break;
791                 case LFUN_EMPH:
792                         flag.setOnOff(tc == "mathcal");
793                         break;
794                 case LFUN_ROMAN:
795                         flag.setOnOff(tc == "mathrm");
796                         break;
797                 case LFUN_CODE:
798                         flag.setOnOff(tc == "mathtt");
799                         break;
800                 case LFUN_NOUN:
801                         flag.setOnOff(tc == "mathbb");
802                         break;
803                 case LFUN_DEFAULT:
804                         flag.setOnOff(tc == "mathnormal");
805                         break;
806                 default:
807                         break;
808                 }
809         }
810
811         // this one is difficult to get right. As a half-baked
812         // solution, we consider only the first action of the sequence
813         if (ev.action == LFUN_SEQUENCE) {
814                 // argument contains ';'-terminated commands
815 #warning LyXAction arguements not handled here.
816                 flag = getStatus(FuncRequest(lyxaction.lookupFunc(token(ev.argument, ';', 0))));
817         }
818
819         return flag;
820 }
821
822
823 namespace {
824
825 bool ensureBufferClean(BufferView * bv)
826 {
827         Buffer & buf = *bv->buffer();
828         if (buf.isClean())
829                 return true;
830
831         string const file = MakeDisplayPath(buf.fileName(), 30);
832         string text = bformat(_("The document %1$s has unsaved "
833                                 "changes.\n\nDo you want to save "
834                                 "the document?"), file);
835         int const ret = Alert::prompt(_("Save changed document?"),
836                                       text, 0, 1, _("&Save"),
837                                       _("&Cancel"));
838
839         if (ret == 0)
840                 bv->owner()->dispatch(FuncRequest(LFUN_MENUWRITE));
841
842         return buf.isClean();
843 }
844
845 } //namespace anon
846
847
848 void LyXFunc::dispatch(FuncRequest const & func, bool verbose)
849 {
850         string argument = func.argument;
851         kb_action action = func.action;
852
853         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: action[" << action
854                               <<"] arg[" << argument << ']' << endl;
855
856         // we have not done anything wrong yet.
857         errorstat = false;
858         dispatch_buffer.erase();
859
860 #ifdef NEW_DISPATCHER
861         // We try do call the most specific dispatcher first:
862         //  1. the lockinginset's dispatch
863         //  2. the bufferview's dispatch
864         //  3. the lyxview's dispatch
865 #endif
866
867         selection_possible = false;
868
869         // We cannot use this function here
870         if (getStatus(func).disabled()) {
871                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
872                        << lyxaction.getActionName(action)
873                        << " [" << action << "] is disabled at this location"
874                        << endl;
875                 setErrorMessage(getStatusMessage());
876                 goto exit_with_message;
877         }
878
879         if (view()->available())
880                 view()->hideCursor();
881
882 #if 0
883         {
884                 Cursor cursor;
885                 buildCursor(cursor, *view());
886                 if (cursor.dispatch(FuncRequest(func, view())) == DISPATCHED) {
887                         lyxerr << "dispatched by Cursor::dispatch()\n";
888                         goto exit_with_message;
889                 }
890         }
891 #endif
892
893
894         if (view()->available() && view()->theLockingInset()) {
895                 dispatch_result result;
896                 if (action > 1 || (action == LFUN_UNKNOWN_ACTION &&
897                                      !keyseq.deleted()))
898                 {
899                         UpdatableInset * inset = view()->theLockingInset();
900 #if 1
901                         int inset_x;
902                         int dummy_y;
903                         inset->getCursorPos(view(), inset_x, dummy_y);
904 #endif
905                         if (action == LFUN_UNKNOWN_ACTION
906                             && argument.empty()) {
907                                 argument = encoded_last_key;
908                         }
909
910                         // the insets can't try to handle this,
911                         // a table cell in the dummy position will
912                         // lock its insettext, the insettext will
913                         // pass it the bufferview, and succeed,
914                         // so it will stay not locked. Not good
915                         // if we've just done LFUN_ESCAPE (which
916                         // injects an LFUN_PARAGRAPH_UPDATE)
917                         if (action == LFUN_PARAGRAPH_UPDATE) {
918                                 view()->dispatch(func);
919                                 goto exit_with_message;
920                         }
921
922                         // Undo/Redo is a bit tricky for insets.
923                         if (action == LFUN_UNDO) {
924                                 view()->undo();
925                                 goto exit_with_message;
926                         }
927
928                         if (action == LFUN_REDO) {
929                                 view()->redo();
930                                 goto exit_with_message;
931                         }
932
933                         // Hand-over to inset's own dispatch:
934                         result = inset->dispatch(FuncRequest(view(), action, argument));
935                         if (result == DISPATCHED || result == DISPATCHED_NOUPDATE) {
936                                 goto exit_with_message;
937                         }
938
939                         // If UNDISPATCHED, just soldier on
940                         if (result == FINISHED) {
941                                 owner->clearMessage();
942                                 goto exit_with_message;
943                                 // We do not need special RTL handling here:
944                                 // FINISHED means that the cursor should be
945                                 // one position after the inset.
946                         }
947
948                         if (result == FINISHED_RIGHT) {
949                                 view()->text->cursorRight(view());
950                                 moveCursorUpdate();
951                                 owner->clearMessage();
952                                 goto exit_with_message;
953                         }
954
955                         if (result == FINISHED_UP) {
956                                 RowList::iterator const irow = view()->text->cursorRow();
957                                 if (irow != view()->text->firstRow()) {
958 #if 1
959                                         view()->text->setCursorFromCoordinates(
960                                                 view()->text->cursor.x() + inset_x,
961                                                 view()->text->cursor.y() -
962                                                 irow->baseline() - 1);
963                                         view()->text->cursor.x_fix(view()->text->cursor.x());
964 #else
965                                         view()->text->cursorUp(view());
966 #endif
967                                         moveCursorUpdate();
968                                 } else {
969                                         view()->update();
970                                 }
971                                 owner->clearMessage();
972                                 goto exit_with_message;
973                         }
974
975                         if (result == FINISHED_DOWN) {
976                                 RowList::iterator const irow = view()->text->cursorRow();
977                                 if (irow != view()->text->lastRow()) {
978 #if 1
979                                         view()->text->setCursorFromCoordinates(
980                                                 view()->text->cursor.x() + inset_x,
981                                                 view()->text->cursor.y() -
982                                                 irow->baseline() +
983                                                 irow->height() + 1);
984                                         view()->text->cursor.x_fix(view()->text->cursor.x());
985 #else
986                                         view()->text->cursorDown(view());
987 #endif
988                                 } else {
989                                         view()->text->cursorRight(view());
990                                 }
991                                 moveCursorUpdate();
992                                 owner->clearMessage();
993                                 goto exit_with_message;
994                         }
995
996 #warning I am not sure this is still right, please have a look! (Jug 20020417)
997                         // result == UNDISPATCHED
998                         //setMessage(N_("Text mode"));
999                         switch (action) {
1000                         case LFUN_UNKNOWN_ACTION:
1001                         case LFUN_BREAKPARAGRAPH:
1002                         case LFUN_BREAKLINE:
1003                                 view()->text->cursorRight(view());
1004                                 view()->switchKeyMap();
1005                                 owner->view_state_changed();
1006                                 break;
1007                         case LFUN_RIGHT:
1008                                 if (!view()->text->cursorPar()->isRightToLeftPar(owner->buffer()->params())) {
1009                                         view()->text->cursorRight(view());
1010                                         moveCursorUpdate();
1011                                         owner->view_state_changed();
1012                                 }
1013                                 goto exit_with_message;
1014                         case LFUN_LEFT:
1015                                 if (view()->text->cursorPar()->isRightToLeftPar(owner->buffer()->params())) {
1016                                         view()->text->cursorRight(view());
1017                                         moveCursorUpdate();
1018                                         owner->view_state_changed();
1019                                 }
1020                                 goto exit_with_message;
1021                         case LFUN_DOWN:
1022                                 if (view()->text->cursorRow() != view()->text->lastRow())
1023                                         view()->text->cursorDown(view());
1024                                 else
1025                                         view()->text->cursorRight(view());
1026                                 moveCursorUpdate();
1027                                 owner->view_state_changed();
1028                                 goto exit_with_message;
1029                         default:
1030                                 break;
1031                         }
1032                 }
1033         }
1034
1035         switch (action) {
1036
1037         case LFUN_ESCAPE: {
1038                 if (!view()->available())
1039                         break;
1040                 // this function should be used always [asierra060396]
1041                 UpdatableInset * tli = view()->theLockingInset();
1042                 if (tli) {
1043                         UpdatableInset * lock = tli->getLockingInset();
1044
1045                         if (tli == lock) {
1046                                 view()->unlockInset(tli);
1047                                 view()->text->cursorRight(view());
1048                                 moveCursorUpdate();
1049                                 owner->view_state_changed();
1050                         } else {
1051                                 tli->unlockInsetInInset(view(), lock, true);
1052                         }
1053                         finishUndo();
1054                         // Tell the paragraph dialog that we changed paragraph
1055                         dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1056                 }
1057                 break;
1058         }
1059
1060         case LFUN_WORDFINDFORWARD:
1061         case LFUN_WORDFINDBACKWARD: {
1062                 static string last_search;
1063                 string searched_string;
1064
1065                 if (!argument.empty()) {
1066                         last_search = argument;
1067                         searched_string = argument;
1068                 } else {
1069                         searched_string = last_search;
1070                 }
1071                 bool fw = (action == LFUN_WORDFINDFORWARD);
1072                 if (!searched_string.empty())
1073                         lyx::find::find(view(), searched_string, fw);
1074                 break;
1075         }
1076
1077         case LFUN_PREFIX:
1078                 if (view()->available() && !view()->theLockingInset())
1079                         view()->update();
1080                 owner->message(keyseq.printOptions());
1081                 break;
1082
1083         // --- Misc -------------------------------------------
1084         case LFUN_EXEC_COMMAND:
1085                 owner->focus_command_buffer();
1086                 break;
1087
1088         case LFUN_CANCEL:
1089                 keyseq.reset();
1090                 meta_fake_bit = key_modifier::none;
1091                 if (view()->available())
1092                         // cancel any selection
1093                         dispatch(FuncRequest(LFUN_MARK_OFF));
1094                 setMessage(N_("Cancel"));
1095                 break;
1096
1097         case LFUN_META_FAKE:
1098                 meta_fake_bit = key_modifier::alt;
1099                 setMessage(keyseq.print());
1100                 break;
1101
1102         case LFUN_READ_ONLY_TOGGLE:
1103                 if (owner->buffer()->lyxvc().inUse())
1104                         owner->buffer()->lyxvc().toggleReadOnly();
1105                 else
1106                         owner->buffer()->setReadonly(
1107                                 !owner->buffer()->isReadonly());
1108                 break;
1109
1110         case LFUN_CENTER: // this is center and redraw.
1111                 view()->center();
1112                 break;
1113
1114                 // --- Menus -----------------------------------------------
1115         case LFUN_MENUNEW:
1116                 menuNew(argument, false);
1117                 break;
1118
1119         case LFUN_MENUNEWTMPLT:
1120                 menuNew(argument, true);
1121                 break;
1122
1123         case LFUN_CLOSEBUFFER:
1124                 closeBuffer();
1125                 break;
1126
1127         case LFUN_MENUWRITE:
1128                 if (!owner->buffer()->isUnnamed()) {
1129                         string const str = bformat(_("Saving document %1$s..."),
1130                            MakeDisplayPath(owner->buffer()->fileName()));
1131                         owner->message(str);
1132                         MenuWrite(owner->buffer());
1133                         owner->message(str + _(" done."));
1134                 } else
1135                         WriteAs(owner->buffer());
1136                 break;
1137
1138         case LFUN_WRITEAS:
1139                 WriteAs(owner->buffer(), argument);
1140                 break;
1141
1142         case LFUN_MENURELOAD: {
1143                 string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
1144                 string text = bformat(_("Any changes will be lost. Are you sure "
1145                         "you want to revert to the saved version of the document %1$s?"), file);
1146                 int const ret = Alert::prompt(_("Revert to saved document?"),
1147                         text, 0, 1, _("&Revert"), _("&Cancel"));
1148
1149                 if (ret == 0)
1150                         view()->reload();
1151                 break;
1152         }
1153
1154         case LFUN_UPDATE:
1155                 Exporter::Export(owner->buffer(), argument, true);
1156                 view()->showErrorList(BufferFormat(*owner->buffer()));
1157                 break;
1158
1159         case LFUN_PREVIEW:
1160                 Exporter::Preview(owner->buffer(), argument);
1161                 view()->showErrorList(BufferFormat(*owner->buffer()));
1162                 break;
1163
1164         case LFUN_BUILDPROG:
1165                 Exporter::Export(owner->buffer(), "program", true);
1166                 view()->showErrorList(_("Build"));
1167                 break;
1168
1169         case LFUN_RUNCHKTEX:
1170                 owner->buffer()->runChktex();
1171                 view()->showErrorList(_("ChkTeX"));
1172                 break;
1173
1174         case LFUN_EXPORT:
1175                 if (argument == "custom")
1176                         owner->getDialogs().showSendto();
1177                 else {
1178                         Exporter::Export(owner->buffer(), argument, false);
1179                         view()->showErrorList(BufferFormat(*owner->buffer()));
1180                 }
1181                 break;
1182
1183         case LFUN_IMPORT:
1184                 doImport(argument);
1185                 break;
1186
1187         case LFUN_QUIT:
1188                 QuitLyX();
1189                 break;
1190
1191         case LFUN_TOCVIEW:
1192         {
1193                 InsetCommandParams p("tableofcontents");
1194                 string const data = InsetCommandMailer::params2string("toc", p);
1195                 owner->getDialogs().show("toc", data, 0);
1196                 break;
1197         }
1198
1199         case LFUN_AUTOSAVE:
1200                 AutoSave(view());
1201                 break;
1202
1203         case LFUN_UNDO:
1204                 view()->undo();
1205                 break;
1206
1207         case LFUN_REDO:
1208                 view()->redo();
1209                 break;
1210
1211         case LFUN_FREEFONT_APPLY:
1212                 apply_freefont(view());
1213                 break;
1214
1215         case LFUN_FREEFONT_UPDATE:
1216                 update_and_apply_freefont(view(), argument);
1217                 break;
1218
1219         case LFUN_RECONFIGURE:
1220                 Reconfigure(view());
1221                 break;
1222
1223 #if 0
1224         case LFUN_FLOATSOPERATE:
1225                 if (argument == "openfoot")
1226                         view()->allFloats(1,0);
1227                 else if (argument == "closefoot")
1228                         view()->allFloats(0,0);
1229                 else if (argument == "openfig")
1230                         view()->allFloats(1,1);
1231                 else if (argument == "closefig")
1232                         view()->allFloats(0,1);
1233                 break;
1234 #else
1235 #ifdef WITH_WARNINGS
1236 //#warning Find another implementation here (or another lyxfunc)!
1237 #endif
1238 #endif
1239         case LFUN_HELP_OPEN:
1240         {
1241                 string const arg = argument;
1242                 if (arg.empty()) {
1243                         setErrorMessage(N_("Missing argument"));
1244                         break;
1245                 }
1246                 string const fname = i18nLibFileSearch("doc", arg, "lyx");
1247                 if (fname.empty()) {
1248                         lyxerr << "LyX: unable to find documentation file `"
1249                                << arg << "'. Bad installation?" << endl;
1250                         break;
1251                 }
1252                 owner->message(bformat(_("Opening help file %1$s..."),
1253                         MakeDisplayPath(fname)));
1254                 view()->loadLyXFile(fname, false);
1255                 break;
1256         }
1257
1258                 // --- version control -------------------------------
1259         case LFUN_VC_REGISTER:
1260         {
1261                 if (!ensureBufferClean(view()))
1262                         break;
1263                 if (!owner->buffer()->lyxvc().inUse()) {
1264                         owner->buffer()->lyxvc().registrer();
1265                         view()->reload();
1266                 }
1267         }
1268         break;
1269
1270         case LFUN_VC_CHECKIN:
1271         {
1272                 if (!ensureBufferClean(view()))
1273                         break;
1274                 if (owner->buffer()->lyxvc().inUse()
1275                     && !owner->buffer()->isReadonly()) {
1276                         owner->buffer()->lyxvc().checkIn();
1277                         view()->reload();
1278                 }
1279         }
1280         break;
1281
1282         case LFUN_VC_CHECKOUT:
1283         {
1284                 if (!ensureBufferClean(view()))
1285                         break;
1286                 if (owner->buffer()->lyxvc().inUse()
1287                     && owner->buffer()->isReadonly()) {
1288                         owner->buffer()->lyxvc().checkOut();
1289                         view()->reload();
1290                 }
1291         }
1292         break;
1293
1294         case LFUN_VC_REVERT:
1295         {
1296                 owner->buffer()->lyxvc().revert();
1297                 view()->reload();
1298         }
1299         break;
1300
1301         case LFUN_VC_UNDO:
1302         {
1303                 owner->buffer()->lyxvc().undoLast();
1304                 view()->reload();
1305         }
1306         break;
1307
1308         // --- buffers ----------------------------------------
1309
1310         case LFUN_SWITCHBUFFER:
1311                 view()->buffer(bufferlist.getBuffer(argument));
1312                 break;
1313
1314         case LFUN_FILE_NEW:
1315                 NewFile(view(), argument);
1316                 break;
1317
1318         case LFUN_FILE_OPEN:
1319                 open(argument);
1320                 break;
1321
1322         case LFUN_LAYOUT_TABULAR:
1323             if (view()->theLockingInset()) {
1324                 if (view()->theLockingInset()->lyxCode()== InsetOld::TABULAR_CODE) {
1325                     InsetTabular * inset = static_cast<InsetTabular *>
1326                         (view()->theLockingInset());
1327                     inset->openLayoutDialog(view());
1328                 } else if (view()->theLockingInset()->
1329                            getFirstLockingInsetOfType(InsetOld::TABULAR_CODE)!=0) {
1330                     InsetTabular * inset = static_cast<InsetTabular *>(
1331                         view()->theLockingInset()->getFirstLockingInsetOfType(InsetOld::TABULAR_CODE));
1332                     inset->openLayoutDialog(view());
1333                 }
1334             }
1335             break;
1336
1337         case LFUN_DROP_LAYOUTS_CHOICE:
1338                 owner->getToolbar().openLayoutList();
1339                 break;
1340
1341         case LFUN_MENU_OPEN_BY_NAME:
1342                 owner->getMenubar().openByName(argument);
1343                 break; // RVDK_PATCH_5
1344
1345         // --- lyxserver commands ----------------------------
1346
1347
1348         case LFUN_GETNAME:
1349                 setMessage(owner->buffer()->fileName());
1350                 lyxerr[Debug::INFO] << "FNAME["
1351                                << owner->buffer()->fileName()
1352                                << "] " << endl;
1353                 break;
1354
1355         case LFUN_NOTIFY:
1356         {
1357                 dispatch_buffer = keyseq.print();
1358                 lyxserver->notifyClient(dispatch_buffer);
1359         }
1360         break;
1361
1362         case LFUN_GOTOFILEROW:
1363         {
1364                 string file_name;
1365                 int row;
1366                 istringstream istr(argument.c_str());
1367                 istr >> file_name >> row;
1368                 if (prefixIs(file_name, getTmpDir())) {
1369                         // Needed by inverse dvi search. If it is a file
1370                         // in tmpdir, call the apropriated function
1371                         view()->buffer(bufferlist.getBufferFromTmp(file_name));
1372                 } else {
1373                         // Must replace extension of the file to be .lyx
1374                         // and get full path
1375                         string const s(ChangeExtension(file_name, ".lyx"));
1376                         // Either change buffer or load the file
1377                         if (bufferlist.exists(s)) {
1378                                 view()->buffer(bufferlist.getBuffer(s));
1379                         } else {
1380                                 view()->loadLyXFile(s);
1381                         }
1382                 }
1383
1384                 view()->setCursorFromRow(row);
1385
1386                 view()->center();
1387                 // see BufferView_pimpl::center()
1388                 view()->updateScrollbar();
1389         }
1390         break;
1391
1392         case LFUN_GOTO_PARAGRAPH:
1393         {
1394                 istringstream istr(argument.c_str());
1395
1396                 int id;
1397                 istr >> id;
1398                 ParIterator par = owner->buffer()->getParFromID(id);
1399                 if (par == owner->buffer()->par_iterator_end()) {
1400                         lyxerr[Debug::INFO] << "No matching paragraph found! ["
1401                                             << id << ']' << endl;
1402                         break;
1403                 } else {
1404                         lyxerr[Debug::INFO] << "Paragraph " << par->id()
1405                                             << " found." << endl;
1406                 }
1407
1408                 if (view()->theLockingInset())
1409                         view()->unlockInset(view()->theLockingInset());
1410                 if (par->inInset()) {
1411                         FuncRequest cmd(view(), LFUN_INSET_EDIT, "left");
1412                         par->inInset()->dispatch(cmd);
1413                 }
1414                 // Set the cursor
1415                 view()->getLyXText()->setCursor(par.pit(), 0);
1416                 view()->switchKeyMap();
1417                 owner->view_state_changed();
1418
1419                 view()->center();
1420                 // see BufferView_pimpl::center()
1421                 view()->updateScrollbar();
1422         }
1423         break;
1424
1425         // --- insert characters ----------------------------------------
1426
1427         // ---  Mathed stuff. If we are here, there is no locked inset yet.
1428         case LFUN_MATH_EXTERN:
1429         case LFUN_MATH_NUMBER:
1430         case LFUN_MATH_NONUMBER:
1431         case LFUN_MATH_LIMITS:
1432         {
1433                 setErrorMessage(N_("This is only allowed in math mode!"));
1434         }
1435         break;
1436
1437         // passthrough hat and underscore outside mathed:
1438         case LFUN_SUBSCRIPT:
1439                 dispatch(FuncRequest(view(), LFUN_SELFINSERT, "_"));
1440                 break;
1441         case LFUN_SUPERSCRIPT:
1442                 dispatch(FuncRequest(view(), LFUN_SELFINSERT, "^"));
1443                 break;
1444
1445         case LFUN_DIALOG_SHOW: {
1446                 string const name = func.getArg(0);
1447                 string data = trim(func.argument.substr(name.size()));
1448
1449                 if (name == "character") {
1450                         data = freefont2string();
1451                         if (!data.empty())
1452                                 owner->getDialogs().show("character", data);
1453                 } else if (name == "document")
1454                         owner->getDialogs().showDocument();
1455                 else if (name == "findreplace")
1456                         owner->getDialogs().showSearch();
1457                 else if (name == "forks")
1458                         owner->getDialogs().showForks();
1459                 else if (name == "preamble")
1460                         owner->getDialogs().showPreamble();
1461                 else if (name == "preferences")
1462                         owner->getDialogs().showPreferences();
1463                 else if (name == "print")
1464                         owner->getDialogs().showPrint();
1465                 else if (name == "spellchecker")
1466                         owner->getDialogs().showSpellchecker();
1467                 else
1468                         owner->getDialogs().show(name, data);
1469                 break;
1470         }
1471
1472         case LFUN_DIALOG_SHOW_NEW_INSET: {
1473                 string const & name = argument;
1474                 string data;
1475                 if (name == "bibitem" ||
1476                     name == "bibtex" ||
1477                     name == "include" ||
1478                     name == "index" ||
1479                     name == "ref" ||
1480                     name == "toc" ||
1481                     name == "url") {
1482                         InsetCommandParams p(name);
1483                         data = InsetCommandMailer::params2string(name, p);
1484                 } else if (name == "citation") {
1485                         InsetCommandParams p("cite");
1486                         data = InsetCommandMailer::params2string(name, p);
1487                 }
1488                 owner->getDialogs().show(name, data, 0);
1489         }
1490         break;
1491
1492         case LFUN_DIALOG_SHOW_NEXT_INSET: {
1493         }
1494         break;
1495
1496         case LFUN_INSET_DIALOG_SHOW: {
1497                 LyXText * lt = view()->getLyXText();
1498                 InsetOld * inset = lt->getInset();
1499                 if (inset) {
1500                         FuncRequest cmd(view(), LFUN_INSET_DIALOG_SHOW);
1501                         inset->dispatch(cmd);
1502                 }
1503         }
1504         break;
1505
1506         case LFUN_DIALOG_UPDATE: {
1507                 string const & name = argument;
1508                 // Can only update a dialog connected to an existing inset
1509                 InsetBase * inset = owner->getDialogs().getOpenInset(name);
1510                 if (inset) {
1511                         FuncRequest fr(view(), LFUN_INSET_DIALOG_UPDATE,
1512                                        func.argument);
1513                         inset->dispatch(fr);
1514                 } else if (name == "paragraph") {
1515                         dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1516                 }
1517         }
1518         break;
1519
1520         case LFUN_DIALOG_HIDE:
1521                 Dialogs::hide(argument, 0);
1522                 break;
1523
1524         case LFUN_DIALOG_DISCONNECT_INSET:
1525                 owner->getDialogs().disconnect(argument);
1526                 break;
1527
1528         case LFUN_CHILDOPEN:
1529         {
1530                 string const filename =
1531                         MakeAbsPath(argument, owner->buffer()->filePath());
1532                 setMessage(N_("Opening child document ") +
1533                            MakeDisplayPath(filename) + "...");
1534                 view()->savePosition(0);
1535                 if (bufferlist.exists(filename))
1536                         view()->buffer(bufferlist.getBuffer(filename));
1537                 else
1538                         view()->loadLyXFile(filename);
1539         }
1540         break;
1541
1542         case LFUN_TOGGLECURSORFOLLOW:
1543                 lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1544                 break;
1545
1546         case LFUN_KMAP_OFF:
1547                 owner->getIntl().KeyMapOn(false);
1548                 break;
1549
1550         case LFUN_KMAP_PRIM:
1551                 owner->getIntl().KeyMapPrim();
1552                 break;
1553
1554         case LFUN_KMAP_SEC:
1555                 owner->getIntl().KeyMapSec();
1556                 break;
1557
1558         case LFUN_KMAP_TOGGLE:
1559                 owner->getIntl().ToggleKeyMap();
1560                 break;
1561
1562         case LFUN_SEQUENCE:
1563         {
1564                 // argument contains ';'-terminated commands
1565                 while (!argument.empty()) {
1566                         string first;
1567                         argument = split(argument, first, ';');
1568                         dispatch(lyxaction.lookupFunc(first));
1569                 }
1570         }
1571         break;
1572
1573         case LFUN_SAVEPREFERENCES:
1574         {
1575                 Path p(user_lyxdir());
1576                 lyxrc.write("preferences");
1577         }
1578         break;
1579
1580         case LFUN_SCREEN_FONT_UPDATE:
1581         {
1582                 // handle the screen font changes.
1583                 lyxrc.set_font_norm_type();
1584                 lyx_gui::update_fonts();
1585                 // All visible buffers will need resize
1586                 view()->resize();
1587                 view()->update();
1588         }
1589         break;
1590
1591         case LFUN_SET_COLOR:
1592         {
1593                 string lyx_name;
1594                 string const x11_name = split(argument, lyx_name, ' ');
1595                 if (lyx_name.empty() || x11_name.empty()) {
1596                         setErrorMessage(N_("Syntax: set-color <lyx_name>"
1597                                                 " <x11_name>"));
1598                         break;
1599                         }
1600
1601                 bool const graphicsbg_changed =
1602                         (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1603                          x11_name != lcolor.getX11Name(LColor::graphicsbg));
1604
1605                 if (!lcolor.setColor(lyx_name, x11_name)) {
1606                         setErrorMessage(
1607                                 bformat(_("Set-color \"%1$s\" failed "
1608                                                   "- color is undefined or "
1609                                                   "may not be redefined"), lyx_name));
1610                         break;
1611                 }
1612
1613                 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1614
1615                 if (graphicsbg_changed) {
1616 #ifdef WITH_WARNINGS
1617 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1618 #endif
1619 #if 0
1620                         lyx::graphics::GCache & gc =
1621                                 lyx::graphics::GCache::get();
1622                         gc.changeDisplay(true);
1623 #endif
1624                 }
1625
1626                 view()->update();
1627                 break;
1628         }
1629
1630         case LFUN_MESSAGE:
1631                 owner->message(argument);
1632                 break;
1633
1634         case LFUN_FORKS_KILL:
1635         {
1636                 if (!isStrInt(argument))
1637                         break;
1638
1639                 pid_t const pid = strToInt(argument);
1640                 ForkedcallsController & fcc = ForkedcallsController::get();
1641                 fcc.kill(pid);
1642                 break;
1643         }
1644
1645         case LFUN_TOOLTIPS_TOGGLE:
1646                 owner->getDialogs().toggleTooltips();
1647                 break;
1648
1649         case LFUN_EXTERNAL_EDIT: {
1650                 InsetExternal().dispatch(FuncRequest(view(), action, argument));
1651                 break;
1652         }
1653
1654         default:
1655                 // Then if it was none of the above
1656                 // Trying the BufferView::pimpl dispatch:
1657                 if (!view()->dispatch(func))
1658                         lyxerr << "A truly unknown func ["
1659                                << lyxaction.getActionName(func.action) << "]!"
1660                                << endl;
1661                 break;
1662         } // end of switch
1663
1664 exit_with_message:
1665
1666         view()->owner()->updateLayoutChoice();
1667
1668         if (view()->available()) {
1669                 if (view()->fitCursor()) {
1670                         //lyxerr << "LyXFunc->fitCursor->update" << endl;
1671                         view()->update();
1672                 }
1673
1674                 // If we executed a mutating lfun, mark the buffer as dirty
1675                 if (!getStatus(func).disabled()
1676                     && !lyxaction.funcHasFlag(func.action, LyXAction::NoBuffer)
1677                     && !lyxaction.funcHasFlag(func.action, LyXAction::ReadOnly))
1678                         view()->buffer()->markDirty();
1679         }
1680
1681         sendDispatchMessage(getMessage(), func, verbose);
1682 }
1683
1684
1685 void LyXFunc::sendDispatchMessage(string const & msg,
1686                                   FuncRequest const & func, bool verbose)
1687 {
1688         owner->updateMenubar();
1689         owner->updateToolbar();
1690
1691         if (func.action == LFUN_SELFINSERT || !verbose) {
1692                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1693                 if (!msg.empty())
1694                         owner->message(msg);
1695                 return;
1696         }
1697
1698         string dispatch_msg = msg;
1699         if (!dispatch_msg.empty())
1700                 dispatch_msg += ' ';
1701
1702         string comname = lyxaction.getActionName(func.action);
1703
1704         bool argsadded = false;
1705
1706         if (!func.argument.empty()) {
1707                 if (func.action != LFUN_UNKNOWN_ACTION) {
1708                         comname += ' ' + func.argument;
1709                         argsadded = true;
1710                 }
1711         }
1712
1713         string const shortcuts = toplevel_keymap->findbinding(func);
1714
1715         if (!shortcuts.empty()) {
1716                 comname += ": " + shortcuts;
1717         } else if (!argsadded && !func.argument.empty()) {
1718                 comname += ' ' + func.argument;
1719         }
1720
1721         if (!comname.empty()) {
1722                 comname = rtrim(comname);
1723                 dispatch_msg += '(' + comname + ')';
1724         }
1725
1726         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1727         if (!dispatch_msg.empty())
1728                 owner->message(dispatch_msg);
1729 }
1730
1731
1732 void LyXFunc::setupLocalKeymap()
1733 {
1734         keyseq.stdmap = keyseq.curmap = toplevel_keymap.get();
1735         cancel_meta_seq.stdmap = cancel_meta_seq.curmap = toplevel_keymap.get();
1736 }
1737
1738
1739 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1740 {
1741         string initpath = lyxrc.document_path;
1742         string filename(name);
1743
1744         if (view()->available()) {
1745                 string const trypath = owner->buffer()->filePath();
1746                 // If directory is writeable, use this as default.
1747                 if (IsDirWriteable(trypath))
1748                         initpath = trypath;
1749         }
1750
1751         static int newfile_number;
1752
1753         if (filename.empty()) {
1754                 filename = AddName(lyxrc.document_path,
1755                             "newfile" + tostr(++newfile_number) + ".lyx");
1756                 FileInfo fi(filename);
1757                 while (bufferlist.exists(filename) || fi.readable()) {
1758                         ++newfile_number;
1759                         filename = AddName(lyxrc.document_path,
1760                                     "newfile" + tostr(newfile_number) +
1761                                     ".lyx");
1762                         fi.newFile(filename);
1763                 }
1764         }
1765
1766         // The template stuff
1767         string templname;
1768         if (fromTemplate) {
1769                 FileDialog fileDlg(_("Select template file"),
1770                         LFUN_SELECT_FILE_SYNC,
1771                         make_pair(string(_("Documents|#o#O")),
1772                                   string(lyxrc.document_path)),
1773                         make_pair(string(_("Templates|#T#t")),
1774                                   string(lyxrc.template_path)));
1775
1776                 FileDialog::Result result =
1777                         fileDlg.open(lyxrc.template_path,
1778                                        _("*.lyx| LyX Documents (*.lyx)"));
1779
1780                 if (result.first == FileDialog::Later)
1781                         return;
1782
1783                 string const fname = result.second;
1784
1785                 if (fname.empty())
1786                         return;
1787                 templname = fname;
1788         }
1789
1790         view()->newFile(filename, templname, !name.empty());
1791 }
1792
1793
1794 void LyXFunc::open(string const & fname)
1795 {
1796         string initpath = lyxrc.document_path;
1797
1798         if (view()->available()) {
1799                 string const trypath = owner->buffer()->filePath();
1800                 // If directory is writeable, use this as default.
1801                 if (IsDirWriteable(trypath))
1802                         initpath = trypath;
1803         }
1804
1805         string filename;
1806
1807         if (fname.empty()) {
1808                 FileDialog fileDlg(_("Select document to open"),
1809                         LFUN_FILE_OPEN,
1810                         make_pair(string(_("Documents|#o#O")),
1811                                   string(lyxrc.document_path)),
1812                         make_pair(string(_("Examples|#E#e")),
1813                                   string(AddPath(system_lyxdir(), "examples"))));
1814
1815                 FileDialog::Result result =
1816                         fileDlg.open(initpath,
1817                                        _("*.lyx| LyX Documents (*.lyx)"));
1818
1819                 if (result.first == FileDialog::Later)
1820                         return;
1821
1822                 filename = result.second;
1823
1824                 // check selected filename
1825                 if (filename.empty()) {
1826                         owner->message(_("Canceled."));
1827                         return;
1828                 }
1829         } else
1830                 filename = fname;
1831
1832         // get absolute path of file and add ".lyx" to the filename if
1833         // necessary
1834         string const fullpath = FileSearch(string(), filename, "lyx");
1835         if (!fullpath.empty()) {
1836                 filename = fullpath;
1837         }
1838
1839         string const disp_fn(MakeDisplayPath(filename));
1840
1841         // if the file doesn't exist, let the user create one
1842         FileInfo const f(filename, true);
1843         if (!f.exist()) {
1844                 // the user specifically chose this name. Believe them.
1845                 view()->newFile(filename, "", true);
1846                 return;
1847         }
1848
1849         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1850
1851         string str2;
1852         if (view()->loadLyXFile(filename)) {
1853                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1854         } else {
1855                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1856         }
1857         owner->message(str2);
1858 }
1859
1860
1861 void LyXFunc::doImport(string const & argument)
1862 {
1863         string format;
1864         string filename = split(argument, format, ' ');
1865
1866         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1867                             << " file: " << filename << endl;
1868
1869         // need user interaction
1870         if (filename.empty()) {
1871                 string initpath = lyxrc.document_path;
1872
1873                 if (view()->available()) {
1874                         string const trypath = owner->buffer()->filePath();
1875                         // If directory is writeable, use this as default.
1876                         if (IsDirWriteable(trypath))
1877                                 initpath = trypath;
1878                 }
1879
1880                 string const text = bformat(_("Select %1$s file to import"),
1881                         formats.prettyName(format));
1882
1883                 FileDialog fileDlg(text,
1884                         LFUN_IMPORT,
1885                         make_pair(string(_("Documents|#o#O")),
1886                                   string(lyxrc.document_path)),
1887                         make_pair(string(_("Examples|#E#e")),
1888                                   string(AddPath(system_lyxdir(), "examples"))));
1889
1890                 string const extension = "*." + formats.extension(format)
1891                         + "| " + formats.prettyName(format)
1892                         + " (*." + formats.extension(format) + ')';
1893
1894                 FileDialog::Result result = fileDlg.open(initpath,
1895                                                            extension);
1896
1897                 if (result.first == FileDialog::Later)
1898                         return;
1899
1900                 filename = result.second;
1901
1902                 // check selected filename
1903                 if (filename.empty())
1904                         owner->message(_("Canceled."));
1905         }
1906
1907         if (filename.empty())
1908                 return;
1909
1910         // get absolute path of file
1911         filename = MakeAbsPath(filename);
1912
1913         string const lyxfile = ChangeExtension(filename, ".lyx");
1914
1915         // Check if the document already is open
1916         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1917                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1918                         owner->message(_("Canceled."));
1919                         return;
1920                 }
1921         }
1922
1923         // if the file exists already, and we didn't do
1924         // -i lyx thefile.lyx, warn
1925         if (FileInfo(lyxfile, true).exist() && filename != lyxfile) {
1926                 string const file = MakeDisplayPath(lyxfile, 30);
1927
1928                 string text = bformat(_("The document %1$s already exists.\n\n"
1929                         "Do you want to over-write that document?"), file);
1930                 int const ret = Alert::prompt(_("Over-write document?"),
1931                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1932
1933                 if (ret == 1) {
1934                         owner->message(_("Canceled."));
1935                         return;
1936                 }
1937         }
1938
1939         Importer::Import(owner, filename, format);
1940 }
1941
1942
1943 void LyXFunc::closeBuffer()
1944 {
1945         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1946                 if (bufferlist.empty()) {
1947                         // need this otherwise SEGV may occur while
1948                         // trying to set variables that don't exist
1949                         // since there's no current buffer
1950                         owner->getDialogs().hideBufferDependent();
1951                 } else {
1952                         view()->buffer(bufferlist.first());
1953                 }
1954         }
1955 }
1956
1957
1958 // Each "owner" should have it's own message method. lyxview and
1959 // the minibuffer would use the minibuffer, but lyxserver would
1960 // send an ERROR signal to its client.  Alejandro 970603
1961 // This func is bit problematic when it comes to NLS, to make the
1962 // lyx servers client be language indepenent we must not translate
1963 // strings sent to this func.
1964 void LyXFunc::setErrorMessage(string const & m) const
1965 {
1966         dispatch_buffer = m;
1967         errorstat = true;
1968 }
1969
1970
1971 void LyXFunc::setMessage(string const & m) const
1972 {
1973         dispatch_buffer = m;
1974 }
1975
1976
1977 void LyXFunc::setStatusMessage(string const & m) const
1978 {
1979         status_buffer = m;
1980 }
1981
1982
1983 string const LyXFunc::view_status_message()
1984 {
1985         // When meta-fake key is pressed, show the key sequence so far + "M-".
1986         if (wasMetaKey()) {
1987                 return keyseq.print() + "M-";
1988         }
1989
1990         // Else, when a non-complete key sequence is pressed,
1991         // show the available options.
1992         if (keyseq.length() > 0 && !keyseq.deleted()) {
1993                 return keyseq.printOptions();
1994         }
1995
1996         if (!view()->available())
1997                 return _("Welcome to LyX!");
1998
1999         return currentState(view());
2000 }
2001
2002
2003 BufferView * LyXFunc::view() const
2004 {
2005         BOOST_ASSERT(owner);
2006         return owner->view().get();
2007 }
2008
2009
2010 bool LyXFunc::wasMetaKey() const
2011 {
2012         return (meta_fake_bit != key_modifier::none);
2013 }