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