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