]> git.lyx.org Git - lyx.git/blob - src/text2.C
more not-so-useful stuff removed
[lyx.git] / src / text2.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #include "lyxtext.h"
14 #include "LString.h"
15 #include "Lsstream.h"
16 #include "paragraph.h"
17 #include "funcrequest.h"
18 #include "frontends/LyXView.h"
19 #include "undo_funcs.h"
20 #include "buffer.h"
21 #include "buffer_funcs.h"
22 #include "bufferparams.h"
23 #include "errorlist.h"
24 #include "gettext.h"
25 #include "BufferView.h"
26 #include "CutAndPaste.h"
27 #include "frontends/Painter.h"
28 #include "frontends/font_metrics.h"
29 #include "debug.h"
30 #include "lyxrc.h"
31 #include "lyxrow.h"
32 #include "FloatList.h"
33 #include "language.h"
34 #include "ParagraphParameters.h"
35 #include "counters.h"
36 #include "lyxrow_funcs.h"
37 #include "metricsinfo.h"
38 #include "paragraph_funcs.h"
39
40 #include "insets/insetbibitem.h"
41 #include "insets/insetenv.h"
42 #include "insets/insetfloat.h"
43 #include "insets/insetwrap.h"
44
45 #include "support/LAssert.h"
46 #include "support/textutils.h"
47 #include "support/lstrings.h"
48
49 #include <boost/tuple/tuple.hpp>
50
51 #include <algorithm>
52
53 using namespace lyx::support;
54
55 using std::vector;
56 using std::copy;
57 using std::endl;
58 using std::find;
59 using std::pair;
60 using lyx::pos_type;
61
62
63 LyXText::LyXText(BufferView * bv)
64         : height(0), width(0), anchor_row_offset_(0),
65           inset_owner(0), the_locking_inset(0), bv_owner(bv)
66 {
67         anchor_row_ = rows().end();
68 }
69
70
71 LyXText::LyXText(BufferView * bv, InsetText * inset)
72         : height(0), width(0), anchor_row_offset_(0),
73           inset_owner(inset), the_locking_inset(0), bv_owner(bv)
74 {
75         anchor_row_ = rows().end();
76 }
77
78
79 void LyXText::init(BufferView * bview)
80 {
81         bv_owner = bview;
82
83         rowlist_.clear();
84         width = 0;
85         height = 0;
86
87         anchor_row_ = rows().end();
88         anchor_row_offset_ = 0;
89
90         current_font = getFont(ownerParagraphs().begin(), 0);
91
92         redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
93
94         setCursorIntern(rowlist_.begin()->par(), 0);
95         selection.cursor = cursor;
96
97         updateCounters();
98 }
99
100
101 // Gets the fully instantiated font at a given position in a paragraph
102 // Basically the same routine as Paragraph::getFont() in paragraph.C.
103 // The difference is that this one is used for displaying, and thus we
104 // are allowed to make cosmetic improvements. For instance make footnotes
105 // smaller. (Asger)
106 // If position is -1, we get the layout font of the paragraph.
107 // If position is -2, we get the font of the manual label of the paragraph.
108 LyXFont LyXText::getFont(ParagraphList::iterator pit, pos_type pos) const
109 {
110         Assert(pos >= 0);
111
112         LyXLayout_ptr const & layout = pit->layout();
113 #warning broken?
114         BufferParams const & params = bv()->buffer()->params;
115
116         // We specialize the 95% common case:
117         if (!pit->getDepth()) {
118                 if (layout->labeltype == LABEL_MANUAL
119                     && pos < pit->beginningOfBody()) {
120                         // 1% goes here
121                         LyXFont f = pit->getFontSettings(params, pos);
122                         if (pit->inInset())
123                                 pit->inInset()->getDrawFont(f);
124                         return f.realize(layout->reslabelfont);
125                 } else {
126                         LyXFont f = pit->getFontSettings(params, pos);
127                         if (pit->inInset())
128                                 pit->inInset()->getDrawFont(f);
129                         return f.realize(layout->resfont);
130                 }
131         }
132
133         // The uncommon case need not be optimized as much
134
135         LyXFont layoutfont;
136
137         if (pos < pit->beginningOfBody()) {
138                 // 1% goes here
139                 layoutfont = layout->labelfont;
140         } else {
141                 // 99% goes here
142                 layoutfont = layout->font;
143         }
144
145         LyXFont tmpfont = pit->getFontSettings(params, pos);
146         tmpfont.realize(layoutfont);
147
148         if (pit->inInset())
149                 pit->inInset()->getDrawFont(tmpfont);
150
151         // Realize with the fonts of lesser depth.
152         tmpfont.realize(outerFont(pit, ownerParagraphs()));
153         tmpfont.realize(defaultfont_);
154
155         return tmpfont;
156 }
157
158
159 LyXFont LyXText::getLayoutFont(ParagraphList::iterator pit) const
160 {
161         LyXLayout_ptr const & layout = pit->layout();
162
163         if (!pit->getDepth())
164                 return layout->resfont;
165
166         LyXFont font = layout->font;
167         // Realize with the fonts of lesser depth.
168         font.realize(outerFont(pit, ownerParagraphs()));
169         font.realize(defaultfont_);
170
171         return font;
172 }
173
174
175 LyXFont LyXText::getLabelFont(ParagraphList::iterator pit) const
176 {
177         LyXLayout_ptr const & layout = pit->layout();
178
179         if (!pit->getDepth())
180                 return layout->reslabelfont;
181
182         LyXFont font = layout->labelfont;
183         // Realize with the fonts of lesser depth.
184         font.realize(outerFont(pit, ownerParagraphs()));
185         font.realize(defaultfont_);
186
187         return font;
188 }
189
190
191 void LyXText::setCharFont(ParagraphList::iterator pit,
192                           pos_type pos, LyXFont const & fnt,
193                           bool toggleall)
194 {
195         BufferParams const & params = bv()->buffer()->params;
196         LyXFont font = getFont(pit, pos);
197         font.update(fnt, params.language, toggleall);
198         // Let the insets convert their font
199         if (pit->isInset(pos)) {
200                 InsetOld * inset = pit->getInset(pos);
201                 if (isEditableInset(inset)) {
202                         static_cast<UpdatableInset *>(inset)
203                                 ->setFont(bv(), fnt, toggleall, true);
204                 }
205         }
206
207         // Plug through to version below:
208         setCharFont(pit, pos, font);
209 }
210
211
212 void LyXText::setCharFont(
213         ParagraphList::iterator pit, pos_type pos, LyXFont const & fnt)
214 {
215         LyXFont font = fnt;
216         LyXLayout_ptr const & layout = pit->layout();
217
218         // Get concrete layout font to reduce against
219         LyXFont layoutfont;
220
221         if (pos < pit->beginningOfBody())
222                 layoutfont = layout->labelfont;
223         else
224                 layoutfont = layout->font;
225
226         // Realize against environment font information
227         if (pit->getDepth()) {
228                 ParagraphList::iterator tp = pit;
229                 while (!layoutfont.resolved() &&
230                        tp != ownerParagraphs().end() &&
231                        tp->getDepth()) {
232                         tp = outerHook(tp, ownerParagraphs());
233                         if (tp != ownerParagraphs().end())
234                                 layoutfont.realize(tp->layout()->font);
235                 }
236         }
237
238         layoutfont.realize(defaultfont_);
239
240         // Now, reduce font against full layout font
241         font.reduce(layoutfont);
242
243         pit->setFont(pos, font);
244 }
245
246
247 // removes the row and reset the touched counters
248 void LyXText::removeRow(RowList::iterator rit)
249 {
250         if (anchor_row_ == rit) {
251                 if (rit != rows().begin()) {
252                         anchor_row_ = boost::prior(rit);
253                         anchor_row_offset_ += anchor_row_->height();
254                 } else {
255                         anchor_row_ = boost::next(rit);
256                         anchor_row_offset_ -= rit->height();
257                 }
258         }
259
260         // the text becomes smaller
261         height -= rit->height();
262
263         rowlist_.erase(rit);
264 }
265
266
267 // remove all following rows of the paragraph of the specified row.
268 void LyXText::removeParagraph(RowList::iterator rit)
269 {
270         ParagraphList::iterator tmppit = rit->par();
271         ++rit;
272
273         while (rit != rows().end() && rit->par() == tmppit) {
274                 RowList::iterator tmprit = boost::next(rit);
275                 removeRow(rit);
276                 rit = tmprit;
277         }
278 }
279
280
281 void LyXText::insertParagraph(ParagraphList::iterator pit,
282                               RowList::iterator rit)
283 {
284         // insert a new row, starting at position 0
285         rit = rowlist_.insert(rit, Row(pit, 0));
286
287         // and now append the whole paragraph before the new row
288         appendParagraph(rit);
289 }
290
291
292 InsetOld * LyXText::getInset() const
293 {
294         ParagraphList::iterator pit = cursor.par();
295         pos_type const pos = cursor.pos();
296
297         if (pos < pit->size() && pit->isInset(pos)) {
298                 return pit->getInset(pos);
299         }
300         return 0;
301 }
302
303
304 void LyXText::toggleInset()
305 {
306         InsetOld * inset = getInset();
307         // is there an editable inset at cursor position?
308         if (!isEditableInset(inset)) {
309                 // No, try to see if we are inside a collapsable inset
310                 if (inset_owner && inset_owner->owner()
311                     && inset_owner->owner()->isOpen()) {
312                         bv()->unlockInset(inset_owner->owner());
313                         inset_owner->owner()->close(bv());
314                         bv()->getLyXText()->cursorRight(bv());
315                 }
316                 return;
317         }
318         //bv()->owner()->message(inset->editMessage());
319
320         // do we want to keep this?? (JMarc)
321         if (!isHighlyEditableInset(inset))
322                 recordUndo(bv(), Undo::ATOMIC);
323
324         if (inset->isOpen())
325                 inset->close(bv());
326         else
327                 inset->open(bv());
328
329         bv()->updateInset();
330 }
331
332
333 /* used in setlayout */
334 // Asger is not sure we want to do this...
335 void LyXText::makeFontEntriesLayoutSpecific(BufferParams const & params,
336                                             Paragraph & par)
337 {
338         LyXLayout_ptr const & layout = par.layout();
339         pos_type const psize = par.size();
340
341         LyXFont layoutfont;
342         for (pos_type pos = 0; pos < psize; ++pos) {
343                 if (pos < par.beginningOfBody())
344                         layoutfont = layout->labelfont;
345                 else
346                         layoutfont = layout->font;
347
348                 LyXFont tmpfont = par.getFontSettings(params, pos);
349                 tmpfont.reduce(layoutfont);
350                 par.setFont(pos, tmpfont);
351         }
352 }
353
354
355 ParagraphList::iterator
356 LyXText::setLayout(LyXCursor & cur, LyXCursor & sstart_cur,
357                    LyXCursor & send_cur,
358                    string const & layout)
359 {
360         ParagraphList::iterator endpit = boost::next(send_cur.par());
361         ParagraphList::iterator undoendpit = endpit;
362         ParagraphList::iterator pars_end = ownerParagraphs().end();
363
364         if (endpit != pars_end && endpit->getDepth()) {
365                 while (endpit != pars_end && endpit->getDepth()) {
366                         ++endpit;
367                         undoendpit = endpit;
368                 }
369         } else if (endpit != pars_end) {
370                 // because of parindents etc.
371                 ++endpit;
372         }
373
374         recordUndo(bv(), Undo::ATOMIC, sstart_cur.par(), boost::prior(undoendpit));
375
376         // ok we have a selection. This is always between sstart_cur
377         // and sel_end cursor
378         cur = sstart_cur;
379         ParagraphList::iterator pit = sstart_cur.par();
380         ParagraphList::iterator epit = boost::next(send_cur.par());
381
382         LyXLayout_ptr const & lyxlayout =
383                 bv()->buffer()->params.getLyXTextClass()[layout];
384
385         do {
386                 pit->applyLayout(lyxlayout);
387                 makeFontEntriesLayoutSpecific(bv()->buffer()->params, *pit);
388                 ParagraphList::iterator fppit = pit;
389                 fppit->params().spaceTop(lyxlayout->fill_top ?
390                                          VSpace(VSpace::VFILL)
391                                          : VSpace(VSpace::NONE));
392                 fppit->params().spaceBottom(lyxlayout->fill_bottom ?
393                                             VSpace(VSpace::VFILL)
394                                             : VSpace(VSpace::NONE));
395                 if (lyxlayout->margintype == MARGIN_MANUAL)
396                         pit->setLabelWidthString(lyxlayout->labelstring());
397                 cur.par(pit);
398                 ++pit;
399         } while (pit != epit);
400
401         return endpit;
402 }
403
404
405 // set layout over selection and make a total rebreak of those paragraphs
406 void LyXText::setLayout(string const & layout)
407 {
408         LyXCursor tmpcursor = cursor;  // store the current cursor
409
410         // if there is no selection just set the layout
411         // of the current paragraph
412         if (!selection.set()) {
413                 selection.start = cursor;  // dummy selection
414                 selection.end = cursor;
415         }
416
417         // special handling of new environment insets
418         BufferParams const & params = bv()->buffer()->params;
419         LyXLayout_ptr const & lyxlayout = params.getLyXTextClass()[layout];
420         if (lyxlayout->is_environment) {
421                 // move everything in a new environment inset
422                 lyxerr << "setting layout " << layout << endl;
423                 bv()->owner()->dispatch(FuncRequest(LFUN_HOME));
424                 bv()->owner()->dispatch(FuncRequest(LFUN_ENDSEL));
425                 bv()->owner()->dispatch(FuncRequest(LFUN_CUT));
426                 InsetOld * inset = new InsetEnvironment(params, layout);
427                 if (bv()->insertInset(inset)) {
428                         //inset->edit(bv());
429                         //bv()->owner()->dispatch(FuncRequest(LFUN_PASTE));
430                 }
431                 else
432                         delete inset;
433                 return;
434         }
435
436         ParagraphList::iterator endpit = setLayout(cursor, selection.start,
437                                                    selection.end, layout);
438         redoParagraphs(selection.start.par(), endpit);
439
440         // we have to reset the selection, because the
441         // geometry could have changed
442         setCursor(selection.start.par(), selection.start.pos(), false);
443         selection.cursor = cursor;
444         setCursor(selection.end.par(), selection.end.pos(), false);
445         updateCounters();
446         clearSelection();
447         setSelection();
448         setCursor(tmpcursor.par(), tmpcursor.pos(), true);
449 }
450
451
452 bool LyXText::changeDepth(bv_funcs::DEPTH_CHANGE type, bool test_only)
453 {
454         ParagraphList::iterator pit(cursor.par());
455         ParagraphList::iterator end(cursor.par());
456         ParagraphList::iterator start = pit;
457
458         if (selection.set()) {
459                 pit = selection.start.par();
460                 end = selection.end.par();
461                 start = pit;
462         }
463
464         ParagraphList::iterator pastend = boost::next(end);
465
466         if (!test_only)
467                 recordUndo(bv(), Undo::ATOMIC, start, end);
468
469         bool changed = false;
470
471         int prev_after_depth = 0;
472 #warning parlist ... could be nicer ?
473         if (start != ownerParagraphs().begin()) {
474                 prev_after_depth = boost::prior(start)->getMaxDepthAfter();
475         }
476
477         while (true) {
478                 int const depth = pit->params().depth();
479                 if (type == bv_funcs::INC_DEPTH) {
480                         if (depth < prev_after_depth
481                             && pit->layout()->labeltype != LABEL_BIBLIO) {
482                                 changed = true;
483                                 if (!test_only) {
484                                         pit->params().depth(depth + 1);
485                                 }
486
487                         }
488                 } else if (depth) {
489                         changed = true;
490                         if (!test_only)
491                                 pit->params().depth(depth - 1);
492                 }
493
494                 prev_after_depth = pit->getMaxDepthAfter();
495
496                 if (pit == end) {
497                         break;
498                 }
499
500                 ++pit;
501         }
502
503         if (test_only)
504                 return changed;
505
506
507         redoParagraphs(start, pastend);
508
509         // We need to actually move the text->cursor. I don't
510         // understand why ...
511         LyXCursor tmpcursor = cursor;
512
513         // we have to reset the visual selection because the
514         // geometry could have changed
515         if (selection.set()) {
516                 setCursor(selection.start.par(), selection.start.pos());
517                 selection.cursor = cursor;
518                 setCursor(selection.end.par(), selection.end.pos());
519         }
520
521         // this handles the counter labels, and also fixes up
522         // depth values for follow-on (child) paragraphs
523         updateCounters();
524
525         setSelection();
526         setCursor(tmpcursor.par(), tmpcursor.pos());
527
528         return changed;
529 }
530
531
532 // set font over selection and make a total rebreak of those paragraphs
533 void LyXText::setFont(LyXFont const & font, bool toggleall)
534 {
535         // if there is no selection just set the current_font
536         if (!selection.set()) {
537                 // Determine basis font
538                 LyXFont layoutfont;
539                 if (cursor.pos() < cursor.par()->beginningOfBody()) {
540                         layoutfont = getLabelFont(cursor.par());
541                 } else {
542                         layoutfont = getLayoutFont(cursor.par());
543                 }
544                 // Update current font
545                 real_current_font.update(font,
546                                          bv()->buffer()->params.language,
547                                          toggleall);
548
549                 // Reduce to implicit settings
550                 current_font = real_current_font;
551                 current_font.reduce(layoutfont);
552                 // And resolve it completely
553                 real_current_font.realize(layoutfont);
554
555                 return;
556         }
557
558         LyXCursor tmpcursor = cursor; // store the current cursor
559
560         // ok we have a selection. This is always between sel_start_cursor
561         // and sel_end cursor
562
563         recordUndo(bv(), Undo::ATOMIC, selection.start.par(), selection.end.par());
564         freezeUndo();
565         cursor = selection.start;
566         while (cursor.par() != selection.end.par() ||
567                cursor.pos() < selection.end.pos())
568         {
569                 if (cursor.pos() < cursor.par()->size()) {
570                         // an open footnote should behave like a closed one
571                         setCharFont(cursor.par(), cursor.pos(),
572                                     font, toggleall);
573                         cursor.pos(cursor.pos() + 1);
574                 } else {
575                         cursor.pos(0);
576                         cursor.par(boost::next(cursor.par()));
577                 }
578         }
579         unFreezeUndo();
580
581         redoParagraph(selection.start.par());
582
583         // we have to reset the selection, because the
584         // geometry could have changed, but we keep
585         // it for user convenience
586         setCursor(selection.start.par(), selection.start.pos());
587         selection.cursor = cursor;
588         setCursor(selection.end.par(), selection.end.pos());
589         setSelection();
590         setCursor(tmpcursor.par(), tmpcursor.pos(), true,
591                   tmpcursor.boundary());
592 }
593
594
595 RowList::iterator LyXText::firstRow(ParagraphList::iterator pit)
596 {
597         RowList::iterator rit;
598         for (rit = rows().begin(); rit != rows().end(); ++rit)
599                 if (rit->par() == pit)
600                         break;
601         return rit;
602 }
603
604
605 // rebreaks all paragraphs between the specified pars
606 // This function is needed after SetLayout and SetFont etc.
607 void LyXText::redoParagraphs(ParagraphList::iterator start,
608   ParagraphList::iterator end)
609 {
610         for ( ; start != end; ++start)
611                 redoParagraph(start);
612 }
613
614
615 void LyXText::redoParagraph(ParagraphList::iterator pit)
616 {
617         RowList::iterator rit = firstRow(pit);
618
619         // remove paragraph from rowlist
620         while (rit != rows().end() && rit->par() == pit) {
621                 RowList::iterator rit2 = rit++;
622                 removeRow(rit2);
623         }
624
625         // reinsert the paragraph
626         // insert a new row, starting at position 0
627         Row newrow(pit, 0);
628         rit = rowlist_.insert(rit, newrow);
629
630         // and now append the whole paragraph before the new row
631         pos_type const last = rit->par()->size();
632         bool done = false;
633
634         do {
635                 pos_type z = rowBreakPoint(*rit);
636
637                 RowList::iterator tmprow = rit;
638
639                 if (z < last) {
640                         ++z;
641                         Row newrow(rit->par(), z);
642                         rit = rowlist_.insert(boost::next(rit), newrow);
643                 } else {
644                         done = true;
645                 }
646
647                 // Set the dimensions of the row
648                 // fixed fill setting now by calling inset->update() in
649                 // SingleWidth when needed!
650                 tmprow->fill(fill(tmprow, workWidth()));
651                 setHeightOfRow(tmprow);
652
653         } while (!done);
654
655         setHeightOfRow(rows().begin());
656 }
657
658
659 void LyXText::fullRebreak()
660 {
661         redoParagraphs(ownerParagraphs().begin(), ownerParagraphs().end());
662         setCursorIntern(cursor.par(), cursor.pos());
663         selection.cursor = cursor;
664 }
665
666
667 void LyXText::metrics(MetricsInfo & mi, Dimension & dim)
668 {
669         //lyxerr << "LyXText::metrics: width: " << mi.base.textwidth << endl;
670         //Assert(mi.base.textwidth);
671
672         // rebuild row cache
673         rowlist_.clear();
674         width = 0;
675         height = 0;
676
677         anchor_row_ = rows().end();
678         anchor_row_offset_ = 0;
679
680         ParagraphList::iterator pit = ownerParagraphs().begin();
681         ParagraphList::iterator end = ownerParagraphs().end();
682
683         for (; pit != end; ++pit) {
684                 InsetList::iterator ii = pit->insetlist.begin();
685                 InsetList::iterator iend = pit->insetlist.end();
686                 for (; ii != iend; ++ii) {
687                         Dimension dim;
688                         MetricsInfo m = mi;
689 #warning FIXME: pos != 0
690                         m.base.font = getFont(pit, 0);
691                         ii->inset->metrics(m, dim);
692                 }
693
694 #if 1
695                 // insert a new row, starting at position 0
696                 Row newrow(pit, 0);
697                 RowList::iterator rit = rowlist_.insert(rowlist_.end(), newrow);
698
699                 // and now append the whole paragraph before the new row
700                 appendParagraph(rit);
701 #else
702                 redoParagraph(pit);
703 #endif
704         }
705
706         // compute height
707         //lyxerr << "height 0: " << height << endl;
708         //for (RowList::iterator rit = rows().begin(); rit != rows().end(); ++rit) {
709         //      height += rit->height();
710         //}
711         //lyxerr << "height 1: " << height << endl;
712
713         // final dimension
714         dim.asc = rows().begin()->ascent_of_text();
715         dim.des = height - dim.asc;
716         dim.wid = std::max(mi.base.textwidth, int(width));
717 }
718
719
720 void LyXText::partialRebreak()
721 {
722         if (rows().empty()) {
723                 init(bv());
724                 return;
725         }
726         breakAgain(rows().begin());
727 }
728
729
730 // important for the screen
731
732
733 // the cursor set functions have a special mechanism. When they
734 // realize, that you left an empty paragraph, they will delete it.
735 // They also delete the corresponding row
736
737 // need the selection cursor:
738 void LyXText::setSelection()
739 {
740         TextCursor::setSelection();
741 }
742
743
744
745 void LyXText::clearSelection()
746 {
747         TextCursor::clearSelection();
748
749         // reset this in the bv_owner!
750         if (bv_owner && bv_owner->text)
751                 bv_owner->text->xsel_cache.set(false);
752 }
753
754
755 void LyXText::cursorHome()
756 {
757         setCursor(cursor.par(), cursorRow()->pos());
758 }
759
760
761 void LyXText::cursorEnd()
762 {
763         if (cursor.par()->empty())
764                 return;
765
766         RowList::iterator rit = cursorRow();
767         RowList::iterator next_rit = boost::next(rit);
768         ParagraphList::iterator pit = rit->par();
769         pos_type last_pos = lastPos(*this, rit);
770
771         if (next_rit == rows().end() || next_rit->par() != pit) {
772                 ++last_pos;
773         } else {
774                 if (pit->empty() ||
775                     (pit->getChar(last_pos) != ' ' && !pit->isNewline(last_pos))) {
776                         ++last_pos;
777                 }
778         }
779
780         setCursor(pit, last_pos);
781 }
782
783
784 void LyXText::cursorTop()
785 {
786         setCursor(ownerParagraphs().begin(), 0);
787 }
788
789
790 void LyXText::cursorBottom()
791 {
792         ParagraphList::iterator lastpit =
793                 boost::prior(ownerParagraphs().end());
794         setCursor(lastpit, lastpit->size());
795 }
796
797
798 void LyXText::toggleFree(LyXFont const & font, bool toggleall)
799 {
800         // If the mask is completely neutral, tell user
801         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
802                 // Could only happen with user style
803                 bv()->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
804                 return;
805         }
806
807         // Try implicit word selection
808         // If there is a change in the language the implicit word selection
809         // is disabled.
810         LyXCursor resetCursor = cursor;
811         bool implicitSelection = (font.language() == ignore_language
812                                   && font.number() == LyXFont::IGNORE)
813                 ? selectWordWhenUnderCursor(lyx::WHOLE_WORD_STRICT) : false;
814
815         // Set font
816         setFont(font, toggleall);
817
818         // Implicit selections are cleared afterwards
819         //and cursor is set to the original position.
820         if (implicitSelection) {
821                 clearSelection();
822                 cursor = resetCursor;
823                 setCursor(cursor.par(), cursor.pos());
824                 selection.cursor = cursor;
825         }
826 }
827
828
829 string LyXText::getStringToIndex()
830 {
831         // Try implicit word selection
832         // If there is a change in the language the implicit word selection
833         // is disabled.
834         LyXCursor const reset_cursor = cursor;
835         bool const implicitSelection =
836                 selectWordWhenUnderCursor(lyx::PREVIOUS_WORD);
837
838         string idxstring;
839         if (!selection.set())
840                 bv()->owner()->message(_("Nothing to index!"));
841         else if (selection.start.par() != selection.end.par())
842                 bv()->owner()->message(_("Cannot index more than one paragraph!"));
843         else
844                 idxstring = selectionAsString(bv()->buffer(), false);
845
846         // Reset cursors to their original position.
847         cursor = reset_cursor;
848         setCursor(cursor.par(), cursor.pos());
849         selection.cursor = cursor;
850
851         // Clear the implicit selection.
852         if (implicitSelection)
853                 clearSelection();
854
855         return idxstring;
856 }
857
858
859 // the DTP switches for paragraphs. LyX will store them in the first
860 // physicla paragraph. When a paragraph is broken, the top settings rest,
861 // the bottom settings are given to the new one. So I can make shure,
862 // they do not duplicate themself and you cannnot make dirty things with
863 // them!
864
865 void LyXText::setParagraph(bool line_top, bool line_bottom,
866                            bool pagebreak_top, bool pagebreak_bottom,
867                            VSpace const & space_top,
868                            VSpace const & space_bottom,
869                            Spacing const & spacing,
870                            LyXAlignment align,
871                            string const & labelwidthstring,
872                            bool noindent)
873 {
874         LyXCursor tmpcursor = cursor;
875         if (!selection.set()) {
876                 selection.start = cursor;
877                 selection.end = cursor;
878         }
879
880         // make sure that the depth behind the selection are restored, too
881         ParagraphList::iterator endpit = boost::next(selection.end.par());
882         ParagraphList::iterator undoendpit = endpit;
883         ParagraphList::iterator pars_end = ownerParagraphs().end();
884
885         if (endpit != pars_end && endpit->getDepth()) {
886                 while (endpit != pars_end && endpit->getDepth()) {
887                         ++endpit;
888                         undoendpit = endpit;
889                 }
890         } else if (endpit != pars_end) {
891                 // because of parindents etc.
892                 ++endpit;
893         }
894
895         recordUndo(bv(), Undo::ATOMIC, selection.start.par(),
896                 boost::prior(undoendpit));
897
898
899         ParagraphList::iterator tmppit = selection.end.par();
900
901         while (tmppit != boost::prior(selection.start.par())) {
902                 setCursor(tmppit, 0);
903
904                 ParagraphList::iterator pit = cursor.par();
905                 ParagraphParameters & params = pit->params();
906
907                 params.lineTop(line_top);
908                 params.lineBottom(line_bottom);
909                 params.pagebreakTop(pagebreak_top);
910                 params.pagebreakBottom(pagebreak_bottom);
911                 params.spaceTop(space_top);
912                 params.spaceBottom(space_bottom);
913                 params.spacing(spacing);
914                 // does the layout allow the new alignment?
915                 LyXLayout_ptr const & layout = pit->layout();
916
917                 if (align == LYX_ALIGN_LAYOUT)
918                         align = layout->align;
919                 if (align & layout->alignpossible) {
920                         if (align == layout->align)
921                                 params.align(LYX_ALIGN_LAYOUT);
922                         else
923                                 params.align(align);
924                 }
925                 pit->setLabelWidthString(labelwidthstring);
926                 params.noindent(noindent);
927                 tmppit = boost::prior(pit);
928         }
929
930         redoParagraphs(selection.start.par(), endpit);
931
932         clearSelection();
933         setCursor(selection.start.par(), selection.start.pos());
934         selection.cursor = cursor;
935         setCursor(selection.end.par(), selection.end.pos());
936         setSelection();
937         setCursor(tmpcursor.par(), tmpcursor.pos());
938         if (inset_owner)
939                 bv()->updateInset();
940 }
941
942
943 // set the counter of a paragraph. This includes the labels
944 void LyXText::setCounter(Buffer const * buf, ParagraphList::iterator pit)
945 {
946         LyXTextClass const & textclass = buf->params.getLyXTextClass();
947         LyXLayout_ptr const & layout = pit->layout();
948
949         if (pit != ownerParagraphs().begin()) {
950
951                 pit->params().appendix(boost::prior(pit)->params().appendix());
952                 if (!pit->params().appendix() &&
953                     pit->params().startOfAppendix()) {
954                         pit->params().appendix(true);
955                         textclass.counters().reset();
956                 }
957                 pit->enumdepth = boost::prior(pit)->enumdepth;
958                 pit->itemdepth = boost::prior(pit)->itemdepth;
959         } else {
960                 pit->params().appendix(pit->params().startOfAppendix());
961                 pit->enumdepth = 0;
962                 pit->itemdepth = 0;
963         }
964
965         /* Maybe we have to increment the enumeration depth.
966          * BUT, enumeration in a footnote is considered in isolation from its
967          *      surrounding paragraph so don't increment if this is the
968          *      first line of the footnote
969          * AND, bibliographies can't have their depth changed ie. they
970          *      are always of depth 0
971          */
972         if (pit != ownerParagraphs().begin()
973             && boost::prior(pit)->getDepth() < pit->getDepth()
974             && boost::prior(pit)->layout()->labeltype == LABEL_COUNTER_ENUMI
975             && pit->enumdepth < 3
976             && layout->labeltype != LABEL_BIBLIO) {
977                 pit->enumdepth++;
978         }
979
980         // Maybe we have to decrement the enumeration depth, see note above
981         if (pit != ownerParagraphs().begin()
982             && boost::prior(pit)->getDepth() > pit->getDepth()
983             && layout->labeltype != LABEL_BIBLIO) {
984                 pit->enumdepth = depthHook(pit, ownerParagraphs(),
985                                            pit->getDepth())->enumdepth;
986         }
987
988         if (!pit->params().labelString().empty()) {
989                 pit->params().labelString(string());
990         }
991
992         if (layout->margintype == MARGIN_MANUAL) {
993                 if (pit->params().labelWidthString().empty()) {
994                         pit->setLabelWidthString(layout->labelstring());
995                 }
996         } else {
997                 pit->setLabelWidthString(string());
998         }
999
1000         // is it a layout that has an automatic label?
1001         if (layout->labeltype >= LABEL_COUNTER_CHAPTER) {
1002                 int const i = layout->labeltype - LABEL_COUNTER_CHAPTER;
1003
1004                 ostringstream s;
1005
1006                 if (i >= 0 && i <= buf->params.secnumdepth) {
1007                         string numbertype;
1008                         string langtype;
1009
1010                         textclass.counters().step(layout->latexname());
1011
1012                         // Is there a label? Useful for Chapter layout
1013                         if (!pit->params().appendix()) {
1014                                 s << buf->B_(layout->labelstring());
1015                         } else {
1016                                 s << buf->B_(layout->labelstring_appendix());
1017                         }
1018
1019                         // Use of an integer is here less than elegant. For now.
1020                         int head = textclass.maxcounter() - LABEL_COUNTER_CHAPTER;
1021                         if (!pit->params().appendix()) {
1022                                 numbertype = "sectioning";
1023                         } else {
1024                                 numbertype = "appendix";
1025                                 if (pit->isRightToLeftPar(buf->params))
1026                                         langtype = "hebrew";
1027                                 else
1028                                         langtype = "latin";
1029                         }
1030
1031                         s << " "
1032                           << textclass.counters()
1033                                 .numberLabel(layout->latexname(),
1034                                              numbertype, langtype, head);
1035
1036                         pit->params().labelString(STRCONV(s.str()));
1037
1038                         // reset enum counters
1039                         textclass.counters().reset("enum");
1040                 } else if (layout->labeltype < LABEL_COUNTER_ENUMI) {
1041                         textclass.counters().reset("enum");
1042                 } else if (layout->labeltype == LABEL_COUNTER_ENUMI) {
1043                         // FIXME
1044                         // Yes I know this is a really, really! bad solution
1045                         // (Lgb)
1046                         string enumcounter("enum");
1047
1048                         switch (pit->enumdepth) {
1049                         case 2:
1050                                 enumcounter += 'i';
1051                         case 1:
1052                                 enumcounter += 'i';
1053                         case 0:
1054                                 enumcounter += 'i';
1055                                 break;
1056                         case 3:
1057                                 enumcounter += "iv";
1058                                 break;
1059                         default:
1060                                 // not a valid enumdepth...
1061                                 break;
1062                         }
1063
1064                         textclass.counters().step(enumcounter);
1065
1066                         s << textclass.counters()
1067                                 .numberLabel(enumcounter, "enumeration");
1068                         pit->params().labelString(STRCONV(s.str()));
1069                 }
1070         } else if (layout->labeltype == LABEL_BIBLIO) {// ale970302
1071                 textclass.counters().step("bibitem");
1072                 int number = textclass.counters().value("bibitem");
1073                 if (pit->bibitem()) {
1074                         pit->bibitem()->setCounter(number);
1075                         pit->params().labelString(layout->labelstring());
1076                 }
1077                 // In biblio should't be following counters but...
1078         } else {
1079                 string s = buf->B_(layout->labelstring());
1080
1081                 // the caption hack:
1082                 if (layout->labeltype == LABEL_SENSITIVE) {
1083                         ParagraphList::iterator end = ownerParagraphs().end();
1084                         ParagraphList::iterator tmppit = pit;
1085                         InsetOld * in = 0;
1086                         bool isOK = false;
1087                         while (tmppit != end && tmppit->inInset()
1088                                // the single '=' is intended below
1089                                && (in = tmppit->inInset()->owner()))
1090                         {
1091                                 if (in->lyxCode() == InsetOld::FLOAT_CODE ||
1092                                     in->lyxCode() == InsetOld::WRAP_CODE) {
1093                                         isOK = true;
1094                                         break;
1095                                 } else {
1096                                         tmppit = ownerParagraphs().begin();
1097                                         for ( ; tmppit != end; ++tmppit)
1098                                                 if (&*tmppit == in->parOwner())
1099                                                         break;
1100                                 }
1101                         }
1102
1103                         if (isOK) {
1104                                 string type;
1105
1106                                 if (in->lyxCode() == InsetOld::FLOAT_CODE)
1107                                         type = static_cast<InsetFloat*>(in)->params().type;
1108                                 else if (in->lyxCode() == InsetOld::WRAP_CODE)
1109                                         type = static_cast<InsetWrap*>(in)->params().type;
1110                                 else
1111                                         Assert(0);
1112
1113                                 Floating const & fl = textclass.floats().getType(type);
1114
1115                                 textclass.counters().step(fl.type());
1116
1117                                 // Doesn't work... yet.
1118                                 s = bformat(_("%1$s #:"), buf->B_(fl.name()));
1119                         } else {
1120                                 // par->SetLayout(0);
1121                                 // s = layout->labelstring;
1122                                 s = _("Senseless: ");
1123                         }
1124                 }
1125                 pit->params().labelString(s);
1126
1127                 // reset the enumeration counter. They are always reset
1128                 // when there is any other layout between
1129                 // Just fall-through between the cases so that all
1130                 // enum counters deeper than enumdepth is also reset.
1131                 switch (pit->enumdepth) {
1132                 case 0:
1133                         textclass.counters().reset("enumi");
1134                 case 1:
1135                         textclass.counters().reset("enumii");
1136                 case 2:
1137                         textclass.counters().reset("enumiii");
1138                 case 3:
1139                         textclass.counters().reset("enumiv");
1140                 }
1141         }
1142 }
1143
1144
1145 // Updates all counters. Paragraphs with changed label string will be rebroken
1146 void LyXText::updateCounters()
1147 {
1148         RowList::iterator rowit = rows().begin();
1149         ParagraphList::iterator pit = rowit->par();
1150
1151         // CHECK if this is really needed. (Lgb)
1152         bv()->buffer()->params.getLyXTextClass().counters().reset();
1153
1154         ParagraphList::iterator beg = ownerParagraphs().begin();
1155         ParagraphList::iterator end = ownerParagraphs().end();
1156         for (; pit != end; ++pit) {
1157                 while (rowit->par() != pit)
1158                         ++rowit;
1159
1160                 string const oldLabel = pit->params().labelString();
1161
1162                 size_t maxdepth = 0;
1163                 if (pit != beg)
1164                         maxdepth = boost::prior(pit)->getMaxDepthAfter();
1165
1166                 if (pit->params().depth() > maxdepth)
1167                         pit->params().depth(maxdepth);
1168
1169                 // setCounter can potentially change the labelString.
1170                 setCounter(bv()->buffer(), pit);
1171
1172                 string const & newLabel = pit->params().labelString();
1173
1174                 if (oldLabel.empty() && !newLabel.empty()) {
1175                         removeParagraph(rowit);
1176                         appendParagraph(rowit);
1177                 }
1178         }
1179 }
1180
1181
1182 void LyXText::insertInset(InsetOld * inset)
1183 {
1184         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1185                 return;
1186         recordUndo(bv(), Undo::ATOMIC, cursor.par());
1187         freezeUndo();
1188         cursor.par()->insertInset(cursor.pos(), inset);
1189         // Just to rebreak and refresh correctly.
1190         // The character will not be inserted a second time
1191         insertChar(Paragraph::META_INSET);
1192         // If we enter a highly editable inset the cursor should be to before
1193         // the inset. This couldn't happen before as Undo was not handled inside
1194         // inset now after the Undo LyX tries to call inset->Edit(...) again
1195         // and cannot do this as the cursor is behind the inset and GetInset
1196         // does not return the inset!
1197         if (isHighlyEditableInset(inset)) {
1198                 cursorLeft(true);
1199         }
1200         unFreezeUndo();
1201 }
1202
1203
1204 void LyXText::cutSelection(bool doclear, bool realcut)
1205 {
1206         // Stuff what we got on the clipboard. Even if there is no selection.
1207
1208         // There is a problem with having the stuffing here in that the
1209         // larger the selection the slower LyX will get. This can be
1210         // solved by running the line below only when the selection has
1211         // finished. The solution used currently just works, to make it
1212         // faster we need to be more clever and probably also have more
1213         // calls to stuffClipboard. (Lgb)
1214         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1215
1216         // This doesn't make sense, if there is no selection
1217         if (!selection.set())
1218                 return;
1219
1220         // OK, we have a selection. This is always between selection.start
1221         // and selection.end
1222
1223         // make sure that the depth behind the selection are restored, too
1224         ParagraphList::iterator endpit = boost::next(selection.end.par());
1225         ParagraphList::iterator undoendpit = endpit;
1226         ParagraphList::iterator pars_end = ownerParagraphs().end();
1227
1228         if (endpit != pars_end && endpit->getDepth()) {
1229                 while (endpit != pars_end && endpit->getDepth()) {
1230                         ++endpit;
1231                         undoendpit = endpit;
1232                 }
1233         } else if (endpit != pars_end) {
1234                 // because of parindents etc.
1235                 ++endpit;
1236         }
1237
1238         recordUndo(bv(), Undo::DELETE, selection.start.par(),
1239                    boost::prior(undoendpit));
1240
1241
1242         endpit = selection.end.par();
1243         int endpos = selection.end.pos();
1244
1245         boost::tie(endpit, endpos) = realcut ?
1246                 CutAndPaste::cutSelection(bv()->buffer()->params,
1247                                           ownerParagraphs(),
1248                                           selection.start.par(), endpit,
1249                                           selection.start.pos(), endpos,
1250                                           bv()->buffer()->params.textclass,
1251                                           doclear)
1252                 : CutAndPaste::eraseSelection(bv()->buffer()->params,
1253                                               ownerParagraphs(),
1254                                               selection.start.par(), endpit,
1255                                               selection.start.pos(), endpos,
1256                                               doclear);
1257         // sometimes necessary
1258         if (doclear)
1259                 selection.start.par()->stripLeadingSpaces();
1260
1261         redoParagraphs(selection.start.par(), boost::next(endpit));
1262         // cutSelection can invalidate the cursor so we need to set
1263         // it anew. (Lgb)
1264         // we prefer the end for when tracking changes
1265         cursor.pos(endpos);
1266         cursor.par(endpit);
1267
1268         // need a valid cursor. (Lgb)
1269         clearSelection();
1270
1271         setCursor(cursor.par(), cursor.pos());
1272         selection.cursor = cursor;
1273         updateCounters();
1274 }
1275
1276
1277 void LyXText::copySelection()
1278 {
1279         // stuff the selection onto the X clipboard, from an explicit copy request
1280         bv()->stuffClipboard(selectionAsString(bv()->buffer(), true));
1281
1282         // this doesnt make sense, if there is no selection
1283         if (!selection.set())
1284                 return;
1285
1286         // ok we have a selection. This is always between selection.start
1287         // and sel_end cursor
1288
1289         // copy behind a space if there is one
1290         while (selection.start.par()->size() > selection.start.pos()
1291                && selection.start.par()->isLineSeparator(selection.start.pos())
1292                && (selection.start.par() != selection.end.par()
1293                    || selection.start.pos() < selection.end.pos()))
1294                 selection.start.pos(selection.start.pos() + 1);
1295
1296         CutAndPaste::copySelection(selection.start.par(),
1297                                    selection.end.par(),
1298                                    selection.start.pos(), selection.end.pos(),
1299                                    bv()->buffer()->params.textclass);
1300 }
1301
1302
1303 void LyXText::pasteSelection(size_t sel_index)
1304 {
1305         // this does not make sense, if there is nothing to paste
1306         if (!CutAndPaste::checkPastePossible())
1307                 return;
1308
1309         recordUndo(bv(), Undo::INSERT, cursor.par());
1310
1311         ParagraphList::iterator endpit;
1312         PitPosPair ppp;
1313
1314         ErrorList el;
1315
1316         boost::tie(ppp, endpit) =
1317                 CutAndPaste::pasteSelection(*bv()->buffer(),
1318                                             ownerParagraphs(),
1319                                             cursor.par(), cursor.pos(),
1320                                             bv()->buffer()->params.textclass,
1321                                             sel_index, el);
1322         bufferErrors(*bv()->buffer(), el);
1323         bv()->showErrorList(_("Paste"));
1324
1325         redoParagraphs(cursor.par(), endpit);
1326
1327         setCursor(cursor.par(), cursor.pos());
1328         clearSelection();
1329
1330         selection.cursor = cursor;
1331         setCursor(ppp.first, ppp.second);
1332         setSelection();
1333         updateCounters();
1334 }
1335
1336
1337 void LyXText::setSelectionRange(lyx::pos_type length)
1338 {
1339         if (!length)
1340                 return;
1341
1342         selection.cursor = cursor;
1343         while (length--)
1344                 cursorRight(bv());
1345         setSelection();
1346 }
1347
1348
1349 // simple replacing. The font of the first selected character is used
1350 void LyXText::replaceSelectionWithString(string const & str)
1351 {
1352         recordUndo(bv(), Undo::ATOMIC);
1353         freezeUndo();
1354
1355         if (!selection.set()) { // create a dummy selection
1356                 selection.end = cursor;
1357                 selection.start = cursor;
1358         }
1359
1360         // Get font setting before we cut
1361         pos_type pos = selection.end.pos();
1362         LyXFont const font = selection.start.par()
1363                 ->getFontSettings(bv()->buffer()->params,
1364                                   selection.start.pos());
1365
1366         // Insert the new string
1367         string::const_iterator cit = str.begin();
1368         string::const_iterator end = str.end();
1369         for (; cit != end; ++cit) {
1370                 selection.end.par()->insertChar(pos, (*cit), font);
1371                 ++pos;
1372         }
1373
1374         // Cut the selection
1375         cutSelection(true, false);
1376
1377         unFreezeUndo();
1378 }
1379
1380
1381 // needed to insert the selection
1382 void LyXText::insertStringAsLines(string const & str)
1383 {
1384         ParagraphList::iterator pit = cursor.par();
1385         pos_type pos = cursor.pos();
1386         ParagraphList::iterator endpit = boost::next(cursor.par());
1387
1388         recordUndo(bv(), Undo::ATOMIC);
1389
1390         // only to be sure, should not be neccessary
1391         clearSelection();
1392
1393         bv()->buffer()->insertStringAsLines(pit, pos, current_font, str);
1394
1395         redoParagraphs(cursor.par(), endpit);
1396         setCursor(cursor.par(), cursor.pos());
1397         selection.cursor = cursor;
1398         setCursor(pit, pos);
1399         setSelection();
1400 }
1401
1402
1403 // turns double-CR to single CR, others where converted into one
1404 // blank. Then InsertStringAsLines is called
1405 void LyXText::insertStringAsParagraphs(string const & str)
1406 {
1407         string linestr(str);
1408         bool newline_inserted = false;
1409         string::size_type const siz = linestr.length();
1410
1411         for (string::size_type i = 0; i < siz; ++i) {
1412                 if (linestr[i] == '\n') {
1413                         if (newline_inserted) {
1414                                 // we know that \r will be ignored by
1415                                 // InsertStringA. Of course, it is a dirty
1416                                 // trick, but it works...
1417                                 linestr[i - 1] = '\r';
1418                                 linestr[i] = '\n';
1419                         } else {
1420                                 linestr[i] = ' ';
1421                                 newline_inserted = true;
1422                         }
1423                 } else if (IsPrintable(linestr[i])) {
1424                         newline_inserted = false;
1425                 }
1426         }
1427         insertStringAsLines(linestr);
1428 }
1429
1430
1431 bool LyXText::setCursor(ParagraphList::iterator pit,
1432                         pos_type pos,
1433                         bool setfont, bool boundary)
1434 {
1435         LyXCursor old_cursor = cursor;
1436         setCursorIntern(pit, pos, setfont, boundary);
1437         return deleteEmptyParagraphMechanism(old_cursor);
1438 }
1439
1440
1441 void LyXText::setCursor(LyXCursor & cur, ParagraphList::iterator pit,
1442                         pos_type pos, bool boundary)
1443 {
1444         Assert(pit != ownerParagraphs().end());
1445
1446         cur.par(pit);
1447         cur.pos(pos);
1448         cur.boundary(boundary);
1449         if (rows().empty())
1450                 return;
1451
1452         // get the cursor y position in text
1453         int y = 0;
1454         RowList::iterator row = getRow(pit, pos, y);
1455         RowList::iterator beg = rows().begin();
1456
1457         RowList::iterator old_row = row;
1458         // if we are before the first char of this row and are still in the
1459         // same paragraph and there is a previous row then put the cursor on
1460         // the end of the previous row
1461         cur.iy(y + row->baseline());
1462         if (row != beg &&
1463             pos &&
1464             boost::prior(row)->par() == row->par() &&
1465             pos < pit->size() &&
1466             pit->getChar(pos) == Paragraph::META_INSET) {
1467                 InsetOld * ins = pit->getInset(pos);
1468                 if (ins && (ins->needFullRow() || ins->display())) {
1469                         --row;
1470                         y -= row->height();
1471                 }
1472         }
1473
1474         // y is now the beginning of the cursor row
1475         y += row->baseline();
1476         // y is now the cursor baseline
1477         cur.y(y);
1478
1479         pos_type last = lastPrintablePos(*this, old_row);
1480
1481         // None of these should happen, but we're scaredy-cats
1482         if (pos > pit->size()) {
1483                 lyxerr << "dont like 1 please report" << endl;
1484                 pos = 0;
1485                 cur.pos(0);
1486         } else if (pos > last + 1) {
1487                 lyxerr << "dont like 2 please report" << endl;
1488                 // This shouldn't happen.
1489                 pos = last + 1;
1490                 cur.pos(pos);
1491         } else if (pos < row->pos()) {
1492                 lyxerr << "dont like 3 please report" << endl;
1493                 pos = row->pos();
1494                 cur.pos(pos);
1495         }
1496
1497         // now get the cursors x position
1498         float x = getCursorX(row, pos, last, boundary);
1499         cur.x(int(x));
1500         cur.x_fix(cur.x());
1501         if (old_row != row) {
1502                 x = getCursorX(old_row, pos, last, boundary);
1503                 cur.ix(int(x));
1504         } else
1505                 cur.ix(cur.x());
1506 /* We take out this for the time being because 1) the redraw code is not
1507    prepared to this yet and 2) because some good policy has yet to be decided
1508    while editting: for instance how to act on rows being created/deleted
1509    because of DEPM.
1510 */
1511 #if 0
1512         //if the cursor is in a visible row, anchor to it
1513         int topy = top_y();
1514         if (topy < y && y < topy + bv()->workHeight())
1515                 anchor_row(row);
1516 #endif
1517 }
1518
1519
1520 float LyXText::getCursorX(RowList::iterator rit,
1521                           pos_type pos, pos_type last, bool boundary) const
1522 {
1523         pos_type cursor_vpos = 0;
1524         double x;
1525         double fill_separator;
1526         double fill_hfill;
1527         double fill_label_hfill;
1528         // This call HAS to be here because of the BidiTables!!!
1529         prepareToPrint(rit, x, fill_separator, fill_hfill,
1530                        fill_label_hfill);
1531
1532         ParagraphList::iterator rit_par = rit->par();
1533         pos_type const rit_pos = rit->pos();
1534
1535         if (last < rit_pos)
1536                 cursor_vpos = rit_pos;
1537         else if (pos > last && !boundary)
1538                 cursor_vpos = (rit_par->isRightToLeftPar(bv()->buffer()->params))
1539                         ? rit_pos : last + 1;
1540         else if (pos > rit_pos && (pos > last || boundary))
1541                 /// Place cursor after char at (logical) position pos - 1
1542                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
1543                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
1544         else
1545                 /// Place cursor before char at (logical) position pos
1546                 cursor_vpos = (bidi_level(pos) % 2 == 0)
1547                         ? log2vis(pos) : log2vis(pos) + 1;
1548
1549         pos_type body_pos = rit_par->beginningOfBody();
1550         if (body_pos > 0 &&
1551             (body_pos - 1 > last || !rit_par->isLineSeparator(body_pos - 1)))
1552                 body_pos = 0;
1553
1554         for (pos_type vpos = rit_pos; vpos < cursor_vpos; ++vpos) {
1555                 pos_type pos = vis2log(vpos);
1556                 if (body_pos > 0 && pos == body_pos - 1) {
1557                         x += fill_label_hfill +
1558                                 font_metrics::width(
1559                                         rit_par->layout()->labelsep, getLabelFont(rit_par));
1560                         if (rit_par->isLineSeparator(body_pos - 1))
1561                                 x -= singleWidth(rit_par, body_pos - 1);
1562                 }
1563
1564                 if (hfillExpansion(*this, rit, pos)) {
1565                         x += singleWidth(rit_par, pos);
1566                         if (pos >= body_pos)
1567                                 x += fill_hfill;
1568                         else
1569                                 x += fill_label_hfill;
1570                 } else if (rit_par->isSeparator(pos)) {
1571                         x += singleWidth(rit_par, pos);
1572                         if (pos >= body_pos)
1573                                 x += fill_separator;
1574                 } else
1575                         x += singleWidth(rit_par, pos);
1576         }
1577         return x;
1578 }
1579
1580
1581 void LyXText::setCursorIntern(ParagraphList::iterator pit,
1582                               pos_type pos, bool setfont, bool boundary)
1583 {
1584         UpdatableInset * it = pit->inInset();
1585         if (it) {
1586                 if (it != inset_owner) {
1587                         lyxerr[Debug::INSETS] << "InsetText   is " << it
1588                                               << endl
1589                                               << "inset_owner is "
1590                                               << inset_owner << endl;
1591 #ifdef WITH_WARNINGS
1592 #warning I believe this code is wrong. (Lgb)
1593 #warning Jürgen, have a look at this. (Lgb)
1594 #warning Hmmm, I guess you are right but we
1595 #warning should verify when this is needed
1596 #endif
1597                         // Jürgen, would you like to have a look?
1598                         // I guess we need to move the outer cursor
1599                         // and open and lock the inset (bla bla bla)
1600                         // stuff I don't know... so can you have a look?
1601                         // (Lgb)
1602                         // I moved the lyxerr stuff in here so we can see if
1603                         // this is actually really needed and where!
1604                         // (Jug)
1605                         // it->getLyXText(bv())->setCursorIntern(bv(), par, pos, setfont, boundary);
1606                         return;
1607                 }
1608         }
1609
1610         setCursor(cursor, pit, pos, boundary);
1611         if (setfont)
1612                 setCurrentFont();
1613 }
1614
1615
1616 void LyXText::setCurrentFont()
1617 {
1618         pos_type pos = cursor.pos();
1619         ParagraphList::iterator pit = cursor.par();
1620
1621         if (cursor.boundary() && pos > 0)
1622                 --pos;
1623
1624         if (pos > 0) {
1625                 if (pos == pit->size())
1626                         --pos;
1627                 else // potentional bug... BUG (Lgb)
1628                         if (pit->isSeparator(pos)) {
1629                                 if (pos > cursorRow()->pos() &&
1630                                     bidi_level(pos) % 2 ==
1631                                     bidi_level(pos - 1) % 2)
1632                                         --pos;
1633                                 else if (pos + 1 < pit->size())
1634                                         ++pos;
1635                         }
1636         }
1637
1638         current_font = pit->getFontSettings(bv()->buffer()->params, pos);
1639         real_current_font = getFont(pit, pos);
1640
1641         if (cursor.pos() == pit->size() &&
1642             isBoundary(bv()->buffer(), *pit, cursor.pos()) &&
1643             !cursor.boundary()) {
1644                 Language const * lang =
1645                         pit->getParLanguage(bv()->buffer()->params);
1646                 current_font.setLanguage(lang);
1647                 current_font.setNumber(LyXFont::OFF);
1648                 real_current_font.setLanguage(lang);
1649                 real_current_font.setNumber(LyXFont::OFF);
1650         }
1651 }
1652
1653
1654 // returns the column near the specified x-coordinate of the row
1655 // x is set to the real beginning of this column
1656 pos_type
1657 LyXText::getColumnNearX(RowList::iterator rit, int & x, bool & boundary) const
1658 {
1659         double tmpx = 0;
1660         double fill_separator;
1661         double fill_hfill;
1662         double fill_label_hfill;
1663
1664         prepareToPrint(rit, tmpx, fill_separator, fill_hfill, fill_label_hfill);
1665
1666         pos_type vc = rit->pos();
1667         pos_type last = lastPrintablePos(*this, rit);
1668         pos_type c = 0;
1669
1670         ParagraphList::iterator rit_par = rit->par();
1671         LyXLayout_ptr const & layout = rit->par()->layout();
1672
1673         bool left_side = false;
1674
1675         pos_type body_pos = rit_par->beginningOfBody();
1676         double last_tmpx = tmpx;
1677
1678         if (body_pos > 0 &&
1679             (body_pos - 1 > last ||
1680              !rit_par->isLineSeparator(body_pos - 1)))
1681                 body_pos = 0;
1682
1683         // check for empty row
1684         if (!rit_par->size()) {
1685                 x = int(tmpx);
1686                 return 0;
1687         }
1688
1689         while (vc <= last && tmpx <= x) {
1690                 c = vis2log(vc);
1691                 last_tmpx = tmpx;
1692                 if (body_pos > 0 && c == body_pos - 1) {
1693                         tmpx += fill_label_hfill +
1694                                 font_metrics::width(layout->labelsep, getLabelFont(rit_par));
1695                         if (rit_par->isLineSeparator(body_pos - 1))
1696                                 tmpx -= singleWidth(rit_par, body_pos - 1);
1697                 }
1698
1699                 if (hfillExpansion(*this, rit, c)) {
1700                         tmpx += singleWidth(rit_par, c);
1701                         if (c >= body_pos)
1702                                 tmpx += fill_hfill;
1703                         else
1704                                 tmpx += fill_label_hfill;
1705                 } else if (rit_par->isSeparator(c)) {
1706                         tmpx += singleWidth(rit_par, c);
1707                         if (c >= body_pos)
1708                                 tmpx += fill_separator;
1709                 } else {
1710                         tmpx += singleWidth(rit_par, c);
1711                 }
1712                 ++vc;
1713         }
1714
1715         if ((tmpx + last_tmpx) / 2 > x) {
1716                 tmpx = last_tmpx;
1717                 left_side = true;
1718         }
1719
1720         if (vc > last + 1)  // This shouldn't happen.
1721                 vc = last + 1;
1722
1723         boundary = false;
1724         // This (rtl_support test) is not needed, but gives
1725         // some speedup if rtl_support=false
1726         RowList::iterator next_rit = boost::next(rit);
1727
1728         bool const lastrow = lyxrc.rtl_support &&
1729                 (next_rit == rowlist_.end() ||
1730                  next_rit->par() != rit_par);
1731
1732         // If lastrow is false, we don't need to compute
1733         // the value of rtl.
1734         bool const rtl = (lastrow)
1735                 ? rit_par->isRightToLeftPar(bv()->buffer()->params)
1736                 : false;
1737         if (lastrow &&
1738                  ((rtl &&  left_side && vc == rit->pos() && x < tmpx - 5) ||
1739                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
1740                 c = last + 1;
1741         else if (vc == rit->pos()) {
1742                 c = vis2log(vc);
1743                 if (bidi_level(c) % 2 == 1)
1744                         ++c;
1745         } else {
1746                 c = vis2log(vc - 1);
1747                 bool const rtl = (bidi_level(c) % 2 == 1);
1748                 if (left_side == rtl) {
1749                         ++c;
1750                         boundary = isBoundary(bv()->buffer(), *rit_par, c);
1751                 }
1752         }
1753
1754         if (rit->pos() <= last && c > last
1755             && rit_par->isNewline(last)) {
1756                 if (bidi_level(last) % 2 == 0)
1757                         tmpx -= singleWidth(rit_par, last);
1758                 else
1759                         tmpx += singleWidth(rit_par, last);
1760                 c = last;
1761         }
1762
1763         c -= rit->pos();
1764         x = int(tmpx);
1765         return c;
1766 }
1767
1768
1769 void LyXText::setCursorFromCoordinates(int x, int y)
1770 {
1771         //LyXCursor old_cursor = cursor;
1772         setCursorFromCoordinates(cursor, x, y);
1773         setCurrentFont();
1774 #warning DEPM disabled, otherwise crash when entering new table
1775         //deleteEmptyParagraphMechanism(old_cursor);
1776 }
1777
1778
1779 namespace {
1780
1781         /**
1782          * return true if the cursor given is at the end of a row,
1783          * and the next row is filled by an inset that spans an entire
1784          * row.
1785          */
1786         bool beforeFullRowInset(LyXText & lt, LyXCursor const & cur)
1787         {
1788                 RowList::iterator row = lt.getRow(cur);
1789                 if (boost::next(row) == lt.rows().end())
1790                         return false;
1791
1792                 Row const & next = *boost::next(row);
1793
1794                 if (next.pos() != cur.pos() || next.par() != cur.par())
1795                         return false;
1796
1797                 if (cur.pos() == cur.par()->size()
1798                     || !cur.par()->isInset(cur.pos()))
1799                         return false;
1800
1801                 InsetOld const * inset = cur.par()->getInset(cur.pos());
1802                 if (inset->needFullRow() || inset->display())
1803                         return true;
1804
1805                 return false;
1806         }
1807 }
1808
1809
1810 void LyXText::setCursorFromCoordinates(LyXCursor & cur, int x, int y)
1811 {
1812         // Get the row first.
1813
1814         RowList::iterator row = getRowNearY(y);
1815         bool bound = false;
1816         pos_type const column = getColumnNearX(row, x, bound);
1817         cur.par(row->par());
1818         cur.pos(row->pos() + column);
1819         cur.x(x);
1820         cur.y(y + row->baseline());
1821
1822         if (beforeFullRowInset(*this, cur)) {
1823                 pos_type const last = lastPrintablePos(*this, row);
1824                 RowList::iterator next_row = boost::next(row);
1825
1826                 float x = getCursorX(next_row, cur.pos(), last, bound);
1827                 cur.ix(int(x));
1828                 cur.iy(y + row->height() + next_row->baseline());
1829         } else {
1830                 cur.iy(cur.y());
1831                 cur.ix(cur.x());
1832         }
1833         cur.boundary(bound);
1834 }
1835
1836
1837 void LyXText::cursorLeft(bool internal)
1838 {
1839         if (cursor.pos() > 0) {
1840                 bool boundary = cursor.boundary();
1841                 setCursor(cursor.par(), cursor.pos() - 1, true, false);
1842                 if (!internal && !boundary &&
1843                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos() + 1))
1844                         setCursor(cursor.par(), cursor.pos() + 1, true, true);
1845         } else if (cursor.par() != ownerParagraphs().begin()) {
1846                 // steps into the paragraph above
1847                 ParagraphList::iterator pit = boost::prior(cursor.par());
1848                 setCursor(pit, pit->size());
1849         }
1850 }
1851
1852
1853 void LyXText::cursorRight(bool internal)
1854 {
1855         bool const at_end = (cursor.pos() == cursor.par()->size());
1856         bool const at_newline = !at_end &&
1857                 cursor.par()->isNewline(cursor.pos());
1858
1859         if (!internal && cursor.boundary() && !at_newline)
1860                 setCursor(cursor.par(), cursor.pos(), true, false);
1861         else if (!at_end) {
1862                 setCursor(cursor.par(), cursor.pos() + 1, true, false);
1863                 if (!internal &&
1864                     isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()))
1865                         setCursor(cursor.par(), cursor.pos(), true, true);
1866         } else if (boost::next(cursor.par()) != ownerParagraphs().end())
1867                 setCursor(boost::next(cursor.par()), 0);
1868 }
1869
1870
1871 void LyXText::cursorUp(bool selecting)
1872 {
1873 #if 1
1874         int x = cursor.x_fix();
1875         int y = cursor.y() - cursorRow()->baseline() - 1;
1876         setCursorFromCoordinates(x, y);
1877         if (!selecting) {
1878                 int topy = top_y();
1879                 int y1 = cursor.iy() - topy;
1880                 int y2 = y1;
1881                 y -= topy;
1882                 InsetOld * inset_hit = checkInsetHit(x, y1);
1883                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1884                         inset_hit->localDispatch(
1885                                 FuncRequest(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none));
1886                 }
1887         }
1888 #else
1889         setCursorFromCoordinates(bv(), cursor.x_fix(),
1890                                  cursor.y() - cursorRow()->baseline() - 1);
1891 #endif
1892 }
1893
1894
1895 void LyXText::cursorDown(bool selecting)
1896 {
1897 #if 1
1898         int x = cursor.x_fix();
1899         int y = cursor.y() - cursorRow()->baseline() + cursorRow()->height() + 1;
1900         setCursorFromCoordinates(x, y);
1901         if (!selecting && cursorRow() == cursorIRow()) {
1902                 int topy = top_y();
1903                 int y1 = cursor.iy() - topy;
1904                 int y2 = y1;
1905                 y -= topy;
1906                 InsetOld * inset_hit = checkInsetHit(x, y1);
1907                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
1908                         FuncRequest cmd(bv(), LFUN_INSET_EDIT, x, y - (y2 - y1), mouse_button::none);
1909                         inset_hit->localDispatch(cmd);
1910                 }
1911         }
1912 #else
1913         setCursorFromCoordinates(bv(), cursor.x_fix(),
1914                                  cursor.y() - cursorRow()->baseline()
1915                                  + cursorRow()->height() + 1);
1916 #endif
1917 }
1918
1919
1920 void LyXText::cursorUpParagraph()
1921 {
1922         if (cursor.pos() > 0)
1923                 setCursor(cursor.par(), 0);
1924         else if (cursor.par() != ownerParagraphs().begin())
1925                 setCursor(boost::prior(cursor.par()), 0);
1926 }
1927
1928
1929 void LyXText::cursorDownParagraph()
1930 {
1931         ParagraphList::iterator par = cursor.par();
1932         ParagraphList::iterator next_par = boost::next(par);
1933
1934         if (next_par != ownerParagraphs().end())
1935                 setCursor(next_par, 0);
1936         else
1937                 setCursor(par, par->size());
1938 }
1939
1940
1941 // fix the cursor `cur' after a characters has been deleted at `where'
1942 // position. Called by deleteEmptyParagraphMechanism
1943 void LyXText::fixCursorAfterDelete(LyXCursor & cur,
1944                                    LyXCursor const & where)
1945 {
1946         // if cursor is not in the paragraph where the delete occured,
1947         // do nothing
1948         if (cur.par() != where.par())
1949                 return;
1950
1951         // if cursor position is after the place where the delete occured,
1952         // update it
1953         if (cur.pos() > where.pos())
1954                 cur.pos(cur.pos()-1);
1955
1956         // check also if we don't want to set the cursor on a spot behind the
1957         // pagragraph because we erased the last character.
1958         if (cur.pos() > cur.par()->size())
1959                 cur.pos(cur.par()->size());
1960
1961         // recompute row et al. for this cursor
1962         setCursor(cur, cur.par(), cur.pos(), cur.boundary());
1963 }
1964
1965
1966 bool LyXText::deleteEmptyParagraphMechanism(LyXCursor const & old_cursor)
1967 {
1968         // Would be wrong to delete anything if we have a selection.
1969         if (selection.set())
1970                 return false;
1971
1972         // We allow all kinds of "mumbo-jumbo" when freespacing.
1973         if (old_cursor.par()->layout()->free_spacing
1974             || old_cursor.par()->isFreeSpacing()) {
1975                 return false;
1976         }
1977
1978         /* Ok I'll put some comments here about what is missing.
1979            I have fixed BackSpace (and thus Delete) to not delete
1980            double-spaces automagically. I have also changed Cut,
1981            Copy and Paste to hopefully do some sensible things.
1982            There are still some small problems that can lead to
1983            double spaces stored in the document file or space at
1984            the beginning of paragraphs. This happens if you have
1985            the cursor betwenn to spaces and then save. Or if you
1986            cut and paste and the selection have a space at the
1987            beginning and then save right after the paste. I am
1988            sure none of these are very hard to fix, but I will
1989            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
1990            that I can get some feedback. (Lgb)
1991         */
1992
1993         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
1994         // delete the LineSeparator.
1995         // MISSING
1996
1997         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
1998         // delete the LineSeparator.
1999         // MISSING
2000
2001         // If the pos around the old_cursor were spaces, delete one of them.
2002         if (old_cursor.par() != cursor.par()
2003             || old_cursor.pos() != cursor.pos()) {
2004                 // Only if the cursor has really moved
2005
2006                 if (old_cursor.pos() > 0
2007                     && old_cursor.pos() < old_cursor.par()->size()
2008                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2009                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2010                         bool erased = old_cursor.par()->erase(old_cursor.pos() - 1);
2011                         redoParagraph(old_cursor.par());
2012
2013                         if (!erased)
2014                                 return false;
2015 #ifdef WITH_WARNINGS
2016 #warning This will not work anymore when we have multiple views of the same buffer
2017 // In this case, we will have to correct also the cursors held by
2018 // other bufferviews. It will probably be easier to do that in a more
2019 // automated way in LyXCursor code. (JMarc 26/09/2001)
2020 #endif
2021                         // correct all cursors held by the LyXText
2022                         fixCursorAfterDelete(cursor, old_cursor);
2023                         fixCursorAfterDelete(selection.cursor, old_cursor);
2024                         fixCursorAfterDelete(selection.start, old_cursor);
2025                         fixCursorAfterDelete(selection.end, old_cursor);
2026                         return false;
2027                 }
2028         }
2029
2030         // don't delete anything if this is the ONLY paragraph!
2031         if (ownerParagraphs().size() == 1)
2032                 return false;
2033
2034         // Do not delete empty paragraphs with keepempty set.
2035         if (old_cursor.par()->allowEmpty())
2036                 return false;
2037
2038         // only do our magic if we changed paragraph
2039         if (old_cursor.par() == cursor.par())
2040                 return false;
2041
2042         // record if we have deleted a paragraph
2043         // we can't possibly have deleted a paragraph before this point
2044         bool deleted = false;
2045
2046         if (old_cursor.par()->empty() ||
2047             (old_cursor.par()->size() == 1 &&
2048              old_cursor.par()->isLineSeparator(0))) {
2049                 // ok, we will delete anything
2050                 LyXCursor tmpcursor;
2051
2052                 deleted = true;
2053
2054                 bool selection_position_was_oldcursor_position = (
2055                         selection.cursor.par()  == old_cursor.par()
2056                         && selection.cursor.pos() == old_cursor.pos());
2057
2058                 if (getRow(old_cursor) != rows().begin()) {
2059                         RowList::iterator prevrow = boost::prior(getRow(old_cursor));
2060                         tmpcursor = cursor;
2061                         cursor = old_cursor; // that undo can restore the right cursor position
2062                         #warning FIXME. --end() iterator is usable here
2063                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2064                         while (endpit != ownerParagraphs().end() &&
2065                                endpit->getDepth()) {
2066                                 ++endpit;
2067                         }
2068
2069                         recordUndo(bv(), Undo::DELETE, old_cursor.par(),
2070                                 boost::prior(endpit));
2071                         cursor = tmpcursor;
2072
2073                         // delete old row
2074                         removeRow(getRow(old_cursor));
2075                         // delete old par
2076                         ownerParagraphs().erase(old_cursor.par());
2077
2078                         /* Breakagain the next par. Needed because of
2079                          * the parindent that can occur or dissappear.
2080                          * The next row can change its height, if
2081                          * there is another layout before */
2082                         RowList::iterator tmprit = boost::next(prevrow);
2083                         if (tmprit != rows().end()) {
2084                                 breakAgain(tmprit);
2085                                 updateCounters();
2086                         }
2087                         setHeightOfRow(prevrow);
2088                 } else {
2089                         RowList::iterator nextrow = boost::next(getRow(old_cursor));
2090
2091                         tmpcursor = cursor;
2092                         cursor = old_cursor; // that undo can restore the right cursor position
2093 #warning FIXME. --end() iterator is usable here
2094                         ParagraphList::iterator endpit = boost::next(old_cursor.par());
2095                         while (endpit != ownerParagraphs().end() &&
2096                                endpit->getDepth()) {
2097                                 ++endpit;
2098                         }
2099
2100                         recordUndo(bv(), Undo::DELETE, old_cursor.par(), boost::prior(endpit));
2101                         cursor = tmpcursor;
2102
2103                         // delete old row
2104                         removeRow(getRow(old_cursor));
2105                         // delete old par
2106                         ownerParagraphs().erase(old_cursor.par());
2107
2108                         /* Breakagain the next par. Needed because of
2109                            the parindent that can occur or dissappear.
2110                            The next row can change its height, if
2111                            there is another layout before */
2112                         if (nextrow != rows().end()) {
2113                                 breakAgain(nextrow);
2114                                 updateCounters();
2115                         }
2116                 }
2117
2118                 // correct cursor y
2119                 setCursorIntern(cursor.par(), cursor.pos());
2120
2121                 if (selection_position_was_oldcursor_position) {
2122                         // correct selection
2123                         selection.cursor = cursor;
2124                 }
2125         }
2126         if (!deleted) {
2127                 if (old_cursor.par()->stripLeadingSpaces()) {
2128                         redoParagraph(old_cursor.par());
2129                         // correct cursor y
2130                         setCursorIntern(cursor.par(), cursor.pos());
2131                         selection.cursor = cursor;
2132                 }
2133         }
2134         return deleted;
2135 }
2136
2137
2138 ParagraphList & LyXText::ownerParagraphs() const
2139 {
2140         if (inset_owner) {
2141                 return inset_owner->paragraphs;
2142         }
2143         return bv_owner->buffer()->paragraphs;
2144 }
2145
2146
2147 bool LyXText::isInInset() const
2148 {
2149         // Sub-level has non-null bv owner and
2150         // non-null inset owner.
2151         return inset_owner != 0 && bv_owner != 0;
2152 }
2153
2154
2155 int defaultRowHeight()
2156 {
2157         LyXFont const font(LyXFont::ALL_SANE);
2158         return int(font_metrics::maxAscent(font)
2159                  + font_metrics::maxDescent(font) * 1.5);
2160 }