]> git.lyx.org Git - lyx.git/blob - src/lyxfunc.C
tostr -> convert and some bformat work
[lyx.git] / src / lyxfunc.C
1 /**
2  * \file lyxfunc.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "lyxfunc.h"
23
24 #include "BranchList.h"
25 #include "buffer.h"
26 #include "buffer_funcs.h"
27 #include "bufferlist.h"
28 #include "bufferparams.h"
29 #include "BufferView.h"
30 #include "cursor.h"
31 #include "CutAndPaste.h"
32 #include "debug.h"
33 #include "dispatchresult.h"
34 #include "encoding.h"
35 #include "errorlist.h"
36 #include "exporter.h"
37 #include "format.h"
38 #include "funcrequest.h"
39 #include "gettext.h"
40 #include "importer.h"
41 #include "insetiterator.h"
42 #include "intl.h"
43 #include "kbmap.h"
44 #include "language.h"
45 #include "LColor.h"
46 #include "lyx_cb.h"
47 #include "LyXAction.h"
48 #include "lyxfind.h"
49 #include "lyxlex.h"
50 #include "lyxrc.h"
51 #include "lyxrow.h"
52 #include "lyxserver.h"
53 #include "lyxtextclasslist.h"
54 #include "lyxvc.h"
55 #include "paragraph.h"
56 #include "pariterator.h"
57 #include "ParagraphParameters.h"
58 #include "undo.h"
59
60 #include "insets/insetbox.h"
61 #include "insets/insetbranch.h"
62 #include "insets/insetcommand.h"
63 #include "insets/insetert.h"
64 #include "insets/insetexternal.h"
65 #include "insets/insetfloat.h"
66 #include "insets/insetgraphics.h"
67 #include "insets/insetnote.h"
68 #include "insets/insettabular.h"
69 #include "insets/insetvspace.h"
70 #include "insets/insetwrap.h"
71
72 #include "frontends/Alert.h"
73 #include "frontends/Dialogs.h"
74 #include "frontends/FileDialog.h"
75 #include "frontends/lyx_gui.h"
76 #include "frontends/LyXKeySym.h"
77 #include "frontends/LyXView.h"
78 #include "frontends/Menubar.h"
79 #include "frontends/Toolbars.h"
80
81 #include "support/filefilterlist.h"
82 #include "support/FileInfo.h"
83 #include "support/filetools.h"
84 #include "support/forkedcontr.h"
85 #include "support/lstrings.h"
86 #include "support/path.h"
87 #include "support/path_defines.h"
88 #include "support/systemcall.h"
89 #include "support/tostr.h"
90 #include "support/os.h"
91
92 #include <sstream>
93
94 using bv_funcs::freefont2string;
95
96 using lyx::support::AddName;
97 using lyx::support::AddPath;
98 using lyx::support::bformat;
99 using lyx::support::ChangeExtension;
100 using lyx::support::contains;
101 using lyx::support::FileFilterList;
102 using lyx::support::FileInfo;
103 using lyx::support::FileSearch;
104 using lyx::support::ForkedcallsController;
105 using lyx::support::i18nLibFileSearch;
106 using lyx::support::IsDirWriteable;
107 using lyx::support::IsFileReadable;
108 using lyx::support::isStrInt;
109 using lyx::support::MakeAbsPath;
110 using lyx::support::MakeDisplayPath;
111 using lyx::support::Path;
112 using lyx::support::QuoteName;
113 using lyx::support::rtrim;
114 using lyx::support::split;
115 using lyx::support::strToInt;
116 using lyx::support::strToUnsignedInt;
117 using lyx::support::subst;
118 using lyx::support::system_lyxdir;
119 using lyx::support::Systemcall;
120 using lyx::support::token;
121 using lyx::support::trim;
122 using lyx::support::user_lyxdir;
123 using lyx::support::prefixIs;
124 using lyx::support::os::getTmpDir;
125
126 using std::endl;
127 using std::make_pair;
128 using std::pair;
129 using std::string;
130 using std::istringstream;
131
132 namespace biblio = lyx::biblio;
133
134
135 extern BufferList bufferlist;
136 extern LyXServer * lyxserver;
137
138 extern boost::scoped_ptr<kb_keymap> toplevel_keymap;
139
140 // (alkis)
141 extern tex_accent_struct get_accent(kb_action action);
142
143
144 namespace {
145
146 bool getStatus(LCursor cursor,
147                FuncRequest const & cmd, FuncStatus & status)
148 {
149         // This is, of course, a mess. Better create a new doc iterator and use
150         // this in Inset::getStatus. This might require an additional
151         // BufferView * arg, though (which should be avoided)
152         //LCursor safe = *this;
153         bool res = false;
154         for ( ; cursor.size(); cursor.pop()) {
155                 //lyxerr << "\nLCursor::getStatus: cmd: " << cmd << endl << *this << endl;
156                 DocIterator::idx_type & idx = cursor.idx();
157                 DocIterator::idx_type const lastidx = cursor.lastidx();
158
159                 if (idx > lastidx) {
160                         lyxerr << "wrong idx " << idx << ", max is " << lastidx
161                                 << ". Trying to correct this."  << endl;
162                         idx = lastidx;
163                 }
164
165                 DocIterator::pit_type & pit = cursor.pit();
166                 DocIterator::pit_type const lastpit = cursor.lastpit();
167
168                 if (pit > lastpit) {
169                         lyxerr << "wrong par " << pit << ", max is " << lastpit
170                                 << ". Trying to correct this."  << endl;
171                         pit = lastpit;
172                 }
173
174                 DocIterator::pos_type & pos = cursor.pos();
175                 DocIterator::pos_type const lastpos = cursor.lastpos();
176
177                 if (pos > lastpos) {
178                         lyxerr << "wrong pos " << pos << ", max is " << lastpos
179                                 << ". Trying to correct this."  << endl;
180                         pos = lastpos;
181                 }
182
183                 // The inset's getStatus() will return 'true' if it made
184                 // a definitive decision on whether it want to handle the
185                 // request or not. The result of this decision is put into
186                 // the 'status' parameter.
187                 if (cursor.inset().getStatus(cursor, cmd, status)) {
188                         res = true;
189                         break;
190                 }
191         }
192         return res;
193 }
194
195 }
196
197 LyXFunc::LyXFunc(LyXView * lv)
198         : owner(lv),
199         encoded_last_key(0),
200         keyseq(toplevel_keymap.get(), toplevel_keymap.get()),
201         cancel_meta_seq(toplevel_keymap.get(), toplevel_keymap.get()),
202         meta_fake_bit(key_modifier::none)
203 {
204 }
205
206
207 void LyXFunc::handleKeyFunc(kb_action action)
208 {
209         char c = encoded_last_key;
210
211         if (keyseq.length()) {
212                 c = 0;
213         }
214
215         owner->getIntl().getTransManager()
216                 .deadkey(c, get_accent(action).accent, view()->getLyXText());
217         // Need to clear, in case the minibuffer calls these
218         // actions
219         keyseq.clear();
220         // copied verbatim from do_accent_char
221         view()->cursor().resetAnchor();
222         view()->update();
223 }
224
225
226 void LyXFunc::processKeySym(LyXKeySymPtr keysym, key_modifier::state state)
227 {
228         lyxerr[Debug::KEY] << "KeySym is " << keysym->getSymbolName() << endl;
229
230         // Do nothing if we have nothing (JMarc)
231         if (!keysym->isOK()) {
232                 lyxerr[Debug::KEY] << "Empty kbd action (probably composing)"
233                                    << endl;
234                 return;
235         }
236
237         if (keysym->isModifier()) {
238                 lyxerr[Debug::KEY] << "isModifier true" << endl;
239                 return;
240         }
241
242         Encoding const * encoding = view()->cursor().getEncoding();
243
244         encoded_last_key = keysym->getISOEncoded(encoding ? encoding->Name() : "");
245
246         // Do a one-deep top-level lookup for
247         // cancel and meta-fake keys. RVDK_PATCH_5
248         cancel_meta_seq.reset();
249
250         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
251         lyxerr[Debug::KEY] << "action first set to [" << func.action << ']' << endl;
252
253         // When not cancel or meta-fake, do the normal lookup.
254         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
255         // Mostly, meta_fake_bit = key_modifier::none. RVDK_PATCH_5.
256         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_FAKE)) {
257                 // remove Caps Lock and Mod2 as a modifiers
258                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
259                 lyxerr[Debug::KEY] << "action now set to ["
260                         << func.action << ']' << endl;
261         }
262
263         // Dont remove this unless you know what you are doing.
264         meta_fake_bit = key_modifier::none;
265
266         // can this happen now ?
267         if (func.action == LFUN_NOACTION) {
268                 func = FuncRequest(LFUN_PREFIX);
269         }
270
271         if (lyxerr.debugging(Debug::KEY)) {
272                 lyxerr << "Key [action="
273                        << func.action << "]["
274                        << keyseq.print() << ']'
275                        << endl;
276         }
277
278         // already here we know if it any point in going further
279         // why not return already here if action == -1 and
280         // num_bytes == 0? (Lgb)
281
282         if (keyseq.length() > 1) {
283                 owner->message(keyseq.print());
284         }
285
286
287         // Maybe user can only reach the key via holding down shift.
288         // Let's see. But only if shift is the only modifier
289         if (func.action == LFUN_UNKNOWN_ACTION &&
290             state == key_modifier::shift) {
291                 lyxerr[Debug::KEY] << "Trying without shift" << endl;
292                 func = keyseq.addkey(keysym, key_modifier::none);
293                 lyxerr[Debug::KEY] << "Action now " << func.action << endl;
294         }
295
296         if (func.action == LFUN_UNKNOWN_ACTION) {
297                 // Hmm, we didn't match any of the keysequences. See
298                 // if it's normal insertable text not already covered
299                 // by a binding
300                 if (keysym->isText() && keyseq.length() == 1) {
301                         lyxerr[Debug::KEY] << "isText() is true, inserting." << endl;
302                         func = FuncRequest(LFUN_SELFINSERT);
303                 } else {
304                         lyxerr[Debug::KEY] << "Unknown, !isText() - giving up" << endl;
305                         owner->message(_("Unknown function."));
306                         return;
307                 }
308         }
309
310         if (func.action == LFUN_SELFINSERT) {
311                 if (encoded_last_key != 0) {
312                         string arg(1, encoded_last_key);
313                         dispatch(FuncRequest(LFUN_SELFINSERT, arg));
314                         lyxerr[Debug::KEY]
315                                 << "SelfInsert arg[`" << arg << "']" << endl;
316                 }
317         } else {
318                 dispatch(func);
319         }
320 }
321
322
323 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
324 {
325         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
326         FuncStatus flag;
327         LCursor & cur = view()->cursor();
328
329         /* In LyX/Mac, when a dialog is open, the menus of the
330            application can still be accessed without giving focus to
331            the main window. In this case, we want to disable the menu
332            entries that are buffer-related.
333         */
334         Buffer * buf;
335         if (cmd.origin == FuncRequest::UI && !owner->hasFocus())
336                 buf = 0;
337         else
338                 buf = owner->buffer();
339
340         if (cmd.action == LFUN_NOACTION) {
341                 flag.message(N_("Nothing to do"));
342                 flag.enabled(false);
343                 return flag;
344         }
345
346         switch (cmd.action) {
347         case LFUN_UNKNOWN_ACTION:
348 #ifndef HAVE_LIBAIKSAURUS
349         case LFUN_THESAURUS_ENTRY:
350 #endif
351                 flag.unknown(true);
352                 flag.enabled(false);
353                 break;
354         default:
355                 flag |= lyx_gui::getStatus(cmd);
356         }
357
358         if (flag.unknown()) {
359                 flag.message(N_("Unknown action"));
360                 return flag;
361         }
362
363         if (!flag.enabled()) {
364                 if (flag.message().empty())
365                         flag.message(N_("Command disabled"));
366                 return flag;
367         }
368
369         // Check whether we need a buffer
370         if (!lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer) && !buf) {
371                 // no, exit directly
372                 flag.message(N_("Command not allowed with"
373                                     "out any document open"));
374                 flag.enabled(false);
375                 return flag;
376         }
377
378         // I would really like to avoid having this switch and rather try to
379         // encode this in the function itself.
380         // -- And I'd rather let an inset decide which LFUNs it is willing
381         // to handle (Andre')
382         bool enable = true;
383         switch (cmd.action) {
384         case LFUN_TOOLTIPS_TOGGLE:
385                 flag.setOnOff(owner->getDialogs().tooltipsEnabled());
386                 break;
387
388         case LFUN_READ_ONLY_TOGGLE:
389                 flag.setOnOff(buf->isReadonly());
390                 break;
391
392         case LFUN_SWITCHBUFFER:
393                 // toggle on the current buffer, but do not toggle off
394                 // the other ones (is that a good idea?)
395                 if (cmd.argument == buf->fileName())
396                         flag.setOnOff(true);
397                 break;
398
399         case LFUN_EXPORT:
400                 enable = cmd.argument == "custom"
401                         || Exporter::IsExportable(*buf, cmd.argument);
402                 break;
403         case LFUN_CUT:
404         case LFUN_COPY:
405                 enable = cur.selection();
406                 break;
407
408         case LFUN_RUNCHKTEX:
409                 enable = buf->isLatex() && lyxrc.chktex_command != "none";
410                 break;
411
412         case LFUN_BUILDPROG:
413                 enable = Exporter::IsExportable(*buf, "program");
414                 break;
415
416         case LFUN_LAYOUT_TABULAR:
417                 enable = cur.innerInsetOfType(InsetBase::TABULAR_CODE);
418                 break;
419
420         case LFUN_LAYOUT:
421         case LFUN_LAYOUT_PARAGRAPH:
422                 enable = !cur.inset().forceDefaultParagraphs(&cur.inset());
423                 break;
424
425         case LFUN_VC_REGISTER:
426                 enable = !buf->lyxvc().inUse();
427                 break;
428         case LFUN_VC_CHECKIN:
429                 enable = buf->lyxvc().inUse() && !buf->isReadonly();
430                 break;
431         case LFUN_VC_CHECKOUT:
432                 enable = buf->lyxvc().inUse() && buf->isReadonly();
433                 break;
434         case LFUN_VC_REVERT:
435         case LFUN_VC_UNDO:
436                 enable = buf->lyxvc().inUse();
437                 break;
438         case LFUN_MENURELOAD:
439                 enable = !buf->isUnnamed() && !buf->isClean();
440                 break;
441
442
443         case LFUN_INSET_SETTINGS: {
444                 enable = false;
445                 if (!cur.size())
446                         break;
447                 UpdatableInset * inset = cur.inset().asUpdatableInset();
448                 lyxerr << "inset: " << inset << endl;
449                 if (!inset)
450                         break;
451
452                 InsetBase::Code code = inset->lyxCode();
453                 switch (code) {
454                         case InsetBase::TABULAR_CODE:
455                                 enable = cmd.argument == "tabular";
456                                 break;
457                         case InsetBase::ERT_CODE:
458                                 enable = cmd.argument == "ert";
459                                 break;
460                         case InsetBase::FLOAT_CODE:
461                                 enable = cmd.argument == "float";
462                                 break;
463                         case InsetBase::WRAP_CODE:
464                                 enable = cmd.argument == "wrap";
465                                 break;
466                         case InsetBase::NOTE_CODE:
467                                 enable = cmd.argument == "note";
468                                 break;
469                         case InsetBase::BRANCH_CODE:
470                                 enable = cmd.argument == "branch";
471                                 break;
472                         case InsetBase::BOX_CODE:
473                                 enable = cmd.argument == "box";
474                                 break;
475                         default:
476                                 break;
477                 }
478                 break;
479         }
480
481         case LFUN_DIALOG_SHOW: {
482                 string const name = cmd.getArg(0);
483                 if (!buf)
484                         enable = name == "aboutlyx"
485                                 || name == "file"
486                                 || name == "forks"
487                                 || name == "prefs"
488                                 || name == "texinfo";
489                 else if (name == "print")
490                         enable = Exporter::IsExportable(*buf, "dvi")
491                                 && lyxrc.print_command != "none";
492                 else if (name == "character")
493                         enable = cur.inset().lyxCode() != InsetBase::ERT_CODE;
494                 else if (name == "vclog")
495                         enable = buf->lyxvc().inUse();
496                 else if (name == "latexlog")
497                         enable = IsFileReadable(buf->getLogName().second);
498                 break;
499         }
500
501         case LFUN_DIALOG_UPDATE: {
502                 string const name = cmd.getArg(0);
503                 if (!buf)
504                         enable = name == "prefs";
505                 break;
506         }
507
508         // this one is difficult to get right. As a half-baked
509         // solution, we consider only the first action of the sequence
510         case LFUN_SEQUENCE: {
511                 // argument contains ';'-terminated commands
512                 string const firstcmd = token(cmd.argument, ';', 0);
513                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
514                 func.origin = cmd.origin;
515                 flag = getStatus(func);
516         }
517
518         case LFUN_MENUNEW:
519         case LFUN_MENUNEWTMPLT:
520         case LFUN_WORDFINDFORWARD:
521         case LFUN_WORDFINDBACKWARD:
522         case LFUN_PREFIX:
523         case LFUN_EXEC_COMMAND:
524         case LFUN_CANCEL:
525         case LFUN_META_FAKE:
526         case LFUN_CLOSEBUFFER:
527         case LFUN_MENUWRITE:
528         case LFUN_WRITEAS:
529         case LFUN_UPDATE:
530         case LFUN_PREVIEW:
531         case LFUN_IMPORT:
532         case LFUN_QUIT:
533         case LFUN_TOCVIEW:
534         case LFUN_AUTOSAVE:
535         case LFUN_RECONFIGURE:
536         case LFUN_HELP_OPEN:
537         case LFUN_FILE_NEW:
538         case LFUN_FILE_OPEN:
539         case LFUN_DROP_LAYOUTS_CHOICE:
540         case LFUN_MENU_OPEN_BY_NAME:
541         case LFUN_GETNAME:
542         case LFUN_NOTIFY:
543         case LFUN_GOTOFILEROW:
544         case LFUN_GOTO_PARAGRAPH:
545         case LFUN_DIALOG_SHOW_NEW_INSET:
546         case LFUN_DIALOG_SHOW_NEXT_INSET:
547         case LFUN_DIALOG_HIDE:
548         case LFUN_DIALOG_DISCONNECT_INSET:
549         case LFUN_CHILDOPEN:
550         case LFUN_TOGGLECURSORFOLLOW:
551         case LFUN_KMAP_OFF:
552         case LFUN_KMAP_PRIM:
553         case LFUN_KMAP_SEC:
554         case LFUN_KMAP_TOGGLE:
555         case LFUN_REPEAT:
556         case LFUN_EXPORT_CUSTOM:
557         case LFUN_PRINT:
558         case LFUN_SAVEPREFERENCES:
559         case LFUN_SCREEN_FONT_UPDATE:
560         case LFUN_SET_COLOR:
561         case LFUN_MESSAGE:
562         case LFUN_EXTERNAL_EDIT:
563         case LFUN_GRAPHICS_EDIT:
564         case LFUN_ALL_INSETS_TOGGLE:
565         case LFUN_LANGUAGE_BUFFER:
566         case LFUN_TEXTCLASS_APPLY:
567         case LFUN_TEXTCLASS_LOAD:
568         case LFUN_SAVE_AS_DEFAULT:
569         case LFUN_BUFFERPARAMS_APPLY:
570         case LFUN_LYXRC_APPLY:
571         case LFUN_NEXTBUFFER:
572         case LFUN_PREVIOUSBUFFER:
573                 // these are handled in our dispatch()
574                 break;
575
576         default:
577
578                 if (!::getStatus(cur, cmd, flag))
579                         flag = view()->getStatus(cmd);
580         }
581
582         if (!enable)
583                 flag.enabled(false);
584
585         // Can we use a readonly buffer?
586         if (buf && buf->isReadonly()
587             && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly)
588             && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)) {
589                 flag.message(N_("Document is read-only"));
590                 flag.enabled(false);
591         }
592
593         // the default error message if we disable the command
594         if (!flag.enabled() && flag.message().empty())
595                 flag.message(N_("Command disabled"));
596
597         return flag;
598 }
599
600
601 namespace {
602
603 bool ensureBufferClean(BufferView * bv)
604 {
605         Buffer & buf = *bv->buffer();
606         if (buf.isClean())
607                 return true;
608
609         string const file = MakeDisplayPath(buf.fileName(), 30);
610         string text = bformat(_("The document %1$s has unsaved "
611                                 "changes.\n\nDo you want to save "
612                                 "the document?"), file);
613         int const ret = Alert::prompt(_("Save changed document?"),
614                                       text, 0, 1, _("&Save"),
615                                       _("&Cancel"));
616
617         if (ret == 0)
618                 bv->owner()->dispatch(FuncRequest(LFUN_MENUWRITE));
619
620         return buf.isClean();
621 }
622
623
624 void showPrintError(string const & name)
625 {
626         string str = bformat(_("Could not print the document %1$s.\n"
627                                "Check that your printer is set up correctly."),
628                              MakeDisplayPath(name, 50));
629         Alert::error(_("Print document failed"), str);
630 }
631
632
633 void loadTextclass(string const & name)
634 {
635         std::pair<bool, lyx::textclass_type> const tc_pair =
636                 textclasslist.NumberOfClass(name);
637
638         if (!tc_pair.first) {
639                 lyxerr << "Document class \"" << name
640                        << "\" does not exist."
641                        << std::endl;
642                 return;
643         }
644
645         lyx::textclass_type const tc = tc_pair.second;
646
647         if (!textclasslist[tc].load()) {
648                 string s = bformat(_("The document could not be converted\n"
649                                      "into the document class %1$s."),
650                                    textclasslist[tc].name());
651                 Alert::error(_("Could not change class"), s);
652         }
653 }
654
655 } //namespace anon
656
657
658 void LyXFunc::dispatch(FuncRequest const & cmd)
659 {
660         BOOST_ASSERT(view());
661         string const argument = cmd.argument;
662         kb_action const action = cmd.action;
663
664         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: cmd: " << cmd << endl;
665         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
666
667         // we have not done anything wrong yet.
668         errorstat = false;
669         dispatch_buffer.erase();
670
671         bool update = true;
672
673         FuncStatus const flag = getStatus(cmd);
674         if (!flag.enabled()) {
675                 // We cannot use this function here
676                 lyxerr[Debug::ACTION] << "LyXFunc::dispatch: "
677                        << lyxaction.getActionName(action)
678                        << " [" << action << "] is disabled at this location"
679                        << endl;
680                 setErrorMessage(flag.message());
681         } else {
682
683                 if (view()->available())
684                         view()->hideCursor();
685
686                 switch (action) {
687
688                 case LFUN_WORDFINDFORWARD:
689                 case LFUN_WORDFINDBACKWARD: {
690                         static string last_search;
691                         string searched_string;
692
693                         if (!argument.empty()) {
694                                 last_search = argument;
695                                 searched_string = argument;
696                         } else {
697                                 searched_string = last_search;
698                         }
699
700                         if (searched_string.empty())
701                                 break;
702
703                         bool const fw = action == LFUN_WORDFINDFORWARD;
704                         string const data =
705                                 lyx::find::find2string(searched_string, true, false, fw);
706                         lyx::find::find(view(), FuncRequest(LFUN_WORD_FIND, data));
707                         break;
708                 }
709
710                 case LFUN_PREFIX:
711                         owner->message(keyseq.printOptions());
712                         break;
713
714                 case LFUN_EXEC_COMMAND:
715                         owner->getToolbars().display("minibuffer", true);
716                         owner->focus_command_buffer();
717                         break;
718
719                 case LFUN_CANCEL:
720                         keyseq.reset();
721                         meta_fake_bit = key_modifier::none;
722                         if (view()->available())
723                                 // cancel any selection
724                                 dispatch(FuncRequest(LFUN_MARK_OFF));
725                         setMessage(N_("Cancel"));
726                         break;
727
728                 case LFUN_META_FAKE:
729                         meta_fake_bit = key_modifier::alt;
730                         setMessage(keyseq.print());
731                         break;
732
733                 case LFUN_READ_ONLY_TOGGLE:
734                         if (owner->buffer()->lyxvc().inUse())
735                                 owner->buffer()->lyxvc().toggleReadOnly();
736                         else
737                                 owner->buffer()->setReadonly(
738                                         !owner->buffer()->isReadonly());
739                         break;
740
741                 // --- Menus -----------------------------------------------
742                 case LFUN_MENUNEW:
743                         menuNew(argument, false);
744                         break;
745
746                 case LFUN_MENUNEWTMPLT:
747                         menuNew(argument, true);
748                         break;
749
750                 case LFUN_CLOSEBUFFER:
751                         closeBuffer();
752                         break;
753
754                 case LFUN_MENUWRITE:
755                         if (!owner->buffer()->isUnnamed()) {
756                                 string const str = bformat(_("Saving document %1$s..."),
757                                          MakeDisplayPath(owner->buffer()->fileName()));
758                                 owner->message(str);
759                                 MenuWrite(owner->buffer());
760                                 owner->message(str + _(" done."));
761                         } else
762                                 WriteAs(owner->buffer());
763                         break;
764
765                 case LFUN_WRITEAS:
766                         WriteAs(owner->buffer(), argument);
767                         break;
768
769                 case LFUN_MENURELOAD: {
770                         string const file = MakeDisplayPath(view()->buffer()->fileName(), 20);
771                         string text = bformat(_("Any changes will be lost. Are you sure "
772                                 "you want to revert to the saved version of the document %1$s?"), file);
773                         int const ret = Alert::prompt(_("Revert to saved document?"),
774                                 text, 0, 1, _("&Revert"), _("&Cancel"));
775
776                         if (ret == 0)
777                                 view()->reload();
778                         break;
779                 }
780
781                 case LFUN_UPDATE:
782                         Exporter::Export(owner->buffer(), argument, true);
783                         view()->showErrorList(BufferFormat(*owner->buffer()));
784                         break;
785
786                 case LFUN_PREVIEW:
787                         Exporter::Preview(owner->buffer(), argument);
788                         view()->showErrorList(BufferFormat(*owner->buffer()));
789                         break;
790
791                 case LFUN_BUILDPROG:
792                         Exporter::Export(owner->buffer(), "program", true);
793                         view()->showErrorList(_("Build"));
794                         break;
795
796                 case LFUN_RUNCHKTEX:
797                         owner->buffer()->runChktex();
798                         view()->showErrorList(_("ChkTeX"));
799                         break;
800
801                 case LFUN_EXPORT:
802                         if (argument == "custom")
803                                 owner->getDialogs().show("sendto");
804                         else {
805                                 Exporter::Export(owner->buffer(), argument, false);
806                                 view()->showErrorList(BufferFormat(*owner->buffer()));
807                         }
808                         break;
809
810                 case LFUN_EXPORT_CUSTOM: {
811                         string format_name;
812                         string command = split(argument, format_name, ' ');
813                         Format const * format = formats.getFormat(format_name);
814                         if (!format) {
815                                 lyxerr << "Format \"" << format_name
816                                        << "\" not recognized!"
817                                        << std::endl;
818                                 break;
819                         }
820
821                         Buffer * buffer = owner->buffer();
822
823                         // The name of the file created by the conversion process
824                         string filename;
825
826                         // Output to filename
827                         if (format->name() == "lyx") {
828                                 string const latexname =
829                                         buffer->getLatexName(false);
830                                 filename = ChangeExtension(latexname,
831                                                            format->extension());
832                                 filename = AddName(buffer->temppath(), filename);
833
834                                 if (!buffer->writeFile(filename))
835                                         break;
836
837                         } else {
838                                 Exporter::Export(buffer, format_name, true,
839                                                  filename);
840                         }
841
842                         // Substitute $$FName for filename
843                         if (!contains(command, "$$FName"))
844                                 command = "( " + command + " ) < $$FName";
845                         command = subst(command, "$$FName", filename);
846
847                         // Execute the command in the background
848                         Systemcall call;
849                         call.startscript(Systemcall::DontWait, command);
850                         break;
851                 }
852
853                 case LFUN_PRINT: {
854                         string target;
855                         string target_name;
856                         string command = split(split(argument, target, ' '),
857                                                target_name, ' ');
858
859                         if (target.empty()
860                             || target_name.empty()
861                             || command.empty()) {
862                                 lyxerr << "Unable to parse \""
863                                        << argument << '"' << std::endl;
864                                 break;
865                         }
866                         if (target != "printer" && target != "file") {
867                                 lyxerr << "Unrecognized target \""
868                                        << target << '"' << std::endl;
869                                 break;
870                         }
871
872                         Buffer * buffer = owner->buffer();
873
874                         if (!Exporter::Export(buffer, "dvi", true)) {
875                                 showPrintError(buffer->fileName());
876                                 break;
877                         }
878
879                         // Push directory path.
880                         string const path = buffer->temppath();
881                         Path p(path);
882
883                         // there are three cases here:
884                         // 1. we print to a file
885                         // 2. we print directly to a printer
886                         // 3. we print using a spool command (print to file first)
887                         Systemcall one;
888                         int res = 0;
889                         string const dviname =
890                                 ChangeExtension(buffer->getLatexName(true),
891                                                 "dvi");
892
893                         if (target == "printer") {
894                                 if (!lyxrc.print_spool_command.empty()) {
895                                         // case 3: print using a spool
896                                         string const psname =
897                                                 ChangeExtension(dviname,".ps");
898                                         command += lyxrc.print_to_file
899                                                 + QuoteName(psname)
900                                                 + ' '
901                                                 + QuoteName(dviname);
902
903                                         string command2 =
904                                                 lyxrc.print_spool_command +' ';
905                                         if (target_name != "default") {
906                                                 command2 += lyxrc.print_spool_printerprefix
907                                                         + target_name
908                                                         + ' ';
909                                         }
910                                         command2 += QuoteName(psname);
911                                         // First run dvips.
912                                         // If successful, then spool command
913                                         res = one.startscript(
914                                                 Systemcall::Wait,
915                                                 command);
916
917                                         if (res == 0)
918                                                 res = one.startscript(
919                                                         Systemcall::DontWait,
920                                                         command2);
921                                 } else {
922                                         // case 2: print directly to a printer
923                                         res = one.startscript(
924                                                 Systemcall::DontWait,
925                                                 command + QuoteName(dviname));
926                                 }
927
928                         } else {
929                                 // case 1: print to a file
930                                 command += lyxrc.print_to_file
931                                         + QuoteName(MakeAbsPath(target_name,
932                                                                 path))
933                                         + ' '
934                                         + QuoteName(dviname);
935                                 res = one.startscript(Systemcall::DontWait,
936                                                       command);
937                         }
938
939                         if (res != 0)
940                                 showPrintError(buffer->fileName());
941                         break;
942                 }
943
944                 case LFUN_IMPORT:
945                         doImport(argument);
946                         break;
947
948                 case LFUN_QUIT:
949                         QuitLyX();
950                         break;
951
952                 case LFUN_TOCVIEW: {
953                         InsetCommandParams p("tableofcontents");
954                         string const data = InsetCommandMailer::params2string("toc", p);
955                         owner->getDialogs().show("toc", data, 0);
956                         break;
957                 }
958
959                 case LFUN_AUTOSAVE:
960                         AutoSave(view());
961                         break;
962
963                 case LFUN_RECONFIGURE:
964                         Reconfigure(view());
965                         break;
966
967                 case LFUN_HELP_OPEN: {
968                         string const arg = argument;
969                         if (arg.empty()) {
970                                 setErrorMessage(N_("Missing argument"));
971                                 break;
972                         }
973                         string const fname = i18nLibFileSearch("doc", arg, "lyx");
974                         if (fname.empty()) {
975                                 lyxerr << "LyX: unable to find documentation file `"
976                                                          << arg << "'. Bad installation?" << endl;
977                                 break;
978                         }
979                         owner->message(bformat(_("Opening help file %1$s..."),
980                                 MakeDisplayPath(fname)));
981                         view()->loadLyXFile(fname, false);
982                         break;
983                 }
984
985                 // --- version control -------------------------------
986                 case LFUN_VC_REGISTER:
987                         if (!ensureBufferClean(view()))
988                                 break;
989                         if (!owner->buffer()->lyxvc().inUse()) {
990                                 owner->buffer()->lyxvc().registrer();
991                                 view()->reload();
992                         }
993                         break;
994
995                 case LFUN_VC_CHECKIN:
996                         if (!ensureBufferClean(view()))
997                                 break;
998                         if (owner->buffer()->lyxvc().inUse()
999                                         && !owner->buffer()->isReadonly()) {
1000                                 owner->buffer()->lyxvc().checkIn();
1001                                 view()->reload();
1002                         }
1003                         break;
1004
1005                 case LFUN_VC_CHECKOUT:
1006                         if (!ensureBufferClean(view()))
1007                                 break;
1008                         if (owner->buffer()->lyxvc().inUse()
1009                                         && owner->buffer()->isReadonly()) {
1010                                 owner->buffer()->lyxvc().checkOut();
1011                                 view()->reload();
1012                         }
1013                         break;
1014
1015                 case LFUN_VC_REVERT:
1016                         owner->buffer()->lyxvc().revert();
1017                         view()->reload();
1018                         break;
1019
1020                 case LFUN_VC_UNDO:
1021                         owner->buffer()->lyxvc().undoLast();
1022                         view()->reload();
1023                         break;
1024
1025                 // --- buffers ----------------------------------------
1026                 case LFUN_SWITCHBUFFER:
1027                         view()->setBuffer(bufferlist.getBuffer(argument));
1028                         break;
1029
1030                 case LFUN_NEXTBUFFER:
1031                         view()->setBuffer(bufferlist.next(view()->buffer()));
1032                         break;
1033
1034                 case LFUN_PREVIOUSBUFFER:
1035                         view()->setBuffer(bufferlist.previous(view()->buffer()));
1036                         break;
1037
1038                 case LFUN_FILE_NEW:
1039                         NewFile(view(), argument);
1040                         break;
1041
1042                 case LFUN_FILE_OPEN:
1043                         open(argument);
1044                         break;
1045
1046                 case LFUN_DROP_LAYOUTS_CHOICE:
1047                         owner->getToolbars().openLayoutList();
1048                         break;
1049
1050                 case LFUN_MENU_OPEN_BY_NAME:
1051                         owner->getMenubar().openByName(argument);
1052                         break;
1053
1054                 // --- lyxserver commands ----------------------------
1055                 case LFUN_GETNAME:
1056                         setMessage(owner->buffer()->fileName());
1057                         lyxerr[Debug::INFO] << "FNAME["
1058                                                          << owner->buffer()->fileName()
1059                                                          << "] " << endl;
1060                         break;
1061
1062                 case LFUN_NOTIFY:
1063                         dispatch_buffer = keyseq.print();
1064                         lyxserver->notifyClient(dispatch_buffer);
1065                         break;
1066
1067                 case LFUN_GOTOFILEROW: {
1068                         string file_name;
1069                         int row;
1070                         istringstream is(argument);
1071                         is >> file_name >> row;
1072                         if (prefixIs(file_name, getTmpDir())) {
1073                                 // Needed by inverse dvi search. If it is a file
1074                                 // in tmpdir, call the apropriated function
1075                                 view()->setBuffer(bufferlist.getBufferFromTmp(file_name));
1076                         } else {
1077                                 // Must replace extension of the file to be .lyx
1078                                 // and get full path
1079                                 string const s = ChangeExtension(file_name, ".lyx");
1080                                 // Either change buffer or load the file
1081                                 if (bufferlist.exists(s)) {
1082                                         view()->setBuffer(bufferlist.getBuffer(s));
1083                                 } else {
1084                                         view()->loadLyXFile(s);
1085                                 }
1086                         }
1087
1088                         view()->setCursorFromRow(row);
1089
1090                         view()->center();
1091                         // see BufferView_pimpl::center()
1092                         view()->updateScrollbar();
1093                         break;
1094                 }
1095
1096                 case LFUN_GOTO_PARAGRAPH: {
1097                         istringstream is(argument);
1098                         int id;
1099                         is >> id;
1100                         ParIterator par = owner->buffer()->getParFromID(id);
1101                         if (par == owner->buffer()->par_iterator_end()) {
1102                                 lyxerr[Debug::INFO] << "No matching paragraph found! ["
1103                                                                 << id << ']' << endl;
1104                                 break;
1105                         } else {
1106                                 lyxerr[Debug::INFO] << "Paragraph " << par->id()
1107                                                                 << " found." << endl;
1108                         }
1109
1110                         // Set the cursor
1111                         view()->setCursor(par, 0);
1112
1113                         view()->switchKeyMap();
1114                         owner->view_state_changed();
1115
1116                         view()->center();
1117                         // see BufferView_pimpl::center()
1118                         view()->updateScrollbar();
1119                         break;
1120                 }
1121
1122                 case LFUN_DIALOG_SHOW: {
1123                         string const name = cmd.getArg(0);
1124                         string data = trim(cmd.argument.substr(name.size()));
1125
1126                         if (name == "character") {
1127                                 data = freefont2string();
1128                                 if (!data.empty())
1129                                         owner->getDialogs().show("character", data);
1130                         }
1131
1132                         else if (name == "latexlog") {
1133                                 pair<Buffer::LogType, string> const logfile =
1134                                         owner->buffer()->getLogName();
1135                                 switch (logfile.first) {
1136                                 case Buffer::latexlog:
1137                                         data = "latex ";
1138                                         break;
1139                                 case Buffer::buildlog:
1140                                         data = "literate ";
1141                                         break;
1142                                 }
1143                                 data += logfile.second;
1144                                 owner->getDialogs().show("log", data);
1145                         }
1146                         else if (name == "vclog") {
1147                                 string const data = "vc " +
1148                                         owner->buffer()->lyxvc().getLogFile();
1149                                 owner->getDialogs().show("log", data);
1150                         }
1151                         else
1152                                 owner->getDialogs().show(name, data);
1153                         break;
1154                 }
1155
1156                 case LFUN_DIALOG_SHOW_NEW_INSET: {
1157                         string const name = cmd.getArg(0);
1158                         string data = trim(cmd.argument.substr(name.size()));
1159                         if (name == "bibitem" ||
1160                             name == "bibtex" ||
1161                             name == "include" ||
1162                             name == "index" ||
1163                             name == "label" ||
1164                             name == "ref" ||
1165                             name == "toc" ||
1166                             name == "url") {
1167                                 InsetCommandParams p(name);
1168                                 data = InsetCommandMailer::params2string(name, p);
1169                         } else if (name == "box") {
1170                                 // \c data == "Boxed" || "Frameless" etc
1171                                 InsetBoxParams p(data);
1172                                 data = InsetBoxMailer::params2string(p);
1173                         } else if (name == "branch") {
1174                                 InsetBranchParams p;
1175                                 data = InsetBranchMailer::params2string(p);
1176                         } else if (name == "citation") {
1177                                 InsetCommandParams p("cite");
1178                                 data = InsetCommandMailer::params2string(name, p);
1179                         } else if (name == "ert") {
1180                                 data = InsetERTMailer::params2string(InsetCollapsable::Open);
1181                         } else if (name == "external") {
1182                                 InsetExternalParams p;
1183                                 Buffer const & buffer = *owner->buffer();
1184                                 data = InsetExternalMailer::params2string(p, buffer);
1185                         } else if (name == "float") {
1186                                 InsetFloatParams p;
1187                                 data = InsetFloatMailer::params2string(p);
1188                         } else if (name == "graphics") {
1189                                 InsetGraphicsParams p;
1190                                 Buffer const & buffer = *owner->buffer();
1191                                 data = InsetGraphicsMailer::params2string(p, buffer);
1192                         } else if (name == "note") {
1193                                 InsetNoteParams p;
1194                                 data = InsetNoteMailer::params2string(p);
1195                         } else if (name == "vspace") {
1196                                 VSpace space;
1197                                 data = InsetVSpaceMailer::params2string(space);
1198                         } else if (name == "wrap") {
1199                                 InsetWrapParams p;
1200                                 data = InsetWrapMailer::params2string(p);
1201                         }
1202                         owner->getDialogs().show(name, data, 0);
1203                         break;
1204                 }
1205
1206                 case LFUN_DIALOG_SHOW_NEXT_INSET:
1207                         break;
1208
1209                 case LFUN_DIALOG_UPDATE: {
1210                         string const & name = argument;
1211                         // Can only update a dialog connected to an existing inset
1212                         InsetBase * inset = owner->getDialogs().getOpenInset(name);
1213                         if (inset) {
1214                                 FuncRequest fr(LFUN_INSET_DIALOG_UPDATE, cmd.argument);
1215                                 inset->dispatch(view()->cursor(), fr);
1216                         } else if (name == "paragraph") {
1217                                 dispatch(FuncRequest(LFUN_PARAGRAPH_UPDATE));
1218                         } else if (name == "prefs") {
1219                                 owner->getDialogs().update(name, string());
1220                         }
1221                         break;
1222                 }
1223
1224                 case LFUN_DIALOG_HIDE:
1225                         Dialogs::hide(argument, 0);
1226                         break;
1227
1228                 case LFUN_DIALOG_DISCONNECT_INSET:
1229                         owner->getDialogs().disconnect(argument);
1230                         break;
1231
1232                 case LFUN_CHILDOPEN: {
1233                         string const filename =
1234                                 MakeAbsPath(argument, owner->buffer()->filePath());
1235                         setMessage(N_("Opening child document ") +
1236                                          MakeDisplayPath(filename) + "...");
1237                         view()->savePosition(0);
1238                         string const parentfilename = owner->buffer()->fileName();
1239                         if (bufferlist.exists(filename))
1240                                 view()->setBuffer(bufferlist.getBuffer(filename));
1241                         else
1242                                 view()->loadLyXFile(filename);
1243                         // Set the parent name of the child document.
1244                         // This makes insertion of citations and references in the child work,
1245                         // when the target is in the parent or another child document.
1246                         owner->buffer()->setParentName(parentfilename);
1247                         break;
1248                 }
1249
1250                 case LFUN_TOGGLECURSORFOLLOW:
1251                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
1252                         break;
1253
1254                 case LFUN_KMAP_OFF:
1255                         owner->getIntl().KeyMapOn(false);
1256                         break;
1257
1258                 case LFUN_KMAP_PRIM:
1259                         owner->getIntl().KeyMapPrim();
1260                         break;
1261
1262                 case LFUN_KMAP_SEC:
1263                         owner->getIntl().KeyMapSec();
1264                         break;
1265
1266                 case LFUN_KMAP_TOGGLE:
1267                         owner->getIntl().ToggleKeyMap();
1268                         break;
1269
1270                 case LFUN_REPEAT: {
1271                         // repeat command
1272                         string countstr;
1273                         string rest = split(argument, countstr, ' ');
1274                         istringstream is(countstr);
1275                         int count = 0;
1276                         is >> count;
1277                         lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
1278                         for (int i = 0; i < count; ++i)
1279                                 dispatch(lyxaction.lookupFunc(rest));
1280                         break;
1281                 }
1282
1283                 case LFUN_SEQUENCE: {
1284                         // argument contains ';'-terminated commands
1285                         string arg = argument;
1286                         while (!arg.empty()) {
1287                                 string first;
1288                                 arg = split(arg, first, ';');
1289                                 FuncRequest func(lyxaction.lookupFunc(first));
1290                                 func.origin = cmd.origin;
1291                                 dispatch(func);
1292                         }
1293                         break;
1294                 }
1295
1296                 case LFUN_SAVEPREFERENCES: {
1297                         Path p(user_lyxdir());
1298                         lyxrc.write("preferences", false);
1299                         break;
1300                 }
1301
1302                 case LFUN_SCREEN_FONT_UPDATE:
1303                         // handle the screen font changes.
1304                         lyxrc.set_font_norm_type();
1305                         lyx_gui::update_fonts();
1306                         // All visible buffers will need resize
1307                         view()->resize();
1308                         break;
1309
1310                 case LFUN_SET_COLOR: {
1311                         string lyx_name;
1312                         string const x11_name = split(argument, lyx_name, ' ');
1313                         if (lyx_name.empty() || x11_name.empty()) {
1314                                 setErrorMessage(N_("Syntax: set-color <lyx_name>"
1315                                                         " <x11_name>"));
1316                                 break;
1317                         }
1318
1319                         bool const graphicsbg_changed =
1320                                 (lyx_name == lcolor.getLyXName(LColor::graphicsbg) &&
1321                                  x11_name != lcolor.getX11Name(LColor::graphicsbg));
1322
1323                         if (!lcolor.setColor(lyx_name, x11_name)) {
1324                                 setErrorMessage(
1325                                         bformat(_("Set-color \"%1$s\" failed "
1326                                                                 "- color is undefined or "
1327                                                                 "may not be redefined"), lyx_name));
1328                                 break;
1329                         }
1330
1331                         lyx_gui::update_color(lcolor.getFromLyXName(lyx_name));
1332
1333                         if (graphicsbg_changed) {
1334 #ifdef WITH_WARNINGS
1335 #warning FIXME!! The graphics cache no longer has a changeDisplay method.
1336 #endif
1337 #if 0
1338                                 lyx::graphics::GCache::get().changeDisplay(true);
1339 #endif
1340                         }
1341                         break;
1342                 }
1343
1344                 case LFUN_MESSAGE:
1345                         owner->message(argument);
1346                         break;
1347
1348                 case LFUN_TOOLTIPS_TOGGLE:
1349                         owner->getDialogs().toggleTooltips();
1350                         break;
1351
1352                 case LFUN_EXTERNAL_EDIT: {
1353                         FuncRequest fr(action, argument);
1354                         InsetExternal().dispatch(view()->cursor(), fr);
1355                         break;
1356                 }
1357
1358                 case LFUN_GRAPHICS_EDIT: {
1359                         FuncRequest fr(action, argument);
1360                         InsetGraphics().dispatch(view()->cursor(), fr);
1361                         break;
1362                 }
1363
1364                 case LFUN_ALL_INSETS_TOGGLE: {
1365                         string action;
1366                         string const name = split(argument, action, ' ');
1367                         InsetBase::Code const inset_code =
1368                                 InsetBase::translate(name);
1369
1370                         LCursor & cur = view()->cursor();
1371                         FuncRequest fr(LFUN_INSET_TOGGLE, action);
1372
1373                         InsetBase & inset = owner->buffer()->inset();
1374                         InsetIterator it  = inset_iterator_begin(inset);
1375                         InsetIterator const end = inset_iterator_end(inset);
1376                         for (; it != end; ++it) {
1377                                 if (inset_code == InsetBase::NO_CODE
1378                                     || inset_code == it->lyxCode())
1379                                         it->dispatch(cur, fr);
1380                         }
1381                         break;
1382                 }
1383
1384                 case LFUN_LANGUAGE_BUFFER: {
1385                         Buffer & buffer = *owner->buffer();
1386                         Language const * oldL = buffer.params().language;
1387                         Language const * newL = languages.getLanguage(argument);
1388                         if (!newL || oldL == newL)
1389                                 break;
1390
1391                         if (oldL->RightToLeft() == newL->RightToLeft()
1392                             && !buffer.isMultiLingual())
1393                                 buffer.changeLanguage(oldL, newL);
1394                         else
1395                                 buffer.updateDocLang(newL);
1396                         break;
1397                 }
1398
1399                 case LFUN_SAVE_AS_DEFAULT: {
1400                         string const fname =
1401                                 AddName(AddPath(user_lyxdir(), "templates/"),
1402                                         "defaults.lyx");
1403                         Buffer defaults(fname);
1404
1405                         istringstream ss(argument);
1406                         LyXLex lex(0,0);
1407                         lex.setStream(ss);
1408                         int const unknown_tokens = defaults.readHeader(lex);
1409
1410                         if (unknown_tokens != 0) {
1411                                 lyxerr << "Warning in LFUN_SAVE_AS_DEFAULT!\n"
1412                                        << unknown_tokens << " unknown token"
1413                                        << (unknown_tokens == 1 ? "" : "s")
1414                                        << endl;
1415                         }
1416
1417                         if (defaults.writeFile(defaults.fileName()))
1418                                 setMessage(_("Document defaults saved in ")
1419                                            + MakeDisplayPath(fname));
1420                         else
1421                                 setErrorMessage(_("Unable to save document defaults"));
1422                         break;
1423                 }
1424
1425                 case LFUN_BUFFERPARAMS_APPLY: {
1426                         biblio::CiteEngine const engine =
1427                                 owner->buffer()->params().cite_engine;
1428
1429                         istringstream ss(argument);
1430                         LyXLex lex(0,0);
1431                         lex.setStream(ss);
1432                         int const unknown_tokens =
1433                                 owner->buffer()->readHeader(lex);
1434
1435                         if (unknown_tokens != 0) {
1436                                 lyxerr << "Warning in LFUN_BUFFERPARAMS_APPLY!\n"
1437                                        << unknown_tokens << " unknown token"
1438                                        << (unknown_tokens == 1 ? "" : "s")
1439                                        << endl;
1440                         }
1441                         if (engine == owner->buffer()->params().cite_engine)
1442                                 break;
1443
1444                         LCursor & cur = view()->cursor();
1445                         FuncRequest fr(LFUN_INSET_REFRESH);
1446
1447                         InsetBase & inset = owner->buffer()->inset();
1448                         InsetIterator it  = inset_iterator_begin(inset);
1449                         InsetIterator const end = inset_iterator_end(inset);
1450                         for (; it != end; ++it)
1451                                 if (it->lyxCode() == InsetBase::CITE_CODE)
1452                                         it->dispatch(cur, fr);
1453                         break;
1454                 }
1455
1456                 case LFUN_TEXTCLASS_APPLY: {
1457                         Buffer * buffer = owner->buffer();
1458
1459                         lyx::textclass_type const old_class =
1460                                 buffer->params().textclass;
1461
1462                         loadTextclass(argument);
1463
1464                         std::pair<bool, lyx::textclass_type> const tc_pair =
1465                                 textclasslist.NumberOfClass(argument);
1466
1467                         if (!tc_pair.first)
1468                                 break;
1469
1470                         lyx::textclass_type const new_class = tc_pair.second;
1471                         if (old_class == new_class)
1472                                 // nothing to do
1473                                 break;
1474
1475                         owner->message(_("Converting document to new document class..."));
1476                         ErrorList el;
1477                         lyx::cap::SwitchLayoutsBetweenClasses(
1478                                 old_class, new_class,
1479                                 buffer->paragraphs(), el);
1480
1481                         bufferErrors(*buffer, el);
1482                         view()->showErrorList(_("Class switch"));
1483                         break;
1484                 }
1485
1486                 case LFUN_TEXTCLASS_LOAD:
1487                         loadTextclass(argument);
1488                         break;
1489
1490                 case LFUN_LYXRC_APPLY: {
1491                         istringstream ss(argument);
1492                         bool const success = lyxrc.read(ss) == 0;
1493
1494                         if (!success) {
1495                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
1496                                        << "Unable to read lyxrc data"
1497                                        << endl;
1498                         }
1499                         break;
1500                 }
1501
1502                 default: {
1503                         update = false;
1504                         view()->cursor().dispatch(cmd);
1505                         if (view()->cursor().result().dispatched())
1506                                 update |= view()->cursor().result().update();
1507                         else
1508                                 update |= view()->dispatch(cmd);
1509                         break;
1510                 }
1511                 }
1512
1513                 if (view()->available()) {
1514                         // Redraw screen unless explicitly told otherwise.
1515                         // This also initializes the position cache for all insets
1516                         // in (at least partially) visible top-level paragraphs.
1517                         view()->update(true, update);
1518
1519                         // if we executed a mutating lfun, mark the buffer as dirty
1520                         if (getStatus(cmd).enabled()
1521                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::NoBuffer)
1522                                         && !lyxaction.funcHasFlag(cmd.action, LyXAction::ReadOnly))
1523                                 view()->buffer()->markDirty();
1524                 }
1525
1526                 if (view()->cursor().inTexted()) {
1527                         view()->owner()->updateLayoutChoice();
1528                 }
1529         }
1530         sendDispatchMessage(getMessage(), cmd);
1531 }
1532
1533
1534 void LyXFunc::sendDispatchMessage(string const & msg, FuncRequest const & cmd)
1535 {
1536         owner->updateMenubar();
1537         owner->updateToolbars();
1538
1539         const bool verbose = (cmd.origin == FuncRequest::UI
1540                               || cmd.origin == FuncRequest::COMMANDBUFFER);
1541
1542         if (cmd.action == LFUN_SELFINSERT || !verbose) {
1543                 lyxerr[Debug::ACTION] << "dispatch msg is " << msg << endl;
1544                 if (!msg.empty())
1545                         owner->message(msg);
1546                 return;
1547         }
1548
1549         string dispatch_msg = msg;
1550         if (!dispatch_msg.empty())
1551                 dispatch_msg += ' ';
1552
1553         string comname = lyxaction.getActionName(cmd.action);
1554
1555         bool argsadded = false;
1556
1557         if (!cmd.argument.empty()) {
1558                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
1559                         comname += ' ' + cmd.argument;
1560                         argsadded = true;
1561                 }
1562         }
1563
1564         string const shortcuts = toplevel_keymap->printbindings(cmd);
1565
1566         if (!shortcuts.empty()) {
1567                 comname += ": " + shortcuts;
1568         } else if (!argsadded && !cmd.argument.empty()) {
1569                 comname += ' ' + cmd.argument;
1570         }
1571
1572         if (!comname.empty()) {
1573                 comname = rtrim(comname);
1574                 dispatch_msg += '(' + comname + ')';
1575         }
1576
1577         lyxerr[Debug::ACTION] << "verbose dispatch msg " << dispatch_msg << endl;
1578         if (!dispatch_msg.empty())
1579                 owner->message(dispatch_msg);
1580 }
1581
1582
1583 void LyXFunc::setupLocalKeymap()
1584 {
1585         keyseq.stdmap = toplevel_keymap.get();
1586         keyseq.curmap = toplevel_keymap.get();
1587         cancel_meta_seq.stdmap = toplevel_keymap.get();
1588         cancel_meta_seq.curmap = toplevel_keymap.get();
1589 }
1590
1591
1592 void LyXFunc::menuNew(string const & name, bool fromTemplate)
1593 {
1594         string initpath = lyxrc.document_path;
1595         string filename(name);
1596
1597         if (view()->available()) {
1598                 string const trypath = owner->buffer()->filePath();
1599                 // If directory is writeable, use this as default.
1600                 if (IsDirWriteable(trypath))
1601                         initpath = trypath;
1602         }
1603
1604         static int newfile_number;
1605
1606         if (filename.empty()) {
1607                 filename = AddName(lyxrc.document_path,
1608                             "newfile" + convert<string>(++newfile_number) + ".lyx");
1609                 FileInfo fi(filename);
1610                 while (bufferlist.exists(filename) || fi.readable()) {
1611                         ++newfile_number;
1612                         filename = AddName(lyxrc.document_path,
1613                                            "newfile" +  convert<string>(newfile_number) +
1614                                     ".lyx");
1615                         fi.newFile(filename);
1616                 }
1617         }
1618
1619         // The template stuff
1620         string templname;
1621         if (fromTemplate) {
1622                 FileDialog fileDlg(_("Select template file"),
1623                         LFUN_SELECT_FILE_SYNC,
1624                         make_pair(string(_("Documents|#o#O")),
1625                                   string(lyxrc.document_path)),
1626                         make_pair(string(_("Templates|#T#t")),
1627                                   string(lyxrc.template_path)));
1628
1629                 FileDialog::Result result =
1630                         fileDlg.open(lyxrc.template_path,
1631                                      FileFilterList(_("LyX Documents (*.lyx)")),
1632                                      string());
1633
1634                 if (result.first == FileDialog::Later)
1635                         return;
1636                 if (result.second.empty())
1637                         return;
1638                 templname = result.second;
1639         }
1640
1641         view()->newFile(filename, templname, !name.empty());
1642 }
1643
1644
1645 void LyXFunc::open(string const & fname)
1646 {
1647         string initpath = lyxrc.document_path;
1648
1649         if (view()->available()) {
1650                 string const trypath = owner->buffer()->filePath();
1651                 // If directory is writeable, use this as default.
1652                 if (IsDirWriteable(trypath))
1653                         initpath = trypath;
1654         }
1655
1656         string filename;
1657
1658         if (fname.empty()) {
1659                 FileDialog fileDlg(_("Select document to open"),
1660                         LFUN_FILE_OPEN,
1661                         make_pair(string(_("Documents|#o#O")),
1662                                   string(lyxrc.document_path)),
1663                         make_pair(string(_("Examples|#E#e")),
1664                                   string(AddPath(system_lyxdir(), "examples"))));
1665
1666                 FileDialog::Result result =
1667                         fileDlg.open(initpath,
1668                                      FileFilterList(_("LyX Documents (*.lyx)")),
1669                                      string());
1670
1671                 if (result.first == FileDialog::Later)
1672                         return;
1673
1674                 filename = result.second;
1675
1676                 // check selected filename
1677                 if (filename.empty()) {
1678                         owner->message(_("Canceled."));
1679                         return;
1680                 }
1681         } else
1682                 filename = fname;
1683
1684         // get absolute path of file and add ".lyx" to the filename if
1685         // necessary
1686         string const fullpath = FileSearch(string(), filename, "lyx");
1687         if (!fullpath.empty()) {
1688                 filename = fullpath;
1689         }
1690
1691         string const disp_fn(MakeDisplayPath(filename));
1692
1693         // if the file doesn't exist, let the user create one
1694         FileInfo const f(filename, true);
1695         if (!f.exist()) {
1696                 // the user specifically chose this name. Believe them.
1697                 view()->newFile(filename, "", true);
1698                 return;
1699         }
1700
1701         owner->message(bformat(_("Opening document %1$s..."), disp_fn));
1702
1703         string str2;
1704         if (view()->loadLyXFile(filename)) {
1705                 str2 = bformat(_("Document %1$s opened."), disp_fn);
1706         } else {
1707                 str2 = bformat(_("Could not open document %1$s"), disp_fn);
1708         }
1709         owner->message(str2);
1710 }
1711
1712
1713 void LyXFunc::doImport(string const & argument)
1714 {
1715         string format;
1716         string filename = split(argument, format, ' ');
1717
1718         lyxerr[Debug::INFO] << "LyXFunc::doImport: " << format
1719                             << " file: " << filename << endl;
1720
1721         // need user interaction
1722         if (filename.empty()) {
1723                 string initpath = lyxrc.document_path;
1724
1725                 if (view()->available()) {
1726                         string const trypath = owner->buffer()->filePath();
1727                         // If directory is writeable, use this as default.
1728                         if (IsDirWriteable(trypath))
1729                                 initpath = trypath;
1730                 }
1731
1732                 string const text = bformat(_("Select %1$s file to import"),
1733                         formats.prettyName(format));
1734
1735                 FileDialog fileDlg(text,
1736                         LFUN_IMPORT,
1737                         make_pair(string(_("Documents|#o#O")),
1738                                   string(lyxrc.document_path)),
1739                         make_pair(string(_("Examples|#E#e")),
1740                                   string(AddPath(system_lyxdir(), "examples"))));
1741
1742                 string const filter = formats.prettyName(format)
1743                         + " (*." + formats.extension(format) + ')';
1744
1745                 FileDialog::Result result =
1746                         fileDlg.open(initpath,
1747                                      FileFilterList(filter),
1748                                      string());
1749
1750                 if (result.first == FileDialog::Later)
1751                         return;
1752
1753                 filename = result.second;
1754
1755                 // check selected filename
1756                 if (filename.empty())
1757                         owner->message(_("Canceled."));
1758         }
1759
1760         if (filename.empty())
1761                 return;
1762
1763         // get absolute path of file
1764         filename = MakeAbsPath(filename);
1765
1766         string const lyxfile = ChangeExtension(filename, ".lyx");
1767
1768         // Check if the document already is open
1769         if (lyx_gui::use_gui && bufferlist.exists(lyxfile)) {
1770                 if (!bufferlist.close(bufferlist.getBuffer(lyxfile), true)) {
1771                         owner->message(_("Canceled."));
1772                         return;
1773                 }
1774         }
1775
1776         // if the file exists already, and we didn't do
1777         // -i lyx thefile.lyx, warn
1778         if (FileInfo(lyxfile, true).exist() && filename != lyxfile) {
1779                 string const file = MakeDisplayPath(lyxfile, 30);
1780
1781                 string text = bformat(_("The document %1$s already exists.\n\n"
1782                         "Do you want to over-write that document?"), file);
1783                 int const ret = Alert::prompt(_("Over-write document?"),
1784                         text, 0, 1, _("&Over-write"), _("&Cancel"));
1785
1786                 if (ret == 1) {
1787                         owner->message(_("Canceled."));
1788                         return;
1789                 }
1790         }
1791
1792         Importer::Import(owner, filename, format);
1793 }
1794
1795
1796 void LyXFunc::closeBuffer()
1797 {
1798         if (bufferlist.close(owner->buffer(), true) && !quitting) {
1799                 if (bufferlist.empty()) {
1800                         // need this otherwise SEGV may occur while
1801                         // trying to set variables that don't exist
1802                         // since there's no current buffer
1803                         owner->getDialogs().hideBufferDependent();
1804                 } else {
1805                         view()->setBuffer(bufferlist.first());
1806                 }
1807         }
1808 }
1809
1810
1811 // Each "owner" should have it's own message method. lyxview and
1812 // the minibuffer would use the minibuffer, but lyxserver would
1813 // send an ERROR signal to its client.  Alejandro 970603
1814 // This function is bit problematic when it comes to NLS, to make the
1815 // lyx servers client be language indepenent we must not translate
1816 // strings sent to this func.
1817 void LyXFunc::setErrorMessage(string const & m) const
1818 {
1819         dispatch_buffer = m;
1820         errorstat = true;
1821 }
1822
1823
1824 void LyXFunc::setMessage(string const & m) const
1825 {
1826         dispatch_buffer = m;
1827 }
1828
1829
1830 string const LyXFunc::viewStatusMessage()
1831 {
1832         // When meta-fake key is pressed, show the key sequence so far + "M-".
1833         if (wasMetaKey())
1834                 return keyseq.print() + "M-";
1835
1836         // Else, when a non-complete key sequence is pressed,
1837         // show the available options.
1838         if (keyseq.length() > 0 && !keyseq.deleted())
1839                 return keyseq.printOptions();
1840
1841         if (!view()->available())
1842                 return _("Welcome to LyX!");
1843
1844         return view()->cursor().currentState();
1845 }
1846
1847
1848 BufferView * LyXFunc::view() const
1849 {
1850         BOOST_ASSERT(owner);
1851         return owner->view().get();
1852 }
1853
1854
1855 bool LyXFunc::wasMetaKey() const
1856 {
1857         return (meta_fake_bit != key_modifier::none);
1858 }