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