]> git.lyx.org Git - lyx.git/blob - src/text3.C
more cursor up/down smoothing
[lyx.git] / src / text3.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2002 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include "lyxtext.h"
14 #include "lyxrow.h"
15 #include "paragraph.h"
16 #include "BufferView.h"
17 #include "funcrequest.h"
18 #include "lyxrc.h"
19 #include "debug.h"
20 #include "bufferparams.h"
21 #include "buffer.h"
22 #include "ParagraphParameters.h"
23 #include "gettext.h"
24 #include "intl.h"
25 #include "support/lstrings.h"
26 #include "frontends/LyXView.h"
27 #include "frontends/screen.h"
28 #include "frontends/WorkArea.h"
29 #include "insets/insetspecialchar.h"
30 #include "insets/insettext.h"
31 #include "undo_funcs.h"
32
33 using std::endl;
34
35 extern string current_layout;
36
37
38 namespace {
39
40 void cursorPrevious(LyXText * text, BufferView * bv)
41 {
42         if (!text->cursor.row()->previous()) {
43                 if (text->first_y > 0) {
44                         int new_y = bv->text->first_y - bv->workarea().workHeight();
45                         bv->screen().draw(bv->text, bv, new_y < 0 ? 0 : new_y);
46                         bv->updateScrollbar();
47                 }
48                 return;
49         }
50
51         int y = text->first_y;
52         Row * cursorrow = text->cursor.row();
53
54         text->setCursorFromCoordinates(bv, text->cursor.x_fix(), y);
55         finishUndo();
56
57         int new_y;
58         if (cursorrow == bv->text->cursor.row()) {
59                 // we have a row which is higher than the workarea so we leave the
60                 // cursor on the start of the row and move only the draw up as soon
61                 // as we move the cursor or do something while inside the row (it may
62                 // span several workarea-heights) we'll move to the top again, but this
63                 // is better than just jump down and only display part of the row.
64                 new_y = bv->text->first_y - bv->workarea().workHeight();
65         } else {
66                 if (text->inset_owner) {
67                         new_y = bv->text->cursor.iy()
68                                 + bv->theLockingInset()->insetInInsetY() + y
69                                 + text->cursor.row()->height()
70                                 - bv->workarea().workHeight() + 1;
71                 } else {
72                         new_y = text->cursor.y()
73                                 - text->cursor.row()->baseline()
74                                 + text->cursor.row()->height()
75                                 - bv->workarea().workHeight() + 1;
76                 }
77         }
78         bv->screen().draw(bv->text, bv, new_y < 0 ? 0 : new_y);
79         if (text->cursor.row()->previous()) {
80                 LyXCursor cur;
81                 text->setCursor(bv, cur, text->cursor.row()->previous()->par(),
82                                                 text->cursor.row()->previous()->pos(), false);
83                 if (cur.y() > text->first_y) {
84                         text->cursorUp(bv, true);
85                 }
86         }
87         bv->updateScrollbar();
88 }
89
90
91 void cursorNext(LyXText * text, BufferView * bv)
92 {
93         if (!text->cursor.row()->next()) {
94                 int y = text->cursor.y() - text->cursor.row()->baseline() +
95                         text->cursor.row()->height();
96                 if (y > int(text->first_y + bv->workarea().workHeight())) {
97                         bv->screen().draw(bv->text, bv,
98                                                   bv->text->first_y + bv->workarea().workHeight());
99                         bv->updateScrollbar();
100                 }
101                 return;
102         }
103
104         int y = text->first_y + bv->workarea().workHeight();
105         if (text->inset_owner && !text->first_y) {
106                 y -= (bv->text->cursor.iy()
107                           - bv->text->first_y
108                           + bv->theLockingInset()->insetInInsetY());
109         }
110
111         text->getRowNearY(y);
112
113         Row * cursorrow = text->cursor.row();
114         text->setCursorFromCoordinates(bv, text->cursor.x_fix(), y);
115         // + workarea().workHeight());
116         finishUndo();
117
118         int new_y;
119         if (cursorrow == bv->text->cursor.row()) {
120                 // we have a row which is higher than the workarea so we leave the
121                 // cursor on the start of the row and move only the draw down as soon
122                 // as we move the cursor or do something while inside the row (it may
123                 // span several workarea-heights) we'll move to the top again, but this
124                 // is better than just jump down and only display part of the row.
125                 new_y = bv->text->first_y + bv->workarea().workHeight();
126         } else {
127                 if (text->inset_owner) {
128                         new_y = bv->text->cursor.iy()
129                                 + bv->theLockingInset()->insetInInsetY()
130                                 + y - text->cursor.row()->baseline();
131                 } else {
132                         new_y =  text->cursor.y() - text->cursor.row()->baseline();
133                 }
134         }
135         bv->screen().draw(bv->text, bv, new_y);
136         if (text->cursor.row()->next()) {
137                 LyXCursor cur;
138                 text->setCursor(bv, cur, text->cursor.row()->next()->par(),
139                                                 text->cursor.row()->next()->pos(), false);
140                 if (cur.y() < int(text->first_y + bv->workarea().workHeight())) {
141                         text->cursorDown(bv, true);
142                 }
143         }
144         bv->updateScrollbar();
145 }
146
147 }
148
149
150 void LyXText::update(BufferView * bv, bool changed)
151 {
152         BufferView::UpdateCodes c = BufferView::SELECT | BufferView::FITCUR;
153         if (changed)
154                 bv->update(this, c | BufferView::CHANGE);
155         else
156                 bv->update(this, c);
157 }
158
159
160 void specialChar(LyXText * lt, BufferView * bv, InsetSpecialChar::Kind kind)
161 {
162         bv->hideCursor();
163         lt->update(bv);
164         InsetSpecialChar * new_inset = new InsetSpecialChar(kind);
165         if (!bv->insertInset(new_inset))
166                 delete new_inset;
167         else
168                 bv->updateInset(new_inset, true);
169 }
170
171
172 Inset::RESULT LyXText::dispatch(FuncRequest const & cmd)
173 {
174         lyxerr[Debug::ACTION] << "LyXFunc::dispatch: action[" << cmd.action
175                               <<"] arg[" << cmd.argument << "]" << endl;
176
177         BufferView * bv = cmd.view();
178
179         switch (cmd.action) {
180
181         case LFUN_APPENDIX: {
182                 Paragraph * par = cursor.par();
183                 bool start = !par->params().startOfAppendix();
184
185                 // ensure that we have only one start_of_appendix in this document
186                 Paragraph * tmp = ownerParagraph();
187                 for (; tmp; tmp = tmp->next())
188                         tmp->params().startOfAppendix(false);
189
190                 par->params().startOfAppendix(start);
191
192                 // we can set the refreshing parameters now
193                 status(cmd.view(), LyXText::NEED_MORE_REFRESH);
194                 refresh_y = 0;
195                 refresh_row = 0; // not needed for full update
196                 updateCounters(cmd.view());
197                 setCursor(cmd.view(), cursor.par(), cursor.pos());
198                 update(bv);
199                 break;
200         }
201
202         case LFUN_DELETE_WORD_FORWARD:
203                 bv->beforeChange(this);
204                 update(bv, false);
205                 deleteWordForward(bv);
206                 update(bv);
207                 bv->finishChange();
208                 break;
209
210         case LFUN_DELETE_WORD_BACKWARD:
211                 bv->beforeChange(this);
212                 update(bv, false);
213                 deleteWordBackward(bv);
214                 update(bv, true);
215                 bv->finishChange();
216                 break;
217
218         case LFUN_DELETE_LINE_FORWARD:
219                 bv->beforeChange(this);
220                 update(bv, false);
221                 deleteLineForward(bv);
222                 update(bv);
223                 bv->finishChange();
224                 break;
225
226         case LFUN_SHIFT_TAB:
227         case LFUN_TAB:
228                 if (!selection.mark())
229                         bv->beforeChange(this);
230                 update(bv, false);
231                 cursorTab(bv);
232                 bv->finishChange();
233                 break;
234
235         case LFUN_WORDRIGHT:
236                 if (!selection.mark())
237                         bv->beforeChange(this);
238                 update(bv, false);
239                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
240                         cursorLeftOneWord(bv);
241                 else
242                         cursorRightOneWord(bv);
243                 bv->finishChange();
244                 break;
245
246         case LFUN_WORDLEFT:
247                 if (!selection.mark())
248                         bv->beforeChange(this);
249                 update(bv, false);
250                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
251                         cursorRightOneWord(bv);
252                 else
253                         cursorLeftOneWord(bv);
254                 bv->finishChange();
255                 break;
256
257         case LFUN_BEGINNINGBUF:
258                 if (!selection.mark())
259                         bv->beforeChange(this);
260                 update(bv, false);
261                 cursorTop(bv);
262                 bv->finishChange();
263                 break;
264
265         case LFUN_ENDBUF:
266                 if (selection.mark())
267                         bv->beforeChange(this);
268                 update(bv, false);
269                 cursorBottom(bv);
270                 bv->finishChange();
271                 break;
272
273         case LFUN_RIGHTSEL:
274                 update(bv, false);
275                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
276                         cursorLeft(bv);
277                 else
278                         cursorRight(bv);
279                 bv->finishChange(true);
280                 break;
281
282         case LFUN_LEFTSEL:
283                 update(bv, false);
284                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
285                         cursorRight(bv);
286                 else
287                         cursorLeft(bv);
288                 bv->finishChange(true);
289                 break;
290
291         case LFUN_UPSEL:
292                 update(bv, false);
293                 cursorUp(bv, true);
294                 bv->finishChange(true);
295                 break;
296
297         case LFUN_DOWNSEL:
298                 update(bv, false);
299                 cursorDown(bv, true);
300                 bv->finishChange(true);
301                 break;
302
303         case LFUN_UP_PARAGRAPHSEL:
304                 update(bv, false);
305                 cursorUpParagraph(bv);
306                 bv->finishChange(true);
307                 break;
308
309         case LFUN_DOWN_PARAGRAPHSEL:
310                 update(bv, false);
311                 cursorDownParagraph(bv);
312                 bv->finishChange(true);
313                 break;
314
315         case LFUN_PRIORSEL:
316                 update(bv, false);
317                 cursorPrevious(this, bv);
318                 bv->finishChange(true);
319                 break;
320
321         case LFUN_NEXTSEL:
322                 update(bv, false);
323                 cursorNext(this, bv);
324                 bv->finishChange();
325                 break;
326
327         case LFUN_HOMESEL:
328                 update(bv, false);
329                 cursorHome(bv);
330                 bv->finishChange(true);
331                 break;
332
333         case LFUN_ENDSEL:
334                 update(bv, false);
335                 cursorEnd(bv);
336                 bv->finishChange(true);
337                 break;
338
339         case LFUN_WORDRIGHTSEL:
340                 update(bv, false);
341                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
342                         cursorLeftOneWord(bv);
343                 else
344                         cursorRightOneWord(bv);
345                 bv->finishChange(true);
346                 break;
347
348         case LFUN_WORDLEFTSEL:
349                 update(bv, false);
350                 if (cursor.par()->isRightToLeftPar(bv->buffer()->params))
351                         cursorRightOneWord(bv);
352                 else
353                         cursorLeftOneWord(bv);
354                 bv->finishChange(true);
355                 break;
356
357         case LFUN_RIGHT: {
358                 bool is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
359                 if (!selection.mark())
360                         bv->beforeChange(this);
361                 update(bv);
362                 if (is_rtl)
363                         cursorLeft(bv, false);
364                 if (cursor.pos() < cursor.par()->size()
365                     && cursor.par()->isInset(cursor.pos())
366                     && isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
367                         Inset * tmpinset = cursor.par()->getInset(cursor.pos());
368                         cmd.message(tmpinset->editMessage());
369                         tmpinset->edit(bv, !is_rtl);
370                         break;
371                 }
372                 if (!is_rtl)
373                         cursorRight(bv, false);
374                 bv->finishChange(false);
375                 break;
376         }
377
378         case LFUN_LEFT: {
379                 // This is soooo ugly. Isn`t it possible to make
380                 // it simpler? (Lgb)
381                 bool const is_rtl = cursor.par()->isRightToLeftPar(bv->buffer()->params);
382                 if (!selection.mark())
383                         bv->beforeChange(this);
384                 update(bv);
385                 LyXCursor const cur = cursor;
386                 if (!is_rtl)
387                         cursorLeft(bv, false);
388                 if ((is_rtl || cur != cursor) && // only if really moved!
389                     cursor.pos() < cursor.par()->size() &&
390                     cursor.par()->isInset(cursor.pos()) &&
391                     isHighlyEditableInset(cursor.par()->getInset(cursor.pos()))) {
392                         Inset * tmpinset = cursor.par()->getInset(cursor.pos());
393                         cmd.message(tmpinset->editMessage());
394                         tmpinset->edit(bv, is_rtl);
395                         break;
396                 }
397                 if (is_rtl)
398                         cursorRight(bv, false);
399                 bv->finishChange(false);
400                 break;
401         }
402
403         case LFUN_UP:
404                 if (!selection.mark())
405                         bv->beforeChange(this);
406                 bv->update(this, BufferView::UPDATE);
407                 cursorUp(bv);
408                 bv->finishChange(false);
409                 break;
410
411         case LFUN_DOWN:
412                 if (!selection.mark())
413                         bv->beforeChange(this);
414                 bv->update(this, BufferView::UPDATE);
415                 cursorDown(bv);
416                 bv->finishChange();
417                 break;
418
419         case LFUN_UP_PARAGRAPH:
420                 if (!selection.mark())
421                         bv->beforeChange(this);
422                 bv->update(this, BufferView::UPDATE);
423                 cursorUpParagraph(bv);
424                 bv->finishChange();
425                 break;
426
427         case LFUN_DOWN_PARAGRAPH:
428                 if (!selection.mark())
429                         bv->beforeChange(this);
430                 bv->update(this, BufferView::UPDATE);
431                 cursorDownParagraph(bv);
432                 bv->finishChange(false);
433                 break;
434
435         case LFUN_PRIOR:
436                 if (!selection.mark())
437                         bv->beforeChange(this);
438                 bv->update(this, BufferView::UPDATE);
439                 cursorPrevious(this, bv);
440                 bv->finishChange(false);
441                 // was:
442                 // finishUndo();
443                 // moveCursorUpdate(false, false);
444                 // owner_->view_state_changed();
445                 break;
446
447         case LFUN_NEXT:
448                 if (!selection.mark())
449                         bv->beforeChange(this);
450                 bv->update(this, BufferView::UPDATE);
451                 cursorNext(this, bv);
452                 bv->finishChange(false);
453                 break;
454
455         case LFUN_HOME:
456                 if (!selection.mark())
457                         bv->beforeChange(this);
458                 update(bv);
459                 cursorHome(bv);
460                 bv->finishChange(false);
461                 break;
462
463         case LFUN_END:
464                 if (!selection.mark())
465                         bv->beforeChange(this);
466                 update(bv);
467                 cursorEnd(bv);
468                 bv->finishChange(false);
469                 break;
470
471         case LFUN_BREAKLINE:
472                 bv->beforeChange(this);
473                 insertChar(bv, Paragraph::META_NEWLINE);
474                 update(bv, true);
475                 setCursor(bv, cursor.par(), cursor.pos());
476                 bv->moveCursorUpdate(false);
477                 break;
478
479         case LFUN_DELETE:
480                 if (!selection.set()) {
481                         Delete(bv);
482                         selection.cursor = cursor;
483                         update(bv);
484                         // It is possible to make it a lot faster still
485                         // just comment out the line below...
486                         bv->showCursor();
487                 } else {
488                         bv->cut(false);
489                 }
490                 bv->moveCursorUpdate(false);
491                 bv->owner()->view_state_changed();
492                 bv->switchKeyMap();
493                 break;
494
495         case LFUN_DELETE_SKIP:
496                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
497                 if (!selection.set()) {
498                         LyXCursor cur = cursor;
499                         if (cur.pos() == cur.par()->size()) {
500                                 cursorRight(bv);
501                                 cur = cursor;
502                                 if (cur.pos() == 0
503                                     && !(cur.par()->params().spaceTop()
504                                          == VSpace (VSpace::NONE))) {
505                                         setParagraph(bv,
506                                                  cur.par()->params().lineTop(),
507                                                  cur.par()->params().lineBottom(),
508                                                  cur.par()->params().pagebreakTop(),
509                                                  cur.par()->params().pagebreakBottom(),
510                                                  VSpace(VSpace::NONE),
511                                                  cur.par()->params().spaceBottom(),
512                                                  cur.par()->params().spacing(),
513                                                  cur.par()->params().align(),
514                                                  cur.par()->params().labelWidthString(), 0);
515                                         cursorLeft(bv);
516                                         update(bv);
517                                 } else {
518                                         cursorLeft(bv);
519                                         Delete(bv);
520                                         selection.cursor = cursor;
521                                         update(bv);
522                                 }
523                         } else {
524                                 Delete(bv);
525                                 selection.cursor = cursor;
526                                 update(bv);
527                         }
528                 } else
529                         bv->cut(false);
530                 break;
531
532
533         case LFUN_BACKSPACE:
534                 if (!selection.set()) {
535                         if (bv->owner()->getIntl().getTransManager().backspace()) {
536                                 backspace(bv);
537                                 selection.cursor = cursor;
538                                 update(bv);
539                                 // It is possible to make it a lot faster still
540                                 // just comment out the line below...
541                                 bv->showCursor();
542                         }
543                 } else
544                         bv->cut(false);
545                 bv->owner()->view_state_changed();
546                 bv->switchKeyMap();
547                 break;
548
549         case LFUN_BACKSPACE_SKIP:
550                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
551                 if (!selection.set()) {
552                         LyXCursor cur = cursor;
553                         if (cur.pos() == 0
554                             && !(cur.par()->params().spaceTop()
555                                  == VSpace (VSpace::NONE))) {
556                                 setParagraph(bv,
557                                          cur.par()->params().lineTop(),
558                                          cur.par()->params().lineBottom(),
559                                          cur.par()->params().pagebreakTop(),
560                                          cur.par()->params().pagebreakBottom(),
561                                          VSpace(VSpace::NONE), cur.par()->params().spaceBottom(),
562                                          cur.par()->params().spacing(),
563                                          cur.par()->params().align(),
564                                          cur.par()->params().labelWidthString(), 0);
565                                 update(bv);
566                         } else {
567                                 backspace(bv);
568                                 selection.cursor = cur;
569                                 update(bv);
570                         }
571                 } else
572                         bv->cut(false);
573                 break;
574
575         case LFUN_BREAKPARAGRAPH:
576                 bv->beforeChange(this);
577                 breakParagraph(bv, 0);
578                 update(bv);
579                 selection.cursor = cursor;
580                 bv->switchKeyMap();
581                 bv->owner()->view_state_changed();
582                 break;
583
584         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
585                 bv->beforeChange(this);
586                 breakParagraph(bv, 1);
587                 update(bv);
588                 selection.cursor = cursor;
589                 bv->switchKeyMap();
590                 bv->owner()->view_state_changed();
591                 break;
592
593         case LFUN_BREAKPARAGRAPH_SKIP: {
594                 // When at the beginning of a paragraph, remove
595                 // indentation and add a "defskip" at the top.
596                 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
597                 LyXCursor cur = cursor;
598                 bv->beforeChange(this);
599                 if (cur.pos() == 0) {
600                         if (cur.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
601                                 setParagraph(bv,
602                                          cur.par()->params().lineTop(),
603                                          cur.par()->params().lineBottom(),
604                                          cur.par()->params().pagebreakTop(),
605                                          cur.par()->params().pagebreakBottom(),
606                                          VSpace(VSpace::DEFSKIP), cur.par()->params().spaceBottom(),
607                                          cur.par()->params().spacing(),
608                                          cur.par()->params().align(),
609                                          cur.par()->params().labelWidthString(), 1);
610                                 //update(bv);
611                         }
612                 }
613                 else {
614                         breakParagraph(bv, 0);
615                         //update(bv);
616                 }
617                 update(bv);
618                 selection.cursor = cur;
619                 bv->switchKeyMap();
620                 bv->owner()->view_state_changed();
621                 break;
622         }
623
624         case LFUN_PARAGRAPH_SPACING: {
625                 Paragraph * par = cursor.par();
626                 Spacing::Space cur_spacing = par->params().spacing().getSpace();
627                 float cur_value = 1.0;
628                 if (cur_spacing == Spacing::Other)
629                         cur_value = par->params().spacing().getValue();
630
631                 istringstream is(cmd.argument.c_str());
632                 string tmp;
633                 is >> tmp;
634                 Spacing::Space new_spacing = cur_spacing;
635                 float new_value = cur_value;
636                 if (tmp.empty()) {
637                         lyxerr << "Missing argument to `paragraph-spacing'"
638                                << endl;
639                 } else if (tmp == "single") {
640                         new_spacing = Spacing::Single;
641                 } else if (tmp == "onehalf") {
642                         new_spacing = Spacing::Onehalf;
643                 } else if (tmp == "double") {
644                         new_spacing = Spacing::Double;
645                 } else if (tmp == "other") {
646                         new_spacing = Spacing::Other;
647                         float tmpval = 0.0;
648                         is >> tmpval;
649                         lyxerr << "new_value = " << tmpval << endl;
650                         if (tmpval != 0.0)
651                                 new_value = tmpval;
652                 } else if (tmp == "default") {
653                         new_spacing = Spacing::Default;
654                 } else {
655                         lyxerr << _("Unknown spacing argument: ")
656                                << cmd.argument << endl;
657                 }
658                 if (cur_spacing != new_spacing || cur_value != new_value) {
659                         par->params().spacing(Spacing(new_spacing, new_value));
660                         redoParagraph(bv);
661                         update(bv);
662                 }
663                 break;
664         }
665
666         case LFUN_INSET_TOGGLE:
667                 bv->hideCursor();
668                 bv->beforeChange(this);
669                 update(bv, false);
670                 toggleInset(bv);
671                 update(bv, false);
672                 bv->switchKeyMap();
673                 break;
674
675         case LFUN_PROTECTEDSPACE:
676                 if (cursor.par()->layout()->free_spacing) {
677                         insertChar(bv, ' ');
678                         update(bv);
679                 } else {
680                         specialChar(this, bv, InsetSpecialChar::PROTECTED_SEPARATOR);
681                 }
682                 bv->moveCursorUpdate(false);
683                 break;
684
685         case LFUN_HYPHENATION:
686                 specialChar(this, bv, InsetSpecialChar::HYPHENATION);
687                 break;
688
689         case LFUN_LIGATURE_BREAK:
690                 specialChar(this, bv, InsetSpecialChar::LIGATURE_BREAK);
691                 break;
692
693         case LFUN_LDOTS:
694                 specialChar(this, bv, InsetSpecialChar::LDOTS);
695                 break;
696
697         case LFUN_HFILL:
698                 bv->hideCursor();
699                 update(bv, false);
700                 insertChar(bv, Paragraph::META_HFILL);
701                 update(bv);
702                 break;
703
704         case LFUN_END_OF_SENTENCE:
705                 specialChar(this, bv, InsetSpecialChar::END_OF_SENTENCE);
706                 break;
707
708         case LFUN_MENU_SEPARATOR:
709                 specialChar(this, bv, InsetSpecialChar::MENU_SEPARATOR);
710                 break;
711
712         case LFUN_MARK_OFF:
713                 bv->beforeChange(this);
714                 update(bv, false);
715                 selection.cursor = cursor;
716                 cmd.message(N_("Mark off"));
717                 break;
718
719         case LFUN_MARK_ON:
720                 bv->beforeChange(this);
721                 selection.mark(true);
722                 update(bv, false);
723                 selection.cursor = cursor;
724                 cmd.message(N_("Mark on"));
725                 break;
726
727         case LFUN_SETMARK:
728                 bv->beforeChange(this);
729                 if (selection.mark()) {
730                         update(bv);
731                         cmd.message(N_("Mark removed"));
732                 } else {
733                         selection.mark(true);
734                         update(bv);
735                         cmd.message(N_("Mark set"));
736                 }
737                 selection.cursor = cursor;
738                 break;
739
740         case LFUN_UPCASE_WORD:
741                 update(bv, false);
742                 changeCase(bv, LyXText::text_uppercase);
743                 if (inset_owner)
744                         bv->updateInset(inset_owner, true);
745                 update(bv);
746                 break;
747
748         case LFUN_LOWCASE_WORD:
749                 update(bv, false);
750                 changeCase(bv, LyXText::text_lowercase);
751                 if (inset_owner)
752                         bv->updateInset(inset_owner, true);
753                 update(bv);
754                 break;
755
756         case LFUN_CAPITALIZE_WORD:
757                 update(bv, false);
758                 changeCase(bv, LyXText::text_capitalization);
759                 if (inset_owner)
760                         bv->updateInset(inset_owner, true);
761                 update(bv);
762                 break;
763
764         case LFUN_TRANSPOSE_CHARS:
765                 update(bv, false);
766                 transposeChars(*bv);
767                 if (inset_owner)
768                         bv->updateInset(inset_owner, true);
769                 update(bv);
770                 break;
771
772         case LFUN_BEGINNINGBUFSEL:
773                 if (inset_owner)
774                         return Inset::UNDISPATCHED;
775                 update(bv, false);
776                 cursorTop(bv);
777                 bv->finishChange(true);
778                 break;
779
780         case LFUN_ENDBUFSEL:
781                 if (inset_owner)
782                         return Inset::UNDISPATCHED;
783                 update(bv, false);
784                 cursorBottom(bv);
785                 bv->finishChange(true);
786                 break;
787
788         case LFUN_GETXY:
789                 cmd.message(tostr(cursor.x()) + ' ' + tostr(cursor.y()));
790                 break;
791
792         case LFUN_SETXY: {
793                 int x = 0;
794                 int y = 0;
795                 istringstream is(cmd.argument.c_str());
796                 is >> x >> y;
797                 if (!is)
798                         lyxerr << "SETXY: Could not parse coordinates in '"
799                                << cmd.argument << std::endl;
800                 else 
801                         setCursorFromCoordinates(bv, x, y);
802                 break;
803         }
804
805         case LFUN_GETFONT:
806                 if (current_font.shape() == LyXFont::ITALIC_SHAPE)
807                         cmd.message("E");
808                 else if (current_font.shape() == LyXFont::SMALLCAPS_SHAPE)
809                         cmd.message("N");
810                 else
811                         cmd.message("0");
812                 break;
813
814         case LFUN_GETLAYOUT:
815                 cmd.message(tostr(cursor.par()->layout()));
816                 break;
817
818         case LFUN_LAYOUT: {
819                 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
820                   << cmd.argument << endl;
821
822                 // This is not the good solution to the empty argument
823                 // problem, but it will hopefully suffice for 1.2.0.
824                 // The correct solution would be to augument the
825                 // function list/array with information about what
826                 // functions needs arguments and their type.
827                 if (cmd.argument.empty()) {
828                         cmd.errorMessage(_("LyX function 'layout' needs an argument."));
829                         break;
830                 }
831
832                 // Derive layout number from given argument (string)
833                 // and current buffer's textclass (number)
834                 LyXTextClass const & tclass = bv->buffer()->params.getLyXTextClass();
835                 bool hasLayout = tclass.hasLayout(cmd.argument);
836                 string layout = cmd.argument;
837
838                 // If the entry is obsolete, use the new one instead.
839                 if (hasLayout) {
840                         string const & obs = tclass[layout]->obsoleted_by();
841                         if (!obs.empty())
842                                 layout = obs;
843                 }
844
845                 if (!hasLayout) {
846                         cmd.errorMessage(string(N_("Layout ")) + cmd.argument +
847                                 N_(" not known"));
848                         break;
849                 }
850
851                 bool change_layout = (current_layout != layout);
852                 if (!change_layout && selection.set() &&
853                         selection.start.par() != selection.end.par())
854                 {
855                         Paragraph * spar = selection.start.par();
856                         Paragraph * epar = selection.end.par()->next();
857                         while (spar != epar) {
858                                 if (spar->layout()->name() != current_layout) {
859                                         change_layout = true;
860                                         break;
861                                 }
862                         }
863                 }
864                 if (change_layout) {
865                         bv->hideCursor();
866                         current_layout = layout;
867                         update(bv, false);
868                         setLayout(bv, layout);
869                         bv->owner()->setLayout(layout);
870                         update(bv);
871                         bv->switchKeyMap();
872                 }
873                 break;
874         }
875
876         case LFUN_PASTESELECTION: {
877                 if (!bv->buffer())
878                         break;
879                 bv->hideCursor();
880                 // this was originally a beforeChange(bv->text), i.e
881                 // the outermost LyXText!
882                 bv->beforeChange(this);
883                 string const clip(bv->workarea().getClipboard());
884                 if (!clip.empty()) {
885                         if (cmd.argument == "paragraph")
886                                 insertStringAsParagraphs(bv, clip);
887                         else
888                                 insertStringAsLines(bv, clip);
889                         clearSelection();
890                         update(bv);
891                 }
892                 break;
893         }
894
895         case LFUN_SELFINSERT: {
896                 if (cmd.argument.empty())
897                         break;
898
899                 // Automatically delete the currently selected
900                 // text and replace it with what is being
901                 // typed in now. Depends on lyxrc settings
902                 // "auto_region_delete", which defaults to
903                 // true (on).
904
905                 if (lyxrc.auto_region_delete) {
906                         if (selection.set()) {
907                                 cutSelection(bv, false, false);
908                                 update(bv);
909                         }
910                         bv->workarea().haveSelection(false);
911                 }
912
913                 bv->beforeChange(this);
914                 LyXFont const old_font(real_current_font);
915
916                 string::const_iterator cit = cmd.argument.begin();
917                 string::const_iterator end = cmd.argument.end();
918                 for (; cit != end; ++cit)
919                         bv->owner()->getIntl().getTransManager().
920                                 TranslateAndInsert(*cit, this);
921
922                 update(bv);
923                 selection.cursor = cursor;
924                 bv->moveCursorUpdate(false);
925
926                 // real_current_font.number can change so we need to
927                 // update the minibuffer
928                 if (old_font != real_current_font)
929                         bv->owner()->view_state_changed();
930                 break;
931         }
932
933         default:
934                 return Inset::UNDISPATCHED;
935         }
936
937         return Inset::DISPATCHED;
938 }