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