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