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