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