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