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