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