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