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