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