]> git.lyx.org Git - lyx.git/blob - src/text2.C
fix some crash when we start selection outside an inset and get the
[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(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         lyxerr << "checkInsetHit: x: " << x << " y: " << y << endl;
117         lyxerr << "  pit: " << pit << " end: " << end << endl;
118         for (; pit != end; ++pit) {
119                 InsetList::const_iterator iit = pars_[pit].insetlist.begin();
120                 InsetList::const_iterator iend = pars_[pit].insetlist.end();
121                 for (; iit != iend; ++iit) {
122                         InsetBase * inset = iit->inset;
123 #if 1
124                         lyxerr << "examining inset " << inset << endl;
125                         if (theCoords.insets_.has(inset))
126                                 lyxerr
127                                         << " xo: " << inset->xo() << "..." << inset->xo() + inset->width()
128                                         << " yo: " << inset->yo() - inset->ascent() << "..."
129                                         << inset->yo() + inset->descent() << endl;
130                         else
131                                 lyxerr << " inset has no cached position";
132 #endif
133                         if (inset->covers(x, y)) {
134                                 lyxerr << "Hit inset: " << inset << endl;
135                                 return inset;
136                         }
137                 }
138         }
139         lyxerr << "No inset hit. " << endl;
140         return 0;
141 }
142
143
144
145 // Gets the fully instantiated font at a given position in a paragraph
146 // Basically the same routine as Paragraph::getFont() in paragraph.C.
147 // The difference is that this one is used for displaying, and thus we
148 // are allowed to make cosmetic improvements. For instance make footnotes
149 // smaller. (Asger)
150 LyXFont LyXText::getFont(par_type pit, pos_type pos) const
151 {
152         BOOST_ASSERT(pos >= 0);
153
154         LyXLayout_ptr const & layout = pars_[pit].layout();
155 #ifdef WITH_WARNINGS
156 #warning broken?
157 #endif
158         BufferParams const & params = bv()->buffer()->params();
159         pos_type const body_pos = pars_[pit].beginOfBody();
160
161         // We specialize the 95% common case:
162         if (!pars_[pit].getDepth()) {
163                 LyXFont f = pars_[pit].getFontSettings(params, pos);
164                 if (!isMainText())
165                         f.realize(font_);
166                 if (layout->labeltype == LABEL_MANUAL && pos < body_pos)
167                         return f.realize(layout->reslabelfont);
168                 else
169                         return f.realize(layout->resfont);
170         }
171
172         // The uncommon case need not be optimized as much
173         LyXFont layoutfont;
174         if (pos < body_pos)
175                 layoutfont = layout->labelfont;
176         else
177                 layoutfont = layout->font;
178
179         LyXFont font = pars_[pit].getFontSettings(params, pos);
180         font.realize(layoutfont);
181
182         if (!isMainText())
183                 font.realize(font_);
184
185         // Realize with the fonts of lesser depth.
186         //font.realize(outerFont(pit, paragraphs()));
187         font.realize(defaultfont_);
188
189         return font;
190 }
191
192
193 LyXFont LyXText::getLayoutFont(par_type pit) const
194 {
195         LyXLayout_ptr const & layout = pars_[pit].layout();
196
197         if (!pars_[pit].getDepth())
198                 return layout->resfont;
199
200         LyXFont font = layout->font;
201         // Realize with the fonts of lesser depth.
202         //font.realize(outerFont(pit, paragraphs()));
203         font.realize(defaultfont_);
204
205         return font;
206 }
207
208
209 LyXFont LyXText::getLabelFont(par_type pit) const
210 {
211         LyXLayout_ptr const & layout = pars_[pit].layout();
212
213         if (!pars_[pit].getDepth())
214                 return layout->reslabelfont;
215
216         LyXFont font = layout->labelfont;
217         // Realize with the fonts of lesser depth.
218         font.realize(outerFont(pit, paragraphs()));
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(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 pos = cur.selectionBegin();
461         DocIterator posend = cur.selectionEnd();
462
463         lyxerr[Debug::DEBUG] << "pos: " << pos << " posend: " << posend
464                              << endl;
465
466         BufferParams const & params = cur.buffer().params();
467
468         // Don't use forwardChar here as posend might have
469         // pos() == lastpos() and forwardChar would miss it.
470         for (; pos != posend; pos.forwardPos()) {
471                 if (pos.pos() != pos.lastpos()) {
472                         LyXFont f = getFont(pos.par(), pos.pos());
473                         f.update(font, params.language, toggleall);
474                         setCharFont(pos.par(), pos.pos(), f);
475                 }
476         }
477
478         redoParagraphs(beg, end + 1);
479 }
480
481
482 // the cursor set functions have a special mechanism. When they
483 // realize you left an empty paragraph, they will delete it.
484
485 void LyXText::cursorHome(LCursor & cur)
486 {
487         BOOST_ASSERT(this == cur.text());
488         setCursor(cur, cur.par(), cur.textRow().pos());
489 }
490
491
492 void LyXText::cursorEnd(LCursor & cur)
493 {
494         BOOST_ASSERT(this == cur.text());
495         // if not on the last row of the par, put the cursor before
496         // the final space
497         pos_type const end = cur.textRow().endpos();
498         setCursor(cur, cur.par(), end == cur.lastpos() ? end : end - 1);
499 }
500
501
502 void LyXText::cursorTop(LCursor & cur)
503 {
504         BOOST_ASSERT(this == cur.text());
505         setCursor(cur, 0, 0);
506 }
507
508
509 void LyXText::cursorBottom(LCursor & cur)
510 {
511         BOOST_ASSERT(this == cur.text());
512         setCursor(cur, cur.lastpar(), boost::prior(paragraphs().end())->size());
513 }
514
515
516 void LyXText::toggleFree(LCursor & cur, LyXFont const & font, bool toggleall)
517 {
518         BOOST_ASSERT(this == cur.text());
519         // If the mask is completely neutral, tell user
520         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
521                 // Could only happen with user style
522                 cur.message(_("No font change defined. "
523                         "Use Character under the Layout menu to define font change."));
524                 return;
525         }
526
527         // Try implicit word selection
528         // If there is a change in the language the implicit word selection
529         // is disabled.
530         CursorSlice resetCursor = cur.top();
531         bool implicitSelection =
532                 font.language() == ignore_language
533                 && font.number() == LyXFont::IGNORE
534                 && selectWordWhenUnderCursor(cur, lyx::WHOLE_WORD_STRICT);
535
536         // Set font
537         setFont(cur, font, toggleall);
538
539         // Implicit selections are cleared afterwards
540         // and cursor is set to the original position.
541         if (implicitSelection) {
542                 cur.clearSelection();
543                 cur.top() = resetCursor;
544                 cur.resetAnchor();
545         }
546 }
547
548
549 string LyXText::getStringToIndex(LCursor & cur)
550 {
551         BOOST_ASSERT(this == cur.text());
552         // Try implicit word selection
553         // If there is a change in the language the implicit word selection
554         // is disabled.
555         CursorSlice const reset_cursor = cur.top();
556         bool const implicitSelection =
557                 selectWordWhenUnderCursor(cur, lyx::PREVIOUS_WORD);
558
559         string idxstring;
560         if (!cur.selection())
561                 cur.message(_("Nothing to index!"));
562         else if (cur.selBegin().par() != cur.selEnd().par())
563                 cur.message(_("Cannot index more than one paragraph!"));
564         else
565                 idxstring = cur.selectionAsString(false);
566
567         // Reset cursors to their original position.
568         cur.top() = reset_cursor;
569         cur.resetAnchor();
570
571         // Clear the implicit selection.
572         if (implicitSelection)
573                 cur.clearSelection();
574
575         return idxstring;
576 }
577
578
579 void LyXText::setParagraph(LCursor & cur,
580         Spacing const & spacing, LyXAlignment align,
581         string const & labelwidthstring, bool noindent)
582 {
583         BOOST_ASSERT(cur.text());
584         // make sure that the depth behind the selection are restored, too
585         par_type undopit = undoSpan(cur.selEnd().par());
586         recUndo(cur.selBegin().par(), undopit - 1);
587
588         for (par_type pit = cur.selBegin().par(), end = cur.selEnd().par();
589                         pit <= end; ++pit) {
590                 Paragraph & par = pars_[pit];
591                 ParagraphParameters & params = par.params();
592                 params.spacing(spacing);
593
594                 // does the layout allow the new alignment?
595                 LyXLayout_ptr const & layout = par.layout();
596
597                 if (align == LYX_ALIGN_LAYOUT)
598                         align = layout->align;
599                 if (align & layout->alignpossible) {
600                         if (align == layout->align)
601                                 params.align(LYX_ALIGN_LAYOUT);
602                         else
603                                 params.align(align);
604                 }
605                 par.setLabelWidthString(labelwidthstring);
606                 params.noindent(noindent);
607         }
608
609         redoParagraphs(cur.selBegin().par(), undopit);
610 }
611
612
613 string expandLabel(LyXTextClass const & textclass,
614         LyXLayout_ptr const & layout, bool appendix)
615 {
616         string fmt = appendix ?
617                 layout->labelstring_appendix() : layout->labelstring();
618
619         // handle 'inherited level parts' in 'fmt',
620         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
621         size_t const i = fmt.find('@', 0);
622         if (i != string::npos) {
623                 size_t const j = fmt.find('@', i + 1);
624                 if (j != string::npos) {
625                         string parent(fmt, i + 1, j - i - 1);
626                         string label = expandLabel(textclass, textclass[parent], appendix);
627                         fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
628                 }
629         }
630
631         return textclass.counters().counterLabel(fmt);
632 }
633
634
635 namespace {
636
637 void incrementItemDepth(ParagraphList & pars, par_type pit, par_type first_pit)
638 {
639         int const cur_labeltype = pars[pit].layout()->labeltype;
640
641         if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
642                 return;
643
644         int const cur_depth = pars[pit].getDepth();
645
646         par_type prev_pit = pit - 1;
647         while (true) {
648                 int const prev_depth = pars[prev_pit].getDepth();
649                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
650                 if (prev_depth == 0 && cur_depth > 0) {
651                         if (prev_labeltype == cur_labeltype) {
652                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
653                         }
654                         break;
655                 } else if (prev_depth < cur_depth) {
656                         if (prev_labeltype == cur_labeltype) {
657                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
658                                 break;
659                         }
660                 } else if (prev_depth == cur_depth) {
661                         if (prev_labeltype == cur_labeltype) {
662                                 pars[pit].itemdepth = pars[prev_pit].itemdepth;
663                                 break;
664                         }
665                 }
666                 if (prev_pit == first_pit)
667                         break;
668
669                 --prev_pit;
670         }
671 }
672
673
674 void resetEnumCounterIfNeeded(ParagraphList & pars, par_type pit,
675         par_type firstpit, Counters & counters)
676 {
677         if (pit == firstpit)
678                 return;
679
680         int const cur_depth = pars[pit].getDepth();
681         par_type prev_pit = pit - 1;
682         while (true) {
683                 int const prev_depth = pars[prev_pit].getDepth();
684                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
685                 if (prev_depth <= cur_depth) {
686                         if (prev_labeltype != LABEL_ENUMERATE) {
687                                 switch (pars[pit].itemdepth) {
688                                 case 0:
689                                         counters.reset("enumi");
690                                 case 1:
691                                         counters.reset("enumii");
692                                 case 2:
693                                         counters.reset("enumiii");
694                                 case 3:
695                                         counters.reset("enumiv");
696                                 }
697                         }
698                         break;
699                 }
700
701                 if (prev_pit == firstpit)
702                         break;
703
704                 --prev_pit;
705         }
706 }
707
708 } // anon namespace
709
710
711 // set the counter of a paragraph. This includes the labels
712 void LyXText::setCounter(Buffer const & buf, par_type pit)
713 {
714         BufferParams const & bufparams = buf.params();
715         LyXTextClass const & textclass = bufparams.getLyXTextClass();
716         LyXLayout_ptr const & layout = pars_[pit].layout();
717         par_type first_pit = 0;
718         Counters & counters = textclass.counters();
719
720         // Always reset
721         pars_[pit].itemdepth = 0;
722
723         if (pit == first_pit) {
724                 pars_[pit].params().appendix(pars_[pit].params().startOfAppendix());
725         } else {
726                 pars_[pit].params().appendix(pars_[pit - 1].params().appendix());
727                 if (!pars_[pit].params().appendix() &&
728                     pars_[pit].params().startOfAppendix()) {
729                         pars_[pit].params().appendix(true);
730                         textclass.counters().reset();
731                 }
732
733                 // Maybe we have to increment the item depth.
734                 incrementItemDepth(pars_, pit, first_pit);
735         }
736
737         // erase what was there before
738         pars_[pit].params().labelString(string());
739
740         if (layout->margintype == MARGIN_MANUAL) {
741                 if (pars_[pit].params().labelWidthString().empty())
742                         pars_[pit].setLabelWidthString(layout->labelstring());
743         } else {
744                 pars_[pit].setLabelWidthString(string());
745         }
746
747         // is it a layout that has an automatic label?
748         if (layout->labeltype == LABEL_COUNTER) {
749                 BufferParams const & bufparams = buf.params();
750                 LyXTextClass const & textclass = bufparams.getLyXTextClass();
751                 counters.step(layout->counter);
752                 string label = expandLabel(textclass, layout, pars_[pit].params().appendix());
753                 pars_[pit].params().labelString(label);
754         } else if (layout->labeltype == LABEL_ITEMIZE) {
755                 // At some point of time we should do something more
756                 // clever here, like:
757                 //   pars_[pit].params().labelString(
758                 //    bufparams.user_defined_bullet(pars_[pit].itemdepth).getText());
759                 // for now, use a simple hardcoded label
760                 string itemlabel;
761                 switch (pars_[pit].itemdepth) {
762                 case 0:
763                         itemlabel = "*";
764                         break;
765                 case 1:
766                         itemlabel = "-";
767                         break;
768                 case 2:
769                         itemlabel = "@";
770                         break;
771                 case 3:
772                         itemlabel = "·";
773                         break;
774                 }
775
776                 pars_[pit].params().labelString(itemlabel);
777         } else if (layout->labeltype == LABEL_ENUMERATE) {
778                 // Maybe we have to reset the enumeration counter.
779                 resetEnumCounterIfNeeded(pars_, pit, first_pit, counters);
780
781                 // FIXME
782                 // Yes I know this is a really, really! bad solution
783                 // (Lgb)
784                 string enumcounter = "enum";
785
786                 switch (pars_[pit].itemdepth) {
787                 case 2:
788                         enumcounter += 'i';
789                 case 1:
790                         enumcounter += 'i';
791                 case 0:
792                         enumcounter += 'i';
793                         break;
794                 case 3:
795                         enumcounter += "iv";
796                         break;
797                 default:
798                         // not a valid enumdepth...
799                         break;
800                 }
801
802                 counters.step(enumcounter);
803
804                 pars_[pit].params().labelString(counters.enumLabel(enumcounter));
805         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
806                 counters.step("bibitem");
807                 int number = counters.value("bibitem");
808                 if (pars_[pit].bibitem()) {
809                         pars_[pit].bibitem()->setCounter(number);
810                         pars_[pit].params().labelString(layout->labelstring());
811                 }
812                 // In biblio should't be following counters but...
813         } else {
814                 string s = buf.B_(layout->labelstring());
815
816                 // the caption hack:
817                 if (layout->labeltype == LABEL_SENSITIVE) {
818                         par_type end = paragraphs().size();
819                         par_type tmppit = pit;
820                         InsetBase * in = 0;
821                         bool isOK = false;
822                         while (tmppit != end) {
823                                 in = pars_[tmppit].inInset();
824                                 if (in->lyxCode() == InsetBase::FLOAT_CODE ||
825                                     in->lyxCode() == InsetBase::WRAP_CODE) {
826                                         isOK = true;
827                                         break;
828                                 } else {
829                                         Paragraph const * owner = &ownerPar(buf, in);
830                                         tmppit = first_pit;
831                                         for ( ; tmppit != end; ++tmppit)
832                                                 if (&pars_[tmppit] == owner)
833                                                         break;
834                                 }
835                         }
836
837                         if (isOK) {
838                                 string type;
839
840                                 if (in->lyxCode() == InsetBase::FLOAT_CODE)
841                                         type = static_cast<InsetFloat*>(in)->params().type;
842                                 else if (in->lyxCode() == InsetBase::WRAP_CODE)
843                                         type = static_cast<InsetWrap*>(in)->params().type;
844                                 else
845                                         BOOST_ASSERT(false);
846
847                                 Floating const & fl = textclass.floats().getType(type);
848
849                                 counters.step(fl.type());
850
851                                 // Doesn't work... yet.
852                                 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
853                         } else {
854                                 // par->SetLayout(0);
855                                 // s = layout->labelstring;
856                                 s = _("Senseless: ");
857                         }
858                 }
859                 pars_[pit].params().labelString(s);
860
861         }
862 }
863
864
865 // Updates all counters.
866 void LyXText::updateCounters()
867 {
868         // start over
869         bv()->buffer()->params().getLyXTextClass().counters().reset();
870
871         bool update_pos = false;
872
873         par_type end = paragraphs().size();
874         for (par_type pit = 0; pit != end; ++pit) {
875                 string const oldLabel = pars_[pit].params().labelString();
876                 size_t maxdepth = 0;
877                 if (pit != 0)
878                         maxdepth = pars_[pit - 1].getMaxDepthAfter();
879
880                 if (pars_[pit].params().depth() > maxdepth)
881                         pars_[pit].params().depth(maxdepth);
882
883                 // setCounter can potentially change the labelString.
884                 setCounter(*bv()->buffer(), pit);
885                 string const & newLabel = pars_[pit].params().labelString();
886                 if (oldLabel != newLabel) {
887                         //lyxerr[Debug::DEBUG] << "changing labels: old: " << oldLabel << " new: "
888                         //      << newLabel << endl;
889                         redoParagraphInternal(pit);
890                         update_pos = true;
891                 }
892         }
893         if (update_pos)
894                 updateParPositions();
895 }
896
897
898 // this really should just inset the inset and not move the cursor.
899 void LyXText::insertInset(LCursor & cur, InsetBase * inset)
900 {
901         BOOST_ASSERT(this == cur.text());
902         BOOST_ASSERT(inset);
903         cur.paragraph().insertInset(cur.pos(), inset);
904         redoParagraph(cur);
905 }
906
907
908 // needed to insert the selection
909 void LyXText::insertStringAsLines(LCursor & cur, string const & str)
910 {
911         par_type pit = cur.par();
912         par_type endpit = cur.par() + 1;
913         pos_type pos = cur.pos();
914         recordUndo(cur);
915
916         // only to be sure, should not be neccessary
917         cur.clearSelection();
918         cur.buffer().insertStringAsLines(pars_, pit, pos, current_font, str);
919
920         redoParagraphs(cur.par(), endpit);
921         cur.resetAnchor();
922         setCursor(cur, cur.par(), pos);
923         cur.setSelection();
924 }
925
926
927 // turn double CR to single CR, others are converted into one
928 // blank. Then insertStringAsLines is called
929 void LyXText::insertStringAsParagraphs(LCursor & cur, string const & str)
930 {
931         string linestr = str;
932         bool newline_inserted = false;
933
934         for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
935                 if (linestr[i] == '\n') {
936                         if (newline_inserted) {
937                                 // we know that \r will be ignored by
938                                 // insertStringAsLines. Of course, it is a dirty
939                                 // trick, but it works...
940                                 linestr[i - 1] = '\r';
941                                 linestr[i] = '\n';
942                         } else {
943                                 linestr[i] = ' ';
944                                 newline_inserted = true;
945                         }
946                 } else if (IsPrintable(linestr[i])) {
947                         newline_inserted = false;
948                 }
949         }
950         insertStringAsLines(cur, linestr);
951 }
952
953
954 bool LyXText::setCursor(LCursor & cur, par_type par, pos_type pos,
955         bool setfont, bool boundary)
956 {
957         LCursor old = cur;
958         setCursorIntern(cur, par, pos, setfont, boundary);
959         return deleteEmptyParagraphMechanism(cur, old);
960 }
961
962
963 void LyXText::setCursor(CursorSlice & cur, par_type par,
964         pos_type pos, bool boundary)
965 {
966         BOOST_ASSERT(par != int(paragraphs().size()));
967
968         cur.par() = par;
969         cur.pos() = pos;
970         cur.boundary() = boundary;
971
972         // no rows, no fun...
973         if (paragraphs().begin()->rows.empty())
974                 return;
975
976         // now some strict checking
977         Paragraph & para = getPar(par);
978         Row const & row = *para.getRow(pos);
979         pos_type const end = row.endpos();
980
981         // None of these should happen, but we're scaredy-cats
982         if (pos < 0) {
983                 lyxerr << "dont like -1" << endl;
984                 BOOST_ASSERT(false);
985         }
986
987         if (pos > para.size()) {
988                 lyxerr << "dont like 1, pos: " << pos
989                        << " size: " << para.size()
990                        << " row.pos():" << row.pos()
991                        << " par: " << par << endl;
992                 BOOST_ASSERT(false);
993         }
994
995         if (pos > end) {
996                 lyxerr << "dont like 2, pos: " << pos
997                        << " size: " << para.size()
998                        << " row.pos():" << row.pos()
999                        << " par: " << par << endl;
1000                 // This shouldn't happen.
1001                 BOOST_ASSERT(false);
1002         }
1003
1004         if (pos < row.pos()) {
1005                 lyxerr << "dont like 3 please report pos:" << pos
1006                        << " size: " << para.size()
1007                        << " row.pos():" << row.pos()
1008                        << " par: " << par << endl;
1009                 BOOST_ASSERT(false);
1010         }
1011 }
1012
1013
1014 void LyXText::setCursorIntern(LCursor & cur,
1015         par_type par, pos_type pos, bool setfont, bool boundary)
1016 {
1017         setCursor(cur.top(), par, pos, boundary);
1018         cur.x_target() = cursorX(cur.top());
1019         if (setfont)
1020                 setCurrentFont(cur);
1021 }
1022
1023
1024 void LyXText::setCurrentFont(LCursor & cur)
1025 {
1026         BOOST_ASSERT(this == cur.text());
1027         pos_type pos = cur.pos();
1028         par_type pit = cur.par();
1029
1030         if (cur.boundary() && pos > 0)
1031                 --pos;
1032
1033         if (pos > 0) {
1034                 if (pos == cur.lastpos())
1035                         --pos;
1036                 else // potentional bug... BUG (Lgb)
1037                         if (pars_[pit].isSeparator(pos)) {
1038                                 if (pos > cur.textRow().pos() &&
1039                                     bidi.level(pos) % 2 ==
1040                                     bidi.level(pos - 1) % 2)
1041                                         --pos;
1042                                 else if (pos + 1 < cur.lastpos())
1043                                         ++pos;
1044                         }
1045         }
1046
1047         BufferParams const & bufparams = cur.buffer().params();
1048         current_font = pars_[pit].getFontSettings(bufparams, pos);
1049         real_current_font = getFont(pit, pos);
1050
1051         if (cur.pos() == cur.lastpos()
1052             && bidi.isBoundary(cur.buffer(), pars_[pit], cur.pos())
1053             && !cur.boundary()) {
1054                 Language const * lang = pars_[pit].getParLanguage(bufparams);
1055                 current_font.setLanguage(lang);
1056                 current_font.setNumber(LyXFont::OFF);
1057                 real_current_font.setLanguage(lang);
1058                 real_current_font.setNumber(LyXFont::OFF);
1059         }
1060 }
1061
1062
1063 // x is an absolute screen coord
1064 // returns the column near the specified x-coordinate of the row
1065 // x is set to the real beginning of this column
1066 pos_type LyXText::getColumnNearX(par_type pit,
1067         Row const & row, int & x, bool & boundary) const
1068 {
1069         x -= xo_;
1070         RowMetrics const r = computeRowMetrics(pit, row);
1071
1072         pos_type vc = row.pos();
1073         pos_type end = row.endpos();
1074         pos_type c = 0;
1075         LyXLayout_ptr const & layout = pars_[pit].layout();
1076
1077         bool left_side = false;
1078
1079         pos_type body_pos = pars_[pit].beginOfBody();
1080
1081         double tmpx = r.x;
1082         double last_tmpx = tmpx;
1083
1084         if (body_pos > 0 &&
1085             (body_pos > end || !pars_[pit].isLineSeparator(body_pos - 1)))
1086                 body_pos = 0;
1087
1088         // check for empty row
1089         if (vc == end) {
1090                 x = int(tmpx) + xo_;
1091                 return 0;
1092         }
1093
1094         while (vc < end && tmpx <= x) {
1095                 c = bidi.vis2log(vc);
1096                 last_tmpx = tmpx;
1097                 if (body_pos > 0 && c == body_pos - 1) {
1098                         tmpx += r.label_hfill +
1099                                 font_metrics::width(layout->labelsep, getLabelFont(pit));
1100                         if (pars_[pit].isLineSeparator(body_pos - 1))
1101                                 tmpx -= singleWidth(pit, body_pos - 1);
1102                 }
1103
1104                 if (hfillExpansion(pars_[pit], row, c)) {
1105                         tmpx += singleWidth(pit, c);
1106                         if (c >= body_pos)
1107                                 tmpx += r.hfill;
1108                         else
1109                                 tmpx += r.label_hfill;
1110                 } else if (pars_[pit].isSeparator(c)) {
1111                         tmpx += singleWidth(pit, c);
1112                         if (c >= body_pos)
1113                                 tmpx += r.separator;
1114                 } else {
1115                         tmpx += singleWidth(pit, c);
1116                 }
1117                 ++vc;
1118         }
1119
1120         if ((tmpx + last_tmpx) / 2 > x) {
1121                 tmpx = last_tmpx;
1122                 left_side = true;
1123         }
1124
1125         BOOST_ASSERT(vc <= end);  // This shouldn't happen.
1126
1127         boundary = false;
1128         // This (rtl_support test) is not needed, but gives
1129         // some speedup if rtl_support == false
1130         bool const lastrow = lyxrc.rtl_support && row.endpos() == pars_[pit].size();
1131
1132         // If lastrow is false, we don't need to compute
1133         // the value of rtl.
1134         bool const rtl = lastrow ? isRTL(pars_[pit]) : false;
1135         if (lastrow &&
1136                  ((rtl  &&  left_side && vc == row.pos() && x < tmpx - 5) ||
1137                   (!rtl && !left_side && vc == end  && x > tmpx + 5)))
1138                 c = end;
1139         else if (vc == row.pos()) {
1140                 c = bidi.vis2log(vc);
1141                 if (bidi.level(c) % 2 == 1)
1142                         ++c;
1143         } else {
1144                 c = bidi.vis2log(vc - 1);
1145                 bool const rtl = (bidi.level(c) % 2 == 1);
1146                 if (left_side == rtl) {
1147                         ++c;
1148                         boundary = bidi.isBoundary(*bv()->buffer(), pars_[pit], c);
1149                 }
1150         }
1151
1152         if (row.pos() < end && c >= end && pars_[pit].isNewline(end - 1)) {
1153                 if (bidi.level(end -1) % 2 == 0)
1154                         tmpx -= singleWidth(pit, end - 1);
1155                 else
1156                         tmpx += singleWidth(pit, end - 1);
1157                 c = end - 1;
1158         }
1159
1160         x = int(tmpx) + xo_;
1161         return c - row.pos();
1162 }
1163
1164
1165 // x,y are absolute coordinates
1166 // sets cursor only within this LyXText
1167 void LyXText::setCursorFromCoordinates(LCursor & cur, int x, int y)
1168 {
1169         x -= xo_;
1170         y -= yo_;
1171         par_type pit;
1172         Row const & row = getRowNearY(y, pit);
1173         lyxerr[Debug::DEBUG] << "setCursorFromCoordinates:: hit row at: "
1174                              << row.pos() << endl;
1175         bool bound = false;
1176         int xx = x + xo_; // getRowNearX get absolute x coords
1177         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1178         setCursor(cur, pit, pos, true, bound);
1179 }
1180
1181
1182 // x,y are absolute screen coordinates
1183 // sets cursor recursively descending into nested editable insets
1184 InsetBase * LyXText::editXY(LCursor & cur, int x, int y) const
1185 {
1186         par_type pit;
1187         Row const & row = getRowNearY(y - yo_, pit);
1188         bool bound = false;
1189
1190         int xx = x; // is modified by getColumnNearX
1191         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1192         cur.par() = pit;
1193         cur.pos() = pos;
1194         cur.boundary() = bound;
1195
1196         // try to descend into nested insets
1197         InsetBase * inset = checkInsetHit(x, y);
1198         lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
1199         if (!inset)
1200                 return 0;
1201
1202         // This should be just before or just behind the
1203         // cursor position set above.
1204         BOOST_ASSERT((pos != 0 && inset == pars_[pit].getInset(pos - 1))
1205                      || inset == pars_[pit].getInset(pos));
1206         // Make sure the cursor points to the position before
1207         // this inset.
1208         if (inset == pars_[pit].getInset(pos - 1))
1209                 --cur.pos();
1210         return inset->editXY(cur, x, y);
1211 }
1212
1213
1214 bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
1215 {
1216         if (cur.selection())
1217                 return false;
1218         if (cur.pos() == cur.lastpos())
1219                 return false;
1220         InsetBase * inset = cur.nextInset();
1221         if (!isHighlyEditableInset(inset))
1222                 return false;
1223         inset->edit(cur, front);
1224         return true;
1225 }
1226
1227
1228 void LyXText::cursorLeft(LCursor & cur)
1229 {
1230         if (cur.pos() != 0) {
1231                 bool boundary = cur.boundary();
1232                 setCursor(cur, cur.par(), cur.pos() - 1, true, false);
1233                 if (!checkAndActivateInset(cur, false)) {
1234                         if (false && !boundary &&
1235                                         bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
1236                                 setCursor(cur, cur.par(), cur.pos() + 1, true, true);
1237                 }
1238                 return;
1239         }
1240
1241         if (cur.par() != 0) {
1242                 // steps into the paragraph above
1243                 setCursor(cur, cur.par() - 1, getPar(cur.par() - 1).size());
1244         }
1245 }
1246
1247
1248 void LyXText::cursorRight(LCursor & cur)
1249 {
1250         if (false && cur.boundary()) {
1251                 setCursor(cur, cur.par(), cur.pos(), true, false);
1252                 return;
1253         }
1254
1255         if (cur.pos() != cur.lastpos()) {
1256                 if (!checkAndActivateInset(cur, true)) {
1257                         setCursor(cur, cur.par(), cur.pos() + 1, true, false);
1258                         if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
1259                                                          cur.pos()))
1260                                 setCursor(cur, cur.par(), cur.pos(), true, true);
1261                 }
1262                 return;
1263         }
1264
1265         if (cur.par() != cur.lastpar())
1266                 setCursor(cur, cur.par() + 1, 0);
1267 }
1268
1269
1270 void LyXText::cursorUp(LCursor & cur)
1271 {
1272         Row const & row = cur.textRow();
1273         int x = cur.x_target();
1274         int y = cursorY(cur.top()) - row.baseline() - 1;
1275         setCursorFromCoordinates(cur, x, y);
1276
1277         if (!cur.selection()) {
1278                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1279                 if (inset_hit && isHighlyEditableInset(inset_hit))
1280                         inset_hit->editXY(cur, cur.x_target(), y);
1281         }
1282 }
1283
1284
1285 void LyXText::cursorDown(LCursor & cur)
1286 {
1287         Row const & row = cur.textRow();
1288         int x = cur.x_target();
1289         int y = cursorY(cur.top()) - row.baseline() + row.height() + 1;
1290         setCursorFromCoordinates(cur, x, y);
1291
1292         if (!cur.selection()) {
1293                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1294                 if (inset_hit && isHighlyEditableInset(inset_hit))
1295                         inset_hit->editXY(cur, cur.x_target(), y);
1296         }
1297 }
1298
1299
1300 void LyXText::cursorUpParagraph(LCursor & cur)
1301 {
1302         if (cur.pos() > 0)
1303                 setCursor(cur, cur.par(), 0);
1304         else if (cur.par() != 0)
1305                 setCursor(cur, cur.par() - 1, 0);
1306 }
1307
1308
1309 void LyXText::cursorDownParagraph(LCursor & cur)
1310 {
1311         if (cur.par() != cur.lastpar())
1312                 setCursor(cur, cur.par() + 1, 0);
1313         else
1314                 setCursor(cur, cur.par(), cur.lastpos());
1315 }
1316
1317
1318 // fix the cursor `cur' after a characters has been deleted at `where'
1319 // position. Called by deleteEmptyParagraphMechanism
1320 void LyXText::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
1321 {
1322         // do notheing if cursor is not in the paragraph where the
1323         // deletion occured,
1324         if (cur.par() != where.par())
1325                 return;
1326
1327         // if cursor position is after the deletion place update it
1328         if (cur.pos() > where.pos())
1329                 --cur.pos();
1330
1331         // check also if we don't want to set the cursor on a spot behind the
1332         // pagragraph because we erased the last character.
1333         if (cur.pos() > cur.lastpos())
1334                 cur.pos() = cur.lastpos();
1335 }
1336
1337
1338 bool LyXText::deleteEmptyParagraphMechanism(LCursor & cur, LCursor const & old)
1339 {
1340         BOOST_ASSERT(cur.size() == old.size());
1341         // Would be wrong to delete anything if we have a selection.
1342         if (cur.selection())
1343                 return false;
1344
1345         //lyxerr[Debug::DEBUG] << "DEPM: cur:\n" << cur << "old:\n" << old << endl;
1346         Paragraph const & oldpar = pars_[old.par()];
1347
1348         // We allow all kinds of "mumbo-jumbo" when freespacing.
1349         if (oldpar.isFreeSpacing())
1350                 return false;
1351
1352         /* Ok I'll put some comments here about what is missing.
1353            I have fixed BackSpace (and thus Delete) to not delete
1354            double-spaces automagically. I have also changed Cut,
1355            Copy and Paste to hopefully do some sensible things.
1356            There are still some small problems that can lead to
1357            double spaces stored in the document file or space at
1358            the beginning of paragraphs(). This happens if you have
1359            the cursor between to spaces and then save. Or if you
1360            cut and paste and the selection have a space at the
1361            beginning and then save right after the paste. I am
1362            sure none of these are very hard to fix, but I will
1363            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1364            that I can get some feedback. (Lgb)
1365         */
1366
1367         // If old.pos() == 0 and old.pos()(1) == LineSeparator
1368         // delete the LineSeparator.
1369         // MISSING
1370
1371         // If old.pos() == 1 and old.pos()(0) == LineSeparator
1372         // delete the LineSeparator.
1373         // MISSING
1374
1375         // If the chars around the old cursor were spaces, delete one of them.
1376         if (old.par() != cur.par() || old.pos() != cur.pos()) {
1377
1378                 // Only if the cursor has really moved.
1379                 if (old.pos() > 0
1380                     && old.pos() < oldpar.size()
1381                     && oldpar.isLineSeparator(old.pos())
1382                     && oldpar.isLineSeparator(old.pos() - 1)) {
1383                         pars_[old.par()].erase(old.pos() - 1);
1384 #ifdef WITH_WARNINGS
1385 #warning This will not work anymore when we have multiple views of the same buffer
1386 // In this case, we will have to correct also the cursors held by
1387 // other bufferviews. It will probably be easier to do that in a more
1388 // automated way in CursorSlice code. (JMarc 26/09/2001)
1389 #endif
1390                         // correct all cursor parts
1391                         fixCursorAfterDelete(cur.top(), old.top());
1392 #ifdef WITH_WARNINGS
1393 #warning DEPM, look here
1394 #endif
1395                         //fixCursorAfterDelete(cur.anchor(), old.top());
1396                         return false;
1397                 }
1398         }
1399
1400         // only do our magic if we changed paragraph
1401         if (old.par() == cur.par())
1402                 return false;
1403
1404         // don't delete anything if this is the ONLY paragraph!
1405         if (pars_.size() == 1)
1406                 return false;
1407
1408         // Do not delete empty paragraphs with keepempty set.
1409         if (oldpar.allowEmpty())
1410                 return false;
1411
1412         // record if we have deleted a paragraph
1413         // we can't possibly have deleted a paragraph before this point
1414         bool deleted = false;
1415
1416         if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
1417                 // ok, we will delete something
1418                 CursorSlice tmpcursor;
1419
1420                 deleted = true;
1421
1422                 bool selection_position_was_oldcursor_position =
1423                         cur.anchor().par() == old.par() && cur.anchor().pos() == old.pos();
1424
1425                 // This is a bit of a overkill. We change the old and the cur par
1426                 // at max, certainly not everything in between...
1427                 recUndo(old.par(), cur.par());
1428
1429                 // Delete old par.
1430                 pars_.erase(pars_.begin() + old.par());
1431
1432                 // Update cursor par offset if necessary.
1433                 // Some 'iterator registration' would be nice that takes care of
1434                 // such events. Maybe even signal/slot?
1435                 if (cur.par() > old.par())
1436                         --cur.par();
1437 #ifdef WITH_WARNINGS
1438 #warning DEPM, look here
1439 #endif
1440 //              if (cur.anchor().par() > old.par())
1441 //                      --cur.anchor().par();
1442
1443                 if (selection_position_was_oldcursor_position) {
1444                         // correct selection
1445                         cur.resetAnchor();
1446                 }
1447         }
1448
1449         if (deleted)
1450                 return true;
1451
1452         if (pars_[old.par()].stripLeadingSpaces())
1453                 cur.resetAnchor();
1454
1455         return false;
1456 }
1457
1458
1459 ParagraphList & LyXText::paragraphs() const
1460 {
1461         return const_cast<ParagraphList &>(pars_);
1462 }
1463
1464
1465 void LyXText::recUndo(par_type first, par_type last) const
1466 {
1467         recordUndo(bv()->cursor(), Undo::ATOMIC, first, last);
1468 }
1469
1470
1471 void LyXText::recUndo(par_type par) const
1472 {
1473         recordUndo(bv()->cursor(), Undo::ATOMIC, par, par);
1474 }
1475
1476
1477 int defaultRowHeight()
1478 {
1479         return int(font_metrics::maxHeight(LyXFont(LyXFont::ALL_SANE)) *  1.2);
1480 }