]> git.lyx.org Git - lyx.git/blob - src/text3.C
e59ac3bd0a981b52b6740d7822ba5a68ad60f713
[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                         update(bv, false);
489                         cutSelection(bv, true);
490                         update(bv);
491                 }
492                 bv->moveCursorUpdate(false);
493                 bv->owner()->view_state_changed();
494                 bv->switchKeyMap();
495                 break;
496
497         case LFUN_DELETE_SKIP:
498                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
499                 if (!selection.set()) {
500                         LyXCursor cur = cursor;
501                         if (cur.pos() == cur.par()->size()) {
502                                 cursorRight(bv);
503                                 cur = cursor;
504                                 if (cur.pos() == 0
505                                     && !(cur.par()->params().spaceTop()
506                                          == VSpace (VSpace::NONE))) {
507                                         setParagraph(bv,
508                                                  cur.par()->params().lineTop(),
509                                                  cur.par()->params().lineBottom(),
510                                                  cur.par()->params().pagebreakTop(),
511                                                  cur.par()->params().pagebreakBottom(),
512                                                  VSpace(VSpace::NONE),
513                                                  cur.par()->params().spaceBottom(),
514                                                  cur.par()->params().spacing(),
515                                                  cur.par()->params().align(),
516                                                  cur.par()->params().labelWidthString(), 0);
517                                         cursorLeft(bv);
518                                         update(bv);
519                                 } else {
520                                         cursorLeft(bv);
521                                         Delete(bv);
522                                         selection.cursor = cursor;
523                                 }
524                         } else {
525                                 Delete(bv);
526                                 selection.cursor = cursor;
527                         }
528                 } else {
529                         update(bv, false);
530                         cutSelection(bv, true);
531                 }
532                 update(bv);
533                 break;
534
535
536         case LFUN_BACKSPACE:
537                 if (!selection.set()) {
538                         if (bv->owner()->getIntl().getTransManager().backspace()) {
539                                 backspace(bv);
540                                 selection.cursor = cursor;
541                                 update(bv);
542                                 // It is possible to make it a lot faster still
543                                 // just comment out the line below...
544                                 bv->showCursor();
545                         }
546                 } else {
547                         update(bv, false);
548                         cutSelection(bv, true);
549                         update(bv);
550                 }
551                 bv->owner()->view_state_changed();
552                 bv->switchKeyMap();
553                 break;
554
555         case LFUN_BACKSPACE_SKIP:
556                 // Reverse the effect of LFUN_BREAKPARAGRAPH_SKIP.
557                 if (!selection.set()) {
558                         LyXCursor cur = cursor;
559                         if (cur.pos() == 0
560                             && !(cur.par()->params().spaceTop()
561                                  == VSpace (VSpace::NONE))) {
562                                 setParagraph(bv,
563                                          cur.par()->params().lineTop(),
564                                          cur.par()->params().lineBottom(),
565                                          cur.par()->params().pagebreakTop(),
566                                          cur.par()->params().pagebreakBottom(),
567                                          VSpace(VSpace::NONE), cur.par()->params().spaceBottom(),
568                                          cur.par()->params().spacing(),
569                                          cur.par()->params().align(),
570                                          cur.par()->params().labelWidthString(), 0);
571                         } else {
572                                 backspace(bv);
573                                 selection.cursor = cur;
574                         }
575                 } else {
576                         update(bv, false);
577                         cutSelection(bv, true);
578                 }
579                 update(bv);
580                 break;
581
582         case LFUN_BREAKPARAGRAPH:
583                 bv->beforeChange(this);
584                 breakParagraph(bv, 0);
585                 update(bv);
586                 selection.cursor = cursor;
587                 bv->switchKeyMap();
588                 bv->owner()->view_state_changed();
589                 break;
590
591         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
592                 bv->beforeChange(this);
593                 breakParagraph(bv, 1);
594                 update(bv);
595                 selection.cursor = cursor;
596                 bv->switchKeyMap();
597                 bv->owner()->view_state_changed();
598                 break;
599
600         case LFUN_BREAKPARAGRAPH_SKIP: {
601                 // When at the beginning of a paragraph, remove
602                 // indentation and add a "defskip" at the top.
603                 // Otherwise, do the same as LFUN_BREAKPARAGRAPH.
604                 LyXCursor cur = cursor;
605                 bv->beforeChange(this);
606                 if (cur.pos() == 0) {
607                         if (cur.par()->params().spaceTop() == VSpace(VSpace::NONE)) {
608                                 setParagraph(bv,
609                                          cur.par()->params().lineTop(),
610                                          cur.par()->params().lineBottom(),
611                                          cur.par()->params().pagebreakTop(),
612                                          cur.par()->params().pagebreakBottom(),
613                                          VSpace(VSpace::DEFSKIP), cur.par()->params().spaceBottom(),
614                                          cur.par()->params().spacing(),
615                                          cur.par()->params().align(),
616                                          cur.par()->params().labelWidthString(), 1);
617                                 //update(bv);
618                         }
619                 }
620                 else {
621                         breakParagraph(bv, 0);
622                         //update(bv);
623                 }
624                 update(bv);
625                 selection.cursor = cur;
626                 bv->switchKeyMap();
627                 bv->owner()->view_state_changed();
628                 break;
629         }
630
631         case LFUN_PARAGRAPH_SPACING: {
632                 Paragraph * par = cursor.par();
633                 Spacing::Space cur_spacing = par->params().spacing().getSpace();
634                 float cur_value = 1.0;
635                 if (cur_spacing == Spacing::Other)
636                         cur_value = par->params().spacing().getValue();
637
638                 istringstream is(cmd.argument.c_str());
639                 string tmp;
640                 is >> tmp;
641                 Spacing::Space new_spacing = cur_spacing;
642                 float new_value = cur_value;
643                 if (tmp.empty()) {
644                         lyxerr << "Missing argument to `paragraph-spacing'"
645                                << endl;
646                 } else if (tmp == "single") {
647                         new_spacing = Spacing::Single;
648                 } else if (tmp == "onehalf") {
649                         new_spacing = Spacing::Onehalf;
650                 } else if (tmp == "double") {
651                         new_spacing = Spacing::Double;
652                 } else if (tmp == "other") {
653                         new_spacing = Spacing::Other;
654                         float tmpval = 0.0;
655                         is >> tmpval;
656                         lyxerr << "new_value = " << tmpval << endl;
657                         if (tmpval != 0.0)
658                                 new_value = tmpval;
659                 } else if (tmp == "default") {
660                         new_spacing = Spacing::Default;
661                 } else {
662                         lyxerr << _("Unknown spacing argument: ")
663                                << cmd.argument << endl;
664                 }
665                 if (cur_spacing != new_spacing || cur_value != new_value) {
666                         par->params().spacing(Spacing(new_spacing, new_value));
667                         redoParagraph(bv);
668                         update(bv);
669                 }
670                 break;
671         }
672
673         case LFUN_INSET_TOGGLE:
674                 bv->hideCursor();
675                 bv->beforeChange(this);
676                 update(bv, false);
677                 toggleInset(bv);
678                 update(bv, false);
679                 bv->switchKeyMap();
680                 break;
681
682         case LFUN_PROTECTEDSPACE:
683                 if (cursor.par()->layout()->free_spacing) {
684                         insertChar(bv, ' ');
685                         update(bv);
686                 } else {
687                         specialChar(this, bv, InsetSpecialChar::PROTECTED_SEPARATOR);
688                 }
689                 bv->moveCursorUpdate(false);
690                 break;
691
692         case LFUN_HYPHENATION:
693                 specialChar(this, bv, InsetSpecialChar::HYPHENATION);
694                 break;
695
696         case LFUN_LIGATURE_BREAK:
697                 specialChar(this, bv, InsetSpecialChar::LIGATURE_BREAK);
698                 break;
699
700         case LFUN_LDOTS:
701                 specialChar(this, bv, InsetSpecialChar::LDOTS);
702                 break;
703
704         case LFUN_HFILL:
705                 bv->hideCursor();
706                 update(bv, false);
707                 insertChar(bv, Paragraph::META_HFILL);
708                 update(bv);
709                 break;
710
711         case LFUN_END_OF_SENTENCE:
712                 specialChar(this, bv, InsetSpecialChar::END_OF_SENTENCE);
713                 break;
714
715         case LFUN_MENU_SEPARATOR:
716                 specialChar(this, bv, InsetSpecialChar::MENU_SEPARATOR);
717                 break;
718
719         case LFUN_MARK_OFF:
720                 bv->beforeChange(this);
721                 update(bv, false);
722                 selection.cursor = cursor;
723                 cmd.message(N_("Mark off"));
724                 break;
725
726         case LFUN_MARK_ON:
727                 bv->beforeChange(this);
728                 selection.mark(true);
729                 update(bv, false);
730                 selection.cursor = cursor;
731                 cmd.message(N_("Mark on"));
732                 break;
733
734         case LFUN_SETMARK:
735                 bv->beforeChange(this);
736                 if (selection.mark()) {
737                         update(bv);
738                         cmd.message(N_("Mark removed"));
739                 } else {
740                         selection.mark(true);
741                         update(bv);
742                         cmd.message(N_("Mark set"));
743                 }
744                 selection.cursor = cursor;
745                 break;
746
747         case LFUN_UPCASE_WORD:
748                 update(bv, false);
749                 changeCase(bv, LyXText::text_uppercase);
750                 if (inset_owner)
751                         bv->updateInset(inset_owner, true);
752                 update(bv);
753                 break;
754
755         case LFUN_LOWCASE_WORD:
756                 update(bv, false);
757                 changeCase(bv, LyXText::text_lowercase);
758                 if (inset_owner)
759                         bv->updateInset(inset_owner, true);
760                 update(bv);
761                 break;
762
763         case LFUN_CAPITALIZE_WORD:
764                 update(bv, false);
765                 changeCase(bv, LyXText::text_capitalization);
766                 if (inset_owner)
767                         bv->updateInset(inset_owner, true);
768                 update(bv);
769                 break;
770
771         case LFUN_TRANSPOSE_CHARS:
772                 update(bv, false);
773                 transposeChars(*bv);
774                 if (inset_owner)
775                         bv->updateInset(inset_owner, true);
776                 update(bv);
777                 break;
778
779         case LFUN_PASTE:
780                 cmd.message(_("Paste"));
781                 bv->hideCursor();
782                 // clear the selection
783                 bv->toggleSelection();
784                 clearSelection();
785                 update(bv, false);
786                 pasteSelection(bv);
787                 clearSelection(); // bug 393
788                 update(bv, false);
789                 update(bv);
790                 bv->switchKeyMap();
791                 break;
792
793         case LFUN_CUT:
794                 bv->hideCursor();
795                 update(bv, false);
796                 cutSelection(bv, true);
797                 update(bv);
798                 cmd.message(_("Cut"));
799                 break;
800
801         case LFUN_COPY:
802                 copySelection(bv);
803                 cmd.message(_("Copy"));
804                 break;
805
806         case LFUN_BEGINNINGBUFSEL:
807                 if (inset_owner)
808                         return Inset::UNDISPATCHED;
809                 update(bv, false);
810                 cursorTop(bv);
811                 bv->finishChange(true);
812                 break;
813
814         case LFUN_ENDBUFSEL:
815                 if (inset_owner)
816                         return Inset::UNDISPATCHED;
817                 update(bv, false);
818                 cursorBottom(bv);
819                 bv->finishChange(true);
820                 break;
821
822         case LFUN_GETXY:
823                 cmd.message(tostr(cursor.x()) + ' ' + tostr(cursor.y()));
824                 break;
825
826         case LFUN_SETXY: {
827                 int x = 0;
828                 int y = 0;
829                 istringstream is(cmd.argument.c_str());
830                 is >> x >> y;
831                 if (!is)
832                         lyxerr << "SETXY: Could not parse coordinates in '"
833                                << cmd.argument << std::endl;
834                 else 
835                         setCursorFromCoordinates(bv, x, y);
836                 break;
837         }
838
839         case LFUN_GETFONT:
840                 if (current_font.shape() == LyXFont::ITALIC_SHAPE)
841                         cmd.message("E");
842                 else if (current_font.shape() == LyXFont::SMALLCAPS_SHAPE)
843                         cmd.message("N");
844                 else
845                         cmd.message("0");
846                 break;
847
848         case LFUN_GETLAYOUT:
849                 cmd.message(tostr(cursor.par()->layout()));
850                 break;
851
852         case LFUN_LAYOUT: {
853                 lyxerr[Debug::INFO] << "LFUN_LAYOUT: (arg) "
854                   << cmd.argument << endl;
855
856                 // This is not the good solution to the empty argument
857                 // problem, but it will hopefully suffice for 1.2.0.
858                 // The correct solution would be to augument the
859                 // function list/array with information about what
860                 // functions needs arguments and their type.
861                 if (cmd.argument.empty()) {
862                         cmd.errorMessage(_("LyX function 'layout' needs an argument."));
863                         break;
864                 }
865
866                 // Derive layout number from given argument (string)
867                 // and current buffer's textclass (number)
868                 LyXTextClass const & tclass = bv->buffer()->params.getLyXTextClass();
869                 bool hasLayout = tclass.hasLayout(cmd.argument);
870                 string layout = cmd.argument;
871
872                 // If the entry is obsolete, use the new one instead.
873                 if (hasLayout) {
874                         string const & obs = tclass[layout]->obsoleted_by();
875                         if (!obs.empty())
876                                 layout = obs;
877                 }
878
879                 if (!hasLayout) {
880                         cmd.errorMessage(string(N_("Layout ")) + cmd.argument +
881                                 N_(" not known"));
882                         break;
883                 }
884
885                 bool change_layout = (current_layout != layout);
886                 if (!change_layout && selection.set() &&
887                         selection.start.par() != selection.end.par())
888                 {
889                         Paragraph * spar = selection.start.par();
890                         Paragraph * epar = selection.end.par()->next();
891                         while (spar != epar) {
892                                 if (spar->layout()->name() != current_layout) {
893                                         change_layout = true;
894                                         break;
895                                 }
896                         }
897                 }
898                 if (change_layout) {
899                         bv->hideCursor();
900                         current_layout = layout;
901                         update(bv, false);
902                         setLayout(bv, layout);
903                         bv->owner()->setLayout(layout);
904                         update(bv);
905                         bv->switchKeyMap();
906                 }
907                 break;
908         }
909
910         case LFUN_PASTESELECTION: {
911                 if (!bv->buffer())
912                         break;
913                 bv->hideCursor();
914                 // this was originally a beforeChange(bv->text), i.e
915                 // the outermost LyXText!
916                 bv->beforeChange(this);
917                 string const clip(bv->workarea().getClipboard());
918                 if (!clip.empty()) {
919                         if (cmd.argument == "paragraph")
920                                 insertStringAsParagraphs(bv, clip);
921                         else
922                                 insertStringAsLines(bv, clip);
923                         clearSelection();
924                         update(bv);
925                 }
926                 break;
927         }
928
929         case LFUN_SELFINSERT: {
930                 if (cmd.argument.empty())
931                         break;
932
933                 // Automatically delete the currently selected
934                 // text and replace it with what is being
935                 // typed in now. Depends on lyxrc settings
936                 // "auto_region_delete", which defaults to
937                 // true (on).
938
939                 if (lyxrc.auto_region_delete) {
940                         if (selection.set()) {
941                                 cutSelection(bv, false, false);
942                                 update(bv);
943                         }
944                         bv->workarea().haveSelection(false);
945                 }
946
947                 bv->beforeChange(this);
948                 LyXFont const old_font(real_current_font);
949
950                 string::const_iterator cit = cmd.argument.begin();
951                 string::const_iterator end = cmd.argument.end();
952                 for (; cit != end; ++cit)
953                         bv->owner()->getIntl().getTransManager().
954                                 TranslateAndInsert(*cit, this);
955
956                 update(bv);
957                 selection.cursor = cursor;
958                 bv->moveCursorUpdate(false);
959
960                 // real_current_font.number can change so we need to
961                 // update the minibuffer
962                 if (old_font != real_current_font)
963                         bv->owner()->view_state_changed();
964                 break;
965         }
966
967         default:
968                 return Inset::UNDISPATCHED;
969         }
970
971         return Inset::DISPATCHED;
972 }