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