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