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