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