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