]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
In insets:
[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 "TextCache.h"
52 #include "undo.h"
53
54 #include "insets/insetcommand.h"
55 #include "insets/insetexternal.h"
56 #include "insets/insettabular.h"
57
58 #include "mathed/math_cursor.h"
59
60 #include "frontends/Alert.h"
61 #include "frontends/Dialogs.h"
62 #include "frontends/FileDialog.h"
63 #include "frontends/lyx_gui.h"
64 #include "frontends/LyXKeySym.h"
65 #include "frontends/LyXView.h"
66 #include "frontends/Menubar.h"
67 #include "frontends/Toolbar.h"
68
69 #include "support/FileInfo.h"
70 #include "support/filetools.h"
71 #include "support/forkedcontr.h"
72 #include "support/path.h"
73 #include "support/path_defines.h"
74 #include "support/tostr.h"
75 #include "support/std_sstream.h"
76 #include "support/os.h"
77
78 using bv_funcs::apply_freefont;
79 using bv_funcs::changeDepth;
80 using bv_funcs::currentState;
81 using bv_funcs::DEC_DEPTH;
82 using bv_funcs::freefont2string;
83 using bv_funcs::INC_DEPTH;
84 using bv_funcs::update_and_apply_freefont;
85
86 using lyx::support::AddName;
87 using lyx::support::AddPath;
88 using lyx::support::bformat;
89 using lyx::support::ChangeExtension;
90 using lyx::support::FileInfo;
91 using lyx::support::FileSearch;
92 using lyx::support::ForkedcallsController;
93 using lyx::support::i18nLibFileSearch;
94 using lyx::support::IsDirWriteable;
95 using lyx::support::IsFileReadable;
96 using lyx::support::isStrInt;
97 using lyx::support::MakeAbsPath;
98 using lyx::support::MakeDisplayPath;
99 using lyx::support::Path;
100 using lyx::support::rtrim;
101 using lyx::support::split;
102 using lyx::support::strToInt;
103 using lyx::support::strToUnsignedInt;
104 using lyx::support::system_lyxdir;
105 using lyx::support::token;
106 using lyx::support::trim;
107 using lyx::support::user_lyxdir;
108 using lyx::support::prefixIs;
109 using lyx::support::os::getTmpDir;
110
111 using std::endl;
112 using std::make_pair;
113 using std::string;
114 using std::istringstream;
115
116
117 extern BufferList bufferlist;
118 extern LyXServer * lyxserver;
119 extern bool selection_possible;
120
121 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
122
123 // (alkis)
124 extern tex_accent_struct get_accent(kb_action action);
125
126 extern void ShowLatexLog();
127
128
129 LyXFunc::LyXFunc(LyXView * o)
130         : owner(o),
131         encoded_last_key(0),
132         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
133         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
134         meta_fake_bit(key_modifier::none)
135 {
136 }
137
138
139 void LyXFunc::moveCursorUpdate()
140 {
141         LyXText * lt = view()->text;
142         if (lt->selection.mark())
143                 lt->setSelection();
144         view()->update();
145         view()->switchKeyMap();
146 }
147
148
149 void LyXFunc::handleKeyFunc(kb_action action)
150 {
151         char c = encoded_last_key;
152
153         if (keyseq.length()) {
154                 c = 0;
155         }
156
157         owner->getIntl().getTransManager()
158                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
159         // Need to clear, in case the minibuffer calls these
160         // actions
161         keyseq.clear();
162         // copied verbatim from do_accent_char
163         view()->update();
164         view()->getLyXText()->selection.cursor = view()->getLyXText()->cursor;
165 }
166
167
168 void LyXFunc::processKeySym(LyXKeySymPtr keysym,
169                             key_modifier::state state)
170 {
171         string argument;
172
173         if (lyxerr.debugging(Debug::KEY)) {
174                 lyxerr << "KeySym is "
175                        << keysym->getSymbolName()
176                        << endl;
177         }
178
179         // Do nothing if we have nothing (JMarc)
180         if (!keysym->isOK()) {
181                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
182                                    << endl;
183                 return;
184         }
185
186         if (keysym->isModifier()) {
187                 lyxerr[Debug::KEY] << "isModifier true" << endl;
188                 return;
189         }
190
191         Encoding const * encoding = view()->getEncoding();
192
193         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
194
195         // Do a one-deep top-level lookup for
196         // cancel and meta-fake keys. RVDK_PATCH_5
197         cancel_meta_seq.reset();
198
199         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
200         lyxerr[Debug::KEY] << "action first set to [" << func.action << ']' << endl;
201
202         // When not cancel or meta-fake, do the normal lookup.
203         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
204         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
205         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
206                 // remove Caps Lock and Mod2 as a modifiers
207                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
208                 lyxerr[Debug::KEY] << "action now set to ["
209                         << func.action << ']' << endl;
210         }
211
212         // Dont remove this unless you know what you are doing.
213         meta_fake_bit = key_modifier::none;
214
215         // can this happen now ?
216         if (func.action == LFUN_NOACTION) {
217                 func = FuncRequest(LFUN_PREFIX);
218         }
219
220         if (lyxerr.debugging(Debug::KEY)) {
221                 lyxerr << "Key [action="
222                        << func.action << "]["
223                        << keyseq.print() << ']'
224                        << endl;
225         }
226
227         // already here we know if it any point in going further
228         // why not return already here if action == -1 and
229         // num_bytes == 0? (Lgb)
230
231         if (keyseq.length() > 1) {
232                 owner->message(keyseq.print());
233         }
234
235
236         // Maybe user can only reach the key via holding down shift.
237         // Let's see. But only if shift is the only modifier
238         if (func.action == LFUN_UNKNOWN_ACTION &&
239             state == key_modifier::shift) {
240                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
241                 func = keyseq.addkey(keysym, key_modifier::none);
242                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
243         }
244
245         if (func.action == LFUN_UNKNOWN_ACTION) {
246                 // Hmm, we didn't match any of the keysequences. See
247                 // if it's normal insertable text not already covered
248                 // by a binding
249                 if (keysym->isText() && keyseq.length() == 1) {
250                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
251                         func = FuncRequest(LFUN_SELFINSERT);
252                 } else {
253                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
254                         owner->message(_("Unknown function."));
255                         return;
256                 }
257         }
258
259         if (func.action == LFUN_SELFINSERT) {
260                 if (encoded_last_key != 0) {
261                         string arg;
262                         arg += encoded_last_key;
263
264                         dispatch(FuncRequest(view(), LFUN_SELFINSERT, arg));
265
266                         lyxerr[Debug::KEY] << "SelfInsert arg[`"
267                                    << argument << "']" << endl;
268                 }
269         } else {
270                 dispatch(func);
271         }
272 }
273
274
275 FuncStatus LyXFunc::getStatus(FuncRequest const & ev) const
276 {
277         FuncStatus flag;
278         Buffer * buf = owner->buffer();
279
280         if (ev.action == LFUN_NOACTION) {
281                 setStatusMessage(N_("Nothing to do"));
282                 return flag.disabled(true);
283         }
284
285         switch (ev.action) {
286         case LFUN_UNKNOWN_ACTION:
287 #ifndef HAVE_LIBAIKSAURUS
288         case LFUN_THESAURUS_ENTRY:
289 #endif
290                 flag.unknown(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->localDispatch(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->cursorIRow();
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->cursorIRow();
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()->localDispatch(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->localDispatch(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->localDispatch(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                 // We also need to empty the textcache so that
1586                 // the buffer will be formatted correctly after
1587                 // a zoom change.
1588                 textcache.clear();
1589                 // Of course we should only do the resize and the textcache.clear
1590                 // if values really changed...but not very important right now. (Lgb)
1591                 // All visible buffers will need resize
1592                 view()->resize();
1593                 view()->update();
1594         }
1595         break;
1596
1597         case LFUN_SET_COLOR:
1598         {
1599                 string lyx_name;
1600                 string const x11_name = split(argument, lyx_name, ' ');
1601                 if (lyx_name.empty() || x11_name.empty()) {
1602                         setErrorMessage(N_("Syntax: set-color <lyx_name>"
1603                                                 " <x11_name>"));
1604                         break;
1605                         }
1606
1607                 bool const graphicsbg_changed =
1608                         (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1609                          x11_name != lcolor.getX11Name(LColor::graphicsbg));
1610
1611                 if (!lcolor.setColor(lyx_name, x11_name)) {
1612                         setErrorMessage(
1613                                 bformat(_("Set-color \"%1$s\" failed "
1614                                                   "- color is undefined or "
1615                                                   "may not be redefined"), lyx_name));
1616                         break;
1617                 }
1618
1619                 lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1620
1621                 if (graphicsbg_changed) {
1622 #ifdef WITH_WARNINGS
1623 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1624 #endif
1625 #if 0
1626                         lyx::graphics::GCache & gc =
1627                                 lyx::graphics::GCache::get();
1628                         gc.changeDisplay(true);
1629 #endif
1630                 }
1631
1632                 view()->update();
1633                 break;
1634         }
1635
1636         case LFUN_MESSAGE:
1637                 owner->message(argument);
1638                 break;
1639
1640         case LFUN_FORKS_KILL:
1641         {
1642                 if (!isStrInt(argument))
1643                         break;
1644
1645                 pid_t const pid = strToInt(argument);
1646                 ForkedcallsController & fcc = ForkedcallsController::get();
1647                 fcc.kill(pid);
1648                 break;
1649         }
1650
1651         case LFUN_TOOLTIPS_TOGGLE:
1652                 owner->getDialogs().toggleTooltips();
1653                 break;
1654
1655         case LFUN_EXTERNAL_EDIT: {
1656                 InsetExternal().localDispatch(FuncRequest(view(), action, argument));
1657                 break;
1658         }
1659
1660         default:
1661                 // Then if it was none of the above
1662                 // Trying the BufferView::pimpl dispatch:
1663                 if (!view()->dispatch(func))
1664                         lyxerr << "A truly unknown func ["
1665                                << lyxaction.getActionName(func.action) << "]!"
1666                                << endl;
1667                 break;
1668         } // end of switch
1669
1670 exit_with_message:
1671
1672         view()->owner()->updateLayoutChoice();
1673
1674         if (view()->available()) {
1675                 if (view()->fitCursor()) {
1676                         //lyxerr << "LyXFunc->fitCursor->update" << endl;
1677                         view()->update();
1678                 }
1679
1680                 // If we executed a mutating lfun, mark the buffer as dirty
1681                 if (!getStatus(func).disabled()
1682                     && !lyxaction.funcHasFlag(func.action, LyXAction::NoBuffer)
1683                     && !lyxaction.funcHasFlag(func.action, LyXAction::ReadOnly))
1684                         view()->buffer()->markDirty();
1685         }
1686
1687         sendDispatchMessage(getMessage(), func, verbose);
1688 }
1689
1690
1691 void LyXFunc::sendDispatchMessage(string const & msg,
1692                                   FuncRequest const & func, bool verbose)
1693 {
1694         owner->updateMenubar();
1695         owner->updateToolbar();
1696
1697         if (func.action == LFUN_SELFINSERT || !verbose) {
1698                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1699                 if (!msg.empty())
1700                         owner->message(msg);
1701                 return;
1702         }
1703
1704         string dispatch_msg = msg;
1705         if (!dispatch_msg.empty())
1706                 dispatch_msg += ' ';
1707
1708         string comname = lyxaction.getActionName(func.action);
1709
1710         bool argsadded = false;
1711
1712         if (!func.argument.empty()) {
1713                 if (func.action != LFUN_UNKNOWN_ACTION) {
1714                         comname += ' ' + func.argument;
1715                         argsadded = true;
1716                 }
1717         }
1718
1719         string const shortcuts = toplevel_keymap->findbinding(func);
1720
1721         if (!shortcuts.empty()) {
1722                 comname += ": " + shortcuts;
1723         } else if (!argsadded && !func.argument.empty()) {
1724                 comname += ' ' + func.argument;
1725         }
1726
1727         if (!comname.empty()) {
1728                 comname = rtrim(comname);
1729                 dispatch_msg += '(' + comname + ')';
1730         }
1731
1732         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1733         if (!dispatch_msg.empty())
1734                 owner->message(dispatch_msg);
1735 }
1736
1737
1738 void LyXFunc::setupLocalKeymap()
1739 {
1740         keyseq.stdmap = keyseq.curmap = toplevel_keymap.get();
1741         cancel_meta_seq.stdmap = cancel_meta_seq.curmap = toplevel_keymap.get();
1742 }
1743
1744
1745 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1746 {
1747         string initpath = lyxrc.document_path;
1748         string filename(name);
1749
1750         if (view()->available()) {
1751                 string const trypath = owner->buffer()->filePath();
1752                 // If directory is writeable, use this as default.
1753                 if (IsDirWriteable(trypath))
1754                         initpath = trypath;
1755         }
1756
1757         static int newfile_number;
1758
1759         if (filename.empty()) {
1760                 filename = AddName(lyxrc.document_path,
1761                             "newfile" + tostr(++newfile_number) + ".lyx");
1762                 FileInfo fi(filename);
1763                 while (bufferlist.exists(filename) || fi.readable()) {
1764                         ++newfile_number;
1765                         filename = AddName(lyxrc.document_path,
1766                                     "newfile" + tostr(newfile_number) +
1767                                     ".lyx");
1768                         fi.newFile(filename);
1769                 }
1770         }
1771
1772         // The template stuff
1773         string templname;
1774         if (fromTemplate) {
1775                 FileDialog fileDlg(_("Select template file"),
1776                         LFUN_SELECT_FILE_SYNC,
1777                         make_pair(string(_("Documents|#o#O")),
1778                                   string(lyxrc.document_path)),
1779                         make_pair(string(_("Templates|#T#t")),
1780                                   string(lyxrc.template_path)));
1781
1782                 FileDialog::Result result =
1783                         fileDlg.open(lyxrc.template_path,
1784                                        _("*.lyx| LyX Documents (*.lyx)"));
1785
1786                 if (result.first == FileDialog::Later)
1787                         return;
1788
1789                 string const fname = result.second;
1790
1791                 if (fname.empty())
1792                         return;
1793                 templname = fname;
1794         }
1795
1796         view()->newFile(filename, templname, !name.empty());
1797 }
1798
1799
1800 void LyXFunc::open(string const & fname)
1801 {
1802         string initpath = lyxrc.document_path;
1803
1804         if (view()->available()) {
1805                 string const trypath = owner->buffer()->filePath();
1806                 // If directory is writeable, use this as default.
1807                 if (IsDirWriteable(trypath))
1808                         initpath = trypath;
1809         }
1810
1811         string filename;
1812
1813         if (fname.empty()) {
1814                 FileDialog fileDlg(_("Select document to open"),
1815                         LFUN_FILE_OPEN,
1816                         make_pair(string(_("Documents|#o#O")),
1817                                   string(lyxrc.document_path)),
1818                         make_pair(string(_("Examples|#E#e")),
1819                                   string(AddPath(system_lyxdir(), "examples"))));
1820
1821                 FileDialog::Result result =
1822                         fileDlg.open(initpath,
1823                                        _("*.lyx| LyX Documents (*.lyx)"));
1824
1825                 if (result.first == FileDialog::Later)
1826                         return;
1827
1828                 filename = result.second;
1829
1830                 // check selected filename
1831                 if (filename.empty()) {
1832                         owner->message(_("Canceled."));
1833                         return;
1834                 }
1835         } else
1836                 filename = fname;
1837
1838         // get absolute path of file and add ".lyx" to the filename if
1839         // necessary
1840         string const fullpath = FileSearch(string(), filename, "lyx");
1841         if (!fullpath.empty()) {
1842                 filename = fullpath;
1843         }
1844
1845         string const disp_fn(MakeDisplayPath(filename));
1846
1847         // if the file doesn't exist, let the user create one
1848         FileInfo const f(filename, true);
1849         if (!f.exist()) {
1850                 // the user specifically chose this name. Believe them.
1851                 view()->newFile(filename, "", true);
1852                 return;
1853         }
1854
1855         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1856
1857         string str2;
1858         if (view()->loadLyXFile(filename)) {
1859                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1860         } else {
1861                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1862         }
1863         owner->message(str2);
1864 }
1865
1866
1867 void LyXFunc::doImport(string const & argument)
1868 {
1869         string format;
1870         string filename = split(argument, format, ' ');
1871
1872         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1873                             << " file: " << filename << endl;
1874
1875         // need user interaction
1876         if (filename.empty()) {
1877                 string initpath = lyxrc.document_path;
1878
1879                 if (view()->available()) {
1880                         string const trypath = owner->buffer()->filePath();
1881                         // If directory is writeable, use this as default.
1882                         if (IsDirWriteable(trypath))
1883                                 initpath = trypath;
1884                 }
1885
1886                 string const text = bformat(_("Select %1$s file to import"),
1887                         formats.prettyName(format));
1888
1889                 FileDialog fileDlg(text,
1890                         LFUN_IMPORT,
1891                         make_pair(string(_("Documents|#o#O")),
1892                                   string(lyxrc.document_path)),
1893                         make_pair(string(_("Examples|#E#e")),
1894                                   string(AddPath(system_lyxdir(), "examples"))));
1895
1896                 string const extension = "*." + formats.extension(format)
1897                         + "| " + formats.prettyName(format)
1898                         + " (*." + formats.extension(format) + ')';
1899
1900                 FileDialog::Result result = fileDlg.open(initpath,
1901                                                            extension);
1902
1903                 if (result.first == FileDialog::Later)
1904                         return;
1905
1906                 filename = result.second;
1907
1908                 // check selected filename
1909                 if (filename.empty())
1910                         owner->message(_("Canceled."));
1911         }
1912
1913         if (filename.empty())
1914                 return;
1915
1916         // get absolute path of file
1917         filename = MakeAbsPath(filename);
1918
1919         string const lyxfile = ChangeExtension(filename, ".lyx");
1920
1921         // Check if the document already is open
1922         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1923                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1924                         owner->message(_("Canceled."));
1925                         return;
1926                 }
1927         }
1928
1929         // if the file exists already, and we didn't do
1930         // -i lyx thefile.lyx, warn
1931         if (FileInfo(lyxfile, true).exist() && filename != lyxfile) {
1932                 string const file = MakeDisplayPath(lyxfile, 30);
1933
1934                 string text = bformat(_("The document %1$s already exists.\n\n"
1935                         "Do you want to over-write that document?"), file);
1936                 int const ret = Alert::prompt(_("Over-write document?"),
1937                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1938
1939                 if (ret == 1) {
1940                         owner->message(_("Canceled."));
1941                         return;
1942                 }
1943         }
1944
1945         Importer::Import(owner, filename, format);
1946 }
1947
1948
1949 void LyXFunc::closeBuffer()
1950 {
1951         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1952                 if (bufferlist.empty()) {
1953                         // need this otherwise SEGV may occur while
1954                         // trying to set variables that don't exist
1955                         // since there's no current buffer
1956                         owner->getDialogs().hideBufferDependent();
1957                 } else {
1958                         view()->buffer(bufferlist.first());
1959                 }
1960         }
1961 }
1962
1963
1964 // Each "owner" should have it's own message method. lyxview and
1965 // the minibuffer would use the minibuffer, but lyxserver would
1966 // send an ERROR signal to its client.  Alejandro 970603
1967 // This func is bit problematic when it comes to NLS, to make the
1968 // lyx servers client be language indepenent we must not translate
1969 // strings sent to this func.
1970 void LyXFunc::setErrorMessage(string const & m) const
1971 {
1972         dispatch_buffer = m;
1973         errorstat = true;
1974 }
1975
1976
1977 void LyXFunc::setMessage(string const & m) const
1978 {
1979         dispatch_buffer = m;
1980 }
1981
1982
1983 void LyXFunc::setStatusMessage(string const & m) const
1984 {
1985         status_buffer = m;
1986 }
1987
1988
1989 string const LyXFunc::view_status_message()
1990 {
1991         // When meta-fake key is pressed, show the key sequence so far + "M-".
1992         if (wasMetaKey()) {
1993                 return keyseq.print() + "M-";
1994         }
1995
1996         // Else, when a non-complete key sequence is pressed,
1997         // show the available options.
1998         if (keyseq.length() > 0 && !keyseq.deleted()) {
1999                 return keyseq.printOptions();
2000         }
2001
2002         if (!view()->available())
2003                 return _("Welcome to LyX!");
2004
2005         return currentState(view());
2006 }
2007
2008
2009 BufferView * LyXFunc::view() const
2010 {
2011         BOOST_ASSERT(owner);
2012         return owner->view().get();
2013 }
2014
2015
2016 bool LyXFunc::wasMetaKey() const
2017 {
2018         return (meta_fake_bit != key_modifier::none);
2019 }