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