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