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