]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
Rearrange and rename the environment variable setter/getter functions.
[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/filesystem/operations.hpp>
94
95 #include <sstream>
96
97 using bv_funcs::freefont2string;
98
99 using lyx::support::AbsolutePath;
100 using lyx::support::AddName;
101 using lyx::support::AddPath;
102 using lyx::support::bformat;
103 using lyx::support::ChangeExtension;
104 using lyx::support::contains;
105 using lyx::support::FileFilterList;
106 using lyx::support::FileSearch;
107 using lyx::support::ForkedcallsController;
108 using lyx::support::i18nLibFileSearch;
109 using lyx::support::IsDirWriteable;
110 using lyx::support::IsFileReadable;
111 using lyx::support::isStrInt;
112 using lyx::support::MakeAbsPath;
113 using lyx::support::MakeDisplayPath;
114 using lyx::support::package;
115 using lyx::support::Path;
116 using lyx::support::QuoteName;
117 using lyx::support::rtrim;
118 using lyx::support::split;
119 using lyx::support::subst;
120 using lyx::support::Systemcall;
121 using lyx::support::token;
122 using lyx::support::trim;
123 using lyx::support::prefixIs;
124
125 using std::endl;
126 using std::make_pair;
127 using std::pair;
128 using std::string;
129 using std::istringstream;
130
131 namespace biblio = lyx::biblio;
132 namespace fs = boost::filesystem;
133
134
135 extern BufferList bufferlist;
136 extern LyXServer * lyxserver;
137
138 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
139
140 // (alkis)
141 extern tex_accent_struct get_accent(kb_action action);
142
143
144 namespace {
145
146 bool getStatus(LCursor cursor,
147                FuncRequest const & cmd, FuncStatus & status)
148 {
149         // This is, of course, a mess. Better create a new doc iterator and use
150         // this in Inset::getStatus. This might require an additional
151         // BufferView * arg, though (which should be avoided)
152         //LCursor safe = *this;
153         bool res = false;
154         for ( ; cursor.depth(); cursor.pop()) {
155                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
156                 DocIterator::idx_type & idx = cursor.idx();
157                 DocIterator::idx_type const lastidx = cursor.lastidx();
158
159                 if (idx > lastidx) {
160                         lyxerr << "wrong idx " << idx << ", max is " << lastidx
161                                 << ". Trying to correct this."  << endl;
162                         idx = lastidx;
163                 }
164
165                 DocIterator::pit_type & pit = cursor.pit();
166                 DocIterator::pit_type const lastpit = cursor.lastpit();
167
168                 if (pit > lastpit) {
169                         lyxerr << "wrong par " << pit << ", max is " << lastpit
170                                 << ". Trying to correct this."  << endl;
171                         pit = lastpit;
172                 }
173
174                 DocIterator::pos_type & pos = cursor.pos();
175                 DocIterator::pos_type const lastpos = cursor.lastpos();
176
177                 if (pos > lastpos) {
178                         lyxerr << "wrong pos " << pos << ", max is " << lastpos
179                                 << ". Trying to correct this."  << endl;
180                         pos = lastpos;
181                 }
182
183                 // The inset's getStatus() will return 'true' if it made
184                 // a definitive decision on whether it want to handle the
185                 // request or not. The result of this decision is put into
186                 // the 'status' parameter.
187                 if (cursor.inset().getStatus(cursor, cmd, status)) {
188                         res = true;
189                         break;
190                 }
191         }
192         return res;
193 }
194
195 }
196
197 LyXFunc::LyXFunc(LyXView * lv)
198         : owner(lv),
199         encoded_last_key(0),
200         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
201         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
202         meta_fake_bit(key_modifier::none)
203 {
204 }
205
206
207 void LyXFunc::handleKeyFunc(kb_action action)
208 {
209         char c = encoded_last_key;
210
211         if (keyseq.length()) {
212                 c = 0;
213         }
214
215         owner->getIntl().getTransManager()
216                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
217         // Need to clear, in case the minibuffer calls these
218         // actions
219         keyseq.clear();
220         // copied verbatim from do_accent_char
221         view()->cursor().resetAnchor();
222         view()->update();
223 }
224
225
226 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
227 {
228         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
229
230         // Do nothing if we have nothing (JMarc)
231         if (!keysym->isOK()) {
232                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
233                                    << endl;
234                 return;
235         }
236
237         if (keysym->isModifier()) {
238                 lyxerr[Debug::KEY] << "isModifier true" << endl;
239                 return;
240         }
241
242         Encoding const * encoding = view()->cursor().getEncoding();
243
244         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
245
246         // Do a one-deep top-level lookup for
247         // cancel and meta-fake keys. RVDK_PATCH_5
248         cancel_meta_seq.reset();
249
250         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
251         lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
252                            << " action first set to [" << func.action << ']'
253                            << endl;
254
255         // When not cancel or meta-fake, do the normal lookup.
256         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
257         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
258         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
259                 // remove Caps Lock and Mod2 as a modifiers
260                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
261                 lyxerr[Debug::KEY] << BOOST_CURRENT_FUNCTION
262                                    << "action now set to ["
263                                    << func.action << ']' << endl;
264         }
265
266         // Dont remove this unless you know what you are doing.
267         meta_fake_bit = key_modifier::none;
268
269         // Can this happen now ?
270         if (func.action == LFUN_NOACTION) {
271                 func = FuncRequest(LFUN_PREFIX);
272         }
273
274         if (lyxerr.debugging(Debug::KEY)) {
275                 lyxerr << BOOST_CURRENT_FUNCTION
276                        << " Key [action="
277                        << func.action << "]["
278                        << keyseq.print() << ']'
279                        << endl;
280         }
281
282         // already here we know if it any point in going further
283         // why not return already here if action == -1 and
284         // num_bytes == 0? (Lgb)
285
286         if (keyseq.length() > 1) {
287                 owner->message(keyseq.print());
288         }
289
290
291         // Maybe user can only reach the key via holding down shift.
292         // Let's see. But only if shift is the only modifier
293         if (func.action == LFUN_UNKNOWN_ACTION &&
294             state == key_modifier::shift) {
295                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
296                 func = keyseq.addkey(keysym, key_modifier::none);
297                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
298         }
299
300         if (func.action == LFUN_UNKNOWN_ACTION) {
301                 // Hmm, we didn't match any of the keysequences. See
302                 // if it's normal insertable text not already covered
303                 // by a binding
304                 if (keysym->isText() && keyseq.length() == 1) {
305                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
306                         func = FuncRequest(LFUN_SELFINSERT);
307                 } else {
308                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
309                         owner->message(_("Unknown function."));
310                         return;
311                 }
312         }
313
314         if (func.action == LFUN_SELFINSERT) {
315                 if (encoded_last_key != 0) {
316                         string const arg(1, encoded_last_key);
317                         dispatch(FuncRequest(LFUN_SELFINSERT, arg));
318                         lyxerr[Debug::KEY]
319                                 << "SelfInsert arg[`" << arg << "']" << endl;
320                 }
321         } else {
322                 dispatch(func);
323         }
324 }
325
326
327 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
328 {
329         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
330         FuncStatus flag;
331         LCursor & cur = view()->cursor();
332
333         /* In LyX/Mac, when a dialog is open, the menus of the
334            application can still be accessed without giving focus to
335            the main window. In this case, we want to disable the menu
336            entries that are buffer-related.
337         */
338         Buffer * buf;
339         if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
340                 buf = 0;
341         else
342                 buf = owner->buffer();
343
344         if (cmd.action == LFUN_NOACTION) {
345                 flag.message(N_("Nothing to do"));
346                 flag.enabled(false);
347                 return flag;
348         }
349
350         switch (cmd.action) {
351         case LFUN_UNKNOWN_ACTION:
352 #ifndef HAVE_LIBAIKSAURUS
353         case LFUN_THESAURUS_ENTRY:
354 #endif
355                 flag.unknown(true);
356                 flag.enabled(false);
357                 break;
358         default:
359                 flag |= lyx_gui::getStatus(cmd);
360         }
361
362         if (flag.unknown()) {
363                 flag.message(N_("Unknown action"));
364                 return flag;
365         }
366
367         if (!flag.enabled()) {
368                 if (flag.message().empty())
369                         flag.message(N_("Command disabled"));
370                 return flag;
371         }
372
373         // Check whether we need a buffer
374         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
375                 // no, exit directly
376                 flag.message(N_("Command not allowed with"
377                                     "out any document open"));
378                 flag.enabled(false);
379                 return flag;
380         }
381
382         // I would really like to avoid having this switch and rather try to
383         // encode this in the function itself.
384         // -- And I'd rather let an inset decide which LFUNs it is willing
385         // to handle (Andre')
386         bool enable = true;
387         switch (cmd.action) {
388         case LFUN_TOOLTIPS_TOGGLE:
389                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
390                 break;
391
392         case LFUN_READ_ONLY_TOGGLE:
393                 flag.setOnOff(buf->isReadonly());
394                 break;
395
396         case LFUN_SWITCHBUFFER:
397                 // toggle on the current buffer, but do not toggle off
398                 // the other ones (is that a good idea?)
399                 if (cmd.argument == buf->fileName())
400                         flag.setOnOff(true);
401                 break;
402
403         case LFUN_EXPORT:
404                 enable = cmd.argument == "custom"
405                         || Exporter::IsExportable(*buf, cmd.argument);
406                 break;
407         case LFUN_CUT:
408         case LFUN_COPY:
409                 enable = cur.selection();
410                 break;
411
412         case LFUN_RUNCHKTEX:
413                 enable = buf->isLatex() && lyxrc.chktex_command != "none";
414                 break;
415
416         case LFUN_BUILDPROG:
417                 enable = Exporter::IsExportable(*buf, "program");
418                 break;
419
420         case LFUN_LAYOUT_TABULAR:
421                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
422                 break;
423
424         case LFUN_LAYOUT:
425         case LFUN_LAYOUT_PARAGRAPH:
426                 enable = !cur.inset().forceDefaultParagraphs(&cur.inset());
427                 break;
428
429         case LFUN_VC_REGISTER:
430                 enable = !buf->lyxvc().inUse();
431                 break;
432         case LFUN_VC_CHECKIN:
433                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
434                 break;
435         case LFUN_VC_CHECKOUT:
436                 enable = buf->lyxvc().inUse() && buf->isReadonly();
437                 break;
438         case LFUN_VC_REVERT:
439         case LFUN_VC_UNDO:
440                 enable = buf->lyxvc().inUse();
441                 break;
442         case LFUN_MENURELOAD:
443                 enable = !buf->isUnnamed() && !buf->isClean();
444                 break;
445
446         case LFUN_INSET_SETTINGS: {
447                 enable = false;
448                 if (!cur)
449                         break;
450                 UpdatableInset * inset = cur.inset().asUpdatableInset();
451                 lyxerr << "inset: " << inset << endl;
452                 if (!inset)
453                         break;
454
455                 InsetBase::Code code = inset->lyxCode();
456                 switch (code) {
457                         case InsetBase::TABULAR_CODE:
458                                 enable = cmd.argument == "tabular";
459                                 break;
460                         case InsetBase::ERT_CODE:
461                                 enable = cmd.argument == "ert";
462                                 break;
463                         case InsetBase::FLOAT_CODE:
464                                 enable = cmd.argument == "float";
465                                 break;
466                         case InsetBase::WRAP_CODE:
467                                 enable = cmd.argument == "wrap";
468                                 break;
469                         case InsetBase::NOTE_CODE:
470                                 enable = cmd.argument == "note";
471                                 break;
472                         case InsetBase::BRANCH_CODE:
473                                 enable = cmd.argument == "branch";
474                                 break;
475                         case InsetBase::BOX_CODE:
476                                 enable = cmd.argument == "box";
477                                 break;
478                         default:
479                                 break;
480                 }
481                 break;
482         }
483
484         case LFUN_DIALOG_SHOW: {
485                 string const name = cmd.getArg(0);
486                 if (!buf)
487                         enable = name == "aboutlyx"
488                                 || name == "file"
489                                 || name == "forks"
490                                 || name == "prefs"
491                                 || name == "texinfo";
492                 else if (name == "print")
493                         enable = Exporter::IsExportable(*buf, "dvi")
494                                 && lyxrc.print_command != "none";
495                 else if (name == "character" || name == "mathpanel")
496                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
497                 else if (name == "vclog")
498                         enable = buf->lyxvc().inUse();
499                 else if (name == "latexlog")
500                         enable = IsFileReadable(buf->getLogName().second);
501                 break;
502         }
503
504         case LFUN_DIALOG_SHOW_NEW_INSET:
505                 enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
506                 break;
507
508         case LFUN_DIALOG_UPDATE: {
509                 string const name = cmd.getArg(0);
510                 if (!buf)
511                         enable = name == "prefs";
512                 break;
513         }
514
515         // this one is difficult to get right. As a half-baked
516         // solution, we consider only the first action of the sequence
517         case LFUN_SEQUENCE: {
518                 // argument contains ';'-terminated commands
519                 string const firstcmd = token(cmd.argument, ';', 0);
520                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
521                 func.origin = cmd.origin;
522                 flag = getStatus(func);
523         }
524
525         case LFUN_MENUNEW:
526         case LFUN_MENUNEWTMPLT:
527         case LFUN_WORDFINDFORWARD:
528         case LFUN_WORDFINDBACKWARD:
529         case LFUN_PREFIX:
530         case LFUN_EXEC_COMMAND:
531         case LFUN_CANCEL:
532         case LFUN_META_FAKE:
533         case LFUN_CLOSEBUFFER:
534         case LFUN_MENUWRITE:
535         case LFUN_WRITEAS:
536         case LFUN_UPDATE:
537         case LFUN_PREVIEW:
538         case LFUN_IMPORT:
539         case LFUN_QUIT:
540         case LFUN_TOCVIEW:
541         case LFUN_AUTOSAVE:
542         case LFUN_RECONFIGURE:
543         case LFUN_HELP_OPEN:
544         case LFUN_FILE_NEW:
545         case LFUN_FILE_OPEN:
546         case LFUN_DROP_LAYOUTS_CHOICE:
547         case LFUN_MENU_OPEN_BY_NAME:
548         case LFUN_GETNAME:
549         case LFUN_NOTIFY:
550         case LFUN_GOTOFILEROW:
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_DIALOG_SHOW: {
1105                         string const name = cmd.getArg(0);
1106                         string data = trim(cmd.argument.substr(name.size()));
1107
1108                         if (name == "character") {
1109                                 data = freefont2string();
1110                                 if (!data.empty())
1111                                         owner->getDialogs().show("character", data);
1112                         }
1113
1114                         else if (name == "latexlog") {
1115                                 pair<Buffer::LogType, string> const logfile =
1116                                         owner->buffer()->getLogName();
1117                                 switch (logfile.first) {
1118                                 case Buffer::latexlog:
1119                                         data = "latex ";
1120                                         break;
1121                                 case Buffer::buildlog:
1122                                         data = "literate ";
1123                                         break;
1124                                 }
1125                                 data += logfile.second;
1126                                 owner->getDialogs().show("log", data);
1127                         }
1128                         else if (name == "vclog") {
1129                                 string const data = "vc " +
1130                                         owner->buffer()->lyxvc().getLogFile();
1131                                 owner->getDialogs().show("log", data);
1132                         }
1133                         else
1134                                 owner->getDialogs().show(name, data);
1135                         break;
1136                 }
1137
1138                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1139                         string const name = cmd.getArg(0);
1140                         string data = trim(cmd.argument.substr(name.size()));
1141                         if (name == "bibitem" ||
1142                             name == "bibtex" ||
1143                             name == "include" ||
1144                             name == "index" ||
1145                             name == "label" ||
1146                             name == "ref" ||
1147                             name == "toc" ||
1148                             name == "url") {
1149                                 InsetCommandParams p(name);
1150                                 data = InsetCommandMailer::params2string(name, p);
1151                         } else if (name == "box") {
1152                                 // \c data == "Boxed" || "Frameless" etc
1153                                 InsetBoxParams p(data);
1154                                 data = InsetBoxMailer::params2string(p);
1155                         } else if (name == "branch") {
1156                                 InsetBranchParams p;
1157                                 data = InsetBranchMailer::params2string(p);
1158                         } else if (name == "citation") {
1159                                 InsetCommandParams p("cite");
1160                                 data = InsetCommandMailer::params2string(name, p);
1161                         } else if (name == "ert") {
1162                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1163                         } else if (name == "external") {
1164                                 InsetExternalParams p;
1165                                 Buffer const & buffer = *owner->buffer();
1166                                 data = InsetExternalMailer::params2string(p, buffer);
1167                         } else if (name == "float") {
1168                                 InsetFloatParams p;
1169                                 data = InsetFloatMailer::params2string(p);
1170                         } else if (name == "graphics") {
1171                                 InsetGraphicsParams p;
1172                                 Buffer const & buffer = *owner->buffer();
1173                                 data = InsetGraphicsMailer::params2string(p, buffer);
1174                         } else if (name == "note") {
1175                                 InsetNoteParams p;
1176                                 data = InsetNoteMailer::params2string(p);
1177                         } else if (name == "vspace") {
1178                                 VSpace space;
1179                                 data = InsetVSpaceMailer::params2string(space);
1180                         } else if (name == "wrap") {
1181                                 InsetWrapParams p;
1182                                 data = InsetWrapMailer::params2string(p);
1183                         }
1184                         owner->getDialogs().show(name, data, 0);
1185                         break;
1186                 }
1187
1188                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1189                         break;
1190
1191                 case LFUN_DIALOG_UPDATE: {
1192                         string const & name = argument;
1193                         // Can only update a dialog connected to an existing inset
1194                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1195                         if (inset) {
1196                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1197                                 inset->dispatch(view()->cursor(), fr);
1198                         } else if (name == "paragraph") {
1199                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1200                         } else if (name == "prefs") {
1201                                 owner->getDialogs().update(name, string());
1202                         }
1203                         break;
1204                 }
1205
1206                 case LFUN_DIALOG_HIDE:
1207                         Dialogs::hide(argument, 0);
1208                         break;
1209
1210                 case LFUN_DIALOG_DISCONNECT_INSET:
1211                         owner->getDialogs().disconnect(argument);
1212                         break;
1213
1214                 case LFUN_CHILDOPEN: {
1215                         string const filename =
1216                                 MakeAbsPath(argument, owner->buffer()->filePath());
1217                         setMessage(N_("Opening child document ") +
1218                                          MakeDisplayPath(filename) + "...");
1219                         view()->savePosition(0);
1220                         string const parentfilename = owner->buffer()->fileName();
1221                         if (bufferlist.exists(filename))
1222                                 view()->setBuffer(bufferlist.getBuffer(filename));
1223                         else
1224                                 view()->loadLyXFile(filename);
1225                         // Set the parent name of the child document.
1226                         // This makes insertion of citations and references in the child work,
1227                         // when the target is in the parent or another child document.
1228                         owner->buffer()->setParentName(parentfilename);
1229                         break;
1230                 }
1231
1232                 case LFUN_TOGGLECURSORFOLLOW:
1233                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1234                         break;
1235
1236                 case LFUN_KMAP_OFF:
1237                         owner->getIntl().KeyMapOn(false);
1238                         break;
1239
1240                 case LFUN_KMAP_PRIM:
1241                         owner->getIntl().KeyMapPrim();
1242                         break;
1243
1244                 case LFUN_KMAP_SEC:
1245                         owner->getIntl().KeyMapSec();
1246                         break;
1247
1248                 case LFUN_KMAP_TOGGLE:
1249                         owner->getIntl().ToggleKeyMap();
1250                         break;
1251
1252                 case LFUN_REPEAT: {
1253                         // repeat command
1254                         string countstr;
1255                         string rest = split(argument, countstr, ' ');
1256                         istringstream is(countstr);
1257                         int count = 0;
1258                         is >> count;
1259                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1260                         for (int i = 0; i < count; ++i)
1261                                 dispatch(lyxaction.lookupFunc(rest));
1262                         break;
1263                 }
1264
1265                 case LFUN_SEQUENCE: {
1266                         // argument contains ';'-terminated commands
1267                         string arg = argument;
1268                         while (!arg.empty()) {
1269                                 string first;
1270                                 arg = split(arg, first, ';');
1271                                 FuncRequest func(lyxaction.lookupFunc(first));
1272                                 func.origin = cmd.origin;
1273                                 dispatch(func);
1274                         }
1275                         break;
1276                 }
1277
1278                 case LFUN_SAVEPREFERENCES: {
1279                         Path p(package().user_support());
1280                         lyxrc.write("preferences", false);
1281                         break;
1282                 }
1283
1284                 case LFUN_SCREEN_FONT_UPDATE:
1285                         // handle the screen font changes.
1286                         lyxrc.set_font_norm_type();
1287                         lyx_gui::update_fonts();
1288                         // All visible buffers will need resize
1289                         view()->resize();
1290                         break;
1291
1292                 case LFUN_SET_COLOR: {
1293                         string lyx_name;
1294                         string const x11_name = split(argument, lyx_name, ' ');
1295                         if (lyx_name.empty() || x11_name.empty()) {
1296                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1297                                                         " <x11_name>"));
1298                                 break;
1299                         }
1300
1301                         bool const graphicsbg_changed =
1302                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1303                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1304
1305                         if (!lcolor.setColor(lyx_name, x11_name)) {
1306                                 setErrorMessage(
1307                                         bformat(_("Set-color \"%1$s\" failed "
1308                                                                 "- color is undefined or "
1309                                                                 "may not be redefined"), lyx_name));
1310                                 break;
1311                         }
1312
1313                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1314
1315                         if (graphicsbg_changed) {
1316 #ifdef WITH_WARNINGS
1317 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1318 #endif
1319 #if 0
1320                                 lyx::graphics::GCache::get().changeDisplay(true);
1321 #endif
1322                         }
1323                         break;
1324                 }
1325
1326                 case LFUN_MESSAGE:
1327                         owner->message(argument);
1328                         break;
1329
1330                 case LFUN_TOOLTIPS_TOGGLE:
1331                         owner->getDialogs().toggleTooltips();
1332                         break;
1333
1334                 case LFUN_EXTERNAL_EDIT: {
1335                         FuncRequest fr(action, argument);
1336                         InsetExternal().dispatch(view()->cursor(), fr);
1337                         break;
1338                 }
1339
1340                 case LFUN_GRAPHICS_EDIT: {
1341                         FuncRequest fr(action, argument);
1342                         InsetGraphics().dispatch(view()->cursor(), fr);
1343                         break;
1344                 }
1345
1346                 case LFUN_ALL_INSETS_TOGGLE: {
1347                         string action;
1348                         string const name = split(argument, action, ' ');
1349                         InsetBase::Code const inset_code =
1350                                 InsetBase::translate(name);
1351
1352                         LCursor & cur = view()->cursor();
1353                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1354
1355                         InsetBase & inset = owner->buffer()->inset();
1356                         InsetIterator it  = inset_iterator_begin(inset);
1357                         InsetIterator const end = inset_iterator_end(inset);
1358                         for (; it != end; ++it) {
1359                                 if (inset_code == InsetBase::NO_CODE
1360                                     || inset_code == it->lyxCode())
1361                                         it->dispatch(cur, fr);
1362                         }
1363                         break;
1364                 }
1365
1366                 case LFUN_LANGUAGE_BUFFER: {
1367                         Buffer & buffer = *owner->buffer();
1368                         Language const * oldL = buffer.params().language;
1369                         Language const * newL = languages.getLanguage(argument);
1370                         if (!newL || oldL == newL)
1371                                 break;
1372
1373                         if (oldL->RightToLeft() == newL->RightToLeft()
1374                             && !buffer.isMultiLingual())
1375                                 buffer.changeLanguage(oldL, newL);
1376                         else
1377                                 buffer.updateDocLang(newL);
1378                         break;
1379                 }
1380
1381                 case LFUN_SAVE_AS_DEFAULT: {
1382                         string const fname =
1383                                 AddName(AddPath(package().user_support(), "templates/"),
1384                                         "defaults.lyx");
1385                         Buffer defaults(fname);
1386
1387                         istringstream ss(argument);
1388                         LyXLex lex(0,0);
1389                         lex.setStream(ss);
1390                         int const unknown_tokens = defaults.readHeader(lex);
1391
1392                         if (unknown_tokens != 0) {
1393                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1394                                        << unknown_tokens << " unknown token"
1395                                        << (unknown_tokens == 1 ? "" : "s")
1396                                        << endl;
1397                         }
1398
1399                         if (defaults.writeFile(defaults.fileName()))
1400                                 setMessage(_("Document defaults saved in ")
1401                                            + MakeDisplayPath(fname));
1402                         else
1403                                 setErrorMessage(_("Unable to save document defaults"));
1404                         break;
1405                 }
1406
1407                 case LFUN_BUFFERPARAMS_APPLY: {
1408                         biblio::CiteEngine const engine =
1409                                 owner->buffer()->params().cite_engine;
1410
1411                         istringstream ss(argument);
1412                         LyXLex lex(0,0);
1413                         lex.setStream(ss);
1414                         int const unknown_tokens =
1415                                 owner->buffer()->readHeader(lex);
1416
1417                         if (unknown_tokens != 0) {
1418                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1419                                        << unknown_tokens << " unknown token"
1420                                        << (unknown_tokens == 1 ? "" : "s")
1421                                        << endl;
1422                         }
1423                         if (engine == owner->buffer()->params().cite_engine)
1424                                 break;
1425
1426                         LCursor & cur = view()->cursor();
1427                         FuncRequest fr(LFUN_INSET_REFRESH);
1428
1429                         InsetBase & inset = owner->buffer()->inset();
1430                         InsetIterator it  = inset_iterator_begin(inset);
1431                         InsetIterator const end = inset_iterator_end(inset);
1432                         for (; it != end; ++it)
1433                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1434                                         it->dispatch(cur, fr);
1435                         break;
1436                 }
1437
1438                 case LFUN_TEXTCLASS_APPLY: {
1439                         Buffer * buffer = owner->buffer();
1440
1441                         lyx::textclass_type const old_class =
1442                                 buffer->params().textclass;
1443
1444                         loadTextclass(argument);
1445
1446                         std::pair<bool, lyx::textclass_type> const tc_pair =
1447                                 textclasslist.NumberOfClass(argument);
1448
1449                         if (!tc_pair.first)
1450                                 break;
1451
1452                         lyx::textclass_type const new_class = tc_pair.second;
1453                         if (old_class == new_class)
1454                                 // nothing to do
1455                                 break;
1456
1457                         owner->message(_("Converting document to new document class..."));
1458                         ErrorList el;
1459                         lyx::cap::SwitchLayoutsBetweenClasses(
1460                                 old_class, new_class,
1461                                 buffer->paragraphs(), el);
1462
1463                         bufferErrors(*buffer, el);
1464                         view()->showErrorList(_("Class switch"));
1465                         break;
1466                 }
1467
1468                 case LFUN_TEXTCLASS_LOAD:
1469                         loadTextclass(argument);
1470                         break;
1471
1472                 case LFUN_LYXRC_APPLY: {
1473                         LyXRC const lyxrc_orig = lyxrc;
1474
1475                         istringstream ss(argument);
1476                         bool const success = lyxrc.read(ss) == 0;
1477
1478                         if (!success) {
1479                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1480                                        << "Unable to read lyxrc data"
1481                                        << endl;
1482                                 break;
1483                         }
1484
1485                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1486                         break;
1487                 }
1488
1489                 default: {
1490                         view()->cursor().dispatch(cmd);
1491                         update |= view()->cursor().result().update();
1492                         if (!view()->cursor().result().dispatched()) {
1493                                 update |= view()->dispatch(cmd);
1494                         }
1495
1496                         break;
1497                 }
1498                 }
1499
1500                 if (view()->available()) {
1501                         // Redraw screen unless explicitly told otherwise.
1502                         // This also initializes the position cache for all insets
1503                         // in (at least partially) visible top-level paragraphs.
1504                         view()->update(true, update);
1505
1506                         // if we executed a mutating lfun, mark the buffer as dirty
1507                         if (getStatus(cmd).enabled()
1508                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1509                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1510                                 view()->buffer()->markDirty();
1511                 }
1512
1513                 if (view()->cursor().inTexted()) {
1514                         view()->owner()->updateLayoutChoice();
1515                 }
1516         }
1517         sendDispatchMessage(getMessage(), cmd);
1518 }
1519
1520
1521 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1522 {
1523         owner->updateMenubar();
1524         owner->updateToolbars();
1525
1526         const bool verbose = (cmd.origin == FuncRequest::UI
1527                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1528
1529         if (cmd.action == LFUN_SELFINSERT || !verbose) {
1530                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1531                 if (!msg.empty())
1532                         owner->message(msg);
1533                 return;
1534         }
1535
1536         string dispatch_msg = msg;
1537         if (!dispatch_msg.empty())
1538                 dispatch_msg += ' ';
1539
1540         string comname = lyxaction.getActionName(cmd.action);
1541
1542         bool argsadded = false;
1543
1544         if (!cmd.argument.empty()) {
1545                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1546                         comname += ' ' + cmd.argument;
1547                         argsadded = true;
1548                 }
1549         }
1550
1551         string const shortcuts = toplevel_keymap->printbindings(cmd);
1552
1553         if (!shortcuts.empty()) {
1554                 comname += ": " + shortcuts;
1555         } else if (!argsadded && !cmd.argument.empty()) {
1556                 comname += ' ' + cmd.argument;
1557         }
1558
1559         if (!comname.empty()) {
1560                 comname = rtrim(comname);
1561                 dispatch_msg += '(' + comname + ')';
1562         }
1563
1564         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1565         if (!dispatch_msg.empty())
1566                 owner->message(dispatch_msg);
1567 }
1568
1569
1570 void LyXFunc::setupLocalKeymap()
1571 {
1572         keyseq.stdmap = toplevel_keymap.get();
1573         keyseq.curmap = toplevel_keymap.get();
1574         cancel_meta_seq.stdmap = toplevel_keymap.get();
1575         cancel_meta_seq.curmap = toplevel_keymap.get();
1576 }
1577
1578
1579 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1580 {
1581         string initpath = lyxrc.document_path;
1582         string filename(name);
1583
1584         if (view()->available()) {
1585                 string const trypath = owner->buffer()->filePath();
1586                 // If directory is writeable, use this as default.
1587                 if (IsDirWriteable(trypath))
1588                         initpath = trypath;
1589         }
1590
1591         static int newfile_number;
1592
1593         if (filename.empty()) {
1594                 filename = AddName(lyxrc.document_path,
1595                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1596                 while (bufferlist.exists(filename) || fs::is_readable(filename)) {
1597                         ++newfile_number;
1598                         filename = AddName(lyxrc.document_path,
1599                                            "newfile" +  convert<string>(newfile_number) +
1600                                     ".lyx");
1601                 }
1602         }
1603
1604         // The template stuff
1605         string templname;
1606         if (fromTemplate) {
1607                 FileDialog fileDlg(_("Select template file"),
1608                         LFUN_SELECT_FILE_SYNC,
1609                         make_pair(string(_("Documents|#o#O")),
1610                                   string(lyxrc.document_path)),
1611                         make_pair(string(_("Templates|#T#t")),
1612                                   string(lyxrc.template_path)));
1613
1614                 FileDialog::Result result =
1615                         fileDlg.open(lyxrc.template_path,
1616                                      FileFilterList(_("LyX Documents (*.lyx)")),
1617                                      string());
1618
1619                 if (result.first == FileDialog::Later)
1620                         return;
1621                 if (result.second.empty())
1622                         return;
1623                 templname = result.second;
1624         }
1625
1626         view()->newFile(filename, templname, !name.empty());
1627 }
1628
1629
1630 void LyXFunc::open(string const & fname)
1631 {
1632         string initpath = lyxrc.document_path;
1633
1634         if (view()->available()) {
1635                 string const trypath = owner->buffer()->filePath();
1636                 // If directory is writeable, use this as default.
1637                 if (IsDirWriteable(trypath))
1638                         initpath = trypath;
1639         }
1640
1641         string filename;
1642
1643         if (fname.empty()) {
1644                 FileDialog fileDlg(_("Select document to open"),
1645                         LFUN_FILE_OPEN,
1646                         make_pair(string(_("Documents|#o#O")),
1647                                   string(lyxrc.document_path)),
1648                         make_pair(string(_("Examples|#E#e")),
1649                                   string(AddPath(package().system_support(), "examples"))));
1650
1651                 FileDialog::Result result =
1652                         fileDlg.open(initpath,
1653                                      FileFilterList(_("LyX Documents (*.lyx)")),
1654                                      string());
1655
1656                 if (result.first == FileDialog::Later)
1657                         return;
1658
1659                 filename = result.second;
1660
1661                 // check selected filename
1662                 if (filename.empty()) {
1663                         owner->message(_("Canceled."));
1664                         return;
1665                 }
1666         } else
1667                 filename = fname;
1668
1669         // get absolute path of file and add ".lyx" to the filename if
1670         // necessary
1671         string const fullpath = FileSearch(string(), filename, "lyx");
1672         if (!fullpath.empty()) {
1673                 filename = fullpath;
1674         }
1675
1676         string const disp_fn(MakeDisplayPath(filename));
1677
1678         // if the file doesn't exist, let the user create one
1679         if (!fs::exists(filename)) {
1680                 // the user specifically chose this name. Believe them.
1681                 view()->newFile(filename, "", true);
1682                 return;
1683         }
1684
1685         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1686
1687         string str2;
1688         if (view()->loadLyXFile(filename)) {
1689                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1690         } else {
1691                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1692         }
1693         owner->message(str2);
1694 }
1695
1696
1697 void LyXFunc::doImport(string const & argument)
1698 {
1699         string format;
1700         string filename = split(argument, format, ' ');
1701
1702         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1703                             << " file: " << filename << endl;
1704
1705         // need user interaction
1706         if (filename.empty()) {
1707                 string initpath = lyxrc.document_path;
1708
1709                 if (view()->available()) {
1710                         string const trypath = owner->buffer()->filePath();
1711                         // If directory is writeable, use this as default.
1712                         if (IsDirWriteable(trypath))
1713                                 initpath = trypath;
1714                 }
1715
1716                 string const text = bformat(_("Select %1$s file to import"),
1717                         formats.prettyName(format));
1718
1719                 FileDialog fileDlg(text,
1720                         LFUN_IMPORT,
1721                         make_pair(string(_("Documents|#o#O")),
1722                                   string(lyxrc.document_path)),
1723                         make_pair(string(_("Examples|#E#e")),
1724                                   string(AddPath(package().system_support(), "examples"))));
1725
1726                 string const filter = formats.prettyName(format)
1727                         + " (*." + formats.extension(format) + ')';
1728
1729                 FileDialog::Result result =
1730                         fileDlg.open(initpath,
1731                                      FileFilterList(filter),
1732                                      string());
1733
1734                 if (result.first == FileDialog::Later)
1735                         return;
1736
1737                 filename = result.second;
1738
1739                 // check selected filename
1740                 if (filename.empty())
1741                         owner->message(_("Canceled."));
1742         }
1743
1744         if (filename.empty())
1745                 return;
1746
1747         // get absolute path of file
1748         filename = MakeAbsPath(filename);
1749
1750         string const lyxfile = ChangeExtension(filename, ".lyx");
1751
1752         // Check if the document already is open
1753         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1754                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1755                         owner->message(_("Canceled."));
1756                         return;
1757                 }
1758         }
1759
1760         // if the file exists already, and we didn't do
1761         // -i lyx thefile.lyx, warn
1762         if (fs::exists(lyxfile) && filename != lyxfile) {
1763                 string const file = MakeDisplayPath(lyxfile, 30);
1764
1765                 string text = bformat(_("The document %1$s already exists.\n\n"
1766                         "Do you want to over-write that document?"), file);
1767                 int const ret = Alert::prompt(_("Over-write document?"),
1768                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1769
1770                 if (ret == 1) {
1771                         owner->message(_("Canceled."));
1772                         return;
1773                 }
1774         }
1775
1776         Importer::Import(owner, filename, format);
1777 }
1778
1779
1780 void LyXFunc::closeBuffer()
1781 {
1782         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1783                 if (bufferlist.empty()) {
1784                         // need this otherwise SEGV may occur while
1785                         // trying to set variables that don't exist
1786                         // since there's no current buffer
1787                         owner->getDialogs().hideBufferDependent();
1788                 } else {
1789                         view()->setBuffer(bufferlist.first());
1790                 }
1791         }
1792 }
1793
1794
1795 // Each "owner" should have it's own message method. lyxview and
1796 // the minibuffer would use the minibuffer, but lyxserver would
1797 // send an ERROR signal to its client.  Alejandro 970603
1798 // This function is bit problematic when it comes to NLS, to make the
1799 // lyx servers client be language indepenent we must not translate
1800 // strings sent to this func.
1801 void LyXFunc::setErrorMessage(string const & m) const
1802 {
1803         dispatch_buffer = m;
1804         errorstat = true;
1805 }
1806
1807
1808 void LyXFunc::setMessage(string const & m) const
1809 {
1810         dispatch_buffer = m;
1811 }
1812
1813
1814 string const LyXFunc::viewStatusMessage()
1815 {
1816         // When meta-fake key is pressed, show the key sequence so far + "M-".
1817         if (wasMetaKey())
1818                 return keyseq.print() + "M-";
1819
1820         // Else, when a non-complete key sequence is pressed,
1821         // show the available options.
1822         if (keyseq.length() > 0 && !keyseq.deleted())
1823                 return keyseq.printOptions();
1824
1825         if (!view()->available())
1826                 return _("Welcome to LyX!");
1827
1828         return view()->cursor().currentState();
1829 }
1830
1831
1832 BufferView * LyXFunc::view() const
1833 {
1834         BOOST_ASSERT(owner);
1835         return owner->view().get();
1836 }
1837
1838
1839 bool LyXFunc::wasMetaKey() const
1840 {
1841         return (meta_fake_bit != key_modifier::none);
1842 }
1843
1844
1845 namespace {
1846
1847 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
1848 {
1849         // Why the switch you might ask. It is a trick to ensure that all
1850         // the elements in the LyXRCTags enum is handled. As you can see
1851         // there are no breaks at all. So it is just a huge fall-through.
1852         // The nice thing is that we will get a warning from the compiler
1853         // if we forget an element.
1854         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
1855         switch (tag) {
1856         case LyXRC::RC_ACCEPT_COMPOUND:
1857         case LyXRC::RC_ALT_LANG:
1858         case LyXRC::RC_ASCIIROFF_COMMAND:
1859         case LyXRC::RC_ASCII_LINELEN:
1860         case LyXRC::RC_AUTOREGIONDELETE:
1861         case LyXRC::RC_AUTORESET_OPTIONS:
1862         case LyXRC::RC_AUTOSAVE:
1863         case LyXRC::RC_AUTO_NUMBER:
1864         case LyXRC::RC_BACKUPDIR_PATH:
1865         case LyXRC::RC_BIBTEX_COMMAND:
1866         case LyXRC::RC_BINDFILE:
1867         case LyXRC::RC_CHECKLASTFILES:
1868         case LyXRC::RC_CHKTEX_COMMAND:
1869         case LyXRC::RC_CONVERTER:
1870         case LyXRC::RC_COPIER:
1871         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1872         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
1873         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
1874         case LyXRC::RC_CYGWIN_PATH_FIX:
1875                 if (lyxrc_orig.cygwin_path_fix != lyxrc_new.cygwin_path_fix) {
1876                         namespace os = lyx::support::os;
1877                         os::cygwin_path_fix(lyxrc_new.cygwin_path_fix);
1878                 }
1879         case LyXRC::RC_DATE_INSERT_FORMAT:
1880         case LyXRC::RC_DEFAULT_LANGUAGE:
1881         case LyXRC::RC_DEFAULT_PAPERSIZE:
1882         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1883         case LyXRC::RC_DISPLAY_GRAPHICS:
1884         case LyXRC::RC_DOCUMENTPATH:
1885                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1886                         if (fs::exists(lyxrc_new.document_path) &&
1887                             fs::is_directory(lyxrc_new.document_path)) {
1888                                 using lyx::support::package;
1889                                 package().document_dir() = lyxrc.document_path;
1890                         }
1891                 }
1892         case LyXRC::RC_ESC_CHARS:
1893         case LyXRC::RC_FONT_ENCODING:
1894         case LyXRC::RC_FORMAT:
1895         case LyXRC::RC_INDEX_COMMAND:
1896         case LyXRC::RC_INPUT:
1897         case LyXRC::RC_KBMAP:
1898         case LyXRC::RC_KBMAP_PRIMARY:
1899         case LyXRC::RC_KBMAP_SECONDARY:
1900         case LyXRC::RC_LABEL_INIT_LENGTH:
1901         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1902         case LyXRC::RC_LANGUAGE_AUTO_END:
1903         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1904         case LyXRC::RC_LANGUAGE_COMMAND_END:
1905         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1906         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1907         case LyXRC::RC_LANGUAGE_PACKAGE:
1908         case LyXRC::RC_LANGUAGE_USE_BABEL:
1909         case LyXRC::RC_LASTFILES:
1910         case LyXRC::RC_MAKE_BACKUP:
1911         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1912         case LyXRC::RC_NUMLASTFILES:
1913         case LyXRC::RC_PATH_PREFIX:
1914                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1915                         using lyx::support::prependEnvPath;
1916                         prependEnvPath("PATH", lyxrc.path_prefix);
1917                 }
1918         case LyXRC::RC_PERS_DICT:
1919         case LyXRC::RC_POPUP_BOLD_FONT:
1920         case LyXRC::RC_POPUP_FONT_ENCODING:
1921         case LyXRC::RC_POPUP_NORMAL_FONT:
1922         case LyXRC::RC_PREVIEW:
1923         case LyXRC::RC_PREVIEW_HASHED_LABELS:
1924         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1925         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1926         case LyXRC::RC_PRINTCOPIESFLAG:
1927         case LyXRC::RC_PRINTER:
1928         case LyXRC::RC_PRINTEVENPAGEFLAG:
1929         case LyXRC::RC_PRINTEXSTRAOPTIONS:
1930         case LyXRC::RC_PRINTFILEEXTENSION:
1931         case LyXRC::RC_PRINTLANDSCAPEFLAG:
1932         case LyXRC::RC_PRINTODDPAGEFLAG:
1933         case LyXRC::RC_PRINTPAGERANGEFLAG:
1934         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1935         case LyXRC::RC_PRINTPAPERFLAG:
1936         case LyXRC::RC_PRINTREVERSEFLAG:
1937         case LyXRC::RC_PRINTSPOOL_COMMAND:
1938         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1939         case LyXRC::RC_PRINTTOFILE:
1940         case LyXRC::RC_PRINTTOPRINTER:
1941         case LyXRC::RC_PRINT_ADAPTOUTPUT:
1942         case LyXRC::RC_PRINT_COMMAND:
1943         case LyXRC::RC_RTL_SUPPORT:
1944         case LyXRC::RC_SCREEN_DPI:
1945         case LyXRC::RC_SCREEN_FONT_ENCODING:
1946         case LyXRC::RC_SCREEN_FONT_ROMAN:
1947         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1948         case LyXRC::RC_SCREEN_FONT_SANS:
1949         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1950         case LyXRC::RC_SCREEN_FONT_SCALABLE:
1951         case LyXRC::RC_SCREEN_FONT_SIZES:
1952         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1953         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1954         case LyXRC::RC_SCREEN_ZOOM:
1955         case LyXRC::RC_SERVERPIPE:
1956         case LyXRC::RC_SET_COLOR:
1957         case LyXRC::RC_SHOW_BANNER:
1958         case LyXRC::RC_SPELL_COMMAND:
1959         case LyXRC::RC_TEMPDIRPATH:
1960         case LyXRC::RC_TEMPLATEPATH:
1961         case LyXRC::RC_UIFILE:
1962         case LyXRC::RC_USER_EMAIL:
1963         case LyXRC::RC_USER_NAME:
1964         case LyXRC::RC_USETEMPDIR:
1965         case LyXRC::RC_USE_ALT_LANG:
1966         case LyXRC::RC_USE_ESC_CHARS:
1967         case LyXRC::RC_USE_INP_ENC:
1968         case LyXRC::RC_USE_PERS_DICT:
1969         case LyXRC::RC_USE_SPELL_LIB:
1970         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1971         case LyXRC::RC_VIEWER:
1972         case LyXRC::RC_WHEEL_JUMP:
1973         case LyXRC::RC_LAST:
1974                 break;
1975         }
1976 }
1977
1978 } // namespace anon