]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
* indentation corrected
[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_STATE: {
621                 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
622                 if (!(flags & ToolbarInfo::AUTO))
623                         flag.setOnOff(flags & ToolbarInfo::ON);
624                 break;
625         }
626
627         case LFUN_TOOLBAR_TOGGLE: {
628                 bool const current = lyx_view_->getToolbars().visible(cmd.getArg(0));
629                 flag.setOnOff(current);
630                 break;
631         }
632         
633         case LFUN_WINDOW_CLOSE: {
634                 enable = (theApp()->gui().viewIds().size() > 1);
635                 break;
636         }
637
638         // this one is difficult to get right. As a half-baked
639         // solution, we consider only the first action of the sequence
640         case LFUN_COMMAND_SEQUENCE: {
641                 // argument contains ';'-terminated commands
642                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
643                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
644                 func.origin = cmd.origin;
645                 flag = getStatus(func);
646         }
647
648         case LFUN_BUFFER_NEW:
649         case LFUN_BUFFER_NEW_TEMPLATE:
650         case LFUN_WORD_FIND_FORWARD:
651         case LFUN_WORD_FIND_BACKWARD:
652         case LFUN_COMMAND_PREFIX:
653         case LFUN_COMMAND_EXECUTE:
654         case LFUN_CANCEL:
655         case LFUN_META_PREFIX:
656         case LFUN_BUFFER_CLOSE:
657         case LFUN_BUFFER_WRITE_AS:
658         case LFUN_BUFFER_UPDATE:
659         case LFUN_BUFFER_VIEW:
660         case LFUN_BUFFER_IMPORT:
661         case LFUN_BUFFER_AUTO_SAVE:
662         case LFUN_RECONFIGURE:
663         case LFUN_HELP_OPEN:
664         case LFUN_FILE_NEW:
665         case LFUN_FILE_OPEN:
666         case LFUN_DROP_LAYOUTS_CHOICE:
667         case LFUN_MENU_OPEN:
668         case LFUN_SERVER_GET_NAME:
669         case LFUN_SERVER_NOTIFY:
670         case LFUN_SERVER_GOTO_FILE_ROW:
671         case LFUN_DIALOG_HIDE:
672         case LFUN_DIALOG_DISCONNECT_INSET:
673         case LFUN_BUFFER_CHILD_OPEN:
674         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
675         case LFUN_KEYMAP_OFF:
676         case LFUN_KEYMAP_PRIMARY:
677         case LFUN_KEYMAP_SECONDARY:
678         case LFUN_KEYMAP_TOGGLE:
679         case LFUN_REPEAT:
680         case LFUN_BUFFER_EXPORT_CUSTOM:
681         case LFUN_BUFFER_PRINT:
682         case LFUN_PREFERENCES_SAVE:
683         case LFUN_SCREEN_FONT_UPDATE:
684         case LFUN_SET_COLOR:
685         case LFUN_MESSAGE:
686         case LFUN_EXTERNAL_EDIT:
687         case LFUN_GRAPHICS_EDIT:
688         case LFUN_ALL_INSETS_TOGGLE:
689         case LFUN_BUFFER_LANGUAGE:
690         case LFUN_TEXTCLASS_APPLY:
691         case LFUN_TEXTCLASS_LOAD:
692         case LFUN_BUFFER_SAVE_AS_DEFAULT:
693         case LFUN_BUFFER_PARAMS_APPLY:
694         case LFUN_LYXRC_APPLY:
695         case LFUN_BUFFER_NEXT:
696         case LFUN_BUFFER_PREVIOUS:
697         case LFUN_WINDOW_NEW:
698         case LFUN_LYX_QUIT:
699                 // these are handled in our dispatch()
700                 break;
701
702         default:
703                 if (!getLocalStatus(cur, cmd, flag))
704                         flag = view()->getStatus(cmd);
705         }
706
707         if (!enable)
708                 flag.enabled(false);
709
710         // Can we use a readonly buffer?
711         if (buf && buf->isReadonly()
712             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
713             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
714                 flag.message(from_utf8(N_("Document is read-only")));
715                 flag.enabled(false);
716         }
717
718         // Are we in a DELETED change-tracking region?
719         if (buf && lookupChangeType(cur, true) == Change::DELETED
720             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
721             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
722                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
723                 flag.enabled(false);
724         }
725
726         // the default error message if we disable the command
727         if (!flag.enabled() && flag.message().empty())
728                 flag.message(from_utf8(N_("Command disabled")));
729
730         return flag;
731 }
732
733
734 bool LyXFunc::ensureBufferClean(BufferView * bv)
735 {
736         Buffer & buf = *bv->buffer();
737         if (buf.isClean())
738                 return true;
739
740         docstring const file = makeDisplayPath(buf.fileName(), 30);
741         docstring text = bformat(_("The document %1$s has unsaved "
742                                              "changes.\n\nDo you want to save "
743                                              "the document?"), file);
744         int const ret = Alert::prompt(_("Save changed document?"),
745                                       text, 0, 1, _("&Save"),
746                                       _("&Cancel"));
747
748         if (ret == 0)
749                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
750
751         return buf.isClean();
752 }
753
754
755 namespace {
756
757 void showPrintError(string const & name)
758 {
759         docstring str = bformat(_("Could not print the document %1$s.\n"
760                                             "Check that your printer is set up correctly."),
761                              makeDisplayPath(name, 50));
762         Alert::error(_("Print document failed"), str);
763 }
764
765
766 void loadTextclass(string const & name)
767 {
768         std::pair<bool, textclass_type> const tc_pair =
769                 textclasslist.numberOfClass(name);
770
771         if (!tc_pair.first) {
772                 lyxerr << "Document class \"" << name
773                        << "\" does not exist."
774                        << std::endl;
775                 return;
776         }
777
778         textclass_type const tc = tc_pair.second;
779
780         if (!textclasslist[tc].load()) {
781                 docstring s = bformat(_("The document could not be converted\n"
782                                                   "into the document class %1$s."),
783                                    from_utf8(textclasslist[tc].name()));
784                 Alert::error(_("Could not change class"), s);
785         }
786 }
787
788
789 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
790
791 } //namespace anon
792
793
794 void LyXFunc::dispatch(FuncRequest const & cmd)
795 {
796         string const argument = to_utf8(cmd.argument());
797         kb_action const action = cmd.action;
798
799         LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
800         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
801
802         // we have not done anything wrong yet.
803         errorstat = false;
804         dispatch_buffer.erase();
805
806         // redraw the screen at the end (first of the two drawing steps).
807         //This is done unless explicitely requested otherwise
808         Update::flags updateFlags = Update::FitCursor;
809
810         FuncStatus const flag = getStatus(cmd);
811         if (!flag.enabled()) {
812                 // We cannot use this function here
813                 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
814                        << lyxaction.getActionName(action)
815                        << " [" << action << "] is disabled at this location"
816                        << endl;
817                 setErrorMessage(flag.message());
818         } else {
819                 switch (action) {
820
821                 case LFUN_WORD_FIND_FORWARD:
822                 case LFUN_WORD_FIND_BACKWARD: {
823                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
824                         static docstring last_search;
825                         docstring searched_string;
826
827                         if (!cmd.argument().empty()) {
828                                 last_search = cmd.argument();
829                                 searched_string = cmd.argument();
830                         } else {
831                                 searched_string = last_search;
832                         }
833
834                         if (searched_string.empty())
835                                 break;
836
837                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
838                         docstring const data =
839                                 find2string(searched_string, true, false, fw);
840                         find(view(), FuncRequest(LFUN_WORD_FIND, data));
841                         break;
842                 }
843
844                 case LFUN_COMMAND_PREFIX:
845                         BOOST_ASSERT(lyx_view_);
846                         lyx_view_->message(keyseq->printOptions(true));
847                         break;
848
849                 case LFUN_COMMAND_EXECUTE:
850                         BOOST_ASSERT(lyx_view_);
851                         lyx_view_->getToolbars().display("minibuffer", true);
852                         lyx_view_->focus_command_buffer();
853                         break;
854
855                 case LFUN_CANCEL:
856                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
857                         keyseq->reset();
858                         meta_fake_bit = key_modifier::none;
859                         if (view()->buffer())
860                                 // cancel any selection
861                                 dispatch(FuncRequest(LFUN_MARK_OFF));
862                         setMessage(from_ascii(N_("Cancel")));
863                         break;
864
865                 case LFUN_META_PREFIX:
866                         meta_fake_bit = key_modifier::alt;
867                         setMessage(keyseq->print(true));
868                         break;
869
870                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
871                         BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
872                         if (lyx_view_->buffer()->lyxvc().inUse())
873                                 lyx_view_->buffer()->lyxvc().toggleReadOnly();
874                         else
875                                 lyx_view_->buffer()->setReadonly(
876                                         !lyx_view_->buffer()->isReadonly());
877                         break;
878
879                 // --- Menus -----------------------------------------------
880                 case LFUN_BUFFER_NEW:
881                         menuNew(argument, false);
882                         break;
883
884                 case LFUN_BUFFER_NEW_TEMPLATE:
885                         menuNew(argument, true);
886                         break;
887
888                 case LFUN_BUFFER_CLOSE:
889                         closeBuffer();
890                         view()->update();
891                         break;
892
893                 case LFUN_BUFFER_WRITE:
894                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
895                         if (!lyx_view_->buffer()->isUnnamed()) {
896                                 docstring const str = bformat(_("Saving document %1$s..."),
897                                          makeDisplayPath(lyx_view_->buffer()->fileName()));
898                                 lyx_view_->message(str);
899                                 menuWrite(lyx_view_->buffer());
900                                 lyx_view_->message(str + _(" done."));
901                         } else {
902                                 writeAs(lyx_view_->buffer());
903                         }
904                         updateFlags = Update::None;
905                         break;
906
907                 case LFUN_BUFFER_WRITE_AS:
908                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
909                         writeAs(lyx_view_->buffer(), argument);
910                         updateFlags = Update::None;
911                         break;
912
913                 case LFUN_BUFFER_RELOAD: {
914                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
915                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
916                         docstring text = bformat(_("Any changes will be lost. Are you sure "
917                                                              "you want to revert to the saved version of the document %1$s?"), file);
918                         int const ret = Alert::prompt(_("Revert to saved document?"),
919                                 text, 1, 1, _("&Revert"), _("&Cancel"));
920
921                         if (ret == 0)
922                                 reloadBuffer();
923                         break;
924                 }
925
926                 case LFUN_BUFFER_UPDATE:
927                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
928                         Exporter::Export(lyx_view_->buffer(), argument, true);
929                         break;
930
931                 case LFUN_BUFFER_VIEW:
932                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
933                         Exporter::preview(lyx_view_->buffer(), argument);
934                         break;
935
936                 case LFUN_BUILD_PROGRAM:
937                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
938                         Exporter::Export(lyx_view_->buffer(), "program", true);
939                         break;
940
941                 case LFUN_BUFFER_CHKTEX:
942                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
943                         lyx_view_->buffer()->runChktex();
944                         break;
945
946                 case LFUN_BUFFER_EXPORT:
947                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
948                         if (argument == "custom")
949                                 lyx_view_->getDialogs().show("sendto");
950                         else {
951                                 Exporter::Export(lyx_view_->buffer(), argument, false);
952                         }
953                         break;
954
955                 case LFUN_BUFFER_EXPORT_CUSTOM: {
956                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
957                         string format_name;
958                         string command = split(argument, format_name, ' ');
959                         Format const * format = formats.getFormat(format_name);
960                         if (!format) {
961                                 lyxerr << "Format \"" << format_name
962                                        << "\" not recognized!"
963                                        << std::endl;
964                                 break;
965                         }
966
967                         Buffer * buffer = lyx_view_->buffer();
968
969                         // The name of the file created by the conversion process
970                         string filename;
971
972                         // Output to filename
973                         if (format->name() == "lyx") {
974                                 string const latexname =
975                                         buffer->getLatexName(false);
976                                 filename = changeExtension(latexname,
977                                                            format->extension());
978                                 filename = addName(buffer->temppath(), filename);
979
980                                 if (!buffer->writeFile(FileName(filename)))
981                                         break;
982
983                         } else {
984                                 Exporter::Export(buffer, format_name, true, filename);
985                         }
986
987                         // Substitute $$FName for filename
988                         if (!contains(command, "$$FName"))
989                                 command = "( " + command + " ) < $$FName";
990                         command = subst(command, "$$FName", filename);
991
992                         // Execute the command in the background
993                         Systemcall call;
994                         call.startscript(Systemcall::DontWait, command);
995                         break;
996                 }
997
998                 case LFUN_BUFFER_PRINT: {
999                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1000                         string target;
1001                         string target_name;
1002                         string command = split(split(argument, target, ' '),
1003                                                target_name, ' ');
1004
1005                         if (target.empty()
1006                             || target_name.empty()
1007                             || command.empty()) {
1008                                 lyxerr << "Unable to parse \""
1009                                        << argument << '"' << std::endl;
1010                                 break;
1011                         }
1012                         if (target != "printer" && target != "file") {
1013                                 lyxerr << "Unrecognized target \""
1014                                        << target << '"' << std::endl;
1015                                 break;
1016                         }
1017
1018                         Buffer * buffer = lyx_view_->buffer();
1019
1020                         if (!Exporter::Export(buffer, "dvi", true)) {
1021                                 showPrintError(buffer->fileName());
1022                                 break;
1023                         }
1024
1025                         // Push directory path.
1026                         string const path(buffer->temppath());
1027                         // Prevent the compiler from optimizing away p
1028                         FileName pp(path);
1029                         support::Path p(pp);
1030
1031                         // there are three cases here:
1032                         // 1. we print to a file
1033                         // 2. we print directly to a printer
1034                         // 3. we print using a spool command (print to file first)
1035                         Systemcall one;
1036                         int res = 0;
1037                         string const dviname =
1038                                 changeExtension(buffer->getLatexName(true),
1039                                                 "dvi");
1040
1041                         if (target == "printer") {
1042                                 if (!lyxrc.print_spool_command.empty()) {
1043                                         // case 3: print using a spool
1044                                         string const psname =
1045                                                 changeExtension(dviname,".ps");
1046                                         command += lyxrc.print_to_file
1047                                                 + quoteName(psname)
1048                                                 + ' '
1049                                                 + quoteName(dviname);
1050
1051                                         string command2 =
1052                                                 lyxrc.print_spool_command +' ';
1053                                         if (target_name != "default") {
1054                                                 command2 += lyxrc.print_spool_printerprefix
1055                                                         + target_name
1056                                                         + ' ';
1057                                         }
1058                                         command2 += quoteName(psname);
1059                                         // First run dvips.
1060                                         // If successful, then spool command
1061                                         res = one.startscript(
1062                                                 Systemcall::Wait,
1063                                                 command);
1064
1065                                         if (res == 0)
1066                                                 res = one.startscript(
1067                                                         Systemcall::DontWait,
1068                                                         command2);
1069                                 } else {
1070                                         // case 2: print directly to a printer
1071                                         res = one.startscript(
1072                                                 Systemcall::DontWait,
1073                                                 command + quoteName(dviname));
1074                                 }
1075
1076                         } else {
1077                                 // case 1: print to a file
1078                                 FileName const filename(makeAbsPath(target_name, path));
1079                                 if (fs::exists(filename.toFilesystemEncoding())) {
1080                                         docstring text = bformat(
1081                                                 _("The file %1$s already exists.\n\n"
1082                                                   "Do you want to over-write that file?"),
1083                                                 makeDisplayPath(filename.absFilename()));
1084                                         if (Alert::prompt(_("Over-write file?"),
1085                                             text, 0, 1, _("&Over-write"), _("&Cancel")) != 0)
1086                                                 break;
1087                                 }
1088                                 command += lyxrc.print_to_file
1089                                         + quoteName(filename.toFilesystemEncoding())
1090                                         + ' '
1091                                         + quoteName(dviname);
1092                                 res = one.startscript(Systemcall::DontWait,
1093                                                       command);
1094                         }
1095
1096                         if (res != 0)
1097                                 showPrintError(buffer->fileName());
1098                         break;
1099                 }
1100
1101                 case LFUN_BUFFER_IMPORT:
1102                         doImport(argument);
1103                         break;
1104
1105                 case LFUN_LYX_QUIT:
1106                         // quitting is triggered by the gui code
1107                         // (leaving the event loop).
1108                         lyx_view_->message(from_utf8(N_("Exiting.")));
1109                         if (theBufferList().quitWriteAll())
1110                                 theApp()->gui().closeAllViews();
1111                         break;
1112
1113                 case LFUN_BUFFER_AUTO_SAVE:
1114                         autoSave(view());
1115                         break;
1116
1117                 case LFUN_RECONFIGURE:
1118                         BOOST_ASSERT(lyx_view_);
1119                         reconfigure(*lyx_view_);
1120                         break;
1121
1122                 case LFUN_HELP_OPEN: {
1123                         BOOST_ASSERT(lyx_view_);
1124                         string const arg = argument;
1125                         if (arg.empty()) {
1126                                 setErrorMessage(from_ascii(N_("Missing argument")));
1127                                 break;
1128                         }
1129                         FileName const fname = i18nLibFileSearch("doc", arg, "lyx");
1130                         if (fname.empty()) {
1131                                 lyxerr << "LyX: unable to find documentation file `"
1132                                                          << arg << "'. Bad installation?" << endl;
1133                                 break;
1134                         }
1135                         lyx_view_->message(bformat(_("Opening help file %1$s..."),
1136                                 makeDisplayPath(fname.absFilename())));
1137                         lyx_view_->loadLyXFile(fname, false);
1138                         break;
1139                 }
1140
1141                 // --- version control -------------------------------
1142                 case LFUN_VC_REGISTER:
1143                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1144                         if (!ensureBufferClean(view()))
1145                                 break;
1146                         if (!lyx_view_->buffer()->lyxvc().inUse()) {
1147                                 lyx_view_->buffer()->lyxvc().registrer();
1148                                 reloadBuffer();
1149                         }
1150                         updateFlags = Update::Force;
1151                         break;
1152
1153                 case LFUN_VC_CHECK_IN:
1154                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1155                         if (!ensureBufferClean(view()))
1156                                 break;
1157                         if (lyx_view_->buffer()->lyxvc().inUse()
1158                                         && !lyx_view_->buffer()->isReadonly()) {
1159                                 lyx_view_->buffer()->lyxvc().checkIn();
1160                                 reloadBuffer();
1161                         }
1162                         break;
1163
1164                 case LFUN_VC_CHECK_OUT:
1165                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1166                         if (!ensureBufferClean(view()))
1167                                 break;
1168                         if (lyx_view_->buffer()->lyxvc().inUse()
1169                                         && lyx_view_->buffer()->isReadonly()) {
1170                                 lyx_view_->buffer()->lyxvc().checkOut();
1171                                 reloadBuffer();
1172                         }
1173                         break;
1174
1175                 case LFUN_VC_REVERT:
1176                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1177                         lyx_view_->buffer()->lyxvc().revert();
1178                         reloadBuffer();
1179                         break;
1180
1181                 case LFUN_VC_UNDO_LAST:
1182                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1183                         lyx_view_->buffer()->lyxvc().undoLast();
1184                         reloadBuffer();
1185                         break;
1186
1187                 // --- buffers ----------------------------------------
1188                 case LFUN_BUFFER_SWITCH:
1189                         BOOST_ASSERT(lyx_view_);
1190                         lyx_view_->setBuffer(theBufferList().getBuffer(argument));
1191                         break;
1192
1193                 case LFUN_BUFFER_NEXT:
1194                         BOOST_ASSERT(lyx_view_);
1195                         lyx_view_->setBuffer(theBufferList().next(view()->buffer()));
1196                         break;
1197
1198                 case LFUN_BUFFER_PREVIOUS:
1199                         BOOST_ASSERT(lyx_view_);
1200                         lyx_view_->setBuffer(theBufferList().previous(view()->buffer()));
1201                         break;
1202
1203                 case LFUN_FILE_NEW:
1204                         BOOST_ASSERT(lyx_view_);
1205                         newFile(view(), argument);
1206                         break;
1207
1208                 case LFUN_FILE_OPEN:
1209                         BOOST_ASSERT(lyx_view_);
1210                         open(argument);
1211                         break;
1212
1213                 case LFUN_DROP_LAYOUTS_CHOICE:
1214                         BOOST_ASSERT(lyx_view_);
1215                         lyx_view_->getToolbars().openLayoutList();
1216                         break;
1217
1218                 case LFUN_MENU_OPEN:
1219                         BOOST_ASSERT(lyx_view_);
1220                         lyx_view_->getMenubar().openByName(from_utf8(argument));
1221                         break;
1222
1223                 // --- lyxserver commands ----------------------------
1224                 case LFUN_SERVER_GET_NAME:
1225                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
1226                         setMessage(from_utf8(lyx_view_->buffer()->fileName()));
1227                         LYXERR(Debug::INFO) << "FNAME["
1228                                                          << lyx_view_->buffer()->fileName()
1229                                                          << "] " << endl;
1230                         break;
1231
1232                 case LFUN_SERVER_NOTIFY:
1233                         dispatch_buffer = keyseq->print(false);
1234                         theServer().notifyClient(to_utf8(dispatch_buffer));
1235                         break;
1236
1237                 case LFUN_SERVER_GOTO_FILE_ROW: {
1238                         BOOST_ASSERT(lyx_view_);
1239                         string file_name;
1240                         int row;
1241                         istringstream is(argument);
1242                         is >> file_name >> row;
1243                         if (prefixIs(file_name, package().temp_dir().absFilename())) {
1244                                 // Needed by inverse dvi search. If it is a file
1245                                 // in tmpdir, call the apropriated function
1246                                 lyx_view_->setBuffer(theBufferList().getBufferFromTmp(file_name));
1247                         } else {
1248                                 // Must replace extension of the file to be .lyx
1249                                 // and get full path
1250                                 FileName const s = fileSearch(string(), changeExtension(file_name, ".lyx"), "lyx");
1251                                 // Either change buffer or load the file
1252                                 if (theBufferList().exists(s.absFilename())) {
1253                                         lyx_view_->setBuffer(theBufferList().getBuffer(s.absFilename()));
1254                                 } else {
1255                                         lyx_view_->loadLyXFile(s);
1256                                 }
1257                         }
1258
1259                         view()->setCursorFromRow(row);
1260
1261                         updateFlags = Update::FitCursor;
1262                         break;
1263                 }
1264
1265                 case LFUN_DIALOG_SHOW: {
1266                         BOOST_ASSERT(lyx_view_);
1267                         string const name = cmd.getArg(0);
1268                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1269
1270                         if (name == "character") {
1271                                 data = freefont2string();
1272                                 if (!data.empty())
1273                                         lyx_view_->getDialogs().show("character", data);
1274                         } else if (name == "latexlog") {
1275                                 pair<Buffer::LogType, string> const logfile =
1276                                         lyx_view_->buffer()->getLogName();
1277                                 switch (logfile.first) {
1278                                 case Buffer::latexlog:
1279                                         data = "latex ";
1280                                         break;
1281                                 case Buffer::buildlog:
1282                                         data = "literate ";
1283                                         break;
1284                                 }
1285                                 data += Lexer::quoteString(logfile.second);
1286                                 lyx_view_->getDialogs().show("log", data);
1287                         } else if (name == "vclog") {
1288                                 string const data = "vc " +
1289                                         Lexer::quoteString(lyx_view_->buffer()->lyxvc().getLogFile());
1290                                 lyx_view_->getDialogs().show("log", data);
1291                         } else
1292                                 lyx_view_->getDialogs().show(name, data);
1293                         break;
1294                 }
1295
1296                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1297                         BOOST_ASSERT(lyx_view_);
1298                         string const name = cmd.getArg(0);
1299                         string data = trim(to_utf8(cmd.argument()).substr(name.size()));
1300                         if (name == "bibitem" ||
1301                             name == "bibtex" ||
1302                             name == "index" ||
1303                             name == "label" ||
1304                             name == "nomenclature" ||
1305                             name == "ref" ||
1306                             name == "toc" ||
1307                             name == "url") {
1308                                 InsetCommandParams p(name);
1309                                 data = InsetCommandMailer::params2string(name, p);
1310                         } else if (name == "include") {
1311                                 // data is the include type: one of "include",
1312                                 // "input", "verbatiminput" or "verbatiminput*"
1313                                 if (data.empty())
1314                                         // default type is requested
1315                                         data = "include";
1316                                 InsetCommandParams p(data);
1317                                 data = InsetIncludeMailer::params2string(p);
1318                         } else if (name == "box") {
1319                                 // \c data == "Boxed" || "Frameless" etc
1320                                 InsetBoxParams p(data);
1321                                 data = InsetBoxMailer::params2string(p);
1322                         } else if (name == "branch") {
1323                                 InsetBranchParams p;
1324                                 data = InsetBranchMailer::params2string(p);
1325                         } else if (name == "citation") {
1326                                 InsetCommandParams p("cite");
1327                                 data = InsetCommandMailer::params2string(name, p);
1328                         } else if (name == "ert") {
1329                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1330                         } else if (name == "external") {
1331                                 InsetExternalParams p;
1332                                 Buffer const & buffer = *lyx_view_->buffer();
1333                                 data = InsetExternalMailer::params2string(p, buffer);
1334                         } else if (name == "float") {
1335                                 InsetFloatParams p;
1336                                 data = InsetFloatMailer::params2string(p);
1337                         } else if (name == "listings") {
1338                                 InsetListingsParams p;
1339                                 data = InsetListingsMailer::params2string(p);
1340                         } else if (name == "graphics") {
1341                                 InsetGraphicsParams p;
1342                                 Buffer const & buffer = *lyx_view_->buffer();
1343                                 data = InsetGraphicsMailer::params2string(p, buffer);
1344                         } else if (name == "note") {
1345                                 InsetNoteParams p;
1346                                 data = InsetNoteMailer::params2string(p);
1347                         } else if (name == "vspace") {
1348                                 VSpace space;
1349                                 data = InsetVSpaceMailer::params2string(space);
1350                         } else if (name == "wrap") {
1351                                 InsetWrapParams p;
1352                                 data = InsetWrapMailer::params2string(p);
1353                         }
1354                         lyx_view_->getDialogs().show(name, data, 0);
1355                         break;
1356                 }
1357
1358                 case LFUN_DIALOG_UPDATE: {
1359                         BOOST_ASSERT(lyx_view_);
1360                         string const & name = argument;
1361                         // Can only update a dialog connected to an existing inset
1362                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1363                         if (inset) {
1364                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument());
1365                                 inset->dispatch(view()->cursor(), fr);
1366                         } else if (name == "paragraph") {
1367                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1368                         } else if (name == "prefs") {
1369                                 lyx_view_->getDialogs().update(name, string());
1370                         }
1371                         break;
1372                 }
1373
1374                 case LFUN_DIALOG_HIDE:
1375                         Dialogs::hide(argument, 0);
1376                         break;
1377
1378                 case LFUN_DIALOG_TOGGLE: {
1379                         BOOST_ASSERT(lyx_view_);
1380                         if (lyx_view_->getDialogs().visible(cmd.getArg(0)))
1381                                 dispatch(FuncRequest(LFUN_DIALOG_HIDE, argument));
1382                         else
1383                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW, argument));
1384                         break;
1385                 }
1386
1387                 case LFUN_DIALOG_DISCONNECT_INSET:
1388                         BOOST_ASSERT(lyx_view_);
1389                         lyx_view_->getDialogs().disconnect(argument);
1390                         break;
1391
1392
1393                 case LFUN_CITATION_INSERT: {
1394                         BOOST_ASSERT(lyx_view_);
1395                         if (!argument.empty()) {
1396                                 // we can have one optional argument, delimited by '|'
1397                                 // citation-insert <key>|<text_before>
1398                                 // this should be enhanced to also support text_after
1399                                 // and citation style
1400                                 string arg = argument;
1401                                 string opt1;
1402                                 if (contains(argument, "|")) {
1403                                         arg = token(argument, '|', 0);
1404                                         opt1 = token(argument, '|', 1);
1405                                 }
1406                                 InsetCommandParams icp("cite");
1407                                 icp["key"] = from_utf8(arg);
1408                                 if (!opt1.empty())
1409                                         icp["before"] = from_utf8(opt1);
1410                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1411                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1412                                 dispatch(fr);
1413                         } else
1414                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1415                         break;
1416                 }
1417
1418                 case LFUN_BUFFER_CHILD_OPEN: {
1419                         BOOST_ASSERT(lyx_view_);
1420                         FileName const filename =
1421                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1422                         view()->saveBookmark(false);
1423                         string const parentfilename = lyx_view_->buffer()->fileName();
1424                         if (theBufferList().exists(filename.absFilename()))
1425                                 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1426                         else
1427                                 if (lyx_view_->loadLyXFile(filename)) {
1428                                         // Set the parent name of the child document.
1429                                         // This makes insertion of citations and references in the child work,
1430                                         // when the target is in the parent or another child document.
1431                                         lyx_view_->buffer()->setParentName(parentfilename);
1432                                         setMessage(bformat(_("Opening child document %1$s..."),
1433                                                  makeDisplayPath(filename.absFilename())));
1434                                 } else
1435                                         setMessage(_("Document not loaded."));
1436                         break;
1437                 }
1438
1439                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1440                         BOOST_ASSERT(lyx_view_);
1441                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1442                         break;
1443
1444                 case LFUN_KEYMAP_OFF:
1445                         BOOST_ASSERT(lyx_view_);
1446                         lyx_view_->view()->getIntl().keyMapOn(false);
1447                         break;
1448
1449                 case LFUN_KEYMAP_PRIMARY:
1450                         BOOST_ASSERT(lyx_view_);
1451                         lyx_view_->view()->getIntl().keyMapPrim();
1452                         break;
1453
1454                 case LFUN_KEYMAP_SECONDARY:
1455                         BOOST_ASSERT(lyx_view_);
1456                         lyx_view_->view()->getIntl().keyMapSec();
1457                         break;
1458
1459                 case LFUN_KEYMAP_TOGGLE:
1460                         BOOST_ASSERT(lyx_view_);
1461                         lyx_view_->view()->getIntl().toggleKeyMap();
1462                         break;
1463
1464                 case LFUN_REPEAT: {
1465                         // repeat command
1466                         string countstr;
1467                         string rest = split(argument, countstr, ' ');
1468                         istringstream is(countstr);
1469                         int count = 0;
1470                         is >> count;
1471                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1472                         for (int i = 0; i < count; ++i)
1473                                 dispatch(lyxaction.lookupFunc(rest));
1474                         break;
1475                 }
1476
1477                 case LFUN_COMMAND_SEQUENCE: {
1478                         // argument contains ';'-terminated commands
1479                         string arg = argument;
1480                         while (!arg.empty()) {
1481                                 string first;
1482                                 arg = split(arg, first, ';');
1483                                 FuncRequest func(lyxaction.lookupFunc(first));
1484                                 func.origin = cmd.origin;
1485                                 dispatch(func);
1486                         }
1487                         break;
1488                 }
1489
1490                 case LFUN_PREFERENCES_SAVE: {
1491                         lyxrc.write(makeAbsPath("preferences",
1492                                                 package().user_support().absFilename()),
1493                                     false);
1494                         break;
1495                 }
1496
1497                 case LFUN_SCREEN_FONT_UPDATE:
1498                         BOOST_ASSERT(lyx_view_);
1499                         // handle the screen font changes.
1500                         theFontLoader().update();
1501                         /// FIXME: only the current view will be updated. the Gui
1502                         /// class is able to furnish the list of views.
1503                         updateFlags = Update::Force;
1504                         break;
1505
1506                 case LFUN_SET_COLOR: {
1507                         string lyx_name;
1508                         string const x11_name = split(argument, lyx_name, ' ');
1509                         if (lyx_name.empty() || x11_name.empty()) {
1510                                 setErrorMessage(from_ascii(N_(
1511                                                 "Syntax: set-color <lyx_name>"
1512                                                 " <x11_name>")));
1513                                 break;
1514                         }
1515
1516                         bool const graphicsbg_changed =
1517                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1518                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1519
1520                         if (!lcolor.setColor(lyx_name, x11_name)) {
1521                                 setErrorMessage(
1522                                                 bformat(_("Set-color \"%1$s\" failed "
1523                                                                        "- color is undefined or "
1524                                                                        "may not be redefined"),
1525                                                                            from_utf8(lyx_name)));
1526                                 break;
1527                         }
1528
1529                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1530
1531                         if (graphicsbg_changed) {
1532 #ifdef WITH_WARNINGS
1533 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1534 #endif
1535 #if 0
1536                                 graphics::GCache::get().changeDisplay(true);
1537 #endif
1538                         }
1539                         break;
1540                 }
1541
1542                 case LFUN_MESSAGE:
1543                         BOOST_ASSERT(lyx_view_);
1544                         lyx_view_->message(from_utf8(argument));
1545                         break;
1546
1547                 case LFUN_EXTERNAL_EDIT: {
1548                         BOOST_ASSERT(lyx_view_);
1549                         FuncRequest fr(action, argument);
1550                         InsetExternal().dispatch(view()->cursor(), fr);
1551                         break;
1552                 }
1553
1554                 case LFUN_GRAPHICS_EDIT: {
1555                         FuncRequest fr(action, argument);
1556                         InsetGraphics().dispatch(view()->cursor(), fr);
1557                         break;
1558                 }
1559
1560                 case LFUN_INSET_APPLY: {
1561                         BOOST_ASSERT(lyx_view_);
1562                         string const name = cmd.getArg(0);
1563                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1564                         if (inset) {
1565                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1566                                 inset->dispatch(view()->cursor(), fr);
1567                         } else {
1568                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1569                                 dispatch(fr);
1570                         }
1571                         // ideally, the update flag should be set by the insets,
1572                         // but this is not possible currently
1573                         updateFlags = Update::Force | Update::FitCursor;
1574                         break;
1575                 }
1576
1577                 case LFUN_ALL_INSETS_TOGGLE: {
1578                         BOOST_ASSERT(lyx_view_);
1579                         string action;
1580                         string const name = split(argument, action, ' ');
1581                         Inset::Code const inset_code =
1582                                 Inset::translate(name);
1583
1584                         Cursor & cur = view()->cursor();
1585                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1586
1587                         Inset & inset = lyx_view_->buffer()->inset();
1588                         InsetIterator it  = inset_iterator_begin(inset);
1589                         InsetIterator const end = inset_iterator_end(inset);
1590                         for (; it != end; ++it) {
1591                                 if (!it->asInsetMath()
1592                                     && (inset_code == Inset::NO_CODE
1593                                     || inset_code == it->lyxCode())) {
1594                                         Cursor tmpcur = cur;
1595                                         tmpcur.pushLeft(*it);
1596                                         it->dispatch(tmpcur, fr);
1597                                 }
1598                         }
1599                         updateFlags = Update::Force | Update::FitCursor;
1600                         break;
1601                 }
1602
1603                 case LFUN_BUFFER_LANGUAGE: {
1604                         BOOST_ASSERT(lyx_view_);
1605                         Buffer & buffer = *lyx_view_->buffer();
1606                         Language const * oldL = buffer.params().language;
1607                         Language const * newL = languages.getLanguage(argument);
1608                         if (!newL || oldL == newL)
1609                                 break;
1610
1611                         if (oldL->rightToLeft() == newL->rightToLeft()
1612                             && !buffer.isMultiLingual())
1613                                 buffer.changeLanguage(oldL, newL);
1614                         break;
1615                 }
1616
1617                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1618                         string const fname =
1619                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1620                                         "defaults.lyx");
1621                         Buffer defaults(fname);
1622
1623                         istringstream ss(argument);
1624                         Lexer lex(0,0);
1625                         lex.setStream(ss);
1626                         int const unknown_tokens = defaults.readHeader(lex);
1627
1628                         if (unknown_tokens != 0) {
1629                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
1630                                        << unknown_tokens << " unknown token"
1631                                        << (unknown_tokens == 1 ? "" : "s")
1632                                        << endl;
1633                         }
1634
1635                         if (defaults.writeFile(FileName(defaults.fileName())))
1636                                 setMessage(bformat(_("Document defaults saved in %1$s"),
1637                                                    makeDisplayPath(fname)));
1638                         else
1639                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
1640                         break;
1641                 }
1642
1643                 case LFUN_BUFFER_PARAMS_APPLY: {
1644                         BOOST_ASSERT(lyx_view_);
1645                         biblio::CiteEngine const engine =
1646                                 lyx_view_->buffer()->params().getEngine();
1647
1648                         istringstream ss(argument);
1649                         Lexer lex(0,0);
1650                         lex.setStream(ss);
1651                         int const unknown_tokens =
1652                                 lyx_view_->buffer()->readHeader(lex);
1653
1654                         if (unknown_tokens != 0) {
1655                                 lyxerr << "Warning in LFUN_BUFFER_PARAMS_APPLY!\n"
1656                                        << unknown_tokens << " unknown token"
1657                                        << (unknown_tokens == 1 ? "" : "s")
1658                                        << endl;
1659                         }
1660                         if (engine == lyx_view_->buffer()->params().getEngine())
1661                                 break;
1662
1663                         Cursor & cur = view()->cursor();
1664                         FuncRequest fr(LFUN_INSET_REFRESH);
1665
1666                         Inset & inset = lyx_view_->buffer()->inset();
1667                         InsetIterator it  = inset_iterator_begin(inset);
1668                         InsetIterator const end = inset_iterator_end(inset);
1669                         for (; it != end; ++it)
1670                                 if (it->lyxCode() == Inset::CITE_CODE)
1671                                         it->dispatch(cur, fr);
1672                         break;
1673                 }
1674
1675                 case LFUN_TEXTCLASS_APPLY: {
1676                         BOOST_ASSERT(lyx_view_);
1677                         Buffer * buffer = lyx_view_->buffer();
1678
1679                         textclass_type const old_class =
1680                                 buffer->params().textclass;
1681
1682                         loadTextclass(argument);
1683
1684                         std::pair<bool, textclass_type> const tc_pair =
1685                                 textclasslist.numberOfClass(argument);
1686
1687                         if (!tc_pair.first)
1688                                 break;
1689
1690                         textclass_type const new_class = tc_pair.second;
1691                         if (old_class == new_class)
1692                                 // nothing to do
1693                                 break;
1694
1695                         lyx_view_->message(_("Converting document to new document class..."));
1696                         recordUndoFullDocument(view());
1697                         buffer->params().textclass = new_class;
1698                         StableDocIterator backcur(view()->cursor());
1699                         ErrorList & el = buffer->errorList("Class Switch");
1700                         cap::switchBetweenClasses(
1701                                 old_class, new_class,
1702                                 static_cast<InsetText &>(buffer->inset()), el);
1703
1704                         view()->setCursor(backcur.asDocIterator(&(buffer->inset())));
1705
1706                         buffer->errors("Class Switch");
1707                         updateLabels(*buffer);
1708                         updateFlags = Update::Force | Update::FitCursor;
1709                         break;
1710                 }
1711
1712                 case LFUN_TEXTCLASS_LOAD:
1713                         loadTextclass(argument);
1714                         break;
1715
1716                 case LFUN_LYXRC_APPLY: {
1717                         LyXRC const lyxrc_orig = lyxrc;
1718
1719                         istringstream ss(argument);
1720                         bool const success = lyxrc.read(ss) == 0;
1721
1722                         if (!success) {
1723                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1724                                        << "Unable to read lyxrc data"
1725                                        << endl;
1726                                 break;
1727                         }
1728
1729                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
1730
1731                         /// We force the redraw in any case because there might be
1732                         /// some screen font changes.
1733                         /// FIXME: only the current view will be updated. the Gui
1734                         /// class is able to furnish the list of views.
1735                         updateFlags = Update::Force;
1736                         break;
1737                 }
1738
1739                 case LFUN_WINDOW_NEW:
1740                         LyX::ref().newLyXView();
1741                         break;
1742
1743                 case LFUN_WINDOW_CLOSE:
1744                         BOOST_ASSERT(lyx_view_);
1745                         BOOST_ASSERT(theApp());
1746                         // update bookmark pit of the current buffer before window close
1747                         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
1748                                 gotoBookmark(i+1, false, false);
1749                         // ask the user for saving changes or cancel quit
1750                         if (!theBufferList().quitWriteAll())
1751                                 break;
1752                         lyx_view_->close();
1753                         return;
1754
1755                 case LFUN_BOOKMARK_GOTO:
1756                         // go to bookmark, open unopened file and switch to buffer if necessary
1757                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
1758                         break;
1759
1760                 case LFUN_BOOKMARK_CLEAR:
1761                         LyX::ref().session().bookmarks().clear();
1762                         break;
1763
1764                 case LFUN_TOOLBAR_TOGGLE_STATE:
1765                         lyx_view_->toggleToolbarState(argument);
1766                         break;
1767
1768                 case LFUN_TOOLBAR_TOGGLE: {
1769                         BOOST_ASSERT(lyx_view_);
1770                         string const name = to_utf8(cmd.argument());
1771                         bool const current = lyx_view_->getToolbars().visible(name);
1772                         lyx_view_->getToolbars().display(name, !current);
1773                         break;
1774                 }
1775
1776                 default: {
1777                         BOOST_ASSERT(lyx_view_);
1778                         view()->cursor().dispatch(cmd);
1779                         updateFlags = view()->cursor().result().update();
1780                         if (!view()->cursor().result().dispatched())
1781                                 updateFlags = view()->dispatch(cmd);
1782                         break;
1783                 }
1784                 }
1785
1786                 if (lyx_view_ && view()->buffer()) {
1787                         // BufferView::update() updates the ViewMetricsInfo and
1788                         // also initializes the position cache for all insets in
1789                         // (at least partially) visible top-level paragraphs.
1790                         // We will redraw the screen only if needed.
1791                         if (view()->update(updateFlags)) {
1792                                 // Buffer::changed() signals that a repaint is needed.
1793                                 // The frontend (WorkArea) knows which area to repaint
1794                                 // thanks to the ViewMetricsInfo updated above.
1795                                 view()->buffer()->changed();
1796                         }
1797
1798                         lyx_view_->updateStatusBar();
1799
1800                         // if we executed a mutating lfun, mark the buffer as dirty
1801                         if (flag.enabled()
1802                             && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
1803                             && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
1804                                 view()->buffer()->markDirty();
1805
1806                         if (view()->cursor().inTexted()) {
1807                                 lyx_view_->updateLayoutChoice();
1808                         }
1809                 }
1810         }
1811         if (!quitting) {
1812                 lyx_view_->updateMenubar();
1813                 lyx_view_->updateToolbars();
1814                 // Some messages may already be translated, so we cannot use _()
1815                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
1816         }
1817 }
1818
1819
1820 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
1821 {
1822         const bool verbose = (cmd.origin == FuncRequest::MENU
1823                               || cmd.origin == FuncRequest::TOOLBAR
1824                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1825
1826         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
1827                 LYXERR(Debug::ACTION) << "dispatch msg is " << to_utf8(msg) << endl;
1828                 if (!msg.empty())
1829                         lyx_view_->message(msg);
1830                 return;
1831         }
1832
1833         docstring dispatch_msg = msg;
1834         if (!dispatch_msg.empty())
1835                 dispatch_msg += ' ';
1836
1837         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
1838
1839         bool argsadded = false;
1840
1841         if (!cmd.argument().empty()) {
1842                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1843                         comname += ' ' + cmd.argument();
1844                         argsadded = true;
1845                 }
1846         }
1847
1848         docstring const shortcuts = theTopLevelKeymap().printbindings(cmd);
1849
1850         if (!shortcuts.empty())
1851                 comname += ": " + shortcuts;
1852         else if (!argsadded && !cmd.argument().empty())
1853                 comname += ' ' + cmd.argument();
1854
1855         if (!comname.empty()) {
1856                 comname = rtrim(comname);
1857                 dispatch_msg += '(' + rtrim(comname) + ')';
1858         }
1859
1860         LYXERR(Debug::ACTION) << "verbose dispatch msg "
1861                 << to_utf8(dispatch_msg) << endl;
1862         if (!dispatch_msg.empty())
1863                 lyx_view_->message(dispatch_msg);
1864 }
1865
1866
1867 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1868 {
1869         // FIXME: initpath is not used. What to do?
1870         string initpath = lyxrc.document_path;
1871         string filename(name);
1872
1873         if (view()->buffer()) {
1874                 string const trypath = lyx_view_->buffer()->filePath();
1875                 // If directory is writeable, use this as default.
1876                 if (isDirWriteable(FileName(trypath)))
1877                         initpath = trypath;
1878         }
1879
1880         static int newfile_number;
1881
1882         if (filename.empty()) {
1883                 filename = addName(lyxrc.document_path,
1884                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1885                 while (theBufferList().exists(filename) ||
1886                        fs::is_readable(FileName(filename).toFilesystemEncoding())) {
1887                         ++newfile_number;
1888                         filename = addName(lyxrc.document_path,
1889                                            "newfile" +  convert<string>(newfile_number) +
1890                                     ".lyx");
1891                 }
1892         }
1893
1894         // The template stuff
1895         string templname;
1896         if (fromTemplate) {
1897                 FileDialog fileDlg(_("Select template file"),
1898                         LFUN_SELECT_FILE_SYNC,
1899                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1900                         make_pair(_("Templates|#T#t"), from_utf8(lyxrc.template_path)));
1901
1902                 FileDialog::Result result =
1903                         fileDlg.open(from_utf8(lyxrc.template_path),
1904                                      FileFilterList(_("LyX Documents (*.lyx)")),
1905                                      docstring());
1906
1907                 if (result.first == FileDialog::Later)
1908                         return;
1909                 if (result.second.empty())
1910                         return;
1911                 templname = to_utf8(result.second);
1912         }
1913
1914         Buffer * const b = newFile(filename, templname, !name.empty());
1915         if (b) {
1916                 updateLabels(*b);
1917                 lyx_view_->setBuffer(b);
1918         }
1919 }
1920
1921
1922 void LyXFunc::open(string const & fname)
1923 {
1924         string initpath = lyxrc.document_path;
1925
1926         if (view()->buffer()) {
1927                 string const trypath = lyx_view_->buffer()->filePath();
1928                 // If directory is writeable, use this as default.
1929                 if (isDirWriteable(FileName(trypath)))
1930                         initpath = trypath;
1931         }
1932
1933         string filename;
1934
1935         if (fname.empty()) {
1936                 FileDialog fileDlg(_("Select document to open"),
1937                         LFUN_FILE_OPEN,
1938                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
1939                         make_pair(_("Examples|#E#e"), from_utf8(addPath(package().system_support().absFilename(), "examples"))));
1940
1941                 FileDialog::Result result =
1942                         fileDlg.open(from_utf8(initpath),
1943                                      FileFilterList(_("LyX Documents (*.lyx)")),
1944                                      docstring());
1945
1946                 if (result.first == FileDialog::Later)
1947                         return;
1948
1949                 filename = to_utf8(result.second);
1950
1951                 // check selected filename
1952                 if (filename.empty()) {
1953                         lyx_view_->message(_("Canceled."));
1954                         return;
1955                 }
1956         } else
1957                 filename = fname;
1958
1959         // get absolute path of file and add ".lyx" to the filename if
1960         // necessary
1961         FileName const fullname = fileSearch(string(), filename, "lyx");
1962         if (!fullname.empty())
1963                 filename = fullname.absFilename();
1964
1965         // if the file doesn't exist, let the user create one
1966         if (!fs::exists(fullname.toFilesystemEncoding())) {
1967                 // the user specifically chose this name. Believe him.
1968                 Buffer * const b = newFile(filename, string(), true);
1969                 if (b)
1970                         lyx_view_->setBuffer(b);
1971                 return;
1972         }
1973
1974         docstring const disp_fn = makeDisplayPath(filename);
1975         lyx_view_->message(bformat(_("Opening document %1$s..."), disp_fn));
1976
1977         docstring str2;
1978         if (lyx_view_->loadLyXFile(fullname)) {
1979                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1980         } else {
1981                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1982         }
1983         lyx_view_->message(str2);
1984 }
1985
1986
1987 void LyXFunc::doImport(string const & argument)
1988 {
1989         string format;
1990         string filename = split(argument, format, ' ');
1991
1992         LYXERR(Debug::INFO) << "LyXFunc::doImport: " << format
1993                             << " file: " << filename << endl;
1994
1995         // need user interaction
1996         if (filename.empty()) {
1997                 string initpath = lyxrc.document_path;
1998
1999                 if (view()->buffer()) {
2000                         string const trypath = lyx_view_->buffer()->filePath();
2001                         // If directory is writeable, use this as default.
2002                         if (isDirWriteable(FileName(trypath)))
2003                                 initpath = trypath;
2004                 }
2005
2006                 docstring const text = bformat(_("Select %1$s file to import"),
2007                         formats.prettyName(format));
2008
2009                 FileDialog fileDlg(text,
2010                         LFUN_BUFFER_IMPORT,
2011                         make_pair(_("Documents|#o#O"), from_utf8(lyxrc.document_path)),
2012                         make_pair(_("Examples|#E#e"),
2013                                   from_utf8(addPath(package().system_support().absFilename(), "examples"))));
2014
2015                 docstring filter = formats.prettyName(format);
2016                 filter += " (*.";
2017                 // FIXME UNICODE
2018                 filter += from_utf8(formats.extension(format));
2019                 filter += ')';
2020
2021                 FileDialog::Result result =
2022                         fileDlg.open(from_utf8(initpath),
2023                                      FileFilterList(filter),
2024                                      docstring());
2025
2026                 if (result.first == FileDialog::Later)
2027                         return;
2028
2029                 filename = to_utf8(result.second);
2030
2031                 // check selected filename
2032                 if (filename.empty())
2033                         lyx_view_->message(_("Canceled."));
2034         }
2035
2036         if (filename.empty())
2037                 return;
2038
2039         // get absolute path of file
2040         FileName const fullname(makeAbsPath(filename));
2041
2042         FileName const lyxfile(changeExtension(fullname.absFilename(), ".lyx"));
2043
2044         // Check if the document already is open
2045         if (use_gui && theBufferList().exists(lyxfile.absFilename())) {
2046                 if (!theBufferList().close(theBufferList().getBuffer(lyxfile.absFilename()), true)) {
2047                         lyx_view_->message(_("Canceled."));
2048                         return;
2049                 }
2050         }
2051
2052         // if the file exists already, and we didn't do
2053         // -i lyx thefile.lyx, warn
2054         if (fs::exists(lyxfile.toFilesystemEncoding()) && fullname != lyxfile) {
2055                 docstring const file = makeDisplayPath(lyxfile.absFilename(), 30);
2056
2057                 docstring text = bformat(_("The document %1$s already exists.\n\n"
2058                                                      "Do you want to over-write that document?"), file);
2059                 int const ret = Alert::prompt(_("Over-write document?"),
2060                         text, 0, 1, _("&Over-write"), _("&Cancel"));
2061
2062                 if (ret == 1) {
2063                         lyx_view_->message(_("Canceled."));
2064                         return;
2065                 }
2066         }
2067
2068         ErrorList errorList;
2069         Importer::Import(lyx_view_, fullname, format, errorList);
2070         // FIXME (Abdel 12/08/06): Is there a need to display the error list here?
2071 }
2072
2073
2074 void LyXFunc::closeBuffer()
2075 {
2076         // goto bookmark to update bookmark pit.
2077         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2078                 gotoBookmark(i+1, false, false);
2079         if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2080                 if (theBufferList().empty()) {
2081                         // need this otherwise SEGV may occur while
2082                         // trying to set variables that don't exist
2083                         // since there's no current buffer
2084                         lyx_view_->getDialogs().hideBufferDependent();
2085                 } else {
2086                         lyx_view_->setBuffer(theBufferList().first());
2087                 }
2088         }
2089 }
2090
2091
2092 void LyXFunc::reloadBuffer()
2093 {
2094         FileName filename(lyx_view_->buffer()->fileName());
2095         closeBuffer();
2096         lyx_view_->loadLyXFile(filename);
2097 }
2098
2099 // Each "lyx_view_" should have it's own message method. lyxview and
2100 // the minibuffer would use the minibuffer, but lyxserver would
2101 // send an ERROR signal to its client.  Alejandro 970603
2102 // This function is bit problematic when it comes to NLS, to make the
2103 // lyx servers client be language indepenent we must not translate
2104 // strings sent to this func.
2105 void LyXFunc::setErrorMessage(docstring const & m) const
2106 {
2107         dispatch_buffer = m;
2108         errorstat = true;
2109 }
2110
2111
2112 void LyXFunc::setMessage(docstring const & m) const
2113 {
2114         dispatch_buffer = m;
2115 }
2116
2117
2118 docstring const LyXFunc::viewStatusMessage()
2119 {
2120         // When meta-fake key is pressed, show the key sequence so far + "M-".
2121         if (wasMetaKey())
2122                 return keyseq->print(true) + "M-";
2123
2124         // Else, when a non-complete key sequence is pressed,
2125         // show the available options.
2126         if (keyseq->length() > 0 && !keyseq->deleted())
2127                 return keyseq->printOptions(true);
2128
2129         if (!view()->buffer())
2130                 return _("Welcome to LyX!");
2131
2132         return view()->cursor().currentState();
2133 }
2134
2135
2136 BufferView * LyXFunc::view() const
2137 {
2138         BOOST_ASSERT(lyx_view_);
2139         return lyx_view_->view();
2140 }
2141
2142
2143 bool LyXFunc::wasMetaKey() const
2144 {
2145         return (meta_fake_bit != key_modifier::none);
2146 }
2147
2148
2149 namespace {
2150
2151 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2152 {
2153         // Why the switch you might ask. It is a trick to ensure that all
2154         // the elements in the LyXRCTags enum is handled. As you can see
2155         // there are no breaks at all. So it is just a huge fall-through.
2156         // The nice thing is that we will get a warning from the compiler
2157         // if we forget an element.
2158         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2159         switch (tag) {
2160         case LyXRC::RC_ACCEPT_COMPOUND:
2161         case LyXRC::RC_ALT_LANG:
2162         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2163         case LyXRC::RC_PLAINTEXT_LINELEN:
2164         case LyXRC::RC_AUTOREGIONDELETE:
2165         case LyXRC::RC_AUTORESET_OPTIONS:
2166         case LyXRC::RC_AUTOSAVE:
2167         case LyXRC::RC_AUTO_NUMBER:
2168         case LyXRC::RC_BACKUPDIR_PATH:
2169         case LyXRC::RC_BIBTEX_COMMAND:
2170         case LyXRC::RC_BINDFILE:
2171         case LyXRC::RC_CHECKLASTFILES:
2172         case LyXRC::RC_USELASTFILEPOS:
2173         case LyXRC::RC_LOADSESSION:
2174         case LyXRC::RC_CHKTEX_COMMAND:
2175         case LyXRC::RC_CONVERTER:
2176         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2177         case LyXRC::RC_COPIER:
2178         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2179         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2180         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2181         case LyXRC::RC_DATE_INSERT_FORMAT:
2182         case LyXRC::RC_DEFAULT_LANGUAGE:
2183         case LyXRC::RC_DEFAULT_PAPERSIZE:
2184         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2185         case LyXRC::RC_DISPLAY_GRAPHICS:
2186         case LyXRC::RC_DOCUMENTPATH:
2187                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2188                         string const encoded = FileName(
2189                                 lyxrc_new.document_path).toFilesystemEncoding();
2190                         if (fs::exists(encoded) && fs::is_directory(encoded))
2191                                 support::package().document_dir() = FileName(lyxrc.document_path);
2192                 }
2193         case LyXRC::RC_ESC_CHARS:
2194         case LyXRC::RC_FONT_ENCODING:
2195         case LyXRC::RC_FORMAT:
2196         case LyXRC::RC_INDEX_COMMAND:
2197         case LyXRC::RC_INPUT:
2198         case LyXRC::RC_KBMAP:
2199         case LyXRC::RC_KBMAP_PRIMARY:
2200         case LyXRC::RC_KBMAP_SECONDARY:
2201         case LyXRC::RC_LABEL_INIT_LENGTH:
2202         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2203         case LyXRC::RC_LANGUAGE_AUTO_END:
2204         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2205         case LyXRC::RC_LANGUAGE_COMMAND_END:
2206         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2207         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2208         case LyXRC::RC_LANGUAGE_PACKAGE:
2209         case LyXRC::RC_LANGUAGE_USE_BABEL:
2210         case LyXRC::RC_MAKE_BACKUP:
2211         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2212         case LyXRC::RC_NUMLASTFILES:
2213         case LyXRC::RC_PATH_PREFIX:
2214                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2215                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2216                 }
2217         case LyXRC::RC_PERS_DICT:
2218         case LyXRC::RC_PREVIEW:
2219         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2220         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2221         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2222         case LyXRC::RC_PRINTCOPIESFLAG:
2223         case LyXRC::RC_PRINTER:
2224         case LyXRC::RC_PRINTEVENPAGEFLAG:
2225         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2226         case LyXRC::RC_PRINTFILEEXTENSION:
2227         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2228         case LyXRC::RC_PRINTODDPAGEFLAG:
2229         case LyXRC::RC_PRINTPAGERANGEFLAG:
2230         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2231         case LyXRC::RC_PRINTPAPERFLAG:
2232         case LyXRC::RC_PRINTREVERSEFLAG:
2233         case LyXRC::RC_PRINTSPOOL_COMMAND:
2234         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2235         case LyXRC::RC_PRINTTOFILE:
2236         case LyXRC::RC_PRINTTOPRINTER:
2237         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2238         case LyXRC::RC_PRINT_COMMAND:
2239         case LyXRC::RC_RTL_SUPPORT:
2240         case LyXRC::RC_SCREEN_DPI:
2241         case LyXRC::RC_SCREEN_FONT_ROMAN:
2242         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2243         case LyXRC::RC_SCREEN_FONT_SANS:
2244         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2245         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2246         case LyXRC::RC_SCREEN_FONT_SIZES:
2247         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2248         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2249         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2250         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2251         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2252         case LyXRC::RC_SCREEN_ZOOM:
2253         case LyXRC::RC_SERVERPIPE:
2254         case LyXRC::RC_SET_COLOR:
2255         case LyXRC::RC_SHOW_BANNER:
2256         case LyXRC::RC_SPELL_COMMAND:
2257         case LyXRC::RC_TEMPDIRPATH:
2258         case LyXRC::RC_TEMPLATEPATH:
2259         case LyXRC::RC_TEX_ALLOWS_SPACES:
2260         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2261                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2262                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2263                 }
2264         case LyXRC::RC_UIFILE:
2265         case LyXRC::RC_USER_EMAIL:
2266         case LyXRC::RC_USER_NAME:
2267         case LyXRC::RC_USETEMPDIR:
2268         case LyXRC::RC_USE_ALT_LANG:
2269         case LyXRC::RC_USE_CONVERTER_CACHE:
2270         case LyXRC::RC_USE_ESC_CHARS:
2271         case LyXRC::RC_USE_INP_ENC:
2272         case LyXRC::RC_USE_PERS_DICT:
2273         case LyXRC::RC_USE_SPELL_LIB:
2274         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2275         case LyXRC::RC_VIEWER:
2276         case LyXRC::RC_LAST:
2277                 break;
2278         }
2279 }
2280
2281 } // namespace anon
2282
2283
2284 } // namespace lyx