]> git.lyx.org Git - lyx.git/blob - src/text2.C
Squash a couple of MSVC warnings about "truncation of constant value".
[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         // Can't use forwardPos either as this descends into
444         // nested insets. 
445         for (; dit != ditend; dit.forwardPosNoDescend()) {
446                 if (dit.pos() != dit.lastpos()) {
447                         LyXFont f = getFont(dit.paragraph(), dit.pos());
448                         f.update(font, params.language, toggleall);
449                         setCharFont(dit.pit(), dit.pos(), f);
450                 }
451         }
452 }
453
454
455 // the cursor set functions have a special mechanism. When they
456 // realize you left an empty paragraph, they will delete it.
457
458 void LyXText::cursorHome(LCursor & cur)
459 {
460         BOOST_ASSERT(this == cur.text());
461         setCursor(cur, cur.pit(), cur.textRow().pos());
462 }
463
464
465 void LyXText::cursorEnd(LCursor & cur)
466 {
467         BOOST_ASSERT(this == cur.text());
468         // if not on the last row of the par, put the cursor before
469         // the final space
470 // FIXME: does this final space exist?
471         pos_type const end = cur.textRow().endpos();
472         setCursor(cur, cur.pit(), end == cur.lastpos() ? end : end - 1);
473 }
474
475
476 void LyXText::cursorTop(LCursor & cur)
477 {
478         BOOST_ASSERT(this == cur.text());
479         setCursor(cur, 0, 0);
480 }
481
482
483 void LyXText::cursorBottom(LCursor & cur)
484 {
485         BOOST_ASSERT(this == cur.text());
486         setCursor(cur, cur.lastpit(), boost::prior(paragraphs().end())->size());
487 }
488
489
490 void LyXText::toggleFree(LCursor & cur, LyXFont const & font, bool toggleall)
491 {
492         BOOST_ASSERT(this == cur.text());
493         // If the mask is completely neutral, tell user
494         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
495                 // Could only happen with user style
496                 cur.message(_("No font change defined. "
497                         "Use Character under the Layout menu to define font change."));
498                 return;
499         }
500
501         // Try implicit word selection
502         // If there is a change in the language the implicit word selection
503         // is disabled.
504         CursorSlice resetCursor = cur.top();
505         bool implicitSelection =
506                 font.language() == ignore_language
507                 && font.number() == LyXFont::IGNORE
508                 && selectWordWhenUnderCursor(cur, lyx::WHOLE_WORD_STRICT);
509
510         // Set font
511         setFont(cur, font, toggleall);
512
513         // Implicit selections are cleared afterwards
514         // and cursor is set to the original position.
515         if (implicitSelection) {
516                 cur.clearSelection();
517                 cur.top() = resetCursor;
518                 cur.resetAnchor();
519         }
520 }
521
522
523 string LyXText::getStringToIndex(LCursor & cur)
524 {
525         BOOST_ASSERT(this == cur.text());
526         // Try implicit word selection
527         // If there is a change in the language the implicit word selection
528         // is disabled.
529         CursorSlice const reset_cursor = cur.top();
530         bool const implicitSelection =
531                 selectWordWhenUnderCursor(cur, lyx::PREVIOUS_WORD);
532
533         string idxstring;
534         if (!cur.selection())
535                 cur.message(_("Nothing to index!"));
536         else if (cur.selBegin().pit() != cur.selEnd().pit())
537                 cur.message(_("Cannot index more than one paragraph!"));
538         else
539                 idxstring = cur.selectionAsString(false);
540
541         // Reset cursors to their original position.
542         cur.top() = reset_cursor;
543         cur.resetAnchor();
544
545         // Clear the implicit selection.
546         if (implicitSelection)
547                 cur.clearSelection();
548
549         return idxstring;
550 }
551
552
553 void LyXText::setParagraph(LCursor & cur,
554         Spacing const & spacing, LyXAlignment align,
555         string const & labelwidthstring, bool noindent)
556 {
557         BOOST_ASSERT(cur.text());
558         // make sure that the depth behind the selection are restored, too
559         pit_type undopit = undoSpan(cur.selEnd().pit());
560         recUndo(cur.selBegin().pit(), undopit - 1);
561
562         for (pit_type pit = cur.selBegin().pit(), end = cur.selEnd().pit();
563                         pit <= end; ++pit) {
564                 Paragraph & par = pars_[pit];
565                 ParagraphParameters & params = par.params();
566                 params.spacing(spacing);
567
568                 // does the layout allow the new alignment?
569                 LyXLayout_ptr const & layout = par.layout();
570
571                 if (align == LYX_ALIGN_LAYOUT)
572                         align = layout->align;
573                 if (align & layout->alignpossible) {
574                         if (align == layout->align)
575                                 params.align(LYX_ALIGN_LAYOUT);
576                         else
577                                 params.align(align);
578                 }
579                 par.setLabelWidthString(labelwidthstring);
580                 params.noindent(noindent);
581         }
582 }
583
584
585 string expandLabel(LyXTextClass const & textclass,
586         LyXLayout_ptr const & layout, bool appendix)
587 {
588         string fmt = appendix ?
589                 layout->labelstring_appendix() : layout->labelstring();
590
591         // handle 'inherited level parts' in 'fmt',
592         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
593         size_t const i = fmt.find('@', 0);
594         if (i != string::npos) {
595                 size_t const j = fmt.find('@', i + 1);
596                 if (j != string::npos) {
597                         string parent(fmt, i + 1, j - i - 1);
598                         string label = expandLabel(textclass, textclass[parent], appendix);
599                         fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
600                 }
601         }
602
603         return textclass.counters().counterLabel(fmt);
604 }
605
606
607 namespace {
608
609 void incrementItemDepth(ParagraphList & pars, pit_type pit, pit_type first_pit)
610 {
611         int const cur_labeltype = pars[pit].layout()->labeltype;
612
613         if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
614                 return;
615
616         int const cur_depth = pars[pit].getDepth();
617
618         pit_type prev_pit = pit - 1;
619         while (true) {
620                 int const prev_depth = pars[prev_pit].getDepth();
621                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
622                 if (prev_depth == 0 && cur_depth > 0) {
623                         if (prev_labeltype == cur_labeltype) {
624                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
625                         }
626                         break;
627                 } else if (prev_depth < cur_depth) {
628                         if (prev_labeltype == cur_labeltype) {
629                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
630                                 break;
631                         }
632                 } else if (prev_depth == cur_depth) {
633                         if (prev_labeltype == cur_labeltype) {
634                                 pars[pit].itemdepth = pars[prev_pit].itemdepth;
635                                 break;
636                         }
637                 }
638                 if (prev_pit == first_pit)
639                         break;
640
641                 --prev_pit;
642         }
643 }
644
645
646 void resetEnumCounterIfNeeded(ParagraphList & pars, pit_type pit,
647         pit_type firstpit, Counters & counters)
648 {
649         if (pit == firstpit)
650                 return;
651
652         int const cur_depth = pars[pit].getDepth();
653         pit_type prev_pit = pit - 1;
654         while (true) {
655                 int const prev_depth = pars[prev_pit].getDepth();
656                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
657                 if (prev_depth <= cur_depth) {
658                         if (prev_labeltype != LABEL_ENUMERATE) {
659                                 switch (pars[pit].itemdepth) {
660                                 case 0:
661                                         counters.reset("enumi");
662                                 case 1:
663                                         counters.reset("enumii");
664                                 case 2:
665                                         counters.reset("enumiii");
666                                 case 3:
667                                         counters.reset("enumiv");
668                                 }
669                         }
670                         break;
671                 }
672
673                 if (prev_pit == firstpit)
674                         break;
675
676                 --prev_pit;
677         }
678 }
679
680 } // anon namespace
681
682
683 // set the counter of a paragraph. This includes the labels
684 void LyXText::setCounter(Buffer const & buf, pit_type pit)
685 {
686         Paragraph & par = pars_[pit];
687         BufferParams const & bufparams = buf.params();
688         LyXTextClass const & textclass = bufparams.getLyXTextClass();
689         LyXLayout_ptr const & layout = par.layout();
690         Counters & counters = textclass.counters();
691
692         // Always reset
693         par.itemdepth = 0;
694
695         if (pit == 0) {
696                 par.params().appendix(par.params().startOfAppendix());
697         } else {
698                 par.params().appendix(pars_[pit - 1].params().appendix());
699                 if (!par.params().appendix() &&
700                     par.params().startOfAppendix()) {
701                         par.params().appendix(true);
702                         textclass.counters().reset();
703                 }
704
705                 // Maybe we have to increment the item depth.
706                 incrementItemDepth(pars_, pit, 0);
707         }
708
709         // erase what was there before
710         par.params().labelString(string());
711
712         if (layout->margintype == MARGIN_MANUAL) {
713                 if (par.params().labelWidthString().empty())
714                         par.setLabelWidthString(layout->labelstring());
715         } else {
716                 par.setLabelWidthString(string());
717         }
718
719         // is it a layout that has an automatic label?
720         if (layout->labeltype == LABEL_COUNTER) {
721                 BufferParams const & bufparams = buf.params();
722                 LyXTextClass const & textclass = bufparams.getLyXTextClass();
723                 counters.step(layout->counter);
724                 string label = expandLabel(textclass, layout, par.params().appendix());
725                 par.params().labelString(label);
726         } else if (layout->labeltype == LABEL_ITEMIZE) {
727                 // At some point of time we should do something more
728                 // clever here, like:
729                 //   par.params().labelString(
730                 //    bufparams.user_defined_bullet(par.itemdepth).getText());
731                 // for now, use a simple hardcoded label
732                 string itemlabel;
733                 switch (par.itemdepth) {
734                 case 0:
735                         itemlabel = "*";
736                         break;
737                 case 1:
738                         itemlabel = "-";
739                         break;
740                 case 2:
741                         itemlabel = "@";
742                         break;
743                 case 3:
744                         itemlabel = "·";
745                         break;
746                 }
747
748                 par.params().labelString(itemlabel);
749         } else if (layout->labeltype == LABEL_ENUMERATE) {
750                 // Maybe we have to reset the enumeration counter.
751                 resetEnumCounterIfNeeded(pars_, pit, 0, counters);
752
753                 // FIXME
754                 // Yes I know this is a really, really! bad solution
755                 // (Lgb)
756                 string enumcounter = "enum";
757
758                 switch (par.itemdepth) {
759                 case 2:
760                         enumcounter += 'i';
761                 case 1:
762                         enumcounter += 'i';
763                 case 0:
764                         enumcounter += 'i';
765                         break;
766                 case 3:
767                         enumcounter += "iv";
768                         break;
769                 default:
770                         // not a valid enumdepth...
771                         break;
772                 }
773
774                 counters.step(enumcounter);
775
776                 par.params().labelString(counters.enumLabel(enumcounter));
777         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
778                 counters.step("bibitem");
779                 int number = counters.value("bibitem");
780                 if (par.bibitem()) {
781                         par.bibitem()->setCounter(number);
782                         par.params().labelString(layout->labelstring());
783                 }
784                 // In biblio should't be following counters but...
785         } else {
786                 string s = buf.B_(layout->labelstring());
787
788                 // the caption hack:
789                 if (layout->labeltype == LABEL_SENSITIVE) {
790                         pit_type end = paragraphs().size();
791                         pit_type tmppit = pit;
792                         InsetBase * in = 0;
793                         bool isOK = false;
794                         while (tmppit != end) {
795                                 in = pars_[tmppit].inInset();
796                                 // FIXME: in should be always valid.
797                                 if (in &&
798                                     (in->lyxCode() == InsetBase::FLOAT_CODE ||
799                                      in->lyxCode() == InsetBase::WRAP_CODE)) {
800                                         isOK = true;
801                                         break;
802                                 }
803 #ifdef WITH_WARNINGS
804 #warning replace this code by something that works
805 // This code does not work because we have currently no way to move up
806 // in the hierarchy of insets (JMarc 16/08/2004)
807 #endif
808 #if 0
809 /* I think this code is supposed to be useful when one has a caption
810  * in a minipage in a figure inset. We need to go up to be able to see
811  * that the caption should use "Figure" as label
812  */
813                                 else {
814                                         Paragraph const * owner = &ownerPar(buf, in);
815                                         tmppit = 0;
816                                         for ( ; tmppit != end; ++tmppit)
817                                                 if (&pars_[tmppit] == owner)
818                                                         break;
819                                 }
820 #else
821                                 ++tmppit;
822 #endif
823                         }
824
825                         if (isOK) {
826                                 string type;
827
828                                 if (in->lyxCode() == InsetBase::FLOAT_CODE)
829                                         type = static_cast<InsetFloat*>(in)->params().type;
830                                 else if (in->lyxCode() == InsetBase::WRAP_CODE)
831                                         type = static_cast<InsetWrap*>(in)->params().type;
832                                 else
833                                         BOOST_ASSERT(false);
834
835                                 Floating const & fl = textclass.floats().getType(type);
836
837                                 counters.step(fl.type());
838
839                                 // Doesn't work... yet.
840                                 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
841                         } else {
842                                 // par->SetLayout(0);
843                                 // s = layout->labelstring;
844                                 s = _("Senseless: ");
845                         }
846                 }
847                 par.params().labelString(s);
848
849         }
850 }
851
852
853 // Updates all counters.
854 void LyXText::updateCounters()
855 {
856         // start over
857         bv()->buffer()->params().getLyXTextClass().counters().reset();
858
859         bool update_pos = false;
860
861         pit_type end = paragraphs().size();
862         for (pit_type pit = 0; pit != end; ++pit) {
863                 string const oldLabel = pars_[pit].params().labelString();
864                 size_t maxdepth = 0;
865                 if (pit != 0)
866                         maxdepth = pars_[pit - 1].getMaxDepthAfter();
867
868                 if (pars_[pit].params().depth() > maxdepth)
869                         pars_[pit].params().depth(maxdepth);
870
871                 // setCounter can potentially change the labelString.
872                 setCounter(*bv()->buffer(), pit);
873                 string const & newLabel = pars_[pit].params().labelString();
874                 if (oldLabel != newLabel) {
875                         //lyxerr[Debug::DEBUG] << "changing labels: old: " << oldLabel << " new: "
876                         //      << newLabel << endl;
877                         update_pos = true;
878                 }
879         }
880 }
881
882
883 // this really should just insert the inset and not move the cursor.
884 void LyXText::insertInset(LCursor & cur, InsetBase * inset)
885 {
886         BOOST_ASSERT(this == cur.text());
887         BOOST_ASSERT(inset);
888         cur.paragraph().insertInset(cur.pos(), inset);
889 }
890
891
892 // needed to insert the selection
893 void LyXText::insertStringAsLines(LCursor & cur, string const & str)
894 {
895         pit_type pit = cur.pit();
896         pos_type pos = cur.pos();
897         recordUndo(cur);
898
899         // only to be sure, should not be neccessary
900         cur.clearSelection();
901         cur.buffer().insertStringAsLines(pars_, pit, pos, current_font, str,
902                 autoBreakRows_);
903
904         cur.resetAnchor();
905         setCursor(cur, cur.pit(), pos);
906         cur.setSelection();
907 }
908
909
910 // turn double CR to single CR, others are converted into one
911 // blank. Then insertStringAsLines is called
912 void LyXText::insertStringAsParagraphs(LCursor & cur, string const & str)
913 {
914         string linestr = str;
915         bool newline_inserted = false;
916
917         for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
918                 if (linestr[i] == '\n') {
919                         if (newline_inserted) {
920                                 // we know that \r will be ignored by
921                                 // insertStringAsLines. Of course, it is a dirty
922                                 // trick, but it works...
923                                 linestr[i - 1] = '\r';
924                                 linestr[i] = '\n';
925                         } else {
926                                 linestr[i] = ' ';
927                                 newline_inserted = true;
928                         }
929                 } else if (IsPrintable(linestr[i])) {
930                         newline_inserted = false;
931                 }
932         }
933         insertStringAsLines(cur, linestr);
934 }
935
936
937 bool LyXText::setCursor(LCursor & cur, pit_type par, pos_type pos,
938         bool setfont, bool boundary)
939 {
940         LCursor old = cur;
941         setCursorIntern(cur, par, pos, setfont, boundary);
942         return deleteEmptyParagraphMechanism(cur, old);
943 }
944
945
946 void LyXText::setCursor(CursorSlice & cur, pit_type par,
947         pos_type pos, bool boundary)
948 {
949         BOOST_ASSERT(par != int(paragraphs().size()));
950         cur.pit() = par;
951         cur.pos() = pos;
952         cur.boundary() = boundary;
953
954         // now some strict checking
955         Paragraph & para = getPar(par);
956
957         // None of these should happen, but we're scaredy-cats
958         if (pos < 0) {
959                 lyxerr << "dont like -1" << endl;
960                 BOOST_ASSERT(false);
961         }
962
963         if (pos > para.size()) {
964                 lyxerr << "dont like 1, pos: " << pos
965                        << " size: " << para.size()
966                        << " par: " << par << endl;
967                 BOOST_ASSERT(false);
968         }
969 }
970
971
972 void LyXText::setCursorIntern(LCursor & cur,
973         pit_type par, pos_type pos, bool setfont, bool boundary)
974 {
975         setCursor(cur.top(), par, pos, boundary);
976         cur.setTargetX();
977         if (setfont)
978                 setCurrentFont(cur);
979 }
980
981
982 void LyXText::setCurrentFont(LCursor & cur)
983 {
984         BOOST_ASSERT(this == cur.text());
985         pos_type pos = cur.pos();
986         Paragraph & par = cur.paragraph();
987
988         if (cur.boundary() && pos > 0)
989                 --pos;
990
991         if (pos > 0) {
992                 if (pos == cur.lastpos())
993                         --pos;
994                 else // potentional bug... BUG (Lgb)
995                         if (par.isSeparator(pos)) {
996                                 if (pos > cur.textRow().pos() &&
997                                     bidi.level(pos) % 2 ==
998                                     bidi.level(pos - 1) % 2)
999                                         --pos;
1000                                 else if (pos + 1 < cur.lastpos())
1001                                         ++pos;
1002                         }
1003         }
1004
1005         BufferParams const & bufparams = cur.buffer().params();
1006         current_font = par.getFontSettings(bufparams, pos);
1007         real_current_font = getFont(par, pos);
1008
1009         if (cur.pos() == cur.lastpos()
1010             && bidi.isBoundary(cur.buffer(), par, cur.pos())
1011             && !cur.boundary()) {
1012                 Language const * lang = par.getParLanguage(bufparams);
1013                 current_font.setLanguage(lang);
1014                 current_font.setNumber(LyXFont::OFF);
1015                 real_current_font.setLanguage(lang);
1016                 real_current_font.setNumber(LyXFont::OFF);
1017         }
1018 }
1019
1020
1021 // x is an absolute screen coord
1022 // returns the column near the specified x-coordinate of the row
1023 // x is set to the real beginning of this column
1024 pos_type LyXText::getColumnNearX(pit_type const pit,
1025         Row const & row, int & x, bool & boundary) const
1026 {
1027         int const xo = theCoords.get(this, pit).x_;
1028         x -= xo;
1029         RowMetrics const r = computeRowMetrics(pit, row);
1030         Paragraph const & par = pars_[pit];
1031
1032         pos_type vc = row.pos();
1033         pos_type end = row.endpos();
1034         pos_type c = 0;
1035         LyXLayout_ptr const & layout = par.layout();
1036
1037         bool left_side = false;
1038
1039         pos_type body_pos = par.beginOfBody();
1040
1041         double tmpx = r.x;
1042         double last_tmpx = tmpx;
1043
1044         if (body_pos > 0 &&
1045             (body_pos > end || !par.isLineSeparator(body_pos - 1)))
1046                 body_pos = 0;
1047
1048         // check for empty row
1049         if (vc == end) {
1050                 x = int(tmpx) + xo;
1051                 return 0;
1052         }
1053
1054         while (vc < end && tmpx <= x) {
1055                 c = bidi.vis2log(vc);
1056                 last_tmpx = tmpx;
1057                 if (body_pos > 0 && c == body_pos - 1) {
1058                         tmpx += r.label_hfill +
1059                                 font_metrics::width(layout->labelsep, getLabelFont(par));
1060                         if (par.isLineSeparator(body_pos - 1))
1061                                 tmpx -= singleWidth(par, body_pos - 1);
1062                 }
1063
1064                 if (hfillExpansion(par, row, c)) {
1065                         tmpx += singleWidth(par, c);
1066                         if (c >= body_pos)
1067                                 tmpx += r.hfill;
1068                         else
1069                                 tmpx += r.label_hfill;
1070                 } else if (par.isSeparator(c)) {
1071                         tmpx += singleWidth(par, c);
1072                         if (c >= body_pos)
1073                                 tmpx += r.separator;
1074                 } else {
1075                         tmpx += singleWidth(par, c);
1076                 }
1077                 ++vc;
1078         }
1079
1080         if ((tmpx + last_tmpx) / 2 > x) {
1081                 tmpx = last_tmpx;
1082                 left_side = true;
1083         }
1084
1085         BOOST_ASSERT(vc <= end);  // This shouldn't happen.
1086
1087         boundary = false;
1088         // This (rtl_support test) is not needed, but gives
1089         // some speedup if rtl_support == false
1090         bool const lastrow = lyxrc.rtl_support && row.endpos() == par.size();
1091
1092         // If lastrow is false, we don't need to compute
1093         // the value of rtl.
1094         bool const rtl = lastrow ? isRTL(par) : false;
1095         if (lastrow &&
1096                  ((rtl  &&  left_side && vc == row.pos() && x < tmpx - 5) ||
1097                   (!rtl && !left_side && vc == end  && x > tmpx + 5)))
1098                 c = end;
1099         else if (vc == row.pos()) {
1100                 c = bidi.vis2log(vc);
1101                 if (bidi.level(c) % 2 == 1)
1102                         ++c;
1103         } else {
1104                 c = bidi.vis2log(vc - 1);
1105                 bool const rtl = (bidi.level(c) % 2 == 1);
1106                 if (left_side == rtl) {
1107                         ++c;
1108                         boundary = bidi.isBoundary(*bv()->buffer(), par, c);
1109                 }
1110         }
1111
1112         if (row.pos() < end && c >= end && par.isNewline(end - 1)) {
1113                 if (bidi.level(end -1) % 2 == 0)
1114                         tmpx -= singleWidth(par, end - 1);
1115                 else
1116                         tmpx += singleWidth(par, end - 1);
1117                 c = end - 1;
1118         }
1119
1120         x = int(tmpx) + xo;
1121         return c - row.pos();
1122 }
1123
1124
1125 // y is screen coordinate
1126 pit_type LyXText::getPitNearY(int y) const
1127 {
1128         BOOST_ASSERT(!paragraphs().empty());
1129         BOOST_ASSERT(theCoords.getParPos().find(this) != theCoords.getParPos().end());
1130         CoordCache::InnerParPosCache const & cc = theCoords.getParPos().find(this)->second;
1131         lyxerr << "LyXText::getPitNearY: y: " << y << " cache size: "
1132                 << cc.size() << endl;
1133
1134         // look for highest numbered paragraph with y coordinate less than given y
1135         pit_type pit = 0;
1136         int yy = -1;
1137         CoordCache::InnerParPosCache::const_iterator it = cc.begin();
1138         CoordCache::InnerParPosCache::const_iterator et = cc.end();
1139         for (; it != et; ++it) {
1140                 lyxerr << "  examining: pit: " << it->first << " y: "
1141                         << it->second.y_ << endl;
1142                 if (it->first >= pit && int(it->second.y_) - int(pars_[it->first].ascent()) <= y) {
1143                         pit = it->first;
1144                         yy = it->second.y_;
1145                 }
1146         }
1147
1148         lyxerr << " found best y: " << yy << " for pit: " << pit << endl;
1149         return pit;
1150 }
1151
1152
1153 Row const & LyXText::getRowNearY(int y, pit_type pit) const
1154 {
1155         Paragraph const & par = pars_[pit];
1156         int yy = theCoords.get(this, pit).y_ - par.ascent();
1157         BOOST_ASSERT(!par.rows().empty());
1158         RowList::const_iterator rit = par.rows().begin();
1159         RowList::const_iterator const rlast = boost::prior(par.rows().end());
1160         for (; rit != rlast; yy += rit->height(), ++rit)
1161                 if (yy + rit->height() > y)
1162                         break;
1163         return *rit;
1164 }
1165
1166
1167 // x,y are absolute screen coordinates
1168 // sets cursor recursively descending into nested editable insets
1169 InsetBase * LyXText::editXY(LCursor & cur, int x, int y) const
1170 {
1171         pit_type pit = getPitNearY(y);
1172         BOOST_ASSERT(pit != -1);
1173         Row const & row = getRowNearY(y, pit);
1174         bool bound = false;
1175
1176         int xx = x; // is modified by getColumnNearX
1177         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1178         cur.pit() = pit;
1179         cur.pos() = pos;
1180         cur.boundary() = bound;
1181         cur.x_target() = x;
1182
1183         // try to descend into nested insets
1184         InsetBase * inset = checkInsetHit(x, y);
1185         lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
1186         if (!inset) {
1187                 // Either we deconst editXY or better we move current_font
1188                 // and real_current_font to LCursor
1189                 const_cast<LyXText *>(this)->setCurrentFont(cur);
1190                 return 0;
1191         }
1192
1193         // This should be just before or just behind the
1194         // cursor position set above.
1195         BOOST_ASSERT((pos != 0 && inset == pars_[pit].getInset(pos - 1))
1196                      || inset == pars_[pit].getInset(pos));
1197         // Make sure the cursor points to the position before
1198         // this inset.
1199         if (inset == pars_[pit].getInset(pos - 1))
1200                 --cur.pos();
1201         inset = inset->editXY(cur, x, y);
1202         if (cur.top().text() == this)
1203                 const_cast<LyXText *>(this)->setCurrentFont(cur);
1204         return inset;
1205 }
1206
1207
1208 bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
1209 {
1210         if (cur.selection())
1211                 return false;
1212         if (cur.pos() == cur.lastpos())
1213                 return false;
1214         InsetBase * inset = cur.nextInset();
1215         if (!isHighlyEditableInset(inset))
1216                 return false;
1217         inset->edit(cur, front);
1218         return true;
1219 }
1220
1221
1222 bool LyXText::cursorLeft(LCursor & cur)
1223 {
1224         if (cur.pos() != 0) {
1225                 bool boundary = cur.boundary();
1226                 bool updateNeeded = setCursor(cur, cur.pit(), cur.pos() - 1, true, false);
1227                 if (!checkAndActivateInset(cur, false)) {
1228                         if (false && !boundary &&
1229                                         bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
1230                                 updateNeeded |=
1231                                         setCursor(cur, cur.pit(), cur.pos() + 1, true, true);
1232                 }
1233                 return updateNeeded;
1234         }
1235
1236         if (cur.pit() != 0) {
1237                 // Steps into the paragraph above
1238                 return setCursor(cur, cur.pit() - 1, getPar(cur.pit() - 1).size());
1239         }
1240         return false;
1241 }
1242
1243
1244 bool LyXText::cursorRight(LCursor & cur)
1245 {
1246         if (false && cur.boundary()) {
1247                 return setCursor(cur, cur.pit(), cur.pos(), true, false);
1248         }
1249
1250         if (cur.pos() != cur.lastpos()) {
1251                 bool updateNeeded = false;
1252                 if (!checkAndActivateInset(cur, true)) {
1253                         updateNeeded |= setCursor(cur, cur.pit(), cur.pos() + 1, true, false);
1254                         if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
1255                                                          cur.pos()))
1256                                 updateNeeded |= setCursor(cur, cur.pit(), cur.pos(), true, true);
1257                 }
1258                 return updateNeeded;
1259         }
1260
1261         if (cur.pit() != cur.lastpit())
1262                 return setCursor(cur, cur.pit() + 1, 0);
1263         return false;
1264 }
1265
1266
1267 bool LyXText::cursorUp(LCursor & cur)
1268 {
1269         Paragraph const & par = cur.paragraph();
1270         int const row = par.pos2row(cur.pos());
1271         int const x = cur.targetX();
1272
1273         if (!cur.selection()) {
1274                 int const y = bv_funcs::getPos(cur).y_;
1275                 LCursor old = cur;
1276                 editXY(cur, x, y - par.rows()[row].ascent() - 1);
1277
1278                 // This happens when you move out of an inset.
1279                 // And to give the DEPM the possibility of doing
1280                 // something we must provide it with two different
1281                 // cursors. (Lgb)
1282                 LCursor dummy = cur;
1283                 if (dummy == old)
1284                         ++dummy.pos();
1285
1286                 return deleteEmptyParagraphMechanism(dummy, old);
1287         }
1288
1289         bool updateNeeded = false;
1290
1291         if (row > 0) {
1292                 updateNeeded |= setCursor(cur, cur.pit(),
1293                                           x2pos(cur.pit(), row - 1, x));
1294         } else if (cur.pit() > 0) {
1295                 --cur.pit();
1296                 updateNeeded |= setCursor(cur, cur.pit(),
1297                                           x2pos(cur.pit(), par.rows().size() - 1, x));
1298         }
1299
1300         cur.x_target() = x;
1301
1302         return updateNeeded;
1303 }
1304
1305
1306 bool LyXText::cursorDown(LCursor & cur)
1307 {
1308         Paragraph const & par = cur.paragraph();
1309         int const row = par.pos2row(cur.pos());
1310         int const x = cur.targetX();
1311
1312         if (!cur.selection()) {
1313                 int const y = bv_funcs::getPos(cur).y_;
1314                 LCursor old = cur;
1315                 editXY(cur, x, y + par.rows()[row].descent() + 1);
1316
1317                 // This happens when you move out of an inset.
1318                 // And to give the DEPM the possibility of doing
1319                 // something we must provide it with two different
1320                 // cursors. (Lgb)
1321                 LCursor dummy = cur;
1322                 if (dummy == old)
1323                         ++dummy.pos();
1324
1325                 bool const changed = deleteEmptyParagraphMechanism(dummy, old);
1326
1327                 // Make sure that cur gets back whatever happened to dummy(Lgb)
1328                 if (changed)
1329                         cur = dummy;
1330
1331                 return changed;
1332
1333         }
1334
1335         bool updateNeeded = false;
1336
1337         if (row + 1 < int(par.rows().size())) {
1338                 updateNeeded |= setCursor(cur, cur.pit(),
1339                                           x2pos(cur.pit(), row + 1, x));
1340         } else if (cur.pit() + 1 < int(paragraphs().size())) {
1341                 ++cur.pit();
1342                 updateNeeded |= setCursor(cur, cur.pit(),
1343                                           x2pos(cur.pit(), 0, x));
1344         }
1345
1346         cur.x_target() = x;
1347
1348         return updateNeeded;
1349 }
1350
1351
1352 bool LyXText::cursorUpParagraph(LCursor & cur)
1353 {
1354         bool updated = false;
1355         if (cur.pos() > 0)
1356                 updated = setCursor(cur, cur.pit(), 0);
1357         else if (cur.pit() != 0)
1358                 updated = setCursor(cur, cur.pit() - 1, 0);
1359         return updated;
1360 }
1361
1362
1363 bool LyXText::cursorDownParagraph(LCursor & cur)
1364 {
1365         bool updated = false;
1366         if (cur.pit() != cur.lastpit())
1367                 updated = setCursor(cur, cur.pit() + 1, 0);
1368         else
1369                 updated = setCursor(cur, cur.pit(), cur.lastpos());
1370         return updated;
1371 }
1372
1373
1374 // fix the cursor `cur' after a characters has been deleted at `where'
1375 // position. Called by deleteEmptyParagraphMechanism
1376 void LyXText::fixCursorAfterDelete(CursorSlice & cur, CursorSlice const & where)
1377 {
1378         // Do nothing if cursor is not in the paragraph where the
1379         // deletion occured,
1380         if (cur.pit() != where.pit())
1381                 return;
1382
1383         // If cursor position is after the deletion place update it
1384         if (cur.pos() > where.pos())
1385                 --cur.pos();
1386
1387         // Check also if we don't want to set the cursor on a spot behind the
1388         // pagragraph because we erased the last character.
1389         if (cur.pos() > cur.lastpos())
1390                 cur.pos() = cur.lastpos();
1391 }
1392
1393
1394 bool LyXText::deleteEmptyParagraphMechanism(LCursor & cur, LCursor const & old)
1395 {
1396         // Would be wrong to delete anything if we have a selection.
1397         if (cur.selection())
1398                 return false;
1399
1400         //lyxerr[Debug::DEBUG] << "DEPM: cur:\n" << cur << "old:\n" << old << endl;
1401         Paragraph const & oldpar = pars_[old.pit()];
1402
1403         // We allow all kinds of "mumbo-jumbo" when freespacing.
1404         if (oldpar.isFreeSpacing())
1405                 return false;
1406
1407         /* Ok I'll put some comments here about what is missing.
1408            I have fixed BackSpace (and thus Delete) to not delete
1409            double-spaces automagically. I have also changed Cut,
1410            Copy and Paste to hopefully do some sensible things.
1411            There are still some small problems that can lead to
1412            double spaces stored in the document file or space at
1413            the beginning of paragraphs(). This happens if you have
1414            the cursor between to spaces and then save. Or if you
1415            cut and paste and the selection have a space at the
1416            beginning and then save right after the paste. I am
1417            sure none of these are very hard to fix, but I will
1418            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1419            that I can get some feedback. (Lgb)
1420         */
1421
1422         // If old.pos() == 0 and old.pos()(1) == LineSeparator
1423         // delete the LineSeparator.
1424         // MISSING
1425
1426         // If old.pos() == 1 and old.pos()(0) == LineSeparator
1427         // delete the LineSeparator.
1428         // MISSING
1429
1430         // If the chars around the old cursor were spaces, delete one of them.
1431         if (old.pit() != cur.pit() || old.pos() != cur.pos()) {
1432
1433                 // Only if the cursor has really moved.
1434                 if (old.pos() > 0
1435                     && old.pos() < oldpar.size()
1436                     && oldpar.isLineSeparator(old.pos())
1437                     && oldpar.isLineSeparator(old.pos() - 1)) {
1438                         pars_[old.pit()].erase(old.pos() - 1);
1439 #ifdef WITH_WARNINGS
1440 #warning This will not work anymore when we have multiple views of the same buffer
1441 // In this case, we will have to correct also the cursors held by
1442 // other bufferviews. It will probably be easier to do that in a more
1443 // automated way in CursorSlice code. (JMarc 26/09/2001)
1444 #endif
1445                         // correct all cursor parts
1446                         fixCursorAfterDelete(cur.top(), old.top());
1447 #ifdef WITH_WARNINGS
1448 #warning DEPM, look here
1449 #endif
1450                         //fixCursorAfterDelete(cur.anchor(), old.top());
1451                         return true;
1452                 }
1453         }
1454
1455         // only do our magic if we changed paragraph
1456         if (old.pit() == cur.pit())
1457                 return false;
1458
1459         // don't delete anything if this is the ONLY paragraph!
1460         if (pars_.size() == 1)
1461                 return false;
1462
1463         // Do not delete empty paragraphs with keepempty set.
1464         if (oldpar.allowEmpty())
1465                 return false;
1466
1467         // record if we have deleted a paragraph
1468         // we can't possibly have deleted a paragraph before this point
1469         bool deleted = false;
1470
1471         if (oldpar.empty() || (oldpar.size() == 1 && oldpar.isLineSeparator(0))) {
1472                 // ok, we will delete something
1473                 deleted = true;
1474
1475                 bool selection_position_was_oldcursor_position =
1476                         cur.anchor().pit() == old.pit() && cur.anchor().pos() == old.pos();
1477
1478                 // This is a bit of a overkill. We change the old and the cur par
1479                 // at max, certainly not everything in between...
1480                 recUndo(old.pit(), cur.pit());
1481
1482                 // Delete old par.
1483                 pars_.erase(pars_.begin() + old.pit());
1484
1485                 // Update cursor par offset if necessary.
1486                 // Some 'iterator registration' would be nice that takes care of
1487                 // such events. Maybe even signal/slot?
1488                 if (cur.pit() > old.pit())
1489                         --cur.pit();
1490 #ifdef WITH_WARNINGS
1491 #warning DEPM, look here
1492 #endif
1493 //              if (cur.anchor().pit() > old.pit())
1494 //                      --cur.anchor().pit();
1495
1496                 if (selection_position_was_oldcursor_position) {
1497                         // correct selection
1498                         cur.resetAnchor();
1499                 }
1500         }
1501
1502         if (deleted)
1503                 return true;
1504
1505         if (pars_[old.pit()].stripLeadingSpaces())
1506                 cur.resetAnchor();
1507
1508         return false;
1509 }
1510
1511
1512 ParagraphList & LyXText::paragraphs() const
1513 {
1514         return const_cast<ParagraphList &>(pars_);
1515 }
1516
1517
1518 void LyXText::recUndo(pit_type first, pit_type last) const
1519 {
1520         recordUndo(bv()->cursor(), Undo::ATOMIC, first, last);
1521 }
1522
1523
1524 void LyXText::recUndo(pit_type par) const
1525 {
1526         recordUndo(bv()->cursor(), Undo::ATOMIC, par, par);
1527 }
1528
1529
1530 int defaultRowHeight()
1531 {
1532         return int(font_metrics::maxHeight(LyXFont(LyXFont::ALL_SANE)) *  1.2);
1533 }