]> git.lyx.org Git - lyx.git/blob - src/text2.C
some profiler work, a few cleanups
[lyx.git] / src / text2.C
1 /**
2  * \file text2.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Asger Alstrup
7  * \author Lars Gullik Bjønnes
8  * \author Alfredo Braunstein
9  * \author Jean-Marc Lasgouttes
10  * \author Angus Leeming
11  * \author John Levon
12  * \author André Pönitz
13  * \author Allan Rae
14  * \author Dekel Tsur
15  * \author Jürgen Vigna
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "lyxtext.h"
23
24 #include "buffer.h"
25 #include "buffer_funcs.h"
26 #include "bufferparams.h"
27 #include "BufferView.h"
28 #include "Bullet.h"
29 #include "counters.h"
30 #include "coordcache.h"
31 #include "cursor.h"
32 #include "CutAndPaste.h"
33 #include "debug.h"
34 #include "dispatchresult.h"
35 #include "errorlist.h"
36 #include "Floating.h"
37 #include "FloatList.h"
38 #include "funcrequest.h"
39 #include "gettext.h"
40 #include "language.h"
41 #include "LColor.h"
42 #include "lyxrc.h"
43 #include "lyxrow.h"
44 #include "lyxrow_funcs.h"
45 #include "paragraph.h"
46 #include "paragraph_funcs.h"
47 #include "ParagraphParameters.h"
48 #include "undo.h"
49 #include "vspace.h"
50
51 #include "frontends/font_metrics.h"
52 #include "frontends/LyXView.h"
53
54 #include "insets/insetbibitem.h"
55 #include "insets/insetenv.h"
56 #include "insets/insetfloat.h"
57 #include "insets/insetwrap.h"
58
59 #include "support/lstrings.h"
60 #include "support/textutils.h"
61 #include "support/tostr.h"
62
63 #include <sstream>
64
65 using lyx::par_type;
66 using lyx::pos_type;
67 using lyx::support::bformat;
68
69 using std::endl;
70 using std::ostringstream;
71 using std::string;
72
73
74 LyXText::LyXText(BufferView * bv)
75         : width_(0), maxwidth_(bv ? bv->workWidth() : 100), height_(0),
76           background_color_(LColor::background),
77           bv_owner(bv), xo_(0), yo_(0)
78 {}
79
80
81 void LyXText::init(BufferView * bv)
82 {
83         BOOST_ASSERT(bv);
84         bv_owner = bv;
85         maxwidth_ = bv->workWidth();
86         width_ = maxwidth_;
87         height_ = 0;
88
89         par_type const end = paragraphs().size();
90         for (par_type pit = 0; pit != end; ++pit)
91                 pars_[pit].rows.clear();
92
93         current_font = getFont(pars_[0], 0);
94         redoParagraphs(0, end);
95         updateCounters();
96 }
97
98
99 bool LyXText::isMainText() const
100 {
101         return &bv()->buffer()->text() == this;
102 }
103
104
105 // takes absolute x,y coordinates
106 InsetBase * LyXText::checkInsetHit(int x, int y) const 
107 {
108         par_type pit;
109         par_type end;
110
111         getParsInRange(paragraphs(),
112                        bv()->top_y() - yo_,
113                        bv()->top_y() - yo_ + bv()->workHeight(),
114                        pit, end);
115
116         // convert to screen-absolute y coordinate
117         y -= bv()->top_y();
118         lyxerr << "checkInsetHit: x: " << x << " y: " << y << endl;
119         lyxerr << "  pit: " << pit << " end: " << end << endl;
120         for (; pit != end; ++pit) {
121                 InsetList::const_iterator iit = pars_[pit].insetlist.begin();
122                 InsetList::const_iterator iend = pars_[pit].insetlist.end();
123                 for (; iit != iend; ++iit) {
124                         InsetBase * inset = iit->inset;
125 #if 1
126                         lyxerr << "examining inset " << inset << endl;
127                         if (theCoords.insets_.has(inset))
128                                 lyxerr
129                                         << " xo: " << inset->xo() << "..." << inset->xo() + inset->width()
130                                         << " yo: " << inset->yo() - inset->ascent() << "..."
131                                         << inset->yo() + inset->descent() << endl;
132                         else
133                                 lyxerr << " inset has no cached position";
134 #endif
135                         if (inset->covers(x, y)) {
136                                 lyxerr << "Hit inset: " << inset << endl;
137                                 return inset;
138                         }
139                 }
140         }
141         lyxerr << "No inset hit. " << endl;
142         return 0;
143 }
144
145
146
147 // Gets the fully instantiated font at a given position in a paragraph
148 // Basically the same routine as Paragraph::getFont() in paragraph.C.
149 // The difference is that this one is used for displaying, and thus we
150 // are allowed to make cosmetic improvements. For instance make footnotes
151 // smaller. (Asger)
152 LyXFont LyXText::getFont(Paragraph const & par, pos_type const pos) const
153 {
154         BOOST_ASSERT(pos >= 0);
155
156         LyXLayout_ptr const & layout = par.layout();
157 #ifdef WITH_WARNINGS
158 #warning broken?
159 #endif
160         BufferParams const & params = bv()->buffer()->params();
161         pos_type const body_pos = par.beginOfBody();
162
163         // We specialize the 95% common case:
164         if (!par.getDepth()) {
165                 LyXFont f = par.getFontSettings(params, pos);
166                 if (!isMainText())
167                         f.realize(font_);
168                 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
169                         return f.realize(layout->reslabelfont);
170                 else
171                         return f.realize(layout->resfont);
172         }
173
174         // The uncommon case need not be optimized as much
175         LyXFont layoutfont;
176         if (pos < body_pos)
177                 layoutfont = layout->labelfont;
178         else
179                 layoutfont = layout->font;
180
181         LyXFont font = par.getFontSettings(params, pos);
182         font.realize(layoutfont);
183
184         if (!isMainText())
185                 font.realize(font_);
186
187         // Realize with the fonts of lesser depth.
188         font.realize(defaultfont_);
189
190         return font;
191 }
192
193
194 LyXFont LyXText::getLayoutFont(par_type const pit) const
195 {
196         LyXLayout_ptr const & layout = pars_[pit].layout();
197
198         if (!pars_[pit].getDepth())
199                 return layout->resfont;
200
201         LyXFont font = layout->font;
202         // Realize with the fonts of lesser depth.
203         //font.realize(outerFont(pit, paragraphs()));
204         font.realize(defaultfont_);
205
206         return font;
207 }
208
209
210 LyXFont LyXText::getLabelFont(Paragraph const & par) const
211 {
212         LyXLayout_ptr const & layout = par.layout();
213
214         if (!par.getDepth())
215                 return layout->reslabelfont;
216
217         LyXFont font = layout->labelfont;
218         // Realize with the fonts of lesser depth.
219         font.realize(defaultfont_);
220
221         return font;
222 }
223
224
225 void LyXText::setCharFont(par_type pit, pos_type pos, LyXFont const & fnt)
226 {
227         LyXFont font = fnt;
228         LyXLayout_ptr const & layout = pars_[pit].layout();
229
230         // Get concrete layout font to reduce against
231         LyXFont layoutfont;
232
233         if (pos < pars_[pit].beginOfBody())
234                 layoutfont = layout->labelfont;
235         else
236                 layoutfont = layout->font;
237
238         // Realize against environment font information
239         if (pars_[pit].getDepth()) {
240                 par_type tp = pit;
241                 while (!layoutfont.resolved() &&
242                        tp != par_type(paragraphs().size()) &&
243                        pars_[tp].getDepth()) {
244                         tp = outerHook(tp, paragraphs());
245                         if (tp != par_type(paragraphs().size()))
246                                 layoutfont.realize(pars_[tp].layout()->font);
247                 }
248         }
249
250         layoutfont.realize(defaultfont_);
251
252         // Now, reduce font against full layout font
253         font.reduce(layoutfont);
254
255         pars_[pit].setFont(pos, font);
256 }
257
258
259 // used in setLayout
260 // Asger is not sure we want to do this...
261 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
262                                             Paragraph & par)
263 {
264         LyXLayout_ptr const & layout = par.layout();
265         pos_type const psize = par.size();
266
267         LyXFont layoutfont;
268         for (pos_type pos = 0; pos < psize; ++pos) {
269                 if (pos < par.beginOfBody())
270                         layoutfont = layout->labelfont;
271                 else
272                         layoutfont = layout->font;
273
274                 LyXFont tmpfont = par.getFontSettings(params, pos);
275                 tmpfont.reduce(layoutfont);
276                 par.setFont(pos, tmpfont);
277         }
278 }
279
280
281 // return past-the-last paragraph influenced by a layout change on pit
282 par_type LyXText::undoSpan(par_type pit)
283 {
284         par_type end = paragraphs().size();
285         par_type nextpit = pit + 1;
286         if (nextpit == end)
287                 return nextpit;
288         //because of parindents
289         if (!pars_[pit].getDepth())
290                 return boost::next(nextpit);
291         //because of depth constrains
292         for (; nextpit != end; ++pit, ++nextpit) {
293                 if (!pars_[pit].getDepth())
294                         break;
295         }
296         return nextpit;
297 }
298
299
300 par_type LyXText::setLayout(par_type start, par_type end, string const & layout)
301 {
302         BOOST_ASSERT(start != end);
303         par_type undopit = undoSpan(end - 1);
304         recUndo(start, undopit - 1);
305
306         BufferParams const & bufparams = bv()->buffer()->params();
307         LyXLayout_ptr const & lyxlayout = bufparams.getLyXTextClass()[layout];
308
309         for (par_type pit = start; pit != end; ++pit) {
310                 pars_[pit].applyLayout(lyxlayout);
311                 makeFontEntriesLayoutSpecific(bufparams, pars_[pit]);
312                 if (lyxlayout->margintype == MARGIN_MANUAL)
313                         pars_[pit].setLabelWidthString(lyxlayout->labelstring());
314         }
315
316         return undopit;
317 }
318
319
320 // set layout over selection and make a total rebreak of those paragraphs
321 void LyXText::setLayout(LCursor & cur, string const & layout)
322 {
323         BOOST_ASSERT(this == cur.text());
324         // special handling of new environment insets
325         BufferView & bv = cur.bv();
326         BufferParams const & params = bv.buffer()->params();
327         LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
328         if (lyxlayout->is_environment) {
329                 // move everything in a new environment inset
330                 lyxerr[Debug::DEBUG] << "setting layout " << layout << endl;
331                 bv.owner()->dispatch(FuncRequest(LFUN_HOME));
332                 bv.owner()->dispatch(FuncRequest(LFUN_ENDSEL));
333                 bv.owner()->dispatch(FuncRequest(LFUN_CUT));
334                 InsetBase * inset = new InsetEnvironment(params, layout);
335                 insertInset(cur, inset);
336                 //inset->edit(cur, true);
337                 //bv.owner()->dispatch(FuncRequest(LFUN_PASTE));
338                 return;
339         }
340
341         par_type start = cur.selBegin().par();
342         par_type end = cur.selEnd().par() + 1;
343         par_type endpit = setLayout(start, end, layout);
344         redoParagraphs(start, endpit);
345         updateCounters();
346 }
347
348
349 namespace {
350
351
352 void getSelectionSpan(LCursor & cur, par_type & beg, par_type & end)
353 {
354         if (!cur.selection()) {
355                 beg = cur.par();
356                 end = cur.par() + 1;
357         } else {
358                 beg = cur.selBegin().par();
359                 end = cur.selEnd().par() + 1;
360         }
361 }
362
363
364 bool changeDepthAllowed(LyXText::DEPTH_CHANGE type,
365         Paragraph const & par, int max_depth)
366 {
367         if (par.layout()->labeltype == LABEL_BIBLIO)
368                 return false;
369         int const depth = par.params().depth();
370         if (type == LyXText::INC_DEPTH && depth < max_depth)
371                 return true;
372         if (type == LyXText::DEC_DEPTH && depth > 0)
373                 return true;
374         return false;
375 }
376
377
378 }
379
380
381 bool LyXText::changeDepthAllowed(LCursor & cur, DEPTH_CHANGE type) const
382 {
383         BOOST_ASSERT(this == cur.text());
384         par_type beg, end;
385         getSelectionSpan(cur, beg, end);
386         int max_depth = 0;
387         if (beg != 0)
388                 max_depth = pars_[beg - 1].getMaxDepthAfter();
389
390         for (par_type pit = beg; pit != end; ++pit) {
391                 if (::changeDepthAllowed(type, pars_[pit], max_depth))
392                         return true;
393                 max_depth = pars_[pit].getMaxDepthAfter();
394         }
395         return false;
396 }
397
398
399 void LyXText::changeDepth(LCursor & cur, DEPTH_CHANGE type)
400 {
401         BOOST_ASSERT(this == cur.text());
402         par_type beg, end;
403         getSelectionSpan(cur, beg, end);
404         recordUndoSelection(cur);
405
406         int max_depth = 0;
407         if (beg != 0)
408                 max_depth = pars_[beg - 1].getMaxDepthAfter();
409
410         for (par_type pit = beg; pit != end; ++pit) {
411                 if (::changeDepthAllowed(type, pars_[pit], max_depth)) {
412                         int const depth = pars_[pit].params().depth();
413                         if (type == INC_DEPTH)
414                                 pars_[pit].params().depth(depth + 1);
415                         else
416                                 pars_[pit].params().depth(depth - 1);
417                 }
418                 max_depth = pars_[pit].getMaxDepthAfter();
419         }
420         // this handles the counter labels, and also fixes up
421         // depth values for follow-on (child) paragraphs
422         updateCounters();
423 }
424
425
426 // set font over selection
427 void LyXText::setFont(LCursor & cur, LyXFont const & font, bool toggleall)
428 {
429         BOOST_ASSERT(this == cur.text());
430         // if there is no selection just set the current_font
431         if (!cur.selection()) {
432                 // Determine basis font
433                 LyXFont layoutfont;
434                 par_type pit = cur.par();
435                 if (cur.pos() < pars_[pit].beginOfBody())
436                         layoutfont = getLabelFont(pars_[pit]);
437                 else
438                         layoutfont = getLayoutFont(pit);
439
440                 // Update current font
441                 real_current_font.update(font,
442                                          cur.buffer().params().language,
443                                          toggleall);
444
445                 // Reduce to implicit settings
446                 current_font = real_current_font;
447                 current_font.reduce(layoutfont);
448                 // And resolve it completely
449                 real_current_font.realize(layoutfont);
450
451                 return;
452         }
453
454         // Ok, we have a selection.
455         recordUndoSelection(cur);
456
457         par_type const beg = cur.selBegin().par();
458         par_type const end = cur.selEnd().par();
459
460         DocIterator dit = cur.selectionBegin();
461         DocIterator ditend = cur.selectionEnd();
462
463         BufferParams const & params = cur.buffer().params();
464
465         // Don't use forwardChar here as ditend might have
466         // pos() == lastpos() and forwardChar would miss it.
467         for (; dit != ditend; dit.forwardPos()) {
468                 if (dit.pos() != dit.lastpos()) {
469                         LyXFont f = getFont(dit.paragraph(), dit.pos());
470                         f.update(font, params.language, toggleall);
471                         setCharFont(dit.par(), dit.pos(), f);
472                 }
473         }
474
475         redoParagraphs(beg, end + 1);
476 }
477
478
479 // the cursor set functions have a special mechanism. When they
480 // realize you left an empty paragraph, they will delete it.
481
482 void LyXText::cursorHome(LCursor & cur)
483 {
484         BOOST_ASSERT(this == cur.text());
485         setCursor(cur, cur.par(), cur.textRow().pos());
486 }
487
488
489 void LyXText::cursorEnd(LCursor & cur)
490 {
491         BOOST_ASSERT(this == cur.text());
492         // if not on the last row of the par, put the cursor before
493         // the final space
494         pos_type const end = cur.textRow().endpos();
495         setCursor(cur, cur.par(), end == cur.lastpos() ? end : end - 1);
496 }
497
498
499 void LyXText::cursorTop(LCursor & cur)
500 {
501         BOOST_ASSERT(this == cur.text());
502         setCursor(cur, 0, 0);
503 }
504
505
506 void LyXText::cursorBottom(LCursor & cur)
507 {
508         BOOST_ASSERT(this == cur.text());
509         setCursor(cur, cur.lastpar(), boost::prior(paragraphs().end())->size());
510 }
511
512
513 void LyXText::toggleFree(LCursor & cur, LyXFont const & font, bool toggleall)
514 {
515         BOOST_ASSERT(this == cur.text());
516         // If the mask is completely neutral, tell user
517         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
518                 // Could only happen with user style
519                 cur.message(_("No font change defined. "
520                         "Use Character under the Layout menu to define font change."));
521                 return;
522         }
523
524         // Try implicit word selection
525         // If there is a change in the language the implicit word selection
526         // is disabled.
527         CursorSlice resetCursor = cur.top();
528         bool implicitSelection =
529                 font.language() == ignore_language
530                 && font.number() == LyXFont::IGNORE
531                 && selectWordWhenUnderCursor(cur, lyx::WHOLE_WORD_STRICT);
532
533         // Set font
534         setFont(cur, font, toggleall);
535
536         // Implicit selections are cleared afterwards
537         // and cursor is set to the original position.
538         if (implicitSelection) {
539                 cur.clearSelection();
540                 cur.top() = resetCursor;
541                 cur.resetAnchor();
542         }
543 }
544
545
546 string LyXText::getStringToIndex(LCursor & cur)
547 {
548         BOOST_ASSERT(this == cur.text());
549         // Try implicit word selection
550         // If there is a change in the language the implicit word selection
551         // is disabled.
552         CursorSlice const reset_cursor = cur.top();
553         bool const implicitSelection =
554                 selectWordWhenUnderCursor(cur, lyx::PREVIOUS_WORD);
555
556         string idxstring;
557         if (!cur.selection())
558                 cur.message(_("Nothing to index!"));
559         else if (cur.selBegin().par() != cur.selEnd().par())
560                 cur.message(_("Cannot index more than one paragraph!"));
561         else
562                 idxstring = cur.selectionAsString(false);
563
564         // Reset cursors to their original position.
565         cur.top() = reset_cursor;
566         cur.resetAnchor();
567
568         // Clear the implicit selection.
569         if (implicitSelection)
570                 cur.clearSelection();
571
572         return idxstring;
573 }
574
575
576 void LyXText::setParagraph(LCursor & cur,
577         Spacing const & spacing, LyXAlignment align,
578         string const & labelwidthstring, bool noindent)
579 {
580         BOOST_ASSERT(cur.text());
581         // make sure that the depth behind the selection are restored, too
582         par_type undopit = undoSpan(cur.selEnd().par());
583         recUndo(cur.selBegin().par(), undopit - 1);
584
585         for (par_type pit = cur.selBegin().par(), end = cur.selEnd().par();
586                         pit <= end; ++pit) {
587                 Paragraph & par = pars_[pit];
588                 ParagraphParameters & params = par.params();
589                 params.spacing(spacing);
590
591                 // does the layout allow the new alignment?
592                 LyXLayout_ptr const & layout = par.layout();
593
594                 if (align == LYX_ALIGN_LAYOUT)
595                         align = layout->align;
596                 if (align & layout->alignpossible) {
597                         if (align == layout->align)
598                                 params.align(LYX_ALIGN_LAYOUT);
599                         else
600                                 params.align(align);
601                 }
602                 par.setLabelWidthString(labelwidthstring);
603                 params.noindent(noindent);
604         }
605
606         redoParagraphs(cur.selBegin().par(), undopit);
607 }
608
609
610 string expandLabel(LyXTextClass const & textclass,
611         LyXLayout_ptr const & layout, bool appendix)
612 {
613         string fmt = appendix ?
614                 layout->labelstring_appendix() : layout->labelstring();
615
616         // handle 'inherited level parts' in 'fmt',
617         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
618         size_t const i = fmt.find('@', 0);
619         if (i != string::npos) {
620                 size_t const j = fmt.find('@', i + 1);
621                 if (j != string::npos) {
622                         string parent(fmt, i + 1, j - i - 1);
623                         string label = expandLabel(textclass, textclass[parent], appendix);
624                         fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
625                 }
626         }
627
628         return textclass.counters().counterLabel(fmt);
629 }
630
631
632 namespace {
633
634 void incrementItemDepth(ParagraphList & pars, par_type pit, par_type first_pit)
635 {
636         int const cur_labeltype = pars[pit].layout()->labeltype;
637
638         if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
639                 return;
640
641         int const cur_depth = pars[pit].getDepth();
642
643         par_type prev_pit = pit - 1;
644         while (true) {
645                 int const prev_depth = pars[prev_pit].getDepth();
646                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
647                 if (prev_depth == 0 && cur_depth > 0) {
648                         if (prev_labeltype == cur_labeltype) {
649                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
650                         }
651                         break;
652                 } else if (prev_depth < cur_depth) {
653                         if (prev_labeltype == cur_labeltype) {
654                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
655                                 break;
656                         }
657                 } else if (prev_depth == cur_depth) {
658                         if (prev_labeltype == cur_labeltype) {
659                                 pars[pit].itemdepth = pars[prev_pit].itemdepth;
660                                 break;
661                         }
662                 }
663                 if (prev_pit == first_pit)
664                         break;
665
666                 --prev_pit;
667         }
668 }
669
670
671 void resetEnumCounterIfNeeded(ParagraphList & pars, par_type pit,
672         par_type firstpit, Counters & counters)
673 {
674         if (pit == firstpit)
675                 return;
676
677         int const cur_depth = pars[pit].getDepth();
678         par_type prev_pit = pit - 1;
679         while (true) {
680                 int const prev_depth = pars[prev_pit].getDepth();
681                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
682                 if (prev_depth <= cur_depth) {
683                         if (prev_labeltype != LABEL_ENUMERATE) {
684                                 switch (pars[pit].itemdepth) {
685                                 case 0:
686                                         counters.reset("enumi");
687                                 case 1:
688                                         counters.reset("enumii");
689                                 case 2:
690                                         counters.reset("enumiii");
691                                 case 3:
692                                         counters.reset("enumiv");
693                                 }
694                         }
695                         break;
696                 }
697
698                 if (prev_pit == firstpit)
699                         break;
700
701                 --prev_pit;
702         }
703 }
704
705 } // anon namespace
706
707
708 // set the counter of a paragraph. This includes the labels
709 void LyXText::setCounter(Buffer const & buf, par_type pit)
710 {
711         Paragraph & par = pars_[pit];
712         BufferParams const & bufparams = buf.params();
713         LyXTextClass const & textclass = bufparams.getLyXTextClass();
714         LyXLayout_ptr const & layout = par.layout();
715         par_type first_pit = 0;
716         Counters & counters = textclass.counters();
717
718         // Always reset
719         par.itemdepth = 0;
720
721         if (pit == first_pit) {
722                 par.params().appendix(par.params().startOfAppendix());
723         } else {
724                 par.params().appendix(pars_[pit - 1].params().appendix());
725                 if (!par.params().appendix() &&
726                     par.params().startOfAppendix()) {
727                         par.params().appendix(true);
728                         textclass.counters().reset();
729                 }
730
731                 // Maybe we have to increment the item depth.
732                 incrementItemDepth(pars_, pit, first_pit);
733         }
734
735         // erase what was there before
736         par.params().labelString(string());
737
738         if (layout->margintype == MARGIN_MANUAL) {
739                 if (par.params().labelWidthString().empty())
740                         par.setLabelWidthString(layout->labelstring());
741         } else {
742                 par.setLabelWidthString(string());
743         }
744
745         // is it a layout that has an automatic label?
746         if (layout->labeltype == LABEL_COUNTER) {
747                 BufferParams const & bufparams = buf.params();
748                 LyXTextClass const & textclass = bufparams.getLyXTextClass();
749                 counters.step(layout->counter);
750                 string label = expandLabel(textclass, layout, par.params().appendix());
751                 par.params().labelString(label);
752         } else if (layout->labeltype == LABEL_ITEMIZE) {
753                 // At some point of time we should do something more
754                 // clever here, like:
755                 //   par.params().labelString(
756                 //    bufparams.user_defined_bullet(par.itemdepth).getText());
757                 // for now, use a simple hardcoded label
758                 string itemlabel;
759                 switch (par.itemdepth) {
760                 case 0:
761                         itemlabel = "*";
762                         break;
763                 case 1:
764                         itemlabel = "-";
765                         break;
766                 case 2:
767                         itemlabel = "@";
768                         break;
769                 case 3:
770                         itemlabel = "·";
771                         break;
772                 }
773
774                 par.params().labelString(itemlabel);
775         } else if (layout->labeltype == LABEL_ENUMERATE) {
776                 // Maybe we have to reset the enumeration counter.
777                 resetEnumCounterIfNeeded(pars_, pit, first_pit, counters);
778
779                 // FIXME
780                 // Yes I know this is a really, really! bad solution
781                 // (Lgb)
782                 string enumcounter = "enum";
783
784                 switch (par.itemdepth) {
785                 case 2:
786                         enumcounter += 'i';
787                 case 1:
788                         enumcounter += 'i';
789                 case 0:
790                         enumcounter += 'i';
791                         break;
792                 case 3:
793                         enumcounter += "iv";
794                         break;
795                 default:
796                         // not a valid enumdepth...
797                         break;
798                 }
799
800                 counters.step(enumcounter);
801
802                 par.params().labelString(counters.enumLabel(enumcounter));
803         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
804                 counters.step("bibitem");
805                 int number = counters.value("bibitem");
806                 if (par.bibitem()) {
807                         par.bibitem()->setCounter(number);
808                         par.params().labelString(layout->labelstring());
809                 }
810                 // In biblio should't be following counters but...
811         } else {
812                 string s = buf.B_(layout->labelstring());
813
814                 // the caption hack:
815                 if (layout->labeltype == LABEL_SENSITIVE) {
816                         par_type end = paragraphs().size();
817                         par_type tmppit = pit;
818                         InsetBase * in = 0;
819                         bool isOK = false;
820                         while (tmppit != end) {
821                                 in = pars_[tmppit].inInset();
822                                 if (in->lyxCode() == InsetBase::FLOAT_CODE ||
823                                     in->lyxCode() == InsetBase::WRAP_CODE) {
824                                         isOK = true;
825                                         break;
826                                 } else {
827                                         Paragraph const * owner = &ownerPar(buf, in);
828                                         tmppit = first_pit;
829                                         for ( ; tmppit != end; ++tmppit)
830                                                 if (&pars_[tmppit] == owner)
831                                                         break;
832                                 }
833                         }
834
835                         if (isOK) {
836                                 string type;
837
838                                 if (in->lyxCode() == InsetBase::FLOAT_CODE)
839                                         type = static_cast<InsetFloat*>(in)->params().type;
840                                 else if (in->lyxCode() == InsetBase::WRAP_CODE)
841                                         type = static_cast<InsetWrap*>(in)->params().type;
842                                 else
843                                         BOOST_ASSERT(false);
844
845                                 Floating const & fl = textclass.floats().getType(type);
846
847                                 counters.step(fl.type());
848
849                                 // Doesn't work... yet.
850                                 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
851                         } else {
852                                 // par->SetLayout(0);
853                                 // s = layout->labelstring;
854                                 s = _("Senseless: ");
855                         }
856                 }
857                 par.params().labelString(s);
858
859         }
860 }
861
862
863 // Updates all counters.
864 void LyXText::updateCounters()
865 {
866         // start over
867         bv()->buffer()->params().getLyXTextClass().counters().reset();
868
869         bool update_pos = false;
870
871         par_type end = paragraphs().size();
872         for (par_type pit = 0; pit != end; ++pit) {
873                 string const oldLabel = pars_[pit].params().labelString();
874                 size_t maxdepth = 0;
875                 if (pit != 0)
876                         maxdepth = pars_[pit - 1].getMaxDepthAfter();
877
878                 if (pars_[pit].params().depth() > maxdepth)
879                         pars_[pit].params().depth(maxdepth);
880
881                 // setCounter can potentially change the labelString.
882                 setCounter(*bv()->buffer(), pit);
883                 string const & newLabel = pars_[pit].params().labelString();
884                 if (oldLabel != newLabel) {
885                         //lyxerr[Debug::DEBUG] << "changing labels: old: " << oldLabel << " new: "
886                         //      << newLabel << endl;
887                         redoParagraphInternal(pit);
888                         update_pos = true;
889                 }
890         }
891         if (update_pos)
892                 updateParPositions();
893 }
894
895
896 // this really should just inset the inset and not move the cursor.
897 void LyXText::insertInset(LCursor & cur, InsetBase * inset)
898 {
899         BOOST_ASSERT(this == cur.text());
900         BOOST_ASSERT(inset);
901         cur.paragraph().insertInset(cur.pos(), inset);
902         redoParagraph(cur);
903 }
904
905
906 // needed to insert the selection
907 void LyXText::insertStringAsLines(LCursor & cur, string const & str)
908 {
909         par_type pit = cur.par();
910         par_type endpit = cur.par() + 1;
911         pos_type pos = cur.pos();
912         recordUndo(cur);
913
914         // only to be sure, should not be neccessary
915         cur.clearSelection();
916         cur.buffer().insertStringAsLines(pars_, pit, pos, current_font, str);
917
918         redoParagraphs(cur.par(), endpit);
919         cur.resetAnchor();
920         setCursor(cur, cur.par(), pos);
921         cur.setSelection();
922 }
923
924
925 // turn double CR to single CR, others are converted into one
926 // blank. Then insertStringAsLines is called
927 void LyXText::insertStringAsParagraphs(LCursor & cur, string const & str)
928 {
929         string linestr = str;
930         bool newline_inserted = false;
931
932         for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
933                 if (linestr[i] == '\n') {
934                         if (newline_inserted) {
935                                 // we know that \r will be ignored by
936                                 // insertStringAsLines. Of course, it is a dirty
937                                 // trick, but it works...
938                                 linestr[i - 1] = '\r';
939                                 linestr[i] = '\n';
940                         } else {
941                                 linestr[i] = ' ';
942                                 newline_inserted = true;
943                         }
944                 } else if (IsPrintable(linestr[i])) {
945                         newline_inserted = false;
946                 }
947         }
948         insertStringAsLines(cur, linestr);
949 }
950
951
952 bool LyXText::setCursor(LCursor & cur, par_type par, pos_type pos,
953         bool setfont, bool boundary)
954 {
955         LCursor old = cur;
956         setCursorIntern(cur, par, pos, setfont, boundary);
957         return deleteEmptyParagraphMechanism(cur, old);
958 }
959
960
961 void LyXText::setCursor(CursorSlice & cur, par_type par,
962         pos_type pos, bool boundary)
963 {
964         BOOST_ASSERT(par != int(paragraphs().size()));
965
966         cur.par() = par;
967         cur.pos() = pos;
968         cur.boundary() = boundary;
969
970         // no rows, no fun...
971         if (paragraphs().begin()->rows.empty())
972                 return;
973
974         // now some strict checking
975         Paragraph & para = getPar(par);
976         Row const & row = *para.getRow(pos);
977         pos_type const end = row.endpos();
978
979         // None of these should happen, but we're scaredy-cats
980         if (pos < 0) {
981                 lyxerr << "dont like -1" << endl;
982                 BOOST_ASSERT(false);
983         }
984
985         if (pos > para.size()) {
986                 lyxerr << "dont like 1, pos: " << pos
987                        << " size: " << para.size()
988                        << " row.pos():" << row.pos()
989                        << " par: " << par << endl;
990                 BOOST_ASSERT(false);
991         }
992
993         if (pos > end) {
994                 lyxerr << "dont like 2, pos: " << pos
995                        << " size: " << para.size()
996                        << " row.pos():" << row.pos()
997                        << " par: " << par << endl;
998                 // This shouldn't happen.
999                 BOOST_ASSERT(false);
1000         }
1001
1002         if (pos < row.pos()) {
1003                 lyxerr << "dont like 3 please report pos:" << pos
1004                        << " size: " << para.size()
1005                        << " row.pos():" << row.pos()
1006                        << " par: " << par << endl;
1007                 BOOST_ASSERT(false);
1008         }
1009 }
1010
1011
1012 void LyXText::setCursorIntern(LCursor & cur,
1013         par_type par, pos_type pos, bool setfont, bool boundary)
1014 {
1015         setCursor(cur.top(), par, pos, boundary);
1016         cur.x_target() = cursorX(cur.top());
1017         if (setfont)
1018                 setCurrentFont(cur);
1019 }
1020
1021
1022 void LyXText::setCurrentFont(LCursor & cur)
1023 {
1024         BOOST_ASSERT(this == cur.text());
1025         pos_type pos = cur.pos();
1026         Paragraph & par = cur.paragraph();
1027
1028         if (cur.boundary() && pos > 0)
1029                 --pos;
1030
1031         if (pos > 0) {
1032                 if (pos == cur.lastpos())
1033                         --pos;
1034                 else // potentional bug... BUG (Lgb)
1035                         if (par.isSeparator(pos)) {
1036                                 if (pos > cur.textRow().pos() &&
1037                                     bidi.level(pos) % 2 ==
1038                                     bidi.level(pos - 1) % 2)
1039                                         --pos;
1040                                 else if (pos + 1 < cur.lastpos())
1041                                         ++pos;
1042                         }
1043         }
1044
1045         BufferParams const & bufparams = cur.buffer().params();
1046         current_font = par.getFontSettings(bufparams, pos);
1047         real_current_font = getFont(par, pos);
1048
1049         if (cur.pos() == cur.lastpos()
1050             && bidi.isBoundary(cur.buffer(), par, cur.pos())
1051             && !cur.boundary()) {
1052                 Language const * lang = par.getParLanguage(bufparams);
1053                 current_font.setLanguage(lang);
1054                 current_font.setNumber(LyXFont::OFF);
1055                 real_current_font.setLanguage(lang);
1056                 real_current_font.setNumber(LyXFont::OFF);
1057         }
1058 }
1059
1060
1061 // x is an absolute screen coord
1062 // returns the column near the specified x-coordinate of the row
1063 // x is set to the real beginning of this column
1064 pos_type LyXText::getColumnNearX(par_type const pit,
1065         Row const & row, int & x, bool & boundary) const
1066 {
1067         x -= xo_;
1068         RowMetrics const r = computeRowMetrics(pit, row);
1069         Paragraph const & par = pars_[pit];
1070
1071         pos_type vc = row.pos();
1072         pos_type end = row.endpos();
1073         pos_type c = 0;
1074         LyXLayout_ptr const & layout = par.layout();
1075
1076         bool left_side = false;
1077
1078         pos_type body_pos = par.beginOfBody();
1079
1080         double tmpx = r.x;
1081         double last_tmpx = tmpx;
1082
1083         if (body_pos > 0 &&
1084             (body_pos > end || !par.isLineSeparator(body_pos - 1)))
1085                 body_pos = 0;
1086
1087         // check for empty row
1088         if (vc == end) {
1089                 x = int(tmpx) + xo_;
1090                 return 0;
1091         }
1092
1093         while (vc < end && tmpx <= x) {
1094                 c = bidi.vis2log(vc);
1095                 last_tmpx = tmpx;
1096                 if (body_pos > 0 && c == body_pos - 1) {
1097                         tmpx += r.label_hfill +
1098                                 font_metrics::width(layout->labelsep, getLabelFont(par));
1099                         if (par.isLineSeparator(body_pos - 1))
1100                                 tmpx -= singleWidth(par, body_pos - 1);
1101                 }
1102
1103                 if (hfillExpansion(par, row, c)) {
1104                         tmpx += singleWidth(par, c);
1105                         if (c >= body_pos)
1106                                 tmpx += r.hfill;
1107                         else
1108                                 tmpx += r.label_hfill;
1109                 } else if (par.isSeparator(c)) {
1110                         tmpx += singleWidth(par, c);
1111                         if (c >= body_pos)
1112                                 tmpx += r.separator;
1113                 } else {
1114                         tmpx += singleWidth(par, c);
1115                 }
1116                 ++vc;
1117         }
1118
1119         if ((tmpx + last_tmpx) / 2 > x) {
1120                 tmpx = last_tmpx;
1121                 left_side = true;
1122         }
1123
1124         BOOST_ASSERT(vc <= end);  // This shouldn't happen.
1125
1126         boundary = false;
1127         // This (rtl_support test) is not needed, but gives
1128         // some speedup if rtl_support == false
1129         bool const lastrow = lyxrc.rtl_support && row.endpos() == par.size();
1130
1131         // If lastrow is false, we don't need to compute
1132         // the value of rtl.
1133         bool const rtl = lastrow ? isRTL(par) : false;
1134         if (lastrow &&
1135                  ((rtl  &&  left_side && vc == row.pos() && x < tmpx - 5) ||
1136                   (!rtl && !left_side && vc == end  && x > tmpx + 5)))
1137                 c = end;
1138         else if (vc == row.pos()) {
1139                 c = bidi.vis2log(vc);
1140                 if (bidi.level(c) % 2 == 1)
1141                         ++c;
1142         } else {
1143                 c = bidi.vis2log(vc - 1);
1144                 bool const rtl = (bidi.level(c) % 2 == 1);
1145                 if (left_side == rtl) {
1146                         ++c;
1147                         boundary = bidi.isBoundary(*bv()->buffer(), par, c);
1148                 }
1149         }
1150
1151         if (row.pos() < end && c >= end && par.isNewline(end - 1)) {
1152                 if (bidi.level(end -1) % 2 == 0)
1153                         tmpx -= singleWidth(par, end - 1);
1154                 else
1155                         tmpx += singleWidth(par, end - 1);
1156                 c = end - 1;
1157         }
1158
1159         x = int(tmpx) + xo_;
1160         return c - row.pos();
1161 }
1162
1163
1164 // y is relative to this LyXText's top
1165 // this is only used in the two functions below
1166 Row const & LyXText::getRowNearY(int y, par_type & pit) const
1167 {
1168         BOOST_ASSERT(!paragraphs().empty());
1169         BOOST_ASSERT(!paragraphs().begin()->rows.empty());
1170         par_type const pend = paragraphs().size() - 1;
1171         pit = 0;
1172         while (int(pars_[pit].y + pars_[pit].height) < y && pit != pend)
1173                 ++pit;
1174
1175         RowList::iterator rit = pars_[pit].rows.end();
1176         RowList::iterator const rbegin = pars_[pit].rows.begin();
1177         do {
1178                 --rit;
1179         } while (rit != rbegin && int(pars_[pit].y + rit->y_offset()) > y);
1180
1181         return *rit;
1182 }
1183
1184
1185 // x,y are absolute coordinates
1186 // sets cursor only within this LyXText
1187 void LyXText::setCursorFromCoordinates(LCursor & cur, int x, int y)
1188 {
1189         x -= xo_;
1190         y -= yo_;
1191         par_type pit;
1192         Row const & row = getRowNearY(y, pit);
1193         lyxerr[Debug::DEBUG] << "setCursorFromCoordinates:: hit row at: "
1194                              << row.pos() << endl;
1195         bool bound = false;
1196         int xx = x + xo_; // getRowNearX get absolute x coords
1197         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1198         setCursor(cur, pit, pos, true, bound);
1199 }
1200
1201
1202 // x,y are absolute screen coordinates
1203 // sets cursor recursively descending into nested editable insets
1204 InsetBase * LyXText::editXY(LCursor & cur, int x, int y) const
1205 {
1206         par_type pit;
1207         Row const & row = getRowNearY(y - yo_, pit);
1208         bool bound = false;
1209
1210         int xx = x; // is modified by getColumnNearX
1211         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1212         cur.par() = pit;
1213         cur.pos() = pos;
1214         cur.boundary() = bound;
1215
1216         // try to descend into nested insets
1217         InsetBase * inset = checkInsetHit(x, y);
1218         lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
1219         if (!inset)
1220                 return 0;
1221
1222         // This should be just before or just behind the
1223         // cursor position set above.
1224         BOOST_ASSERT((pos != 0 && inset == pars_[pit].getInset(pos - 1))
1225                      || inset == pars_[pit].getInset(pos));
1226         // Make sure the cursor points to the position before
1227         // this inset.
1228         if (inset == pars_[pit].getInset(pos - 1))
1229                 --cur.pos();
1230         return inset->editXY(cur, x, y);
1231 }
1232
1233
1234 bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
1235 {
1236         if (cur.selection())
1237                 return false;
1238         if (cur.pos() == cur.lastpos())
1239                 return false;
1240         InsetBase * inset = cur.nextInset();
1241         if (!isHighlyEditableInset(inset))
1242                 return false;
1243         inset->edit(cur, front);
1244         return true;
1245 }
1246
1247
1248 void LyXText::cursorLeft(LCursor & cur)
1249 {
1250         if (cur.pos() != 0) {
1251                 bool boundary = cur.boundary();
1252                 setCursor(cur, cur.par(), cur.pos() - 1, true, false);
1253                 if (!checkAndActivateInset(cur, false)) {
1254                         if (false && !boundary &&
1255                                         bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
1256                                 setCursor(cur, cur.par(), cur.pos() + 1, true, true);
1257                 }
1258                 return;
1259         }
1260
1261         if (cur.par() != 0) {
1262                 // steps into the paragraph above
1263                 setCursor(cur, cur.par() - 1, getPar(cur.par() - 1).size());
1264         }
1265 }
1266
1267
1268 void LyXText::cursorRight(LCursor & cur)
1269 {
1270         if (false && cur.boundary()) {
1271                 setCursor(cur, cur.par(), cur.pos(), true, false);
1272                 return;
1273         }
1274
1275         if (cur.pos() != cur.lastpos()) {
1276                 if (!checkAndActivateInset(cur, true)) {
1277                         setCursor(cur, cur.par(), cur.pos() + 1, true, false);
1278                         if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
1279                                                          cur.pos()))
1280                                 setCursor(cur, cur.par(), cur.pos(), true, true);
1281                 }
1282                 return;
1283         }
1284
1285         if (cur.par() != cur.lastpar())
1286                 setCursor(cur, cur.par() + 1, 0);
1287 }
1288
1289
1290 void LyXText::cursorUp(LCursor & cur)
1291 {
1292         Row const & row = cur.textRow();
1293         int x = cur.x_target();
1294         int y = cursorY(cur.top()) - row.baseline() - 1;
1295         setCursorFromCoordinates(cur, x, y);
1296
1297         if (!cur.selection()) {
1298                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1299                 if (inset_hit && isHighlyEditableInset(inset_hit))
1300                         inset_hit->editXY(cur, cur.x_target(), y);
1301         }
1302 }
1303
1304
1305 void LyXText::cursorDown(LCursor & cur)
1306 {
1307         Row const & row = cur.textRow();
1308         int x = cur.x_target();
1309         int y = cursorY(cur.top()) - row.baseline() + row.height() + 1;
1310         setCursorFromCoordinates(cur, x, y);
1311
1312         if (!cur.selection()) {
1313                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1314                 if (inset_hit && isHighlyEditableInset(inset_hit))
1315                         inset_hit->editXY(cur, cur.x_target(), y);
1316         }
1317 }
1318
1319
1320 void LyXText::cursorUpParagraph(LCursor & cur)
1321 {
1322         if (cur.pos() > 0)
1323                 setCursor(cur, cur.par(), 0);
1324         else if (cur.par() != 0)
1325                 setCursor(cur, cur.par() - 1, 0);
1326 }
1327
1328
1329 void LyXText::cursorDownParagraph(LCursor & cur)
1330 {
1331         if (cur.par() != cur.lastpar())
1332                 setCursor(cur, cur.par() + 1, 0);
1333         else
1334                 setCursor(cur, cur.par(), cur.lastpos());
1335 }
1336
1337
1338 // fix the cursor `cur' after a characters has been deleted at `where'
1339 // position. Called by deleteEmptyParagraphMechanism
1340 void LyXText::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
1341 {
1342         // do notheing if cursor is not in the paragraph where the
1343         // deletion occured,
1344         if (cur.par() != where.par())
1345                 return;
1346
1347         // if cursor position is after the deletion place update it
1348         if (cur.pos() > where.pos())
1349                 --cur.pos();
1350
1351         // check also if we don't want to set the cursor on a spot behind the
1352         // pagragraph because we erased the last character.
1353         if (cur.pos() > cur.lastpos())
1354                 cur.pos() = cur.lastpos();
1355 }
1356
1357
1358 bool LyXText::deleteEmptyParagraphMechanism(LCursor & cur, LCursor const & old)
1359 {
1360         BOOST_ASSERT(cur.size() == old.size());
1361         // Would be wrong to delete anything if we have a selection.
1362         if (cur.selection())
1363                 return false;
1364
1365         //lyxerr[Debug::DEBUG] << "DEPM: cur:\n" << cur << "old:\n" << old << endl;
1366         Paragraph const & oldpar = pars_[old.par()];
1367
1368         // We allow all kinds of "mumbo-jumbo" when freespacing.
1369         if (oldpar.isFreeSpacing())
1370                 return false;
1371
1372         /* Ok I'll put some comments here about what is missing.
1373            I have fixed BackSpace (and thus Delete) to not delete
1374            double-spaces automagically. I have also changed Cut,
1375            Copy and Paste to hopefully do some sensible things.
1376            There are still some small problems that can lead to
1377            double spaces stored in the document file or space at
1378            the beginning of paragraphs(). This happens if you have
1379            the cursor between to spaces and then save. Or if you
1380            cut and paste and the selection have a space at the
1381            beginning and then save right after the paste. I am
1382            sure none of these are very hard to fix, but I will
1383            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1384            that I can get some feedback. (Lgb)
1385         */
1386
1387         // If old.pos() == 0 and old.pos()(1) == LineSeparator
1388         // delete the LineSeparator.
1389         // MISSING
1390
1391         // If old.pos() == 1 and old.pos()(0) == LineSeparator
1392         // delete the LineSeparator.
1393         // MISSING
1394
1395         // If the chars around the old cursor were spaces, delete one of them.
1396         if (old.par() != cur.par() || old.pos() != cur.pos()) {
1397
1398                 // Only if the cursor has really moved.
1399                 if (old.pos() > 0
1400                     && old.pos() < oldpar.size()
1401                     && oldpar.isLineSeparator(old.pos())
1402                     && oldpar.isLineSeparator(old.pos() - 1)) {
1403                         pars_[old.par()].erase(old.pos() - 1);
1404 #ifdef WITH_WARNINGS
1405 #warning This will not work anymore when we have multiple views of the same buffer
1406 // In this case, we will have to correct also the cursors held by
1407 // other bufferviews. It will probably be easier to do that in a more
1408 // automated way in CursorSlice code. (JMarc 26/09/2001)
1409 #endif
1410                         // correct all cursor parts
1411                         fixCursorAfterDelete(cur.top(), old.top());
1412 #ifdef WITH_WARNINGS
1413 #warning DEPM, look here
1414 #endif
1415                         //fixCursorAfterDelete(cur.anchor(), old.top());
1416                         return false;
1417                 }
1418         }
1419
1420         // only do our magic if we changed paragraph
1421         if (old.par() == cur.par())
1422                 return false;
1423
1424         // don't delete anything if this is the ONLY paragraph!
1425         if (pars_.size() == 1)
1426                 return false;
1427
1428         // Do not delete empty paragraphs with keepempty set.
1429         if (oldpar.allowEmpty())
1430                 return false;
1431
1432         // record if we have deleted a paragraph
1433         // we can't possibly have deleted a paragraph before this point
1434         bool deleted = false;
1435
1436         if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
1437                 // ok, we will delete something
1438                 CursorSlice tmpcursor;
1439
1440                 deleted = true;
1441
1442                 bool selection_position_was_oldcursor_position =
1443                         cur.anchor().par() == old.par() && cur.anchor().pos() == old.pos();
1444
1445                 // This is a bit of a overkill. We change the old and the cur par
1446                 // at max, certainly not everything in between...
1447                 recUndo(old.par(), cur.par());
1448
1449                 // Delete old par.
1450                 pars_.erase(pars_.begin() + old.par());
1451
1452                 // Update cursor par offset if necessary.
1453                 // Some 'iterator registration' would be nice that takes care of
1454                 // such events. Maybe even signal/slot?
1455                 if (cur.par() > old.par())
1456                         --cur.par();
1457 #ifdef WITH_WARNINGS
1458 #warning DEPM, look here
1459 #endif
1460 //              if (cur.anchor().par() > old.par())
1461 //                      --cur.anchor().par();
1462
1463                 if (selection_position_was_oldcursor_position) {
1464                         // correct selection
1465                         cur.resetAnchor();
1466                 }
1467         }
1468
1469         if (deleted)
1470                 return true;
1471
1472         if (pars_[old.par()].stripLeadingSpaces())
1473                 cur.resetAnchor();
1474
1475         return false;
1476 }
1477
1478
1479 ParagraphList & LyXText::paragraphs() const
1480 {
1481         return const_cast<ParagraphList &>(pars_);
1482 }
1483
1484
1485 void LyXText::recUndo(par_type first, par_type last) const
1486 {
1487         recordUndo(bv()->cursor(), Undo::ATOMIC, first, last);
1488 }
1489
1490
1491 void LyXText::recUndo(par_type par) const
1492 {
1493         recordUndo(bv()->cursor(), Undo::ATOMIC, par, par);
1494 }
1495
1496
1497 int defaultRowHeight()
1498 {
1499         return int(font_metrics::maxHeight(LyXFont(LyXFont::ALL_SANE)) *  1.2);
1500 }