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