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