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