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