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