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