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