]> git.lyx.org Git - lyx.git/blob - src/text2.C
make all cached coordinates mean the same (0,0) == upper left screen
[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(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(par_type pit, pos_type pos) const
153 {
154         BOOST_ASSERT(pos >= 0);
155
156         LyXLayout_ptr const & layout = pars_[pit].layout();
157 #ifdef WITH_WARNINGS
158 #warning broken?
159 #endif
160         BufferParams const & params = bv()->buffer()->params();
161         pos_type const body_pos = pars_[pit].beginOfBody();
162
163         // We specialize the 95% common case:
164         if (!pars_[pit].getDepth()) {
165                 LyXFont f = pars_[pit].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 = pars_[pit].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(outerFont(pit, paragraphs()));
189         font.realize(defaultfont_);
190
191         return font;
192 }
193
194
195 LyXFont LyXText::getLayoutFont(par_type pit) const
196 {
197         LyXLayout_ptr const & layout = pars_[pit].layout();
198
199         if (!pars_[pit].getDepth())
200                 return layout->resfont;
201
202         LyXFont font = layout->font;
203         // Realize with the fonts of lesser depth.
204         //font.realize(outerFont(pit, paragraphs()));
205         font.realize(defaultfont_);
206
207         return font;
208 }
209
210
211 LyXFont LyXText::getLabelFont(par_type pit) const
212 {
213         LyXLayout_ptr const & layout = pars_[pit].layout();
214
215         if (!pars_[pit].getDepth())
216                 return layout->reslabelfont;
217
218         LyXFont font = layout->labelfont;
219         // Realize with the fonts of lesser depth.
220         font.realize(outerFont(pit, paragraphs()));
221         font.realize(defaultfont_);
222
223         return font;
224 }
225
226
227 void LyXText::setCharFont(par_type pit, pos_type pos, LyXFont const & fnt)
228 {
229         LyXFont font = fnt;
230         LyXLayout_ptr const & layout = pars_[pit].layout();
231
232         // Get concrete layout font to reduce against
233         LyXFont layoutfont;
234
235         if (pos < pars_[pit].beginOfBody())
236                 layoutfont = layout->labelfont;
237         else
238                 layoutfont = layout->font;
239
240         // Realize against environment font information
241         if (pars_[pit].getDepth()) {
242                 par_type tp = pit;
243                 while (!layoutfont.resolved() &&
244                        tp != par_type(paragraphs().size()) &&
245                        pars_[tp].getDepth()) {
246                         tp = outerHook(tp, paragraphs());
247                         if (tp != par_type(paragraphs().size()))
248                                 layoutfont.realize(pars_[tp].layout()->font);
249                 }
250         }
251
252         layoutfont.realize(defaultfont_);
253
254         // Now, reduce font against full layout font
255         font.reduce(layoutfont);
256
257         pars_[pit].setFont(pos, font);
258 }
259
260
261 // used in setLayout
262 // Asger is not sure we want to do this...
263 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
264                                             Paragraph & par)
265 {
266         LyXLayout_ptr const & layout = par.layout();
267         pos_type const psize = par.size();
268
269         LyXFont layoutfont;
270         for (pos_type pos = 0; pos < psize; ++pos) {
271                 if (pos < par.beginOfBody())
272                         layoutfont = layout->labelfont;
273                 else
274                         layoutfont = layout->font;
275
276                 LyXFont tmpfont = par.getFontSettings(params, pos);
277                 tmpfont.reduce(layoutfont);
278                 par.setFont(pos, tmpfont);
279         }
280 }
281
282
283 // return past-the-last paragraph influenced by a layout change on pit
284 par_type LyXText::undoSpan(par_type pit)
285 {
286         par_type end = paragraphs().size();
287         par_type nextpit = pit + 1;
288         if (nextpit == end)
289                 return nextpit;
290         //because of parindents
291         if (!pars_[pit].getDepth())
292                 return boost::next(nextpit);
293         //because of depth constrains
294         for (; nextpit != end; ++pit, ++nextpit) {
295                 if (!pars_[pit].getDepth())
296                         break;
297         }
298         return nextpit;
299 }
300
301
302 par_type LyXText::setLayout(par_type start, par_type end, string const & layout)
303 {
304         BOOST_ASSERT(start != end);
305         par_type undopit = undoSpan(end - 1);
306         recUndo(start, undopit - 1);
307
308         BufferParams const & bufparams = bv()->buffer()->params();
309         LyXLayout_ptr const & lyxlayout = bufparams.getLyXTextClass()[layout];
310
311         for (par_type pit = start; pit != end; ++pit) {
312                 pars_[pit].applyLayout(lyxlayout);
313                 makeFontEntriesLayoutSpecific(bufparams, pars_[pit]);
314                 if (lyxlayout->margintype == MARGIN_MANUAL)
315                         pars_[pit].setLabelWidthString(lyxlayout->labelstring());
316         }
317
318         return undopit;
319 }
320
321
322 // set layout over selection and make a total rebreak of those paragraphs
323 void LyXText::setLayout(LCursor & cur, string const & layout)
324 {
325         BOOST_ASSERT(this == cur.text());
326         // special handling of new environment insets
327         BufferView & bv = cur.bv();
328         BufferParams const & params = bv.buffer()->params();
329         LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
330         if (lyxlayout->is_environment) {
331                 // move everything in a new environment inset
332                 lyxerr[Debug::DEBUG] << "setting layout " << layout << endl;
333                 bv.owner()->dispatch(FuncRequest(LFUN_HOME));
334                 bv.owner()->dispatch(FuncRequest(LFUN_ENDSEL));
335                 bv.owner()->dispatch(FuncRequest(LFUN_CUT));
336                 InsetBase * inset = new InsetEnvironment(params, layout);
337                 insertInset(cur, inset);
338                 //inset->edit(cur, true);
339                 //bv.owner()->dispatch(FuncRequest(LFUN_PASTE));
340                 return;
341         }
342
343         par_type start = cur.selBegin().par();
344         par_type end = cur.selEnd().par() + 1;
345         par_type endpit = setLayout(start, end, layout);
346         redoParagraphs(start, endpit);
347         updateCounters();
348 }
349
350
351 namespace {
352
353
354 void getSelectionSpan(LCursor & cur, par_type & beg, par_type & end)
355 {
356         if (!cur.selection()) {
357                 beg = cur.par();
358                 end = cur.par() + 1;
359         } else {
360                 beg = cur.selBegin().par();
361                 end = cur.selEnd().par() + 1;
362         }
363 }
364
365
366 bool changeDepthAllowed(LyXText::DEPTH_CHANGE type,
367         Paragraph const & par, int max_depth)
368 {
369         if (par.layout()->labeltype == LABEL_BIBLIO)
370                 return false;
371         int const depth = par.params().depth();
372         if (type == LyXText::INC_DEPTH && depth < max_depth)
373                 return true;
374         if (type == LyXText::DEC_DEPTH && depth > 0)
375                 return true;
376         return false;
377 }
378
379
380 }
381
382
383 bool LyXText::changeDepthAllowed(LCursor & cur, DEPTH_CHANGE type) const
384 {
385         BOOST_ASSERT(this == cur.text());
386         par_type beg, end;
387         getSelectionSpan(cur, beg, end);
388         int max_depth = 0;
389         if (beg != 0)
390                 max_depth = pars_[beg - 1].getMaxDepthAfter();
391
392         for (par_type pit = beg; pit != end; ++pit) {
393                 if (::changeDepthAllowed(type, pars_[pit], max_depth))
394                         return true;
395                 max_depth = pars_[pit].getMaxDepthAfter();
396         }
397         return false;
398 }
399
400
401 void LyXText::changeDepth(LCursor & cur, DEPTH_CHANGE type)
402 {
403         BOOST_ASSERT(this == cur.text());
404         par_type beg, end;
405         getSelectionSpan(cur, beg, end);
406         recordUndoSelection(cur);
407
408         int max_depth = 0;
409         if (beg != 0)
410                 max_depth = pars_[beg - 1].getMaxDepthAfter();
411
412         for (par_type pit = beg; pit != end; ++pit) {
413                 if (::changeDepthAllowed(type, pars_[pit], max_depth)) {
414                         int const depth = pars_[pit].params().depth();
415                         if (type == INC_DEPTH)
416                                 pars_[pit].params().depth(depth + 1);
417                         else
418                                 pars_[pit].params().depth(depth - 1);
419                 }
420                 max_depth = pars_[pit].getMaxDepthAfter();
421         }
422         // this handles the counter labels, and also fixes up
423         // depth values for follow-on (child) paragraphs
424         updateCounters();
425 }
426
427
428 // set font over selection
429 void LyXText::setFont(LCursor & cur, LyXFont const & font, bool toggleall)
430 {
431         BOOST_ASSERT(this == cur.text());
432         // if there is no selection just set the current_font
433         if (!cur.selection()) {
434                 // Determine basis font
435                 LyXFont layoutfont;
436                 par_type pit = cur.par();
437                 if (cur.pos() < pars_[pit].beginOfBody())
438                         layoutfont = getLabelFont(pit);
439                 else
440                         layoutfont = getLayoutFont(pit);
441
442                 // Update current font
443                 real_current_font.update(font,
444                                          cur.buffer().params().language,
445                                          toggleall);
446
447                 // Reduce to implicit settings
448                 current_font = real_current_font;
449                 current_font.reduce(layoutfont);
450                 // And resolve it completely
451                 real_current_font.realize(layoutfont);
452
453                 return;
454         }
455
456         // Ok, we have a selection.
457         recordUndoSelection(cur);
458
459         par_type const beg = cur.selBegin().par();
460         par_type const end = cur.selEnd().par();
461
462         DocIterator pos = cur.selectionBegin();
463         DocIterator posend = cur.selectionEnd();
464
465         lyxerr[Debug::DEBUG] << "pos: " << pos << " posend: " << posend
466                              << endl;
467
468         BufferParams const & params = cur.buffer().params();
469
470         // Don't use forwardChar here as posend might have
471         // pos() == lastpos() and forwardChar would miss it.
472         for (; pos != posend; pos.forwardPos()) {
473                 if (pos.pos() != pos.lastpos()) {
474                         LyXFont f = getFont(pos.par(), pos.pos());
475                         f.update(font, params.language, toggleall);
476                         setCharFont(pos.par(), pos.pos(), f);
477                 }
478         }
479
480         redoParagraphs(beg, end + 1);
481 }
482
483
484 // the cursor set functions have a special mechanism. When they
485 // realize you left an empty paragraph, they will delete it.
486
487 void LyXText::cursorHome(LCursor & cur)
488 {
489         BOOST_ASSERT(this == cur.text());
490         setCursor(cur, cur.par(), cur.textRow().pos());
491 }
492
493
494 void LyXText::cursorEnd(LCursor & cur)
495 {
496         BOOST_ASSERT(this == cur.text());
497         // if not on the last row of the par, put the cursor before
498         // the final space
499         pos_type const end = cur.textRow().endpos();
500         setCursor(cur, cur.par(), end == cur.lastpos() ? end : end - 1);
501 }
502
503
504 void LyXText::cursorTop(LCursor & cur)
505 {
506         BOOST_ASSERT(this == cur.text());
507         setCursor(cur, 0, 0);
508 }
509
510
511 void LyXText::cursorBottom(LCursor & cur)
512 {
513         BOOST_ASSERT(this == cur.text());
514         setCursor(cur, cur.lastpar(), boost::prior(paragraphs().end())->size());
515 }
516
517
518 void LyXText::toggleFree(LCursor & cur, LyXFont const & font, bool toggleall)
519 {
520         BOOST_ASSERT(this == cur.text());
521         // If the mask is completely neutral, tell user
522         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
523                 // Could only happen with user style
524                 cur.message(_("No font change defined. "
525                         "Use Character under the Layout menu to define font change."));
526                 return;
527         }
528
529         // Try implicit word selection
530         // If there is a change in the language the implicit word selection
531         // is disabled.
532         CursorSlice resetCursor = cur.top();
533         bool implicitSelection =
534                 font.language() == ignore_language
535                 && font.number() == LyXFont::IGNORE
536                 && selectWordWhenUnderCursor(cur, lyx::WHOLE_WORD_STRICT);
537
538         // Set font
539         setFont(cur, font, toggleall);
540
541         // Implicit selections are cleared afterwards
542         // and cursor is set to the original position.
543         if (implicitSelection) {
544                 cur.clearSelection();
545                 cur.top() = resetCursor;
546                 cur.resetAnchor();
547         }
548 }
549
550
551 string LyXText::getStringToIndex(LCursor & cur)
552 {
553         BOOST_ASSERT(this == cur.text());
554         // Try implicit word selection
555         // If there is a change in the language the implicit word selection
556         // is disabled.
557         CursorSlice const reset_cursor = cur.top();
558         bool const implicitSelection =
559                 selectWordWhenUnderCursor(cur, lyx::PREVIOUS_WORD);
560
561         string idxstring;
562         if (!cur.selection())
563                 cur.message(_("Nothing to index!"));
564         else if (cur.selBegin().par() != cur.selEnd().par())
565                 cur.message(_("Cannot index more than one paragraph!"));
566         else
567                 idxstring = cur.selectionAsString(false);
568
569         // Reset cursors to their original position.
570         cur.top() = reset_cursor;
571         cur.resetAnchor();
572
573         // Clear the implicit selection.
574         if (implicitSelection)
575                 cur.clearSelection();
576
577         return idxstring;
578 }
579
580
581 void LyXText::setParagraph(LCursor & cur,
582         Spacing const & spacing, LyXAlignment align,
583         string const & labelwidthstring, bool noindent)
584 {
585         BOOST_ASSERT(cur.text());
586         // make sure that the depth behind the selection are restored, too
587         par_type undopit = undoSpan(cur.selEnd().par());
588         recUndo(cur.selBegin().par(), undopit - 1);
589
590         for (par_type pit = cur.selBegin().par(), end = cur.selEnd().par();
591                         pit <= end; ++pit) {
592                 Paragraph & par = pars_[pit];
593                 ParagraphParameters & params = par.params();
594                 params.spacing(spacing);
595
596                 // does the layout allow the new alignment?
597                 LyXLayout_ptr const & layout = par.layout();
598
599                 if (align == LYX_ALIGN_LAYOUT)
600                         align = layout->align;
601                 if (align & layout->alignpossible) {
602                         if (align == layout->align)
603                                 params.align(LYX_ALIGN_LAYOUT);
604                         else
605                                 params.align(align);
606                 }
607                 par.setLabelWidthString(labelwidthstring);
608                 params.noindent(noindent);
609         }
610
611         redoParagraphs(cur.selBegin().par(), undopit);
612 }
613
614
615 string expandLabel(LyXTextClass const & textclass,
616         LyXLayout_ptr const & layout, bool appendix)
617 {
618         string fmt = appendix ?
619                 layout->labelstring_appendix() : layout->labelstring();
620
621         // handle 'inherited level parts' in 'fmt',
622         // i.e. the stuff between '@' in   '@Section@.\arabic{subsection}'
623         size_t const i = fmt.find('@', 0);
624         if (i != string::npos) {
625                 size_t const j = fmt.find('@', i + 1);
626                 if (j != string::npos) {
627                         string parent(fmt, i + 1, j - i - 1);
628                         string label = expandLabel(textclass, textclass[parent], appendix);
629                         fmt = string(fmt, 0, i) + label + string(fmt, j + 1, string::npos);
630                 }
631         }
632
633         return textclass.counters().counterLabel(fmt);
634 }
635
636
637 namespace {
638
639 void incrementItemDepth(ParagraphList & pars, par_type pit, par_type first_pit)
640 {
641         int const cur_labeltype = pars[pit].layout()->labeltype;
642
643         if (cur_labeltype != LABEL_ENUMERATE && cur_labeltype != LABEL_ITEMIZE)
644                 return;
645
646         int const cur_depth = pars[pit].getDepth();
647
648         par_type prev_pit = pit - 1;
649         while (true) {
650                 int const prev_depth = pars[prev_pit].getDepth();
651                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
652                 if (prev_depth == 0 && cur_depth > 0) {
653                         if (prev_labeltype == cur_labeltype) {
654                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
655                         }
656                         break;
657                 } else if (prev_depth < cur_depth) {
658                         if (prev_labeltype == cur_labeltype) {
659                                 pars[pit].itemdepth = pars[prev_pit].itemdepth + 1;
660                                 break;
661                         }
662                 } else if (prev_depth == cur_depth) {
663                         if (prev_labeltype == cur_labeltype) {
664                                 pars[pit].itemdepth = pars[prev_pit].itemdepth;
665                                 break;
666                         }
667                 }
668                 if (prev_pit == first_pit)
669                         break;
670
671                 --prev_pit;
672         }
673 }
674
675
676 void resetEnumCounterIfNeeded(ParagraphList & pars, par_type pit,
677         par_type firstpit, Counters & counters)
678 {
679         if (pit == firstpit)
680                 return;
681
682         int const cur_depth = pars[pit].getDepth();
683         par_type prev_pit = pit - 1;
684         while (true) {
685                 int const prev_depth = pars[prev_pit].getDepth();
686                 int const prev_labeltype = pars[prev_pit].layout()->labeltype;
687                 if (prev_depth <= cur_depth) {
688                         if (prev_labeltype != LABEL_ENUMERATE) {
689                                 switch (pars[pit].itemdepth) {
690                                 case 0:
691                                         counters.reset("enumi");
692                                 case 1:
693                                         counters.reset("enumii");
694                                 case 2:
695                                         counters.reset("enumiii");
696                                 case 3:
697                                         counters.reset("enumiv");
698                                 }
699                         }
700                         break;
701                 }
702
703                 if (prev_pit == firstpit)
704                         break;
705
706                 --prev_pit;
707         }
708 }
709
710 } // anon namespace
711
712
713 // set the counter of a paragraph. This includes the labels
714 void LyXText::setCounter(Buffer const & buf, par_type pit)
715 {
716         BufferParams const & bufparams = buf.params();
717         LyXTextClass const & textclass = bufparams.getLyXTextClass();
718         LyXLayout_ptr const & layout = pars_[pit].layout();
719         par_type first_pit = 0;
720         Counters & counters = textclass.counters();
721
722         // Always reset
723         pars_[pit].itemdepth = 0;
724
725         if (pit == first_pit) {
726                 pars_[pit].params().appendix(pars_[pit].params().startOfAppendix());
727         } else {
728                 pars_[pit].params().appendix(pars_[pit - 1].params().appendix());
729                 if (!pars_[pit].params().appendix() &&
730                     pars_[pit].params().startOfAppendix()) {
731                         pars_[pit].params().appendix(true);
732                         textclass.counters().reset();
733                 }
734
735                 // Maybe we have to increment the item depth.
736                 incrementItemDepth(pars_, pit, first_pit);
737         }
738
739         // erase what was there before
740         pars_[pit].params().labelString(string());
741
742         if (layout->margintype == MARGIN_MANUAL) {
743                 if (pars_[pit].params().labelWidthString().empty())
744                         pars_[pit].setLabelWidthString(layout->labelstring());
745         } else {
746                 pars_[pit].setLabelWidthString(string());
747         }
748
749         // is it a layout that has an automatic label?
750         if (layout->labeltype == LABEL_COUNTER) {
751                 BufferParams const & bufparams = buf.params();
752                 LyXTextClass const & textclass = bufparams.getLyXTextClass();
753                 counters.step(layout->counter);
754                 string label = expandLabel(textclass, layout, pars_[pit].params().appendix());
755                 pars_[pit].params().labelString(label);
756         } else if (layout->labeltype == LABEL_ITEMIZE) {
757                 // At some point of time we should do something more
758                 // clever here, like:
759                 //   pars_[pit].params().labelString(
760                 //    bufparams.user_defined_bullet(pars_[pit].itemdepth).getText());
761                 // for now, use a simple hardcoded label
762                 string itemlabel;
763                 switch (pars_[pit].itemdepth) {
764                 case 0:
765                         itemlabel = "*";
766                         break;
767                 case 1:
768                         itemlabel = "-";
769                         break;
770                 case 2:
771                         itemlabel = "@";
772                         break;
773                 case 3:
774                         itemlabel = "·";
775                         break;
776                 }
777
778                 pars_[pit].params().labelString(itemlabel);
779         } else if (layout->labeltype == LABEL_ENUMERATE) {
780                 // Maybe we have to reset the enumeration counter.
781                 resetEnumCounterIfNeeded(pars_, pit, first_pit, counters);
782
783                 // FIXME
784                 // Yes I know this is a really, really! bad solution
785                 // (Lgb)
786                 string enumcounter = "enum";
787
788                 switch (pars_[pit].itemdepth) {
789                 case 2:
790                         enumcounter += 'i';
791                 case 1:
792                         enumcounter += 'i';
793                 case 0:
794                         enumcounter += 'i';
795                         break;
796                 case 3:
797                         enumcounter += "iv";
798                         break;
799                 default:
800                         // not a valid enumdepth...
801                         break;
802                 }
803
804                 counters.step(enumcounter);
805
806                 pars_[pit].params().labelString(counters.enumLabel(enumcounter));
807         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
808                 counters.step("bibitem");
809                 int number = counters.value("bibitem");
810                 if (pars_[pit].bibitem()) {
811                         pars_[pit].bibitem()->setCounter(number);
812                         pars_[pit].params().labelString(layout->labelstring());
813                 }
814                 // In biblio should't be following counters but...
815         } else {
816                 string s = buf.B_(layout->labelstring());
817
818                 // the caption hack:
819                 if (layout->labeltype == LABEL_SENSITIVE) {
820                         par_type end = paragraphs().size();
821                         par_type tmppit = pit;
822                         InsetBase * in = 0;
823                         bool isOK = false;
824                         while (tmppit != end) {
825                                 in = pars_[tmppit].inInset();
826                                 if (in->lyxCode() == InsetBase::FLOAT_CODE ||
827                                     in->lyxCode() == InsetBase::WRAP_CODE) {
828                                         isOK = true;
829                                         break;
830                                 } else {
831                                         Paragraph const * owner = &ownerPar(buf, in);
832                                         tmppit = first_pit;
833                                         for ( ; tmppit != end; ++tmppit)
834                                                 if (&pars_[tmppit] == owner)
835                                                         break;
836                                 }
837                         }
838
839                         if (isOK) {
840                                 string type;
841
842                                 if (in->lyxCode() == InsetBase::FLOAT_CODE)
843                                         type = static_cast<InsetFloat*>(in)->params().type;
844                                 else if (in->lyxCode() == InsetBase::WRAP_CODE)
845                                         type = static_cast<InsetWrap*>(in)->params().type;
846                                 else
847                                         BOOST_ASSERT(false);
848
849                                 Floating const & fl = textclass.floats().getType(type);
850
851                                 counters.step(fl.type());
852
853                                 // Doesn't work... yet.
854                                 s = bformat(_("%1$s #:"), buf.B_(fl.name()));
855                         } else {
856                                 // par->SetLayout(0);
857                                 // s = layout->labelstring;
858                                 s = _("Senseless: ");
859                         }
860                 }
861                 pars_[pit].params().labelString(s);
862
863         }
864 }
865
866
867 // Updates all counters.
868 void LyXText::updateCounters()
869 {
870         // start over
871         bv()->buffer()->params().getLyXTextClass().counters().reset();
872
873         bool update_pos = false;
874
875         par_type end = paragraphs().size();
876         for (par_type pit = 0; pit != end; ++pit) {
877                 string const oldLabel = pars_[pit].params().labelString();
878                 size_t maxdepth = 0;
879                 if (pit != 0)
880                         maxdepth = pars_[pit - 1].getMaxDepthAfter();
881
882                 if (pars_[pit].params().depth() > maxdepth)
883                         pars_[pit].params().depth(maxdepth);
884
885                 // setCounter can potentially change the labelString.
886                 setCounter(*bv()->buffer(), pit);
887                 string const & newLabel = pars_[pit].params().labelString();
888                 if (oldLabel != newLabel) {
889                         //lyxerr[Debug::DEBUG] << "changing labels: old: " << oldLabel << " new: "
890                         //      << newLabel << endl;
891                         redoParagraphInternal(pit);
892                         update_pos = true;
893                 }
894         }
895         if (update_pos)
896                 updateParPositions();
897 }
898
899
900 // this really should just inset the inset and not move the cursor.
901 void LyXText::insertInset(LCursor & cur, InsetBase * inset)
902 {
903         BOOST_ASSERT(this == cur.text());
904         BOOST_ASSERT(inset);
905         cur.paragraph().insertInset(cur.pos(), inset);
906         redoParagraph(cur);
907 }
908
909
910 // needed to insert the selection
911 void LyXText::insertStringAsLines(LCursor & cur, string const & str)
912 {
913         par_type pit = cur.par();
914         par_type endpit = cur.par() + 1;
915         pos_type pos = cur.pos();
916         recordUndo(cur);
917
918         // only to be sure, should not be neccessary
919         cur.clearSelection();
920         cur.buffer().insertStringAsLines(pars_, pit, pos, current_font, str);
921
922         redoParagraphs(cur.par(), endpit);
923         cur.resetAnchor();
924         setCursor(cur, cur.par(), pos);
925         cur.setSelection();
926 }
927
928
929 // turn double CR to single CR, others are converted into one
930 // blank. Then insertStringAsLines is called
931 void LyXText::insertStringAsParagraphs(LCursor & cur, string const & str)
932 {
933         string linestr = str;
934         bool newline_inserted = false;
935
936         for (string::size_type i = 0, siz = linestr.size(); i < siz; ++i) {
937                 if (linestr[i] == '\n') {
938                         if (newline_inserted) {
939                                 // we know that \r will be ignored by
940                                 // insertStringAsLines. Of course, it is a dirty
941                                 // trick, but it works...
942                                 linestr[i - 1] = '\r';
943                                 linestr[i] = '\n';
944                         } else {
945                                 linestr[i] = ' ';
946                                 newline_inserted = true;
947                         }
948                 } else if (IsPrintable(linestr[i])) {
949                         newline_inserted = false;
950                 }
951         }
952         insertStringAsLines(cur, linestr);
953 }
954
955
956 bool LyXText::setCursor(LCursor & cur, par_type par, pos_type pos,
957         bool setfont, bool boundary)
958 {
959         LCursor old = cur;
960         setCursorIntern(cur, par, pos, setfont, boundary);
961         return deleteEmptyParagraphMechanism(cur, old);
962 }
963
964
965 void LyXText::setCursor(CursorSlice & cur, par_type par,
966         pos_type pos, bool boundary)
967 {
968         BOOST_ASSERT(par != int(paragraphs().size()));
969
970         cur.par() = par;
971         cur.pos() = pos;
972         cur.boundary() = boundary;
973
974         // no rows, no fun...
975         if (paragraphs().begin()->rows.empty())
976                 return;
977
978         // now some strict checking
979         Paragraph & para = getPar(par);
980         Row const & row = *para.getRow(pos);
981         pos_type const end = row.endpos();
982
983         // None of these should happen, but we're scaredy-cats
984         if (pos < 0) {
985                 lyxerr << "dont like -1" << endl;
986                 BOOST_ASSERT(false);
987         }
988
989         if (pos > para.size()) {
990                 lyxerr << "dont like 1, pos: " << pos
991                        << " size: " << para.size()
992                        << " row.pos():" << row.pos()
993                        << " par: " << par << endl;
994                 BOOST_ASSERT(false);
995         }
996
997         if (pos > end) {
998                 lyxerr << "dont like 2, pos: " << pos
999                        << " size: " << para.size()
1000                        << " row.pos():" << row.pos()
1001                        << " par: " << par << endl;
1002                 // This shouldn't happen.
1003                 BOOST_ASSERT(false);
1004         }
1005
1006         if (pos < row.pos()) {
1007                 lyxerr << "dont like 3 please report pos:" << pos
1008                        << " size: " << para.size()
1009                        << " row.pos():" << row.pos()
1010                        << " par: " << par << endl;
1011                 BOOST_ASSERT(false);
1012         }
1013 }
1014
1015
1016 void LyXText::setCursorIntern(LCursor & cur,
1017         par_type par, pos_type pos, bool setfont, bool boundary)
1018 {
1019         setCursor(cur.top(), par, pos, boundary);
1020         cur.x_target() = cursorX(cur.top());
1021         if (setfont)
1022                 setCurrentFont(cur);
1023 }
1024
1025
1026 void LyXText::setCurrentFont(LCursor & cur)
1027 {
1028         BOOST_ASSERT(this == cur.text());
1029         pos_type pos = cur.pos();
1030         par_type pit = cur.par();
1031
1032         if (cur.boundary() && pos > 0)
1033                 --pos;
1034
1035         if (pos > 0) {
1036                 if (pos == cur.lastpos())
1037                         --pos;
1038                 else // potentional bug... BUG (Lgb)
1039                         if (pars_[pit].isSeparator(pos)) {
1040                                 if (pos > cur.textRow().pos() &&
1041                                     bidi.level(pos) % 2 ==
1042                                     bidi.level(pos - 1) % 2)
1043                                         --pos;
1044                                 else if (pos + 1 < cur.lastpos())
1045                                         ++pos;
1046                         }
1047         }
1048
1049         BufferParams const & bufparams = cur.buffer().params();
1050         current_font = pars_[pit].getFontSettings(bufparams, pos);
1051         real_current_font = getFont(pit, pos);
1052
1053         if (cur.pos() == cur.lastpos()
1054             && bidi.isBoundary(cur.buffer(), pars_[pit], cur.pos())
1055             && !cur.boundary()) {
1056                 Language const * lang = pars_[pit].getParLanguage(bufparams);
1057                 current_font.setLanguage(lang);
1058                 current_font.setNumber(LyXFont::OFF);
1059                 real_current_font.setLanguage(lang);
1060                 real_current_font.setNumber(LyXFont::OFF);
1061         }
1062 }
1063
1064
1065 // x is an absolute screen coord
1066 // returns the column near the specified x-coordinate of the row
1067 // x is set to the real beginning of this column
1068 pos_type LyXText::getColumnNearX(par_type pit,
1069         Row const & row, int & x, bool & boundary) const
1070 {
1071         x -= xo_;
1072         RowMetrics const r = computeRowMetrics(pit, row);
1073
1074         pos_type vc = row.pos();
1075         pos_type end = row.endpos();
1076         pos_type c = 0;
1077         LyXLayout_ptr const & layout = pars_[pit].layout();
1078
1079         bool left_side = false;
1080
1081         pos_type body_pos = pars_[pit].beginOfBody();
1082
1083         double tmpx = r.x;
1084         double last_tmpx = tmpx;
1085
1086         if (body_pos > 0 &&
1087             (body_pos > end || !pars_[pit].isLineSeparator(body_pos - 1)))
1088                 body_pos = 0;
1089
1090         // check for empty row
1091         if (vc == end) {
1092                 x = int(tmpx) + xo_;
1093                 return 0;
1094         }
1095
1096         while (vc < end && tmpx <= x) {
1097                 c = bidi.vis2log(vc);
1098                 last_tmpx = tmpx;
1099                 if (body_pos > 0 && c == body_pos - 1) {
1100                         tmpx += r.label_hfill +
1101                                 font_metrics::width(layout->labelsep, getLabelFont(pit));
1102                         if (pars_[pit].isLineSeparator(body_pos - 1))
1103                                 tmpx -= singleWidth(pit, body_pos - 1);
1104                 }
1105
1106                 if (hfillExpansion(pars_[pit], row, c)) {
1107                         tmpx += singleWidth(pit, c);
1108                         if (c >= body_pos)
1109                                 tmpx += r.hfill;
1110                         else
1111                                 tmpx += r.label_hfill;
1112                 } else if (pars_[pit].isSeparator(c)) {
1113                         tmpx += singleWidth(pit, c);
1114                         if (c >= body_pos)
1115                                 tmpx += r.separator;
1116                 } else {
1117                         tmpx += singleWidth(pit, c);
1118                 }
1119                 ++vc;
1120         }
1121
1122         if ((tmpx + last_tmpx) / 2 > x) {
1123                 tmpx = last_tmpx;
1124                 left_side = true;
1125         }
1126
1127         BOOST_ASSERT(vc <= end);  // This shouldn't happen.
1128
1129         boundary = false;
1130         // This (rtl_support test) is not needed, but gives
1131         // some speedup if rtl_support == false
1132         bool const lastrow = lyxrc.rtl_support && row.endpos() == pars_[pit].size();
1133
1134         // If lastrow is false, we don't need to compute
1135         // the value of rtl.
1136         bool const rtl = lastrow ? isRTL(pars_[pit]) : false;
1137         if (lastrow &&
1138                  ((rtl  &&  left_side && vc == row.pos() && x < tmpx - 5) ||
1139                   (!rtl && !left_side && vc == end  && x > tmpx + 5)))
1140                 c = end;
1141         else if (vc == row.pos()) {
1142                 c = bidi.vis2log(vc);
1143                 if (bidi.level(c) % 2 == 1)
1144                         ++c;
1145         } else {
1146                 c = bidi.vis2log(vc - 1);
1147                 bool const rtl = (bidi.level(c) % 2 == 1);
1148                 if (left_side == rtl) {
1149                         ++c;
1150                         boundary = bidi.isBoundary(*bv()->buffer(), pars_[pit], c);
1151                 }
1152         }
1153
1154         if (row.pos() < end && c >= end && pars_[pit].isNewline(end - 1)) {
1155                 if (bidi.level(end -1) % 2 == 0)
1156                         tmpx -= singleWidth(pit, end - 1);
1157                 else
1158                         tmpx += singleWidth(pit, end - 1);
1159                 c = end - 1;
1160         }
1161
1162         x = int(tmpx) + xo_;
1163         return c - row.pos();
1164 }
1165
1166
1167 // y is relative to this LyXText's top
1168 // this is only used in the two functions below
1169 Row const & LyXText::getRowNearY(int y, par_type & pit) const
1170 {
1171         BOOST_ASSERT(!paragraphs().empty());
1172         BOOST_ASSERT(!paragraphs().begin()->rows.empty());
1173         par_type const pend = paragraphs().size() - 1;
1174         pit = 0;
1175         while (int(pars_[pit].y + pars_[pit].height) < y && pit != pend)
1176                 ++pit;
1177
1178         RowList::iterator rit = pars_[pit].rows.end();
1179         RowList::iterator const rbegin = pars_[pit].rows.begin();
1180         do {
1181                 --rit;
1182         } while (rit != rbegin && int(pars_[pit].y + rit->y_offset()) > y);
1183
1184         return *rit;
1185 }
1186
1187
1188 // x,y are absolute coordinates
1189 // sets cursor only within this LyXText
1190 void LyXText::setCursorFromCoordinates(LCursor & cur, int x, int y)
1191 {
1192         x -= xo_;
1193         y -= yo_;
1194         par_type pit;
1195         Row const & row = getRowNearY(y, pit);
1196         lyxerr[Debug::DEBUG] << "setCursorFromCoordinates:: hit row at: "
1197                              << row.pos() << endl;
1198         bool bound = false;
1199         int xx = x + xo_; // getRowNearX get absolute x coords
1200         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1201         setCursor(cur, pit, pos, true, bound);
1202 }
1203
1204
1205 // x,y are absolute screen coordinates
1206 // sets cursor recursively descending into nested editable insets
1207 InsetBase * LyXText::editXY(LCursor & cur, int x, int y) const
1208 {
1209         par_type pit;
1210         Row const & row = getRowNearY(y - yo_, pit);
1211         bool bound = false;
1212
1213         int xx = x; // is modified by getColumnNearX
1214         pos_type const pos = row.pos() + getColumnNearX(pit, row, xx, bound);
1215         cur.par() = pit;
1216         cur.pos() = pos;
1217         cur.boundary() = bound;
1218
1219         // try to descend into nested insets
1220         InsetBase * inset = checkInsetHit(x, y);
1221         lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
1222         if (!inset)
1223                 return 0;
1224
1225         // This should be just before or just behind the
1226         // cursor position set above.
1227         BOOST_ASSERT((pos != 0 && inset == pars_[pit].getInset(pos - 1))
1228                      || inset == pars_[pit].getInset(pos));
1229         // Make sure the cursor points to the position before
1230         // this inset.
1231         if (inset == pars_[pit].getInset(pos - 1))
1232                 --cur.pos();
1233         return inset->editXY(cur, x, y);
1234 }
1235
1236
1237 bool LyXText::checkAndActivateInset(LCursor & cur, bool front)
1238 {
1239         if (cur.selection())
1240                 return false;
1241         if (cur.pos() == cur.lastpos())
1242                 return false;
1243         InsetBase * inset = cur.nextInset();
1244         if (!isHighlyEditableInset(inset))
1245                 return false;
1246         inset->edit(cur, front);
1247         return true;
1248 }
1249
1250
1251 void LyXText::cursorLeft(LCursor & cur)
1252 {
1253         if (cur.pos() != 0) {
1254                 bool boundary = cur.boundary();
1255                 setCursor(cur, cur.par(), cur.pos() - 1, true, false);
1256                 if (!checkAndActivateInset(cur, false)) {
1257                         if (false && !boundary &&
1258                                         bidi.isBoundary(cur.buffer(), cur.paragraph(), cur.pos() + 1))
1259                                 setCursor(cur, cur.par(), cur.pos() + 1, true, true);
1260                 }
1261                 return;
1262         }
1263
1264         if (cur.par() != 0) {
1265                 // steps into the paragraph above
1266                 setCursor(cur, cur.par() - 1, getPar(cur.par() - 1).size());
1267         }
1268 }
1269
1270
1271 void LyXText::cursorRight(LCursor & cur)
1272 {
1273         if (false && cur.boundary()) {
1274                 setCursor(cur, cur.par(), cur.pos(), true, false);
1275                 return;
1276         }
1277
1278         if (cur.pos() != cur.lastpos()) {
1279                 if (!checkAndActivateInset(cur, true)) {
1280                         setCursor(cur, cur.par(), cur.pos() + 1, true, false);
1281                         if (false && bidi.isBoundary(cur.buffer(), cur.paragraph(),
1282                                                          cur.pos()))
1283                                 setCursor(cur, cur.par(), cur.pos(), true, true);
1284                 }
1285                 return;
1286         }
1287
1288         if (cur.par() != cur.lastpar())
1289                 setCursor(cur, cur.par() + 1, 0);
1290 }
1291
1292
1293 void LyXText::cursorUp(LCursor & cur)
1294 {
1295         Row const & row = cur.textRow();
1296         int x = cur.x_target();
1297         int y = cursorY(cur.top()) - row.baseline() - 1;
1298         setCursorFromCoordinates(cur, x, y);
1299
1300         if (!cur.selection()) {
1301                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1302                 if (inset_hit && isHighlyEditableInset(inset_hit))
1303                         inset_hit->editXY(cur, cur.x_target(), y);
1304         }
1305 }
1306
1307
1308 void LyXText::cursorDown(LCursor & cur)
1309 {
1310         Row const & row = cur.textRow();
1311         int x = cur.x_target();
1312         int y = cursorY(cur.top()) - row.baseline() + row.height() + 1;
1313         setCursorFromCoordinates(cur, x, y);
1314
1315         if (!cur.selection()) {
1316                 InsetBase * inset_hit = checkInsetHit(cur.x_target(), y);
1317                 if (inset_hit && isHighlyEditableInset(inset_hit))
1318                         inset_hit->editXY(cur, cur.x_target(), y);
1319         }
1320 }
1321
1322
1323 void LyXText::cursorUpParagraph(LCursor & cur)
1324 {
1325         if (cur.pos() > 0)
1326                 setCursor(cur, cur.par(), 0);
1327         else if (cur.par() != 0)
1328                 setCursor(cur, cur.par() - 1, 0);
1329 }
1330
1331
1332 void LyXText::cursorDownParagraph(LCursor & cur)
1333 {
1334         if (cur.par() != cur.lastpar())
1335                 setCursor(cur, cur.par() + 1, 0);
1336         else
1337                 setCursor(cur, cur.par(), 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.par() != where.par())
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.par()];
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.par() != cur.par() || 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.par()].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.par() == cur.par())
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().par() == old.par() && 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.par(), cur.par());
1451
1452                 // Delete old par.
1453                 pars_.erase(pars_.begin() + old.par());
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.par() > old.par())
1459                         --cur.par();
1460 #ifdef WITH_WARNINGS
1461 #warning DEPM, look here
1462 #endif
1463 //              if (cur.anchor().par() > old.par())
1464 //                      --cur.anchor().par();
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.par()].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(par_type first, par_type last) const
1489 {
1490         recordUndo(bv()->cursor(), Undo::ATOMIC, first, last);
1491 }
1492
1493
1494 void LyXText::recUndo(par_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 }