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