]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
I just noticed that createLyXTmpDir wouldn't compile under OS/2.
[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 } //namespace anon
654
655
656 void LyXFunc::dispatch(FuncRequest const & cmd)
657 {
658         BOOST_ASSERT(view());
659         string const argument = cmd.argument;
660         kb_action const action = cmd.action;
661
662         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
663         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
664
665         // we have not done anything wrong yet.
666         errorstat = false;
667         dispatch_buffer.erase();
668
669         bool update = true;
670
671         FuncStatus const flag = getStatus(cmd);
672         if (!flag.enabled()) {
673                 // We cannot use this function here
674                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
675                        << lyxaction.getActionName(action)
676                        << " [" << action << "] is disabled at this location"
677                        << endl;
678                 setErrorMessage(flag.message());
679         } else {
680
681                 if (view()->available())
682                         view()->hideCursor();
683
684                 switch (action) {
685
686                 case LFUN_WORDFINDFORWARD:
687                 case LFUN_WORDFINDBACKWARD: {
688                         static string last_search;
689                         string searched_string;
690
691                         if (!argument.empty()) {
692                                 last_search = argument;
693                                 searched_string = argument;
694                         } else {
695                                 searched_string = last_search;
696                         }
697
698                         if (searched_string.empty())
699                                 break;
700
701                         bool const fw = action == LFUN_WORDFINDFORWARD;
702                         string const data =
703                                 lyx::find::find2string(searched_string, true, false, fw);
704                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
705                         break;
706                 }
707
708                 case LFUN_PREFIX:
709                         owner->message(keyseq.printOptions());
710                         break;
711
712                 case LFUN_EXEC_COMMAND:
713                         owner->getToolbars().display("minibuffer", true);
714                         owner->focus_command_buffer();
715                         break;
716
717                 case LFUN_CANCEL:
718                         keyseq.reset();
719                         meta_fake_bit = key_modifier::none;
720                         if (view()->available())
721                                 // cancel any selection
722                                 dispatch(FuncRequest(LFUN_MARK_OFF));
723                         setMessage(N_("Cancel"));
724                         break;
725
726                 case LFUN_META_FAKE:
727                         meta_fake_bit = key_modifier::alt;
728                         setMessage(keyseq.print());
729                         break;
730
731                 case LFUN_READ_ONLY_TOGGLE:
732                         if (owner->buffer()->lyxvc().inUse())
733                                 owner->buffer()->lyxvc().toggleReadOnly();
734                         else
735                                 owner->buffer()->setReadonly(
736                                         !owner->buffer()->isReadonly());
737                         break;
738
739                 // --- Menus -----------------------------------------------
740                 case LFUN_MENUNEW:
741                         menuNew(argument, false);
742                         break;
743
744                 case LFUN_MENUNEWTMPLT:
745                         menuNew(argument, true);
746                         break;
747
748                 case LFUN_CLOSEBUFFER:
749                         closeBuffer();
750                         break;
751
752                 case LFUN_MENUWRITE:
753                         if (!owner->buffer()->isUnnamed()) {
754                                 string const str = bformat(_("Saving document %1$s..."),
755                                          MakeDisplayPath(owner->buffer()->fileName()));
756                                 owner->message(str);
757                                 MenuWrite(owner->buffer());
758                                 owner->message(str + _(" done."));
759                         } else
760                                 WriteAs(owner->buffer());
761                         break;
762
763                 case LFUN_WRITEAS:
764                         WriteAs(owner->buffer(), argument);
765                         break;
766
767                 case LFUN_MENURELOAD: {
768                         string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
769                         string text = bformat(_("Any changes will be lost. Are you sure "
770                                 "you want to revert to the saved version of the document %1$s?"), file);
771                         int const ret = Alert::prompt(_("Revert to saved document?"),
772                                 text, 0, 1, _("&Revert"), _("&Cancel"));
773
774                         if (ret == 0)
775                                 view()->reload();
776                         break;
777                 }
778
779                 case LFUN_UPDATE:
780                         Exporter::Export(owner->buffer(), argument, true);
781                         view()->showErrorList(BufferFormat(*owner->buffer()));
782                         break;
783
784                 case LFUN_PREVIEW:
785                         Exporter::Preview(owner->buffer(), argument);
786                         view()->showErrorList(BufferFormat(*owner->buffer()));
787                         break;
788
789                 case LFUN_BUILDPROG:
790                         Exporter::Export(owner->buffer(), "program", true);
791                         view()->showErrorList(_("Build"));
792                         break;
793
794                 case LFUN_RUNCHKTEX:
795                         owner->buffer()->runChktex();
796                         view()->showErrorList(_("ChkTeX"));
797                         break;
798
799                 case LFUN_EXPORT:
800                         if (argument == "custom")
801                                 owner->getDialogs().show("sendto");
802                         else {
803                                 Exporter::Export(owner->buffer(), argument, false);
804                                 view()->showErrorList(BufferFormat(*owner->buffer()));
805                         }
806                         break;
807
808                 case LFUN_EXPORT_CUSTOM: {
809                         string format_name;
810                         string command = split(argument, format_name, ' ');
811                         Format const * format = formats.getFormat(format_name);
812                         if (!format) {
813                                 lyxerr << "Format \"" << format_name
814                                        << "\" not recognized!"
815                                        << std::endl;
816                                 break;
817                         }
818
819                         Buffer * buffer = owner->buffer();
820
821                         // The name of the file created by the conversion process
822                         string filename;
823
824                         // Output to filename
825                         if (format->name() == "lyx") {
826                                 string const latexname =
827                                         buffer->getLatexName(false);
828                                 filename = ChangeExtension(latexname,
829                                                            format->extension());
830                                 filename = AddName(buffer->temppath(), filename);
831
832                                 if (!buffer->writeFile(filename))
833                                         break;
834
835                         } else {
836                                 Exporter::Export(buffer, format_name, true,
837                                                  filename);
838                         }
839
840                         // Substitute $$FName for filename
841                         if (!contains(command, "$$FName"))
842                                 command = "( " + command + " ) < $$FName";
843                         command = subst(command, "$$FName", filename);
844
845                         // Execute the command in the background
846                         Systemcall call;
847                         call.startscript(Systemcall::DontWait, command);
848                         break;
849                 }
850
851                 case LFUN_PRINT: {
852                         string target;
853                         string target_name;
854                         string command = split(split(argument, target, ' '),
855                                                target_name, ' ');
856
857                         if (target.empty()
858                             || target_name.empty()
859                             || command.empty()) {
860                                 lyxerr << "Unable to parse \""
861                                        << argument << '"' << std::endl;
862                                 break;
863                         }
864                         if (target != "printer" && target != "file") {
865                                 lyxerr << "Unrecognized target \""
866                                        << target << '"' << std::endl;
867                                 break;
868                         }
869
870                         Buffer * buffer = owner->buffer();
871
872                         if (!Exporter::Export(buffer, "dvi", true)) {
873                                 showPrintError(buffer->fileName());
874                                 break;
875                         }
876
877                         // Push directory path.
878                         string const path = buffer->temppath();
879                         Path p(path);
880
881                         // there are three cases here:
882                         // 1. we print to a file
883                         // 2. we print directly to a printer
884                         // 3. we print using a spool command (print to file first)
885                         Systemcall one;
886                         int res = 0;
887                         string const dviname =
888                                 ChangeExtension(buffer->getLatexName(true),
889                                                 "dvi");
890
891                         if (target == "printer") {
892                                 if (!lyxrc.print_spool_command.empty()) {
893                                         // case 3: print using a spool
894                                         string const psname =
895                                                 ChangeExtension(dviname,".ps");
896                                         command += lyxrc.print_to_file
897                                                 + QuoteName(psname)
898                                                 + ' '
899                                                 + QuoteName(dviname);
900
901                                         string command2 =
902                                                 lyxrc.print_spool_command +' ';
903                                         if (target_name != "default") {
904                                                 command2 += lyxrc.print_spool_printerprefix
905                                                         + target_name
906                                                         + ' ';
907                                         }
908                                         command2 += QuoteName(psname);
909                                         // First run dvips.
910                                         // If successful, then spool command
911                                         res = one.startscript(
912                                                 Systemcall::Wait,
913                                                 command);
914
915                                         if (res == 0)
916                                                 res = one.startscript(
917                                                         Systemcall::DontWait,
918                                                         command2);
919                                 } else {
920                                         // case 2: print directly to a printer
921                                         res = one.startscript(
922                                                 Systemcall::DontWait,
923                                                 command + QuoteName(dviname));
924                                 }
925
926                         } else {
927                                 // case 1: print to a file
928                                 command += lyxrc.print_to_file
929                                         + QuoteName(MakeAbsPath(target_name,
930                                                                 path))
931                                         + ' '
932                                         + QuoteName(dviname);
933                                 res = one.startscript(Systemcall::DontWait,
934                                                       command);
935                         }
936
937                         if (res != 0)
938                                 showPrintError(buffer->fileName());
939                         break;
940                 }
941
942                 case LFUN_IMPORT:
943                         doImport(argument);
944                         break;
945
946                 case LFUN_QUIT:
947                         QuitLyX();
948                         break;
949
950                 case LFUN_TOCVIEW: {
951                         InsetCommandParams p("tableofcontents");
952                         string const data = InsetCommandMailer::params2string("toc", p);
953                         owner->getDialogs().show("toc", data, 0);
954                         break;
955                 }
956
957                 case LFUN_AUTOSAVE:
958                         AutoSave(view());
959                         break;
960
961                 case LFUN_RECONFIGURE:
962                         Reconfigure(view());
963                         break;
964
965                 case LFUN_HELP_OPEN: {
966                         string const arg = argument;
967                         if (arg.empty()) {
968                                 setErrorMessage(N_("Missing argument"));
969                                 break;
970                         }
971                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
972                         if (fname.empty()) {
973                                 lyxerr << "LyX: unable to find documentation file `"
974                                                          << arg << "'. Bad installation?" << endl;
975                                 break;
976                         }
977                         owner->message(bformat(_("Opening help file %1$s..."),
978                                 MakeDisplayPath(fname)));
979                         view()->loadLyXFile(fname, false);
980                         break;
981                 }
982
983                 // --- version control -------------------------------
984                 case LFUN_VC_REGISTER:
985                         if (!ensureBufferClean(view()))
986                                 break;
987                         if (!owner->buffer()->lyxvc().inUse()) {
988                                 owner->buffer()->lyxvc().registrer();
989                                 view()->reload();
990                         }
991                         break;
992
993                 case LFUN_VC_CHECKIN:
994                         if (!ensureBufferClean(view()))
995                                 break;
996                         if (owner->buffer()->lyxvc().inUse()
997                                         && !owner->buffer()->isReadonly()) {
998                                 owner->buffer()->lyxvc().checkIn();
999                                 view()->reload();
1000                         }
1001                         break;
1002
1003                 case LFUN_VC_CHECKOUT:
1004                         if (!ensureBufferClean(view()))
1005                                 break;
1006                         if (owner->buffer()->lyxvc().inUse()
1007                                         && owner->buffer()->isReadonly()) {
1008                                 owner->buffer()->lyxvc().checkOut();
1009                                 view()->reload();
1010                         }
1011                         break;
1012
1013                 case LFUN_VC_REVERT:
1014                         owner->buffer()->lyxvc().revert();
1015                         view()->reload();
1016                         break;
1017
1018                 case LFUN_VC_UNDO:
1019                         owner->buffer()->lyxvc().undoLast();
1020                         view()->reload();
1021                         break;
1022
1023                 // --- buffers ----------------------------------------
1024                 case LFUN_SWITCHBUFFER:
1025                         view()->setBuffer(bufferlist.getBuffer(argument));
1026                         break;
1027
1028                 case LFUN_NEXTBUFFER:
1029                         view()->setBuffer(bufferlist.next(view()->buffer()));
1030                         break;
1031
1032                 case LFUN_PREVIOUSBUFFER:
1033                         view()->setBuffer(bufferlist.previous(view()->buffer()));
1034                         break;
1035
1036                 case LFUN_FILE_NEW:
1037                         NewFile(view(), argument);
1038                         break;
1039
1040                 case LFUN_FILE_OPEN:
1041                         open(argument);
1042                         break;
1043
1044                 case LFUN_DROP_LAYOUTS_CHOICE:
1045                         owner->getToolbars().openLayoutList();
1046                         break;
1047
1048                 case LFUN_MENU_OPEN_BY_NAME:
1049                         owner->getMenubar().openByName(argument);
1050                         break;
1051
1052                 // --- lyxserver commands ----------------------------
1053                 case LFUN_GETNAME:
1054                         setMessage(owner->buffer()->fileName());
1055                         lyxerr[Debug::INFO] << "FNAME["
1056                                                          << owner->buffer()->fileName()
1057                                                          << "] " << endl;
1058                         break;
1059
1060                 case LFUN_NOTIFY:
1061                         dispatch_buffer = keyseq.print();
1062                         lyxserver->notifyClient(dispatch_buffer);
1063                         break;
1064
1065                 case LFUN_GOTOFILEROW: {
1066                         string file_name;
1067                         int row;
1068                         istringstream is(argument);
1069                         is >> file_name >> row;
1070                         if (prefixIs(file_name, package().temp_dir())) {
1071                                 // Needed by inverse dvi search. If it is a file
1072                                 // in tmpdir, call the apropriated function
1073                                 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1074                         } else {
1075                                 // Must replace extension of the file to be .lyx
1076                                 // and get full path
1077                                 string const s = ChangeExtension(file_name, ".lyx");
1078                                 // Either change buffer or load the file
1079                                 if (bufferlist.exists(s)) {
1080                                         view()->setBuffer(bufferlist.getBuffer(s));
1081                                 } else {
1082                                         view()->loadLyXFile(s);
1083                                 }
1084                         }
1085
1086                         view()->setCursorFromRow(row);
1087
1088                         view()->center();
1089                         // see BufferView_pimpl::center()
1090                         view()->updateScrollbar();
1091                         break;
1092                 }
1093
1094                 case LFUN_GOTO_PARAGRAPH: {
1095                         istringstream is(argument);
1096                         int id;
1097                         is >> id;
1098                         ParIterator par = owner->buffer()->getParFromID(id);
1099                         if (par == owner->buffer()->par_iterator_end()) {
1100                                 lyxerr[Debug::INFO] << "No matching paragraph found! ["
1101                                                                 << id << ']' << endl;
1102                                 break;
1103                         } else {
1104                                 lyxerr[Debug::INFO] << "Paragraph " << par->id()
1105                                                                 << " found." << endl;
1106                         }
1107
1108                         // Set the cursor
1109                         view()->setCursor(par, 0);
1110
1111                         view()->switchKeyMap();
1112                         owner->view_state_changed();
1113
1114                         view()->center();
1115                         // see BufferView_pimpl::center()
1116                         view()->updateScrollbar();
1117                         break;
1118                 }
1119
1120                 case LFUN_DIALOG_SHOW: {
1121                         string const name = cmd.getArg(0);
1122                         string data = trim(cmd.argument.substr(name.size()));
1123
1124                         if (name == "character") {
1125                                 data = freefont2string();
1126                                 if (!data.empty())
1127                                         owner->getDialogs().show("character", data);
1128                         }
1129
1130                         else if (name == "latexlog") {
1131                                 pair<Buffer::LogType, string> const logfile =
1132                                         owner->buffer()->getLogName();
1133                                 switch (logfile.first) {
1134                                 case Buffer::latexlog:
1135                                         data = "latex ";
1136                                         break;
1137                                 case Buffer::buildlog:
1138                                         data = "literate ";
1139                                         break;
1140                                 }
1141                                 data += logfile.second;
1142                                 owner->getDialogs().show("log", data);
1143                         }
1144                         else if (name == "vclog") {
1145                                 string const data = "vc " +
1146                                         owner->buffer()->lyxvc().getLogFile();
1147                                 owner->getDialogs().show("log", data);
1148                         }
1149                         else
1150                                 owner->getDialogs().show(name, data);
1151                         break;
1152                 }
1153
1154                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1155                         string const name = cmd.getArg(0);
1156                         string data = trim(cmd.argument.substr(name.size()));
1157                         if (name == "bibitem" ||
1158                             name == "bibtex" ||
1159                             name == "include" ||
1160                             name == "index" ||
1161                             name == "label" ||
1162                             name == "ref" ||
1163                             name == "toc" ||
1164                             name == "url") {
1165                                 InsetCommandParams p(name);
1166                                 data = InsetCommandMailer::params2string(name, p);
1167                         } else if (name == "box") {
1168                                 // \c data == "Boxed" || "Frameless" etc
1169                                 InsetBoxParams p(data);
1170                                 data = InsetBoxMailer::params2string(p);
1171                         } else if (name == "branch") {
1172                                 InsetBranchParams p;
1173                                 data = InsetBranchMailer::params2string(p);
1174                         } else if (name == "citation") {
1175                                 InsetCommandParams p("cite");
1176                                 data = InsetCommandMailer::params2string(name, p);
1177                         } else if (name == "ert") {
1178                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1179                         } else if (name == "external") {
1180                                 InsetExternalParams p;
1181                                 Buffer const & buffer = *owner->buffer();
1182                                 data = InsetExternalMailer::params2string(p, buffer);
1183                         } else if (name == "float") {
1184                                 InsetFloatParams p;
1185                                 data = InsetFloatMailer::params2string(p);
1186                         } else if (name == "graphics") {
1187                                 InsetGraphicsParams p;
1188                                 Buffer const & buffer = *owner->buffer();
1189                                 data = InsetGraphicsMailer::params2string(p, buffer);
1190                         } else if (name == "note") {
1191                                 InsetNoteParams p;
1192                                 data = InsetNoteMailer::params2string(p);
1193                         } else if (name == "vspace") {
1194                                 VSpace space;
1195                                 data = InsetVSpaceMailer::params2string(space);
1196                         } else if (name == "wrap") {
1197                                 InsetWrapParams p;
1198                                 data = InsetWrapMailer::params2string(p);
1199                         }
1200                         owner->getDialogs().show(name, data, 0);
1201                         break;
1202                 }
1203
1204                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1205                         break;
1206
1207                 case LFUN_DIALOG_UPDATE: {
1208                         string const & name = argument;
1209                         // Can only update a dialog connected to an existing inset
1210                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1211                         if (inset) {
1212                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1213                                 inset->dispatch(view()->cursor(), fr);
1214                         } else if (name == "paragraph") {
1215                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1216                         } else if (name == "prefs") {
1217                                 owner->getDialogs().update(name, string());
1218                         }
1219                         break;
1220                 }
1221
1222                 case LFUN_DIALOG_HIDE:
1223                         Dialogs::hide(argument, 0);
1224                         break;
1225
1226                 case LFUN_DIALOG_DISCONNECT_INSET:
1227                         owner->getDialogs().disconnect(argument);
1228                         break;
1229
1230                 case LFUN_CHILDOPEN: {
1231                         string const filename =
1232                                 MakeAbsPath(argument, owner->buffer()->filePath());
1233                         setMessage(N_("Opening child document ") +
1234                                          MakeDisplayPath(filename) + "...");
1235                         view()->savePosition(0);
1236                         string const parentfilename = owner->buffer()->fileName();
1237                         if (bufferlist.exists(filename))
1238                                 view()->setBuffer(bufferlist.getBuffer(filename));
1239                         else
1240                                 view()->loadLyXFile(filename);
1241                         // Set the parent name of the child document.
1242                         // This makes insertion of citations and references in the child work,
1243                         // when the target is in the parent or another child document.
1244                         owner->buffer()->setParentName(parentfilename);
1245                         break;
1246                 }
1247
1248                 case LFUN_TOGGLECURSORFOLLOW:
1249                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1250                         break;
1251
1252                 case LFUN_KMAP_OFF:
1253                         owner->getIntl().KeyMapOn(false);
1254                         break;
1255
1256                 case LFUN_KMAP_PRIM:
1257                         owner->getIntl().KeyMapPrim();
1258                         break;
1259
1260                 case LFUN_KMAP_SEC:
1261                         owner->getIntl().KeyMapSec();
1262                         break;
1263
1264                 case LFUN_KMAP_TOGGLE:
1265                         owner->getIntl().ToggleKeyMap();
1266                         break;
1267
1268                 case LFUN_REPEAT: {
1269                         // repeat command
1270                         string countstr;
1271                         string rest = split(argument, countstr, ' ');
1272                         istringstream is(countstr);
1273                         int count = 0;
1274                         is >> count;
1275                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1276                         for (int i = 0; i < count; ++i)
1277                                 dispatch(lyxaction.lookupFunc(rest));
1278                         break;
1279                 }
1280
1281                 case LFUN_SEQUENCE: {
1282                         // argument contains ';'-terminated commands
1283                         string arg = argument;
1284                         while (!arg.empty()) {
1285                                 string first;
1286                                 arg = split(arg, first, ';');
1287                                 FuncRequest func(lyxaction.lookupFunc(first));
1288                                 func.origin = cmd.origin;
1289                                 dispatch(func);
1290                         }
1291                         break;
1292                 }
1293
1294                 case LFUN_SAVEPREFERENCES: {
1295                         Path p(package().user_support());
1296                         lyxrc.write("preferences", false);
1297                         break;
1298                 }
1299
1300                 case LFUN_SCREEN_FONT_UPDATE:
1301                         // handle the screen font changes.
1302                         lyxrc.set_font_norm_type();
1303                         lyx_gui::update_fonts();
1304                         // All visible buffers will need resize
1305                         view()->resize();
1306                         break;
1307
1308                 case LFUN_SET_COLOR: {
1309                         string lyx_name;
1310                         string const x11_name = split(argument, lyx_name, ' ');
1311                         if (lyx_name.empty() || x11_name.empty()) {
1312                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1313                                                         " <x11_name>"));
1314                                 break;
1315                         }
1316
1317                         bool const graphicsbg_changed =
1318                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1319                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1320
1321                         if (!lcolor.setColor(lyx_name, x11_name)) {
1322                                 setErrorMessage(
1323                                         bformat(_("Set-color \"%1$s\" failed "
1324                                                                 "- color is undefined or "
1325                                                                 "may not be redefined"), lyx_name));
1326                                 break;
1327                         }
1328
1329                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1330
1331                         if (graphicsbg_changed) {
1332 #ifdef WITH_WARNINGS
1333 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1334 #endif
1335 #if 0
1336                                 lyx::graphics::GCache::get().changeDisplay(true);
1337 #endif
1338                         }
1339                         break;
1340                 }
1341
1342                 case LFUN_MESSAGE:
1343                         owner->message(argument);
1344                         break;
1345
1346                 case LFUN_TOOLTIPS_TOGGLE:
1347                         owner->getDialogs().toggleTooltips();
1348                         break;
1349
1350                 case LFUN_EXTERNAL_EDIT: {
1351                         FuncRequest fr(action, argument);
1352                         InsetExternal().dispatch(view()->cursor(), fr);
1353                         break;
1354                 }
1355
1356                 case LFUN_GRAPHICS_EDIT: {
1357                         FuncRequest fr(action, argument);
1358                         InsetGraphics().dispatch(view()->cursor(), fr);
1359                         break;
1360                 }
1361
1362                 case LFUN_ALL_INSETS_TOGGLE: {
1363                         string action;
1364                         string const name = split(argument, action, ' ');
1365                         InsetBase::Code const inset_code =
1366                                 InsetBase::translate(name);
1367
1368                         LCursor & cur = view()->cursor();
1369                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1370
1371                         InsetBase & inset = owner->buffer()->inset();
1372                         InsetIterator it  = inset_iterator_begin(inset);
1373                         InsetIterator const end = inset_iterator_end(inset);
1374                         for (; it != end; ++it) {
1375                                 if (inset_code == InsetBase::NO_CODE
1376                                     || inset_code == it->lyxCode())
1377                                         it->dispatch(cur, fr);
1378                         }
1379                         break;
1380                 }
1381
1382                 case LFUN_LANGUAGE_BUFFER: {
1383                         Buffer & buffer = *owner->buffer();
1384                         Language const * oldL = buffer.params().language;
1385                         Language const * newL = languages.getLanguage(argument);
1386                         if (!newL || oldL == newL)
1387                                 break;
1388
1389                         if (oldL->RightToLeft() == newL->RightToLeft()
1390                             && !buffer.isMultiLingual())
1391                                 buffer.changeLanguage(oldL, newL);
1392                         else
1393                                 buffer.updateDocLang(newL);
1394                         break;
1395                 }
1396
1397                 case LFUN_SAVE_AS_DEFAULT: {
1398                         string const fname =
1399                                 AddName(AddPath(package().user_support(), "templates/"),
1400                                         "defaults.lyx");
1401                         Buffer defaults(fname);
1402
1403                         istringstream ss(argument);
1404                         LyXLex lex(0,0);
1405                         lex.setStream(ss);
1406                         int const unknown_tokens = defaults.readHeader(lex);
1407
1408                         if (unknown_tokens != 0) {
1409                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1410                                        << unknown_tokens << " unknown token"
1411                                        << (unknown_tokens == 1 ? "" : "s")
1412                                        << endl;
1413                         }
1414
1415                         if (defaults.writeFile(defaults.fileName()))
1416                                 setMessage(_("Document defaults saved in ")
1417                                            + MakeDisplayPath(fname));
1418                         else
1419                                 setErrorMessage(_("Unable to save document defaults"));
1420                         break;
1421                 }
1422
1423                 case LFUN_BUFFERPARAMS_APPLY: {
1424                         biblio::CiteEngine const engine =
1425                                 owner->buffer()->params().cite_engine;
1426
1427                         istringstream ss(argument);
1428                         LyXLex lex(0,0);
1429                         lex.setStream(ss);
1430                         int const unknown_tokens =
1431                                 owner->buffer()->readHeader(lex);
1432
1433                         if (unknown_tokens != 0) {
1434                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1435                                        << unknown_tokens << " unknown token"
1436                                        << (unknown_tokens == 1 ? "" : "s")
1437                                        << endl;
1438                         }
1439                         if (engine == owner->buffer()->params().cite_engine)
1440                                 break;
1441
1442                         LCursor & cur = view()->cursor();
1443                         FuncRequest fr(LFUN_INSET_REFRESH);
1444
1445                         InsetBase & inset = owner->buffer()->inset();
1446                         InsetIterator it  = inset_iterator_begin(inset);
1447                         InsetIterator const end = inset_iterator_end(inset);
1448                         for (; it != end; ++it)
1449                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1450                                         it->dispatch(cur, fr);
1451                         break;
1452                 }
1453
1454                 case LFUN_TEXTCLASS_APPLY: {
1455                         Buffer * buffer = owner->buffer();
1456
1457                         lyx::textclass_type const old_class =
1458                                 buffer->params().textclass;
1459
1460                         loadTextclass(argument);
1461
1462                         std::pair<bool, lyx::textclass_type> const tc_pair =
1463                                 textclasslist.NumberOfClass(argument);
1464
1465                         if (!tc_pair.first)
1466                                 break;
1467
1468                         lyx::textclass_type const new_class = tc_pair.second;
1469                         if (old_class == new_class)
1470                                 // nothing to do
1471                                 break;
1472
1473                         owner->message(_("Converting document to new document class..."));
1474                         ErrorList el;
1475                         lyx::cap::SwitchLayoutsBetweenClasses(
1476                                 old_class, new_class,
1477                                 buffer->paragraphs(), el);
1478
1479                         bufferErrors(*buffer, el);
1480                         view()->showErrorList(_("Class switch"));
1481                         break;
1482                 }
1483
1484                 case LFUN_TEXTCLASS_LOAD:
1485                         loadTextclass(argument);
1486                         break;
1487
1488                 case LFUN_LYXRC_APPLY: {
1489                         istringstream ss(argument);
1490                         bool const success = lyxrc.read(ss) == 0;
1491
1492                         if (!success) {
1493                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1494                                        << "Unable to read lyxrc data"
1495                                        << endl;
1496                         }
1497                         break;
1498                 }
1499
1500                 default: {
1501                         update = false;
1502                         view()->cursor().dispatch(cmd);
1503                         if (view()->cursor().result().dispatched())
1504                                 update |= view()->cursor().result().update();
1505                         else
1506                                 update |= view()->dispatch(cmd);
1507                         break;
1508                 }
1509                 }
1510
1511                 if (view()->available()) {
1512                         // Redraw screen unless explicitly told otherwise.
1513                         // This also initializes the position cache for all insets
1514                         // in (at least partially) visible top-level paragraphs.
1515                         view()->update(true, update);
1516
1517                         // if we executed a mutating lfun, mark the buffer as dirty
1518                         if (getStatus(cmd).enabled()
1519                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1520                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1521                                 view()->buffer()->markDirty();
1522                 }
1523
1524                 if (view()->cursor().inTexted()) {
1525                         view()->owner()->updateLayoutChoice();
1526                 }
1527         }
1528         sendDispatchMessage(getMessage(), cmd);
1529 }
1530
1531
1532 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1533 {
1534         owner->updateMenubar();
1535         owner->updateToolbars();
1536
1537         const bool verbose = (cmd.origin == FuncRequest::UI
1538                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1539
1540         if (cmd.action == LFUN_SELFINSERT || !verbose) {
1541                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1542                 if (!msg.empty())
1543                         owner->message(msg);
1544                 return;
1545         }
1546
1547         string dispatch_msg = msg;
1548         if (!dispatch_msg.empty())
1549                 dispatch_msg += ' ';
1550
1551         string comname = lyxaction.getActionName(cmd.action);
1552
1553         bool argsadded = false;
1554
1555         if (!cmd.argument.empty()) {
1556                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1557                         comname += ' ' + cmd.argument;
1558                         argsadded = true;
1559                 }
1560         }
1561
1562         string const shortcuts = toplevel_keymap->printbindings(cmd);
1563
1564         if (!shortcuts.empty()) {
1565                 comname += ": " + shortcuts;
1566         } else if (!argsadded && !cmd.argument.empty()) {
1567                 comname += ' ' + cmd.argument;
1568         }
1569
1570         if (!comname.empty()) {
1571                 comname = rtrim(comname);
1572                 dispatch_msg += '(' + comname + ')';
1573         }
1574
1575         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1576         if (!dispatch_msg.empty())
1577                 owner->message(dispatch_msg);
1578 }
1579
1580
1581 void LyXFunc::setupLocalKeymap()
1582 {
1583         keyseq.stdmap = toplevel_keymap.get();
1584         keyseq.curmap = toplevel_keymap.get();
1585         cancel_meta_seq.stdmap = toplevel_keymap.get();
1586         cancel_meta_seq.curmap = toplevel_keymap.get();
1587 }
1588
1589
1590 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1591 {
1592         string initpath = lyxrc.document_path;
1593         string filename(name);
1594
1595         if (view()->available()) {
1596                 string const trypath = owner->buffer()->filePath();
1597                 // If directory is writeable, use this as default.
1598                 if (IsDirWriteable(trypath))
1599                         initpath = trypath;
1600         }
1601
1602         static int newfile_number;
1603
1604         if (filename.empty()) {
1605                 filename = AddName(lyxrc.document_path,
1606                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1607                 FileInfo fi(filename);
1608                 while (bufferlist.exists(filename) || fi.readable()) {
1609                         ++newfile_number;
1610                         filename = AddName(lyxrc.document_path,
1611                                            "newfile" +  convert<string>(newfile_number) +
1612                                     ".lyx");
1613                         fi.newFile(filename);
1614                 }
1615         }
1616
1617         // The template stuff
1618         string templname;
1619         if (fromTemplate) {
1620                 FileDialog fileDlg(_("Select template file"),
1621                         LFUN_SELECT_FILE_SYNC,
1622                         make_pair(string(_("Documents|#o#O")),
1623                                   string(lyxrc.document_path)),
1624                         make_pair(string(_("Templates|#T#t")),
1625                                   string(lyxrc.template_path)));
1626
1627                 FileDialog::Result result =
1628                         fileDlg.open(lyxrc.template_path,
1629                                      FileFilterList(_("LyX Documents (*.lyx)")),
1630                                      string());
1631
1632                 if (result.first == FileDialog::Later)
1633                         return;
1634                 if (result.second.empty())
1635                         return;
1636                 templname = result.second;
1637         }
1638
1639         view()->newFile(filename, templname, !name.empty());
1640 }
1641
1642
1643 void LyXFunc::open(string const & fname)
1644 {
1645         string initpath = lyxrc.document_path;
1646
1647         if (view()->available()) {
1648                 string const trypath = owner->buffer()->filePath();
1649                 // If directory is writeable, use this as default.
1650                 if (IsDirWriteable(trypath))
1651                         initpath = trypath;
1652         }
1653
1654         string filename;
1655
1656         if (fname.empty()) {
1657                 FileDialog fileDlg(_("Select document to open"),
1658                         LFUN_FILE_OPEN,
1659                         make_pair(string(_("Documents|#o#O")),
1660                                   string(lyxrc.document_path)),
1661                         make_pair(string(_("Examples|#E#e")),
1662                                   string(AddPath(package().system_support(), "examples"))));
1663
1664                 FileDialog::Result result =
1665                         fileDlg.open(initpath,
1666                                      FileFilterList(_("LyX Documents (*.lyx)")),
1667                                      string());
1668
1669                 if (result.first == FileDialog::Later)
1670                         return;
1671
1672                 filename = result.second;
1673
1674                 // check selected filename
1675                 if (filename.empty()) {
1676                         owner->message(_("Canceled."));
1677                         return;
1678                 }
1679         } else
1680                 filename = fname;
1681
1682         // get absolute path of file and add ".lyx" to the filename if
1683         // necessary
1684         string const fullpath = FileSearch(string(), filename, "lyx");
1685         if (!fullpath.empty()) {
1686                 filename = fullpath;
1687         }
1688
1689         string const disp_fn(MakeDisplayPath(filename));
1690
1691         // if the file doesn't exist, let the user create one
1692         FileInfo const f(filename, true);
1693         if (!f.exist()) {
1694                 // the user specifically chose this name. Believe them.
1695                 view()->newFile(filename, "", true);
1696                 return;
1697         }
1698
1699         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1700
1701         string str2;
1702         if (view()->loadLyXFile(filename)) {
1703                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1704         } else {
1705                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1706         }
1707         owner->message(str2);
1708 }
1709
1710
1711 void LyXFunc::doImport(string const & argument)
1712 {
1713         string format;
1714         string filename = split(argument, format, ' ');
1715
1716         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1717                             << " file: " << filename << endl;
1718
1719         // need user interaction
1720         if (filename.empty()) {
1721                 string initpath = lyxrc.document_path;
1722
1723                 if (view()->available()) {
1724                         string const trypath = owner->buffer()->filePath();
1725                         // If directory is writeable, use this as default.
1726                         if (IsDirWriteable(trypath))
1727                                 initpath = trypath;
1728                 }
1729
1730                 string const text = bformat(_("Select %1$s file to import"),
1731                         formats.prettyName(format));
1732
1733                 FileDialog fileDlg(text,
1734                         LFUN_IMPORT,
1735                         make_pair(string(_("Documents|#o#O")),
1736                                   string(lyxrc.document_path)),
1737                         make_pair(string(_("Examples|#E#e")),
1738                                   string(AddPath(package().system_support(), "examples"))));
1739
1740                 string const filter = formats.prettyName(format)
1741                         + " (*." + formats.extension(format) + ')';
1742
1743                 FileDialog::Result result =
1744                         fileDlg.open(initpath,
1745                                      FileFilterList(filter),
1746                                      string());
1747
1748                 if (result.first == FileDialog::Later)
1749                         return;
1750
1751                 filename = result.second;
1752
1753                 // check selected filename
1754                 if (filename.empty())
1755                         owner->message(_("Canceled."));
1756         }
1757
1758         if (filename.empty())
1759                 return;
1760
1761         // get absolute path of file
1762         filename = MakeAbsPath(filename);
1763
1764         string const lyxfile = ChangeExtension(filename, ".lyx");
1765
1766         // Check if the document already is open
1767         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1768                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1769                         owner->message(_("Canceled."));
1770                         return;
1771                 }
1772         }
1773
1774         // if the file exists already, and we didn't do
1775         // -i lyx thefile.lyx, warn
1776         if (FileInfo(lyxfile, true).exist() && filename != lyxfile) {
1777                 string const file = MakeDisplayPath(lyxfile, 30);
1778
1779                 string text = bformat(_("The document %1$s already exists.\n\n"
1780                         "Do you want to over-write that document?"), file);
1781                 int const ret = Alert::prompt(_("Over-write document?"),
1782                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1783
1784                 if (ret == 1) {
1785                         owner->message(_("Canceled."));
1786                         return;
1787                 }
1788         }
1789
1790         Importer::Import(owner, filename, format);
1791 }
1792
1793
1794 void LyXFunc::closeBuffer()
1795 {
1796         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1797                 if (bufferlist.empty()) {
1798                         // need this otherwise SEGV may occur while
1799                         // trying to set variables that don't exist
1800                         // since there's no current buffer
1801                         owner->getDialogs().hideBufferDependent();
1802                 } else {
1803                         view()->setBuffer(bufferlist.first());
1804                 }
1805         }
1806 }
1807
1808
1809 // Each "owner" should have it's own message method. lyxview and
1810 // the minibuffer would use the minibuffer, but lyxserver would
1811 // send an ERROR signal to its client.  Alejandro 970603
1812 // This function is bit problematic when it comes to NLS, to make the
1813 // lyx servers client be language indepenent we must not translate
1814 // strings sent to this func.
1815 void LyXFunc::setErrorMessage(string const & m) const
1816 {
1817         dispatch_buffer = m;
1818         errorstat = true;
1819 }
1820
1821
1822 void LyXFunc::setMessage(string const & m) const
1823 {
1824         dispatch_buffer = m;
1825 }
1826
1827
1828 string const LyXFunc::viewStatusMessage()
1829 {
1830         // When meta-fake key is pressed, show the key sequence so far + "M-".
1831         if (wasMetaKey())
1832                 return keyseq.print() + "M-";
1833
1834         // Else, when a non-complete key sequence is pressed,
1835         // show the available options.
1836         if (keyseq.length() > 0 && !keyseq.deleted())
1837                 return keyseq.printOptions();
1838
1839         if (!view()->available())
1840                 return _("Welcome to LyX!");
1841
1842         return view()->cursor().currentState();
1843 }
1844
1845
1846 BufferView * LyXFunc::view() const
1847 {
1848         BOOST_ASSERT(owner);
1849         return owner->view().get();
1850 }
1851
1852
1853 bool LyXFunc::wasMetaKey() const
1854 {
1855         return (meta_fake_bit != key_modifier::none);
1856 }