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