]> git.lyx.org Git - lyx.git/blob - src/Text3.cpp
680494835dd8944618699dc2844b6716a57a2ed1
[lyx.git] / src / Text3.cpp
1 /**
2  * \file Text3.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Alfredo Braunstein
9  * \author Angus Leeming
10  * \author John Levon
11  * \author André Pönitz
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "Text.h"
19
20 #include "Bidi.h"
21 #include "BranchList.h"
22 #include "FloatList.h"
23 #include "FuncStatus.h"
24 #include "Buffer.h"
25 #include "buffer_funcs.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
28 #include "Changes.h"
29 #include "Cursor.h"
30 #include "CutAndPaste.h"
31 #include "DispatchResult.h"
32 #include "ErrorList.h"
33 #include "factory.h"
34 #include "FuncRequest.h"
35 #include "InsetList.h"
36 #include "Intl.h"
37 #include "Language.h"
38 #include "Layout.h"
39 #include "LyXAction.h"
40 #include "LyXFunc.h"
41 #include "Lexer.h"
42 #include "LyXRC.h"
43 #include "Paragraph.h"
44 #include "ParagraphParameters.h"
45 #include "SpellChecker.h"
46 #include "TextClass.h"
47 #include "TextMetrics.h"
48 #include "VSpace.h"
49 #include "WordLangTuple.h"
50
51 #include "frontends/Application.h"
52 #include "frontends/Clipboard.h"
53 #include "frontends/Selection.h"
54
55 #include "insets/InsetCollapsable.h"
56 #include "insets/InsetCommand.h"
57 #include "insets/InsetExternal.h"
58 #include "insets/InsetFloat.h"
59 #include "insets/InsetFloatList.h"
60 #include "insets/InsetGraphics.h"
61 #include "insets/InsetGraphicsParams.h"
62 #include "insets/InsetNewline.h"
63 #include "insets/InsetQuotes.h"
64 #include "insets/InsetSpecialChar.h"
65 #include "insets/InsetText.h"
66 #include "insets/InsetWrap.h"
67
68 #include "support/convert.h"
69 #include "support/debug.h"
70 #include "support/gettext.h"
71 #include "support/lstrings.h"
72 #include "support/lyxtime.h"
73 #include "support/os.h"
74
75 #include "mathed/InsetMathHull.h"
76 #include "mathed/MathMacroTemplate.h"
77
78 #include <boost/next_prior.hpp>
79
80 #include <clocale>
81 #include <sstream>
82
83 using namespace std;
84 using namespace lyx::support;
85
86 namespace lyx {
87
88 using cap::copySelection;
89 using cap::cutSelection;
90 using cap::pasteFromStack;
91 using cap::pasteClipboardText;
92 using cap::pasteClipboardGraphics;
93 using cap::replaceSelection;
94 using cap::grabAndEraseSelection;
95 using cap::selClearOrDel;
96
97 // globals...
98 static Font freefont(ignore_font, ignore_language);
99 static bool toggleall = false;
100
101 static void toggleAndShow(Cursor & cur, Text * text,
102         Font const & font, bool toggleall = true)
103 {
104         text->toggleFree(cur, font, toggleall);
105
106         if (font.language() != ignore_language ||
107             font.fontInfo().number() != FONT_IGNORE) {
108                 TextMetrics const & tm = cur.bv().textMetrics(text);
109                 if (cur.boundary() != tm.isRTLBoundary(cur.pit(), cur.pos(),
110                                                        cur.real_current_font))
111                         text->setCursor(cur, cur.pit(), cur.pos(),
112                                         false, !cur.boundary());
113         }
114 }
115
116
117 static void moveCursor(Cursor & cur, bool selecting)
118 {
119         if (selecting || cur.mark())
120                 cur.setSelection();
121 }
122
123
124 static void finishChange(Cursor & cur, bool selecting)
125 {
126         cur.finishUndo();
127         moveCursor(cur, selecting);
128 }
129
130
131 static void mathDispatch(Cursor & cur, FuncRequest const & cmd, bool display)
132 {
133         cur.recordUndo();
134         docstring sel = cur.selectionAsString(false);
135
136         // It may happen that sel is empty but there is a selection
137         replaceSelection(cur);
138
139         // Is this a valid formula?
140         bool valid = true;
141
142         if (sel.empty()) {
143 #ifdef ENABLE_ASSERTIONS
144                 const int old_pos = cur.pos();
145 #endif
146                 cur.insert(new InsetMathHull(cur.buffer(), hullSimple));
147 #ifdef ENABLE_ASSERTIONS
148                 LASSERT(old_pos == cur.pos(), /**/);
149 #endif
150                 cur.nextInset()->edit(cur, true);
151                 // don't do that also for LFUN_MATH_MODE
152                 // unless you want end up with always changing
153                 // to mathrm when opening an inlined inset --
154                 // I really hate "LyXfunc overloading"...
155                 if (display)
156                         cur.dispatch(FuncRequest(LFUN_MATH_DISPLAY));
157                 // Avoid an unnecessary undo step if cmd.argument
158                 // is empty
159                 if (!cmd.argument().empty())
160                         cur.dispatch(FuncRequest(LFUN_MATH_INSERT,
161                                                  cmd.argument()));
162         } else {
163                 // create a macro if we see "\\newcommand"
164                 // somewhere, and an ordinary formula
165                 // otherwise
166                 if (sel.find(from_ascii("\\newcommand")) == string::npos
167                                 && sel.find(from_ascii("\\newlyxcommand")) == string::npos
168                                 && sel.find(from_ascii("\\def")) == string::npos)
169                 {
170                         InsetMathHull * formula = new InsetMathHull(cur.buffer());
171                         string const selstr = to_utf8(sel);
172                         istringstream is(selstr);
173                         Lexer lex;
174                         lex.setStream(is);
175                         if (!formula->readQuiet(lex)) {
176                                 // No valid formula, let's try with delims
177                                 is.str("$" + selstr + "$");
178                                 lex.setStream(is);
179                                 if (!formula->readQuiet(lex)) {
180                                         // Still not valid, leave it as is
181                                         valid = false;
182                                         delete formula;
183                                         cur.insert(sel);
184                                 } else
185                                         cur.insert(formula);
186                         } else
187                                 cur.insert(formula);
188                 } else {
189                         cur.insert(new MathMacroTemplate(cur.buffer(), sel));
190                 }
191         }
192         if (valid)
193                 cur.message(from_utf8(N_("Math editor mode")));
194         else
195                 cur.message(from_utf8(N_("No valid math formula")));
196 }
197
198
199 void regexpDispatch(Cursor & cur, FuncRequest const & cmd)
200 {
201         BOOST_ASSERT(cmd.action == LFUN_REGEXP_MODE);
202         if (cur.inRegexped()) {
203                 cur.message(_("Already in regular expression mode"));
204                 return;
205         }
206         cur.recordUndo();
207         docstring sel = cur.selectionAsString(false);
208
209         // It may happen that sel is empty but there is a selection
210         replaceSelection(cur);
211
212         cur.insert(new InsetMathHull(cur.buffer(), hullRegexp));
213         cur.nextInset()->edit(cur, true);
214         cur.niceInsert(sel);
215
216         cur.message(_("Regexp editor mode"));
217 }
218
219
220 static void specialChar(Cursor & cur, InsetSpecialChar::Kind kind)
221 {
222         cur.recordUndo();
223         cap::replaceSelection(cur);
224         cur.insert(new InsetSpecialChar(kind));
225         cur.posForward();
226 }
227
228
229 static bool doInsertInset(Cursor & cur, Text * text,
230         FuncRequest const & cmd, bool edit, bool pastesel)
231 {
232         Buffer & buffer = cur.bv().buffer();
233         BufferParams const & bparams = buffer.params();
234         Inset * inset = createInset(&buffer, cmd);
235         if (!inset)
236                 return false;
237
238         if (InsetCollapsable * ci = inset->asInsetCollapsable())
239                 ci->setButtonLabel();
240
241         cur.recordUndo();
242         if (cmd.action == LFUN_INDEX_INSERT) {
243                 docstring ds = subst(text->getStringToIndex(cur), '\n', ' ');
244                 text->insertInset(cur, inset);
245                 if (edit)
246                         inset->edit(cur, true);
247                 // Now put this into inset
248                 cur.text()->insertStringAsLines(cur, ds, cur.current_font);
249                 cur.leaveInset(*inset);
250                 return true;
251         }
252
253         bool gotsel = false;
254         if (cur.selection()) {
255                 cutSelection(cur, false, pastesel);
256                 cur.clearSelection();
257                 gotsel = true;
258         }
259         text->insertInset(cur, inset);
260
261         if (edit)
262                 inset->edit(cur, true);
263
264         if (!gotsel || !pastesel)
265                 return true;
266
267         pasteFromStack(cur, cur.buffer()->errorList("Paste"), 0);
268         cur.buffer()->errors("Paste");
269         cur.clearSelection(); // bug 393
270         cur.finishUndo();
271         InsetText * insetText = dynamic_cast<InsetText *>(inset);
272         if (insetText) {
273                 insetText->fixParagraphsFont();
274                 if (!insetText->allowMultiPar() || cur.lastpit() == 0) {
275                         // reset first par to default
276                         cur.text()->paragraphs().begin()
277                                 ->setPlainOrDefaultLayout(bparams.documentClass());
278                         cur.pos() = 0;
279                         cur.pit() = 0;
280                         // Merge multiple paragraphs -- hack
281                         while (cur.lastpit() > 0)
282                                 mergeParagraph(bparams, cur.text()->paragraphs(), 0);
283                         cur.leaveInset(*inset);
284                 }
285         } else {
286                 cur.leaveInset(*inset);
287                 // reset surrounding par to default
288                 DocumentClass const & dc = bparams.documentClass();
289                 docstring const layoutname = inset->usePlainLayout()
290                         ? dc.plainLayoutName()
291                         : dc.defaultLayoutName();
292                 text->setLayout(cur, layoutname);
293         }
294         return true;
295 }
296
297
298 string const freefont2string()
299 {
300         return freefont.toString(toggleall);
301 }
302
303
304 /// the type of outline operation
305 enum OutlineOp {
306         OutlineUp, // Move this header with text down
307         OutlineDown,   // Move this header with text up
308         OutlineIn, // Make this header deeper
309         OutlineOut // Make this header shallower
310 };
311
312
313 static void outline(OutlineOp mode, Cursor & cur)
314 {
315         Buffer & buf = *cur.buffer();
316         pit_type & pit = cur.pit();
317         ParagraphList & pars = buf.text().paragraphs();
318         ParagraphList::iterator const bgn = pars.begin();
319         // The first paragraph of the area to be copied:
320         ParagraphList::iterator start = boost::next(bgn, pit);
321         // The final paragraph of area to be copied:
322         ParagraphList::iterator finish = start;
323         ParagraphList::iterator const end = pars.end();
324
325         DocumentClass const & tc = buf.params().documentClass();
326
327         int const thistoclevel = start->layout().toclevel;
328         int toclevel;
329
330         // Move out (down) from this section header
331         if (finish != end)
332                 ++finish;
333
334         // Seek the one (on same level) below
335         for (; finish != end; ++finish) {
336                 toclevel = finish->layout().toclevel;
337                 if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
338                         break;
339         }
340
341         switch (mode) {
342                 case OutlineUp: {
343                         if (start == pars.begin())
344                                 // Nothing to move.
345                                 return;
346                         ParagraphList::iterator dest = start;
347                         // Move out (up) from this header
348                         if (dest == bgn)
349                                 return;
350                         // Search previous same-level header above
351                         do {
352                                 --dest;
353                                 toclevel = dest->layout().toclevel;
354                         } while(dest != bgn
355                                 && (toclevel == Layout::NOT_IN_TOC
356                                     || toclevel > thistoclevel));
357                         // Not found; do nothing
358                         if (toclevel == Layout::NOT_IN_TOC || toclevel > thistoclevel)
359                                 return;
360                         pit_type const newpit = distance(bgn, dest);
361                         pit_type const len = distance(start, finish);
362                         pit_type const deletepit = pit + len;
363                         buf.undo().recordUndo(cur, ATOMIC_UNDO, newpit, deletepit - 1);
364                         pars.splice(dest, start, finish);
365                         cur.pit() = newpit;
366                         break;
367                 }
368                 case OutlineDown: {
369                         if (finish == end)
370                                 // Nothing to move.
371                                 return;
372                         // Go one down from *this* header:
373                         ParagraphList::iterator dest = boost::next(finish, 1);
374                         // Go further down to find header to insert in front of:
375                         for (; dest != end; ++dest) {
376                                 toclevel = dest->layout().toclevel;
377                                 if (toclevel != Layout::NOT_IN_TOC
378                                       && toclevel <= thistoclevel)
379                                         break;
380                         }
381                         // One such was found:
382                         pit_type newpit = distance(bgn, dest);
383                         buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, newpit - 1);
384                         pit_type const len = distance(start, finish);
385                         pars.splice(dest, start, finish);
386                         cur.pit() = newpit - len;
387                         break;
388                 }
389                 case OutlineIn: {
390                         pit_type const len = distance(start, finish);
391                         buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1);
392                         for (; start != finish; ++start) {
393                                 toclevel = start->layout().toclevel;
394                                 if (toclevel == Layout::NOT_IN_TOC)
395                                         continue;
396                                 DocumentClass::const_iterator lit = tc.begin();
397                                 DocumentClass::const_iterator len = tc.end();
398                                 for (; lit != len; ++lit) {
399                                         if (lit->toclevel == toclevel + 1 &&
400                                             start->layout().labeltype == lit->labeltype) {
401                                                 start->setLayout(*lit);
402                                                 break;
403                                         }
404                                 }
405                         }
406                         break;
407                 }
408                 case OutlineOut: {
409                         pit_type const len = distance(start, finish);
410                         buf.undo().recordUndo(cur, ATOMIC_UNDO, pit, pit + len - 1);
411                         for (; start != finish; ++start) {
412                                 toclevel = start->layout().toclevel;
413                                 if (toclevel == Layout::NOT_IN_TOC)
414                                         continue;
415                                 DocumentClass::const_iterator lit = tc.begin();
416                                 DocumentClass::const_iterator len = tc.end();
417                                 for (; lit != len; ++lit) {
418                                         if (lit->toclevel == toclevel - 1 &&
419                                                 start->layout().labeltype == lit->labeltype) {
420                                                         start->setLayout(*lit);
421                                                         break;
422                                         }
423                                 }
424                         }
425                         break;
426                 }
427         }
428 }
429
430
431 void Text::number(Cursor & cur)
432 {
433         FontInfo font = ignore_font;
434         font.setNumber(FONT_TOGGLE);
435         toggleAndShow(cur, this, Font(font, ignore_language));
436 }
437
438
439 bool Text::isRTL(Paragraph const & par) const
440 {
441         Buffer const & buffer = owner_->buffer();
442         return par.isRTL(buffer.params());
443 }
444
445
446 void Text::dispatch(Cursor & cur, FuncRequest & cmd)
447 {
448         LYXERR(Debug::ACTION, "Text::dispatch: cmd: " << cmd);
449
450         // Dispatch if the cursor is inside the text. It is not the
451         // case for context menus (bug 5797).
452         if (cur.text() != this) {
453                 cur.undispatched();
454                 return;
455         }
456
457         BufferView * bv = &cur.bv();
458         TextMetrics * tm = &bv->textMetrics(this);
459         if (!tm->contains(cur.pit())) {
460                 lyx::dispatch(FuncRequest(LFUN_SCREEN_SHOW_CURSOR));
461                 tm = &bv->textMetrics(this);
462         }
463
464         // FIXME: We use the update flag to indicates wether a singlePar or a
465         // full screen update is needed. We reset it here but shall we restore it
466         // at the end?
467         cur.noUpdate();
468
469         LASSERT(cur.text() == this, /**/);
470         CursorSlice oldTopSlice = cur.top();
471         bool oldBoundary = cur.boundary();
472         bool sel = cur.selection();
473         // Signals that, even if needsUpdate == false, an update of the
474         // cursor paragraph is required
475         bool singleParUpdate = lyxaction.funcHasFlag(cmd.action,
476                 LyXAction::SingleParUpdate);
477         // Signals that a full-screen update is required
478         bool needsUpdate = !(lyxaction.funcHasFlag(cmd.action,
479                 LyXAction::NoUpdate) || singleParUpdate);
480
481         switch (cmd.action) {
482
483         case LFUN_PARAGRAPH_MOVE_DOWN: {
484                 pit_type const pit = cur.pit();
485                 recUndo(cur, pit, pit + 1);
486                 cur.finishUndo();
487                 pars_.swap(pit, pit + 1);
488                 cur.buffer()->updateLabels();
489                 needsUpdate = true;
490                 ++cur.pit();
491                 break;
492         }
493
494         case LFUN_PARAGRAPH_MOVE_UP: {
495                 pit_type const pit = cur.pit();
496                 recUndo(cur, pit - 1, pit);
497                 cur.finishUndo();
498                 pars_.swap(pit, pit - 1);
499                 cur.buffer()->updateLabels();
500                 --cur.pit();
501                 needsUpdate = true;
502                 break;
503         }
504
505         case LFUN_APPENDIX: {
506                 Paragraph & par = cur.paragraph();
507                 bool start = !par.params().startOfAppendix();
508
509 // FIXME: The code below only makes sense at top level.
510 // Should LFUN_APPENDIX be restricted to top-level paragraphs?
511                 // ensure that we have only one start_of_appendix in this document
512                 // FIXME: this don't work for multipart document!
513                 for (pit_type tmp = 0, end = pars_.size(); tmp != end; ++tmp) {
514                         if (pars_[tmp].params().startOfAppendix()) {
515                                 recUndo(cur, tmp);
516                                 pars_[tmp].params().startOfAppendix(false);
517                                 break;
518                         }
519                 }
520
521                 cur.recordUndo();
522                 par.params().startOfAppendix(start);
523
524                 // we can set the refreshing parameters now
525                 cur.buffer()->updateLabels();
526                 break;
527         }
528
529         case LFUN_WORD_DELETE_FORWARD:
530                 if (cur.selection())
531                         cutSelection(cur, true, false);
532                 else
533                         deleteWordForward(cur);
534                 finishChange(cur, false);
535                 break;
536
537         case LFUN_WORD_DELETE_BACKWARD:
538                 if (cur.selection())
539                         cutSelection(cur, true, false);
540                 else
541                         deleteWordBackward(cur);
542                 finishChange(cur, false);
543                 break;
544
545         case LFUN_LINE_DELETE:
546                 if (cur.selection())
547                         cutSelection(cur, true, false);
548                 else
549                         tm->deleteLineForward(cur);
550                 finishChange(cur, false);
551                 break;
552
553         case LFUN_BUFFER_BEGIN:
554         case LFUN_BUFFER_BEGIN_SELECT:
555                 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_BEGIN_SELECT);
556                 if (cur.depth() == 1)
557                         needsUpdate |= cursorTop(cur);
558                 else
559                         cur.undispatched();
560                 cur.updateFlags(Update::FitCursor);
561                 break;
562
563         case LFUN_BUFFER_END:
564         case LFUN_BUFFER_END_SELECT:
565                 needsUpdate |= cur.selHandle(cmd.action == LFUN_BUFFER_END_SELECT);
566                 if (cur.depth() == 1)
567                         needsUpdate |= cursorBottom(cur);
568                 else
569                         cur.undispatched();
570                 cur.updateFlags(Update::FitCursor);
571                 break;
572
573         case LFUN_INSET_BEGIN:
574         case LFUN_INSET_BEGIN_SELECT:
575                 needsUpdate |= cur.selHandle(cmd.action == LFUN_INSET_BEGIN_SELECT);
576                 if (cur.depth() == 1 || !cur.top().at_begin())
577                         needsUpdate |= cursorTop(cur);
578                 else
579                         cur.undispatched();
580                 cur.updateFlags(Update::FitCursor);
581                 break;
582
583         case LFUN_INSET_END:
584         case LFUN_INSET_END_SELECT:
585                 needsUpdate |= cur.selHandle(cmd.action == LFUN_INSET_END_SELECT);
586                 if (cur.depth() == 1 || !cur.top().at_end())
587                         needsUpdate |= cursorBottom(cur);
588                 else
589                         cur.undispatched();
590                 cur.updateFlags(Update::FitCursor);
591                 break;
592
593         case LFUN_INSET_SELECT_ALL:
594                 if (cur.depth() == 1 || !cur.selection() || !cur.selBegin().at_begin()
595                           || !cur.selEnd().at_end()) {
596                         needsUpdate |= cur.selHandle(false);
597                         needsUpdate |= cursorTop(cur);
598                         needsUpdate |= cur.selHandle(true);
599                         needsUpdate |= cursorBottom(cur);
600                 } else 
601                         cur.undispatched();
602                 cur.updateFlags(Update::FitCursor);
603                 break;
604
605         case LFUN_CHAR_FORWARD:
606         case LFUN_CHAR_FORWARD_SELECT:
607                 //LYXERR0(" LFUN_CHAR_FORWARD[SEL]:\n" << cur);
608                 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
609                 needsUpdate |= cursorForward(cur);
610
611                 if (!needsUpdate && oldTopSlice == cur.top()
612                                 && cur.boundary() == oldBoundary) {
613                         cur.undispatched();
614                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
615                 
616                         // we will probably be moving out the inset, so we should execute
617                         // the depm-mechanism, but only when the cursor has a place to 
618                         // go outside this inset, i.e. in a slice above.
619                         if (cur.depth() > 1 && cur.pos() == cur.lastpos() 
620                                   && cur.pit() == cur.lastpit()) {
621                                 // The cursor hasn't changed yet. To give the 
622                                 // DEPM the possibility of doing something we must
623                                 // provide it with two different cursors.
624                                 Cursor dummy = cur;
625                                 dummy.pos() = dummy.pit() = 0;
626                                 cur.bv().checkDepm(dummy, cur);
627                         }
628                 }
629                 break;
630
631         case LFUN_CHAR_BACKWARD:
632         case LFUN_CHAR_BACKWARD_SELECT:
633                 //lyxerr << "handle LFUN_CHAR_BACKWARD[_SELECT]:\n" << cur << endl;
634                 needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
635                 needsUpdate |= cursorBackward(cur);
636
637                 if (!needsUpdate && oldTopSlice == cur.top()
638                         && cur.boundary() == oldBoundary) {
639                         cur.undispatched();
640                         cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
641
642                         // we will probably be moving out the inset, so we should execute
643                         // the depm-mechanism, but only when the cursor has a place to 
644                         // go outside this inset, i.e. in a slice above.
645                         if (cur.depth() > 1 && cur.pos() == 0 && cur.pit() == 0) {
646                                 // The cursor hasn't changed yet. To give the 
647                                 // DEPM the possibility of doing something we must
648                                 // provide it with two different cursors.
649                                 Cursor dummy = cur;
650                                 dummy.pos() = cur.lastpos();
651                                 dummy.pit() = cur.lastpit();
652                                 cur.bv().checkDepm(dummy, cur);
653                         }
654                 }
655                 break;
656
657         case LFUN_CHAR_LEFT:
658         case LFUN_CHAR_LEFT_SELECT:
659                 if (lyxrc.visual_cursor) {
660                         needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_LEFT_SELECT);
661                         needsUpdate |= cursorVisLeft(cur);
662                         if (!needsUpdate && oldTopSlice == cur.top()
663                                         && cur.boundary() == oldBoundary) {
664                                 cur.undispatched();
665                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
666                         }
667                 } else {
668                         if (reverseDirectionNeeded(cur)) {
669                                 cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
670                                         LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD;
671                         } else {
672                                 cmd.action = cmd.action == LFUN_CHAR_LEFT_SELECT ?
673                                         LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD;
674                         }
675                         dispatch(cur, cmd);
676                         return;
677                 }
678                 break;
679
680         case LFUN_CHAR_RIGHT:
681         case LFUN_CHAR_RIGHT_SELECT:
682                 if (lyxrc.visual_cursor) {
683                         needsUpdate |= cur.selHandle(cmd.action == LFUN_CHAR_RIGHT_SELECT);
684                         needsUpdate |= cursorVisRight(cur);
685                         if (!needsUpdate && oldTopSlice == cur.top()
686                                         && cur.boundary() == oldBoundary) {
687                                 cur.undispatched();
688                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
689                         }
690                 } else {
691                         if (reverseDirectionNeeded(cur)) {
692                                 cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
693                                         LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD;
694                         } else {
695                                 cmd.action = cmd.action == LFUN_CHAR_RIGHT_SELECT ?
696                                         LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD;
697                         }
698                         dispatch(cur, cmd);
699                         return;
700                 }
701                 break;
702
703
704         case LFUN_UP_SELECT:
705         case LFUN_DOWN_SELECT:
706         case LFUN_UP:
707         case LFUN_DOWN: {
708                 // stop/start the selection
709                 bool select = cmd.action == LFUN_DOWN_SELECT ||
710                         cmd.action == LFUN_UP_SELECT;
711
712                 // move cursor up/down
713                 bool up = cmd.action == LFUN_UP_SELECT || cmd.action == LFUN_UP;
714                 bool const atFirstOrLastRow = cur.atFirstOrLastRow(up);
715
716                 if (!atFirstOrLastRow) {
717                         needsUpdate |= cur.selHandle(select);   
718                         cur.selHandle(select);
719                         cur.upDownInText(up, needsUpdate);
720                         needsUpdate |= cur.beforeDispatchCursor().inMathed();
721                 } else {
722                         // if the cursor cannot be moved up or down do not remove
723                         // the selection right now, but wait for the next dispatch.
724                         if (select)
725                                 needsUpdate |= cur.selHandle(select);   
726                         cur.upDownInText(up, needsUpdate);
727                         cur.undispatched();
728                 }
729
730                 break;
731         }
732
733         case LFUN_PARAGRAPH_UP:
734         case LFUN_PARAGRAPH_UP_SELECT:
735                 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_UP_SELECT);
736                 needsUpdate |= cursorUpParagraph(cur);
737                 break;
738
739         case LFUN_PARAGRAPH_DOWN:
740         case LFUN_PARAGRAPH_DOWN_SELECT:
741                 needsUpdate |= cur.selHandle(cmd.action == LFUN_PARAGRAPH_DOWN_SELECT);
742                 needsUpdate |= cursorDownParagraph(cur);
743                 break;
744
745         case LFUN_LINE_BEGIN:
746         case LFUN_LINE_BEGIN_SELECT:
747                 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_BEGIN_SELECT);
748                 needsUpdate |= tm->cursorHome(cur);
749                 break;
750
751         case LFUN_LINE_END:
752         case LFUN_LINE_END_SELECT:
753                 needsUpdate |= cur.selHandle(cmd.action == LFUN_LINE_END_SELECT);
754                 needsUpdate |= tm->cursorEnd(cur);
755                 break;
756
757         case LFUN_SECTION_SELECT: {
758                 Buffer const & buf = *cur.buffer();
759                 pit_type const pit = cur.pit();
760                 ParagraphList & pars = buf.text().paragraphs();
761                 ParagraphList::iterator bgn = pars.begin();
762                 // The first paragraph of the area to be selected:
763                 ParagraphList::iterator start = boost::next(bgn, pit);
764                 // The final paragraph of area to be selected:
765                 ParagraphList::iterator finish = start;
766                 ParagraphList::iterator end = pars.end();
767
768                 int const thistoclevel = start->layout().toclevel;
769                 if (thistoclevel == Layout::NOT_IN_TOC)
770                         break;
771
772                 cur.pos() = 0;
773                 Cursor const old_cur = cur;
774                 needsUpdate |= cur.selHandle(true);
775
776                 // Move out (down) from this section header
777                 if (finish != end)
778                         ++finish;
779
780                 // Seek the one (on same level) below
781                 for (; finish != end; ++finish, ++cur.pit()) {
782                         int const toclevel = finish->layout().toclevel;
783                         if (toclevel != Layout::NOT_IN_TOC && toclevel <= thistoclevel)
784                                 break;
785                 }
786                 cur.pos() = cur.lastpos();
787                 
788                 needsUpdate |= cur != old_cur;
789                 break;
790         }
791
792         case LFUN_WORD_RIGHT:
793         case LFUN_WORD_RIGHT_SELECT:
794                 if (lyxrc.visual_cursor) {
795                         needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_RIGHT_SELECT);
796                         needsUpdate |= cursorVisRightOneWord(cur);
797                         if (!needsUpdate && oldTopSlice == cur.top()
798                                         && cur.boundary() == oldBoundary) {
799                                 cur.undispatched();
800                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
801                         }
802                 } else {
803                         if (reverseDirectionNeeded(cur)) {
804                                 cmd.action = cmd.action == LFUN_WORD_RIGHT_SELECT ?
805                                                 LFUN_WORD_BACKWARD_SELECT : LFUN_WORD_BACKWARD;
806                         } else {
807                                 cmd.action = cmd.action == LFUN_WORD_RIGHT_SELECT ?
808                                                 LFUN_WORD_FORWARD_SELECT : LFUN_WORD_FORWARD;
809                         }
810                         dispatch(cur, cmd);
811                         return;
812                 }
813                 break;
814
815         case LFUN_WORD_FORWARD:
816         case LFUN_WORD_FORWARD_SELECT:
817                 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT);
818                 needsUpdate |= cursorForwardOneWord(cur);
819                 break;
820
821         case LFUN_WORD_LEFT:
822         case LFUN_WORD_LEFT_SELECT:
823                 if (lyxrc.visual_cursor) {
824                         needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_LEFT_SELECT);
825                         needsUpdate |= cursorVisLeftOneWord(cur);
826                         if (!needsUpdate && oldTopSlice == cur.top()
827                                         && cur.boundary() == oldBoundary) {
828                                 cur.undispatched();
829                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
830                         }
831                 } else {
832                         if (reverseDirectionNeeded(cur)) {
833                                 cmd.action = cmd.action == LFUN_WORD_LEFT_SELECT ?
834                                                 LFUN_WORD_FORWARD_SELECT : LFUN_WORD_FORWARD;
835                         } else {
836                                 cmd.action = cmd.action == LFUN_WORD_LEFT_SELECT ?
837                                                 LFUN_WORD_BACKWARD_SELECT : LFUN_WORD_BACKWARD;
838                         }
839                         dispatch(cur, cmd);
840                         return;
841                 }
842                 break;
843
844         case LFUN_WORD_BACKWARD:
845         case LFUN_WORD_BACKWARD_SELECT:
846                 needsUpdate |= cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT);
847                 needsUpdate |= cursorBackwardOneWord(cur);
848                 break;
849
850         case LFUN_WORD_SELECT: {
851                 selectWord(cur, WHOLE_WORD);
852                 finishChange(cur, true);
853                 break;
854         }
855
856         case LFUN_NEWLINE_INSERT: {
857                 InsetNewlineParams inp;
858                 docstring arg = cmd.argument();
859                 // this avoids a double undo
860                 // FIXME: should not be needed, ideally
861                 if (!cur.selection())
862                         cur.recordUndo();
863                 cap::replaceSelection(cur);
864                 if (arg == "linebreak")
865                         inp.kind = InsetNewlineParams::LINEBREAK;
866                 else
867                         inp.kind = InsetNewlineParams::NEWLINE;
868                 cur.insert(new InsetNewline(inp));
869                 cur.posForward();
870                 moveCursor(cur, false);
871                 break;
872         }
873
874         case LFUN_TAB_INSERT: {
875                 bool const multi_par_selection = cur.selection() &&
876                         cur.selBegin().pit() != cur.selEnd().pit();
877                 if (multi_par_selection) {
878                         // If there is a multi-paragraph selection, a tab is inserted
879                         // at the beginning of each paragraph.
880                         cur.recordUndoSelection();
881                         pit_type const pit_end = cur.selEnd().pit();
882                         for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) {
883                                 pars_[pit].insertChar(0, '\t', 
884                                                       bv->buffer().params().trackChanges);
885                                 // Update the selection pos to make sure the selection does not
886                                 // change as the inserted tab will increase the logical pos.
887                                 if (cur.anchor_.pit() == pit)
888                                         cur.anchor_.forwardPos();
889                                 if (cur.pit() == pit)
890                                         cur.forwardPos();
891                         }
892                         cur.finishUndo();
893                 } else {
894                         // Maybe we shouldn't allow tabs within a line, because they
895                         // are not (yet) aligned as one might do expect.
896                         FuncRequest cmd(LFUN_SELF_INSERT, from_ascii("\t"));
897                         dispatch(cur, cmd);     
898                 }
899                 break;
900         }
901
902         case LFUN_TAB_DELETE: {
903                 bool const tc = bv->buffer().params().trackChanges;
904                 if (cur.selection()) {
905                         // If there is a selection, a tab (if present) is removed from
906                         // the beginning of each paragraph.
907                         cur.recordUndoSelection();
908                         pit_type const pit_end = cur.selEnd().pit();
909                         for (pit_type pit = cur.selBegin().pit(); pit <= pit_end; pit++) {
910                                 Paragraph & par = paragraphs()[pit];
911                                 if (par.getChar(0) == '\t') {
912                                         if (cur.pit() == pit)
913                                                 cur.posBackward();
914                                         if (cur.anchor_.pit() == pit && cur.anchor_.pos() > 0 )
915                                                 cur.anchor_.backwardPos();
916                                         
917                                         par.eraseChar(0, tc);
918                                 } else 
919                                         // If no tab was present, try to remove up to four spaces.
920                                         for (int n_spaces = 0;
921                                              par.getChar(0) == ' ' && n_spaces < 4; ++n_spaces) {
922                                                 if (cur.pit() == pit)
923                                                         cur.posBackward();
924                                                 if (cur.anchor_.pit() == pit && cur.anchor_.pos() > 0 )
925                                                         cur.anchor_.backwardPos();
926                                                 
927                                                 par.eraseChar(0, tc);
928                                         }
929                         }
930                         cur.finishUndo();
931                 } else {
932                         // If there is no selection, try to remove a tab or some spaces 
933                         // before the position of the cursor.
934                         Paragraph & par = paragraphs()[cur.pit()];
935                         pos_type const pos = cur.pos();
936                         
937                         if (pos == 0)
938                                 break;
939                         
940                         char_type const c = par.getChar(pos - 1);
941                         cur.recordUndo();
942                         if (c == '\t') {
943                                 cur.posBackward();
944                                 par.eraseChar(cur.pos(), tc);
945                         } else
946                                 for (int n_spaces = 0; 
947                                      cur.pos() > 0
948                                              && par.getChar(cur.pos() - 1) == ' ' 
949                                              && n_spaces < 4;
950                                      ++n_spaces) {
951                                         cur.posBackward();
952                                         par.eraseChar(cur.pos(), tc);
953                                 }
954                         cur.finishUndo();
955                 }
956                 break;
957         }
958
959         case LFUN_CHAR_DELETE_FORWARD:
960                 if (!cur.selection()) {
961                         if (cur.pos() == cur.paragraph().size())
962                                 // Par boundary, force full-screen update
963                                 singleParUpdate = false;
964                         needsUpdate |= erase(cur);
965                         cur.resetAnchor();
966                         // It is possible to make it a lot faster still
967                         // just comment out the line below...
968                 } else {
969                         cutSelection(cur, true, false);
970                         singleParUpdate = false;
971                 }
972                 moveCursor(cur, false);
973                 break;
974
975         case LFUN_CHAR_DELETE_BACKWARD:
976                 if (!cur.selection()) {
977                         if (bv->getIntl().getTransManager().backspace()) {
978                                 // Par boundary, full-screen update
979                                 if (cur.pos() == 0)
980                                         singleParUpdate = false;
981                                 needsUpdate |= backspace(cur);
982                                 cur.resetAnchor();
983                                 // It is possible to make it a lot faster still
984                                 // just comment out the line below...
985                         }
986                 } else {
987                         cutSelection(cur, true, false);
988                         singleParUpdate = false;
989                 }
990                 break;
991
992         case LFUN_BREAK_PARAGRAPH:
993                 cap::replaceSelection(cur);
994                 breakParagraph(cur, cmd.argument() == "inverse");
995                 cur.resetAnchor();
996                 break;
997
998         case LFUN_INSET_INSERT: {
999                 cur.recordUndo();
1000
1001                 // We have to avoid triggering InstantPreview loading
1002                 // before inserting into the document. See bug #5626.
1003                 bool loaded = bv->buffer().isFullyLoaded();
1004                 bv->buffer().setFullyLoaded(false);
1005                 Inset * inset = createInset(&bv->buffer(), cmd);
1006                 bv->buffer().setFullyLoaded(loaded);
1007
1008                 if (inset) {
1009                         // FIXME (Abdel 01/02/2006):
1010                         // What follows would be a partial fix for bug 2154:
1011                         //   http://www.lyx.org/trac/ticket/2154
1012                         // This automatically put the label inset _after_ a
1013                         // numbered section. It should be possible to extend the mechanism
1014                         // to any kind of LateX environement.
1015                         // The correct way to fix that bug would be at LateX generation.
1016                         // I'll let the code here for reference as it could be used for some
1017                         // other feature like "automatic labelling".
1018                         /*
1019                         Paragraph & par = pars_[cur.pit()];
1020                         if (inset->lyxCode() == LABEL_CODE
1021                                 && par.layout().labeltype == LABEL_COUNTER) {
1022                                 // Go to the end of the paragraph
1023                                 // Warning: Because of Change-Tracking, the last
1024                                 // position is 'size()' and not 'size()-1':
1025                                 cur.pos() = par.size();
1026                                 // Insert a new paragraph
1027                                 FuncRequest fr(LFUN_BREAK_PARAGRAPH);
1028                                 dispatch(cur, fr);
1029                         }
1030                         */
1031                         if (cur.selection())
1032                                 cutSelection(cur, true, false);
1033                         cur.insert(inset);
1034                         cur.posForward();
1035
1036                         // trigger InstantPreview now
1037                         if (inset->lyxCode() == EXTERNAL_CODE) {
1038                                 InsetExternal & ins =
1039                                         static_cast<InsetExternal &>(*inset);
1040                                 ins.updatePreview();
1041                         }
1042                 }
1043
1044                 break;
1045         }
1046
1047         case LFUN_INSET_DISSOLVE: {
1048                 if (dissolveInset(cur))
1049                         needsUpdate = true;
1050                 break;
1051         }
1052
1053         case LFUN_SET_GRAPHICS_GROUP: {
1054                 InsetGraphics * ins = graphics::getCurrentGraphicsInset(cur);
1055                 if (!ins)
1056                         break;
1057
1058                 cur.recordUndo();
1059
1060                 string id = to_utf8(cmd.argument());
1061                 string grp = graphics::getGroupParams(bv->buffer(), id);
1062                 InsetGraphicsParams tmp, inspar = ins->getParams();
1063
1064                 if (id.empty())
1065                         inspar.groupId = to_utf8(cmd.argument());
1066                 else {
1067                         InsetGraphics::string2params(grp, bv->buffer(), tmp);
1068                         tmp.filename = inspar.filename;
1069                         inspar = tmp;
1070                 }
1071
1072                 ins->setParams(inspar);
1073         }
1074
1075         case LFUN_SPACE_INSERT:
1076                 if (cur.paragraph().layout().free_spacing)
1077                         insertChar(cur, ' ');
1078                 else {
1079                         doInsertInset(cur, this, cmd, false, false);
1080                         cur.posForward();
1081                 }
1082                 moveCursor(cur, false);
1083                 break;
1084
1085         case LFUN_SPECIALCHAR_INSERT: {
1086                 string const name = to_utf8(cmd.argument());
1087                 if (name == "hyphenation")
1088                         specialChar(cur, InsetSpecialChar::HYPHENATION);
1089                 else if (name == "ligature-break")
1090                         specialChar(cur, InsetSpecialChar::LIGATURE_BREAK);
1091                 else if (name == "slash")
1092                         specialChar(cur, InsetSpecialChar::SLASH);
1093                 else if (name == "nobreakdash")
1094                         specialChar(cur, InsetSpecialChar::NOBREAKDASH);
1095                 else if (name == "dots")
1096                         specialChar(cur, InsetSpecialChar::LDOTS);
1097                 else if (name == "end-of-sentence")
1098                         specialChar(cur, InsetSpecialChar::END_OF_SENTENCE);
1099                 else if (name == "menu-separator")
1100                         specialChar(cur, InsetSpecialChar::MENU_SEPARATOR);
1101                 else if (name.empty())
1102                         lyxerr << "LyX function 'specialchar-insert' needs an argument." << endl;
1103                 else
1104                         lyxerr << "Wrong argument for LyX function 'specialchar-insert'." << endl;
1105                 break;
1106         }
1107
1108         case LFUN_WORD_UPCASE:
1109                 changeCase(cur, text_uppercase);
1110                 break;
1111
1112         case LFUN_WORD_LOWCASE:
1113                 changeCase(cur, text_lowercase);
1114                 break;
1115
1116         case LFUN_WORD_CAPITALIZE:
1117                 changeCase(cur, text_capitalization);
1118                 break;
1119
1120         case LFUN_CHARS_TRANSPOSE:
1121                 charsTranspose(cur);
1122                 break;
1123
1124         case LFUN_PASTE: {
1125                 cur.message(_("Paste"));
1126                 LASSERT(cur.selBegin().idx() == cur.selEnd().idx(), /**/);
1127                 cap::replaceSelection(cur);
1128
1129                 // without argument?
1130                 string const arg = to_utf8(cmd.argument());
1131                 if (arg.empty()) {
1132                         if (theClipboard().isInternal())
1133                                 pasteFromStack(cur, bv->buffer().errorList("Paste"), 0);
1134                         else if (theClipboard().hasGraphicsContents() 
1135                                      && !theClipboard().hasTextContents())
1136                                 pasteClipboardGraphics(cur, bv->buffer().errorList("Paste"));
1137                         else
1138                                 pasteClipboardText(cur, bv->buffer().errorList("Paste"));
1139                 } else if (isStrUnsignedInt(arg)) {
1140                         // we have a numerical argument
1141                         pasteFromStack(cur, bv->buffer().errorList("Paste"),
1142                                        convert<unsigned int>(arg));
1143                 } else {
1144                         Clipboard::GraphicsType type = Clipboard::AnyGraphicsType;
1145                         if (arg == "pdf")
1146                                 type = Clipboard::PdfGraphicsType;
1147                         else if (arg == "png")
1148                                 type = Clipboard::PngGraphicsType;
1149                         else if (arg == "jpeg")
1150                                 type = Clipboard::JpegGraphicsType;
1151                         else if (arg == "linkback")
1152                                 type = Clipboard::LinkBackGraphicsType;
1153                         else if (arg == "emf")
1154                                 type = Clipboard::EmfGraphicsType;
1155                         else if (arg == "wmf")
1156                                 type = Clipboard::WmfGraphicsType;
1157
1158                         else
1159                                 LASSERT(false, /**/);
1160
1161                         pasteClipboardGraphics(cur, bv->buffer().errorList("Paste"), type);
1162                 }
1163
1164                 bv->buffer().errors("Paste");
1165                 cur.clearSelection(); // bug 393
1166                 cur.finishUndo();
1167                 break;
1168         }
1169
1170         case LFUN_CUT:
1171                 cutSelection(cur, true, true);
1172                 cur.message(_("Cut"));
1173                 break;
1174
1175         case LFUN_COPY:
1176                 copySelection(cur);
1177                 cur.message(_("Copy"));
1178                 break;
1179
1180         case LFUN_SERVER_GET_XY:
1181                 cur.message(from_utf8(
1182                         convert<string>(tm->cursorX(cur.top(), cur.boundary()))
1183                         + ' ' + convert<string>(tm->cursorY(cur.top(), cur.boundary()))));
1184                 break;
1185
1186         case LFUN_SERVER_SET_XY: {
1187                 int x = 0;
1188                 int y = 0;
1189                 istringstream is(to_utf8(cmd.argument()));
1190                 is >> x >> y;
1191                 if (!is)
1192                         lyxerr << "SETXY: Could not parse coordinates in '"
1193                                << to_utf8(cmd.argument()) << endl;
1194                 else
1195                         tm->setCursorFromCoordinates(cur, x, y);
1196                 break;
1197         }
1198
1199         case LFUN_SERVER_GET_LAYOUT:
1200                 cur.message(cur.paragraph().layout().name());
1201                 break;
1202
1203         case LFUN_LAYOUT: {
1204                 docstring layout = cmd.argument();
1205                 LYXERR(Debug::INFO, "LFUN_LAYOUT: (arg) " << to_utf8(layout));
1206
1207                 Paragraph const & para = cur.paragraph();
1208                 docstring const old_layout = para.layout().name();
1209                 DocumentClass const & tclass = bv->buffer().params().documentClass();
1210
1211                 if (layout.empty())
1212                         layout = tclass.defaultLayoutName();
1213
1214                 if (owner_->forcePlainLayout())
1215                         // in this case only the empty layout is allowed
1216                         layout = tclass.plainLayoutName();
1217                 else if (para.usePlainLayout()) {
1218                         // in this case, default layout maps to empty layout
1219                         if (layout == tclass.defaultLayoutName())
1220                                 layout = tclass.plainLayoutName();
1221                 } else {
1222                         // otherwise, the empty layout maps to the default
1223                         if (layout == tclass.plainLayoutName())
1224                                 layout = tclass.defaultLayoutName();
1225                 }
1226
1227                 bool hasLayout = tclass.hasLayout(layout);
1228
1229                 // If the entry is obsolete, use the new one instead.
1230                 if (hasLayout) {
1231                         docstring const & obs = tclass[layout].obsoleted_by();
1232                         if (!obs.empty())
1233                                 layout = obs;
1234                 }
1235
1236                 if (!hasLayout) {
1237                         cur.errorMessage(from_utf8(N_("Layout ")) + cmd.argument() +
1238                                 from_utf8(N_(" not known")));
1239                         break;
1240                 }
1241
1242                 bool change_layout = (old_layout != layout);
1243
1244                 if (!change_layout && cur.selection() &&
1245                         cur.selBegin().pit() != cur.selEnd().pit())
1246                 {
1247                         pit_type spit = cur.selBegin().pit();
1248                         pit_type epit = cur.selEnd().pit() + 1;
1249                         while (spit != epit) {
1250                                 if (pars_[spit].layout().name() != old_layout) {
1251                                         change_layout = true;
1252                                         break;
1253                                 }
1254                                 ++spit;
1255                         }
1256                 }
1257
1258                 if (change_layout)
1259                         setLayout(cur, layout);
1260
1261                 break;
1262         }
1263
1264         case LFUN_CLIPBOARD_PASTE:
1265                 cur.clearSelection();
1266                 pasteClipboardText(cur, bv->buffer().errorList("Paste"),
1267                                cmd.argument() == "paragraph");
1268                 bv->buffer().errors("Paste");
1269                 break;
1270
1271         case LFUN_PRIMARY_SELECTION_PASTE:
1272                 pasteString(cur, theSelection().get(),
1273                             cmd.argument() == "paragraph");
1274                 break;
1275
1276         case LFUN_SELECTION_PASTE:
1277                 // Copy the selection buffer to the clipboard stack,
1278                 // because we want it to appear in the "Edit->Paste
1279                 // recent" menu.
1280                 cap::copySelectionToStack();
1281                 cap::pasteSelection(bv->cursor(), bv->buffer().errorList("Paste"));
1282                 bv->buffer().errors("Paste");
1283                 break;
1284
1285         case LFUN_UNICODE_INSERT: {
1286                 if (cmd.argument().empty())
1287                         break;
1288                 docstring hexstring = cmd.argument();
1289                 if (isHex(hexstring)) {
1290                         char_type c = hexToInt(hexstring);
1291                         if (c >= 32 && c < 0x10ffff) {
1292                                 lyxerr << "Inserting c: " << c << endl;
1293                                 docstring s = docstring(1, c);
1294                                 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, s));
1295                         }
1296                 }
1297                 break;
1298         }
1299
1300         case LFUN_QUOTE_INSERT: {
1301                 Paragraph & par = cur.paragraph();
1302                 pos_type pos = cur.pos();
1303                 BufferParams const & bufparams = bv->buffer().params();
1304                 Layout const & style = par.layout();
1305                 InsetLayout const & ilayout = cur.inset().getLayout();
1306                 if (!style.pass_thru && !ilayout.isPassThru()
1307                     && par.getFontSettings(bufparams, pos).language()->lang() != "hebrew") {
1308                         // this avoids a double undo
1309                         // FIXME: should not be needed, ideally
1310                         if (!cur.selection())
1311                                 cur.recordUndo();
1312                         cap::replaceSelection(cur);
1313                         pos = cur.pos();
1314                         char_type c;
1315                         if (pos == 0)
1316                                 c = ' ';
1317                         else if (cur.prevInset() && cur.prevInset()->isSpace())
1318                                 c = ' ';
1319                         else
1320                                 c = par.getChar(pos - 1);
1321                         string arg = to_utf8(cmd.argument());
1322                         cur.insert(new InsetQuotes(cur.buffer(), c, (arg == "single")
1323                                 ? InsetQuotes::SingleQuotes : InsetQuotes::DoubleQuotes));
1324                         cur.posForward();
1325                 }
1326                 else
1327                         lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, "\""));
1328                 break;
1329         }
1330
1331         case LFUN_DATE_INSERT: {
1332                 string const format = cmd.argument().empty()
1333                         ? lyxrc.date_insert_format : to_utf8(cmd.argument());
1334                 string const time = formatted_time(current_time(), format);
1335                 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, time));
1336                 break;
1337         }
1338
1339         case LFUN_MOUSE_TRIPLE:
1340                 if (cmd.button() == mouse_button::button1) {
1341                         tm->cursorHome(cur);
1342                         cur.resetAnchor();
1343                         tm->cursorEnd(cur);
1344                         cur.setSelection();
1345                         bv->cursor() = cur;
1346                 }
1347                 break;
1348
1349         case LFUN_MOUSE_DOUBLE:
1350                 if (cmd.button() == mouse_button::button1) {
1351                         selectWord(cur, WHOLE_WORD_STRICT);
1352                         bv->cursor() = cur;
1353                 }
1354                 break;
1355
1356         // Single-click on work area
1357         case LFUN_MOUSE_PRESS: {
1358                 // We are not marking a selection with the keyboard in any case.
1359                 Cursor & bvcur = cur.bv().cursor();
1360                 bvcur.setMark(false);
1361                 switch (cmd.button()) {
1362                 case mouse_button::button1:
1363                         // Set the cursor
1364                         if (!bv->mouseSetCursor(cur, cmd.argument() == "region-select"))
1365                                 cur.updateFlags(Update::SinglePar | Update::FitCursor);
1366                         if (bvcur.wordSelection())
1367                                 selectWord(bvcur, WHOLE_WORD);
1368                         break;
1369
1370                 case mouse_button::button2:
1371                         // Middle mouse pasting.
1372                         bv->mouseSetCursor(cur);
1373                         lyx::dispatch(
1374                                 FuncRequest(LFUN_COMMAND_ALTERNATIVES,
1375                                             "selection-paste ; primary-selection-paste paragraph"));
1376                         cur.noUpdate();
1377                         break;
1378
1379                 case mouse_button::button3: {
1380                         // Don't do anything if we right-click a
1381                         // selection, a context menu will popup.
1382                         if (bvcur.selection() && cur >= bvcur.selectionBegin()
1383                             && cur < bvcur.selectionEnd()) {
1384                                 cur.noUpdate();
1385                                 return;
1386                         }
1387                         if (!bv->mouseSetCursor(cur, false))
1388                                 cur.updateFlags(Update::SinglePar | Update::FitCursor);
1389                         break;
1390                 }
1391
1392                 default:
1393                         break;
1394                 } // switch (cmd.button())
1395                 break;
1396         }
1397         case LFUN_MOUSE_MOTION: {
1398                 // Mouse motion with right or middle mouse do nothing for now.
1399                 if (cmd.button() != mouse_button::button1) {
1400                         cur.noUpdate();
1401                         return;
1402                 }
1403                 // ignore motions deeper nested than the real anchor
1404                 Cursor & bvcur = cur.bv().cursor();
1405                 if (!bvcur.anchor_.hasPart(cur)) {
1406                         cur.undispatched();
1407                         break;
1408                 }
1409                 CursorSlice old = bvcur.top();
1410
1411                 int const wh = bv->workHeight();
1412                 int const y = max(0, min(wh - 1, cmd.y));
1413
1414                 tm->setCursorFromCoordinates(cur, cmd.x, y);
1415                 cur.setTargetX(cmd.x);
1416                 if (cmd.y >= wh)
1417                         lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
1418                 else if (cmd.y < 0)
1419                         lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
1420                 // This is to allow jumping over large insets
1421                 if (cur.top() == old) {
1422                         if (cmd.y >= wh)
1423                                 lyx::dispatch(FuncRequest(LFUN_DOWN_SELECT));
1424                         else if (cmd.y < 0)
1425                                 lyx::dispatch(FuncRequest(LFUN_UP_SELECT));
1426                 }
1427                 // We continue with our existing selection or start a new one, so don't
1428                 // reset the anchor.
1429                 bvcur.setCursor(cur);
1430                 bvcur.setSelection(true);
1431                 if (cur.top() == old) {
1432                         // We didn't move one iota, so no need to update the screen.
1433                         cur.updateFlags(Update::SinglePar | Update::FitCursor);
1434                         //cur.noUpdate();
1435                         return;
1436                 }
1437                 break;
1438         }
1439
1440         case LFUN_MOUSE_RELEASE:
1441                 switch (cmd.button()) {
1442                 case mouse_button::button1:
1443                         // Cursor was set at LFUN_MOUSE_PRESS or LFUN_MOUSE_MOTION time.
1444                         // If there is a new selection, update persistent selection;
1445                         // otherwise, single click does not clear persistent selection
1446                         // buffer.
1447                         if (cur.selection()) {
1448                                 // Finish selection. If double click,
1449                                 // cur is moved to the end of word by
1450                                 // selectWord but bvcur is current
1451                                 // mouse position.
1452                                 cur.bv().cursor().setSelection();
1453                                 // We might have removed an empty but drawn selection
1454                                 // (probably a margin)
1455                                 cur.updateFlags(Update::SinglePar | Update::FitCursor);
1456                         } else
1457                                 cur.noUpdate();
1458                         // FIXME: We could try to handle drag and drop of selection here.
1459                         return;
1460
1461                 case mouse_button::button2:
1462                         // Middle mouse pasting is handled at mouse press time,
1463                         // see LFUN_MOUSE_PRESS.
1464                         cur.noUpdate();
1465                         return;
1466
1467                 case mouse_button::button3:
1468                         // Cursor was set at LFUN_MOUSE_PRESS time.
1469                         // FIXME: If there is a selection we could try to handle a special
1470                         // drag & drop context menu.
1471                         cur.noUpdate();
1472                         return;
1473
1474                 case mouse_button::none:
1475                 case mouse_button::button4:
1476                 case mouse_button::button5:
1477                         break;
1478                 } // switch (cmd.button())
1479
1480                 break;
1481
1482         case LFUN_SELF_INSERT: {
1483                 if (cmd.argument().empty())
1484                         break;
1485
1486                 // Automatically delete the currently selected
1487                 // text and replace it with what is being
1488                 // typed in now. Depends on lyxrc settings
1489                 // "auto_region_delete", which defaults to
1490                 // true (on).
1491
1492                 if (lyxrc.auto_region_delete && cur.selection())
1493                         cutSelection(cur, false, false);
1494
1495                 cur.clearSelection();
1496
1497                 docstring::const_iterator cit = cmd.argument().begin();
1498                 docstring::const_iterator const end = cmd.argument().end();
1499                 for (; cit != end; ++cit)
1500                         bv->translateAndInsert(*cit, this, cur);
1501
1502                 cur.resetAnchor();
1503                 moveCursor(cur, false);
1504                 bv->bookmarkEditPosition();
1505                 break;
1506         }
1507
1508         case LFUN_HYPERLINK_INSERT: {
1509                 InsetCommandParams p(HYPERLINK_CODE);
1510                 docstring content;
1511                 if (cur.selection()) {
1512                         content = cur.selectionAsString(false);
1513                         cutSelection(cur, true, false);
1514                 }
1515                 p["target"] = (cmd.argument().empty()) ?
1516                         content : cmd.argument();
1517                 string const data = InsetCommand::params2string("href", p);
1518                 if (p["target"].empty()) {
1519                         bv->showDialog("href", data);
1520                 } else {
1521                         FuncRequest fr(LFUN_INSET_INSERT, data);
1522                         dispatch(cur, fr);
1523                 }
1524                 break;
1525         }
1526
1527         case LFUN_LABEL_INSERT: {
1528                 InsetCommandParams p(LABEL_CODE);
1529                 // Try to generate a valid label
1530                 p["name"] = (cmd.argument().empty()) ?
1531                         cur.getPossibleLabel() :
1532                         cmd.argument();
1533                 string const data = InsetCommand::params2string("label", p);
1534
1535                 if (cmd.argument().empty()) {
1536                         bv->showDialog("label", data);
1537                 } else {
1538                         FuncRequest fr(LFUN_INSET_INSERT, data);
1539                         dispatch(cur, fr);
1540                 }
1541                 break;
1542         }
1543
1544         case LFUN_INFO_INSERT: {
1545                 Inset * inset;
1546                 if (cmd.argument().empty() && cur.selection()) {
1547                         // if command argument is empty use current selection as parameter.
1548                         docstring ds = cur.selectionAsString(false);
1549                         cutSelection(cur, true, false);
1550                         FuncRequest cmd0(cmd, ds);
1551                         inset = createInset(cur.buffer(), cmd0);
1552                 } else {
1553                         inset = createInset(cur.buffer(), cmd);
1554                 }
1555                 if (!inset)
1556                         break;
1557                 cur.recordUndo();
1558                 insertInset(cur, inset);
1559                 cur.posForward();
1560                 break;
1561         }
1562         case LFUN_CAPTION_INSERT:
1563         case LFUN_FOOTNOTE_INSERT:
1564         case LFUN_NOTE_INSERT:
1565         case LFUN_FLEX_INSERT:
1566         case LFUN_BOX_INSERT:
1567         case LFUN_BRANCH_INSERT:
1568         case LFUN_PHANTOM_INSERT:
1569         case LFUN_ERT_INSERT:
1570         case LFUN_LISTING_INSERT:
1571         case LFUN_MARGINALNOTE_INSERT:
1572         case LFUN_OPTIONAL_INSERT:
1573         case LFUN_INDEX_INSERT:
1574                 // Open the inset, and move the current selection
1575                 // inside it.
1576                 doInsertInset(cur, this, cmd, true, true);
1577                 cur.posForward();
1578                 // Some insets are numbered, others are shown in the outline pane so
1579                 // let's update the labels and the toc backend.
1580                 bv->buffer().updateLabels();
1581                 break;
1582
1583         case LFUN_TABULAR_INSERT:
1584                 // if there were no arguments, just open the dialog
1585                 if (doInsertInset(cur, this, cmd, false, true))
1586                         cur.posForward();
1587                 else
1588                         bv->showDialog("tabularcreate");
1589
1590                 break;
1591
1592         case LFUN_FLOAT_INSERT:
1593         case LFUN_FLOAT_WIDE_INSERT:
1594         case LFUN_WRAP_INSERT: {
1595                 // will some text be moved into the inset?
1596                 bool content = cur.selection();
1597
1598                 doInsertInset(cur, this, cmd, true, true);
1599                 cur.posForward();
1600
1601                 // If some text is moved into the inset, doInsertInset 
1602                 // puts the cursor outside the inset. To insert the
1603                 // caption we put it back into the inset.
1604                 if (content)
1605                         cur.backwardPos();
1606
1607                 ParagraphList & pars = cur.text()->paragraphs();
1608
1609                 DocumentClass const & tclass = bv->buffer().params().documentClass();
1610
1611                 // add a separate paragraph for the caption inset
1612                 pars.push_back(Paragraph());
1613                 pars.back().setInsetOwner(&cur.text()->inset());
1614                 pars.back().setPlainOrDefaultLayout(tclass);
1615                 int cap_pit = pars.size() - 1;
1616
1617                 // if an empty inset was created, we create an additional empty
1618                 // paragraph at the bottom so that the user can choose where to put
1619                 // the graphics (or table).
1620                 if (!content) {
1621                         pars.push_back(Paragraph());
1622                         pars.back().setInsetOwner(&cur.text()->inset());
1623                         pars.back().setPlainOrDefaultLayout(tclass);
1624                 }
1625
1626                 // reposition the cursor to the caption
1627                 cur.pit() = cap_pit;
1628                 cur.pos() = 0;
1629                 // FIXME: This Text/Cursor dispatch handling is a mess!
1630                 // We cannot use Cursor::dispatch here it needs access to up to
1631                 // date metrics.
1632                 FuncRequest cmd_caption(LFUN_CAPTION_INSERT);
1633                 doInsertInset(cur, cur.text(), cmd_caption, true, false);
1634                 bv->buffer().updateLabels();
1635                 cur.updateFlags(Update::Force);
1636                 // FIXME: When leaving the Float (or Wrap) inset we should
1637                 // delete any empty paragraph left above or below the
1638                 // caption.
1639                 break;
1640         }
1641
1642         case LFUN_NOMENCL_INSERT: {
1643                 InsetCommandParams p(NOMENCL_CODE);
1644                 if (cmd.argument().empty())
1645                         p["symbol"] = bv->cursor().innerText()->getStringToIndex(bv->cursor());
1646                 else
1647                         p["symbol"] = cmd.argument();
1648                 string const data = InsetCommand::params2string("nomenclature", p);
1649                 bv->showDialog("nomenclature", data);
1650                 break;
1651         }
1652
1653         case LFUN_INDEX_PRINT: {
1654                 InsetCommandParams p(INDEX_PRINT_CODE);
1655                 if (cmd.argument().empty())
1656                         p["type"] = from_ascii("idx");
1657                 else
1658                         p["type"] = cmd.argument();
1659                 string const data = InsetCommand::params2string("index_print", p);
1660                 FuncRequest fr(LFUN_INSET_INSERT, data);
1661                 dispatch(cur, fr);
1662                 break;
1663         }
1664         
1665         case LFUN_NOMENCL_PRINT:
1666         case LFUN_TOC_INSERT:
1667         case LFUN_LINE_INSERT:
1668         case LFUN_NEWPAGE_INSERT:
1669                 // do nothing fancy
1670                 doInsertInset(cur, this, cmd, false, false);
1671                 cur.posForward();
1672                 break;
1673
1674         case LFUN_DEPTH_DECREMENT:
1675                 changeDepth(cur, DEC_DEPTH);
1676                 break;
1677
1678         case LFUN_DEPTH_INCREMENT:
1679                 changeDepth(cur, INC_DEPTH);
1680                 break;
1681
1682         case LFUN_MATH_DISPLAY:
1683                 mathDispatch(cur, cmd, true);
1684                 break;
1685
1686         case LFUN_REGEXP_MODE:
1687                 regexpDispatch(cur, cmd);
1688                 break;
1689
1690         case LFUN_MATH_MODE:
1691                 if (cmd.argument() == "on")
1692                         // don't pass "on" as argument
1693                         mathDispatch(cur, FuncRequest(LFUN_MATH_MODE), false);
1694                 else
1695                         mathDispatch(cur, cmd, false);
1696                 break;
1697
1698         case LFUN_MATH_MACRO:
1699                 if (cmd.argument().empty())
1700                         cur.errorMessage(from_utf8(N_("Missing argument")));
1701                 else {
1702                         string s = to_utf8(cmd.argument());
1703                         string const s1 = token(s, ' ', 1);
1704                         int const nargs = s1.empty() ? 0 : convert<int>(s1);
1705                         string const s2 = token(s, ' ', 2);
1706                         MacroType type = MacroTypeNewcommand;
1707                         if (s2 == "def")
1708                                 type = MacroTypeDef;
1709                         MathMacroTemplate * inset = new MathMacroTemplate(cur.buffer(),
1710                                 from_utf8(token(s, ' ', 0)), nargs, false, type);
1711                         inset->setBuffer(bv->buffer());
1712                         insertInset(cur, inset);
1713
1714                         // enter macro inset and select the name
1715                         cur.push(*inset);
1716                         cur.top().pos() = cur.top().lastpos();
1717                         cur.resetAnchor();
1718                         cur.setSelection(true);
1719                         cur.top().pos() = 0;
1720                 }
1721                 break;
1722
1723         // passthrough hat and underscore outside mathed:
1724         case LFUN_MATH_SUBSCRIPT:
1725                 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "_"), false);
1726                 break;
1727         case LFUN_MATH_SUPERSCRIPT:
1728                 mathDispatch(cur, FuncRequest(LFUN_SELF_INSERT, "^"), false);
1729                 break;
1730
1731         case LFUN_MATH_INSERT:
1732         case LFUN_MATH_AMS_MATRIX:
1733         case LFUN_MATH_MATRIX:
1734         case LFUN_MATH_DELIM:
1735         case LFUN_MATH_BIGDELIM: {
1736                 cur.recordUndo();
1737                 cap::replaceSelection(cur);
1738                 cur.insert(new InsetMathHull(cur.buffer(), hullSimple));
1739                 checkAndActivateInset(cur, true);
1740                 LASSERT(cur.inMathed(), /**/);
1741                 cur.dispatch(cmd);
1742                 break;
1743         }
1744
1745         case LFUN_FONT_EMPH: {
1746                 Font font(ignore_font, ignore_language);
1747                 font.fontInfo().setEmph(FONT_TOGGLE);
1748                 toggleAndShow(cur, this, font);
1749                 break;
1750         }
1751
1752         case LFUN_FONT_ITAL: {
1753                 Font font(ignore_font, ignore_language);
1754                 font.fontInfo().setShape(ITALIC_SHAPE);
1755                 toggleAndShow(cur, this, font);
1756                 break;
1757         }
1758
1759         case LFUN_FONT_BOLD:
1760         case LFUN_FONT_BOLDSYMBOL: {
1761                 Font font(ignore_font, ignore_language);
1762                 font.fontInfo().setSeries(BOLD_SERIES);
1763                 toggleAndShow(cur, this, font);
1764                 break;
1765         }
1766
1767         case LFUN_FONT_NOUN: {
1768                 Font font(ignore_font, ignore_language);
1769                 font.fontInfo().setNoun(FONT_TOGGLE);
1770                 toggleAndShow(cur, this, font);
1771                 break;
1772         }
1773
1774         case LFUN_FONT_TYPEWRITER: {
1775                 Font font(ignore_font, ignore_language);
1776                 font.fontInfo().setFamily(TYPEWRITER_FAMILY); // no good
1777                 toggleAndShow(cur, this, font);
1778                 break;
1779         }
1780
1781         case LFUN_FONT_SANS: {
1782                 Font font(ignore_font, ignore_language);
1783                 font.fontInfo().setFamily(SANS_FAMILY);
1784                 toggleAndShow(cur, this, font);
1785                 break;
1786         }
1787
1788         case LFUN_FONT_ROMAN: {
1789                 Font font(ignore_font, ignore_language);
1790                 font.fontInfo().setFamily(ROMAN_FAMILY);
1791                 toggleAndShow(cur, this, font);
1792                 break;
1793         }
1794
1795         case LFUN_FONT_DEFAULT: {
1796                 Font font(inherit_font, ignore_language);
1797                 toggleAndShow(cur, this, font);
1798                 break;
1799         }
1800
1801         case LFUN_FONT_STRIKEOUT: {
1802                 Font font(ignore_font, ignore_language);
1803                 font.fontInfo().setStrikeout(FONT_TOGGLE);
1804                 toggleAndShow(cur, this, font);
1805                 break;
1806         }
1807
1808         case LFUN_FONT_UULINE: {
1809                 Font font(ignore_font, ignore_language);
1810                 font.fontInfo().setUuline(FONT_TOGGLE);
1811                 toggleAndShow(cur, this, font);
1812                 break;
1813         }
1814
1815         case LFUN_FONT_UWAVE: {
1816                 Font font(ignore_font, ignore_language);
1817                 font.fontInfo().setUwave(FONT_TOGGLE);
1818                 toggleAndShow(cur, this, font);
1819                 break;
1820         }
1821
1822         case LFUN_FONT_UNDERLINE: {
1823                 Font font(ignore_font, ignore_language);
1824                 font.fontInfo().setUnderbar(FONT_TOGGLE);
1825                 toggleAndShow(cur, this, font);
1826                 break;
1827         }
1828
1829         case LFUN_FONT_SIZE: {
1830                 Font font(ignore_font, ignore_language);
1831                 setLyXSize(to_utf8(cmd.argument()), font.fontInfo());
1832                 toggleAndShow(cur, this, font);
1833                 break;
1834         }
1835
1836         case LFUN_LANGUAGE: {
1837                 Language const * lang = languages.getLanguage(to_utf8(cmd.argument()));
1838                 if (!lang)
1839                         break;
1840                 Font font(ignore_font, lang);
1841                 toggleAndShow(cur, this, font);
1842                 break;
1843         }
1844
1845         case LFUN_TEXTSTYLE_APPLY:
1846                 toggleAndShow(cur, this, freefont, toggleall);
1847                 cur.message(_("Character set"));
1848                 break;
1849
1850         // Set the freefont using the contents of \param data dispatched from
1851         // the frontends and apply it at the current cursor location.
1852         case LFUN_TEXTSTYLE_UPDATE: {
1853                 Font font;
1854                 bool toggle;
1855                 if (font.fromString(to_utf8(cmd.argument()), toggle)) {
1856                         freefont = font;
1857                         toggleall = toggle;
1858                         toggleAndShow(cur, this, freefont, toggleall);
1859                         cur.message(_("Character set"));
1860                 } else {
1861                         lyxerr << "Argument not ok";
1862                 }
1863                 break;
1864         }
1865
1866         case LFUN_FINISHED_LEFT:
1867                 LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_LEFT:\n" << cur);
1868                 // We're leaving an inset, going left. If the inset is LTR, we're
1869                 // leaving from the front, so we should not move (remain at --- but
1870                 // not in --- the inset). If the inset is RTL, move left, without
1871                 // entering the inset itself; i.e., move to after the inset.
1872                 if (cur.paragraph().getFontSettings(
1873                                 cur.bv().buffer().params(), cur.pos()).isRightToLeft())
1874                         cursorVisLeft(cur, true);
1875                 break;
1876
1877         case LFUN_FINISHED_RIGHT:
1878                 LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_RIGHT:\n" << cur);
1879                 // We're leaving an inset, going right. If the inset is RTL, we're
1880                 // leaving from the front, so we should not move (remain at --- but
1881                 // not in --- the inset). If the inset is LTR, move right, without
1882                 // entering the inset itself; i.e., move to after the inset.
1883                 if (!cur.paragraph().getFontSettings(
1884                                 cur.bv().buffer().params(), cur.pos()).isRightToLeft())
1885                         cursorVisRight(cur, true);
1886                 break;
1887
1888         case LFUN_FINISHED_BACKWARD:
1889                 LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_BACKWARD:\n" << cur);
1890                 break;
1891
1892         case LFUN_FINISHED_FORWARD:
1893                 LYXERR(Debug::DEBUG, "handle LFUN_FINISHED_FORWARD:\n" << cur);
1894                 ++cur.pos();
1895                 cur.setCurrentFont();
1896                 break;
1897
1898         case LFUN_LAYOUT_PARAGRAPH: {
1899                 string data;
1900                 params2string(cur.paragraph(), data);
1901                 data = "show\n" + data;
1902                 bv->showDialog("paragraph", data);
1903                 break;
1904         }
1905
1906         case LFUN_PARAGRAPH_UPDATE: {
1907                 string data;
1908                 params2string(cur.paragraph(), data);
1909
1910                 // Will the paragraph accept changes from the dialog?
1911                 bool const accept =
1912                         cur.inset().allowParagraphCustomization(cur.idx());
1913
1914                 data = "update " + convert<string>(accept) + '\n' + data;
1915                 bv->updateDialog("paragraph", data);
1916                 break;
1917         }
1918
1919         case LFUN_ACCENT_UMLAUT:
1920         case LFUN_ACCENT_CIRCUMFLEX:
1921         case LFUN_ACCENT_GRAVE:
1922         case LFUN_ACCENT_ACUTE:
1923         case LFUN_ACCENT_TILDE:
1924         case LFUN_ACCENT_CEDILLA:
1925         case LFUN_ACCENT_MACRON:
1926         case LFUN_ACCENT_DOT:
1927         case LFUN_ACCENT_UNDERDOT:
1928         case LFUN_ACCENT_UNDERBAR:
1929         case LFUN_ACCENT_CARON:
1930         case LFUN_ACCENT_BREVE:
1931         case LFUN_ACCENT_TIE:
1932         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1933         case LFUN_ACCENT_CIRCLE:
1934         case LFUN_ACCENT_OGONEK:
1935                 theApp()->handleKeyFunc(cmd.action);
1936                 if (!cmd.argument().empty())
1937                         // FIXME: Are all these characters encoded in one byte in utf8?
1938                         bv->translateAndInsert(cmd.argument()[0], this, cur);
1939                 break;
1940
1941         case LFUN_FLOAT_LIST_INSERT: {
1942                 DocumentClass const & tclass = bv->buffer().params().documentClass();
1943                 if (tclass.floats().typeExist(to_utf8(cmd.argument()))) {
1944                         cur.recordUndo();
1945                         if (cur.selection())
1946                                 cutSelection(cur, true, false);
1947                         breakParagraph(cur);
1948
1949                         if (cur.lastpos() != 0) {
1950                                 cursorBackward(cur);
1951                                 breakParagraph(cur);
1952                         }
1953
1954                         docstring const laystr = cur.inset().usePlainLayout() ?
1955                                 tclass.plainLayoutName() :
1956                                 tclass.defaultLayoutName();
1957                         setLayout(cur, laystr);
1958                         ParagraphParameters p;
1959                         // FIXME If this call were replaced with one to clearParagraphParams(),
1960                         // then we could get rid of this method altogether.
1961                         setParagraphs(cur, p);
1962                         // FIXME This should be simplified when InsetFloatList takes a
1963                         // Buffer in its constructor.
1964                         InsetFloatList * ifl = new InsetFloatList(cur.buffer(), to_utf8(cmd.argument()));
1965                         ifl->setBuffer(bv->buffer());
1966                         insertInset(cur, ifl);
1967                         cur.posForward();
1968                 } else {
1969                         lyxerr << "Non-existent float type: "
1970                                << to_utf8(cmd.argument()) << endl;
1971                 }
1972                 break;
1973         }
1974
1975         case LFUN_CHANGE_ACCEPT: {
1976                 acceptOrRejectChanges(cur, ACCEPT);
1977                 break;
1978         }
1979
1980         case LFUN_CHANGE_REJECT: {
1981                 acceptOrRejectChanges(cur, REJECT);
1982                 break;
1983         }
1984
1985         case LFUN_THESAURUS_ENTRY: {
1986                 docstring arg = cmd.argument();
1987                 if (arg.empty()) {
1988                         arg = cur.selectionAsString(false);
1989                         // FIXME
1990                         if (arg.size() > 100 || arg.empty()) {
1991                                 // Get word or selection
1992                                 selectWordWhenUnderCursor(cur, WHOLE_WORD);
1993                                 arg = cur.selectionAsString(false);
1994                                 arg += " lang=" + from_ascii(cur.getFont().language()->lang());
1995                         }
1996                 }
1997                 bv->showDialog("thesaurus", to_utf8(arg));
1998                 break;
1999         }
2000
2001         case LFUN_SPELLING_ADD: {
2002                 docstring word = from_utf8(cmd.getArg(0));
2003                 string code;
2004                 string variety;
2005                 if (word.empty()) {
2006                         word = cur.selectionAsString(false);
2007                         // FIXME
2008                         if (word.size() > 100 || word.empty()) {
2009                                 // Get word or selection
2010                                 selectWordWhenUnderCursor(cur, WHOLE_WORD);
2011                                 word = cur.selectionAsString(false);
2012                         }
2013                         code = cur.getFont().language()->code();
2014                         variety = cur.getFont().language()->variety();
2015                 } else
2016                         variety = split(cmd.getArg(1), code, '-');
2017                 WordLangTuple wl(word, code, variety);
2018                 theSpellChecker()->insert(wl);
2019                 break;
2020         }
2021
2022         case LFUN_SPELLING_IGNORE: {
2023                 docstring word = from_utf8(cmd.getArg(0));
2024                 string code;
2025                 string variety;
2026                 if (word.empty()) {
2027                         word = cur.selectionAsString(false);
2028                         // FIXME
2029                         if (word.size() > 100 || word.empty()) {
2030                                 // Get word or selection
2031                                 selectWordWhenUnderCursor(cur, WHOLE_WORD);
2032                                 word = cur.selectionAsString(false);
2033                         }
2034                         code = cur.getFont().language()->code();
2035                         variety = cur.getFont().language()->variety();
2036                 } else
2037                         variety = split(cmd.getArg(1), code, '-');
2038                 WordLangTuple wl(word, code, variety);
2039                 theSpellChecker()->accept(wl);
2040                 break;
2041         }
2042
2043         case LFUN_PARAGRAPH_PARAMS_APPLY: {
2044                 // Given data, an encoding of the ParagraphParameters
2045                 // generated in the Paragraph dialog, this function sets
2046                 // the current paragraph, or currently selected paragraphs,
2047                 // appropriately.
2048                 // NOTE: This function overrides all existing settings.
2049                 setParagraphs(cur, cmd.argument());
2050                 cur.message(_("Paragraph layout set"));
2051                 break;
2052         }
2053
2054         case LFUN_PARAGRAPH_PARAMS: {
2055                 // Given data, an encoding of the ParagraphParameters as we'd
2056                 // find them in a LyX file, this function modifies the current paragraph,
2057                 // or currently selected paragraphs.
2058                 // NOTE: This function only modifies, and does not override, existing
2059                 // settings.
2060                 setParagraphs(cur, cmd.argument(), true);
2061                 cur.message(_("Paragraph layout set"));
2062                 break;
2063         }
2064
2065         case LFUN_ESCAPE:
2066                 if (cur.selection()) {
2067                         cur.setSelection(false);
2068                 } else {
2069                         cur.undispatched();
2070                         // This used to be LFUN_FINISHED_RIGHT, I think FORWARD is more
2071                         // correct, but I'm not 100% sure -- dov, 071019
2072                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
2073                 }
2074                 break;
2075
2076         case LFUN_OUTLINE_UP:
2077                 outline(OutlineUp, cur);
2078                 setCursor(cur, cur.pit(), 0);
2079                 cur.buffer()->updateLabels();
2080                 needsUpdate = true;
2081                 break;
2082
2083         case LFUN_OUTLINE_DOWN:
2084                 outline(OutlineDown, cur);
2085                 setCursor(cur, cur.pit(), 0);
2086                 cur.buffer()->updateLabels();
2087                 needsUpdate = true;
2088                 break;
2089
2090         case LFUN_OUTLINE_IN:
2091                 outline(OutlineIn, cur);
2092                 cur.buffer()->updateLabels();
2093                 needsUpdate = true;
2094                 break;
2095
2096         case LFUN_OUTLINE_OUT:
2097                 outline(OutlineOut, cur);
2098                 cur.buffer()->updateLabels();
2099                 needsUpdate = true;
2100                 break;
2101
2102         default:
2103                 LYXERR(Debug::ACTION, "Command " << cmd << " not DISPATCHED by Text");
2104                 cur.undispatched();
2105                 break;
2106         }
2107
2108         needsUpdate |= (cur.pos() != cur.lastpos()) && cur.selection();
2109
2110         // FIXME: The cursor flag is reset two lines below
2111         // so we need to check here if some of the LFUN did touch that.
2112         // for now only Text::erase() and Text::backspace() do that.
2113         // The plan is to verify all the LFUNs and then to remove this
2114         // singleParUpdate boolean altogether.
2115         if (cur.result().update() & Update::Force) {
2116                 singleParUpdate = false;
2117                 needsUpdate = true;
2118         }
2119
2120         // FIXME: the following code should go in favor of fine grained
2121         // update flag treatment.
2122         if (singleParUpdate) {
2123                 // Inserting characters does not change par height in general. So, try
2124                 // to update _only_ this paragraph. BufferView will detect if a full
2125                 // metrics update is needed anyway.
2126                 cur.updateFlags(Update::SinglePar | Update::FitCursor);
2127                 return;
2128         }
2129
2130         if (!needsUpdate
2131             && &oldTopSlice.inset() == &cur.inset()
2132             && oldTopSlice.idx() == cur.idx()
2133             && !sel // sel is a backup of cur.selection() at the beginning of the function.
2134             && !cur.selection())
2135                 // FIXME: it would be better if we could just do this
2136                 //
2137                 //if (cur.result().update() != Update::FitCursor)
2138                 //      cur.noUpdate();
2139                 //
2140                 // But some LFUNs do not set Update::FitCursor when needed, so we
2141                 // do it for all. This is not very harmfull as FitCursor will provoke
2142                 // a full redraw only if needed but still, a proper review of all LFUN
2143                 // should be done and this needsUpdate boolean can then be removed.
2144                 cur.updateFlags(Update::FitCursor);
2145         else
2146                 cur.updateFlags(Update::Force | Update::FitCursor);
2147 }
2148
2149
2150 bool Text::getStatus(Cursor & cur, FuncRequest const & cmd,
2151                         FuncStatus & flag) const
2152 {
2153         LASSERT(cur.text() == this, /**/);
2154
2155         FontInfo const & fontinfo = cur.real_current_font.fontInfo();
2156         bool enable = true;
2157         InsetCode code = NO_CODE;
2158
2159         switch (cmd.action) {
2160
2161         case LFUN_DEPTH_DECREMENT:
2162                 enable = changeDepthAllowed(cur, DEC_DEPTH);
2163                 break;
2164
2165         case LFUN_DEPTH_INCREMENT:
2166                 enable = changeDepthAllowed(cur, INC_DEPTH);
2167                 break;
2168
2169         case LFUN_APPENDIX:
2170                 flag.setOnOff(cur.paragraph().params().startOfAppendix());
2171                 break;
2172
2173         case LFUN_DIALOG_SHOW_NEW_INSET:
2174                 if (cmd.argument() == "bibitem")
2175                         code = BIBITEM_CODE;
2176                 else if (cmd.argument() == "bibtex") {
2177                         code = BIBTEX_CODE;
2178                         // not allowed in description items
2179                         enable = !inDescriptionItem(cur);
2180                 }
2181                 else if (cmd.argument() == "box")
2182                         code = BOX_CODE;
2183                 else if (cmd.argument() == "branch")
2184                         code = BRANCH_CODE;
2185                 else if (cmd.argument() == "citation")
2186                         code = CITE_CODE;
2187                 else if (cmd.argument() == "ert")
2188                         code = ERT_CODE;
2189                 else if (cmd.argument() == "external")
2190                         code = EXTERNAL_CODE;
2191                 else if (cmd.argument() == "float")
2192                         code = FLOAT_CODE;
2193                 else if (cmd.argument() == "graphics")
2194                         code = GRAPHICS_CODE;
2195                 else if (cmd.argument() == "href")
2196                         code = HYPERLINK_CODE;
2197                 else if (cmd.argument() == "include")
2198                         code = INCLUDE_CODE;
2199                 else if (cmd.argument() == "index")
2200                         code = INDEX_CODE;
2201                 else if (cmd.argument() == "index_print")
2202                         code = INDEX_PRINT_CODE;
2203                 else if (cmd.argument() == "nomenclature")
2204                         code = NOMENCL_CODE;
2205                 else if (cmd.argument() == "nomencl_print")
2206                         code = NOMENCL_PRINT_CODE;
2207                 else if (cmd.argument() == "label")
2208                         code = LABEL_CODE;
2209                 else if (cmd.argument() == "note")
2210                         code = NOTE_CODE;
2211                 else if (cmd.argument() == "phantom")
2212                         code = PHANTOM_CODE;
2213                 else if (cmd.argument() == "ref")
2214                         code = REF_CODE;
2215                 else if (cmd.argument() == "space")
2216                         code = SPACE_CODE;
2217                 else if (cmd.argument() == "toc")
2218                         code = TOC_CODE;
2219                 else if (cmd.argument() == "vspace")
2220                         code = VSPACE_CODE;
2221                 else if (cmd.argument() == "wrap")
2222                         code = WRAP_CODE;
2223                 else if (cmd.argument() == "listings")
2224                         code = LISTINGS_CODE;
2225                 break;
2226
2227         case LFUN_ERT_INSERT:
2228                 code = ERT_CODE;
2229                 break;
2230         case LFUN_LISTING_INSERT:
2231                 code = LISTINGS_CODE;
2232                 // not allowed in description items
2233                 enable = !inDescriptionItem(cur);
2234                 break;
2235         case LFUN_FOOTNOTE_INSERT:
2236                 code = FOOT_CODE;
2237                 break;
2238         case LFUN_TABULAR_INSERT:
2239                 code = TABULAR_CODE;
2240                 break;
2241         case LFUN_MARGINALNOTE_INSERT:
2242                 code = MARGIN_CODE;
2243                 break;
2244         case LFUN_FLOAT_INSERT:
2245         case LFUN_FLOAT_WIDE_INSERT:
2246                 // FIXME: If there is a selection, we should check whether there
2247                 // are floats in the selection, but this has performance issues, see
2248                 // LFUN_CHANGE_ACCEPT/REJECT.
2249                 code = FLOAT_CODE;
2250                 if (inDescriptionItem(cur))
2251                         // not allowed in description items
2252                         enable = false;
2253                 else {
2254                         InsetCode const inset_code = cur.inset().lyxCode();
2255
2256                         // algorithm floats cannot be put in another float
2257                         if (to_utf8(cmd.argument()) == "algorithm") {
2258                                 enable = inset_code != WRAP_CODE && inset_code != FLOAT_CODE;
2259                                 break;
2260                         }
2261
2262                         // for figures and tables: only allow in another
2263                         // float or wrap if it is of the same type and
2264                         // not a subfloat already
2265                         if(cur.inset().lyxCode() == code) {
2266                                 InsetFloat const & ins =
2267                                         static_cast<InsetFloat const &>(cur.inset());
2268                                 enable = ins.params().type == to_utf8(cmd.argument())
2269                                         && !ins.params().subfloat;
2270                         } else if(cur.inset().lyxCode() == WRAP_CODE) {
2271                                 InsetWrap const & ins =
2272                                         static_cast<InsetWrap const &>(cur.inset());
2273                                 enable = ins.params().type == to_utf8(cmd.argument());
2274                         }
2275                 }
2276                 break;
2277         case LFUN_WRAP_INSERT:
2278                 code = WRAP_CODE;
2279                 // not allowed in description items
2280                 enable = !inDescriptionItem(cur);
2281                 break;
2282         case LFUN_FLOAT_LIST_INSERT:
2283                 code = FLOAT_LIST_CODE;
2284                 // not allowed in description items
2285                 enable = !inDescriptionItem(cur);
2286                 break;
2287         case LFUN_CAPTION_INSERT:
2288                 code = CAPTION_CODE;
2289                 // not allowed in description items
2290                 enable = !inDescriptionItem(cur);
2291                 break;
2292         case LFUN_NOTE_INSERT:
2293                 code = NOTE_CODE;
2294                 // in commands (sections etc.) and description items,
2295                 // only Notes are allowed
2296                 enable = (cmd.argument().empty() || cmd.getArg(0) == "Note" ||
2297                           (!cur.paragraph().layout().isCommand()
2298                            && !inDescriptionItem(cur)));
2299                 break;
2300         case LFUN_FLEX_INSERT: {
2301                 code = FLEX_CODE;
2302                 string s = cmd.getArg(0);
2303                 InsetLayout il =
2304                         cur.buffer()->params().documentClass().insetLayout(from_utf8(s));
2305                 if (il.lyxtype() != InsetLayout::CHARSTYLE &&
2306                     il.lyxtype() != InsetLayout::CUSTOM &&
2307                     il.lyxtype() != InsetLayout::ELEMENT &&
2308                     il.lyxtype ()!= InsetLayout::STANDARD)
2309                         enable = false;
2310                 break;
2311                 }
2312         case LFUN_BOX_INSERT:
2313                 code = BOX_CODE;
2314                 break;
2315         case LFUN_BRANCH_INSERT:
2316                 code = BRANCH_CODE;
2317                 if (cur.buffer()->masterBuffer()->params().branchlist().empty()
2318                     && cur.buffer()->params().branchlist().empty())
2319                         enable = false;
2320                 break;
2321         case LFUN_PHANTOM_INSERT:
2322                 code = PHANTOM_CODE;
2323                 break;
2324         case LFUN_LABEL_INSERT:
2325                 code = LABEL_CODE;
2326                 break;
2327         case LFUN_LINE_INSERT:
2328                 code = LINE_CODE;
2329                 break;
2330         case LFUN_INFO_INSERT:
2331                 code = INFO_CODE;
2332                 break;
2333         case LFUN_OPTIONAL_INSERT:
2334                 code = OPTARG_CODE;
2335                 enable = cur.paragraph().insetList().count(OPTARG_CODE)
2336                         < cur.paragraph().layout().optionalargs;
2337                 break;
2338         case LFUN_INDEX_INSERT:
2339                 code = INDEX_CODE;
2340                 break;
2341         case LFUN_INDEX_PRINT:
2342                 code = INDEX_PRINT_CODE;
2343                 // not allowed in description items
2344                 enable = !inDescriptionItem(cur);
2345                 break;
2346         case LFUN_NOMENCL_INSERT:
2347                 if (cur.selIsMultiCell() || cur.selIsMultiLine()) {
2348                         enable = false;
2349                         break;
2350                 }
2351                 code = NOMENCL_CODE;
2352                 break;
2353         case LFUN_NOMENCL_PRINT:
2354                 code = NOMENCL_PRINT_CODE;
2355                 // not allowed in description items
2356                 enable = !inDescriptionItem(cur);
2357                 break;
2358         case LFUN_TOC_INSERT:
2359                 code = TOC_CODE;
2360                 // not allowed in description items
2361                 enable = !inDescriptionItem(cur);
2362                 break;
2363         case LFUN_HYPERLINK_INSERT:
2364                 if (cur.selIsMultiCell() || cur.selIsMultiLine()) {
2365                         enable = false;
2366                         break;
2367                 }
2368                 code = HYPERLINK_CODE;
2369                 break;
2370         case LFUN_QUOTE_INSERT:
2371                 // always allow this, since we will inset a raw quote
2372                 // if an inset is not allowed.
2373                 break;
2374         case LFUN_SPECIALCHAR_INSERT:
2375                 code = SPECIALCHAR_CODE;
2376                 break;
2377         case LFUN_SPACE_INSERT:
2378                 // slight hack: we know this is allowed in math mode
2379                 if (cur.inTexted())
2380                         code = SPACE_CODE;
2381                 break;
2382
2383         case LFUN_MATH_INSERT:
2384         case LFUN_MATH_AMS_MATRIX:
2385         case LFUN_MATH_MATRIX:
2386         case LFUN_MATH_DELIM:
2387         case LFUN_MATH_BIGDELIM:
2388         case LFUN_MATH_DISPLAY:
2389         case LFUN_MATH_MODE:
2390         case LFUN_MATH_MACRO:
2391         case LFUN_MATH_SUBSCRIPT:
2392         case LFUN_MATH_SUPERSCRIPT:
2393                 code = MATH_HULL_CODE;
2394                 break;
2395
2396         case LFUN_REGEXP_MODE:
2397                 code = MATH_HULL_CODE;
2398                 enable = cur.buffer()->isInternal() && !cur.inRegexped();
2399                 break;
2400
2401         case LFUN_INSET_MODIFY:
2402                 // We need to disable this, because we may get called for a
2403                 // tabular cell via
2404                 // InsetTabular::getStatus() -> InsetText::getStatus()
2405                 // and we don't handle LFUN_INSET_MODIFY.
2406                 enable = false;
2407                 break;
2408
2409         case LFUN_FONT_EMPH:
2410                 flag.setOnOff(fontinfo.emph() == FONT_ON);
2411                 enable = !cur.inset().getLayout().isPassThru();
2412                 break;
2413
2414         case LFUN_FONT_ITAL:
2415                 flag.setOnOff(fontinfo.shape() == ITALIC_SHAPE);
2416                 enable = !cur.inset().getLayout().isPassThru();
2417                 break;
2418
2419         case LFUN_FONT_NOUN:
2420                 flag.setOnOff(fontinfo.noun() == FONT_ON);
2421                 enable = !cur.inset().getLayout().isPassThru();
2422                 break;
2423
2424         case LFUN_FONT_BOLD:
2425         case LFUN_FONT_BOLDSYMBOL:
2426                 flag.setOnOff(fontinfo.series() == BOLD_SERIES);
2427                 enable = !cur.inset().getLayout().isPassThru();
2428                 break;
2429
2430         case LFUN_FONT_SANS:
2431                 flag.setOnOff(fontinfo.family() == SANS_FAMILY);
2432                 enable = !cur.inset().getLayout().isPassThru();
2433                 break;
2434
2435         case LFUN_FONT_ROMAN:
2436                 flag.setOnOff(fontinfo.family() == ROMAN_FAMILY);
2437                 enable = !cur.inset().getLayout().isPassThru();
2438                 break;
2439
2440         case LFUN_FONT_TYPEWRITER:
2441                 flag.setOnOff(fontinfo.family() == TYPEWRITER_FAMILY);
2442                 enable = !cur.inset().getLayout().isPassThru();
2443                 break;
2444
2445         case LFUN_CUT:
2446         case LFUN_COPY:
2447                 enable = cur.selection();
2448                 break;
2449
2450         case LFUN_PASTE: {
2451                 if (cmd.argument().empty()) {
2452                         if (theClipboard().isInternal())
2453                                 enable = cap::numberOfSelections() > 0;
2454                         else
2455                                 enable = !theClipboard().empty();
2456                         break;
2457                 }
2458
2459                 // we have an argument
2460                 string const arg = to_utf8(cmd.argument());
2461                 if (isStrUnsignedInt(arg)) {
2462                         // it's a number and therefore means the internal stack
2463                         unsigned int n = convert<unsigned int>(arg);
2464                         enable = cap::numberOfSelections() > n;
2465                         break;
2466                 }
2467
2468                 // explicit graphics type?
2469                 Clipboard::GraphicsType type = Clipboard::AnyGraphicsType;
2470                 if ((arg == "pdf" && (type = Clipboard::PdfGraphicsType))
2471                           || (arg == "png" && (type = Clipboard::PngGraphicsType))
2472                           || (arg == "jpeg" && (type = Clipboard::JpegGraphicsType))
2473                           || (arg == "linkback" &&  (type = Clipboard::LinkBackGraphicsType))
2474                           || (arg == "emf" &&  (type = Clipboard::EmfGraphicsType))
2475                           || (arg == "wmf" &&  (type = Clipboard::WmfGraphicsType))) {
2476                         enable = theClipboard().hasGraphicsContents(type);
2477                         break;
2478                 }
2479
2480                 // unknown argument
2481                 enable = false;
2482                 break;
2483          }
2484
2485         case LFUN_CLIPBOARD_PASTE:
2486                 enable = !theClipboard().empty();
2487                 break;
2488
2489         case LFUN_PRIMARY_SELECTION_PASTE:
2490                 enable = cur.selection() || !theSelection().empty();
2491                 break;
2492
2493         case LFUN_SELECTION_PASTE:
2494                 enable = cap::selection();
2495                 break;
2496
2497         case LFUN_PARAGRAPH_MOVE_UP:
2498                 enable = cur.pit() > 0 && !cur.selection();
2499                 break;
2500
2501         case LFUN_PARAGRAPH_MOVE_DOWN:
2502                 enable = cur.pit() < cur.lastpit() && !cur.selection();
2503                 break;
2504
2505         case LFUN_CHANGE_ACCEPT:
2506         case LFUN_CHANGE_REJECT:
2507                 // In principle, these LFUNs should only be enabled if there
2508                 // is a change at the current position/in the current selection.
2509                 // However, without proper optimizations, this will inevitably
2510                 // result in unacceptable performance - just imagine a user who
2511                 // wants to select the complete content of a long document.
2512                 if (!cur.selection())
2513                         enable = cur.paragraph().isChanged(cur.pos());
2514                 else
2515                         // TODO: context-sensitive enabling of LFUN_CHANGE_ACCEPT/REJECT
2516                         // for selections.
2517                         enable = true;
2518                 break;
2519
2520         case LFUN_OUTLINE_UP:
2521         case LFUN_OUTLINE_DOWN:
2522         case LFUN_OUTLINE_IN:
2523         case LFUN_OUTLINE_OUT:
2524                 // FIXME: LyX is not ready for outlining within inset.
2525                 enable = isMainText()
2526                         && cur.paragraph().layout().toclevel != Layout::NOT_IN_TOC;
2527                 break;
2528
2529         case LFUN_NEWLINE_INSERT:
2530                 // LaTeX restrictions (labels or empty par)
2531                 enable = (cur.pos() > cur.paragraph().beginOfBody());
2532                 break;
2533
2534         case LFUN_TAB_INSERT:
2535         case LFUN_TAB_DELETE:
2536                 enable = cur.inset().getLayout().isPassThru();
2537                 break;
2538
2539         case LFUN_SET_GRAPHICS_GROUP: {
2540                 InsetGraphics * ins = graphics::getCurrentGraphicsInset(cur);
2541                 if (!ins)
2542                         enable = false;
2543                 else
2544                         flag.setOnOff(to_utf8(cmd.argument()) == ins->getParams().groupId);
2545                 break;
2546         }
2547
2548         case LFUN_NEWPAGE_INSERT:
2549                 // not allowed in description items
2550                 code = NEWPAGE_CODE;
2551                 enable = !inDescriptionItem(cur);
2552                 break;
2553
2554         case LFUN_DATE_INSERT: {
2555                 string const format = cmd.argument().empty()
2556                         ? lyxrc.date_insert_format : to_utf8(cmd.argument());
2557                 enable = support::os::is_valid_strftime(format);
2558                 break;
2559         }
2560
2561         case LFUN_LANGUAGE:
2562                 enable = !cur.inset().getLayout().isPassThru();
2563                 flag.setOnOff(to_utf8(cmd.argument()) == cur.real_current_font.language()->lang());
2564                 break;
2565
2566         case LFUN_BREAK_PARAGRAPH:
2567                 enable = cur.inset().getLayout().isMultiPar();
2568                 break;
2569         
2570         case LFUN_SPELLING_ADD:
2571         case LFUN_SPELLING_IGNORE:
2572                 enable = theSpellChecker();
2573                 break;
2574
2575         case LFUN_LAYOUT:
2576                 enable = !cur.inset().forcePlainLayout();
2577                 break;
2578                 
2579         case LFUN_LAYOUT_PARAGRAPH:
2580         case LFUN_PARAGRAPH_PARAMS:
2581         case LFUN_PARAGRAPH_PARAMS_APPLY:
2582         case LFUN_PARAGRAPH_UPDATE:
2583                 enable = cur.inset().allowParagraphCustomization();
2584                 break;
2585
2586         // FIXME: why are accent lfuns forbidden with pass_thru layouts?
2587         case LFUN_ACCENT_ACUTE:
2588         case LFUN_ACCENT_BREVE:
2589         case LFUN_ACCENT_CARON:
2590         case LFUN_ACCENT_CEDILLA:
2591         case LFUN_ACCENT_CIRCLE:
2592         case LFUN_ACCENT_CIRCUMFLEX:
2593         case LFUN_ACCENT_DOT:
2594         case LFUN_ACCENT_GRAVE:
2595         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
2596         case LFUN_ACCENT_MACRON:
2597         case LFUN_ACCENT_OGONEK:
2598         case LFUN_ACCENT_TIE:
2599         case LFUN_ACCENT_TILDE:
2600         case LFUN_ACCENT_UMLAUT:
2601         case LFUN_ACCENT_UNDERBAR:
2602         case LFUN_ACCENT_UNDERDOT:
2603         case LFUN_FONT_DEFAULT:
2604         case LFUN_FONT_FRAK:
2605         case LFUN_FONT_SIZE:
2606         case LFUN_FONT_STATE:
2607         case LFUN_FONT_UNDERLINE:
2608         case LFUN_FONT_STRIKEOUT:
2609         case LFUN_FONT_UULINE:
2610         case LFUN_FONT_UWAVE:
2611         case LFUN_TEXTSTYLE_APPLY:
2612         case LFUN_TEXTSTYLE_UPDATE:
2613                 enable = !cur.inset().getLayout().isPassThru();
2614                 break;
2615
2616         case LFUN_WORD_DELETE_FORWARD:
2617         case LFUN_WORD_DELETE_BACKWARD:
2618         case LFUN_LINE_DELETE:
2619         case LFUN_WORD_FORWARD:
2620         case LFUN_WORD_BACKWARD:
2621         case LFUN_WORD_RIGHT:
2622         case LFUN_WORD_LEFT:
2623         case LFUN_CHAR_FORWARD:
2624         case LFUN_CHAR_FORWARD_SELECT:
2625         case LFUN_CHAR_BACKWARD:
2626         case LFUN_CHAR_BACKWARD_SELECT:
2627         case LFUN_CHAR_LEFT:
2628         case LFUN_CHAR_LEFT_SELECT:
2629         case LFUN_CHAR_RIGHT:
2630         case LFUN_CHAR_RIGHT_SELECT:
2631         case LFUN_UP:
2632         case LFUN_UP_SELECT:
2633         case LFUN_DOWN:
2634         case LFUN_DOWN_SELECT:
2635         case LFUN_PARAGRAPH_UP_SELECT:
2636         case LFUN_PARAGRAPH_DOWN_SELECT:
2637         case LFUN_LINE_BEGIN_SELECT:
2638         case LFUN_LINE_END_SELECT:
2639         case LFUN_WORD_FORWARD_SELECT:
2640         case LFUN_WORD_BACKWARD_SELECT:
2641         case LFUN_WORD_RIGHT_SELECT:
2642         case LFUN_WORD_LEFT_SELECT:
2643         case LFUN_WORD_SELECT:
2644         case LFUN_SECTION_SELECT:
2645         case LFUN_BUFFER_BEGIN:
2646         case LFUN_BUFFER_END:
2647         case LFUN_BUFFER_BEGIN_SELECT:
2648         case LFUN_BUFFER_END_SELECT:
2649         case LFUN_INSET_BEGIN:
2650         case LFUN_INSET_END:
2651         case LFUN_INSET_BEGIN_SELECT:
2652         case LFUN_INSET_END_SELECT:
2653         case LFUN_INSET_SELECT_ALL:
2654         case LFUN_PARAGRAPH_UP:
2655         case LFUN_PARAGRAPH_DOWN:
2656         case LFUN_LINE_BEGIN:
2657         case LFUN_LINE_END:
2658         case LFUN_CHAR_DELETE_FORWARD:
2659         case LFUN_CHAR_DELETE_BACKWARD:
2660         case LFUN_INSET_INSERT:
2661         case LFUN_WORD_UPCASE:
2662         case LFUN_WORD_LOWCASE:
2663         case LFUN_WORD_CAPITALIZE:
2664         case LFUN_CHARS_TRANSPOSE:
2665         case LFUN_SERVER_GET_XY:
2666         case LFUN_SERVER_SET_XY:
2667         case LFUN_SERVER_GET_LAYOUT:
2668         case LFUN_SELF_INSERT:
2669         case LFUN_UNICODE_INSERT:
2670         case LFUN_THESAURUS_ENTRY:
2671         case LFUN_ESCAPE:
2672                 // these are handled in our dispatch()
2673                 enable = true;
2674                 break;
2675
2676         default:
2677                 return false;
2678         }
2679
2680         if (code != NO_CODE
2681             && (cur.empty() 
2682                 || !cur.inset().insetAllowed(code)
2683                 || cur.paragraph().layout().pass_thru))
2684                 enable = false;
2685
2686         flag.setEnabled(enable);
2687         return true;
2688 }
2689
2690
2691 void Text::pasteString(Cursor & cur, docstring const & clip,
2692                 bool asParagraphs)
2693 {
2694         cur.clearSelection();
2695         if (!clip.empty()) {
2696                 cur.recordUndo();
2697                 if (asParagraphs)
2698                         insertStringAsParagraphs(cur, clip, cur.current_font);
2699                 else
2700                         insertStringAsLines(cur, clip, cur.current_font);
2701         }
2702 }
2703
2704
2705 // FIXME: an item inset would make things much easier.
2706 bool Text::inDescriptionItem(Cursor & cur) const
2707 {
2708         Paragraph & par = cur.paragraph();
2709         pos_type const pos = cur.pos();
2710         pos_type const body_pos = par.beginOfBody();
2711
2712         if (par.layout().latextype != LATEX_LIST_ENVIRONMENT
2713             && (par.layout().latextype != LATEX_ITEM_ENVIRONMENT
2714                 || par.layout().margintype != MARGIN_FIRST_DYNAMIC))
2715                 return false;
2716
2717         return (pos < body_pos
2718                 || (pos == body_pos
2719                     && (pos == 0 || par.getChar(pos - 1) != ' ')));
2720 }
2721
2722 } // namespace lyx