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