]> git.lyx.org Git - lyx.git/blob - src/text2.C
fix bug 1598 (crash on cursor up/down in script)
[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                                 if (in &&
795                                     (in->lyxCode() == InsetBase::FLOAT_CODE ||
796                                      in->lyxCode() == InsetBase::WRAP_CODE)) {
797                                         isOK = true;
798                                         break;
799                                 }
800 #ifdef WITH_WARNINGS
801 #warning replace this code by something that works
802 // This code does not work because we have currently no way to move up
803 // in the hierarchy of insets (JMarc 16/08/2004)
804 #endif
805 #if 0
806 /* I think this code is supposed to be useful when one has a caption
807  * in a minipage in a figure inset. We need to go up to be able to see
808  * that the caption should use "Figure" as label
809  */
810                                 else {
811                                         Paragraph const * owner = &ownerPar(buf, in);
812                                         tmppit = 0;
813                                         for ( ; tmppit != end; ++tmppit)
814                                                 if (&pars_[tmppit] == owner)
815                                                         break;
816                                 }
817 #else
818                                 ++tmppit;
819 #endif
820                         }
821
822                         if (isOK) {
823                                 string type;
824
825                                 if (in->lyxCode() == InsetBase::FLOAT_CODE)
826                                         type = static_cast<InsetFloat*>(in)->params().type;
827                                 else if (in->lyxCode() == InsetBase::WRAP_CODE)
828                                         type = static_cast<InsetWrap*>(in)->params().type;
829                                 else
830                                         BOOST_ASSERT(false);
831
832                                 Floating const & fl = textclass.floats().getType(type);
833
834                                 counters.step(fl.type());
835
836                                 // Doesn't work... yet.
837                                 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
838                         } else {
839                                 // par->SetLayout(0);
840                                 // s = layout->labelstring;
841                                 s = _("Senseless: ");
842                         }
843                 }
844                 par.params().labelString(s);
845
846         }
847 }
848
849
850 // Updates all counters.
851 void LyXText::updateCounters()
852 {
853         // start over
854         bv()->buffer()->params().getLyXTextClass().counters().reset();
855
856         bool update_pos = false;
857
858         pit_type end = paragraphs().size();
859         for (pit_type pit = 0; pit != end; ++pit) {
860                 string const oldLabel = pars_[pit].params().labelString();
861                 size_t maxdepth = 0;
862                 if (pit != 0)
863                         maxdepth = pars_[pit - 1].getMaxDepthAfter();
864
865                 if (pars_[pit].params().depth() > maxdepth)
866                         pars_[pit].params().depth(maxdepth);
867
868                 // setCounter can potentially change the labelString.
869                 setCounter(*bv()->buffer(), pit);
870                 string const & newLabel = pars_[pit].params().labelString();
871                 if (oldLabel != newLabel) {
872                         //lyxerr[Debug::DEBUG] << "changing labels: old: " << oldLabel << " new: "
873                         //      << newLabel << endl;
874                         update_pos = true;
875                 }
876         }
877 }
878
879
880 // this really should just insert the inset and not move the cursor.
881 void LyXText::insertInset(LCursor & cur, InsetBase * inset)
882 {
883         BOOST_ASSERT(this == cur.text());
884         BOOST_ASSERT(inset);
885         cur.paragraph().insertInset(cur.pos(), inset);
886 }
887
888
889 // needed to insert the selection
890 void LyXText::insertStringAsLines(LCursor & cur, string const & str)
891 {
892         pit_type pit = cur.pit();
893         pos_type pos = cur.pos();
894         recordUndo(cur);
895
896         // only to be sure, should not be neccessary
897         cur.clearSelection();
898         cur.buffer().insertStringAsLines(pars_, pit, pos, current_font, str,
899                 autoBreakRows_);
900
901         cur.resetAnchor();
902         setCursor(cur, cur.pit(), pos);
903         cur.setSelection();
904 }
905
906
907 // turn double CR to single CR, others are converted into one
908 // blank. Then insertStringAsLines is called
909 void LyXText::insertStringAsParagraphs(LCursor & cur, string const & str)
910 {
911         string linestr = str;
912         bool newline_inserted = false;
913
914         for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
915                 if (linestr[i] == '\n') {
916                         if (newline_inserted) {
917                                 // we know that \r will be ignored by
918                                 // insertStringAsLines. Of course, it is a dirty
919                                 // trick, but it works...
920                                 linestr[i - 1] = '\r';
921                                 linestr[i] = '\n';
922                         } else {
923                                 linestr[i] = ' ';
924                                 newline_inserted = true;
925                         }
926                 } else if (IsPrintable(linestr[i])) {
927                         newline_inserted = false;
928                 }
929         }
930         insertStringAsLines(cur, linestr);
931 }
932
933
934 bool LyXText::setCursor(LCursor & cur, pit_type par, pos_type pos,
935         bool setfont, bool boundary)
936 {
937         LCursor old = cur;
938         setCursorIntern(cur, par, pos, setfont, boundary);
939         return deleteEmptyParagraphMechanism(cur, old);
940 }
941
942
943 void LyXText::setCursor(CursorSlice & cur, pit_type par,
944         pos_type pos, bool boundary)
945 {
946         BOOST_ASSERT(par != int(paragraphs().size()));
947         cur.pit() = par;
948         cur.pos() = pos;
949         cur.boundary() = boundary;
950
951         // now some strict checking
952         Paragraph & para = getPar(par);
953
954         // None of these should happen, but we're scaredy-cats
955         if (pos < 0) {
956                 lyxerr << "dont like -1" << endl;
957                 BOOST_ASSERT(false);
958         }
959
960         if (pos > para.size()) {
961                 lyxerr << "dont like 1, pos: " << pos
962                        << " size: " << para.size()
963                        << " par: " << par << endl;
964                 BOOST_ASSERT(false);
965         }
966 }
967
968
969 void LyXText::setCursorIntern(LCursor & cur,
970         pit_type par, pos_type pos, bool setfont, bool boundary)
971 {
972         setCursor(cur.top(), par, pos, boundary);
973         cur.setTargetX();
974         if (setfont)
975                 setCurrentFont(cur);
976 }
977
978
979 void LyXText::setCurrentFont(LCursor & cur)
980 {
981         BOOST_ASSERT(this == cur.text());
982         pos_type pos = cur.pos();
983         Paragraph & par = cur.paragraph();
984
985         if (cur.boundary() && pos > 0)
986                 --pos;
987
988         if (pos > 0) {
989                 if (pos == cur.lastpos())
990                         --pos;
991                 else // potentional bug... BUG (Lgb)
992                         if (par.isSeparator(pos)) {
993                                 if (pos > cur.textRow().pos() &&
994                                     bidi.level(pos) % 2 ==
995                                     bidi.level(pos - 1) % 2)
996                                         --pos;
997                                 else if (pos + 1 < cur.lastpos())
998                                         ++pos;
999                         }
1000         }
1001
1002         BufferParams const & bufparams = cur.buffer().params();
1003         current_font = par.getFontSettings(bufparams, pos);
1004         real_current_font = getFont(par, pos);
1005
1006         if (cur.pos() == cur.lastpos()
1007             && bidi.isBoundary(cur.buffer(), par, cur.pos())
1008             && !cur.boundary()) {
1009                 Language const * lang = par.getParLanguage(bufparams);
1010                 current_font.setLanguage(lang);
1011                 current_font.setNumber(LyXFont::OFF);
1012                 real_current_font.setLanguage(lang);
1013                 real_current_font.setNumber(LyXFont::OFF);
1014         }
1015 }
1016
1017
1018 // x is an absolute screen coord
1019 // returns the column near the specified x-coordinate of the row
1020 // x is set to the real beginning of this column
1021 pos_type LyXText::getColumnNearX(pit_type const pit,
1022         Row const & row, int & x, bool & boundary) const
1023 {
1024         int const xo = theCoords.get(this, pit).x_;
1025         x -= xo;
1026         RowMetrics const r = computeRowMetrics(pit, row);
1027         Paragraph const & par = pars_[pit];
1028
1029         pos_type vc = row.pos();
1030         pos_type end = row.endpos();
1031         pos_type c = 0;
1032         LyXLayout_ptr const & layout = par.layout();
1033
1034         bool left_side = false;
1035
1036         pos_type body_pos = par.beginOfBody();
1037
1038         double tmpx = r.x;
1039         double last_tmpx = tmpx;
1040
1041         if (body_pos > 0 &&
1042             (body_pos > end || !par.isLineSeparator(body_pos - 1)))
1043                 body_pos = 0;
1044
1045         // check for empty row
1046         if (vc == end) {
1047                 x = int(tmpx) + xo;
1048                 return 0;
1049         }
1050
1051         while (vc < end && tmpx <= x) {
1052                 c = bidi.vis2log(vc);
1053                 last_tmpx = tmpx;
1054                 if (body_pos > 0 && c == body_pos - 1) {
1055                         tmpx += r.label_hfill +
1056                                 font_metrics::width(layout->labelsep, getLabelFont(par));
1057                         if (par.isLineSeparator(body_pos - 1))
1058                                 tmpx -= singleWidth(par, body_pos - 1);
1059                 }
1060
1061                 if (hfillExpansion(par, row, c)) {
1062                         tmpx += singleWidth(par, c);
1063                         if (c >= body_pos)
1064                                 tmpx += r.hfill;
1065                         else
1066                                 tmpx += r.label_hfill;
1067                 } else if (par.isSeparator(c)) {
1068                         tmpx += singleWidth(par, c);
1069                         if (c >= body_pos)
1070                                 tmpx += r.separator;
1071                 } else {
1072                         tmpx += singleWidth(par, c);
1073                 }
1074                 ++vc;
1075         }
1076
1077         if ((tmpx + last_tmpx) / 2 > x) {
1078                 tmpx = last_tmpx;
1079                 left_side = true;
1080         }
1081
1082         BOOST_ASSERT(vc <= end);  // This shouldn't happen.
1083
1084         boundary = false;
1085         // This (rtl_support test) is not needed, but gives
1086         // some speedup if rtl_support == false
1087         bool const lastrow = lyxrc.rtl_support && row.endpos() == par.size();
1088
1089         // If lastrow is false, we don't need to compute
1090         // the value of rtl.
1091         bool const rtl = lastrow ? isRTL(par) : false;
1092         if (lastrow &&
1093                  ((rtl  &&  left_side && vc == row.pos() && x < tmpx - 5) ||
1094                   (!rtl && !left_side && vc == end  && x > tmpx + 5)))
1095                 c = end;
1096         else if (vc == row.pos()) {
1097                 c = bidi.vis2log(vc);
1098                 if (bidi.level(c) % 2 == 1)
1099                         ++c;
1100         } else {
1101                 c = bidi.vis2log(vc - 1);
1102                 bool const rtl = (bidi.level(c) % 2 == 1);
1103                 if (left_side == rtl) {
1104                         ++c;
1105                         boundary = bidi.isBoundary(*bv()->buffer(), par, c);
1106                 }
1107         }
1108
1109         if (row.pos() < end && c >= end && par.isNewline(end - 1)) {
1110                 if (bidi.level(end -1) % 2 == 0)
1111                         tmpx -= singleWidth(par, end - 1);
1112                 else
1113                         tmpx += singleWidth(par, end - 1);
1114                 c = end - 1;
1115         }
1116
1117         x = int(tmpx) + xo;
1118         return c - row.pos();
1119 }
1120
1121
1122 // y is screen coordinate
1123 pit_type LyXText::getPitNearY(int y) const
1124 {
1125         BOOST_ASSERT(!paragraphs().empty());
1126         BOOST_ASSERT(theCoords.getParPos().find(this) != theCoords.getParPos().end());
1127         CoordCache::InnerParPosCache const & cc = theCoords.getParPos().find(this)->second;
1128         lyxerr << "LyXText::getPitNearY: y: " << y << " cache size: "
1129                 << cc.size() << endl;
1130
1131         // look for highest numbered paragraph with y coordinate less than given y
1132         pit_type pit = 0;
1133         int yy = -1;
1134         CoordCache::InnerParPosCache::const_iterator it = cc.begin();
1135         CoordCache::InnerParPosCache::const_iterator et = cc.end();
1136         for (; it != et; ++it) {
1137                 lyxerr << "  examining: pit: " << it->first << " y: "
1138                         << it->second.y_ << endl;
1139                 if (it->first >= pit && int(it->second.y_) - int(pars_[it->first].ascent()) <= y) {
1140                         pit = it->first;
1141                         yy = it->second.y_;
1142                 }
1143         }
1144
1145         lyxerr << " found best y: " << yy << " for pit: " << pit << endl;
1146         return pit;
1147 }
1148
1149
1150 Row const & LyXText::getRowNearY(int y, pit_type pit) const
1151 {
1152         Paragraph const & par = pars_[pit];
1153         int yy = theCoords.get(this, pit).y_ - par.ascent();
1154         BOOST_ASSERT(!par.rows().empty());
1155         RowList::const_iterator rit = par.rows().begin();
1156         RowList::const_iterator const rlast = boost::prior(par.rows().end());
1157         for (; rit != rlast; yy += rit->height(), ++rit)
1158                 if (yy + rit->height() > y)
1159                         break;
1160         return *rit;
1161 }
1162
1163
1164 // x,y are absolute screen coordinates
1165 // sets cursor recursively descending into nested editable insets
1166 InsetBase * LyXText::editXY(LCursor & cur, int x, int y) const
1167 {
1168         pit_type pit = getPitNearY(y);
1169         BOOST_ASSERT(pit != -1);
1170         Row const & row = getRowNearY(y, pit);
1171         bool bound = false;
1172
1173         int xx = x; // is modified by getColumnNearX
1174         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1175         cur.pit() = pit;
1176         cur.pos() = pos;
1177         cur.boundary() = bound;
1178         cur.x_target() = x;
1179
1180         // try to descend into nested insets
1181         InsetBase * inset = checkInsetHit(x, y);
1182         lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
1183         if (!inset) {
1184                 // Either we deconst editXY or better we move current_font
1185                 // and real_current_font to LCursor
1186                 const_cast<LyXText *>(this)->setCurrentFont(cur);
1187                 return 0;
1188         }
1189
1190         // This should be just before or just behind the
1191         // cursor position set above.
1192         BOOST_ASSERT((pos != 0 && inset == pars_[pit].getInset(pos - 1))
1193                      || inset == pars_[pit].getInset(pos));
1194         // Make sure the cursor points to the position before
1195         // this inset.
1196         if (inset == pars_[pit].getInset(pos - 1))
1197                 --cur.pos();
1198         inset = inset->editXY(cur, x, y);
1199         if (cur.top().text() == this)
1200                 const_cast<LyXText *>(this)->setCurrentFont(cur);
1201         return inset;
1202 }
1203
1204
1205 bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
1206 {
1207         if (cur.selection())
1208                 return false;
1209         if (cur.pos() == cur.lastpos())
1210                 return false;
1211         InsetBase * inset = cur.nextInset();
1212         if (!isHighlyEditableInset(inset))
1213                 return false;
1214         inset->edit(cur, front);
1215         return true;
1216 }
1217
1218
1219 bool LyXText::cursorLeft(LCursor & cur)
1220 {
1221         if (cur.pos() != 0) {
1222                 bool boundary = cur.boundary();
1223                 bool updateNeeded = setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
1224                 if (!checkAndActivateInset(cur, false)) {
1225                         if (false && !boundary &&
1226                                         bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
1227                                 updateNeeded |=
1228                                         setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
1229                 }
1230                 return updateNeeded;
1231         }
1232
1233         if (cur.pit() != 0) {
1234                 // Steps into the paragraph above
1235                 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size());
1236         }
1237         return false;
1238 }
1239
1240
1241 bool LyXText::cursorRight(LCursor & cur)
1242 {
1243         if (false && cur.boundary()) {
1244                 return setCursor(cur, cur.pit(), cur.pos(), true, false);
1245         }
1246
1247         if (cur.pos() != cur.lastpos()) {
1248                 bool updateNeeded = false;
1249                 if (!checkAndActivateInset(cur, true)) {
1250                         updateNeeded |= setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
1251                         if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
1252                                                          cur.pos()))
1253                                 updateNeeded |= setCursor(cur, cur.pit(), cur.pos(), true, true);
1254                 }
1255                 return updateNeeded;
1256         }
1257
1258         if (cur.pit() != cur.lastpit())
1259                 return setCursor(cur, cur.pit() + 1, 0);
1260         return false;
1261 }
1262
1263
1264 bool LyXText::cursorUp(LCursor & cur)
1265 {
1266         Paragraph const & par = cur.paragraph();
1267         int const row = par.pos2row(cur.pos());
1268         int const x = cur.targetX();
1269
1270         if (!cur.selection()) {
1271                 int const y = bv_funcs::getPos(cur).y_;
1272                 LCursor old = cur;
1273                 editXY(cur, x, y - par.rows()[row].ascent() - 1);
1274
1275                 // This happens when you move out of an inset.
1276                 // And to give the DEPM the possibility of doing
1277                 // something we must provide it with two different
1278                 // cursors. (Lgb)
1279                 LCursor dummy = cur;
1280                 if (dummy == old)
1281                         ++dummy.pos();
1282
1283                 return deleteEmptyParagraphMechanism(dummy, old);
1284         }
1285
1286         bool updateNeeded = false;
1287
1288         if (row > 0) {
1289                 updateNeeded |= setCursor(cur, cur.pit(),
1290                                           x2pos(cur.pit(), row - 1, x));
1291         } else if (cur.pit() > 0) {
1292                 --cur.pit();
1293                 updateNeeded |= setCursor(cur, cur.pit(),
1294                                           x2pos(cur.pit(), par.rows().size() - 1, x));
1295         }
1296
1297         cur.x_target() = x;
1298
1299         return updateNeeded;
1300 }
1301
1302
1303 bool LyXText::cursorDown(LCursor & cur)
1304 {
1305         Paragraph const & par = cur.paragraph();
1306         int const row = par.pos2row(cur.pos());
1307         int const x = cur.targetX();
1308
1309         if (!cur.selection()) {
1310                 int const y = bv_funcs::getPos(cur).y_;
1311                 LCursor old = cur;
1312                 editXY(cur, x, y + par.rows()[row].descent() + 1);
1313
1314                 // This happens when you move out of an inset.
1315                 // And to give the DEPM the possibility of doing
1316                 // something we must provide it with two different
1317                 // cursors. (Lgb)
1318                 LCursor dummy = cur;
1319                 if (dummy == old)
1320                         ++dummy.pos();
1321
1322                 bool const changed = deleteEmptyParagraphMechanism(dummy, old);
1323
1324                 // Make sure that cur gets back whatever happened to dummy(Lgb)
1325                 if (changed)
1326                         cur = dummy;
1327
1328                 return changed;
1329
1330         }
1331
1332         bool updateNeeded = false;
1333
1334         if (row + 1 < int(par.rows().size())) {
1335                 updateNeeded |= setCursor(cur, cur.pit(),
1336                                           x2pos(cur.pit(), row + 1, x));
1337         } else if (cur.pit() + 1 < int(paragraphs().size())) {
1338                 ++cur.pit();
1339                 updateNeeded |= setCursor(cur, cur.pit(),
1340                                           x2pos(cur.pit(), 0, x));
1341         }
1342
1343         cur.x_target() = x;
1344
1345         return updateNeeded;
1346 }
1347
1348
1349 bool LyXText::cursorUpParagraph(LCursor & cur)
1350 {
1351         bool updated = false;
1352         if (cur.pos() > 0)
1353                 updated = setCursor(cur, cur.pit(), 0);
1354         else if (cur.pit() != 0)
1355                 updated = setCursor(cur, cur.pit() - 1, 0);
1356         return updated;
1357 }
1358
1359
1360 bool LyXText::cursorDownParagraph(LCursor & cur)
1361 {
1362         bool updated = false;
1363         if (cur.pit() != cur.lastpit())
1364                 updated = setCursor(cur, cur.pit() + 1, 0);
1365         else
1366                 updated = setCursor(cur, cur.pit(), cur.lastpos());
1367         return updated;
1368 }
1369
1370
1371 // fix the cursor `cur' after a characters has been deleted at `where'
1372 // position. Called by deleteEmptyParagraphMechanism
1373 void LyXText::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
1374 {
1375         // Do nothing if cursor is not in the paragraph where the
1376         // deletion occured,
1377         if (cur.pit() != where.pit())
1378                 return;
1379
1380         // If cursor position is after the deletion place update it
1381         if (cur.pos() > where.pos())
1382                 --cur.pos();
1383
1384         // Check also if we don't want to set the cursor on a spot behind the
1385         // pagragraph because we erased the last character.
1386         if (cur.pos() > cur.lastpos())
1387                 cur.pos() = cur.lastpos();
1388 }
1389
1390
1391 bool LyXText::deleteEmptyParagraphMechanism(LCursor & cur, LCursor const & old)
1392 {
1393         // Would be wrong to delete anything if we have a selection.
1394         if (cur.selection())
1395                 return false;
1396
1397         //lyxerr[Debug::DEBUG] << "DEPM: cur:\n" << cur << "old:\n" << old << endl;
1398         Paragraph const & oldpar = pars_[old.pit()];
1399
1400         // We allow all kinds of "mumbo-jumbo" when freespacing.
1401         if (oldpar.isFreeSpacing())
1402                 return false;
1403
1404         /* Ok I'll put some comments here about what is missing.
1405            I have fixed BackSpace (and thus Delete) to not delete
1406            double-spaces automagically. I have also changed Cut,
1407            Copy and Paste to hopefully do some sensible things.
1408            There are still some small problems that can lead to
1409            double spaces stored in the document file or space at
1410            the beginning of paragraphs(). This happens if you have
1411            the cursor between to spaces and then save. Or if you
1412            cut and paste and the selection have a space at the
1413            beginning and then save right after the paste. I am
1414            sure none of these are very hard to fix, but I will
1415            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1416            that I can get some feedback. (Lgb)
1417         */
1418
1419         // If old.pos() == 0 and old.pos()(1) == LineSeparator
1420         // delete the LineSeparator.
1421         // MISSING
1422
1423         // If old.pos() == 1 and old.pos()(0) == LineSeparator
1424         // delete the LineSeparator.
1425         // MISSING
1426
1427         // If the chars around the old cursor were spaces, delete one of them.
1428         if (old.pit() != cur.pit() || old.pos() != cur.pos()) {
1429
1430                 // Only if the cursor has really moved.
1431                 if (old.pos() > 0
1432                     && old.pos() < oldpar.size()
1433                     && oldpar.isLineSeparator(old.pos())
1434                     && oldpar.isLineSeparator(old.pos() - 1)) {
1435                         pars_[old.pit()].erase(old.pos() - 1);
1436 #ifdef WITH_WARNINGS
1437 #warning This will not work anymore when we have multiple views of the same buffer
1438 // In this case, we will have to correct also the cursors held by
1439 // other bufferviews. It will probably be easier to do that in a more
1440 // automated way in CursorSlice code. (JMarc 26/09/2001)
1441 #endif
1442                         // correct all cursor parts
1443                         fixCursorAfterDelete(cur.top(), old.top());
1444 #ifdef WITH_WARNINGS
1445 #warning DEPM, look here
1446 #endif
1447                         //fixCursorAfterDelete(cur.anchor(), old.top());
1448                         return true;
1449                 }
1450         }
1451
1452         // only do our magic if we changed paragraph
1453         if (old.pit() == cur.pit())
1454                 return false;
1455
1456         // don't delete anything if this is the ONLY paragraph!
1457         if (pars_.size() == 1)
1458                 return false;
1459
1460         // Do not delete empty paragraphs with keepempty set.
1461         if (oldpar.allowEmpty())
1462                 return false;
1463
1464         // record if we have deleted a paragraph
1465         // we can't possibly have deleted a paragraph before this point
1466         bool deleted = false;
1467
1468         if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
1469                 // ok, we will delete something
1470                 deleted = true;
1471
1472                 bool selection_position_was_oldcursor_position =
1473                         cur.anchor().pit() == old.pit() && cur.anchor().pos() == old.pos();
1474
1475                 // This is a bit of a overkill. We change the old and the cur par
1476                 // at max, certainly not everything in between...
1477                 recUndo(old.pit(), cur.pit());
1478
1479                 // Delete old par.
1480                 pars_.erase(pars_.begin() + old.pit());
1481
1482                 // Update cursor par offset if necessary.
1483                 // Some 'iterator registration' would be nice that takes care of
1484                 // such events. Maybe even signal/slot?
1485                 if (cur.pit() > old.pit())
1486                         --cur.pit();
1487 #ifdef WITH_WARNINGS
1488 #warning DEPM, look here
1489 #endif
1490 //              if (cur.anchor().pit() > old.pit())
1491 //                      --cur.anchor().pit();
1492
1493                 if (selection_position_was_oldcursor_position) {
1494                         // correct selection
1495                         cur.resetAnchor();
1496                 }
1497         }
1498
1499         if (deleted)
1500                 return true;
1501
1502         if (pars_[old.pit()].stripLeadingSpaces())
1503                 cur.resetAnchor();
1504
1505         return false;
1506 }
1507
1508
1509 ParagraphList & LyXText::paragraphs() const
1510 {
1511         return const_cast<ParagraphList &>(pars_);
1512 }
1513
1514
1515 void LyXText::recUndo(pit_type first, pit_type last) const
1516 {
1517         recordUndo(bv()->cursor(), Undo::ATOMIC, first, last);
1518 }
1519
1520
1521 void LyXText::recUndo(pit_type par) const
1522 {
1523         recordUndo(bv()->cursor(), Undo::ATOMIC, par, par);
1524 }
1525
1526
1527 int defaultRowHeight()
1528 {
1529         return int(font_metrics::maxHeight(LyXFont(LyXFont::ALL_SANE)) *  1.2);
1530 }