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