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