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