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