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