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