]> git.lyx.org Git - lyx.git/blob - src/text2.C
Work to improve performance a bit
[lyx.git] / src / text2.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include "lyxtext.h"
14 #include "LString.h"
15 #include "Lsstream.h"
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
20 #include "buffer.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
24 #include "gettext.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
29 #include "debug.h"
30 #include "lyxrc.h"
31 #include "lyxrow.h"
32 #include "FloatList.h"
33 #include "language.h"
34 #include "ParagraphParameters.h"
35 #include "counters.h"
36 #include "lyxrow_funcs.h"
37 #include "metricsinfo.h"
38 #include "paragraph_funcs.h"
39
40 #include "insets/insetbibitem.h"
41 #include "insets/insetenv.h"
42 #include "insets/insetfloat.h"
43 #include "insets/insetwrap.h"
44
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
48
49 #include <boost/tuple/tuple.hpp>
50
51 #include <algorithm>
52
53 using namespace lyx::support;
54
55 using std::vector;
56 using std::copy;
57 using std::endl;
58 using std::find;
59 using std::pair;
60 using lyx::pos_type;
61
62
63 LyXText::LyXText(BufferView * bv)
64         : height(0), width(0), anchor_row_offset_(0),
65           inset_owner(0), the_locking_inset(0), bv_owner(bv)
66 {
67         anchor_row_ = rows().end();
68         need_break_row = rows().end();
69         need_refresh_ = true;
70 }
71
72
73 LyXText::LyXText(BufferView * bv, InsetText * inset)
74         : height(0), width(0), anchor_row_offset_(0),
75           inset_owner(inset), the_locking_inset(0), bv_owner(bv)
76 {
77         anchor_row_ = rows().end();
78         need_break_row = rows().end();
79         need_refresh_ = true;
80 }
81
82
83 void LyXText::init(BufferView * bview)
84 {
85         bv_owner = bview;
86
87         rowlist_.clear();
88         need_break_row = rows().end();
89         width = height = 0;
90         need_refresh_ = true;
91
92         anchor_row_ = rows().end();
93         anchor_row_offset_ = 0;
94
95         ParagraphList::iterator pit = ownerParagraphs().begin();
96         ParagraphList::iterator end = ownerParagraphs().end();
97
98         current_font = getFont(bview->buffer(), pit, 0);
99
100         for (; pit != end; ++pit)
101                 insertParagraph(pit, rowlist_.end());
102
103         setCursorIntern(rowlist_.begin()->par(), 0);
104         selection.cursor = cursor;
105
106         updateCounters();
107 }
108
109
110 // Gets the fully instantiated font at a given position in a paragraph
111 // Basically the same routine as Paragraph::getFont() in paragraph.C.
112 // The difference is that this one is used for displaying, and thus we
113 // are allowed to make cosmetic improvements. For instance make footnotes
114 // smaller. (Asger)
115 // If position is -1, we get the layout font of the paragraph.
116 // If position is -2, we get the font of the manual label of the paragraph.
117 LyXFont const LyXText::getFont(Buffer const * buf, ParagraphList::iterator pit,
118                                pos_type pos) const
119 {
120         Assert(pos >= 0);
121
122         LyXLayout_ptr const & layout = pit->layout();
123
124         // We specialize the 95% common case:
125         if (!pit->getDepth()) {
126                 if (layout->labeltype == LABEL_MANUAL
127                     && pos < pit->beginningOfBody()) {
128                         // 1% goes here
129                         LyXFont f = pit->getFontSettings(buf->params, pos);
130                         if (pit->inInset())
131                                 pit->inInset()->getDrawFont(f);
132                         return f.realize(layout->reslabelfont);
133                 } else {
134                         LyXFont f = pit->getFontSettings(buf->params, pos);
135                         if (pit->inInset())
136                                 pit->inInset()->getDrawFont(f);
137                         return f.realize(layout->resfont);
138                 }
139         }
140
141         // The uncommon case need not be optimized as much
142
143         LyXFont layoutfont;
144
145         if (pos < pit->beginningOfBody()) {
146                 // 1% goes here
147                 layoutfont = layout->labelfont;
148         } else {
149                 // 99% goes here
150                 layoutfont = layout->font;
151         }
152
153         LyXFont tmpfont = pit->getFontSettings(buf->params, pos);
154         tmpfont.realize(layoutfont);
155
156         if (pit->inInset())
157                 pit->inInset()->getDrawFont(tmpfont);
158
159         // Realize with the fonts of lesser depth.
160         tmpfont.realize(outerFont(pit, ownerParagraphs()));
161
162         return realizeFont(tmpfont, buf->params);
163 }
164
165
166 LyXFont const LyXText::getLayoutFont(Buffer const * buf,
167                                      ParagraphList::iterator pit) const
168 {
169         LyXLayout_ptr const & layout = pit->layout();
170
171         if (!pit->getDepth()) {
172                 return layout->resfont;
173         }
174
175         LyXFont font(layout->font);
176         // Realize with the fonts of lesser depth.
177         font.realize(outerFont(pit, ownerParagraphs()));
178
179         return realizeFont(font, buf->params);
180 }
181
182
183 LyXFont const LyXText::getLabelFont(Buffer const * buf,
184                                     ParagraphList::iterator pit) const
185 {
186         LyXLayout_ptr const & layout = pit->layout();
187
188         if (!pit->getDepth()) {
189                 return layout->reslabelfont;
190         }
191
192         LyXFont font(layout->labelfont);
193         // Realize with the fonts of lesser depth.
194         font.realize(outerFont(pit, ownerParagraphs()));
195
196         return realizeFont(layout->labelfont, buf->params);
197 }
198
199
200 void LyXText::setCharFont(ParagraphList::iterator pit,
201                           pos_type pos, LyXFont const & fnt,
202                           bool toggleall)
203 {
204         Buffer const * buf = bv()->buffer();
205         LyXFont font = getFont(buf, pit, pos);
206         font.update(fnt, buf->params.language, toggleall);
207         // Let the insets convert their font
208         if (pit->isInset(pos)) {
209                 InsetOld * inset = pit->getInset(pos);
210                 if (isEditableInset(inset)) {
211                         UpdatableInset * uinset =
212                                 static_cast<UpdatableInset *>(inset);
213                         uinset->setFont(bv(), fnt, toggleall, true);
214                 }
215         }
216
217         // Plug thru to version below:
218         setCharFont(buf, pit, pos, font);
219 }
220
221
222 void LyXText::setCharFont(Buffer const * buf, ParagraphList::iterator pit,
223                           pos_type pos, LyXFont const & fnt)
224 {
225         LyXFont font(fnt);
226
227         LyXTextClass const & tclass = buf->params.getLyXTextClass();
228         LyXLayout_ptr const & layout = pit->layout();
229
230         // Get concrete layout font to reduce against
231         LyXFont layoutfont;
232
233         if (pos < pit->beginningOfBody())
234                 layoutfont = layout->labelfont;
235         else
236                 layoutfont = layout->font;
237
238         // Realize against environment font information
239         if (pit->getDepth()) {
240                 ParagraphList::iterator tp = pit;
241                 while (!layoutfont.resolved() &&
242                        tp != ownerParagraphs().end() &&
243                        tp->getDepth()) {
244                         tp = outerHook(tp, ownerParagraphs());
245                         if (tp != ownerParagraphs().end())
246                                 layoutfont.realize(tp->layout()->font);
247                 }
248         }
249
250         layoutfont.realize(tclass.defaultfont());
251
252         // Now, reduce font against full layout font
253         font.reduce(layoutfont);
254
255         pit->setFont(pos, font);
256 }
257
258
259 // removes the row and reset the touched counters
260 void LyXText::removeRow(RowList::iterator rit)
261 {
262         if (anchor_row_ == rit) {
263                 if (rit != rows().begin()) {
264                         anchor_row_ = boost::prior(rit);
265                         anchor_row_offset_ += anchor_row_->height();
266                 } else {
267                         anchor_row_ = boost::next(rit);
268                         anchor_row_offset_ -= rit->height();
269                 }
270         }
271
272         // the text becomes smaller
273         height -= rit->height();
274
275         rowlist_.erase(rit);
276 }
277
278
279 // remove all following rows of the paragraph of the specified row.
280 void LyXText::removeParagraph(RowList::iterator rit)
281 {
282         ParagraphList::iterator tmppit = rit->par();
283         ++rit;
284
285         while (rit != rows().end() && rit->par() == tmppit) {
286                 RowList::iterator tmprit = boost::next(rit);
287                 removeRow(rit);
288                 rit = tmprit;
289         }
290 }
291
292
293 void LyXText::insertParagraph(ParagraphList::iterator pit,
294                               RowList::iterator rowit)
295 {
296         // insert a new row, starting at position 0
297         Row newrow(pit, 0);
298         RowList::iterator rit = rowlist_.insert(rowit, newrow);
299
300         // and now append the whole paragraph before the new row
301         appendParagraph(rit);
302 }
303
304
305 InsetOld * LyXText::getInset() const
306 {
307         ParagraphList::iterator pit = cursor.par();
308         pos_type const pos = cursor.pos();
309
310         if (pos < pit->size() && pit->isInset(pos)) {
311                 return pit->getInset(pos);
312         }
313         return 0;
314 }
315
316
317 void LyXText::toggleInset()
318 {
319         InsetOld * inset = getInset();
320         // is there an editable inset at cursor position?
321         if (!isEditableInset(inset)) {
322                 // No, try to see if we are inside a collapsable inset
323                 if (inset_owner && inset_owner->owner()
324                     && inset_owner->owner()->isOpen()) {
325                         bv()->unlockInset(inset_owner->owner());
326                         inset_owner->owner()->close(bv());
327                         bv()->getLyXText()->cursorRight(bv());
328                 }
329                 return;
330         }
331         //bv()->owner()->message(inset->editMessage());
332
333         // do we want to keep this?? (JMarc)
334         if (!isHighlyEditableInset(inset))
335                 recordUndo(bv(), Undo::ATOMIC);
336
337         if (inset->isOpen()) {
338                 inset->close(bv());
339         } else {
340                 inset->open(bv());
341         }
342
343         bv()->updateInset(inset);
344 }
345
346
347 /* used in setlayout */
348 // Asger is not sure we want to do this...
349 void LyXText::makeFontEntriesLayoutSpecific(Buffer const & buf,
350                                             Paragraph & par)
351 {
352         LyXLayout_ptr const & layout = par.layout();
353         pos_type const psize = par.size();
354
355         LyXFont layoutfont;
356         for (pos_type pos = 0; pos < psize; ++pos) {
357                 if (pos < par.beginningOfBody())
358                         layoutfont = layout->labelfont;
359                 else
360                         layoutfont = layout->font;
361
362                 LyXFont tmpfont = par.getFontSettings(buf.params, pos);
363                 tmpfont.reduce(layoutfont);
364                 par.setFont(pos, tmpfont);
365         }
366 }
367
368
369 ParagraphList::iterator
370 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
371                    LyXCursor & send_cur,
372                    string const & layout)
373 {
374         ParagraphList::iterator endpit = boost::next(send_cur.par());
375         ParagraphList::iterator undoendpit = endpit;
376         ParagraphList::iterator pars_end = ownerParagraphs().end();
377
378         if (endpit != pars_end && endpit->getDepth()) {
379                 while (endpit != pars_end && endpit->getDepth()) {
380                         ++endpit;
381                         undoendpit = endpit;
382                 }
383         } else if (endpit != pars_end) {
384                 // because of parindents etc.
385                 ++endpit;
386         }
387
388         recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
389
390         // ok we have a selection. This is always between sstart_cur
391         // and sel_end cursor
392         cur = sstart_cur;
393         ParagraphList::iterator pit = sstart_cur.par();
394         ParagraphList::iterator epit = boost::next(send_cur.par());
395
396         LyXLayout_ptr const & lyxlayout =
397                 bv()->buffer()->params.getLyXTextClass()[layout];
398
399         do {
400                 pit->applyLayout(lyxlayout);
401                 makeFontEntriesLayoutSpecific(*bv()->buffer(), *pit);
402                 ParagraphList::iterator fppit = pit;
403                 fppit->params().spaceTop(lyxlayout->fill_top ?
404                                          VSpace(VSpace::VFILL)
405                                          : VSpace(VSpace::NONE));
406                 fppit->params().spaceBottom(lyxlayout->fill_bottom ?
407                                             VSpace(VSpace::VFILL)
408                                             : VSpace(VSpace::NONE));
409                 if (lyxlayout->margintype == MARGIN_MANUAL)
410                         pit->setLabelWidthString(lyxlayout->labelstring());
411                 cur.par(pit);
412                 ++pit;
413         } while (pit != epit);
414
415         return endpit;
416 }
417
418
419 // set layout over selection and make a total rebreak of those paragraphs
420 void LyXText::setLayout(string const & layout)
421 {
422         LyXCursor tmpcursor = cursor;  // store the current cursor
423
424         // if there is no selection just set the layout
425         // of the current paragraph
426         if (!selection.set()) {
427                 selection.start = cursor;  // dummy selection
428                 selection.end = cursor;
429         }
430
431         // special handling of new environment insets
432         BufferParams const & params = bv()->buffer()->params;
433         LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
434         if (lyxlayout->is_environment) {
435                 // move everything in a new environment inset
436                 lyxerr << "setting layout " << layout << endl;
437                 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
438                 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
439                 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
440                 InsetOld * inset = new InsetEnvironment(params, layout);
441                 if (bv()->insertInset(inset)) {
442                         //inset->edit(bv());
443                         //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
444                 }
445                 else
446                         delete inset;
447                 return;
448         }
449
450         ParagraphList::iterator endpit = setLayout(cursor, selection.start,
451                                                    selection.end, layout);
452         redoParagraphs(selection.start, endpit);
453
454         // we have to reset the selection, because the
455         // geometry could have changed
456         setCursor(selection.start.par(), selection.start.pos(), false);
457         selection.cursor = cursor;
458         setCursor(selection.end.par(), selection.end.pos(), false);
459         updateCounters();
460         clearSelection();
461         setSelection();
462         setCursor(tmpcursor.par(), tmpcursor.pos(), true);
463 }
464
465
466 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
467 {
468         ParagraphList::iterator pit(cursor.par());
469         ParagraphList::iterator end(cursor.par());
470         ParagraphList::iterator start = pit;
471
472         if (selection.set()) {
473                 pit = selection.start.par();
474                 end = selection.end.par();
475                 start = pit;
476         }
477
478         ParagraphList::iterator pastend = boost::next(end);
479
480         if (!test_only)
481                 recordUndo(bv(), Undo::ATOMIC, start, end);
482
483         bool changed = false;
484
485         int prev_after_depth = 0;
486 #warning parlist ... could be nicer ?
487         if (start != ownerParagraphs().begin()) {
488                 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
489         }
490
491         while (true) {
492                 int const depth = pit->params().depth();
493                 if (type == bv_funcs::INC_DEPTH) {
494                         if (depth < prev_after_depth
495                             && pit->layout()->labeltype != LABEL_BIBLIO) {
496                                 changed = true;
497                                 if (!test_only) {
498                                         pit->params().depth(depth + 1);
499                                 }
500
501                         }
502                 } else if (depth) {
503                         changed = true;
504                         if (!test_only)
505                                 pit->params().depth(depth - 1);
506                 }
507
508                 prev_after_depth = pit->getMaxDepthAfter();
509
510                 if (pit == end) {
511                         break;
512                 }
513
514                 ++pit;
515         }
516
517         if (test_only)
518                 return changed;
519
520         // Wow, redoParagraphs is stupid.
521         LyXCursor tmpcursor;
522         setCursor(tmpcursor, start, 0);
523
524         redoParagraphs(tmpcursor, pastend);
525
526         // We need to actually move the text->cursor. I don't
527         // understand why ...
528         tmpcursor = cursor;
529
530         // we have to reset the visual selection because the
531         // geometry could have changed
532         if (selection.set()) {
533                 setCursor(selection.start.par(), selection.start.pos());
534                 selection.cursor = cursor;
535                 setCursor(selection.end.par(), selection.end.pos());
536         }
537
538         // this handles the counter labels, and also fixes up
539         // depth values for follow-on (child) paragraphs
540         updateCounters();
541
542         setSelection();
543         setCursor(tmpcursor.par(), tmpcursor.pos());
544
545         return changed;
546 }
547
548
549 // set font over selection and make a total rebreak of those paragraphs
550 void LyXText::setFont(LyXFont const & font, bool toggleall)
551 {
552         // if there is no selection just set the current_font
553         if (!selection.set()) {
554                 // Determine basis font
555                 LyXFont layoutfont;
556                 if (cursor.pos() < cursor.par()->beginningOfBody()) {
557                         layoutfont = getLabelFont(bv()->buffer(),
558                                                   cursor.par());
559                 } else {
560                         layoutfont = getLayoutFont(bv()->buffer(),
561                                                    cursor.par());
562                 }
563                 // Update current font
564                 real_current_font.update(font,
565                                          bv()->buffer()->params.language,
566                                          toggleall);
567
568                 // Reduce to implicit settings
569                 current_font = real_current_font;
570                 current_font.reduce(layoutfont);
571                 // And resolve it completely
572                 real_current_font.realize(layoutfont);
573
574                 return;
575         }
576
577         LyXCursor tmpcursor = cursor; // store the current cursor
578
579         // ok we have a selection. This is always between sel_start_cursor
580         // and sel_end cursor
581
582         recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
583         freezeUndo();
584         cursor = selection.start;
585         while (cursor.par() != selection.end.par() ||
586                cursor.pos() < selection.end.pos())
587         {
588                 if (cursor.pos() < cursor.par()->size()) {
589                         // an open footnote should behave like a closed one
590                         setCharFont(cursor.par(), cursor.pos(),
591                                     font, toggleall);
592                         cursor.pos(cursor.pos() + 1);
593                 } else {
594                         cursor.pos(0);
595                         cursor.par(boost::next(cursor.par()));
596                 }
597         }
598         unFreezeUndo();
599
600         redoParagraphs(selection.start, boost::next(selection.end.par()));
601
602         // we have to reset the selection, because the
603         // geometry could have changed, but we keep
604         // it for user convenience
605         setCursor(selection.start.par(), selection.start.pos());
606         selection.cursor = cursor;
607         setCursor(selection.end.par(), selection.end.pos());
608         setSelection();
609         setCursor(tmpcursor.par(), tmpcursor.pos(), true,
610                   tmpcursor.boundary());
611 }
612
613
614 void LyXText::redoHeightOfParagraph()
615 {
616         RowList::iterator tmprow = cursorRow();
617
618         setHeightOfRow(tmprow);
619
620         while (tmprow != rows().begin()
621                && boost::prior(tmprow)->par() == tmprow->par()) {
622                 --tmprow;
623                 setHeightOfRow(tmprow);
624         }
625
626         postPaint();
627
628         setCursor(cursor.par(), cursor.pos(), false, cursor.boundary());
629 }
630
631
632 // deletes and inserts again all paragraphs between the cursor
633 // and the specified par
634 // This function is needed after SetLayout and SetFont etc.
635 void LyXText::redoParagraphs(LyXCursor const & cur,
636                              ParagraphList::iterator endpit)
637 {
638         RowList::iterator tmprit = getRow(cur);
639
640         ParagraphList::iterator first_phys_pit;
641         RowList::iterator prevrit;
642         if (tmprit == rows().begin()) {
643                 // A trick/hack for UNDO.
644                 // This is needed because in an UNDO/REDO we could have
645                 // changed the ownerParagraph() so the paragraph inside
646                 // the row is NOT my really first par anymore.
647                 // Got it Lars ;) (Jug 20011206)
648                 first_phys_pit = ownerParagraphs().begin();
649                 prevrit = rows().end();
650         } else {
651                 first_phys_pit = tmprit->par();
652                 while (tmprit != rows().begin()
653                        && boost::prior(tmprit)->par() == first_phys_pit)
654                 {
655                         --tmprit;
656                 }
657                 prevrit = boost::prior(tmprit);
658         }
659
660         // remove it
661         while (tmprit != rows().end() && tmprit->par() != endpit) {
662                 RowList::iterator tmprit2 = tmprit++;
663                 removeRow(tmprit2);
664         }
665
666         // Reinsert the paragraphs.
667         ParagraphList::iterator tmppit = first_phys_pit;
668
669         while (tmppit != ownerParagraphs().end()) {
670                 insertParagraph(tmppit, tmprit);
671                 while (tmprit != rows().end()
672                        && tmprit->par() == tmppit) {
673                         ++tmprit;
674                 }
675                 ++tmppit;
676                 if (tmppit == endpit)
677                         break;
678         }
679         if (prevrit != rows().end())
680                 setHeightOfRow(prevrit);
681         else
682                 setHeightOfRow(rows().begin());
683         postPaint();
684         if (tmprit != rows().end())
685                 setHeightOfRow(tmprit);
686
687         updateCounters();
688 }
689
690
691 void LyXText::fullRebreak()
692 {
693         init(bv());
694         setCursorIntern(cursor.par(), cursor.pos());
695 }
696
697
698 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
699 {
700         //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << "\n";
701
702         // rebuild row cache
703         rowlist_.clear();
704         need_break_row = rows().end();
705         width = height = 0;
706
707         anchor_row_ = rows().end();
708         anchor_row_offset_ = 0;
709
710         ParagraphList::iterator pit = ownerParagraphs().begin();
711         ParagraphList::iterator end = ownerParagraphs().end();
712
713         for (; pit != end; ++pit) {
714                 InsetList::iterator ii = pit->insetlist.begin();
715                 InsetList::iterator iend = pit->insetlist.end();
716                 for (; ii != iend; ++ii) {
717                         Dimension dim;
718                         MetricsInfo m = mi;
719                         ii->inset->metrics(m, dim);
720                 }
721
722                 // insert a new row, starting at position 0
723                 Row newrow(pit, 0);
724                 RowList::iterator rit = rowlist_.insert(rowlist_.end(), newrow);
725
726                 // and now append the whole paragraph before the new row
727                 appendParagraph(rit);
728         }
729
730         // compute height
731         //lyxerr << "height 0: " << height << "\n";
732         //for (RowList::iterator rit = rows().begin(); rit != rows().end(); ++rit) {
733         //      height += rit->height();
734         //}
735         //lyxerr << "height 1: " << height << "\n";
736
737         // final dimension
738         dim.asc = rows().begin()->ascent_of_text();
739         dim.des = height - dim.asc;
740         dim.wid = std::max(mi.base.textwidth, int(width));
741 }
742
743
744 void LyXText::partialRebreak()
745 {
746         if (rows().empty()) {
747                 init(bv());
748                 return;
749         }
750
751         RowList::iterator rows_end = rows().end();
752
753         if (need_break_row != rows_end) {
754                 breakAgain(need_break_row);
755                 need_break_row = rows_end;
756                 return;
757         }
758 }
759
760
761 // important for the screen
762
763
764 // the cursor set functions have a special mechanism. When they
765 // realize, that you left an empty paragraph, they will delete it.
766 // They also delete the corresponding row
767
768 // need the selection cursor:
769 void LyXText::setSelection()
770 {
771         bool const lsel = TextCursor::setSelection();
772
773         if (inset_owner && (selection.set() || lsel))
774                 inset_owner->setUpdateStatus(InsetText::SELECTION);
775 }
776
777
778
779 void LyXText::clearSelection()
780 {
781         TextCursor::clearSelection();
782
783         // reset this in the bv_owner!
784         if (bv_owner && bv_owner->text)
785                 bv_owner->text->xsel_cache.set(false);
786 }
787
788
789 void LyXText::cursorHome()
790 {
791         setCursor(cursor.par(), cursorRow()->pos());
792 }
793
794
795 void LyXText::cursorEnd()
796 {
797         if (cursor.par()->empty())
798                 return;
799
800         RowList::iterator rit = cursorRow();
801         RowList::iterator next_rit = boost::next(rit);
802         ParagraphList::iterator pit = rit->par();
803         pos_type last_pos = lastPos(*this, rit);
804
805         if (next_rit == rows().end() || next_rit->par() != pit) {
806                 ++last_pos;
807         } else {
808                 if (pit->empty() ||
809                     (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
810                         ++last_pos;
811                 }
812         }
813
814         setCursor(pit, last_pos);
815 }
816
817
818 void LyXText::cursorTop()
819 {
820         setCursor(ownerParagraphs().begin(), 0);
821 }
822
823
824 void LyXText::cursorBottom()
825 {
826         ParagraphList::iterator lastpit =
827                 boost::prior(ownerParagraphs().end());
828         setCursor(lastpit, lastpit->size());
829 }
830
831
832 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
833 {
834         // If the mask is completely neutral, tell user
835         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
836                 // Could only happen with user style
837                 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
838                 return;
839         }
840
841         // Try implicit word selection
842         // If there is a change in the language the implicit word selection
843         // is disabled.
844         LyXCursor resetCursor = cursor;
845         bool implicitSelection = (font.language() == ignore_language
846                                   && font.number() == LyXFont::IGNORE)
847                 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
848
849         // Set font
850         setFont(font, toggleall);
851
852         // Implicit selections are cleared afterwards
853         //and cursor is set to the original position.
854         if (implicitSelection) {
855                 clearSelection();
856                 cursor = resetCursor;
857                 setCursor(cursor.par(), cursor.pos());
858                 selection.cursor = cursor;
859         }
860         if (inset_owner)
861                 inset_owner->setUpdateStatus(InsetText::CURSOR_PAR);
862 }
863
864
865 string LyXText::getStringToIndex()
866 {
867         // Try implicit word selection
868         // If there is a change in the language the implicit word selection
869         // is disabled.
870         LyXCursor const reset_cursor = cursor;
871         bool const implicitSelection =
872                 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
873
874         string idxstring;
875         if (!selection.set())
876                 bv()->owner()->message(_("Nothing to index!"));
877         else if (selection.start.par() != selection.end.par())
878                 bv()->owner()->message(_("Cannot index more than one paragraph!"));
879         else
880                 idxstring = selectionAsString(bv()->buffer(), false);
881
882         // Reset cursors to their original position.
883         cursor = reset_cursor;
884         setCursor(cursor.par(), cursor.pos());
885         selection.cursor = cursor;
886
887         // Clear the implicit selection.
888         if (implicitSelection)
889                 clearSelection();
890
891         return idxstring;
892 }
893
894
895 // the DTP switches for paragraphs. LyX will store them in the first
896 // physicla paragraph. When a paragraph is broken, the top settings rest,
897 // the bottom settings are given to the new one. So I can make shure,
898 // they do not duplicate themself and you cannnot make dirty things with
899 // them!
900
901 void LyXText::setParagraph(bool line_top, bool line_bottom,
902                            bool pagebreak_top, bool pagebreak_bottom,
903                            VSpace const & space_top,
904                            VSpace const & space_bottom,
905                            Spacing const & spacing,
906                            LyXAlignment align,
907                            string const & labelwidthstring,
908                            bool noindent)
909 {
910         LyXCursor tmpcursor = cursor;
911         if (!selection.set()) {
912                 selection.start = cursor;
913                 selection.end = cursor;
914         }
915
916         // make sure that the depth behind the selection are restored, too
917         ParagraphList::iterator endpit = boost::next(selection.end.par());
918         ParagraphList::iterator undoendpit = endpit;
919         ParagraphList::iterator pars_end = ownerParagraphs().end();
920
921         if (endpit != pars_end && endpit->getDepth()) {
922                 while (endpit != pars_end && endpit->getDepth()) {
923                         ++endpit;
924                         undoendpit = endpit;
925                 }
926         } else if (endpit != pars_end) {
927                 // because of parindents etc.
928                 ++endpit;
929         }
930
931         recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
932                 boost::prior(undoendpit));
933
934
935         ParagraphList::iterator tmppit = selection.end.par();
936
937         while (tmppit != boost::prior(selection.start.par())) {
938                 setCursor(tmppit, 0);
939
940                 ParagraphList::iterator pit = cursor.par();
941                 ParagraphParameters & params = pit->params();
942
943                 params.lineTop(line_top);
944                 params.lineBottom(line_bottom);
945                 params.pagebreakTop(pagebreak_top);
946                 params.pagebreakBottom(pagebreak_bottom);
947                 params.spaceTop(space_top);
948                 params.spaceBottom(space_bottom);
949                 params.spacing(spacing);
950                 // does the layout allow the new alignment?
951                 LyXLayout_ptr const & layout = pit->layout();
952
953                 if (align == LYX_ALIGN_LAYOUT)
954                         align = layout->align;
955                 if (align & layout->alignpossible) {
956                         if (align == layout->align)
957                                 params.align(LYX_ALIGN_LAYOUT);
958                         else
959                                 params.align(align);
960                 }
961                 pit->setLabelWidthString(labelwidthstring);
962                 params.noindent(noindent);
963                 tmppit = boost::prior(pit);
964         }
965         postPaint();
966
967         redoParagraphs(selection.start, endpit);
968
969         clearSelection();
970         setCursor(selection.start.par(), selection.start.pos());
971         selection.cursor = cursor;
972         setCursor(selection.end.par(), selection.end.pos());
973         setSelection();
974         setCursor(tmpcursor.par(), tmpcursor.pos());
975         if (inset_owner)
976                 bv()->updateInset(inset_owner);
977 }
978
979
980 // set the counter of a paragraph. This includes the labels
981 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
982 {
983         LyXTextClass const & textclass = buf->params.getLyXTextClass();
984         LyXLayout_ptr const & layout = pit->layout();
985
986         if (pit != ownerParagraphs().begin()) {
987
988                 pit->params().appendix(boost::prior(pit)->params().appendix());
989                 if (!pit->params().appendix() &&
990                     pit->params().startOfAppendix()) {
991                         pit->params().appendix(true);
992                         textclass.counters().reset();
993                 }
994                 pit->enumdepth = boost::prior(pit)->enumdepth;
995                 pit->itemdepth = boost::prior(pit)->itemdepth;
996         } else {
997                 pit->params().appendix(pit->params().startOfAppendix());
998                 pit->enumdepth = 0;
999                 pit->itemdepth = 0;
1000         }
1001
1002         /* Maybe we have to increment the enumeration depth.
1003          * BUT, enumeration in a footnote is considered in isolation from its
1004          *      surrounding paragraph so don't increment if this is the
1005          *      first line of the footnote
1006          * AND, bibliographies can't have their depth changed ie. they
1007          *      are always of depth 0
1008          */
1009         if (pit != ownerParagraphs().begin()
1010             && boost::prior(pit)->getDepth() < pit->getDepth()
1011             && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
1012             && pit->enumdepth < 3
1013             && layout->labeltype != LABEL_BIBLIO) {
1014                 pit->enumdepth++;
1015         }
1016
1017         // Maybe we have to decrement the enumeration depth, see note above
1018         if (pit != ownerParagraphs().begin()
1019             && boost::prior(pit)->getDepth() > pit->getDepth()
1020             && layout->labeltype != LABEL_BIBLIO) {
1021                 pit->enumdepth = depthHook(pit, ownerParagraphs(),
1022                                            pit->getDepth())->enumdepth;
1023         }
1024
1025         if (!pit->params().labelString().empty()) {
1026                 pit->params().labelString(string());
1027         }
1028
1029         if (layout->margintype == MARGIN_MANUAL) {
1030                 if (pit->params().labelWidthString().empty()) {
1031                         pit->setLabelWidthString(layout->labelstring());
1032                 }
1033         } else {
1034                 pit->setLabelWidthString(string());
1035         }
1036
1037         // is it a layout that has an automatic label?
1038         if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1039                 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1040
1041                 ostringstream s;
1042
1043                 if (i >= 0 && i <= buf->params.secnumdepth) {
1044                         string numbertype;
1045                         string langtype;
1046
1047                         textclass.counters().step(layout->latexname());
1048
1049                         // Is there a label? Useful for Chapter layout
1050                         if (!pit->params().appendix()) {
1051                                 s << buf->B_(layout->labelstring());
1052                         } else {
1053                                 s << buf->B_(layout->labelstring_appendix());
1054                         }
1055
1056                         // Use of an integer is here less than elegant. For now.
1057                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1058                         if (!pit->params().appendix()) {
1059                                 numbertype = "sectioning";
1060                         } else {
1061                                 numbertype = "appendix";
1062                                 if (pit->isRightToLeftPar(buf->params))
1063                                         langtype = "hebrew";
1064                                 else
1065                                         langtype = "latin";
1066                         }
1067
1068                         s << " "
1069                           << textclass.counters()
1070                                 .numberLabel(layout->latexname(),
1071                                              numbertype, langtype, head);
1072
1073                         pit->params().labelString(STRCONV(s.str()));
1074
1075                         // reset enum counters
1076                         textclass.counters().reset("enum");
1077                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1078                         textclass.counters().reset("enum");
1079                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1080                         // FIXME
1081                         // Yes I know this is a really, really! bad solution
1082                         // (Lgb)
1083                         string enumcounter("enum");
1084
1085                         switch (pit->enumdepth) {
1086                         case 2:
1087                                 enumcounter += 'i';
1088                         case 1:
1089                                 enumcounter += 'i';
1090                         case 0:
1091                                 enumcounter += 'i';
1092                                 break;
1093                         case 3:
1094                                 enumcounter += "iv";
1095                                 break;
1096                         default:
1097                                 // not a valid enumdepth...
1098                                 break;
1099                         }
1100
1101                         textclass.counters().step(enumcounter);
1102
1103                         s << textclass.counters()
1104                                 .numberLabel(enumcounter, "enumeration");
1105                         pit->params().labelString(STRCONV(s.str()));
1106                 }
1107         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1108                 textclass.counters().step("bibitem");
1109                 int number = textclass.counters().value("bibitem");
1110                 if (pit->bibitem()) {
1111                         pit->bibitem()->setCounter(number);
1112                         pit->params().labelString(layout->labelstring());
1113                 }
1114                 // In biblio should't be following counters but...
1115         } else {
1116                 string s = buf->B_(layout->labelstring());
1117
1118                 // the caption hack:
1119                 if (layout->labeltype == LABEL_SENSITIVE) {
1120                         ParagraphList::iterator tmppit = pit;
1121                         InsetOld * in = 0;
1122                         bool isOK = false;
1123                         while (tmppit != ownerParagraphs().end() &&
1124                                tmppit->inInset()
1125                                // the single '=' is intended below
1126                                && (in = tmppit->inInset()->owner())) {
1127                                 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1128                                     in->lyxCode() == InsetOld::WRAP_CODE) {
1129                                         isOK = true;
1130                                         break;
1131                                 } else {
1132                                         tmppit = std::find(ownerParagraphs().begin(), ownerParagraphs().end(), *in->parOwner());
1133                                 }
1134                         }
1135
1136                         if (isOK) {
1137                                 string type;
1138
1139                                 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1140                                         type = static_cast<InsetFloat*>(in)->params().type;
1141                                 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1142                                         type = static_cast<InsetWrap*>(in)->params().type;
1143                                 else
1144                                         Assert(0);
1145
1146                                 Floating const & fl = textclass.floats().getType(type);
1147
1148                                 textclass.counters().step(fl.type());
1149
1150                                 // Doesn't work... yet.
1151                                 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1152                         } else {
1153                                 // par->SetLayout(0);
1154                                 // s = layout->labelstring;
1155                                 s = _("Senseless: ");
1156                         }
1157                 }
1158                 pit->params().labelString(s);
1159
1160                 // reset the enumeration counter. They are always reset
1161                 // when there is any other layout between
1162                 // Just fall-through between the cases so that all
1163                 // enum counters deeper than enumdepth is also reset.
1164                 switch (pit->enumdepth) {
1165                 case 0:
1166                         textclass.counters().reset("enumi");
1167                 case 1:
1168                         textclass.counters().reset("enumii");
1169                 case 2:
1170                         textclass.counters().reset("enumiii");
1171                 case 3:
1172                         textclass.counters().reset("enumiv");
1173                 }
1174         }
1175 }
1176
1177
1178 // Updates all counters. Paragraphs with changed label string will be rebroken
1179 void LyXText::updateCounters()
1180 {
1181         RowList::iterator rowit = rows().begin();
1182         ParagraphList::iterator pit = rowit->par();
1183
1184         // CHECK if this is really needed. (Lgb)
1185         bv()->buffer()->params.getLyXTextClass().counters().reset();
1186
1187         ParagraphList::iterator beg = ownerParagraphs().begin();
1188         ParagraphList::iterator end = ownerParagraphs().end();
1189         for (; pit != end; ++pit) {
1190                 while (rowit->par() != pit)
1191                         ++rowit;
1192
1193                 string const oldLabel = pit->params().labelString();
1194
1195                 size_t maxdepth = 0;
1196                 if (pit != beg)
1197                         maxdepth = boost::prior(pit)->getMaxDepthAfter();
1198
1199                 if (pit->params().depth() > maxdepth)
1200                         pit->params().depth(maxdepth);
1201
1202                 // setCounter can potentially change the labelString.
1203                 setCounter(bv()->buffer(), pit);
1204
1205                 string const & newLabel = pit->params().labelString();
1206
1207                 if (oldLabel.empty() && !newLabel.empty()) {
1208                         removeParagraph(rowit);
1209                         appendParagraph(rowit);
1210                 }
1211         }
1212 }
1213
1214
1215 void LyXText::insertInset(InsetOld * inset)
1216 {
1217         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1218                 return;
1219         recordUndo(bv(), Undo::ATOMIC, cursor.par());
1220         freezeUndo();
1221         cursor.par()->insertInset(cursor.pos(), inset);
1222         // Just to rebreak and refresh correctly.
1223         // The character will not be inserted a second time
1224         insertChar(Paragraph::META_INSET);
1225         // If we enter a highly editable inset the cursor should be to before
1226         // the inset. This couldn't happen before as Undo was not handled inside
1227         // inset now after the Undo LyX tries to call inset->Edit(...) again
1228         // and cannot do this as the cursor is behind the inset and GetInset
1229         // does not return the inset!
1230         if (isHighlyEditableInset(inset)) {
1231                 cursorLeft(true);
1232         }
1233         unFreezeUndo();
1234 }
1235
1236
1237 void LyXText::cutSelection(bool doclear, bool realcut)
1238 {
1239         // Stuff what we got on the clipboard. Even if there is no selection.
1240
1241         // There is a problem with having the stuffing here in that the
1242         // larger the selection the slower LyX will get. This can be
1243         // solved by running the line below only when the selection has
1244         // finished. The solution used currently just works, to make it
1245         // faster we need to be more clever and probably also have more
1246         // calls to stuffClipboard. (Lgb)
1247         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1248
1249         // This doesn't make sense, if there is no selection
1250         if (!selection.set())
1251                 return;
1252
1253         // OK, we have a selection. This is always between selection.start
1254         // and selection.end
1255
1256         // make sure that the depth behind the selection are restored, too
1257         ParagraphList::iterator endpit = boost::next(selection.end.par());
1258         ParagraphList::iterator undoendpit = endpit;
1259         ParagraphList::iterator pars_end = ownerParagraphs().end();
1260
1261         if (endpit != pars_end && endpit->getDepth()) {
1262                 while (endpit != pars_end && endpit->getDepth()) {
1263                         ++endpit;
1264                         undoendpit = endpit;
1265                 }
1266         } else if (endpit != pars_end) {
1267                 // because of parindents etc.
1268                 ++endpit;
1269         }
1270
1271         recordUndo(bv(), Undo::DELETE, selection.start.par(),
1272                 boost::prior(undoendpit));
1273
1274
1275         endpit = selection.end.par();
1276         int endpos = selection.end.pos();
1277
1278         boost::tie(endpit, endpos) = realcut ?
1279                 CutAndPaste::cutSelection(bv()->buffer()->params,
1280                                           ownerParagraphs(),
1281                                           selection.start.par(), endpit,
1282                                           selection.start.pos(), endpos,
1283                                           bv()->buffer()->params.textclass,
1284                                           doclear)
1285                 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1286                                               ownerParagraphs(),
1287                                               selection.start.par(), endpit,
1288                                               selection.start.pos(), endpos,
1289                                               doclear);
1290         // sometimes necessary
1291         if (doclear)
1292                 selection.start.par()->stripLeadingSpaces();
1293
1294         redoParagraphs(selection.start, boost::next(endpit));
1295 #warning FIXME latent bug
1296         // endpit will be invalidated on redoParagraphs once ParagraphList
1297         // becomes a std::list? There are maybe other places on which this
1298         // can happend? (Ab)
1299         // cutSelection can invalidate the cursor so we need to set
1300         // it anew. (Lgb)
1301         // we prefer the end for when tracking changes
1302         cursor.pos(endpos);
1303         cursor.par(endpit);
1304
1305         // need a valid cursor. (Lgb)
1306         clearSelection();
1307
1308         setCursor(cursor.par(), cursor.pos());
1309         selection.cursor = cursor;
1310         updateCounters();
1311 }
1312
1313
1314 void LyXText::copySelection()
1315 {
1316         // stuff the selection onto the X clipboard, from an explicit copy request
1317         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1318
1319         // this doesnt make sense, if there is no selection
1320         if (!selection.set())
1321                 return;
1322
1323         // ok we have a selection. This is always between selection.start
1324         // and sel_end cursor
1325
1326         // copy behind a space if there is one
1327         while (selection.start.par()->size() > selection.start.pos()
1328                && selection.start.par()->isLineSeparator(selection.start.pos())
1329                && (selection.start.par() != selection.end.par()
1330                    || selection.start.pos() < selection.end.pos()))
1331                 selection.start.pos(selection.start.pos() + 1);
1332
1333         CutAndPaste::copySelection(selection.start.par(),
1334                                    selection.end.par(),
1335                                    selection.start.pos(), selection.end.pos(),
1336                                    bv()->buffer()->params.textclass);
1337 }
1338
1339
1340 void LyXText::pasteSelection(size_t sel_index)
1341 {
1342         // this does not make sense, if there is nothing to paste
1343         if (!CutAndPaste::checkPastePossible())
1344                 return;
1345
1346         recordUndo(bv(), Undo::INSERT, cursor.par());
1347
1348         ParagraphList::iterator endpit;
1349         PitPosPair ppp;
1350
1351         ErrorList el;
1352
1353         boost::tie(ppp, endpit) =
1354                 CutAndPaste::pasteSelection(*bv()->buffer(),
1355                                             ownerParagraphs(),
1356                                             cursor.par(), cursor.pos(),
1357                                             bv()->buffer()->params.textclass,
1358                                             sel_index, el);
1359         bufferErrors(*bv()->buffer(), el);
1360         bv()->showErrorList(_("Paste"));
1361
1362         redoParagraphs(cursor, endpit);
1363
1364         setCursor(cursor.par(), cursor.pos());
1365         clearSelection();
1366
1367         selection.cursor = cursor;
1368         setCursor(ppp.first, ppp.second);
1369         setSelection();
1370         updateCounters();
1371 }
1372
1373
1374 void LyXText::setSelectionRange(lyx::pos_type length)
1375 {
1376         if (!length)
1377                 return;
1378
1379         selection.cursor = cursor;
1380         while (length--)
1381                 cursorRight(bv());
1382         setSelection();
1383 }
1384
1385
1386 // simple replacing. The font of the first selected character is used
1387 void LyXText::replaceSelectionWithString(string const & str)
1388 {
1389         recordUndo(bv(), Undo::ATOMIC);
1390         freezeUndo();
1391
1392         if (!selection.set()) { // create a dummy selection
1393                 selection.end = cursor;
1394                 selection.start = cursor;
1395         }
1396
1397         // Get font setting before we cut
1398         pos_type pos = selection.end.pos();
1399         LyXFont const font = selection.start.par()
1400                 ->getFontSettings(bv()->buffer()->params,
1401                                   selection.start.pos());
1402
1403         // Insert the new string
1404         string::const_iterator cit = str.begin();
1405         string::const_iterator end = str.end();
1406         for (; cit != end; ++cit) {
1407                 selection.end.par()->insertChar(pos, (*cit), font);
1408                 ++pos;
1409         }
1410
1411         // Cut the selection
1412         cutSelection(true, false);
1413
1414         unFreezeUndo();
1415 }
1416
1417
1418 // needed to insert the selection
1419 void LyXText::insertStringAsLines(string const & str)
1420 {
1421         ParagraphList::iterator pit = cursor.par();
1422         pos_type pos = cursor.pos();
1423         ParagraphList::iterator endpit = boost::next(cursor.par());
1424
1425         recordUndo(bv(), Undo::ATOMIC);
1426
1427         // only to be sure, should not be neccessary
1428         clearSelection();
1429
1430         bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1431
1432         redoParagraphs(cursor, endpit);
1433         setCursor(cursor.par(), cursor.pos());
1434         selection.cursor = cursor;
1435         setCursor(pit, pos);
1436         setSelection();
1437 }
1438
1439
1440 // turns double-CR to single CR, others where converted into one
1441 // blank. Then InsertStringAsLines is called
1442 void LyXText::insertStringAsParagraphs(string const & str)
1443 {
1444         string linestr(str);
1445         bool newline_inserted = false;
1446         string::size_type const siz = linestr.length();
1447
1448         for (string::size_type i = 0; i < siz; ++i) {
1449                 if (linestr[i] == '\n') {
1450                         if (newline_inserted) {
1451                                 // we know that \r will be ignored by
1452                                 // InsertStringA. Of course, it is a dirty
1453                                 // trick, but it works...
1454                                 linestr[i - 1] = '\r';
1455                                 linestr[i] = '\n';
1456                         } else {
1457                                 linestr[i] = ' ';
1458                                 newline_inserted = true;
1459                         }
1460                 } else if (IsPrintable(linestr[i])) {
1461                         newline_inserted = false;
1462                 }
1463         }
1464         insertStringAsLines(linestr);
1465 }
1466
1467
1468 void LyXText::checkParagraph(ParagraphList::iterator pit, pos_type pos)
1469 {
1470         LyXCursor tmpcursor;
1471
1472         pos_type z;
1473         RowList::iterator row = getRow(pit, pos);
1474         RowList::iterator beg = rows().begin();
1475
1476         // is there a break one row above
1477         if (row != beg && boost::prior(row)->par() == row->par()) {
1478                 z = rowBreakPoint(*boost::prior(row));
1479                 if (z >= row->pos()) {
1480                         // set the dimensions of the row above
1481                         postPaint();
1482
1483                         breakAgain(boost::prior(row));
1484
1485                         // set the cursor again. Otherwise
1486                         // dangling pointers are possible
1487                         setCursor(cursor.par(), cursor.pos(),
1488                                   false, cursor.boundary());
1489                         selection.cursor = cursor;
1490                         return;
1491                 }
1492         }
1493
1494         breakAgain(row);
1495         postPaint();
1496
1497         // set the cursor again. Otherwise dangling pointers are possible
1498         // also set the selection
1499
1500         if (selection.set()) {
1501                 tmpcursor = cursor;
1502                 setCursorIntern(selection.cursor.par(), selection.cursor.pos(),
1503                                 false, selection.cursor.boundary());
1504                 selection.cursor = cursor;
1505                 setCursorIntern(selection.start.par(),
1506                                 selection.start.pos(),
1507                                 false, selection.start.boundary());
1508                 selection.start = cursor;
1509                 setCursorIntern(selection.end.par(),
1510                                 selection.end.pos(),
1511                                 false, selection.end.boundary());
1512                 selection.end = cursor;
1513                 setCursorIntern(last_sel_cursor.par(),
1514                                 last_sel_cursor.pos(),
1515                                 false, last_sel_cursor.boundary());
1516                 last_sel_cursor = cursor;
1517                 cursor = tmpcursor;
1518         }
1519         setCursorIntern(cursor.par(), cursor.pos(),
1520                         false, cursor.boundary());
1521 }
1522
1523
1524 // returns false if inset wasn't found
1525 bool LyXText::updateInset(InsetOld * inset)
1526 {
1527         // first check the current paragraph
1528         int pos = cursor.par()->getPositionOfInset(inset);
1529         if (pos != -1) {
1530                 checkParagraph(cursor.par(), pos);
1531                 return true;
1532         }
1533
1534         // check every paragraph
1535
1536         ParagraphList::iterator par = ownerParagraphs().begin();
1537         ParagraphList::iterator end = ownerParagraphs().end();
1538         for (; par != end; ++par) {
1539                 pos = par->getPositionOfInset(inset);
1540                 if (pos != -1) {
1541                         checkParagraph(par, pos);
1542                         return true;
1543                 }
1544         };
1545
1546         return false;
1547 }
1548
1549
1550 bool LyXText::setCursor(ParagraphList::iterator pit,
1551                         pos_type pos,
1552                         bool setfont, bool boundary)
1553 {
1554         LyXCursor old_cursor = cursor;
1555         setCursorIntern(pit, pos, setfont, boundary);
1556         return deleteEmptyParagraphMechanism(old_cursor);
1557 }
1558
1559
1560 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1561                         pos_type pos, bool boundary)
1562 {
1563         Assert(pit != ownerParagraphs().end());
1564
1565         cur.par(pit);
1566         cur.pos(pos);
1567         cur.boundary(boundary);
1568         if (rows().empty())
1569                 return;
1570
1571         // get the cursor y position in text
1572         int y = 0;
1573         RowList::iterator row = getRow(pit, pos, y);
1574         RowList::iterator beg = rows().begin();
1575
1576         RowList::iterator old_row = row;
1577         // if we are before the first char of this row and are still in the
1578         // same paragraph and there is a previous row then put the cursor on
1579         // the end of the previous row
1580         cur.iy(y + row->baseline());
1581         if (row != beg &&
1582             pos &&
1583             boost::prior(row)->par() == row->par() &&
1584             pos < pit->size() &&
1585             pit->getChar(pos) == Paragraph::META_INSET) {
1586                 InsetOld * ins = pit->getInset(pos);
1587                 if (ins && (ins->needFullRow() || ins->display())) {
1588                         --row;
1589                         y -= row->height();
1590                 }
1591         }
1592
1593         // y is now the beginning of the cursor row
1594         y += row->baseline();
1595         // y is now the cursor baseline
1596         cur.y(y);
1597
1598         pos_type last = lastPrintablePos(*this, old_row);
1599
1600         // None of these should happen, but we're scaredy-cats
1601         if (pos > pit->size()) {
1602                 lyxerr << "dont like 1 please report" << endl;
1603                 pos = 0;
1604                 cur.pos(0);
1605         } else if (pos > last + 1) {
1606                 lyxerr << "dont like 2 please report" << endl;
1607                 // This shouldn't happen.
1608                 pos = last + 1;
1609                 cur.pos(pos);
1610         } else if (pos < row->pos()) {
1611                 lyxerr << "dont like 3 please report" << endl;
1612                 pos = row->pos();
1613                 cur.pos(pos);
1614         }
1615
1616         // now get the cursors x position
1617         float x = getCursorX(row, pos, last, boundary);
1618         cur.x(int(x));
1619         cur.x_fix(cur.x());
1620         if (old_row != row) {
1621                 x = getCursorX(old_row, pos, last, boundary);
1622                 cur.ix(int(x));
1623         } else
1624                 cur.ix(cur.x());
1625 /* We take out this for the time being because 1) the redraw code is not
1626    prepared to this yet and 2) because some good policy has yet to be decided
1627    while editting: for instance how to act on rows being created/deleted
1628    because of DEPM.
1629 */
1630 #if 0
1631         //if the cursor is in a visible row, anchor to it
1632         int topy = top_y();
1633         if (topy < y && y < topy + bv()->workHeight())
1634                 anchor_row(row);
1635 #endif
1636 }
1637
1638
1639 float LyXText::getCursorX(RowList::iterator rit,
1640                           pos_type pos, pos_type last, bool boundary) const
1641 {
1642         pos_type cursor_vpos = 0;
1643         int x;
1644         int fill_separator;
1645         int fill_hfill;
1646         int fill_label_hfill;
1647         // This call HAS to be here because of the BidiTables!!!
1648         prepareToPrint(rit, x, fill_separator, fill_hfill,
1649                        fill_label_hfill);
1650
1651         ParagraphList::iterator rit_par = rit->par();
1652         pos_type const rit_pos = rit->pos();
1653
1654         if (last < rit_pos)
1655                 cursor_vpos = rit_pos;
1656         else if (pos > last && !boundary)
1657                 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1658                         ? rit_pos : last + 1;
1659         else if (pos > rit_pos && (pos > last || boundary))
1660                 /// Place cursor after char at (logical) position pos - 1
1661                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1662                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1663         else
1664                 /// Place cursor before char at (logical) position pos
1665                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1666                         ? log2vis(pos) : log2vis(pos) + 1;
1667
1668         pos_type body_pos = rit_par->beginningOfBody();
1669         if ((body_pos > 0) &&
1670             ((body_pos - 1 > last) || !rit_par->isLineSeparator(body_pos - 1)))
1671                 body_pos = 0;
1672
1673         for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1674                 pos_type pos = vis2log(vpos);
1675                 if (body_pos > 0 && pos == body_pos - 1) {
1676                         x += fill_label_hfill +
1677                                 font_metrics::width(
1678                                         rit_par->layout()->labelsep,
1679                                         getLabelFont(bv()->buffer(), rit_par));
1680                         if (rit_par->isLineSeparator(body_pos - 1))
1681                                 x -= singleWidth(rit_par, body_pos - 1);
1682                 }
1683
1684                 if (hfillExpansion(*this, rit, pos)) {
1685                         x += singleWidth(rit_par, pos);
1686                         if (pos >= body_pos)
1687                                 x += fill_hfill;
1688                         else
1689                                 x += fill_label_hfill;
1690                 } else if (rit_par->isSeparator(pos)) {
1691                         x += singleWidth(rit_par, pos);
1692                         if (pos >= body_pos)
1693                                 x += fill_separator;
1694                 } else
1695                         x += singleWidth(rit_par, pos);
1696         }
1697         return x;
1698 }
1699
1700
1701 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1702                               pos_type pos, bool setfont, bool boundary)
1703 {
1704         UpdatableInset * it = pit->inInset();
1705         if (it) {
1706                 if (it != inset_owner) {
1707                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1708                                               << endl
1709                                               << "inset_owner is "
1710                                               << inset_owner << endl;
1711 #ifdef WITH_WARNINGS
1712 #warning I believe this code is wrong. (Lgb)
1713 #warning Jürgen, have a look at this. (Lgb)
1714 #warning Hmmm, I guess you are right but we
1715 #warning should verify when this is needed
1716 #endif
1717                         // Jürgen, would you like to have a look?
1718                         // I guess we need to move the outer cursor
1719                         // and open and lock the inset (bla bla bla)
1720                         // stuff I don't know... so can you have a look?
1721                         // (Lgb)
1722                         // I moved the lyxerr stuff in here so we can see if
1723                         // this is actually really needed and where!
1724                         // (Jug)
1725                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1726                         return;
1727                 }
1728         }
1729
1730         setCursor(cursor, pit, pos, boundary);
1731         if (setfont)
1732                 setCurrentFont();
1733 }
1734
1735
1736 void LyXText::setCurrentFont()
1737 {
1738         pos_type pos = cursor.pos();
1739         ParagraphList::iterator pit = cursor.par();
1740
1741         if (cursor.boundary() && pos > 0)
1742                 --pos;
1743
1744         if (pos > 0) {
1745                 if (pos == pit->size())
1746                         --pos;
1747                 else // potentional bug... BUG (Lgb)
1748                         if (pit->isSeparator(pos)) {
1749                                 if (pos > cursorRow()->pos() &&
1750                                     bidi_level(pos) % 2 ==
1751                                     bidi_level(pos - 1) % 2)
1752                                         --pos;
1753                                 else if (pos + 1 < pit->size())
1754                                         ++pos;
1755                         }
1756         }
1757
1758         current_font =
1759                 pit->getFontSettings(bv()->buffer()->params, pos);
1760         real_current_font = getFont(bv()->buffer(), pit, pos);
1761
1762         if (cursor.pos() == pit->size() &&
1763             isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1764             !cursor.boundary()) {
1765                 Language const * lang =
1766                         pit->getParLanguage(bv()->buffer()->params);
1767                 current_font.setLanguage(lang);
1768                 current_font.setNumber(LyXFont::OFF);
1769                 real_current_font.setLanguage(lang);
1770                 real_current_font.setNumber(LyXFont::OFF);
1771         }
1772 }
1773
1774
1775 // returns the column near the specified x-coordinate of the row
1776 // x is set to the real beginning of this column
1777 pos_type
1778 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1779 {
1780         int tmpx = 0;
1781         int fill_separator;
1782         int fill_hfill;
1783         int fill_label_hfill;
1784
1785         prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1786
1787         pos_type vc = rit->pos();
1788         pos_type last = lastPrintablePos(*this, rit);
1789         pos_type c = 0;
1790
1791         ParagraphList::iterator rit_par = rit->par();
1792         LyXLayout_ptr const & layout = rit->par()->layout();
1793
1794         bool left_side = false;
1795
1796         pos_type body_pos = rit_par->beginningOfBody();
1797         int last_tmpx = tmpx;
1798
1799         if (body_pos > 0 &&
1800             (body_pos - 1 > last ||
1801              !rit_par->isLineSeparator(body_pos - 1)))
1802                 body_pos = 0;
1803
1804         // check for empty row
1805         if (!rit_par->size()) {
1806                 x = tmpx;
1807                 return 0;
1808         }
1809
1810         while (vc <= last && tmpx <= x) {
1811                 c = vis2log(vc);
1812                 last_tmpx = tmpx;
1813                 if (body_pos > 0 && c == body_pos - 1) {
1814                         tmpx += fill_label_hfill +
1815                                 font_metrics::width(layout->labelsep,
1816                                                getLabelFont(bv()->buffer(), rit_par));
1817                         if (rit_par->isLineSeparator(body_pos - 1))
1818                                 tmpx -= singleWidth(rit_par, body_pos - 1);
1819                 }
1820
1821                 if (hfillExpansion(*this, rit, c)) {
1822                         tmpx += singleWidth(rit_par, c);
1823                         if (c >= body_pos)
1824                                 tmpx += fill_hfill;
1825                         else
1826                                 tmpx += fill_label_hfill;
1827                 } else if (rit_par->isSeparator(c)) {
1828                         tmpx += singleWidth(rit_par, c);
1829                         if (c >= body_pos)
1830                                 tmpx += fill_separator;
1831                 } else {
1832                         tmpx += singleWidth(rit_par, c);
1833                 }
1834                 ++vc;
1835         }
1836
1837         if ((tmpx + last_tmpx) / 2 > x) {
1838                 tmpx = last_tmpx;
1839                 left_side = true;
1840         }
1841
1842         if (vc > last + 1)  // This shouldn't happen.
1843                 vc = last + 1;
1844
1845         boundary = false;
1846         // This (rtl_support test) is not needed, but gives
1847         // some speedup if rtl_support=false
1848         RowList::iterator next_rit = boost::next(rit);
1849
1850         bool const lastrow = lyxrc.rtl_support &&
1851                 (next_rit == rowlist_.end() ||
1852                  next_rit->par() != rit_par);
1853
1854         // If lastrow is false, we don't need to compute
1855         // the value of rtl.
1856         bool const rtl = (lastrow)
1857                 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1858                 : false;
1859         if (lastrow &&
1860                  ((rtl &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
1861                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
1862                 c = last + 1;
1863         else if (vc == rit->pos()) {
1864                 c = vis2log(vc);
1865                 if (bidi_level(c) % 2 == 1)
1866                         ++c;
1867         } else {
1868                 c = vis2log(vc - 1);
1869                 bool const rtl = (bidi_level(c) % 2 == 1);
1870                 if (left_side == rtl) {
1871                         ++c;
1872                         boundary = isBoundary(bv()->buffer(), *rit_par, c);
1873                 }
1874         }
1875
1876         if (rit->pos() <= last && c > last
1877             && rit_par->isNewline(last)) {
1878                 if (bidi_level(last) % 2 == 0)
1879                         tmpx -= singleWidth(rit_par, last);
1880                 else
1881                         tmpx += singleWidth(rit_par, last);
1882                 c = last;
1883         }
1884
1885         c -= rit->pos();
1886         x = int(tmpx);
1887         return c;
1888 }
1889
1890
1891 void LyXText::setCursorFromCoordinates(int x, int y)
1892 {
1893         //LyXCursor old_cursor = cursor;
1894         setCursorFromCoordinates(cursor, x, y);
1895         setCurrentFont();
1896 #warning DEPM disabled, otherwise crash when entering new table
1897         //deleteEmptyParagraphMechanism(old_cursor);
1898 }
1899
1900
1901 namespace {
1902
1903         /**
1904          * return true if the cursor given is at the end of a row,
1905          * and the next row is filled by an inset that spans an entire
1906          * row.
1907          */
1908         bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1909         {
1910                 RowList::iterator row = lt.getRow(cur);
1911                 if (boost::next(row) == lt.rows().end())
1912                         return false;
1913
1914                 Row const & next = *boost::next(row);
1915
1916                 if (next.pos() != cur.pos() || next.par() != cur.par())
1917                         return false;
1918
1919                 if (cur.pos() == cur.par()->size()
1920                     || !cur.par()->isInset(cur.pos()))
1921                         return false;
1922
1923                 InsetOld const * inset = cur.par()->getInset(cur.pos());
1924                 if (inset->needFullRow() || inset->display())
1925                         return true;
1926
1927                 return false;
1928         }
1929 }
1930
1931
1932 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1933 {
1934         // Get the row first.
1935
1936         RowList::iterator row = getRowNearY(y);
1937         bool bound = false;
1938         pos_type const column = getColumnNearX(row, x, bound);
1939         cur.par(row->par());
1940         cur.pos(row->pos() + column);
1941         cur.x(x);
1942         cur.y(y + row->baseline());
1943
1944         if (beforeFullRowInset(*this, cur)) {
1945                 pos_type const last = lastPrintablePos(*this, row);
1946                 RowList::iterator next_row = boost::next(row);
1947
1948                 float x = getCursorX(next_row, cur.pos(), last, bound);
1949                 cur.ix(int(x));
1950                 cur.iy(y + row->height() + next_row->baseline());
1951         } else {
1952                 cur.iy(cur.y());
1953                 cur.ix(cur.x());
1954         }
1955         cur.boundary(bound);
1956 }
1957
1958
1959 void LyXText::cursorLeft(bool internal)
1960 {
1961         if (cursor.pos() > 0) {
1962                 bool boundary = cursor.boundary();
1963                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1964                 if (!internal && !boundary &&
1965                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1966                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
1967         } else if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph.
1968                 ParagraphList::iterator pit = boost::prior(cursor.par());
1969                 setCursor(pit, pit->size());
1970         }
1971 }
1972
1973
1974 void LyXText::cursorRight(bool internal)
1975 {
1976         bool const at_end = (cursor.pos() == cursor.par()->size());
1977         bool const at_newline = !at_end &&
1978                 cursor.par()->isNewline(cursor.pos());
1979
1980         if (!internal && cursor.boundary() && !at_newline)
1981                 setCursor(cursor.par(), cursor.pos(), true, false);
1982         else if (!at_end) {
1983                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1984                 if (!internal &&
1985                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1986                         setCursor(cursor.par(), cursor.pos(), true, true);
1987         } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1988                 setCursor(boost::next(cursor.par()), 0);
1989 }
1990
1991
1992 void LyXText::cursorUp(bool selecting)
1993 {
1994 #if 1
1995         int x = cursor.x_fix();
1996         int y = cursor.y() - cursorRow()->baseline() - 1;
1997         setCursorFromCoordinates(x, y);
1998         if (!selecting) {
1999                 int topy = top_y();
2000                 int y1 = cursor.iy() - topy;
2001                 int y2 = y1;
2002                 y -= topy;
2003                 InsetOld * inset_hit = checkInsetHit(x, y1);
2004                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2005                         inset_hit->localDispatch(
2006                                 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
2007                 }
2008         }
2009 #else
2010         setCursorFromCoordinates(bv(), cursor.x_fix(),
2011                                  cursor.y() - cursorRow()->baseline() - 1);
2012 #endif
2013 }
2014
2015
2016 void LyXText::cursorDown(bool selecting)
2017 {
2018 #if 1
2019         int x = cursor.x_fix();
2020         int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
2021         setCursorFromCoordinates(x, y);
2022         if (!selecting && cursorRow() == cursorIRow()) {
2023                 int topy = top_y();
2024                 int y1 = cursor.iy() - topy;
2025                 int y2 = y1;
2026                 y -= topy;
2027                 InsetOld * inset_hit = checkInsetHit(x, y1);
2028                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2029                         FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
2030                         inset_hit->localDispatch(cmd);
2031                 }
2032         }
2033 #else
2034         setCursorFromCoordinates(bv(), cursor.x_fix(),
2035                                  cursor.y() - cursorRow()->baseline()
2036                                  + cursorRow()->height() + 1);
2037 #endif
2038 }
2039
2040
2041 void LyXText::cursorUpParagraph()
2042 {
2043         if (cursor.pos() > 0) {
2044                 setCursor(cursor.par(), 0);
2045         }
2046         else if (cursor.par() != ownerParagraphs().begin()) {
2047                 setCursor(boost::prior(cursor.par()), 0);
2048         }
2049 }
2050
2051
2052 void LyXText::cursorDownParagraph()
2053 {
2054         ParagraphList::iterator par = cursor.par();
2055         ParagraphList::iterator next_par = boost::next(par);
2056
2057         if (next_par != ownerParagraphs().end()) {
2058                 setCursor(next_par, 0);
2059         } else {
2060                 setCursor(par, par->size());
2061         }
2062 }
2063
2064 // fix the cursor `cur' after a characters has been deleted at `where'
2065 // position. Called by deleteEmptyParagraphMechanism
2066 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
2067                                    LyXCursor const & where)
2068 {
2069         // if cursor is not in the paragraph where the delete occured,
2070         // do nothing
2071         if (cur.par() != where.par())
2072                 return;
2073
2074         // if cursor position is after the place where the delete occured,
2075         // update it
2076         if (cur.pos() > where.pos())
2077                 cur.pos(cur.pos()-1);
2078
2079         // check also if we don't want to set the cursor on a spot behind the
2080         // pagragraph because we erased the last character.
2081         if (cur.pos() > cur.par()->size())
2082                 cur.pos(cur.par()->size());
2083
2084         // recompute row et al. for this cursor
2085         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
2086 }
2087
2088
2089 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
2090 {
2091         // Would be wrong to delete anything if we have a selection.
2092         if (selection.set())
2093                 return false;
2094
2095         // We allow all kinds of "mumbo-jumbo" when freespacing.
2096         if (old_cursor.par()->layout()->free_spacing
2097             || old_cursor.par()->isFreeSpacing()) {
2098                 return false;
2099         }
2100
2101         /* Ok I'll put some comments here about what is missing.
2102            I have fixed BackSpace (and thus Delete) to not delete
2103            double-spaces automagically. I have also changed Cut,
2104            Copy and Paste to hopefully do some sensible things.
2105            There are still some small problems that can lead to
2106            double spaces stored in the document file or space at
2107            the beginning of paragraphs. This happens if you have
2108            the cursor betwenn to spaces and then save. Or if you
2109            cut and paste and the selection have a space at the
2110            beginning and then save right after the paste. I am
2111            sure none of these are very hard to fix, but I will
2112            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2113            that I can get some feedback. (Lgb)
2114         */
2115
2116         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2117         // delete the LineSeparator.
2118         // MISSING
2119
2120         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2121         // delete the LineSeparator.
2122         // MISSING
2123
2124         // If the pos around the old_cursor were spaces, delete one of them.
2125         if (old_cursor.par() != cursor.par()
2126             || old_cursor.pos() != cursor.pos()) {
2127                 // Only if the cursor has really moved
2128
2129                 if (old_cursor.pos() > 0
2130                     && old_cursor.pos() < old_cursor.par()->size()
2131                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2132                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2133                         old_cursor.par()->erase(old_cursor.pos() - 1);
2134                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2135
2136 #ifdef WITH_WARNINGS
2137 #warning This will not work anymore when we have multiple views of the same buffer
2138 // In this case, we will have to correct also the cursors held by
2139 // other bufferviews. It will probably be easier to do that in a more
2140 // automated way in LyXCursor code. (JMarc 26/09/2001)
2141 #endif
2142                         // correct all cursors held by the LyXText
2143                         fixCursorAfterDelete(cursor, old_cursor);
2144                         fixCursorAfterDelete(selection.cursor,
2145                                              old_cursor);
2146                         fixCursorAfterDelete(selection.start,
2147                                              old_cursor);
2148                         fixCursorAfterDelete(selection.end, old_cursor);
2149                         fixCursorAfterDelete(last_sel_cursor,
2150                                              old_cursor);
2151                         fixCursorAfterDelete(toggle_cursor, old_cursor);
2152                         fixCursorAfterDelete(toggle_end_cursor,
2153                                              old_cursor);
2154                         return false;
2155                 }
2156         }
2157
2158         // don't delete anything if this is the ONLY paragraph!
2159         if (ownerParagraphs().size() == 1)
2160                 return false;
2161
2162         // Do not delete empty paragraphs with keepempty set.
2163         if (old_cursor.par()->allowEmpty())
2164                 return false;
2165
2166         // only do our magic if we changed paragraph
2167         if (old_cursor.par() == cursor.par())
2168                 return false;
2169
2170         // record if we have deleted a paragraph
2171         // we can't possibly have deleted a paragraph before this point
2172         bool deleted = false;
2173
2174         if (old_cursor.par()->empty() ||
2175             (old_cursor.par()->size() == 1 &&
2176              old_cursor.par()->isLineSeparator(0))) {
2177                 // ok, we will delete anything
2178                 LyXCursor tmpcursor;
2179
2180                 deleted = true;
2181
2182                 bool selection_position_was_oldcursor_position = (
2183                         selection.cursor.par()  == old_cursor.par()
2184                         && selection.cursor.pos() == old_cursor.pos());
2185
2186                 if (getRow(old_cursor) != rows().begin()) {
2187                         RowList::iterator prevrow = boost::prior(getRow(old_cursor));
2188                         postPaint();
2189                         tmpcursor = cursor;
2190                         cursor = old_cursor; // that undo can restore the right cursor position
2191                         #warning FIXME. --end() iterator is usable here
2192                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2193                         while (endpit != ownerParagraphs().end() &&
2194                                endpit->getDepth()) {
2195                                 ++endpit;
2196                         }
2197
2198                         recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2199                                 boost::prior(endpit));
2200                         cursor = tmpcursor;
2201
2202                         // delete old row
2203                         removeRow(getRow(old_cursor));
2204                         // delete old par
2205                         ownerParagraphs().erase(old_cursor.par());
2206
2207                         /* Breakagain the next par. Needed because of
2208                          * the parindent that can occur or dissappear.
2209                          * The next row can change its height, if
2210                          * there is another layout before */
2211                         RowList::iterator tmprit = boost::next(prevrow);
2212                         if (tmprit != rows().end()) {
2213                                 breakAgain(tmprit);
2214                                 updateCounters();
2215                         }
2216                         setHeightOfRow(prevrow);
2217                 } else {
2218                         RowList::iterator nextrow = boost::next(getRow(old_cursor));
2219                         postPaint();
2220
2221                         tmpcursor = cursor;
2222                         cursor = old_cursor; // that undo can restore the right cursor position
2223 #warning FIXME. --end() iterator is usable here
2224                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2225                         while (endpit != ownerParagraphs().end() &&
2226                                endpit->getDepth()) {
2227                                 ++endpit;
2228                         }
2229
2230                         recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2231                         cursor = tmpcursor;
2232
2233                         // delete old row
2234                         removeRow(getRow(old_cursor));
2235                         // delete old par
2236                         ownerParagraphs().erase(old_cursor.par());
2237
2238                         /* Breakagain the next par. Needed because of
2239                            the parindent that can occur or dissappear.
2240                            The next row can change its height, if
2241                            there is another layout before */
2242                         if (nextrow != rows().end()) {
2243                                 breakAgain(nextrow);
2244                                 updateCounters();
2245                         }
2246                 }
2247
2248                 // correct cursor y
2249                 setCursorIntern(cursor.par(), cursor.pos());
2250
2251                 if (selection_position_was_oldcursor_position) {
2252                         // correct selection
2253                         selection.cursor = cursor;
2254                 }
2255         }
2256         if (!deleted) {
2257                 if (old_cursor.par()->stripLeadingSpaces()) {
2258                         redoParagraphs(old_cursor, boost::next(old_cursor.par()));
2259                         // correct cursor y
2260                         setCursorIntern(cursor.par(), cursor.pos());
2261                         selection.cursor = cursor;
2262                 }
2263         }
2264         return deleted;
2265 }
2266
2267
2268 ParagraphList & LyXText::ownerParagraphs() const
2269 {
2270         if (inset_owner) {
2271                 return inset_owner->paragraphs;
2272         }
2273         return bv_owner->buffer()->paragraphs;
2274 }
2275
2276
2277 bool LyXText::needRefresh() const
2278 {
2279         return need_refresh_;
2280 }
2281
2282
2283 void LyXText::clearPaint()
2284 {
2285         need_refresh_ = false;
2286 }
2287
2288
2289 void LyXText::postPaint()
2290 {
2291         need_refresh_ = true;
2292
2293         // We are an inset's lyxtext. Tell the top-level lyxtext
2294         // it needs to update the row we're in.
2295         if (inset_owner)
2296                 bv()->text->postPaint();
2297 }
2298
2299
2300 bool LyXText::isInInset() const
2301 {
2302         // Sub-level has non-null bv owner and
2303         // non-null inset owner.
2304         return inset_owner != 0 && bv_owner != 0;
2305 }
2306
2307
2308 int defaultRowHeight()
2309 {
2310         LyXFont const font(LyXFont::ALL_SANE);
2311         return int(font_metrics::maxAscent(font)
2312                  + font_metrics::maxDescent(font) * 1.5);
2313 }