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