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