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