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