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