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