]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
* Cursor::isRTL(): correctly deal with the inner paragraph when within mathed.
[lyx.git] / src / LyXFunc.cpp
1 /**
2  * \file LyXFunc.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "LyXFunc.h"
23
24 #include "BranchList.h"
25 #include "Buffer.h"
26 #include "buffer_funcs.h"
27 #include "BufferList.h"
28 #include "BufferParams.h"
29 #include "BufferView.h"
30 #include "bufferview_funcs.h"
31 #include "Cursor.h"
32 #include "CutAndPaste.h"
33 #include "debug.h"
34 #include "DispatchResult.h"
35 #include "Encoding.h"
36 #include "ErrorList.h"
37 #include "Exporter.h"
38 #include "Format.h"
39 #include "FuncRequest.h"
40 #include "FuncStatus.h"
41 #include "gettext.h"
42 #include "Importer.h"
43 #include "InsetIterator.h"
44 #include "Intl.h"
45 #include "KeyMap.h"
46 #include "Language.h"
47 #include "Color.h"
48 #include "Session.h"
49 #include "LyX.h"
50 #include "callback.h"
51 #include "LyXAction.h"
52 #include "lyxfind.h"
53 #include "Lexer.h"
54 #include "LyXRC.h"
55 #include "Row.h"
56 #include "Server.h"
57 #include "TextClassList.h"
58 #include "LyXVC.h"
59 #include "Paragraph.h"
60 #include "ParIterator.h"
61 #include "ParagraphParameters.h"
62 #include "Undo.h"
63
64 #include "insets/InsetBox.h"
65 #include "insets/InsetBranch.h"
66 #include "insets/InsetCommand.h"
67 #include "insets/InsetERT.h"
68 #include "insets/InsetExternal.h"
69 #include "insets/InsetFloat.h"
70 #include "insets/InsetGraphics.h"
71 #include "insets/InsetInclude.h"
72 #include "insets/InsetNote.h"
73 #include "insets/InsetTabular.h"
74 #include "insets/InsetVSpace.h"
75 #include "insets/InsetWrap.h"
76
77 #include "frontends/Application.h"
78 #include "frontends/alert.h"
79 #include "frontends/Dialogs.h"
80 #include "frontends/FileDialog.h"
81 #include "frontends/FontLoader.h"
82 #include "frontends/Gui.h"
83 #include "frontends/KeySymbol.h"
84 #include "frontends/LyXView.h"
85 #include "frontends/Menubar.h"
86 #include "frontends/Toolbars.h"
87
88 #include "support/environment.h"
89 #include "support/FileFilterList.h"
90 #include "support/filetools.h"
91 #include "support/ForkedcallsController.h"
92 #include "support/fs_extras.h"
93 #include "support/lstrings.h"
94 #include "support/Path.h"
95 #include "support/Package.h"
96 #include "support/Systemcall.h"
97 #include "support/convert.h"
98 #include "support/os.h"
99
100 #include <boost/current_function.hpp>
101 #include <boost/filesystem/operations.hpp>
102
103 #include <sstream>
104
105
106 namespace lyx {
107
108 using bv_funcs::freefont2string;
109
110 using support::absolutePath;
111 using support::addName;
112 using support::addPath;
113 using support::bformat;
114 using support::changeExtension;
115 using support::contains;
116 using support::FileFilterList;
117 using support::FileName;
118 using support::fileSearch;
119 using support::ForkedcallsController;
120 using support::i18nLibFileSearch;
121 using support::isDirWriteable;
122 using support::isFileReadable;
123 using support::isStrInt;
124 using support::makeAbsPath;
125 using support::makeDisplayPath;
126 using support::package;
127 using support::quoteName;
128 using support::rtrim;
129 using support::split;
130 using support::subst;
131 using support::Systemcall;
132 using support::token;
133 using support::trim;
134 using support::prefixIs;
135
136 using std::endl;
137 using std::make_pair;
138 using std::pair;
139 using std::string;
140 using std::istringstream;
141 using std::ostringstream;
142
143 namespace Alert = frontend::Alert;
144 namespace fs = boost::filesystem;
145
146
147 namespace {
148
149 bool getLocalStatus(Cursor cursor,
150                FuncRequest const & cmd, FuncStatus & status)
151 {
152         // Try to fix cursor in case it is broken.
153         cursor.fixIfBroken();
154
155         // This is, of course, a mess. Better create a new doc iterator and use
156         // this in Inset::getStatus. This might require an additional
157         // BufferView * arg, though (which should be avoided)
158         //Cursor safe = *this;
159         bool res = false;
160         for ( ; cursor.depth(); cursor.pop()) {
161                 //lyxerr << "\nCursor::getStatus: cmd: " << cmd << endl << *this << endl;
162                 BOOST_ASSERT(cursor.idx() <= cursor.lastidx());
163                 BOOST_ASSERT(cursor.pit() <= cursor.lastpit());
164                 BOOST_ASSERT(cursor.pos() <= cursor.lastpos());
165
166                 // The inset's getStatus() will return 'true' if it made
167                 // a definitive decision on whether it want to handle the
168                 // request or not. The result of this decision is put into
169                 // the 'status' parameter.
170                 if (cursor.inset().getStatus(cursor, cmd, status)) {
171                         res = true;
172                         break;
173                 }
174         }
175         return res;
176 }
177
178
179 /** Return the change status at cursor position, taking in account the
180  * status at each level of the document iterator (a table in a deleted
181  * footnote is deleted).
182  * When \param outer is true, the top slice is not looked at.
183  */
184 Change::Type lookupChangeType(DocIterator const & dit, bool outer = false)
185 {
186         size_t const depth = dit.depth() - (outer ? 1 : 0);
187
188         for (size_t i = 0 ; i < depth ; ++i) {
189                 CursorSlice const & slice = dit[i];
190                 if (!slice.inset().inMathed()
191                     && slice.pos() < slice.paragraph().size()) {
192                         Change::Type const ch = slice.paragraph().lookupChange(slice.pos()).type;
193                         if (ch != Change::UNCHANGED)
194                                 return ch;
195                 }
196         }
197         return Change::UNCHANGED;
198 }
199
200 }
201
202 LyXFunc::LyXFunc()
203         : lyx_view_(0),
204         encoded_last_key(0),
205         meta_fake_bit(key_modifier::none)
206 {
207 }
208
209
210 void LyXFunc::initKeySequences(KeyMap * kb)
211 {
212         keyseq.reset(new KeySequence(kb, kb));
213         cancel_meta_seq.reset(new KeySequence(kb, kb));
214 }
215
216
217 void LyXFunc::setLyXView(LyXView * lv)
218 {
219         lyx_view_ = lv;
220 }
221
222
223 void LyXFunc::handleKeyFunc(kb_action action)
224 {
225         char_type c = encoded_last_key;
226
227         if (keyseq->length())
228                 c = 0;
229
230         lyx_view_->view()->getIntl().getTransManager().deadkey(
231                 c, get_accent(action).accent, view()->cursor().innerText(), view()->cursor());
232         // Need to clear, in case the minibuffer calls these
233         // actions
234         keyseq->clear();
235         // copied verbatim from do_accent_char
236         view()->cursor().resetAnchor();
237         view()->update();
238 }
239
240
241 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
242 {
243         BOOST_ASSERT(lyx_view_);
244         if (!LyX::ref().session().bookmarks().isValid(idx))
245                 return;
246         BookmarksSection::Bookmark const & bm = LyX::ref().session().bookmarks().bookmark(idx);
247         BOOST_ASSERT(!bm.filename.empty());
248         string const file = bm.filename.absFilename();
249         // if the file is not opened, open it.
250         if (!theBufferList().exists(file)) {
251                 if (openFile)
252                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
253                 else
254                         return;
255         }
256         // open may fail, so we need to test it again
257         if (theBufferList().exists(file)) {
258                 // if the current buffer is not that one, switch to it.
259                 if (lyx_view_->buffer()->fileName() != file) {
260                         if (switchToBuffer)
261                                 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
262                         else
263                                 return;
264                 }
265                 // moveToPosition use par_id, and par_pit and return new par_id.
266                 pit_type new_pit;
267                 pos_type new_pos;
268                 int new_id;
269                 boost::tie(new_pit, new_pos, new_id) = view()->moveToPosition(bm.bottom_pit, bm.bottom_pos, bm.top_id, bm.top_pos);
270                 // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
271                 // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
272                 if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos || bm.top_id != new_id )
273                         const_cast<BookmarksSection::Bookmark &>(bm).updatePos(new_pit, new_pos, new_id);
274         } 
275 }
276
277
278 void LyXFunc::processKeySym(KeySymbolPtr keysym, key_modifier::state state)
279 {
280         LYXERR(Debug::KEY) << "KeySym is " << keysym->getSymbolName() << endl;
281
282         // Do nothing if we have nothing (JMarc)
283         if (!keysym->isOK()) {
284                 LYXERR(Debug::KEY) << "Empty kbd action (probably composing)"
285                                    << endl;
286                 return;
287         }
288
289         if (keysym->isModifier()) {
290                 LYXERR(Debug::KEY) << "isModifier true" << endl;
291                 return;
292         }
293
294         //Encoding const * encoding = view()->cursor().getEncoding();
295         //encoded_last_key = keysym->getISOEncoded(encoding ? encoding->name() : "");
296         // FIXME: encoded_last_key shadows the member variable of the same
297         // name. Is that intended?
298         char_type encoded_last_key = keysym->getUCSEncoded();
299
300         // Do a one-deep top-level lookup for
301         // cancel and meta-fake keys. RVDK_PATCH_5
302         cancel_meta_seq->reset();
303
304         FuncRequest func = cancel_meta_seq->addkey(keysym, state);
305         LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
306                            << " action first set to [" << func.action << ']'
307                            << endl;
308
309         // When not cancel or meta-fake, do the normal lookup.
310         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
311         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
312         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
313                 // remove Caps Lock and Mod2 as a modifiers
314                 func = keyseq->addkey(keysym, (state | meta_fake_bit));
315                 LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
316                                    << "action now set to ["
317                                    << func.action << ']' << endl;
318         }
319
320         // Dont remove this unless you know what you are doing.
321         meta_fake_bit = key_modifier::none;
322
323         // Can this happen now ?
324         if (func.action == LFUN_NOACTION) {
325                 func = FuncRequest(LFUN_COMMAND_PREFIX);
326         }
327
328         LYXERR(Debug::KEY) << BOOST_CURRENT_FUNCTION
329                << " Key [action="
330                << func.action << "]["
331                << to_utf8(keyseq->print(false)) << ']'
332                << endl;
333
334         // already here we know if it any point in going further
335         // why not return already here if action == -1 and
336         // num_bytes == 0? (Lgb)
337
338         if (keyseq->length() > 1) {
339                 lyx_view_->message(keyseq->print(true));
340         }
341
342
343         // Maybe user can only reach the key via holding down shift.
344         // Let's see. But only if shift is the only modifier
345         if (func.action == LFUN_UNKNOWN_ACTION &&
346             state == key_modifier::shift) {
347                 LYXERR(Debug::KEY) << "Trying without shift" << endl;
348                 func = keyseq->addkey(keysym, key_modifier::none);
349                 LYXERR(Debug::KEY) << "Action now " << func.action << endl;
350         }
351
352         if (func.action == LFUN_UNKNOWN_ACTION) {
353                 // Hmm, we didn't match any of the keysequences. See
354                 // if it's normal insertable text not already covered
355                 // by a binding
356                 if (keysym->isText() && keyseq->length() == 1) {
357                         LYXERR(Debug::KEY) << "isText() is true, inserting." << endl;
358                         func = FuncRequest(LFUN_SELF_INSERT,
359                                            FuncRequest::KEYBOARD);
360                 } else {
361                         LYXERR(Debug::KEY) << "Unknown, !isText() - giving up" << endl;
362                         lyx_view_->message(_("Unknown function."));
363                         return;
364                 }
365         }
366
367         if (func.action == LFUN_SELF_INSERT) {
368                 if (encoded_last_key != 0) {
369                         docstring const arg(1, encoded_last_key);
370                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
371                                              FuncRequest::KEYBOARD));
372                         LYXERR(Debug::KEY)
373                                 << "SelfInsert arg[`" << to_utf8(arg) << "']" << endl;
374                 }
375         } else {
376                 dispatch(func);
377         }
378 }
379
380
381 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
382 {
383         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
384         FuncStatus flag;
385
386         Cursor & cur = view()->cursor();
387
388         /* In LyX/Mac, when a dialog is open, the menus of the
389            application can still be accessed without giving focus to
390            the main window. In this case, we want to disable the menu
391            entries that are buffer-related.
392
393            Note that this code is not perfect, as bug 1941 attests:
394            http://bugzilla.lyx.org/show_bug.cgi?id=1941#c4
395         */
396         Buffer * buf = lyx_view_? lyx_view_->buffer() : 0;
397         if (lyx_view_ && cmd.origin == FuncRequest::MENU && !lyx_view_->hasFocus())
398                 buf = 0;
399
400         if (cmd.action == LFUN_NOACTION) {
401                 flag.message(from_utf8(N_("Nothing to do")));
402                 flag.enabled(false);
403                 return flag;
404         }
405
406         switch (cmd.action) {
407         case LFUN_UNKNOWN_ACTION:
408 #ifndef HAVE_LIBAIKSAURUS
409         case LFUN_THESAURUS_ENTRY:
410 #endif
411                 flag.unknown(true);
412                 flag.enabled(false);
413                 break;
414
415         default:
416                 break;
417         }
418
419         if (flag.unknown()) {
420                 flag.message(from_utf8(N_("Unknown action")));
421                 return flag;
422         }
423
424         if (!flag.enabled()) {
425                 if (flag.message().empty())
426                         flag.message(from_utf8(N_("Command disabled")));
427                 return flag;
428         }
429
430         // Check whether we need a buffer
431         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
432                 // no, exit directly
433                 flag.message(from_utf8(N_("Command not allowed with"
434                                     "out any document open")));
435                 flag.enabled(false);
436                 return flag;
437         }
438
439         // I would really like to avoid having this switch and rather try to
440         // encode this in the function itself.
441         // -- And I'd rather let an inset decide which LFUNs it is willing
442         // to handle (Andre')
443         bool enable = true;
444         switch (cmd.action) {
445         case LFUN_BUFFER_TOGGLE_READ_ONLY:
446                 flag.setOnOff(buf->isReadonly());
447                 break;
448
449         case LFUN_BUFFER_SWITCH:
450                 // toggle on the current buffer, but do not toggle off
451                 // the other ones (is that a good idea?)
452                 if (to_utf8(cmd.argument()) == buf->fileName())
453                         flag.setOnOff(true);
454                 break;
455
456         case LFUN_BUFFER_EXPORT:
457                 enable = cmd.argument() == "custom"
458                         || Exporter::isExportable(*buf, to_utf8(cmd.argument()));
459                 break;
460
461         case LFUN_BUFFER_CHKTEX:
462                 enable = buf->isLatex() && !lyxrc.chktex_command.empty();
463                 break;
464
465         case LFUN_BUILD_PROGRAM:
466                 enable = Exporter::isExportable(*buf, "program");
467                 break;
468
469         case LFUN_LAYOUT_TABULAR:
470                 enable = cur.innerInsetOfType(Inset::TABULAR_CODE);
471                 break;
472
473         case LFUN_LAYOUT:
474         case LFUN_LAYOUT_PARAGRAPH:
475                 enable = !cur.inset().forceDefaultParagraphs(cur.idx());
476                 break;
477
478         case LFUN_VC_REGISTER:
479                 enable = !buf->lyxvc().inUse();
480                 break;
481         case LFUN_VC_CHECK_IN:
482                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
483                 break;
484         case LFUN_VC_CHECK_OUT:
485                 enable = buf->lyxvc().inUse() && buf->isReadonly();
486                 break;
487         case LFUN_VC_REVERT:
488         case LFUN_VC_UNDO_LAST:
489                 enable = buf->lyxvc().inUse();
490                 break;
491         case LFUN_BUFFER_RELOAD:
492                 enable = !buf->isUnnamed() && !buf->isClean();
493                 break;
494
495         case LFUN_INSET_SETTINGS: {
496                 enable = false;
497                 if (!cur)
498                         break;
499                 Inset::Code code = cur.inset().lyxCode();
500                 switch (code) {
501                         case Inset::TABULAR_CODE:
502                                 enable = cmd.argument() == "tabular";
503                                 break;
504                         case Inset::ERT_CODE:
505                                 enable = cmd.argument() == "ert";
506                                 break;
507                         case Inset::FLOAT_CODE:
508                                 enable = cmd.argument() == "float";
509                                 break;
510                         case Inset::WRAP_CODE:
511                                 enable = cmd.argument() == "wrap";
512                                 break;
513                         case Inset::NOTE_CODE:
514                                 enable = cmd.argument() == "note";
515                                 break;
516                         case Inset::BRANCH_CODE:
517                                 enable = cmd.argument() == "branch";
518                                 break;
519                         case Inset::BOX_CODE:
520                                 enable = cmd.argument() == "box";
521                                 break;
522                         default:
523                                 break;
524                 }
525                 break;
526         }
527
528         case LFUN_INSET_APPLY: {
529                 string const name = cmd.getArg(0);
530                 Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
531                 if (inset) {
532                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument());
533                         FuncStatus fs;
534                         if (!inset->getStatus(cur, fr, fs)) {
535                                 // Every inset is supposed to handle this
536                                 BOOST_ASSERT(false);
537                         }
538                         flag |= fs;
539                 } else {
540                         FuncRequest fr(LFUN_INSET_INSERT, cmd.argument());
541                         flag |= getStatus(fr);
542                 }
543                 enable = flag.enabled();
544                 break;
545         }
546
547         case LFUN_DIALOG_SHOW: {
548                 string const name = cmd.getArg(0);
549                 if (!buf)
550                         enable = name == "aboutlyx"
551                                 || name == "file" //FIXME: should be removed.
552                                 || name == "prefs"
553                                 || name == "texinfo";
554                 else if (name == "print")
555                         enable = Exporter::isExportable(*buf, "dvi")
556                                 && lyxrc.print_command != "none";
557                 else if (name == "character")
558                         enable = cur.inset().lyxCode() != Inset::ERT_CODE;
559                 else if (name == "latexlog")
560                         enable = isFileReadable(FileName(buf->getLogName().second));
561                 else if (name == "spellchecker")
562 #if defined (USE_ASPELL) || defined (USE_ISPELL) || defined (USE_PSPELL)
563                         enable = !buf->isReadonly();
564 #else
565                         enable = false;
566 #endif
567                 else if (name == "vclog")
568                         enable = buf->lyxvc().inUse();
569                 break;
570         }
571
572         case LFUN_DIALOG_SHOW_NEW_INSET:
573                 enable = cur.inset().lyxCode() != Inset::ERT_CODE;
574                 if (cur.inset().lyxCode() == Inset::CAPTION_CODE) {
575                         FuncStatus flag;
576                         if (cur.inset().getStatus(cur, cmd, flag))
577                                 return flag;
578                 }
579                 break;
580
581         case LFUN_DIALOG_UPDATE: {
582                 string const name = cmd.getArg(0);
583                 if (!buf)
584                         enable = name == "prefs";
585                 break;
586         }
587
588         case LFUN_CITATION_INSERT: {
589                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
590                 enable = getStatus(fr).enabled();
591                 break;
592         }
593
594         case LFUN_BUFFER_WRITE: {
595                 enable = view()->buffer()->isUnnamed()
596                         || !view()->buffer()->isClean();
597                 break;
598         }
599
600         case LFUN_BOOKMARK_GOTO: {
601                 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
602                 enable = LyX::ref().session().bookmarks().isValid(num);
603                 break;
604         }
605
606         case LFUN_BOOKMARK_CLEAR:
607                 enable = LyX::ref().session().bookmarks().size() > 0;
608                 break;
609
610         case LFUN_TOOLBAR_TOGGLE_STATE: {
611                 ToolbarInfo::Flags flags = lyx_view_->getToolbarState(to_utf8(cmd.argument()));
612                 if (!(flags & ToolbarInfo::AUTO))
613                         flag.setOnOff(flags & ToolbarInfo::ON);
614                 break;
615         }
616
617         // this one is difficult to get right. As a half-baked
618         // solution, we consider only the first action of the sequence
619         case LFUN_COMMAND_SEQUENCE: {
620                 // argument contains ';'-terminated commands
621                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
622                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
623                 func.origin = cmd.origin;
624                 flag = getStatus(func);
625         }
626
627         case LFUN_BUFFER_NEW:
628         case LFUN_BUFFER_NEW_TEMPLATE:
629         case LFUN_WORD_FIND_FORWARD:
630         case LFUN_WORD_FIND_BACKWARD:
631         case LFUN_COMMAND_PREFIX:
632         case LFUN_COMMAND_EXECUTE:
633         case LFUN_CANCEL:
634         case LFUN_META_PREFIX:
635         case LFUN_BUFFER_CLOSE:
636         case LFUN_BUFFER_WRITE_AS:
637         case LFUN_BUFFER_UPDATE:
638         case LFUN_BUFFER_VIEW:
639         case LFUN_BUFFER_IMPORT:
640         case LFUN_TOC_VIEW:
641         case LFUN_BUFFER_AUTO_SAVE:
642         case LFUN_RECONFIGURE:
643         case LFUN_HELP_OPEN:
644         case LFUN_FILE_NEW:
645         case LFUN_FILE_OPEN:
646         case LFUN_DROP_LAYOUTS_CHOICE:
647         case LFUN_MENU_OPEN:
648         case LFUN_SERVER_GET_NAME:
649         case LFUN_SERVER_NOTIFY:
650         case LFUN_SERVER_GOTO_FILE_ROW:
651         case LFUN_DIALOG_HIDE:
652         case LFUN_DIALOG_DISCONNECT_INSET:
653         case LFUN_BUFFER_CHILD_OPEN:
654         case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
655         case LFUN_KEYMAP_OFF:
656         case LFUN_KEYMAP_PRIMARY:
657         case LFUN_KEYMAP_SECONDARY:
658         case LFUN_KEYMAP_TOGGLE:
659         case LFUN_REPEAT:
660         case LFUN_BUFFER_EXPORT_CUSTOM:
661         case LFUN_BUFFER_PRINT:
662         case LFUN_PREFERENCES_SAVE:
663         case LFUN_SCREEN_FONT_UPDATE:
664         case LFUN_SET_COLOR:
665         case LFUN_MESSAGE:
666         case LFUN_EXTERNAL_EDIT:
667         case LFUN_GRAPHICS_EDIT:
668         case LFUN_ALL_INSETS_TOGGLE:
669         case LFUN_BUFFER_LANGUAGE:
670         case LFUN_TEXTCLASS_APPLY:
671         case LFUN_TEXTCLASS_LOAD:
672         case LFUN_BUFFER_SAVE_AS_DEFAULT:
673         case LFUN_BUFFER_PARAMS_APPLY:
674         case LFUN_LYXRC_APPLY:
675         case LFUN_BUFFER_NEXT:
676         case LFUN_BUFFER_PREVIOUS:
677         case LFUN_WINDOW_NEW:
678         case LFUN_WINDOW_CLOSE:
679         case LFUN_LYX_QUIT:
680                 // these are handled in our dispatch()
681                 break;
682
683         default:
684                 if (!getLocalStatus(cur, cmd, flag))
685                         flag = view()->getStatus(cmd);
686         }
687
688         if (!enable)
689                 flag.enabled(false);
690
691         // Can we use a readonly buffer?
692         if (buf && buf->isReadonly()
693             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
694             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
695                 flag.message(from_utf8(N_("Document is read-only")));
696                 flag.enabled(false);
697         }
698
699         // Are we in a DELETED change-tracking region?
700         if (buf && lookupChangeType(cur, true) == Change::DELETED
701             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
702             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
703                 flag.message(from_utf8(N_("This portion of the document is deleted.")));
704                 flag.enabled(false);
705         }
706
707         // the default error message if we disable the command
708         if (!flag.enabled() && flag.message().empty())
709                 flag.message(from_utf8(N_("Command disabled")));
710
711         return flag;
712 }
713
714
715 bool LyXFunc::ensureBufferClean(BufferView * bv)
716 {
717         Buffer & buf = *bv->buffer();
718         if (buf.isClean())
719                 return true;
720
721         docstring const file = makeDisplayPath(buf.fileName(), 30);
722         docstring text = bformat(_("The document %1$s has unsaved "
723                                              "changes.\n\nDo you want to save "
724                                              "the document?"), file);
725         int const ret = Alert::prompt(_("Save changed document?"),
726                                       text, 0, 1, _("&Save"),
727                                       _("&Cancel"));
728
729         if (ret == 0)
730                 dispatch(FuncRequest(LFUN_BUFFER_WRITE));
731
732         return buf.isClean();
733 }
734
735
736 namespace {
737
738 void showPrintError(string const & name)
739 {
740         docstring str = bformat(_("Could not print the document %1$s.\n"
741                                             "Check that your printer is set up correctly."),
742                              makeDisplayPath(name, 50));
743         Alert::error(_("Print document failed"), str);
744 }
745
746
747 void loadTextclass(string const & name)
748 {
749         std::pair<bool, textclass_type> const tc_pair =
750                 textclasslist.numberOfClass(name);
751
752         if (!tc_pair.first) {
753                 lyxerr << "Document class \"" << name
754                        << "\" does not exist."
755                        << std::endl;
756                 return;
757         }
758
759         textclass_type const tc = tc_pair.second;
760
761         if (!textclasslist[tc].load()) {
762                 docstring s = bformat(_("The document could not be converted\n"
763                                                   "into the document class %1$s."),
764                                    from_utf8(textclasslist[tc].name()));
765                 Alert::error(_("Could not change class"), s);
766         }
767 }
768
769
770 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
771
772 } //namespace anon
773
774
775 void LyXFunc::dispatch(FuncRequest const & cmd)
776 {
777         string const argument = to_utf8(cmd.argument());
778         kb_action const action = cmd.action;
779
780         LYXERR(Debug::ACTION) << endl << "LyXFunc::dispatch: cmd: " << cmd << endl;
781         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
782
783         // we have not done anything wrong yet.
784         errorstat = false;
785         dispatch_buffer.erase();
786
787         // redraw the screen at the end (first of the two drawing steps).
788         //This is done unless explicitely requested otherwise
789         Update::flags updateFlags = Update::FitCursor;
790
791         FuncStatus const flag = getStatus(cmd);
792         if (!flag.enabled()) {
793                 // We cannot use this function here
794                 LYXERR(Debug::ACTION) << "LyXFunc::dispatch: "
795                        << lyxaction.getActionName(action)
796                        << " [" << action << "] is disabled at this location"
797                        << endl;
798                 setErrorMessage(flag.message());
799         } else {
800                 switch (action) {
801
802                 case LFUN_WORD_FIND_FORWARD:
803                 case LFUN_WORD_FIND_BACKWARD: {
804                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
805                         static docstring last_search;
806                         docstring searched_string;
807
808                         if (!cmd.argument().empty()) {
809                                 last_search = cmd.argument();
810                                 searched_string = cmd.argument();
811                         } else {
812                                 searched_string = last_search;
813                         }
814
815                         if (searched_string.empty())
816                                 break;
817
818                         bool const fw = action == LFUN_WORD_FIND_FORWARD;
819                         docstring const data =
820                                 find2string(searched_string, true, false, fw);
821                         find(view(), FuncRequest(LFUN_WORD_FIND, data));
822                         break;
823                 }
824
825                 case LFUN_COMMAND_PREFIX:
826                         BOOST_ASSERT(lyx_view_);
827                         lyx_view_->message(keyseq->printOptions(true));
828                         break;
829
830                 case LFUN_COMMAND_EXECUTE:
831                         BOOST_ASSERT(lyx_view_);
832                         lyx_view_->getToolbars().display("minibuffer", true);
833                         lyx_view_->focus_command_buffer();
834                         break;
835
836                 case LFUN_CANCEL:
837                         BOOST_ASSERT(lyx_view_ && lyx_view_->view());
838                         keyseq->reset();
839                         meta_fake_bit = key_modifier::none;
840                         if (view()->buffer())
841                                 // cancel any selection
842                                 dispatch(FuncRequest(LFUN_MARK_OFF));
843                         setMessage(from_ascii(N_("Cancel")));
844                         break;
845
846                 case LFUN_META_PREFIX:
847                         meta_fake_bit = key_modifier::alt;
848                         setMessage(keyseq->print(true));
849                         break;
850
851                 case LFUN_BUFFER_TOGGLE_READ_ONLY:
852                         BOOST_ASSERT(lyx_view_ && lyx_view_->view() && lyx_view_->buffer());
853                         if (lyx_view_->buffer()->lyxvc().inUse())
854                                 lyx_view_->buffer()->lyxvc().toggleReadOnly();
855                         else
856                                 lyx_view_->buffer()->setReadonly(
857                                         !lyx_view_->buffer()->isReadonly());
858                         break;
859
860                 // --- Menus -----------------------------------------------
861                 case LFUN_BUFFER_NEW:
862                         menuNew(argument, false);
863                         break;
864
865                 case LFUN_BUFFER_NEW_TEMPLATE:
866                         menuNew(argument, true);
867                         break;
868
869                 case LFUN_BUFFER_CLOSE:
870                         closeBuffer();
871                         view()->update();
872                         break;
873
874                 case LFUN_BUFFER_WRITE:
875                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
876                         if (!lyx_view_->buffer()->isUnnamed()) {
877                                 docstring const str = bformat(_("Saving document %1$s..."),
878                                          makeDisplayPath(lyx_view_->buffer()->fileName()));
879                                 lyx_view_->message(str);
880                                 menuWrite(lyx_view_->buffer());
881                                 lyx_view_->message(str + _(" done."));
882                         } else {
883                                 writeAs(lyx_view_->buffer());
884                         }
885                         updateFlags = Update::None;
886                         break;
887
888                 case LFUN_BUFFER_WRITE_AS:
889                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
890                         writeAs(lyx_view_->buffer(), argument);
891                         updateFlags = Update::None;
892                         break;
893
894                 case LFUN_BUFFER_RELOAD: {
895                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
896                         docstring const file = makeDisplayPath(view()->buffer()->fileName(), 20);
897                         docstring text = bformat(_("Any changes will be lost. Are you sure "
898                                                              "you want to revert to the saved version of the document %1$s?"), file);
899                         int const ret = Alert::prompt(_("Revert to saved document?"),
900                                 text, 0, 1, _("&Revert"), _("&Cancel"));
901
902                         if (ret == 0)
903                                 reloadBuffer();
904                         break;
905                 }
906
907                 case LFUN_BUFFER_UPDATE:
908                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
909                         Exporter::Export(lyx_view_->buffer(), argument, true);
910                         break;
911
912                 case LFUN_BUFFER_VIEW:
913                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
914                         Exporter::preview(lyx_view_->buffer(), argument);
915                         break;
916
917                 case LFUN_BUILD_PROGRAM:
918                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
919                         Exporter::Export(lyx_view_->buffer(), "program", true);
920                         break;
921
922                 case LFUN_BUFFER_CHKTEX:
923                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
924                         lyx_view_->buffer()->runChktex();
925                         break;
926
927                 case LFUN_BUFFER_EXPORT:
928                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
929                         if (argument == "custom")
930                                 lyx_view_->getDialogs().show("sendto");
931                         else {
932                                 Exporter::Export(lyx_view_->buffer(), argument, false);
933                         }
934                         break;
935
936                 case LFUN_BUFFER_EXPORT_CUSTOM: {
937                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
938                         string format_name;
939                         string command = split(argument, format_name, ' ');
940                         Format const * format = formats.getFormat(format_name);
941                         if (!format) {
942                                 lyxerr << "Format \"" << format_name
943                                        << "\" not recognized!"
944                                        << std::endl;
945                                 break;
946                         }
947
948                         Buffer * buffer = lyx_view_->buffer();
949
950                         // The name of the file created by the conversion process
951                         string filename;
952
953                         // Output to filename
954                         if (format->name() == "lyx") {
955                                 string const latexname =
956                                         buffer->getLatexName(false);
957                                 filename = changeExtension(latexname,
958                                                            format->extension());
959                                 filename = addName(buffer->temppath(), filename);
960
961                                 if (!buffer->writeFile(FileName(filename)))
962                                         break;
963
964                         } else {
965                                 Exporter::Export(buffer, format_name, true, filename);
966                         }
967
968                         // Substitute $$FName for filename
969                         if (!contains(command, "$$FName"))
970                                 command = "( " + command + " ) < $$FName";
971                         command = subst(command, "$$FName", filename);
972
973                         // Execute the command in the background
974                         Systemcall call;
975                         call.startscript(Systemcall::DontWait, command);
976                         break;
977                 }
978
979                 case LFUN_BUFFER_PRINT: {
980                         BOOST_ASSERT(lyx_view_ && lyx_view_->buffer());
981                         string target;
982                         string target_name;
983                         string command = split(split(argument, target, ' '),
984                                                target_name, ' ');
985
986                         if (target.empty()
987                             || target_name.empty()
988                             || command.empty()) {
989                                 lyxerr << "Unable to parse \""
990                                        << argument << '"' << std::endl;
991                                 break;
992                         }
993                         if (target != "printer" && target != "file") {
994                                 lyxerr << "Unrecognized target \""
995                                        << target << '"' << std::endl;
996                                 break;
997                         }
998
999                         Buffer * buffer = lyx_view_->buffer();
1000
1001                         if (!Exporter::Export(buffer, "dvi", true)) {
1002                                 showPrintError(buffer->fileName());
1003                                 break;
1004                         }
1005
1006                         // Push directory path.
1007                         string const path(buffer->temppath());
1008                         // Prevent the compiler from optimizing away p
1009                         FileName pp(path);
1010                         support::Path p(pp);
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                         theServer().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().absFilename())) {
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 += Lexer::quoteString(logfile.second);
1275                                 lyx_view_->getDialogs().show("log", data);
1276                         } else if (name == "vclog") {
1277                                 string const data = "vc " +
1278                                         Lexer::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                         Inset * 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                                 InsetCommandParams icp("cite");
1384                                 icp["key"] = from_utf8(arg);
1385                                 if (!opt1.empty())
1386                                         icp["before"] = from_utf8(opt1);
1387                                 string icstr = InsetCommandMailer::params2string("citation", icp);
1388                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
1389                                 dispatch(fr);
1390                         } else
1391                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
1392                         break;
1393                 }
1394
1395                 case LFUN_BUFFER_CHILD_OPEN: {
1396                         BOOST_ASSERT(lyx_view_);
1397                         FileName const filename =
1398                                 makeAbsPath(argument, lyx_view_->buffer()->filePath());
1399                         setMessage(bformat(_("Opening child document %1$s..."),
1400                                            makeDisplayPath(filename.absFilename())));
1401                         view()->saveBookmark(false);
1402                         string const parentfilename = lyx_view_->buffer()->fileName();
1403                         if (theBufferList().exists(filename.absFilename()))
1404                                 lyx_view_->setBuffer(theBufferList().getBuffer(filename.absFilename()));
1405                         else
1406                                 lyx_view_->loadLyXFile(filename);
1407                         // Set the parent name of the child document.
1408                         // This makes insertion of citations and references in the child work,
1409                         // when the target is in the parent or another child document.
1410                         lyx_view_->buffer()->setParentName(parentfilename);
1411                         break;
1412                 }
1413
1414                 case LFUN_TOGGLE_CURSOR_FOLLOWS_SCROLLBAR:
1415                         BOOST_ASSERT(lyx_view_);
1416                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1417                         break;
1418
1419                 case LFUN_KEYMAP_OFF:
1420                         BOOST_ASSERT(lyx_view_);
1421                         lyx_view_->view()->getIntl().keyMapOn(false);
1422                         break;
1423
1424                 case LFUN_KEYMAP_PRIMARY:
1425                         BOOST_ASSERT(lyx_view_);
1426                         lyx_view_->view()->getIntl().keyMapPrim();
1427                         break;
1428
1429                 case LFUN_KEYMAP_SECONDARY:
1430                         BOOST_ASSERT(lyx_view_);
1431                         lyx_view_->view()->getIntl().keyMapSec();
1432                         break;
1433
1434                 case LFUN_KEYMAP_TOGGLE:
1435                         BOOST_ASSERT(lyx_view_);
1436                         lyx_view_->view()->getIntl().toggleKeyMap();
1437                         break;
1438
1439                 case LFUN_REPEAT: {
1440                         // repeat command
1441                         string countstr;
1442                         string rest = split(argument, countstr, ' ');
1443                         istringstream is(countstr);
1444                         int count = 0;
1445                         is >> count;
1446                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1447                         for (int i = 0; i < count; ++i)
1448                                 dispatch(lyxaction.lookupFunc(rest));
1449                         break;
1450                 }
1451
1452                 case LFUN_COMMAND_SEQUENCE: {
1453                         // argument contains ';'-terminated commands
1454                         string arg = argument;
1455                         while (!arg.empty()) {
1456                                 string first;
1457                                 arg = split(arg, first, ';');
1458                                 FuncRequest func(lyxaction.lookupFunc(first));
1459                                 func.origin = cmd.origin;
1460                                 dispatch(func);
1461                         }
1462                         break;
1463                 }
1464
1465                 case LFUN_PREFERENCES_SAVE: {
1466                         lyxrc.write(makeAbsPath("preferences",
1467                                                 package().user_support().absFilename()),
1468                                     false);
1469                         break;
1470                 }
1471
1472                 case LFUN_SCREEN_FONT_UPDATE:
1473                         BOOST_ASSERT(lyx_view_);
1474                         // handle the screen font changes.
1475                         theFontLoader().update();
1476                         /// FIXME: only the current view will be updated. the Gui
1477                         /// class is able to furnish the list of views.
1478                         updateFlags = Update::Force;
1479                         break;
1480
1481                 case LFUN_SET_COLOR: {
1482                         string lyx_name;
1483                         string const x11_name = split(argument, lyx_name, ' ');
1484                         if (lyx_name.empty() || x11_name.empty()) {
1485                                 setErrorMessage(from_ascii(N_(
1486                                                 "Syntax: set-color <lyx_name>"
1487                                                 " <x11_name>")));
1488                                 break;
1489                         }
1490
1491                         bool const graphicsbg_changed =
1492                                 (lyx_name == lcolor.getLyXName(Color::graphicsbg) &&
1493                                  x11_name != lcolor.getX11Name(Color::graphicsbg));
1494
1495                         if (!lcolor.setColor(lyx_name, x11_name)) {
1496                                 setErrorMessage(
1497                                                 bformat(_("Set-color \"%1$s\" failed "
1498                                                                        "- color is undefined or "
1499                                                                        "may not be redefined"),
1500                                                                            from_utf8(lyx_name)));
1501                                 break;
1502                         }
1503
1504                         theApp()->updateColor(lcolor.getFromLyXName(lyx_name));
1505
1506                         if (graphicsbg_changed) {
1507 #ifdef WITH_WARNINGS
1508 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1509 #endif
1510 #if 0
1511                                 graphics::GCache::get().changeDisplay(true);
1512 #endif
1513                         }
1514                         break;
1515                 }
1516
1517                 case LFUN_MESSAGE:
1518                         BOOST_ASSERT(lyx_view_);
1519                         lyx_view_->message(from_utf8(argument));
1520                         break;
1521
1522                 case LFUN_EXTERNAL_EDIT: {
1523                         BOOST_ASSERT(lyx_view_);
1524                         FuncRequest fr(action, argument);
1525                         InsetExternal().dispatch(view()->cursor(), fr);
1526                         break;
1527                 }
1528
1529                 case LFUN_GRAPHICS_EDIT: {
1530                         FuncRequest fr(action, argument);
1531                         InsetGraphics().dispatch(view()->cursor(), fr);
1532                         break;
1533                 }
1534
1535                 case LFUN_INSET_APPLY: {
1536                         BOOST_ASSERT(lyx_view_);
1537                         string const name = cmd.getArg(0);
1538                         Inset * inset = lyx_view_->getDialogs().getOpenInset(name);
1539                         if (inset) {
1540                                 FuncRequest fr(LFUN_INSET_MODIFY, argument);
1541                                 inset->dispatch(view()->cursor(), fr);
1542                         } else {
1543                                 FuncRequest fr(LFUN_INSET_INSERT, argument);
1544                                 dispatch(fr);
1545                         }
1546                         // ideally, the update flag should be set by the insets,
1547                         // but this is not possible currently
1548                         updateFlags = Update::Force | Update::FitCursor;
1549                         break;
1550                 }
1551
1552                 case LFUN_ALL_INSETS_TOGGLE: {
1553                         BOOST_ASSERT(lyx_view_);
1554                         string action;
1555                         string const name = split(argument, action, ' ');
1556                         Inset::Code const inset_code =
1557                                 Inset::translate(name);
1558
1559                         Cursor & cur = view()->cursor();
1560                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1561
1562                         Inset & inset = lyx_view_->buffer()->inset();
1563                         InsetIterator it  = inset_iterator_begin(inset);
1564                         InsetIterator const end = inset_iterator_end(inset);
1565                         for (; it != end; ++it) {
1566                                 if (!it->asInsetMath()
1567                                     && (inset_code == Inset::NO_CODE
1568                                     || inset_code == it->lyxCode())) {
1569                                         Cursor tmpcur = cur;
1570                                         tmpcur.pushLeft(*it);
1571                                         it->dispatch(tmpcur, fr);
1572                                 }
1573                         }
1574                         updateFlags = Update::Force | Update::FitCursor;
1575                         break;
1576                 }
1577
1578                 case LFUN_BUFFER_LANGUAGE: {
1579                         BOOST_ASSERT(lyx_view_);
1580                         Buffer & buffer = *lyx_view_->buffer();
1581                         Language const * oldL = buffer.params().language;
1582                         Language const * newL = languages.getLanguage(argument);
1583                         if (!newL || oldL == newL)
1584                                 break;
1585
1586                         if (oldL->rightToLeft() == newL->rightToLeft()
1587                             && !buffer.isMultiLingual())
1588                                 buffer.changeLanguage(oldL, newL);
1589                         break;
1590                 }
1591
1592                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
1593                         string const fname =
1594                                 addName(addPath(package().user_support().absFilename(), "templates/"),
1595                                         "defaults.lyx");
1596                         Buffer defaults(fname);
1597
1598                         istringstream ss(argument);
1599                         Lexer 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                         Lexer 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                         Cursor & cur = view()->cursor();
1639                         FuncRequest fr(LFUN_INSET_REFRESH);
1640
1641                         Inset & 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() == Inset::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().absFilename(), "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().absFilename(), "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         // goto bookmark to update bookmark pit.
2044         for (size_t i = 0; i < LyX::ref().session().bookmarks().size(); ++i)
2045                 gotoBookmark(i+1, false, false);
2046         if (theBufferList().close(lyx_view_->buffer(), true) && !quitting) {
2047                 if (theBufferList().empty()) {
2048                         // need this otherwise SEGV may occur while
2049                         // trying to set variables that don't exist
2050                         // since there's no current buffer
2051                         lyx_view_->getDialogs().hideBufferDependent();
2052                 } else {
2053                         lyx_view_->setBuffer(theBufferList().first());
2054                 }
2055         }
2056 }
2057
2058
2059 void LyXFunc::reloadBuffer()
2060 {
2061         FileName filename(lyx_view_->buffer()->fileName());
2062         closeBuffer();
2063         lyx_view_->loadLyXFile(filename);
2064 }
2065
2066 // Each "lyx_view_" should have it's own message method. lyxview and
2067 // the minibuffer would use the minibuffer, but lyxserver would
2068 // send an ERROR signal to its client.  Alejandro 970603
2069 // This function is bit problematic when it comes to NLS, to make the
2070 // lyx servers client be language indepenent we must not translate
2071 // strings sent to this func.
2072 void LyXFunc::setErrorMessage(docstring const & m) const
2073 {
2074         dispatch_buffer = m;
2075         errorstat = true;
2076 }
2077
2078
2079 void LyXFunc::setMessage(docstring const & m) const
2080 {
2081         dispatch_buffer = m;
2082 }
2083
2084
2085 docstring const LyXFunc::viewStatusMessage()
2086 {
2087         // When meta-fake key is pressed, show the key sequence so far + "M-".
2088         if (wasMetaKey())
2089                 return keyseq->print(true) + "M-";
2090
2091         // Else, when a non-complete key sequence is pressed,
2092         // show the available options.
2093         if (keyseq->length() > 0 && !keyseq->deleted())
2094                 return keyseq->printOptions(true);
2095
2096         if (!view()->buffer())
2097                 return _("Welcome to LyX!");
2098
2099         return view()->cursor().currentState();
2100 }
2101
2102
2103 BufferView * LyXFunc::view() const
2104 {
2105         BOOST_ASSERT(lyx_view_);
2106         return lyx_view_->view();
2107 }
2108
2109
2110 bool LyXFunc::wasMetaKey() const
2111 {
2112         return (meta_fake_bit != key_modifier::none);
2113 }
2114
2115
2116 namespace {
2117
2118 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
2119 {
2120         // Why the switch you might ask. It is a trick to ensure that all
2121         // the elements in the LyXRCTags enum is handled. As you can see
2122         // there are no breaks at all. So it is just a huge fall-through.
2123         // The nice thing is that we will get a warning from the compiler
2124         // if we forget an element.
2125         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
2126         switch (tag) {
2127         case LyXRC::RC_ACCEPT_COMPOUND:
2128         case LyXRC::RC_ALT_LANG:
2129         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
2130         case LyXRC::RC_PLAINTEXT_LINELEN:
2131         case LyXRC::RC_AUTOREGIONDELETE:
2132         case LyXRC::RC_AUTORESET_OPTIONS:
2133         case LyXRC::RC_AUTOSAVE:
2134         case LyXRC::RC_AUTO_NUMBER:
2135         case LyXRC::RC_BACKUPDIR_PATH:
2136         case LyXRC::RC_BIBTEX_COMMAND:
2137         case LyXRC::RC_BINDFILE:
2138         case LyXRC::RC_CHECKLASTFILES:
2139         case LyXRC::RC_USELASTFILEPOS:
2140         case LyXRC::RC_LOADSESSION:
2141         case LyXRC::RC_CHKTEX_COMMAND:
2142         case LyXRC::RC_CONVERTER:
2143         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
2144         case LyXRC::RC_COPIER:
2145         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
2146         case LyXRC::RC_CUSTOM_EXPORT_COMMAND:
2147         case LyXRC::RC_CUSTOM_EXPORT_FORMAT:
2148         case LyXRC::RC_DATE_INSERT_FORMAT:
2149         case LyXRC::RC_DEFAULT_LANGUAGE:
2150         case LyXRC::RC_DEFAULT_PAPERSIZE:
2151         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
2152         case LyXRC::RC_DISPLAY_GRAPHICS:
2153         case LyXRC::RC_DOCUMENTPATH:
2154                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
2155                         string const encoded = FileName(
2156                                 lyxrc_new.document_path).toFilesystemEncoding();
2157                         if (fs::exists(encoded) && fs::is_directory(encoded))
2158                                 support::package().document_dir() = FileName(lyxrc.document_path);
2159                 }
2160         case LyXRC::RC_ESC_CHARS:
2161         case LyXRC::RC_FONT_ENCODING:
2162         case LyXRC::RC_FORMAT:
2163         case LyXRC::RC_INDEX_COMMAND:
2164         case LyXRC::RC_INPUT:
2165         case LyXRC::RC_KBMAP:
2166         case LyXRC::RC_KBMAP_PRIMARY:
2167         case LyXRC::RC_KBMAP_SECONDARY:
2168         case LyXRC::RC_LABEL_INIT_LENGTH:
2169         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
2170         case LyXRC::RC_LANGUAGE_AUTO_END:
2171         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
2172         case LyXRC::RC_LANGUAGE_COMMAND_END:
2173         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
2174         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
2175         case LyXRC::RC_LANGUAGE_PACKAGE:
2176         case LyXRC::RC_LANGUAGE_USE_BABEL:
2177         case LyXRC::RC_MAKE_BACKUP:
2178         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
2179         case LyXRC::RC_NUMLASTFILES:
2180         case LyXRC::RC_PATH_PREFIX:
2181                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
2182                         support::prependEnvPath("PATH", lyxrc.path_prefix);
2183                 }
2184         case LyXRC::RC_PERS_DICT:
2185         case LyXRC::RC_PREVIEW:
2186         case LyXRC::RC_PREVIEW_HASHED_LABELS:
2187         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
2188         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
2189         case LyXRC::RC_PRINTCOPIESFLAG:
2190         case LyXRC::RC_PRINTER:
2191         case LyXRC::RC_PRINTEVENPAGEFLAG:
2192         case LyXRC::RC_PRINTEXSTRAOPTIONS:
2193         case LyXRC::RC_PRINTFILEEXTENSION:
2194         case LyXRC::RC_PRINTLANDSCAPEFLAG:
2195         case LyXRC::RC_PRINTODDPAGEFLAG:
2196         case LyXRC::RC_PRINTPAGERANGEFLAG:
2197         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
2198         case LyXRC::RC_PRINTPAPERFLAG:
2199         case LyXRC::RC_PRINTREVERSEFLAG:
2200         case LyXRC::RC_PRINTSPOOL_COMMAND:
2201         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
2202         case LyXRC::RC_PRINTTOFILE:
2203         case LyXRC::RC_PRINTTOPRINTER:
2204         case LyXRC::RC_PRINT_ADAPTOUTPUT:
2205         case LyXRC::RC_PRINT_COMMAND:
2206         case LyXRC::RC_RTL_SUPPORT:
2207         case LyXRC::RC_SCREEN_DPI:
2208         case LyXRC::RC_SCREEN_FONT_ROMAN:
2209         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
2210         case LyXRC::RC_SCREEN_FONT_SANS:
2211         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
2212         case LyXRC::RC_SCREEN_FONT_SCALABLE:
2213         case LyXRC::RC_SCREEN_FONT_SIZES:
2214         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
2215         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
2216         case LyXRC::RC_SCREEN_GEOMETRY_HEIGHT:
2217         case LyXRC::RC_SCREEN_GEOMETRY_WIDTH:
2218         case LyXRC::RC_SCREEN_GEOMETRY_XYSAVED:
2219         case LyXRC::RC_SCREEN_ZOOM:
2220         case LyXRC::RC_SERVERPIPE:
2221         case LyXRC::RC_SET_COLOR:
2222         case LyXRC::RC_SHOW_BANNER:
2223         case LyXRC::RC_SPELL_COMMAND:
2224         case LyXRC::RC_TEMPDIRPATH:
2225         case LyXRC::RC_TEMPLATEPATH:
2226         case LyXRC::RC_TEX_ALLOWS_SPACES:
2227         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
2228                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
2229                         support::os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
2230                 }
2231         case LyXRC::RC_UIFILE:
2232         case LyXRC::RC_USER_EMAIL:
2233         case LyXRC::RC_USER_NAME:
2234         case LyXRC::RC_USETEMPDIR:
2235         case LyXRC::RC_USE_ALT_LANG:
2236         case LyXRC::RC_USE_CONVERTER_CACHE:
2237         case LyXRC::RC_USE_ESC_CHARS:
2238         case LyXRC::RC_USE_INP_ENC:
2239         case LyXRC::RC_USE_PERS_DICT:
2240         case LyXRC::RC_USE_SPELL_LIB:
2241         case LyXRC::RC_VIEWDVI_PAPEROPTION:
2242         case LyXRC::RC_VIEWER:
2243         case LyXRC::RC_LAST:
2244                 break;
2245         }
2246 }
2247
2248 } // namespace anon
2249
2250
2251 } // namespace lyx