]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
3420f6443de1ae4c88ea88866fe2ae280e685638
[lyx.git] / src / lyxfunc.C
1 /**
2  * \file lyxfunc.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "lyxfunc.h"
23
24 #include "BranchList.h"
25 #include "buffer.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
30 #include "cursor.h"
31 #include "CutAndPaste.h"
32 #include "debug.h"
33 #include "dispatchresult.h"
34 #include "encoding.h"
35 #include "errorlist.h"
36 #include "exporter.h"
37 #include "format.h"
38 #include "funcrequest.h"
39 #include "gettext.h"
40 #include "importer.h"
41 #include "insetiterator.h"
42 #include "intl.h"
43 #include "kbmap.h"
44 #include "language.h"
45 #include "LColor.h"
46 #include "lyx_cb.h"
47 #include "LyXAction.h"
48 #include "lyxfind.h"
49 #include "lyxlex.h"
50 #include "lyxrc.h"
51 #include "lyxrow.h"
52 #include "lyxserver.h"
53 #include "lyxtextclasslist.h"
54 #include "lyxvc.h"
55 #include "paragraph.h"
56 #include "pariterator.h"
57 #include "ParagraphParameters.h"
58 #include "undo.h"
59
60 #include "insets/insetbox.h"
61 #include "insets/insetbranch.h"
62 #include "insets/insetcommand.h"
63 #include "insets/insetert.h"
64 #include "insets/insetexternal.h"
65 #include "insets/insetfloat.h"
66 #include "insets/insetgraphics.h"
67 #include "insets/insetnote.h"
68 #include "insets/insettabular.h"
69 #include "insets/insetvspace.h"
70 #include "insets/insetwrap.h"
71
72 #include "frontends/Alert.h"
73 #include "frontends/Dialogs.h"
74 #include "frontends/FileDialog.h"
75 #include "frontends/lyx_gui.h"
76 #include "frontends/LyXKeySym.h"
77 #include "frontends/LyXView.h"
78 #include "frontends/Menubar.h"
79 #include "frontends/Toolbars.h"
80
81 #include "support/filefilterlist.h"
82 #include "support/FileInfo.h"
83 #include "support/filetools.h"
84 #include "support/forkedcontr.h"
85 #include "support/lstrings.h"
86 #include "support/path.h"
87 #include "support/package.h"
88 #include "support/systemcall.h"
89 #include "support/convert.h"
90 #include "support/os.h"
91
92 #include <sstream>
93
94 using bv_funcs::freefont2string;
95
96 using lyx::support::AddName;
97 using lyx::support::AddPath;
98 using lyx::support::bformat;
99 using lyx::support::ChangeExtension;
100 using lyx::support::contains;
101 using lyx::support::FileFilterList;
102 using lyx::support::FileInfo;
103 using lyx::support::FileSearch;
104 using lyx::support::ForkedcallsController;
105 using lyx::support::i18nLibFileSearch;
106 using lyx::support::IsDirWriteable;
107 using lyx::support::IsFileReadable;
108 using lyx::support::isStrInt;
109 using lyx::support::MakeAbsPath;
110 using lyx::support::MakeDisplayPath;
111 using lyx::support::package;
112 using lyx::support::Path;
113 using lyx::support::QuoteName;
114 using lyx::support::rtrim;
115 using lyx::support::split;
116 using lyx::support::strToInt;
117 using lyx::support::strToUnsignedInt;
118 using lyx::support::subst;
119 using lyx::support::Systemcall;
120 using lyx::support::token;
121 using lyx::support::trim;
122 using lyx::support::prefixIs;
123
124 using std::endl;
125 using std::make_pair;
126 using std::pair;
127 using std::string;
128 using std::istringstream;
129
130 namespace biblio = lyx::biblio;
131
132
133 extern BufferList bufferlist;
134 extern LyXServer * lyxserver;
135
136 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
137
138 // (alkis)
139 extern tex_accent_struct get_accent(kb_action action);
140
141
142 namespace {
143
144 bool getStatus(LCursor cursor,
145                FuncRequest const & cmd, FuncStatus & status)
146 {
147         // This is, of course, a mess. Better create a new doc iterator and use
148         // this in Inset::getStatus. This might require an additional
149         // BufferView * arg, though (which should be avoided)
150         //LCursor safe = *this;
151         bool res = false;
152         for ( ; cursor.size(); cursor.pop()) {
153                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
154                 DocIterator::idx_type & idx = cursor.idx();
155                 DocIterator::idx_type const lastidx = cursor.lastidx();
156
157                 if (idx > lastidx) {
158                         lyxerr << "wrong idx " << idx << ", max is " << lastidx
159                                 << ". Trying to correct this."  << endl;
160                         idx = lastidx;
161                 }
162
163                 DocIterator::pit_type & pit = cursor.pit();
164                 DocIterator::pit_type const lastpit = cursor.lastpit();
165
166                 if (pit > lastpit) {
167                         lyxerr << "wrong par " << pit << ", max is " << lastpit
168                                 << ". Trying to correct this."  << endl;
169                         pit = lastpit;
170                 }
171
172                 DocIterator::pos_type & pos = cursor.pos();
173                 DocIterator::pos_type const lastpos = cursor.lastpos();
174
175                 if (pos > lastpos) {
176                         lyxerr << "wrong pos " << pos << ", max is " << lastpos
177                                 << ". Trying to correct this."  << endl;
178                         pos = lastpos;
179                 }
180
181                 // The inset's getStatus() will return 'true' if it made
182                 // a definitive decision on whether it want to handle the
183                 // request or not. The result of this decision is put into
184                 // the 'status' parameter.
185                 if (cursor.inset().getStatus(cursor, cmd, status)) {
186                         res = true;
187                         break;
188                 }
189         }
190         return res;
191 }
192
193 }
194
195 LyXFunc::LyXFunc(LyXView * lv)
196         : owner(lv),
197         encoded_last_key(0),
198         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
199         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
200         meta_fake_bit(key_modifier::none)
201 {
202 }
203
204
205 void LyXFunc::handleKeyFunc(kb_action action)
206 {
207         char c = encoded_last_key;
208
209         if (keyseq.length()) {
210                 c = 0;
211         }
212
213         owner->getIntl().getTransManager()
214                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
215         // Need to clear, in case the minibuffer calls these
216         // actions
217         keyseq.clear();
218         // copied verbatim from do_accent_char
219         view()->cursor().resetAnchor();
220         view()->update();
221 }
222
223
224 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
225 {
226         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
227
228         // Do nothing if we have nothing (JMarc)
229         if (!keysym->isOK()) {
230                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
231                                    << endl;
232                 return;
233         }
234
235         if (keysym->isModifier()) {
236                 lyxerr[Debug::KEY] << "isModifier true" << endl;
237                 return;
238         }
239
240         Encoding const * encoding = view()->cursor().getEncoding();
241
242         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
243
244         // Do a one-deep top-level lookup for
245         // cancel and meta-fake keys. RVDK_PATCH_5
246         cancel_meta_seq.reset();
247
248         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
249         lyxerr[Debug::KEY] << "action first set to [" << func.action << ']' << endl;
250
251         // When not cancel or meta-fake, do the normal lookup.
252         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
253         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
254         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
255                 // remove Caps Lock and Mod2 as a modifiers
256                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
257                 lyxerr[Debug::KEY] << "action now set to ["
258                         << func.action << ']' << endl;
259         }
260
261         // Dont remove this unless you know what you are doing.
262         meta_fake_bit = key_modifier::none;
263
264         // can this happen now ?
265         if (func.action == LFUN_NOACTION) {
266                 func = FuncRequest(LFUN_PREFIX);
267         }
268
269         if (lyxerr.debugging(Debug::KEY)) {
270                 lyxerr << "Key [action="
271                        << func.action << "]["
272                        << keyseq.print() << ']'
273                        << endl;
274         }
275
276         // already here we know if it any point in going further
277         // why not return already here if action == -1 and
278         // num_bytes == 0? (Lgb)
279
280         if (keyseq.length() > 1) {
281                 owner->message(keyseq.print());
282         }
283
284
285         // Maybe user can only reach the key via holding down shift.
286         // Let's see. But only if shift is the only modifier
287         if (func.action == LFUN_UNKNOWN_ACTION &&
288             state == key_modifier::shift) {
289                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
290                 func = keyseq.addkey(keysym, key_modifier::none);
291                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
292         }
293
294         if (func.action == LFUN_UNKNOWN_ACTION) {
295                 // Hmm, we didn't match any of the keysequences. See
296                 // if it's normal insertable text not already covered
297                 // by a binding
298                 if (keysym->isText() && keyseq.length() == 1) {
299                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
300                         func = FuncRequest(LFUN_SELFINSERT);
301                 } else {
302                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
303                         owner->message(_("Unknown function."));
304                         return;
305                 }
306         }
307
308         if (func.action == LFUN_SELFINSERT) {
309                 if (encoded_last_key != 0) {
310                         string arg(1, encoded_last_key);
311                         dispatch(FuncRequest(LFUN_SELFINSERT, arg));
312                         lyxerr[Debug::KEY]
313                                 << "SelfInsert arg[`" << arg << "']" << endl;
314                 }
315         } else {
316                 dispatch(func);
317         }
318 }
319
320
321 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
322 {
323         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
324         FuncStatus flag;
325         LCursor & cur = view()->cursor();
326
327         /* In LyX/Mac, when a dialog is open, the menus of the
328            application can still be accessed without giving focus to
329            the main window. In this case, we want to disable the menu
330            entries that are buffer-related.
331         */
332         Buffer * buf;
333         if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
334                 buf = 0;
335         else
336                 buf = owner->buffer();
337
338         if (cmd.action == LFUN_NOACTION) {
339                 flag.message(N_("Nothing to do"));
340                 flag.enabled(false);
341                 return flag;
342         }
343
344         switch (cmd.action) {
345         case LFUN_UNKNOWN_ACTION:
346 #ifndef HAVE_LIBAIKSAURUS
347         case LFUN_THESAURUS_ENTRY:
348 #endif
349                 flag.unknown(true);
350                 flag.enabled(false);
351                 break;
352         default:
353                 flag |= lyx_gui::getStatus(cmd);
354         }
355
356         if (flag.unknown()) {
357                 flag.message(N_("Unknown action"));
358                 return flag;
359         }
360
361         if (!flag.enabled()) {
362                 if (flag.message().empty())
363                         flag.message(N_("Command disabled"));
364                 return flag;
365         }
366
367         // Check whether we need a buffer
368         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
369                 // no, exit directly
370                 flag.message(N_("Command not allowed with"
371                                     "out any document open"));
372                 flag.enabled(false);
373                 return flag;
374         }
375
376         // I would really like to avoid having this switch and rather try to
377         // encode this in the function itself.
378         // -- And I'd rather let an inset decide which LFUNs it is willing
379         // to handle (Andre')
380         bool enable = true;
381         switch (cmd.action) {
382         case LFUN_TOOLTIPS_TOGGLE:
383                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
384                 break;
385
386         case LFUN_READ_ONLY_TOGGLE:
387                 flag.setOnOff(buf->isReadonly());
388                 break;
389
390         case LFUN_SWITCHBUFFER:
391                 // toggle on the current buffer, but do not toggle off
392                 // the other ones (is that a good idea?)
393                 if (cmd.argument == buf->fileName())
394                         flag.setOnOff(true);
395                 break;
396
397         case LFUN_EXPORT:
398                 enable = cmd.argument == "custom"
399                         || Exporter::IsExportable(*buf, cmd.argument);
400                 break;
401         case LFUN_CUT:
402         case LFUN_COPY:
403                 enable = cur.selection();
404                 break;
405
406         case LFUN_RUNCHKTEX:
407                 enable = buf->isLatex() && lyxrc.chktex_command != "none";
408                 break;
409
410         case LFUN_BUILDPROG:
411                 enable = Exporter::IsExportable(*buf, "program");
412                 break;
413
414         case LFUN_LAYOUT_TABULAR:
415                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
416                 break;
417
418         case LFUN_LAYOUT:
419         case LFUN_LAYOUT_PARAGRAPH:
420                 enable = !cur.inset().forceDefaultParagraphs(&cur.inset());
421                 break;
422
423         case LFUN_VC_REGISTER:
424                 enable = !buf->lyxvc().inUse();
425                 break;
426         case LFUN_VC_CHECKIN:
427                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
428                 break;
429         case LFUN_VC_CHECKOUT:
430                 enable = buf->lyxvc().inUse() && buf->isReadonly();
431                 break;
432         case LFUN_VC_REVERT:
433         case LFUN_VC_UNDO:
434                 enable = buf->lyxvc().inUse();
435                 break;
436         case LFUN_MENURELOAD:
437                 enable = !buf->isUnnamed() && !buf->isClean();
438                 break;
439
440
441         case LFUN_INSET_SETTINGS: {
442                 enable = false;
443                 if (!cur.size())
444                         break;
445                 UpdatableInset * inset = cur.inset().asUpdatableInset();
446                 lyxerr << "inset: " << inset << endl;
447                 if (!inset)
448                         break;
449
450                 InsetBase::Code code = inset->lyxCode();
451                 switch (code) {
452                         case InsetBase::TABULAR_CODE:
453                                 enable = cmd.argument == "tabular";
454                                 break;
455                         case InsetBase::ERT_CODE:
456                                 enable = cmd.argument == "ert";
457                                 break;
458                         case InsetBase::FLOAT_CODE:
459                                 enable = cmd.argument == "float";
460                                 break;
461                         case InsetBase::WRAP_CODE:
462                                 enable = cmd.argument == "wrap";
463                                 break;
464                         case InsetBase::NOTE_CODE:
465                                 enable = cmd.argument == "note";
466                                 break;
467                         case InsetBase::BRANCH_CODE:
468                                 enable = cmd.argument == "branch";
469                                 break;
470                         case InsetBase::BOX_CODE:
471                                 enable = cmd.argument == "box";
472                                 break;
473                         default:
474                                 break;
475                 }
476                 break;
477         }
478
479         case LFUN_DIALOG_SHOW: {
480                 string const name = cmd.getArg(0);
481                 if (!buf)
482                         enable = name == "aboutlyx"
483                                 || name == "file"
484                                 || name == "forks"
485                                 || name == "prefs"
486                                 || name == "texinfo";
487                 else if (name == "print")
488                         enable = Exporter::IsExportable(*buf, "dvi")
489                                 && lyxrc.print_command != "none";
490                 else if (name == "character")
491                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
492                 else if (name == "vclog")
493                         enable = buf->lyxvc().inUse();
494                 else if (name == "latexlog")
495                         enable = IsFileReadable(buf->getLogName().second);
496                 break;
497         }
498
499         case LFUN_DIALOG_UPDATE: {
500                 string const name = cmd.getArg(0);
501                 if (!buf)
502                         enable = name == "prefs";
503                 break;
504         }
505
506         // this one is difficult to get right. As a half-baked
507         // solution, we consider only the first action of the sequence
508         case LFUN_SEQUENCE: {
509                 // argument contains ';'-terminated commands
510                 string const firstcmd = token(cmd.argument, ';', 0);
511                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
512                 func.origin = cmd.origin;
513                 flag = getStatus(func);
514         }
515
516         case LFUN_MENUNEW:
517         case LFUN_MENUNEWTMPLT:
518         case LFUN_WORDFINDFORWARD:
519         case LFUN_WORDFINDBACKWARD:
520         case LFUN_PREFIX:
521         case LFUN_EXEC_COMMAND:
522         case LFUN_CANCEL:
523         case LFUN_META_FAKE:
524         case LFUN_CLOSEBUFFER:
525         case LFUN_MENUWRITE:
526         case LFUN_WRITEAS:
527         case LFUN_UPDATE:
528         case LFUN_PREVIEW:
529         case LFUN_IMPORT:
530         case LFUN_QUIT:
531         case LFUN_TOCVIEW:
532         case LFUN_AUTOSAVE:
533         case LFUN_RECONFIGURE:
534         case LFUN_HELP_OPEN:
535         case LFUN_FILE_NEW:
536         case LFUN_FILE_OPEN:
537         case LFUN_DROP_LAYOUTS_CHOICE:
538         case LFUN_MENU_OPEN_BY_NAME:
539         case LFUN_GETNAME:
540         case LFUN_NOTIFY:
541         case LFUN_GOTOFILEROW:
542         case LFUN_GOTO_PARAGRAPH:
543         case LFUN_DIALOG_SHOW_NEW_INSET:
544         case LFUN_DIALOG_SHOW_NEXT_INSET:
545         case LFUN_DIALOG_HIDE:
546         case LFUN_DIALOG_DISCONNECT_INSET:
547         case LFUN_CHILDOPEN:
548         case LFUN_TOGGLECURSORFOLLOW:
549         case LFUN_KMAP_OFF:
550         case LFUN_KMAP_PRIM:
551         case LFUN_KMAP_SEC:
552         case LFUN_KMAP_TOGGLE:
553         case LFUN_REPEAT:
554         case LFUN_EXPORT_CUSTOM:
555         case LFUN_PRINT:
556         case LFUN_SAVEPREFERENCES:
557         case LFUN_SCREEN_FONT_UPDATE:
558         case LFUN_SET_COLOR:
559         case LFUN_MESSAGE:
560         case LFUN_EXTERNAL_EDIT:
561         case LFUN_GRAPHICS_EDIT:
562         case LFUN_ALL_INSETS_TOGGLE:
563         case LFUN_LANGUAGE_BUFFER:
564         case LFUN_TEXTCLASS_APPLY:
565         case LFUN_TEXTCLASS_LOAD:
566         case LFUN_SAVE_AS_DEFAULT:
567         case LFUN_BUFFERPARAMS_APPLY:
568         case LFUN_LYXRC_APPLY:
569         case LFUN_NEXTBUFFER:
570         case LFUN_PREVIOUSBUFFER:
571                 // these are handled in our dispatch()
572                 break;
573
574         default:
575
576                 if (!::getStatus(cur, cmd, flag))
577                         flag = view()->getStatus(cmd);
578         }
579
580         if (!enable)
581                 flag.enabled(false);
582
583         // Can we use a readonly buffer?
584         if (buf && buf->isReadonly()
585             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
586             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
587                 flag.message(N_("Document is read-only"));
588                 flag.enabled(false);
589         }
590
591         // the default error message if we disable the command
592         if (!flag.enabled() && flag.message().empty())
593                 flag.message(N_("Command disabled"));
594
595         return flag;
596 }
597
598
599 namespace {
600
601 bool ensureBufferClean(BufferView * bv)
602 {
603         Buffer & buf = *bv->buffer();
604         if (buf.isClean())
605                 return true;
606
607         string const file = MakeDisplayPath(buf.fileName(), 30);
608         string text = bformat(_("The document %1$s has unsaved "
609                                 "changes.\n\nDo you want to save "
610                                 "the document?"), file);
611         int const ret = Alert::prompt(_("Save changed document?"),
612                                       text, 0, 1, _("&Save"),
613                                       _("&Cancel"));
614
615         if (ret == 0)
616                 bv->owner()->dispatch(FuncRequest(LFUN_MENUWRITE));
617
618         return buf.isClean();
619 }
620
621
622 void showPrintError(string const & name)
623 {
624         string str = bformat(_("Could not print the document %1$s.\n"
625                                "Check that your printer is set up correctly."),
626                              MakeDisplayPath(name, 50));
627         Alert::error(_("Print document failed"), str);
628 }
629
630
631 void loadTextclass(string const & name)
632 {
633         std::pair<bool, lyx::textclass_type> const tc_pair =
634                 textclasslist.NumberOfClass(name);
635
636         if (!tc_pair.first) {
637                 lyxerr << "Document class \"" << name
638                        << "\" does not exist."
639                        << std::endl;
640                 return;
641         }
642
643         lyx::textclass_type const tc = tc_pair.second;
644
645         if (!textclasslist[tc].load()) {
646                 string s = bformat(_("The document could not be converted\n"
647                                      "into the document class %1$s."),
648                                    textclasslist[tc].name());
649                 Alert::error(_("Could not change class"), s);
650         }
651 }
652
653
654 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
655
656 } //namespace anon
657
658
659 void LyXFunc::dispatch(FuncRequest const & cmd)
660 {
661         BOOST_ASSERT(view());
662         string const argument = cmd.argument;
663         kb_action const action = cmd.action;
664
665         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
666         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
667
668         // we have not done anything wrong yet.
669         errorstat = false;
670         dispatch_buffer.erase();
671
672         bool update = true;
673
674         FuncStatus const flag = getStatus(cmd);
675         if (!flag.enabled()) {
676                 // We cannot use this function here
677                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
678                        << lyxaction.getActionName(action)
679                        << " [" << action << "] is disabled at this location"
680                        << endl;
681                 setErrorMessage(flag.message());
682         } else {
683
684                 if (view()->available())
685                         view()->hideCursor();
686
687                 switch (action) {
688
689                 case LFUN_WORDFINDFORWARD:
690                 case LFUN_WORDFINDBACKWARD: {
691                         static string last_search;
692                         string searched_string;
693
694                         if (!argument.empty()) {
695                                 last_search = argument;
696                                 searched_string = argument;
697                         } else {
698                                 searched_string = last_search;
699                         }
700
701                         if (searched_string.empty())
702                                 break;
703
704                         bool const fw = action == LFUN_WORDFINDFORWARD;
705                         string const data =
706                                 lyx::find::find2string(searched_string, true, false, fw);
707                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
708                         break;
709                 }
710
711                 case LFUN_PREFIX:
712                         owner->message(keyseq.printOptions());
713                         break;
714
715                 case LFUN_EXEC_COMMAND:
716                         owner->getToolbars().display("minibuffer", true);
717                         owner->focus_command_buffer();
718                         break;
719
720                 case LFUN_CANCEL:
721                         keyseq.reset();
722                         meta_fake_bit = key_modifier::none;
723                         if (view()->available())
724                                 // cancel any selection
725                                 dispatch(FuncRequest(LFUN_MARK_OFF));
726                         setMessage(N_("Cancel"));
727                         break;
728
729                 case LFUN_META_FAKE:
730                         meta_fake_bit = key_modifier::alt;
731                         setMessage(keyseq.print());
732                         break;
733
734                 case LFUN_READ_ONLY_TOGGLE:
735                         if (owner->buffer()->lyxvc().inUse())
736                                 owner->buffer()->lyxvc().toggleReadOnly();
737                         else
738                                 owner->buffer()->setReadonly(
739                                         !owner->buffer()->isReadonly());
740                         break;
741
742                 // --- Menus -----------------------------------------------
743                 case LFUN_MENUNEW:
744                         menuNew(argument, false);
745                         break;
746
747                 case LFUN_MENUNEWTMPLT:
748                         menuNew(argument, true);
749                         break;
750
751                 case LFUN_CLOSEBUFFER:
752                         closeBuffer();
753                         break;
754
755                 case LFUN_MENUWRITE:
756                         if (!owner->buffer()->isUnnamed()) {
757                                 string const str = bformat(_("Saving document %1$s..."),
758                                          MakeDisplayPath(owner->buffer()->fileName()));
759                                 owner->message(str);
760                                 MenuWrite(owner->buffer());
761                                 owner->message(str + _(" done."));
762                         } else
763                                 WriteAs(owner->buffer());
764                         break;
765
766                 case LFUN_WRITEAS:
767                         WriteAs(owner->buffer(), argument);
768                         break;
769
770                 case LFUN_MENURELOAD: {
771                         string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
772                         string text = bformat(_("Any changes will be lost. Are you sure "
773                                 "you want to revert to the saved version of the document %1$s?"), file);
774                         int const ret = Alert::prompt(_("Revert to saved document?"),
775                                 text, 0, 1, _("&Revert"), _("&Cancel"));
776
777                         if (ret == 0)
778                                 view()->reload();
779                         break;
780                 }
781
782                 case LFUN_UPDATE:
783                         Exporter::Export(owner->buffer(), argument, true);
784                         view()->showErrorList(BufferFormat(*owner->buffer()));
785                         break;
786
787                 case LFUN_PREVIEW:
788                         Exporter::Preview(owner->buffer(), argument);
789                         view()->showErrorList(BufferFormat(*owner->buffer()));
790                         break;
791
792                 case LFUN_BUILDPROG:
793                         Exporter::Export(owner->buffer(), "program", true);
794                         view()->showErrorList(_("Build"));
795                         break;
796
797                 case LFUN_RUNCHKTEX:
798                         owner->buffer()->runChktex();
799                         view()->showErrorList(_("ChkTeX"));
800                         break;
801
802                 case LFUN_EXPORT:
803                         if (argument == "custom")
804                                 owner->getDialogs().show("sendto");
805                         else {
806                                 Exporter::Export(owner->buffer(), argument, false);
807                                 view()->showErrorList(BufferFormat(*owner->buffer()));
808                         }
809                         break;
810
811                 case LFUN_EXPORT_CUSTOM: {
812                         string format_name;
813                         string command = split(argument, format_name, ' ');
814                         Format const * format = formats.getFormat(format_name);
815                         if (!format) {
816                                 lyxerr << "Format \"" << format_name
817                                        << "\" not recognized!"
818                                        << std::endl;
819                                 break;
820                         }
821
822                         Buffer * buffer = owner->buffer();
823
824                         // The name of the file created by the conversion process
825                         string filename;
826
827                         // Output to filename
828                         if (format->name() == "lyx") {
829                                 string const latexname =
830                                         buffer->getLatexName(false);
831                                 filename = ChangeExtension(latexname,
832                                                            format->extension());
833                                 filename = AddName(buffer->temppath(), filename);
834
835                                 if (!buffer->writeFile(filename))
836                                         break;
837
838                         } else {
839                                 Exporter::Export(buffer, format_name, true,
840                                                  filename);
841                         }
842
843                         // Substitute $$FName for filename
844                         if (!contains(command, "$$FName"))
845                                 command = "( " + command + " ) < $$FName";
846                         command = subst(command, "$$FName", filename);
847
848                         // Execute the command in the background
849                         Systemcall call;
850                         call.startscript(Systemcall::DontWait, command);
851                         break;
852                 }
853
854                 case LFUN_PRINT: {
855                         string target;
856                         string target_name;
857                         string command = split(split(argument, target, ' '),
858                                                target_name, ' ');
859
860                         if (target.empty()
861                             || target_name.empty()
862                             || command.empty()) {
863                                 lyxerr << "Unable to parse \""
864                                        << argument << '"' << std::endl;
865                                 break;
866                         }
867                         if (target != "printer" && target != "file") {
868                                 lyxerr << "Unrecognized target \""
869                                        << target << '"' << std::endl;
870                                 break;
871                         }
872
873                         Buffer * buffer = owner->buffer();
874
875                         if (!Exporter::Export(buffer, "dvi", true)) {
876                                 showPrintError(buffer->fileName());
877                                 break;
878                         }
879
880                         // Push directory path.
881                         string const path = buffer->temppath();
882                         Path p(path);
883
884                         // there are three cases here:
885                         // 1. we print to a file
886                         // 2. we print directly to a printer
887                         // 3. we print using a spool command (print to file first)
888                         Systemcall one;
889                         int res = 0;
890                         string const dviname =
891                                 ChangeExtension(buffer->getLatexName(true),
892                                                 "dvi");
893
894                         if (target == "printer") {
895                                 if (!lyxrc.print_spool_command.empty()) {
896                                         // case 3: print using a spool
897                                         string const psname =
898                                                 ChangeExtension(dviname,".ps");
899                                         command += lyxrc.print_to_file
900                                                 + QuoteName(psname)
901                                                 + ' '
902                                                 + QuoteName(dviname);
903
904                                         string command2 =
905                                                 lyxrc.print_spool_command +' ';
906                                         if (target_name != "default") {
907                                                 command2 += lyxrc.print_spool_printerprefix
908                                                         + target_name
909                                                         + ' ';
910                                         }
911                                         command2 += QuoteName(psname);
912                                         // First run dvips.
913                                         // If successful, then spool command
914                                         res = one.startscript(
915                                                 Systemcall::Wait,
916                                                 command);
917
918                                         if (res == 0)
919                                                 res = one.startscript(
920                                                         Systemcall::DontWait,
921                                                         command2);
922                                 } else {
923                                         // case 2: print directly to a printer
924                                         res = one.startscript(
925                                                 Systemcall::DontWait,
926                                                 command + QuoteName(dviname));
927                                 }
928
929                         } else {
930                                 // case 1: print to a file
931                                 command += lyxrc.print_to_file
932                                         + QuoteName(MakeAbsPath(target_name,
933                                                                 path))
934                                         + ' '
935                                         + QuoteName(dviname);
936                                 res = one.startscript(Systemcall::DontWait,
937                                                       command);
938                         }
939
940                         if (res != 0)
941                                 showPrintError(buffer->fileName());
942                         break;
943                 }
944
945                 case LFUN_IMPORT:
946                         doImport(argument);
947                         break;
948
949                 case LFUN_QUIT:
950                         QuitLyX();
951                         break;
952
953                 case LFUN_TOCVIEW: {
954                         InsetCommandParams p("tableofcontents");
955                         string const data = InsetCommandMailer::params2string("toc", p);
956                         owner->getDialogs().show("toc", data, 0);
957                         break;
958                 }
959
960                 case LFUN_AUTOSAVE:
961                         AutoSave(view());
962                         break;
963
964                 case LFUN_RECONFIGURE:
965                         Reconfigure(view());
966                         break;
967
968                 case LFUN_HELP_OPEN: {
969                         string const arg = argument;
970                         if (arg.empty()) {
971                                 setErrorMessage(N_("Missing argument"));
972                                 break;
973                         }
974                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
975                         if (fname.empty()) {
976                                 lyxerr << "LyX: unable to find documentation file `"
977                                                          << arg << "'. Bad installation?" << endl;
978                                 break;
979                         }
980                         owner->message(bformat(_("Opening help file %1$s..."),
981                                 MakeDisplayPath(fname)));
982                         view()->loadLyXFile(fname, false);
983                         break;
984                 }
985
986                 // --- version control -------------------------------
987                 case LFUN_VC_REGISTER:
988                         if (!ensureBufferClean(view()))
989                                 break;
990                         if (!owner->buffer()->lyxvc().inUse()) {
991                                 owner->buffer()->lyxvc().registrer();
992                                 view()->reload();
993                         }
994                         break;
995
996                 case LFUN_VC_CHECKIN:
997                         if (!ensureBufferClean(view()))
998                                 break;
999                         if (owner->buffer()->lyxvc().inUse()
1000                                         && !owner->buffer()->isReadonly()) {
1001                                 owner->buffer()->lyxvc().checkIn();
1002                                 view()->reload();
1003                         }
1004                         break;
1005
1006                 case LFUN_VC_CHECKOUT:
1007                         if (!ensureBufferClean(view()))
1008                                 break;
1009                         if (owner->buffer()->lyxvc().inUse()
1010                                         && owner->buffer()->isReadonly()) {
1011                                 owner->buffer()->lyxvc().checkOut();
1012                                 view()->reload();
1013                         }
1014                         break;
1015
1016                 case LFUN_VC_REVERT:
1017                         owner->buffer()->lyxvc().revert();
1018                         view()->reload();
1019                         break;
1020
1021                 case LFUN_VC_UNDO:
1022                         owner->buffer()->lyxvc().undoLast();
1023                         view()->reload();
1024                         break;
1025
1026                 // --- buffers ----------------------------------------
1027                 case LFUN_SWITCHBUFFER:
1028                         view()->setBuffer(bufferlist.getBuffer(argument));
1029                         break;
1030
1031                 case LFUN_NEXTBUFFER:
1032                         view()->setBuffer(bufferlist.next(view()->buffer()));
1033                         break;
1034
1035                 case LFUN_PREVIOUSBUFFER:
1036                         view()->setBuffer(bufferlist.previous(view()->buffer()));
1037                         break;
1038
1039                 case LFUN_FILE_NEW:
1040                         NewFile(view(), argument);
1041                         break;
1042
1043                 case LFUN_FILE_OPEN:
1044                         open(argument);
1045                         break;
1046
1047                 case LFUN_DROP_LAYOUTS_CHOICE:
1048                         owner->getToolbars().openLayoutList();
1049                         break;
1050
1051                 case LFUN_MENU_OPEN_BY_NAME:
1052                         owner->getMenubar().openByName(argument);
1053                         break;
1054
1055                 // --- lyxserver commands ----------------------------
1056                 case LFUN_GETNAME:
1057                         setMessage(owner->buffer()->fileName());
1058                         lyxerr[Debug::INFO] << "FNAME["
1059                                                          << owner->buffer()->fileName()
1060                                                          << "] " << endl;
1061                         break;
1062
1063                 case LFUN_NOTIFY:
1064                         dispatch_buffer = keyseq.print();
1065                         lyxserver->notifyClient(dispatch_buffer);
1066                         break;
1067
1068                 case LFUN_GOTOFILEROW: {
1069                         string file_name;
1070                         int row;
1071                         istringstream is(argument);
1072                         is >> file_name >> row;
1073                         if (prefixIs(file_name, package().temp_dir())) {
1074                                 // Needed by inverse dvi search. If it is a file
1075                                 // in tmpdir, call the apropriated function
1076                                 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1077                         } else {
1078                                 // Must replace extension of the file to be .lyx
1079                                 // and get full path
1080                                 string const s = ChangeExtension(file_name, ".lyx");
1081                                 // Either change buffer or load the file
1082                                 if (bufferlist.exists(s)) {
1083                                         view()->setBuffer(bufferlist.getBuffer(s));
1084                                 } else {
1085                                         view()->loadLyXFile(s);
1086                                 }
1087                         }
1088
1089                         view()->setCursorFromRow(row);
1090
1091                         view()->center();
1092                         // see BufferView_pimpl::center()
1093                         view()->updateScrollbar();
1094                         break;
1095                 }
1096
1097                 case LFUN_GOTO_PARAGRAPH: {
1098                         istringstream is(argument);
1099                         int id;
1100                         is >> id;
1101                         ParIterator par = owner->buffer()->getParFromID(id);
1102                         if (par == owner->buffer()->par_iterator_end()) {
1103                                 lyxerr[Debug::INFO] << "No matching paragraph found! ["
1104                                                                 << id << ']' << endl;
1105                                 break;
1106                         } else {
1107                                 lyxerr[Debug::INFO] << "Paragraph " << par->id()
1108                                                                 << " found." << endl;
1109                         }
1110
1111                         // Set the cursor
1112                         view()->setCursor(par, 0);
1113
1114                         view()->switchKeyMap();
1115                         owner->view_state_changed();
1116
1117                         view()->center();
1118                         // see BufferView_pimpl::center()
1119                         view()->updateScrollbar();
1120                         break;
1121                 }
1122
1123                 case LFUN_DIALOG_SHOW: {
1124                         string const name = cmd.getArg(0);
1125                         string data = trim(cmd.argument.substr(name.size()));
1126
1127                         if (name == "character") {
1128                                 data = freefont2string();
1129                                 if (!data.empty())
1130                                         owner->getDialogs().show("character", data);
1131                         }
1132
1133                         else if (name == "latexlog") {
1134                                 pair<Buffer::LogType, string> const logfile =
1135                                         owner->buffer()->getLogName();
1136                                 switch (logfile.first) {
1137                                 case Buffer::latexlog:
1138                                         data = "latex ";
1139                                         break;
1140                                 case Buffer::buildlog:
1141                                         data = "literate ";
1142                                         break;
1143                                 }
1144                                 data += logfile.second;
1145                                 owner->getDialogs().show("log", data);
1146                         }
1147                         else if (name == "vclog") {
1148                                 string const data = "vc " +
1149                                         owner->buffer()->lyxvc().getLogFile();
1150                                 owner->getDialogs().show("log", data);
1151                         }
1152                         else
1153                                 owner->getDialogs().show(name, data);
1154                         break;
1155                 }
1156
1157                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1158                         string const name = cmd.getArg(0);
1159                         string data = trim(cmd.argument.substr(name.size()));
1160                         if (name == "bibitem" ||
1161                             name == "bibtex" ||
1162                             name == "include" ||
1163                             name == "index" ||
1164                             name == "label" ||
1165                             name == "ref" ||
1166                             name == "toc" ||
1167                             name == "url") {
1168                                 InsetCommandParams p(name);
1169                                 data = InsetCommandMailer::params2string(name, p);
1170                         } else if (name == "box") {
1171                                 // \c data == "Boxed" || "Frameless" etc
1172                                 InsetBoxParams p(data);
1173                                 data = InsetBoxMailer::params2string(p);
1174                         } else if (name == "branch") {
1175                                 InsetBranchParams p;
1176                                 data = InsetBranchMailer::params2string(p);
1177                         } else if (name == "citation") {
1178                                 InsetCommandParams p("cite");
1179                                 data = InsetCommandMailer::params2string(name, p);
1180                         } else if (name == "ert") {
1181                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1182                         } else if (name == "external") {
1183                                 InsetExternalParams p;
1184                                 Buffer const & buffer = *owner->buffer();
1185                                 data = InsetExternalMailer::params2string(p, buffer);
1186                         } else if (name == "float") {
1187                                 InsetFloatParams p;
1188                                 data = InsetFloatMailer::params2string(p);
1189                         } else if (name == "graphics") {
1190                                 InsetGraphicsParams p;
1191                                 Buffer const & buffer = *owner->buffer();
1192                                 data = InsetGraphicsMailer::params2string(p, buffer);
1193                         } else if (name == "note") {
1194                                 InsetNoteParams p;
1195                                 data = InsetNoteMailer::params2string(p);
1196                         } else if (name == "vspace") {
1197                                 VSpace space;
1198                                 data = InsetVSpaceMailer::params2string(space);
1199                         } else if (name == "wrap") {
1200                                 InsetWrapParams p;
1201                                 data = InsetWrapMailer::params2string(p);
1202                         }
1203                         owner->getDialogs().show(name, data, 0);
1204                         break;
1205                 }
1206
1207                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1208                         break;
1209
1210                 case LFUN_DIALOG_UPDATE: {
1211                         string const & name = argument;
1212                         // Can only update a dialog connected to an existing inset
1213                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1214                         if (inset) {
1215                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1216                                 inset->dispatch(view()->cursor(), fr);
1217                         } else if (name == "paragraph") {
1218                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1219                         } else if (name == "prefs") {
1220                                 owner->getDialogs().update(name, string());
1221                         }
1222                         break;
1223                 }
1224
1225                 case LFUN_DIALOG_HIDE:
1226                         Dialogs::hide(argument, 0);
1227                         break;
1228
1229                 case LFUN_DIALOG_DISCONNECT_INSET:
1230                         owner->getDialogs().disconnect(argument);
1231                         break;
1232
1233                 case LFUN_CHILDOPEN: {
1234                         string const filename =
1235                                 MakeAbsPath(argument, owner->buffer()->filePath());
1236                         setMessage(N_("Opening child document ") +
1237                                          MakeDisplayPath(filename) + "...");
1238                         view()->savePosition(0);
1239                         string const parentfilename = owner->buffer()->fileName();
1240                         if (bufferlist.exists(filename))
1241                                 view()->setBuffer(bufferlist.getBuffer(filename));
1242                         else
1243                                 view()->loadLyXFile(filename);
1244                         // Set the parent name of the child document.
1245                         // This makes insertion of citations and references in the child work,
1246                         // when the target is in the parent or another child document.
1247                         owner->buffer()->setParentName(parentfilename);
1248                         break;
1249                 }
1250
1251                 case LFUN_TOGGLECURSORFOLLOW:
1252                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1253                         break;
1254
1255                 case LFUN_KMAP_OFF:
1256                         owner->getIntl().KeyMapOn(false);
1257                         break;
1258
1259                 case LFUN_KMAP_PRIM:
1260                         owner->getIntl().KeyMapPrim();
1261                         break;
1262
1263                 case LFUN_KMAP_SEC:
1264                         owner->getIntl().KeyMapSec();
1265                         break;
1266
1267                 case LFUN_KMAP_TOGGLE:
1268                         owner->getIntl().ToggleKeyMap();
1269                         break;
1270
1271                 case LFUN_REPEAT: {
1272                         // repeat command
1273                         string countstr;
1274                         string rest = split(argument, countstr, ' ');
1275                         istringstream is(countstr);
1276                         int count = 0;
1277                         is >> count;
1278                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1279                         for (int i = 0; i < count; ++i)
1280                                 dispatch(lyxaction.lookupFunc(rest));
1281                         break;
1282                 }
1283
1284                 case LFUN_SEQUENCE: {
1285                         // argument contains ';'-terminated commands
1286                         string arg = argument;
1287                         while (!arg.empty()) {
1288                                 string first;
1289                                 arg = split(arg, first, ';');
1290                                 FuncRequest func(lyxaction.lookupFunc(first));
1291                                 func.origin = cmd.origin;
1292                                 dispatch(func);
1293                         }
1294                         break;
1295                 }
1296
1297                 case LFUN_SAVEPREFERENCES: {
1298                         Path p(package().user_support());
1299                         lyxrc.write("preferences", false);
1300                         break;
1301                 }
1302
1303                 case LFUN_SCREEN_FONT_UPDATE:
1304                         // handle the screen font changes.
1305                         lyxrc.set_font_norm_type();
1306                         lyx_gui::update_fonts();
1307                         // All visible buffers will need resize
1308                         view()->resize();
1309                         break;
1310
1311                 case LFUN_SET_COLOR: {
1312                         string lyx_name;
1313                         string const x11_name = split(argument, lyx_name, ' ');
1314                         if (lyx_name.empty() || x11_name.empty()) {
1315                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1316                                                         " <x11_name>"));
1317                                 break;
1318                         }
1319
1320                         bool const graphicsbg_changed =
1321                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1322                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1323
1324                         if (!lcolor.setColor(lyx_name, x11_name)) {
1325                                 setErrorMessage(
1326                                         bformat(_("Set-color \"%1$s\" failed "
1327                                                                 "- color is undefined or "
1328                                                                 "may not be redefined"), lyx_name));
1329                                 break;
1330                         }
1331
1332                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1333
1334                         if (graphicsbg_changed) {
1335 #ifdef WITH_WARNINGS
1336 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1337 #endif
1338 #if 0
1339                                 lyx::graphics::GCache::get().changeDisplay(true);
1340 #endif
1341                         }
1342                         break;
1343                 }
1344
1345                 case LFUN_MESSAGE:
1346                         owner->message(argument);
1347                         break;
1348
1349                 case LFUN_TOOLTIPS_TOGGLE:
1350                         owner->getDialogs().toggleTooltips();
1351                         break;
1352
1353                 case LFUN_EXTERNAL_EDIT: {
1354                         FuncRequest fr(action, argument);
1355                         InsetExternal().dispatch(view()->cursor(), fr);
1356                         break;
1357                 }
1358
1359                 case LFUN_GRAPHICS_EDIT: {
1360                         FuncRequest fr(action, argument);
1361                         InsetGraphics().dispatch(view()->cursor(), fr);
1362                         break;
1363                 }
1364
1365                 case LFUN_ALL_INSETS_TOGGLE: {
1366                         string action;
1367                         string const name = split(argument, action, ' ');
1368                         InsetBase::Code const inset_code =
1369                                 InsetBase::translate(name);
1370
1371                         LCursor & cur = view()->cursor();
1372                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1373
1374                         InsetBase & inset = owner->buffer()->inset();
1375                         InsetIterator it  = inset_iterator_begin(inset);
1376                         InsetIterator const end = inset_iterator_end(inset);
1377                         for (; it != end; ++it) {
1378                                 if (inset_code == InsetBase::NO_CODE
1379                                     || inset_code == it->lyxCode())
1380                                         it->dispatch(cur, fr);
1381                         }
1382                         break;
1383                 }
1384
1385                 case LFUN_LANGUAGE_BUFFER: {
1386                         Buffer & buffer = *owner->buffer();
1387                         Language const * oldL = buffer.params().language;
1388                         Language const * newL = languages.getLanguage(argument);
1389                         if (!newL || oldL == newL)
1390                                 break;
1391
1392                         if (oldL->RightToLeft() == newL->RightToLeft()
1393                             && !buffer.isMultiLingual())
1394                                 buffer.changeLanguage(oldL, newL);
1395                         else
1396                                 buffer.updateDocLang(newL);
1397                         break;
1398                 }
1399
1400                 case LFUN_SAVE_AS_DEFAULT: {
1401                         string const fname =
1402                                 AddName(AddPath(package().user_support(), "templates/"),
1403                                         "defaults.lyx");
1404                         Buffer defaults(fname);
1405
1406                         istringstream ss(argument);
1407                         LyXLex lex(0,0);
1408                         lex.setStream(ss);
1409                         int const unknown_tokens = defaults.readHeader(lex);
1410
1411                         if (unknown_tokens != 0) {
1412                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1413                                        << unknown_tokens << " unknown token"
1414                                        << (unknown_tokens == 1 ? "" : "s")
1415                                        << endl;
1416                         }
1417
1418                         if (defaults.writeFile(defaults.fileName()))
1419                                 setMessage(_("Document defaults saved in ")
1420                                            + MakeDisplayPath(fname));
1421                         else
1422                                 setErrorMessage(_("Unable to save document defaults"));
1423                         break;
1424                 }
1425
1426                 case LFUN_BUFFERPARAMS_APPLY: {
1427                         biblio::CiteEngine const engine =
1428                                 owner->buffer()->params().cite_engine;
1429
1430                         istringstream ss(argument);
1431                         LyXLex lex(0,0);
1432                         lex.setStream(ss);
1433                         int const unknown_tokens =
1434                                 owner->buffer()->readHeader(lex);
1435
1436                         if (unknown_tokens != 0) {
1437                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1438                                        << unknown_tokens << " unknown token"
1439                                        << (unknown_tokens == 1 ? "" : "s")
1440                                        << endl;
1441                         }
1442                         if (engine == owner->buffer()->params().cite_engine)
1443                                 break;
1444
1445                         LCursor & cur = view()->cursor();
1446                         FuncRequest fr(LFUN_INSET_REFRESH);
1447
1448                         InsetBase & inset = owner->buffer()->inset();
1449                         InsetIterator it  = inset_iterator_begin(inset);
1450                         InsetIterator const end = inset_iterator_end(inset);
1451                         for (; it != end; ++it)
1452                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1453                                         it->dispatch(cur, fr);
1454                         break;
1455                 }
1456
1457                 case LFUN_TEXTCLASS_APPLY: {
1458                         Buffer * buffer = owner->buffer();
1459
1460                         lyx::textclass_type const old_class =
1461                                 buffer->params().textclass;
1462
1463                         loadTextclass(argument);
1464
1465                         std::pair<bool, lyx::textclass_type> const tc_pair =
1466                                 textclasslist.NumberOfClass(argument);
1467
1468                         if (!tc_pair.first)
1469                                 break;
1470
1471                         lyx::textclass_type const new_class = tc_pair.second;
1472                         if (old_class == new_class)
1473                                 // nothing to do
1474                                 break;
1475
1476                         owner->message(_("Converting document to new document class..."));
1477                         ErrorList el;
1478                         lyx::cap::SwitchLayoutsBetweenClasses(
1479                                 old_class, new_class,
1480                                 buffer->paragraphs(), el);
1481
1482                         bufferErrors(*buffer, el);
1483                         view()->showErrorList(_("Class switch"));
1484                         break;
1485                 }
1486
1487                 case LFUN_TEXTCLASS_LOAD:
1488                         loadTextclass(argument);
1489                         break;
1490
1491                 case LFUN_LYXRC_APPLY: {
1492                         LyXRC const lyxrc_orig = lyxrc;
1493
1494                         istringstream ss(argument);
1495                         bool const success = lyxrc.read(ss) == 0;
1496
1497                         if (!success) {
1498                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1499                                        << "Unable to read lyxrc data"
1500                                        << endl;
1501                                 break;
1502                         }
1503
1504                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1505                         break;
1506                 }
1507
1508                 default: {
1509                         update = false;
1510                         view()->cursor().dispatch(cmd);
1511                         if (view()->cursor().result().dispatched())
1512                                 update |= view()->cursor().result().update();
1513                         else
1514                                 update |= view()->dispatch(cmd);
1515                         break;
1516                 }
1517                 }
1518
1519                 if (view()->available()) {
1520                         // Redraw screen unless explicitly told otherwise.
1521                         // This also initializes the position cache for all insets
1522                         // in (at least partially) visible top-level paragraphs.
1523                         view()->update(true, update);
1524
1525                         // if we executed a mutating lfun, mark the buffer as dirty
1526                         if (getStatus(cmd).enabled()
1527                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1528                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1529                                 view()->buffer()->markDirty();
1530                 }
1531
1532                 if (view()->cursor().inTexted()) {
1533                         view()->owner()->updateLayoutChoice();
1534                 }
1535         }
1536         sendDispatchMessage(getMessage(), cmd);
1537 }
1538
1539
1540 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1541 {
1542         owner->updateMenubar();
1543         owner->updateToolbars();
1544
1545         const bool verbose = (cmd.origin == FuncRequest::UI
1546                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1547
1548         if (cmd.action == LFUN_SELFINSERT || !verbose) {
1549                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1550                 if (!msg.empty())
1551                         owner->message(msg);
1552                 return;
1553         }
1554
1555         string dispatch_msg = msg;
1556         if (!dispatch_msg.empty())
1557                 dispatch_msg += ' ';
1558
1559         string comname = lyxaction.getActionName(cmd.action);
1560
1561         bool argsadded = false;
1562
1563         if (!cmd.argument.empty()) {
1564                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1565                         comname += ' ' + cmd.argument;
1566                         argsadded = true;
1567                 }
1568         }
1569
1570         string const shortcuts = toplevel_keymap->printbindings(cmd);
1571
1572         if (!shortcuts.empty()) {
1573                 comname += ": " + shortcuts;
1574         } else if (!argsadded && !cmd.argument.empty()) {
1575                 comname += ' ' + cmd.argument;
1576         }
1577
1578         if (!comname.empty()) {
1579                 comname = rtrim(comname);
1580                 dispatch_msg += '(' + comname + ')';
1581         }
1582
1583         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1584         if (!dispatch_msg.empty())
1585                 owner->message(dispatch_msg);
1586 }
1587
1588
1589 void LyXFunc::setupLocalKeymap()
1590 {
1591         keyseq.stdmap = toplevel_keymap.get();
1592         keyseq.curmap = toplevel_keymap.get();
1593         cancel_meta_seq.stdmap = toplevel_keymap.get();
1594         cancel_meta_seq.curmap = toplevel_keymap.get();
1595 }
1596
1597
1598 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1599 {
1600         string initpath = lyxrc.document_path;
1601         string filename(name);
1602
1603         if (view()->available()) {
1604                 string const trypath = owner->buffer()->filePath();
1605                 // If directory is writeable, use this as default.
1606                 if (IsDirWriteable(trypath))
1607                         initpath = trypath;
1608         }
1609
1610         static int newfile_number;
1611
1612         if (filename.empty()) {
1613                 filename = AddName(lyxrc.document_path,
1614                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1615                 FileInfo fi(filename);
1616                 while (bufferlist.exists(filename) || fi.readable()) {
1617                         ++newfile_number;
1618                         filename = AddName(lyxrc.document_path,
1619                                            "newfile" +  convert<string>(newfile_number) +
1620                                     ".lyx");
1621                         fi.newFile(filename);
1622                 }
1623         }
1624
1625         // The template stuff
1626         string templname;
1627         if (fromTemplate) {
1628                 FileDialog fileDlg(_("Select template file"),
1629                         LFUN_SELECT_FILE_SYNC,
1630                         make_pair(string(_("Documents|#o#O")),
1631                                   string(lyxrc.document_path)),
1632                         make_pair(string(_("Templates|#T#t")),
1633                                   string(lyxrc.template_path)));
1634
1635                 FileDialog::Result result =
1636                         fileDlg.open(lyxrc.template_path,
1637                                      FileFilterList(_("LyX Documents (*.lyx)")),
1638                                      string());
1639
1640                 if (result.first == FileDialog::Later)
1641                         return;
1642                 if (result.second.empty())
1643                         return;
1644                 templname = result.second;
1645         }
1646
1647         view()->newFile(filename, templname, !name.empty());
1648 }
1649
1650
1651 void LyXFunc::open(string const & fname)
1652 {
1653         string initpath = lyxrc.document_path;
1654
1655         if (view()->available()) {
1656                 string const trypath = owner->buffer()->filePath();
1657                 // If directory is writeable, use this as default.
1658                 if (IsDirWriteable(trypath))
1659                         initpath = trypath;
1660         }
1661
1662         string filename;
1663
1664         if (fname.empty()) {
1665                 FileDialog fileDlg(_("Select document to open"),
1666                         LFUN_FILE_OPEN,
1667                         make_pair(string(_("Documents|#o#O")),
1668                                   string(lyxrc.document_path)),
1669                         make_pair(string(_("Examples|#E#e")),
1670                                   string(AddPath(package().system_support(), "examples"))));
1671
1672                 FileDialog::Result result =
1673                         fileDlg.open(initpath,
1674                                      FileFilterList(_("LyX Documents (*.lyx)")),
1675                                      string());
1676
1677                 if (result.first == FileDialog::Later)
1678                         return;
1679
1680                 filename = result.second;
1681
1682                 // check selected filename
1683                 if (filename.empty()) {
1684                         owner->message(_("Canceled."));
1685                         return;
1686                 }
1687         } else
1688                 filename = fname;
1689
1690         // get absolute path of file and add ".lyx" to the filename if
1691         // necessary
1692         string const fullpath = FileSearch(string(), filename, "lyx");
1693         if (!fullpath.empty()) {
1694                 filename = fullpath;
1695         }
1696
1697         string const disp_fn(MakeDisplayPath(filename));
1698
1699         // if the file doesn't exist, let the user create one
1700         FileInfo const f(filename, true);
1701         if (!f.exist()) {
1702                 // the user specifically chose this name. Believe them.
1703                 view()->newFile(filename, "", true);
1704                 return;
1705         }
1706
1707         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1708
1709         string str2;
1710         if (view()->loadLyXFile(filename)) {
1711                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1712         } else {
1713                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1714         }
1715         owner->message(str2);
1716 }
1717
1718
1719 void LyXFunc::doImport(string const & argument)
1720 {
1721         string format;
1722         string filename = split(argument, format, ' ');
1723
1724         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1725                             << " file: " << filename << endl;
1726
1727         // need user interaction
1728         if (filename.empty()) {
1729                 string initpath = lyxrc.document_path;
1730
1731                 if (view()->available()) {
1732                         string const trypath = owner->buffer()->filePath();
1733                         // If directory is writeable, use this as default.
1734                         if (IsDirWriteable(trypath))
1735                                 initpath = trypath;
1736                 }
1737
1738                 string const text = bformat(_("Select %1$s file to import"),
1739                         formats.prettyName(format));
1740
1741                 FileDialog fileDlg(text,
1742                         LFUN_IMPORT,
1743                         make_pair(string(_("Documents|#o#O")),
1744                                   string(lyxrc.document_path)),
1745                         make_pair(string(_("Examples|#E#e")),
1746                                   string(AddPath(package().system_support(), "examples"))));
1747
1748                 string const filter = formats.prettyName(format)
1749                         + " (*." + formats.extension(format) + ')';
1750
1751                 FileDialog::Result result =
1752                         fileDlg.open(initpath,
1753                                      FileFilterList(filter),
1754                                      string());
1755
1756                 if (result.first == FileDialog::Later)
1757                         return;
1758
1759                 filename = result.second;
1760
1761                 // check selected filename
1762                 if (filename.empty())
1763                         owner->message(_("Canceled."));
1764         }
1765
1766         if (filename.empty())
1767                 return;
1768
1769         // get absolute path of file
1770         filename = MakeAbsPath(filename);
1771
1772         string const lyxfile = ChangeExtension(filename, ".lyx");
1773
1774         // Check if the document already is open
1775         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1776                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
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                 string const file = MakeDisplayPath(lyxfile, 30);
1786
1787                 string text = bformat(_("The document %1$s already exists.\n\n"
1788                         "Do you want to over-write that document?"), file);
1789                 int const ret = Alert::prompt(_("Over-write document?"),
1790                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1791
1792                 if (ret == 1) {
1793                         owner->message(_("Canceled."));
1794                         return;
1795                 }
1796         }
1797
1798         Importer::Import(owner, filename, format);
1799 }
1800
1801
1802 void LyXFunc::closeBuffer()
1803 {
1804         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1805                 if (bufferlist.empty()) {
1806                         // need this otherwise SEGV may occur while
1807                         // trying to set variables that don't exist
1808                         // since there's no current buffer
1809                         owner->getDialogs().hideBufferDependent();
1810                 } else {
1811                         view()->setBuffer(bufferlist.first());
1812                 }
1813         }
1814 }
1815
1816
1817 // Each "owner" should have it's own message method. lyxview and
1818 // the minibuffer would use the minibuffer, but lyxserver would
1819 // send an ERROR signal to its client.  Alejandro 970603
1820 // This function is bit problematic when it comes to NLS, to make the
1821 // lyx servers client be language indepenent we must not translate
1822 // strings sent to this func.
1823 void LyXFunc::setErrorMessage(string const & m) const
1824 {
1825         dispatch_buffer = m;
1826         errorstat = true;
1827 }
1828
1829
1830 void LyXFunc::setMessage(string const & m) const
1831 {
1832         dispatch_buffer = m;
1833 }
1834
1835
1836 string const LyXFunc::viewStatusMessage()
1837 {
1838         // When meta-fake key is pressed, show the key sequence so far + "M-".
1839         if (wasMetaKey())
1840                 return keyseq.print() + "M-";
1841
1842         // Else, when a non-complete key sequence is pressed,
1843         // show the available options.
1844         if (keyseq.length() > 0 && !keyseq.deleted())
1845                 return keyseq.printOptions();
1846
1847         if (!view()->available())
1848                 return _("Welcome to LyX!");
1849
1850         return view()->cursor().currentState();
1851 }
1852
1853
1854 BufferView * LyXFunc::view() const
1855 {
1856         BOOST_ASSERT(owner);
1857         return owner->view().get();
1858 }
1859
1860
1861 bool LyXFunc::wasMetaKey() const
1862 {
1863         return (meta_fake_bit != key_modifier::none);
1864 }
1865
1866
1867 namespace {
1868
1869 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1870 {
1871         // Why the switch you might ask. It is a trick to ensure that all
1872         // the elements in the LyXRCTags enum is handled. As you can see
1873         // there are no breaks at all. So it is just a huge fall-through.
1874         // The nice thing is that we will get a warning from the compiler
1875         // if we forget an element.
1876         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1877         switch (tag) {
1878         case LyXRC::RC_ACCEPT_COMPOUND:
1879         case LyXRC::RC_ALT_LANG:
1880         case LyXRC::RC_ASCIIROFF_COMMAND:
1881         case LyXRC::RC_ASCII_LINELEN:
1882         case LyXRC::RC_AUTOREGIONDELETE:
1883         case LyXRC::RC_AUTORESET_OPTIONS:
1884         case LyXRC::RC_AUTOSAVE:
1885         case LyXRC::RC_AUTO_NUMBER:
1886         case LyXRC::RC_BACKUPDIR_PATH:
1887         case LyXRC::RC_BIBTEX_COMMAND:
1888         case LyXRC::RC_BINDFILE:
1889         case LyXRC::RC_CHECKLASTFILES:
1890         case LyXRC::RC_CHKTEX_COMMAND:
1891         case LyXRC::RC_CONVERTER:
1892         case LyXRC::RC_COPIER:
1893         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1894         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1895         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1896         case LyXRC::RC_CYGWIN_PATH_FIX:
1897                 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
1898                         namespace os = lyx::support::os;
1899                         os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
1900                 }
1901         case LyXRC::RC_DATE_INSERT_FORMAT:
1902         case LyXRC::RC_DEFAULT_LANGUAGE:
1903         case LyXRC::RC_DEFAULT_PAPERSIZE:
1904         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1905         case LyXRC::RC_DISPLAY_GRAPHICS:
1906         case LyXRC::RC_DOCUMENTPATH:
1907                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1908                         FileInfo fi(lyxrc_new.document_path);
1909                         if (fi.isOK() && fi.isDir()) {
1910                                 using lyx::support::package;
1911                                 package().document_dir() = lyxrc.document_path;
1912                         }
1913                 }
1914         case LyXRC::RC_ESC_CHARS:
1915         case LyXRC::RC_FONT_ENCODING:
1916         case LyXRC::RC_FORMAT:
1917         case LyXRC::RC_INDEX_COMMAND:
1918         case LyXRC::RC_INPUT:
1919         case LyXRC::RC_KBMAP:
1920         case LyXRC::RC_KBMAP_PRIMARY:
1921         case LyXRC::RC_KBMAP_SECONDARY:
1922         case LyXRC::RC_LABEL_INIT_LENGTH:
1923         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1924         case LyXRC::RC_LANGUAGE_AUTO_END:
1925         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1926         case LyXRC::RC_LANGUAGE_COMMAND_END:
1927         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1928         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1929         case LyXRC::RC_LANGUAGE_PACKAGE:
1930         case LyXRC::RC_LANGUAGE_USE_BABEL:
1931         case LyXRC::RC_LASTFILES:
1932         case LyXRC::RC_MAKE_BACKUP:
1933         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1934         case LyXRC::RC_NUMLASTFILES:
1935         case LyXRC::RC_PATH_PREFIX:
1936                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1937                         using lyx::support::prependEnvPath;
1938                         prependEnvPath("PATH", lyxrc.path_prefix);
1939                 }
1940         case LyXRC::RC_PERS_DICT:
1941         case LyXRC::RC_POPUP_BOLD_FONT:
1942         case LyXRC::RC_POPUP_FONT_ENCODING:
1943         case LyXRC::RC_POPUP_NORMAL_FONT:
1944         case LyXRC::RC_PREVIEW:
1945         case LyXRC::RC_PREVIEW_HASHED_LABELS:
1946         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1947         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1948         case LyXRC::RC_PRINTCOPIESFLAG:
1949         case LyXRC::RC_PRINTER:
1950         case LyXRC::RC_PRINTEVENPAGEFLAG:
1951         case LyXRC::RC_PRINTEXSTRAOPTIONS:
1952         case LyXRC::RC_PRINTFILEEXTENSION:
1953         case LyXRC::RC_PRINTLANDSCAPEFLAG:
1954         case LyXRC::RC_PRINTODDPAGEFLAG:
1955         case LyXRC::RC_PRINTPAGERANGEFLAG:
1956         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1957         case LyXRC::RC_PRINTPAPERFLAG:
1958         case LyXRC::RC_PRINTREVERSEFLAG:
1959         case LyXRC::RC_PRINTSPOOL_COMMAND:
1960         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1961         case LyXRC::RC_PRINTTOFILE:
1962         case LyXRC::RC_PRINTTOPRINTER:
1963         case LyXRC::RC_PRINT_ADAPTOUTPUT:
1964         case LyXRC::RC_PRINT_COMMAND:
1965         case LyXRC::RC_RTL_SUPPORT:
1966         case LyXRC::RC_SCREEN_DPI:
1967         case LyXRC::RC_SCREEN_FONT_ENCODING:
1968         case LyXRC::RC_SCREEN_FONT_ROMAN:
1969         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1970         case LyXRC::RC_SCREEN_FONT_SANS:
1971         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1972         case LyXRC::RC_SCREEN_FONT_SCALABLE:
1973         case LyXRC::RC_SCREEN_FONT_SIZES:
1974         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1975         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1976         case LyXRC::RC_SCREEN_ZOOM:
1977         case LyXRC::RC_SERVERPIPE:
1978         case LyXRC::RC_SET_COLOR:
1979         case LyXRC::RC_SHOW_BANNER:
1980         case LyXRC::RC_SPELL_COMMAND:
1981         case LyXRC::RC_TEMPDIRPATH:
1982         case LyXRC::RC_TEMPLATEPATH:
1983         case LyXRC::RC_UIFILE:
1984         case LyXRC::RC_USER_EMAIL:
1985         case LyXRC::RC_USER_NAME:
1986         case LyXRC::RC_USETEMPDIR:
1987         case LyXRC::RC_USE_ALT_LANG:
1988         case LyXRC::RC_USE_ESC_CHARS:
1989         case LyXRC::RC_USE_INP_ENC:
1990         case LyXRC::RC_USE_PERS_DICT:
1991         case LyXRC::RC_USE_SPELL_LIB:
1992         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1993         case LyXRC::RC_VIEWER:
1994         case LyXRC::RC_WHEEL_JUMP:
1995         case LyXRC::RC_LAST:
1996                 break;
1997         }
1998 }
1999
2000 } // namespace anon