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