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