]> git.lyx.org Git - lyx.git/blob - src/LyXFunc.cpp
Transfer LFUN_DIALOG_SHOW_NEW_INSET to BufferView and put InsetCommand related code...
[lyx.git] / src / LyXFunc.cpp
1 /**
2  * \file LyXFunc.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alfredo Braunstein
7  * \author Lars Gullik Bjønnes
8  * \author Jean-Marc Lasgouttes
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  * \author Allan Rae
13  * \author Dekel Tsur
14  * \author Martin Vermeer
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "LyXFunc.h"
23
24 #include "LayoutFile.h"
25 #include "BranchList.h"
26 #include "buffer_funcs.h"
27 #include "Buffer.h"
28 #include "BufferList.h"
29 #include "BufferParams.h"
30 #include "BufferView.h"
31 #include "CmdDef.h"
32 #include "Color.h"
33 #include "Converter.h"
34 #include "Cursor.h"
35 #include "CutAndPaste.h"
36 #include "DispatchResult.h"
37 #include "Encoding.h"
38 #include "ErrorList.h"
39 #include "Format.h"
40 #include "FuncRequest.h"
41 #include "FuncStatus.h"
42 #include "InsetIterator.h"
43 #include "Intl.h"
44 #include "KeyMap.h"
45 #include "Language.h"
46 #include "LaTeXFeatures.h"
47 #include "Lexer.h"
48 #include "LyXAction.h"
49 #include "lyxfind.h"
50 #include "LyX.h"
51 #include "LyXRC.h"
52 #include "LyXVC.h"
53 #include "Paragraph.h"
54 #include "ParagraphParameters.h"
55 #include "ParIterator.h"
56 #include "Row.h"
57 #include "Server.h"
58 #include "Session.h"
59 #include "SpellChecker.h"
60
61 #include "insets/InsetCommand.h"
62
63 #include "frontends/alert.h"
64 #include "frontends/Application.h"
65 #include "frontends/KeySymbol.h"
66 #include "frontends/LyXView.h"
67 #include "frontends/Selection.h"
68
69 #include "support/debug.h"
70 #include "support/environment.h"
71 #include "support/FileName.h"
72 #include "support/filetools.h"
73 #include "support/gettext.h"
74 #include "support/lassert.h"
75 #include "support/lstrings.h"
76 #include "support/Path.h"
77 #include "support/Package.h"
78 #include "support/Systemcall.h"
79 #include "support/convert.h"
80 #include "support/os.h"
81
82 #include <sstream>
83 #include <vector>
84
85 using namespace std;
86 using namespace lyx::support;
87
88 namespace lyx {
89
90 using frontend::LyXView;
91
92 namespace Alert = frontend::Alert;
93
94 namespace {
95
96
97 // This function runs "configure" and then rereads lyx.defaults to
98 // reconfigure the automatic settings.
99 void reconfigure(LyXView * lv, string const & option)
100 {
101         // emit message signal.
102         if (lv)
103                 lv->message(_("Running configure..."));
104
105         // Run configure in user lyx directory
106         PathChanger p(package().user_support());
107         string configure_command = package().configure_command();
108         configure_command += option;
109         Systemcall one;
110         int ret = one.startscript(Systemcall::Wait, configure_command);
111         p.pop();
112         // emit message signal.
113         if (lv)
114                 lv->message(_("Reloading configuration..."));
115         lyxrc.read(libFileSearch(string(), "lyxrc.defaults"));
116         // Re-read packages.lst
117         LaTeXFeatures::getAvailable();
118
119         if (ret)
120                 Alert::information(_("System reconfiguration failed"),
121                            _("The system reconfiguration has failed.\n"
122                                   "Default textclass is used but LyX may "
123                                   "not be able to work properly.\n"
124                                   "Please reconfigure again if needed."));
125         else
126
127                 Alert::information(_("System reconfigured"),
128                            _("The system has been reconfigured.\n"
129                              "You need to restart LyX to make use of any\n"
130                              "updated document class specifications."));
131 }
132
133 }
134
135
136 LyXFunc::LyXFunc()
137         : encoded_last_key(0), meta_fake_bit(NoModifier)
138 {
139 }
140
141
142 void LyXFunc::initKeySequences(KeyMap * kb)
143 {
144         keyseq = KeySequence(kb, kb);
145         cancel_meta_seq = KeySequence(kb, kb);
146 }
147
148
149 void LyXFunc::handleKeyFunc(FuncCode action)
150 {
151         char_type c = encoded_last_key;
152
153         if (keyseq.length())
154                 c = 0;
155         LyXView * lv = theApp()->currentWindow();
156         LASSERT(lv && lv->currentBufferView(), /**/);
157         BufferView * bv = lv->currentBufferView();
158         bv->getIntl().getTransManager().deadkey(
159                 c, get_accent(action).accent, bv->cursor().innerText(),
160                 bv->cursor());
161         // Need to clear, in case the minibuffer calls these
162         // actions
163         keyseq.clear();
164         // copied verbatim from do_accent_char
165         bv->cursor().resetAnchor();
166         bv->processUpdateFlags(Update::FitCursor);
167 }
168
169 //FIXME: bookmark handling is a frontend issue. This code should be transferred
170 // to GuiView and be GuiView and be window dependent.
171 void LyXFunc::gotoBookmark(unsigned int idx, bool openFile, bool switchToBuffer)
172 {
173         LyXView * lv = theApp()->currentWindow();
174         LASSERT(lv, /**/);
175         if (!theSession().bookmarks().isValid(idx))
176                 return;
177         BookmarksSection::Bookmark const & bm = theSession().bookmarks().bookmark(idx);
178         LASSERT(!bm.filename.empty(), /**/);
179         string const file = bm.filename.absFilename();
180         // if the file is not opened, open it.
181         if (!theBufferList().exists(bm.filename)) {
182                 if (openFile)
183                         dispatch(FuncRequest(LFUN_FILE_OPEN, file));
184                 else
185                         return;
186         }
187         // open may fail, so we need to test it again
188         if (!theBufferList().exists(bm.filename))
189                 return;
190
191         // bm can be changed when saving
192         BookmarksSection::Bookmark tmp = bm;
193
194         // Special case idx == 0 used for back-from-back jump navigation
195         if (idx == 0)
196                 dispatch(FuncRequest(LFUN_BOOKMARK_SAVE, "0"));
197
198         // if the current buffer is not that one, switch to it.
199         if (!lv->documentBufferView()
200                 || lv->documentBufferView()->buffer().fileName() != tmp.filename) {
201                 if (!switchToBuffer)
202                         return;
203                 dispatch(FuncRequest(LFUN_BUFFER_SWITCH, file));
204         }
205
206         // moveToPosition try paragraph id first and then paragraph (pit, pos).
207         if (!lv->documentBufferView()->moveToPosition(
208                 tmp.bottom_pit, tmp.bottom_pos, tmp.top_id, tmp.top_pos))
209                 return;
210
211         // bm changed
212         if (idx == 0)
213                 return;
214
215         // Cursor jump succeeded!
216         Cursor const & cur = lv->documentBufferView()->cursor();
217         pit_type new_pit = cur.pit();
218         pos_type new_pos = cur.pos();
219         int new_id = cur.paragraph().id();
220
221         // if bottom_pit, bottom_pos or top_id has been changed, update bookmark
222         // see http://bugzilla.lyx.org/show_bug.cgi?id=3092
223         if (bm.bottom_pit != new_pit || bm.bottom_pos != new_pos 
224                 || bm.top_id != new_id) {
225                 const_cast<BookmarksSection::Bookmark &>(bm).updatePos(
226                         new_pit, new_pos, new_id);
227         }
228 }
229
230
231 void LyXFunc::processKeySym(KeySymbol const & keysym, KeyModifier state)
232 {
233         LYXERR(Debug::KEY, "KeySym is " << keysym.getSymbolName());
234
235         LyXView * lv = theApp()->currentWindow();
236
237         // Do nothing if we have nothing (JMarc)
238         if (!keysym.isOK()) {
239                 LYXERR(Debug::KEY, "Empty kbd action (probably composing)");
240                 lv->restartCursor();
241                 return;
242         }
243
244         if (keysym.isModifier()) {
245                 LYXERR(Debug::KEY, "isModifier true");
246                 if (lv)
247                         lv->restartCursor();
248                 return;
249         }
250
251         //Encoding const * encoding = lv->documentBufferView()->cursor().getEncoding();
252         //encoded_last_key = keysym.getISOEncoded(encoding ? encoding->name() : "");
253         // FIXME: encoded_last_key shadows the member variable of the same
254         // name. Is that intended?
255         char_type encoded_last_key = keysym.getUCSEncoded();
256
257         // Do a one-deep top-level lookup for
258         // cancel and meta-fake keys. RVDK_PATCH_5
259         cancel_meta_seq.reset();
260
261         FuncRequest func = cancel_meta_seq.addkey(keysym, state);
262         LYXERR(Debug::KEY, "action first set to [" << func.action << ']');
263
264         // When not cancel or meta-fake, do the normal lookup.
265         // Note how the meta_fake Mod1 bit is OR-ed in and reset afterwards.
266         // Mostly, meta_fake_bit = NoModifier. RVDK_PATCH_5.
267         if ((func.action != LFUN_CANCEL) && (func.action != LFUN_META_PREFIX)) {
268                 // remove Caps Lock and Mod2 as a modifiers
269                 func = keyseq.addkey(keysym, (state | meta_fake_bit));
270                 LYXERR(Debug::KEY, "action now set to [" << func.action << ']');
271         }
272
273         // Dont remove this unless you know what you are doing.
274         meta_fake_bit = NoModifier;
275
276         // Can this happen now ?
277         if (func.action == LFUN_NOACTION)
278                 func = FuncRequest(LFUN_COMMAND_PREFIX);
279
280         LYXERR(Debug::KEY, " Key [action=" << func.action << "]["
281                 << keyseq.print(KeySequence::Portable) << ']');
282
283         // already here we know if it any point in going further
284         // why not return already here if action == -1 and
285         // num_bytes == 0? (Lgb)
286
287         if (keyseq.length() > 1)
288                 lv->message(keyseq.print(KeySequence::ForGui));
289
290
291         // Maybe user can only reach the key via holding down shift.
292         // Let's see. But only if shift is the only modifier
293         if (func.action == LFUN_UNKNOWN_ACTION && state == ShiftModifier) {
294                 LYXERR(Debug::KEY, "Trying without shift");
295                 func = keyseq.addkey(keysym, NoModifier);
296                 LYXERR(Debug::KEY, "Action now " << func.action);
297         }
298
299         if (func.action == LFUN_UNKNOWN_ACTION) {
300                 // Hmm, we didn't match any of the keysequences. See
301                 // if it's normal insertable text not already covered
302                 // by a binding
303                 if (keysym.isText() && keyseq.length() == 1) {
304                         LYXERR(Debug::KEY, "isText() is true, inserting.");
305                         func = FuncRequest(LFUN_SELF_INSERT,
306                                            FuncRequest::KEYBOARD);
307                 } else {
308                         LYXERR(Debug::KEY, "Unknown, !isText() - giving up");
309                         lv->message(_("Unknown function."));
310                         lv->restartCursor();
311                         return;
312                 }
313         }
314
315         if (func.action == LFUN_SELF_INSERT) {
316                 if (encoded_last_key != 0) {
317                         docstring const arg(1, encoded_last_key);
318                         dispatch(FuncRequest(LFUN_SELF_INSERT, arg,
319                                              FuncRequest::KEYBOARD));
320                         LYXERR(Debug::KEY, "SelfInsert arg[`" << to_utf8(arg) << "']");
321                 }
322         } else {
323                 dispatch(func);
324                 if (!lv)
325                         return;
326         }
327 }
328
329
330 FuncStatus LyXFunc::getStatus(FuncRequest const & cmd) const
331 {
332         //lyxerr << "LyXFunc::getStatus: cmd: " << cmd << endl;
333         FuncStatus flag;
334
335         if (cmd.action == LFUN_NOACTION) {
336                 flag.message(from_utf8(N_("Nothing to do")));
337                 flag.setEnabled(false);
338                 return flag;
339         }
340
341         switch (cmd.action) {
342         case LFUN_UNKNOWN_ACTION:
343                 flag.unknown(true);
344                 flag.setEnabled(false);
345                 break;
346
347         default:
348                 break;
349         }
350
351         if (flag.unknown()) {
352                 flag.message(from_utf8(N_("Unknown action")));
353                 return flag;
354         }
355
356         if (!flag.enabled()) {
357                 if (flag.message().empty())
358                         flag.message(from_utf8(N_("Command disabled")));
359                 return flag;
360         }
361
362         // I would really like to avoid having this switch and rather try to
363         // encode this in the function itself.
364         // -- And I'd rather let an inset decide which LFUNs it is willing
365         // to handle (Andre')
366         bool enable = true;
367         switch (cmd.action) {
368
369         case LFUN_CITATION_INSERT: {
370                 FuncRequest fr(LFUN_INSET_INSERT, "citation");
371                 enable = getStatus(fr).enabled();
372                 break;
373         }
374         
375         // This could be used for the no-GUI version. The GUI version is handled in
376         // LyXView::getStatus(). See above.
377         /*
378         case LFUN_BUFFER_WRITE:
379         case LFUN_BUFFER_WRITE_AS: {
380                 Buffer * b = theBufferList().getBuffer(FileName(cmd.getArg(0)));
381                 enable = b && (b->isUnnamed() || !b->isClean());
382                 break;
383         }
384         */
385
386         case LFUN_BOOKMARK_GOTO: {
387                 const unsigned int num = convert<unsigned int>(to_utf8(cmd.argument()));
388                 enable = theSession().bookmarks().isValid(num);
389                 break;
390         }
391
392         case LFUN_BOOKMARK_CLEAR:
393                 enable = theSession().bookmarks().hasValid();
394                 break;
395
396         // this one is difficult to get right. As a half-baked
397         // solution, we consider only the first action of the sequence
398         case LFUN_COMMAND_SEQUENCE: {
399                 // argument contains ';'-terminated commands
400                 string const firstcmd = token(to_utf8(cmd.argument()), ';', 0);
401                 FuncRequest func(lyxaction.lookupFunc(firstcmd));
402                 func.origin = cmd.origin;
403                 flag = getStatus(func);
404                 break;
405         }
406
407         // we want to check if at least one of these is enabled
408         case LFUN_COMMAND_ALTERNATIVES: {
409                 // argument contains ';'-terminated commands
410                 string arg = to_utf8(cmd.argument());
411                 while (!arg.empty()) {
412                         string first;
413                         arg = split(arg, first, ';');
414                         FuncRequest func(lyxaction.lookupFunc(first));
415                         func.origin = cmd.origin;
416                         flag = getStatus(func);
417                         // if this one is enabled, the whole thing is
418                         if (flag.enabled())
419                                 break;
420                 }
421                 break;
422         }
423
424         case LFUN_CALL: {
425                 FuncRequest func;
426                 string name = to_utf8(cmd.argument());
427                 if (theTopLevelCmdDef().lock(name, func)) {
428                         func.origin = cmd.origin;
429                         flag = getStatus(func);
430                         theTopLevelCmdDef().release(name);
431                 } else {
432                         // catch recursion or unknown command
433                         // definition. all operations until the
434                         // recursion or unknown command definition
435                         // occurs are performed, so set the state to
436                         // enabled
437                         enable = true;
438                 }
439                 break;
440         }
441
442         case LFUN_COMMAND_PREFIX:
443         case LFUN_CANCEL:
444         case LFUN_META_PREFIX:
445         case LFUN_RECONFIGURE:
446         case LFUN_DROP_LAYOUTS_CHOICE:
447         case LFUN_SERVER_GET_FILENAME:
448         case LFUN_SERVER_NOTIFY:
449         case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
450         case LFUN_REPEAT:
451         case LFUN_PREFERENCES_SAVE:
452         case LFUN_INSET_EDIT:
453         case LFUN_BUFFER_SAVE_AS_DEFAULT:
454         case LFUN_LYXRC_APPLY:
455                 // these are handled in our dispatch()
456                 break;
457
458         default:
459                 if (!theApp()) {
460                         enable = false;
461                         break;
462                 }
463                 if (theApp()->getStatus(cmd, flag))
464                         break;
465
466                 // Does the view know something?
467                 LyXView * lv = theApp()->currentWindow();
468                 if (!lv) {
469                         enable = false;
470                         break;
471                 }
472                 if (lv->getStatus(cmd, flag))
473                         break;
474
475                 BufferView * bv = lv->currentBufferView();
476                 BufferView * doc_bv = lv->documentBufferView();
477                 // If we do not have a BufferView, then other functions are disabled
478                 if (!bv) {
479                         enable = false;
480                         break;
481                 }
482                 // try the BufferView
483                 bool decided = bv->getStatus(cmd, flag);
484                 if (!decided)
485                         // try the Buffer
486                         decided = bv->buffer().getStatus(cmd, flag);
487                 if (!decided && doc_bv)
488                         // try the Document Buffer
489                         decided = doc_bv->buffer().getStatus(cmd, flag);
490         }
491
492         if (!enable)
493                 flag.setEnabled(false);
494
495         // the default error message if we disable the command
496         if (!flag.enabled() && flag.message().empty())
497                 flag.message(from_utf8(N_("Command disabled")));
498
499         return flag;
500 }
501
502
503 namespace {
504
505 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new);
506
507 } //namespace anon
508
509
510 void LyXFunc::dispatch(FuncRequest const & cmd)
511 {
512         string const argument = to_utf8(cmd.argument());
513         FuncCode const action = cmd.action;
514
515         LYXERR(Debug::ACTION, "\nLyXFunc::dispatch: cmd: " << cmd);
516         //lyxerr << "LyXFunc::dispatch: cmd: " << cmd << endl;
517
518         // we have not done anything wrong yet.
519         errorstat = false;
520         dispatch_buffer.erase();
521
522         // redraw the screen at the end (first of the two drawing steps).
523         //This is done unless explicitely requested otherwise
524         Update::flags updateFlags = Update::FitCursor;
525
526         LyXView * lv = theApp()->currentWindow();
527
528         FuncStatus const flag = getStatus(cmd);
529         if (!flag.enabled()) {
530                 // We cannot use this function here
531                 LYXERR(Debug::ACTION, "LyXFunc::dispatch: "
532                        << lyxaction.getActionName(action)
533                        << " [" << action << "] is disabled at this location");
534                 setErrorMessage(flag.message());
535                 if (lv)
536                         lv->restartCursor();
537         } else {
538                 Buffer * buffer = 0;
539                 if (lv && lv->currentBufferView())
540                         buffer = &lv->currentBufferView()->buffer();
541                 switch (action) {
542
543                 case LFUN_COMMAND_PREFIX:
544                         LASSERT(lv, /**/);
545                         lv->message(keyseq.printOptions(true));
546                         break;
547
548                 case LFUN_CANCEL:
549                         LASSERT(lv && lv->currentBufferView(), /**/);
550                         keyseq.reset();
551                         meta_fake_bit = NoModifier;
552                         if (buffer)
553                                 // cancel any selection
554                                 dispatch(FuncRequest(LFUN_MARK_OFF));
555                         setMessage(from_ascii(N_("Cancel")));
556                         break;
557
558                 case LFUN_META_PREFIX:
559                         meta_fake_bit = AltModifier;
560                         setMessage(keyseq.print(KeySequence::ForGui));
561                         break;
562
563                 // --- Menus -----------------------------------------------
564                 case LFUN_RECONFIGURE:
565                         // argument is any additional parameter to the configure.py command
566                         reconfigure(lv, argument);
567                         break;
568
569                 // --- lyxserver commands ----------------------------
570                 case LFUN_SERVER_GET_FILENAME:
571                         LASSERT(lv && buffer, /**/);
572                         setMessage(from_utf8(buffer->absFileName()));
573                         LYXERR(Debug::INFO, "FNAME["
574                                 << buffer->absFileName() << ']');
575                         break;
576
577                 case LFUN_SERVER_NOTIFY:
578                         dispatch_buffer = keyseq.print(KeySequence::Portable);
579                         theServer().notifyClient(to_utf8(dispatch_buffer));
580                         break;
581
582                 case LFUN_CITATION_INSERT: {
583                         LASSERT(lv, /**/);
584                         if (!argument.empty()) {
585                                 // we can have one optional argument, delimited by '|'
586                                 // citation-insert <key>|<text_before>
587                                 // this should be enhanced to also support text_after
588                                 // and citation style
589                                 string arg = argument;
590                                 string opt1;
591                                 if (contains(argument, "|")) {
592                                         arg = token(argument, '|', 0);
593                                         opt1 = token(argument, '|', 1);
594                                 }
595                                 InsetCommandParams icp(CITE_CODE);
596                                 icp["key"] = from_utf8(arg);
597                                 if (!opt1.empty())
598                                         icp["before"] = from_utf8(opt1);
599                                 string icstr = InsetCommand::params2string("citation", icp);
600                                 FuncRequest fr(LFUN_INSET_INSERT, icstr);
601                                 dispatch(fr);
602                         } else
603                                 dispatch(FuncRequest(LFUN_DIALOG_SHOW_NEW_INSET, "citation"));
604                         break;
605                 }
606
607                 case LFUN_CURSOR_FOLLOWS_SCROLLBAR_TOGGLE:
608                         LASSERT(lv, /**/);
609                         lyxrc.cursor_follows_scrollbar = !lyxrc.cursor_follows_scrollbar;
610                         break;
611
612                 case LFUN_REPEAT: {
613                         // repeat command
614                         string countstr;
615                         string rest = split(argument, countstr, ' ');
616                         istringstream is(countstr);
617                         int count = 0;
618                         is >> count;
619                         //lyxerr << "repeat: count: " << count << " cmd: " << rest << endl;
620                         for (int i = 0; i < count; ++i)
621                                 dispatch(lyxaction.lookupFunc(rest));
622                         break;
623                 }
624
625                 case LFUN_COMMAND_SEQUENCE: {
626                         // argument contains ';'-terminated commands
627                         string arg = argument;
628                         if (theBufferList().isLoaded(buffer))
629                                 buffer->undo().beginUndoGroup();
630                         while (!arg.empty()) {
631                                 string first;
632                                 arg = split(arg, first, ';');
633                                 FuncRequest func(lyxaction.lookupFunc(first));
634                                 func.origin = cmd.origin;
635                                 dispatch(func);
636                         }
637                         if (theBufferList().isLoaded(buffer))
638                                 buffer->undo().endUndoGroup();
639                         break;
640                 }
641
642                 case LFUN_COMMAND_ALTERNATIVES: {
643                         // argument contains ';'-terminated commands
644                         string arg = argument;
645                         while (!arg.empty()) {
646                                 string first;
647                                 arg = split(arg, first, ';');
648                                 FuncRequest func(lyxaction.lookupFunc(first));
649                                 func.origin = cmd.origin;
650                                 FuncStatus stat = getStatus(func);
651                                 if (stat.enabled()) {
652                                         dispatch(func);
653                                         break;
654                                 }
655                         }
656                         break;
657                 }
658
659                 case LFUN_CALL: {
660                         FuncRequest func;
661                         if (theTopLevelCmdDef().lock(argument, func)) {
662                                 func.origin = cmd.origin;
663                                 dispatch(func);
664                                 theTopLevelCmdDef().release(argument);
665                         } else {
666                                 if (func.action == LFUN_UNKNOWN_ACTION) {
667                                         // unknown command definition
668                                         lyxerr << "Warning: unknown command definition `"
669                                                    << argument << "'"
670                                                    << endl;
671                                 } else {
672                                         // recursion detected
673                                         lyxerr << "Warning: Recursion in the command definition `"
674                                                    << argument << "' detected"
675                                                    << endl;
676                                 }
677                         }
678                         break;
679                 }
680
681                 case LFUN_PREFERENCES_SAVE: {
682                         lyxrc.write(makeAbsPath("preferences",
683                                                 package().user_support().absFilename()),
684                                     false);
685                         break;
686                 }
687
688                 case LFUN_BUFFER_SAVE_AS_DEFAULT: {
689                         string const fname =
690                                 addName(addPath(package().user_support().absFilename(), "templates/"),
691                                         "defaults.lyx");
692                         Buffer defaults(fname);
693
694                         istringstream ss(argument);
695                         Lexer lex;
696                         lex.setStream(ss);
697                         int const unknown_tokens = defaults.readHeader(lex);
698
699                         if (unknown_tokens != 0) {
700                                 lyxerr << "Warning in LFUN_BUFFER_SAVE_AS_DEFAULT!\n"
701                                        << unknown_tokens << " unknown token"
702                                        << (unknown_tokens == 1 ? "" : "s")
703                                        << endl;
704                         }
705
706                         if (defaults.writeFile(FileName(defaults.absFileName())))
707                                 setMessage(bformat(_("Document defaults saved in %1$s"),
708                                                    makeDisplayPath(fname)));
709                         else
710                                 setErrorMessage(from_ascii(N_("Unable to save document defaults")));
711                         break;
712                 }
713
714                 case LFUN_LYXRC_APPLY: {
715                         // reset active key sequences, since the bindings
716                         // are updated (bug 6064)
717                         keyseq.reset();
718                         LyXRC const lyxrc_orig = lyxrc;
719
720                         istringstream ss(argument);
721                         bool const success = lyxrc.read(ss) == 0;
722
723                         if (!success) {
724                                 lyxerr << "Warning in LFUN_LYXRC_APPLY!\n"
725                                        << "Unable to read lyxrc data"
726                                        << endl;
727                                 break;
728                         }
729
730                         actOnUpdatedPrefs(lyxrc_orig, lyxrc);
731
732                         setSpellChecker();
733
734                         theApp()->resetGui();
735
736                         /// We force the redraw in any case because there might be
737                         /// some screen font changes.
738                         /// FIXME: only the current view will be updated. the Gui
739                         /// class is able to furnish the list of views.
740                         updateFlags = Update::Force;
741                         break;
742                 }
743
744                 case LFUN_BOOKMARK_GOTO:
745                         // go to bookmark, open unopened file and switch to buffer if necessary
746                         gotoBookmark(convert<unsigned int>(to_utf8(cmd.argument())), true, true);
747                         updateFlags = Update::FitCursor;
748                         break;
749
750                 case LFUN_BOOKMARK_CLEAR:
751                         theSession().bookmarks().clear();
752                         break;
753
754                 default:
755                         LASSERT(theApp(), /**/);
756                         // Let the frontend dispatch its own actions.
757                         if (theApp()->dispatch(cmd))
758                                 // Nothing more to do.
759                                 return;
760
761                         // Everything below is only for active window
762                         if (lv == 0)
763                                 break;
764
765                         // Start an undo group. This may be needed for
766                         // some stuff like inset-apply on labels.
767                         if (theBufferList().isLoaded(buffer))
768                                 buffer->undo().beginUndoGroup();
769                                 
770                         // Let the current LyXView dispatch its own actions.
771                         if (lv->dispatch(cmd)) {
772                                 BufferView * bv = lv->currentBufferView();
773                                 if (bv) {
774                                         buffer = &(bv->buffer());
775                                         updateFlags = bv->cursor().result().update();
776                                         if (theBufferList().isLoaded(buffer))
777                                                 buffer->undo().endUndoGroup();
778                                 }
779                                 break;
780                         }
781
782                         BufferView * bv = lv->currentBufferView();
783                         LASSERT(bv, /**/);
784
785                         // Let the current BufferView dispatch its own actions.
786                         if (bv->dispatch(cmd)) {
787                                 // The BufferView took care of its own updates if needed.
788                                 buffer = &(bv->buffer());
789                                 updateFlags = Update::None;
790                                 if (theBufferList().isLoaded(buffer))
791                                         buffer->undo().endUndoGroup();
792                                 break;
793                         }
794
795                         BufferView * doc_bv = lv->documentBufferView();
796                         // Try with the document BufferView dispatch if any.
797                         if (doc_bv && doc_bv->dispatch(cmd)) {
798                                 // The BufferView took care of its own updates if needed.
799                                 buffer = &(doc_bv->buffer());
800                                 updateFlags = Update::None;
801                                 if (theBufferList().isLoaded(buffer))
802                                         buffer->undo().endUndoGroup();
803                                 break;
804                         }
805
806                         // OK, so try the current Buffer itself...
807                         DispatchResult dr;
808                         bv->buffer().dispatch(cmd, dr);
809                         if (dr.dispatched()) {
810                                 updateFlags = dr.update();
811                                 break;
812                         }
813                         // and with the document Buffer.
814                         if (doc_bv) {
815                                 doc_bv->buffer().dispatch(cmd, dr);
816                                 if (dr.dispatched()) {
817                                         updateFlags = dr.update();
818                                         break;
819                                 }
820                         }
821
822                         // Is this a function that acts on inset at point?
823                         Inset * inset = bv->cursor().nextInset();
824                         if (lyxaction.funcHasFlag(action, LyXAction::AtPoint)
825                             && inset) {
826                                 bv->cursor().result().dispatched(true);
827                                 bv->cursor().result().update(Update::FitCursor | Update::Force);
828                                 FuncRequest tmpcmd = cmd;
829                                 inset->dispatch(bv->cursor(), tmpcmd);
830                                 if (bv->cursor().result().dispatched()) {
831                                         updateFlags = bv->cursor().result().update();
832                                         break;
833                                 }
834                         }
835
836                         // Let the current Cursor dispatch its own actions.
837                         Cursor old = bv->cursor();
838                         bv->cursor().getPos(cursorPosBeforeDispatchX_,
839                                                 cursorPosBeforeDispatchY_);
840                         bv->cursor().dispatch(cmd);
841
842                         // notify insets we just left
843                         if (bv->cursor() != old) {
844                                 old.fixIfBroken();
845                                 bool badcursor = notifyCursorLeavesOrEnters(old, bv->cursor());
846                                 if (badcursor)
847                                         bv->cursor().fixIfBroken();
848                         }
849
850                         if (theBufferList().isLoaded(buffer))
851                                 buffer->undo().endUndoGroup();
852
853                         // update completion. We do it here and not in
854                         // processKeySym to avoid another redraw just for a
855                         // changed inline completion
856                         if (cmd.origin == FuncRequest::KEYBOARD) {
857                                 if (cmd.action == LFUN_SELF_INSERT
858                                     || (cmd.action == LFUN_ERT_INSERT && bv->cursor().inMathed()))
859                                         lv->updateCompletion(bv->cursor(), true, true);
860                                 else if (cmd.action == LFUN_CHAR_DELETE_BACKWARD)
861                                         lv->updateCompletion(bv->cursor(), false, true);
862                                 else
863                                         lv->updateCompletion(bv->cursor(), false, false);
864                         }
865
866                         updateFlags = bv->cursor().result().update();
867                 }
868
869                 // if we executed a mutating lfun, mark the buffer as dirty
870                 if (theBufferList().isLoaded(buffer) && flag.enabled()
871                     && !lyxaction.funcHasFlag(action, LyXAction::NoBuffer)
872                     && !lyxaction.funcHasFlag(action, LyXAction::ReadOnly))
873                         buffer->markDirty();                    
874
875                 if (lv && lv->currentBufferView()) {
876                         // BufferView::update() updates the ViewMetricsInfo and
877                         // also initializes the position cache for all insets in
878                         // (at least partially) visible top-level paragraphs.
879                         // We will redraw the screen only if needed.
880                         lv->currentBufferView()->processUpdateFlags(updateFlags);
881
882                         // Do we have a selection?
883                         theSelection().haveSelection(
884                                 lv->currentBufferView()->cursor().selection());
885                         
886                         // update gui
887                         lv->restartCursor();
888                 }
889         }
890         if (lv) {
891                 // Some messages may already be translated, so we cannot use _()
892                 sendDispatchMessage(translateIfPossible(getMessage()), cmd);
893         }
894 }
895
896
897 void LyXFunc::sendDispatchMessage(docstring const & msg, FuncRequest const & cmd)
898 {
899         const bool verbose = (cmd.origin == FuncRequest::MENU
900                               || cmd.origin == FuncRequest::TOOLBAR
901                               || cmd.origin == FuncRequest::COMMANDBUFFER);
902
903         LyXView * lv = theApp()->currentWindow();
904         if (cmd.action == LFUN_SELF_INSERT || !verbose) {
905                 LYXERR(Debug::ACTION, "dispatch msg is " << to_utf8(msg));
906                 if (!msg.empty())
907                         lv->message(msg);
908                 return;
909         }
910
911         docstring dispatch_msg = msg;
912         if (!dispatch_msg.empty())
913                 dispatch_msg += ' ';
914
915         docstring comname = from_utf8(lyxaction.getActionName(cmd.action));
916
917         bool argsadded = false;
918
919         if (!cmd.argument().empty()) {
920                 if (cmd.action != LFUN_UNKNOWN_ACTION) {
921                         comname += ' ' + cmd.argument();
922                         argsadded = true;
923                 }
924         }
925
926         docstring const shortcuts = theTopLevelKeymap().printBindings(cmd, KeySequence::ForGui);
927
928         if (!shortcuts.empty())
929                 comname += ": " + shortcuts;
930         else if (!argsadded && !cmd.argument().empty())
931                 comname += ' ' + cmd.argument();
932
933         if (!comname.empty()) {
934                 comname = rtrim(comname);
935                 dispatch_msg += '(' + rtrim(comname) + ')';
936         }
937
938         LYXERR(Debug::ACTION, "verbose dispatch msg " << to_utf8(dispatch_msg));
939         if (!dispatch_msg.empty())
940                 lv->message(dispatch_msg);
941 }
942
943
944 // Each LyXView should have it's own message method. lyxview and
945 // the minibuffer would use the minibuffer, but lyxserver would
946 // send an ERROR signal to its client.  Alejandro 970603
947 // This function is bit problematic when it comes to NLS, to make the
948 // lyx servers client be language indepenent we must not translate
949 // strings sent to this func.
950 void LyXFunc::setErrorMessage(docstring const & m) const
951 {
952         dispatch_buffer = m;
953         errorstat = true;
954 }
955
956
957 void LyXFunc::setMessage(docstring const & m) const
958 {
959         dispatch_buffer = m;
960 }
961
962
963 docstring LyXFunc::viewStatusMessage()
964 {
965         // When meta-fake key is pressed, show the key sequence so far + "M-".
966         if (wasMetaKey())
967                 return keyseq.print(KeySequence::ForGui) + "M-";
968
969         // Else, when a non-complete key sequence is pressed,
970         // show the available options.
971         if (keyseq.length() > 0 && !keyseq.deleted())
972                 return keyseq.printOptions(true);
973
974         LyXView * lv = theApp()->currentWindow();
975         LASSERT(lv, /**/);
976         if (!lv->currentBufferView())
977                 return _("Welcome to LyX!");
978
979         return lv->currentBufferView()->cursor().currentState();
980 }
981
982
983 bool LyXFunc::wasMetaKey() const
984 {
985         return (meta_fake_bit != NoModifier);
986 }
987
988
989 namespace {
990
991 void actOnUpdatedPrefs(LyXRC const & lyxrc_orig, LyXRC const & lyxrc_new)
992 {
993         // Why the switch you might ask. It is a trick to ensure that all
994         // the elements in the LyXRCTags enum is handled. As you can see
995         // there are no breaks at all. So it is just a huge fall-through.
996         // The nice thing is that we will get a warning from the compiler
997         // if we forget an element.
998         LyXRC::LyXRCTags tag = LyXRC::RC_LAST;
999         switch (tag) {
1000         case LyXRC::RC_ACCEPT_COMPOUND:
1001         case LyXRC::RC_ALT_LANG:
1002         case LyXRC::RC_PLAINTEXT_LINELEN:
1003         case LyXRC::RC_PLAINTEXT_ROFF_COMMAND:
1004         case LyXRC::RC_AUTOCORRECTION_MATH:
1005         case LyXRC::RC_AUTOREGIONDELETE:
1006         case LyXRC::RC_AUTORESET_OPTIONS:
1007         case LyXRC::RC_AUTOSAVE:
1008         case LyXRC::RC_AUTO_NUMBER:
1009         case LyXRC::RC_BACKUPDIR_PATH:
1010         case LyXRC::RC_BIBTEX_ALTERNATIVES:
1011         case LyXRC::RC_BIBTEX_COMMAND:
1012         case LyXRC::RC_BINDFILE:
1013         case LyXRC::RC_CHECKLASTFILES:
1014         case LyXRC::RC_COMPLETION_CURSOR_TEXT:
1015         case LyXRC::RC_COMPLETION_INLINE_DELAY:
1016         case LyXRC::RC_COMPLETION_INLINE_DOTS:
1017         case LyXRC::RC_COMPLETION_INLINE_MATH:
1018         case LyXRC::RC_COMPLETION_INLINE_TEXT:
1019         case LyXRC::RC_COMPLETION_POPUP_AFTER_COMPLETE:
1020         case LyXRC::RC_COMPLETION_POPUP_DELAY:
1021         case LyXRC::RC_COMPLETION_POPUP_MATH:
1022         case LyXRC::RC_COMPLETION_POPUP_TEXT:
1023         case LyXRC::RC_USELASTFILEPOS:
1024         case LyXRC::RC_LOADSESSION:
1025         case LyXRC::RC_CHKTEX_COMMAND:
1026         case LyXRC::RC_CONVERTER:
1027         case LyXRC::RC_CONVERTER_CACHE_MAXAGE:
1028         case LyXRC::RC_COPIER:
1029         case LyXRC::RC_CURSOR_FOLLOWS_SCROLLBAR:
1030         case LyXRC::RC_SCROLL_BELOW_DOCUMENT:
1031         case LyXRC::RC_DATE_INSERT_FORMAT:
1032         case LyXRC::RC_DEFAULT_LANGUAGE:
1033         case LyXRC::RC_GUI_LANGUAGE:
1034         case LyXRC::RC_DEFAULT_PAPERSIZE:
1035         case LyXRC::RC_DEFAULT_VIEW_FORMAT:
1036         case LyXRC::RC_DEFFILE:
1037         case LyXRC::RC_DIALOGS_ICONIFY_WITH_MAIN:
1038         case LyXRC::RC_DISPLAY_GRAPHICS:
1039         case LyXRC::RC_DOCUMENTPATH:
1040                 if (lyxrc_orig.document_path != lyxrc_new.document_path) {
1041                         FileName path(lyxrc_new.document_path);
1042                         if (path.exists() && path.isDirectory())
1043                                 package().document_dir() = FileName(lyxrc.document_path);
1044                 }
1045         case LyXRC::RC_EDITOR_ALTERNATIVES:
1046         case LyXRC::RC_ESC_CHARS:
1047         case LyXRC::RC_EXAMPLEPATH:
1048         case LyXRC::RC_FONT_ENCODING:
1049         case LyXRC::RC_FORMAT:
1050         case LyXRC::RC_GROUP_LAYOUTS:
1051         case LyXRC::RC_HUNSPELLDIR_PATH:
1052         case LyXRC::RC_INDEX_ALTERNATIVES:
1053         case LyXRC::RC_INDEX_COMMAND:
1054         case LyXRC::RC_JBIBTEX_COMMAND:
1055         case LyXRC::RC_JINDEX_COMMAND:
1056         case LyXRC::RC_NOMENCL_COMMAND:
1057         case LyXRC::RC_INPUT:
1058         case LyXRC::RC_KBMAP:
1059         case LyXRC::RC_KBMAP_PRIMARY:
1060         case LyXRC::RC_KBMAP_SECONDARY:
1061         case LyXRC::RC_LABEL_INIT_LENGTH:
1062         case LyXRC::RC_LANGUAGE_AUTO_BEGIN:
1063         case LyXRC::RC_LANGUAGE_AUTO_END:
1064         case LyXRC::RC_LANGUAGE_COMMAND_BEGIN:
1065         case LyXRC::RC_LANGUAGE_COMMAND_END:
1066         case LyXRC::RC_LANGUAGE_COMMAND_LOCAL:
1067         case LyXRC::RC_LANGUAGE_GLOBAL_OPTIONS:
1068         case LyXRC::RC_LANGUAGE_PACKAGE:
1069         case LyXRC::RC_LANGUAGE_USE_BABEL:
1070         case LyXRC::RC_MAC_LIKE_WORD_MOVEMENT:
1071         case LyXRC::RC_MACRO_EDIT_STYLE:
1072         case LyXRC::RC_MAKE_BACKUP:
1073         case LyXRC::RC_MARK_FOREIGN_LANGUAGE:
1074         case LyXRC::RC_MOUSE_WHEEL_SPEED:
1075         case LyXRC::RC_NUMLASTFILES:
1076         case LyXRC::RC_PARAGRAPH_MARKERS:
1077         case LyXRC::RC_PATH_PREFIX:
1078                 if (lyxrc_orig.path_prefix != lyxrc_new.path_prefix) {
1079                         prependEnvPath("PATH", lyxrc.path_prefix);
1080                 }
1081         case LyXRC::RC_PERS_DICT:
1082         case LyXRC::RC_PREVIEW:
1083         case LyXRC::RC_PREVIEW_HASHED_LABELS:
1084         case LyXRC::RC_PREVIEW_SCALE_FACTOR:
1085         case LyXRC::RC_PRINTCOLLCOPIESFLAG:
1086         case LyXRC::RC_PRINTCOPIESFLAG:
1087         case LyXRC::RC_PRINTER:
1088         case LyXRC::RC_PRINTEVENPAGEFLAG:
1089         case LyXRC::RC_PRINTEXSTRAOPTIONS:
1090         case LyXRC::RC_PRINTFILEEXTENSION:
1091         case LyXRC::RC_PRINTLANDSCAPEFLAG:
1092         case LyXRC::RC_PRINTODDPAGEFLAG:
1093         case LyXRC::RC_PRINTPAGERANGEFLAG:
1094         case LyXRC::RC_PRINTPAPERDIMENSIONFLAG:
1095         case LyXRC::RC_PRINTPAPERFLAG:
1096         case LyXRC::RC_PRINTREVERSEFLAG:
1097         case LyXRC::RC_PRINTSPOOL_COMMAND:
1098         case LyXRC::RC_PRINTSPOOL_PRINTERPREFIX:
1099         case LyXRC::RC_PRINTTOFILE:
1100         case LyXRC::RC_PRINTTOPRINTER:
1101         case LyXRC::RC_PRINT_ADAPTOUTPUT:
1102         case LyXRC::RC_PRINT_COMMAND:
1103         case LyXRC::RC_RTL_SUPPORT:
1104         case LyXRC::RC_SCREEN_DPI:
1105         case LyXRC::RC_SCREEN_FONT_ROMAN:
1106         case LyXRC::RC_SCREEN_FONT_ROMAN_FOUNDRY:
1107         case LyXRC::RC_SCREEN_FONT_SANS:
1108         case LyXRC::RC_SCREEN_FONT_SANS_FOUNDRY:
1109         case LyXRC::RC_SCREEN_FONT_SCALABLE:
1110         case LyXRC::RC_SCREEN_FONT_SIZES:
1111         case LyXRC::RC_SCREEN_FONT_TYPEWRITER:
1112         case LyXRC::RC_SCREEN_FONT_TYPEWRITER_FOUNDRY:
1113         case LyXRC::RC_GEOMETRY_SESSION:
1114         case LyXRC::RC_SCREEN_ZOOM:
1115         case LyXRC::RC_SERVERPIPE:
1116         case LyXRC::RC_SET_COLOR:
1117         case LyXRC::RC_SHOW_BANNER:
1118         case LyXRC::RC_OPEN_BUFFERS_IN_TABS:
1119         case LyXRC::RC_SPELL_COMMAND:
1120         case LyXRC::RC_SPELLCHECKER:
1121         case LyXRC::RC_SPELLCHECK_CONTINUOUSLY:
1122         case LyXRC::RC_SPLITINDEX_COMMAND:
1123         case LyXRC::RC_TEMPDIRPATH:
1124         case LyXRC::RC_TEMPLATEPATH:
1125         case LyXRC::RC_TEX_ALLOWS_SPACES:
1126         case LyXRC::RC_TEX_EXPECTS_WINDOWS_PATHS:
1127                 if (lyxrc_orig.windows_style_tex_paths != lyxrc_new.windows_style_tex_paths) {
1128                         os::windows_style_tex_paths(lyxrc_new.windows_style_tex_paths);
1129                 }
1130         case LyXRC::RC_THESAURUSDIRPATH:
1131         case LyXRC::RC_UIFILE:
1132         case LyXRC::RC_USER_EMAIL:
1133         case LyXRC::RC_USER_NAME:
1134         case LyXRC::RC_USETEMPDIR:
1135         case LyXRC::RC_USE_ALT_LANG:
1136         case LyXRC::RC_USE_CONVERTER_CACHE:
1137         case LyXRC::RC_USE_ESC_CHARS:
1138         case LyXRC::RC_USE_INP_ENC:
1139         case LyXRC::RC_USE_PERS_DICT:
1140         case LyXRC::RC_USE_TOOLTIP:
1141         case LyXRC::RC_USE_PIXMAP_CACHE:
1142         case LyXRC::RC_USE_SPELL_LIB:
1143         case LyXRC::RC_VIEWDVI_PAPEROPTION:
1144         case LyXRC::RC_SORT_LAYOUTS:
1145         case LyXRC::RC_FULL_SCREEN_LIMIT:
1146         case LyXRC::RC_FULL_SCREEN_SCROLLBAR:
1147         case LyXRC::RC_FULL_SCREEN_MENUBAR:
1148         case LyXRC::RC_FULL_SCREEN_TABBAR:
1149         case LyXRC::RC_FULL_SCREEN_TOOLBARS:
1150         case LyXRC::RC_FULL_SCREEN_WIDTH:
1151         case LyXRC::RC_VISUAL_CURSOR:
1152         case LyXRC::RC_VIEWER:
1153         case LyXRC::RC_VIEWER_ALTERNATIVES:
1154         case LyXRC::RC_LAST:
1155                 break;
1156         }
1157 }
1158
1159 } // namespace anon
1160 } // namespace lyx