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