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