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