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