]> git.lyx.org Git - lyx.git/blob - src/text2.C
put undostack back for now
[lyx.git] / src / text2.C
1 /* This file is part of
2  * ======================================================
3  *
4  *           LyX, The Document Processor
5  *
6  *           Copyright 1995 Matthias Ettrich
7  *           Copyright 1995-2001 The LyX Team.
8  *
9  * ====================================================== */
10
11 #include <config.h>
12
13 #ifdef __GNUG__
14 #pragma implementation "lyxtext.h"
15 #endif
16
17 #include "lyxtext.h"
18 #include "LString.h"
19 #include "paragraph.h"
20 #include "lyxtextclasslist.h"
21 #include "LyXView.h"
22 #include "undo_funcs.h"
23 #include "buffer.h"
24 #include "bufferparams.h"
25 #include "gettext.h"
26 #include "BufferView.h"
27 #include "LyXView.h"
28 #include "CutAndPaste.h"
29 #include "frontends/Painter.h"
30 #include "font.h"
31 #include "debug.h"
32 #include "lyxrc.h"
33 #include "lyxrow.h"
34 #include "FloatList.h"
35 #include "language.h"
36 #include "ParagraphParameters.h"
37
38 #include "insets/inseterror.h"
39 #include "insets/insetbib.h"
40 #include "insets/insetspecialchar.h"
41 #include "insets/insettext.h"
42 #include "insets/insetfloat.h"
43
44 #include "support/LAssert.h"
45 #include "support/textutils.h"
46 #include "support/lstrings.h"
47
48 using std::vector;
49 using std::copy;
50 using std::find;
51 using std::endl;
52 using std::find;
53 using std::pair;
54 using lyx::pos_type;
55
56
57 LyXText::LyXText(BufferView * bv)
58         : number_of_rows(0), height(0), width(0), first_y(0),
59           bv_owner(bv), inset_owner(0), the_locking_inset(0),
60           need_break_row(0), refresh_y(0), refresh_row(0),
61           status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
62 {}
63
64
65 LyXText::LyXText(InsetText * inset)
66         :  number_of_rows(0),  height(0), width(0), first_y(0),
67            bv_owner(0), inset_owner(inset), the_locking_inset(0),
68            need_break_row(0), refresh_y(0), refresh_row(0),
69            status_(LyXText::UNCHANGED), firstrow(0), lastrow(0)
70 {}
71
72
73 void LyXText::init(BufferView * bview, bool reinit)
74 {
75         if (reinit) {
76                 // Delete all rows, this does not touch the paragraphs!
77                 Row * tmprow = firstrow;
78                 while (firstrow) {
79                         tmprow = firstrow->next();
80                         delete firstrow;
81                         firstrow = tmprow;
82                 }
83
84                 lastrow = 0;
85                 refresh_row = 0;
86                 need_break_row = 0;
87                 width = height = 0;
88                 copylayouttype.erase();
89                 number_of_rows = first_y = refresh_y = 0;
90                 status_ = LyXText::UNCHANGED;
91         } else if (firstrow)
92                 return;
93
94         Paragraph * par = ownerParagraph();
95         current_font = getFont(bview->buffer(), par, 0);
96
97         while (par) {
98                 insertParagraph(bview, par, lastrow);
99                 par = par->next();
100         }
101         setCursorIntern(bview, firstrow->par(), 0);
102         selection.cursor = cursor;
103 }
104
105
106 LyXText::~LyXText()
107 {
108         // Delete all rows, this does not touch the paragraphs!
109         Row * tmprow = firstrow;
110         while (firstrow) {
111                 tmprow = firstrow->next();
112                 delete firstrow;
113                 firstrow = tmprow;
114         }
115 }
116
117
118 namespace {
119
120 LyXFont const realizeFont(LyXFont const & font,
121                           Buffer const * buf,
122                           Paragraph * par)
123 {
124         LyXFont tmpfont(font);
125         Paragraph::depth_type par_depth = par->getDepth();
126
127         // Resolve against environment font information
128         while (par && par_depth && !tmpfont.resolved()) {
129                 par = par->outerHook();
130                 if (par) {
131 #ifndef INHERIT_LANGUAGE
132                         tmpfont.realize(textclasslist[buf->params.textclass][
133                                               par->layout()].font);
134 #else
135                         tmpfont.realize(textclasslist.
136                                         Style(buf->params.textclass,
137                                               par->layout()).font,
138                                         buf->params.language);
139 #endif
140                         par_depth = par->getDepth();
141                 }
142         }
143
144 #ifndef INHERIT_LANGUAGE
145         tmpfont.realize(textclasslist[buf->params.textclass].defaultfont());
146 #else
147         tmpfont.realize(textclasslist[buf->params.textclass].defaultfont(),
148                         buf->params.language);
149 #endif
150
151         return tmpfont;
152 }
153
154 }
155
156
157 // Gets the fully instantiated font at a given position in a paragraph
158 // Basically the same routine as Paragraph::getFont() in paragraph.C.
159 // The difference is that this one is used for displaying, and thus we
160 // are allowed to make cosmetic improvements. For instance make footnotes
161 // smaller. (Asger)
162 // If position is -1, we get the layout font of the paragraph.
163 // If position is -2, we get the font of the manual label of the paragraph.
164 LyXFont const LyXText::getFont(Buffer const * buf, Paragraph * par,
165                                pos_type pos) const
166 {
167         lyx::Assert(pos >= 0);
168
169         LyXLayout const & layout =
170                 textclasslist[buf->params.textclass][par->layout()];
171
172         Paragraph::depth_type par_depth = par->getDepth();
173         // We specialize the 95% common case:
174         if (!par_depth) {
175                 if (layout.labeltype == LABEL_MANUAL
176                     && pos < beginningOfMainBody(buf, par)) {
177                         // 1% goes here
178                         LyXFont f = par->getFontSettings(buf->params, pos);
179                         if (par->inInset())
180                                 par->inInset()->getDrawFont(f);
181 #ifndef INHERIT_LANGUAGE
182                         return f.realize(layout.reslabelfont);
183 #else
184                         return f.realize(layout.reslabelfont, buf->params.language);
185 #endif
186                 } else {
187                         LyXFont f = par->getFontSettings(buf->params, pos);
188                         if (par->inInset())
189                                 par->inInset()->getDrawFont(f);
190 #ifndef INHERIT_LANGUAGE
191                         return f.realize(layout.resfont);
192 #else
193                         return f.realize(layout.resfont, buf->params.language);
194 #endif
195                 }
196         }
197
198         // The uncommon case need not be optimized as much
199
200         LyXFont layoutfont;
201
202         if (pos < beginningOfMainBody(buf, par)) {
203                 // 1% goes here
204                 layoutfont = layout.labelfont;
205         } else {
206                 // 99% goes here
207                 layoutfont = layout.font;
208         }
209
210         LyXFont tmpfont = par->getFontSettings(buf->params, pos);
211 #ifndef INHERIT_LANGUAGE
212         tmpfont.realize(layoutfont);
213 #else
214         tmpfont.realize(layoutfont, buf->params.language);
215 #endif
216         if (par->inInset())
217                 par->inInset()->getDrawFont(tmpfont);
218
219         return realizeFont(tmpfont, buf, par);
220 }
221
222
223 LyXFont const LyXText::getLayoutFont(Buffer const * buf, Paragraph * par) const
224 {
225         LyXLayout const & layout =
226                 textclasslist[buf->params.textclass][par->layout()];
227
228         Paragraph::depth_type par_depth = par->getDepth();
229
230         if (!par_depth) {
231                 return layout.resfont;
232         }
233
234         return realizeFont(layout.font, buf, par);
235 }
236
237
238 LyXFont const LyXText::getLabelFont(Buffer const * buf, Paragraph * par) const
239 {
240         LyXLayout const & layout =
241                 textclasslist[buf->params.textclass][par->layout()];
242
243         Paragraph::depth_type par_depth = par->getDepth();
244
245         if (!par_depth) {
246                 return layout.reslabelfont;
247         }
248
249         return realizeFont(layout.labelfont, buf, par);
250 }
251
252
253 void LyXText::setCharFont(BufferView * bv, Paragraph * par,
254                           pos_type pos, LyXFont const & fnt,
255                           bool toggleall)
256 {
257         Buffer const * buf = bv->buffer();
258         LyXFont font = getFont(buf, par, pos);
259         font.update(fnt, buf->params.language, toggleall);
260         // Let the insets convert their font
261         if (par->isInset(pos)) {
262                 Inset * inset = par->getInset(pos);
263                 if (isEditableInset(inset)) {
264                         UpdatableInset * uinset =
265                                 static_cast<UpdatableInset *>(inset);
266                         uinset->setFont(bv, fnt, toggleall, true);
267                 }
268         }
269
270         // Plug thru to version below:
271         setCharFont(buf, par, pos, font);
272 }
273
274
275 void LyXText::setCharFont(Buffer const * buf, Paragraph * par,
276                           pos_type pos, LyXFont const & fnt)
277 {
278         LyXFont font(fnt);
279
280         LyXTextClass const & tclass = textclasslist[buf->params.textclass];
281         LyXLayout const & layout = tclass[par->layout()];
282
283         // Get concrete layout font to reduce against
284         LyXFont layoutfont;
285
286         if (pos < beginningOfMainBody(buf, par))
287                 layoutfont = layout.labelfont;
288         else
289                 layoutfont = layout.font;
290
291         // Realize against environment font information
292         if (par->getDepth()) {
293                 Paragraph * tp = par;
294                 while (!layoutfont.resolved() && tp && tp->getDepth()) {
295                         tp = tp->outerHook();
296                         if (tp)
297 #ifndef INHERIT_LANGUAGE
298                                 layoutfont.realize(tclass[tp->layout()].font);
299 #else
300                                 layoutfont.realize(textclasslist.
301                                                    Style(buf->params.textclass,
302                                                          tp->layout()).font,
303                                                    buf->params.language);
304 #endif
305                 }
306         }
307
308 #ifndef INHERIT_LANGUAGE
309         layoutfont.realize(tclass.defaultfont());
310 #else
311         layoutfont.realize(tclass.defaultfont(), buf->params.language);
312 #endif
313
314         // Now, reduce font against full layout font
315         font.reduce(layoutfont);
316
317         par->setFont(pos, font);
318 }
319
320
321 // inserts a new row behind the specified row, increments
322 // the touched counters
323 void LyXText::insertRow(Row * row, Paragraph * par,
324                         pos_type pos) const
325 {
326         Row * tmprow = new Row;
327         if (!row) {
328                 tmprow->previous(0);
329                 tmprow->next(firstrow);
330                 firstrow = tmprow;
331         } else {
332                 tmprow->previous(row);
333                 tmprow->next(row->next());
334                 row->next(tmprow);
335         }
336
337         if (tmprow->next())
338                 tmprow->next()->previous(tmprow);
339
340         if (tmprow->previous())
341                 tmprow->previous()->next(tmprow);
342
343
344         tmprow->par(par);
345         tmprow->pos(pos);
346
347         if (row == lastrow)
348                 lastrow = tmprow;
349         ++number_of_rows;
350 }
351
352
353 // removes the row and reset the touched counters
354 void LyXText::removeRow(Row * row) const
355 {
356         Row * row_prev = row->previous();
357         if (row->next())
358                 row->next()->previous(row_prev);
359         if (!row_prev) {
360                 firstrow = row->next();
361 //              lyx::Assert(firstrow);
362         } else  {
363                 row_prev->next(row->next());
364         }
365         if (row == lastrow) {
366                 lyx::Assert(!row->next());
367                 lastrow = row_prev;
368         }
369         if (refresh_row == row) {
370                 refresh_row = row_prev ? row_prev : row->next();
371                 // what about refresh_y, refresh_height
372         }
373
374         height -= row->height(); // the text becomes smaller
375
376         delete row;
377         --number_of_rows;       // one row less
378 }
379
380
381 // remove all following rows of the paragraph of the specified row.
382 void LyXText::removeParagraph(Row * row) const
383 {
384         Paragraph * tmppar = row->par();
385         row = row->next();
386
387         Row * tmprow;
388         while (row && row->par() == tmppar) {
389                 tmprow = row->next();
390                 removeRow(row);
391                 row = tmprow;
392         }
393 }
394
395
396 // insert the specified paragraph behind the specified row
397 void LyXText::insertParagraph(BufferView * bview, Paragraph * par,
398                               Row * row) const
399 {
400         insertRow(row, par, 0);            /* insert a new row, starting
401                                             * at postition 0 */
402
403         setCounter(bview->buffer(), par);  // set the counters
404
405         // and now append the whole paragraph behind the new row
406         if (!row) {
407                 firstrow->height(0);
408                 appendParagraph(bview, firstrow);
409         } else {
410                 row->next()->height(0);
411                 appendParagraph(bview, row->next());
412         }
413 }
414
415
416 Inset * LyXText::getInset() const
417 {
418         Inset * inset = 0;
419         if (cursor.pos() == 0 && cursor.par()->bibkey) {
420                 inset = cursor.par()->bibkey;
421         } else if (cursor.pos() < cursor.par()->size()
422                    && cursor.par()->isInset(cursor.pos())) {
423                 inset = cursor.par()->getInset(cursor.pos());
424         }
425         return inset;
426 }
427
428
429 void LyXText::toggleInset(BufferView * bview)
430 {
431         Inset * inset = getInset();
432         // is there an editable inset at cursor position?
433         if (!isEditableInset(inset)) {
434                 // No, try to see if we are inside a collapsable inset
435                 if (inset_owner && inset_owner->owner()
436                     && inset_owner->owner()->isOpen()) {
437                         bview->unlockInset(static_cast<UpdatableInset *>(inset_owner->owner()));
438                         inset_owner->owner()->close(bview);
439                 }
440                 return;
441         }
442         //bview->owner()->message(inset->editMessage());
443
444         // do we want to keep this?? (JMarc)
445         if (!isHighlyEditableInset(inset))
446                 setCursorParUndo(bview);
447
448         if (inset->isOpen()) {
449                 inset->close(bview);
450         } else {
451                 inset->open(bview);
452         }
453 #if 0
454         inset->open(bview, !inset->isOpen());
455 #endif
456 }
457
458
459 /* used in setlayout */
460 // Asger is not sure we want to do this...
461 void LyXText::makeFontEntriesLayoutSpecific(Buffer const * buf,
462                                             Paragraph * par)
463 {
464         LyXLayout const & layout =
465                 textclasslist[buf->params.textclass][par->layout()];
466
467         LyXFont layoutfont;
468         for (pos_type pos = 0; pos < par->size(); ++pos) {
469                 if (pos < beginningOfMainBody(buf, par))
470                         layoutfont = layout.labelfont;
471                 else
472                         layoutfont = layout.font;
473
474                 LyXFont tmpfont = par->getFontSettings(buf->params, pos);
475                 tmpfont.reduce(layoutfont);
476                 par->setFont(pos, tmpfont);
477         }
478 }
479
480
481 Paragraph * LyXText::setLayout(BufferView * bview,
482                                LyXCursor & cur, LyXCursor & sstart_cur,
483                                LyXCursor & send_cur,
484                                string const & layout)
485 {
486         Paragraph * endpar = send_cur.par()->next();
487         Paragraph * undoendpar = endpar;
488
489         if (endpar && endpar->getDepth()) {
490                 while (endpar && endpar->getDepth()) {
491                         endpar = endpar->next();
492                         undoendpar = endpar;
493                 }
494         } else if (endpar) {
495                 endpar = endpar->next(); // because of parindents etc.
496         }
497
498         setUndo(bview, Undo::EDIT, sstart_cur.par(), undoendpar);
499
500         // ok we have a selection. This is always between sstart_cur
501         // and sel_end cursor
502         cur = sstart_cur;
503         Paragraph * par = sstart_cur.par();
504         Paragraph * epar = send_cur.par()->next();
505
506         LyXLayout const & lyxlayout =
507                 textclasslist[bview->buffer()->params.textclass][layout];
508
509         do {
510                 par->applyLayout(layout);
511                 makeFontEntriesLayoutSpecific(bview->buffer(), par);
512                 Paragraph * fppar = par;
513                 fppar->params().spaceTop(lyxlayout.fill_top ?
514                                          VSpace(VSpace::VFILL)
515                                          : VSpace(VSpace::NONE));
516                 fppar->params().spaceBottom(lyxlayout.fill_bottom ?
517                                             VSpace(VSpace::VFILL)
518                                             : VSpace(VSpace::NONE));
519                 if (lyxlayout.margintype == MARGIN_MANUAL)
520                         par->setLabelWidthString(lyxlayout.labelstring());
521                 if (lyxlayout.labeltype != LABEL_BIBLIO
522                     && fppar->bibkey) {
523                         delete fppar->bibkey;
524                         fppar->bibkey = 0;
525                 }
526                 cur.par(par);
527                 par = par->next();
528         } while (par != epar);
529
530         return endpar;
531 }
532
533
534 // set layout over selection and make a total rebreak of those paragraphs
535 void LyXText::setLayout(BufferView * bview, string const & layout)
536 {
537         LyXCursor tmpcursor = cursor;  /* store the current cursor  */
538
539         // if there is no selection just set the layout
540         // of the current paragraph  */
541         if (!selection.set()) {
542                 selection.start = cursor;  // dummy selection
543                 selection.end = cursor;
544         }
545         Paragraph * endpar = setLayout(bview, cursor, selection.start,
546                                        selection.end, layout);
547         redoParagraphs(bview, selection.start, endpar);
548
549         // we have to reset the selection, because the
550         // geometry could have changed
551         setCursor(bview, selection.start.par(),
552                   selection.start.pos(), false);
553         selection.cursor = cursor;
554         setCursor(bview, selection.end.par(), selection.end.pos(), false);
555         updateCounters(bview, cursor.row());
556         clearSelection();
557         setSelection(bview);
558         setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true);
559 }
560
561
562 // increment depth over selection and
563 // make a total rebreak of those paragraphs
564 void  LyXText::incDepth(BufferView * bview)
565 {
566         // If there is no selection, just use the current paragraph
567         if (!selection.set()) {
568                 selection.start = cursor; // dummy selection
569                 selection.end = cursor;
570         }
571
572         // We end at the next paragraph with depth 0
573         Paragraph * endpar = selection.end.par()->next();
574
575         Paragraph * undoendpar = endpar;
576
577         if (endpar && endpar->getDepth()) {
578                 while (endpar && endpar->getDepth()) {
579                         endpar = endpar->next();
580                         undoendpar = endpar;
581                 }
582         } else if (endpar) {
583                 endpar = endpar->next(); // because of parindents etc.
584         }
585
586         setUndo(bview, Undo::EDIT,
587                 selection.start.par(), undoendpar);
588
589         LyXCursor tmpcursor = cursor; // store the current cursor
590
591         // ok we have a selection. This is always between sel_start_cursor
592         // and sel_end cursor
593         cursor = selection.start;
594
595         bool anything_changed = false;
596
597         LyXTextClass const & tclass =
598                 textclasslist[bview->buffer()->params.textclass];
599
600         while (true) {
601                 // NOTE: you can't change the depth of a bibliography entry
602                 if (tclass[cursor.par()->layout()].labeltype != LABEL_BIBLIO) {
603                         Paragraph * prev = cursor.par()->previous();
604
605                         if (prev) {
606                                 if (cursor.par()->getDepth()
607                                     < prev->getMaxDepthAfter(bview->buffer())){
608                                         cursor.par()->params().depth(cursor.par()->getDepth() + 1);
609                                         anything_changed = true;
610                                 }
611                         }
612                 }
613                 if (cursor.par() == selection.end.par())
614                         break;
615                 cursor.par(cursor.par()->next());
616         }
617
618         // if nothing changed set all depth to 0
619         if (!anything_changed) {
620                 cursor = selection.start;
621                 while (cursor.par() != selection.end.par()) {
622                         cursor.par()->params().depth(0);
623                         cursor.par(cursor.par()->next());
624                 }
625                 cursor.par()->params().depth(0);
626         }
627
628         redoParagraphs(bview, selection.start, endpar);
629
630         // we have to reset the selection, because the
631         // geometry could have changed
632         setCursor(bview, selection.start.par(), selection.start.pos());
633         selection.cursor = cursor;
634         setCursor(bview, selection.end.par(), selection.end.pos());
635         updateCounters(bview, cursor.row());
636         clearSelection();
637         setSelection(bview);
638         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
639 }
640
641
642 // decrement depth over selection and
643 // make a total rebreak of those paragraphs
644 void  LyXText::decDepth(BufferView * bview)
645 {
646         // if there is no selection just set the layout
647         // of the current paragraph
648         if (!selection.set()) {
649                 selection.start = cursor; // dummy selection
650                 selection.end = cursor;
651         }
652         Paragraph * endpar = selection.end.par()->next();
653         Paragraph * undoendpar = endpar;
654
655         if (endpar && endpar->getDepth()) {
656                 while (endpar && endpar->getDepth()) {
657                         endpar = endpar->next();
658                         undoendpar = endpar;
659                 }
660         } else if (endpar) {
661                 endpar = endpar->next(); // because of parindents etc.
662         }
663
664         setUndo(bview, Undo::EDIT,
665                 selection.start.par(), undoendpar);
666
667         LyXCursor tmpcursor = cursor; // store the current cursor
668
669         // ok we have a selection. This is always between sel_start_cursor
670         // and sel_end cursor
671         cursor = selection.start;
672
673         while (true) {
674                 if (cursor.par()->params().depth()) {
675                         cursor.par()->params()
676                                 .depth(cursor.par()->params().depth() - 1);
677                 }
678                 if (cursor.par() == selection.end.par()) {
679                         break;
680                 }
681                 cursor.par(cursor.par()->next());
682         }
683
684         redoParagraphs(bview, selection.start, endpar);
685
686         // we have to reset the selection, because the
687         // geometry could have changed
688         setCursor(bview, selection.start.par(),
689                   selection.start.pos());
690         selection.cursor = cursor;
691         setCursor(bview, selection.end.par(), selection.end.pos());
692         updateCounters(bview, cursor.row());
693         clearSelection();
694         setSelection(bview);
695         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
696 }
697
698
699 // set font over selection and make a total rebreak of those paragraphs
700 void LyXText::setFont(BufferView * bview, LyXFont const & font, bool toggleall)
701 {
702         // if there is no selection just set the current_font
703         if (!selection.set()) {
704                 // Determine basis font
705                 LyXFont layoutfont;
706                 if (cursor.pos() < beginningOfMainBody(bview->buffer(),
707                                                        cursor.par())) {
708                         layoutfont = getLabelFont(bview->buffer(),
709                                                   cursor.par());
710                 } else {
711                         layoutfont = getLayoutFont(bview->buffer(),
712                                                    cursor.par());
713                 }
714                 // Update current font
715                 real_current_font.update(font,
716                                          bview->buffer()->params.language,
717                                          toggleall);
718
719                 // Reduce to implicit settings
720                 current_font = real_current_font;
721                 current_font.reduce(layoutfont);
722                 // And resolve it completely
723 #ifndef INHERIT_LANGUAGE
724                 real_current_font.realize(layoutfont);
725 #else
726                 real_current_font.realize(layoutfont,
727                                           bview->buffer()->params.language);
728 #endif
729                 return;
730         }
731
732         LyXCursor tmpcursor = cursor; // store the current cursor
733
734         // ok we have a selection. This is always between sel_start_cursor
735         // and sel_end cursor
736
737         setUndo(bview, Undo::EDIT,
738                 selection.start.par(), selection.end.par()->next());
739         freezeUndo();
740         cursor = selection.start;
741         while (cursor.par() != selection.end.par() ||
742                cursor.pos() < selection.end.pos()) 
743         {
744                 if (cursor.pos() < cursor.par()->size()) {
745                         // an open footnote should behave like a closed one
746                         setCharFont(bview, cursor.par(), cursor.pos(),
747                                     font, toggleall);
748                         cursor.pos(cursor.pos() + 1);
749                 } else {
750                         cursor.pos(0);
751                         cursor.par(cursor.par()->next());
752                 }
753         }
754         unFreezeUndo();
755
756         redoParagraphs(bview, selection.start, selection.end.par()->next());
757
758         // we have to reset the selection, because the
759         // geometry could have changed, but we keep
760         // it for user convenience
761         setCursor(bview, selection.start.par(), selection.start.pos());
762         selection.cursor = cursor;
763         setCursor(bview, selection.end.par(), selection.end.pos());
764         setSelection(bview);
765         setCursor(bview, tmpcursor.par(), tmpcursor.pos(), true,
766                   tmpcursor.boundary());
767 }
768
769
770 void LyXText::redoHeightOfParagraph(BufferView * bview, LyXCursor const & cur)
771 {
772         Row * tmprow = cur.row();
773         int y = cur.y() - tmprow->baseline();
774
775         setHeightOfRow(bview, tmprow);
776
777         while (tmprow->previous()
778                && tmprow->previous()->par() == tmprow->par()) {
779                 tmprow = tmprow->previous();
780                 y -= tmprow->height();
781                 setHeightOfRow(bview, tmprow);
782         }
783
784         // we can set the refreshing parameters now
785         status(bview, LyXText::NEED_MORE_REFRESH);
786         refresh_y = y;
787         refresh_row = tmprow;
788         setCursor(bview, cur.par(), cur.pos(), false, cursor.boundary());
789 }
790
791
792 void LyXText::redoDrawingOfParagraph(BufferView * bview, LyXCursor const & cur)
793 {
794         Row * tmprow = cur.row();
795
796         int y = cur.y() - tmprow->baseline();
797         setHeightOfRow(bview, tmprow);
798
799         while (tmprow->previous()
800                && tmprow->previous()->par() == tmprow->par())  {
801                 tmprow = tmprow->previous();
802                 y -= tmprow->height();
803         }
804
805         // we can set the refreshing parameters now
806         if (status_ == LyXText::UNCHANGED || y < refresh_y) {
807                 refresh_y = y;
808                 refresh_row = tmprow;
809         }
810         status(bview, LyXText::NEED_MORE_REFRESH);
811         setCursor(bview, cur.par(), cur.pos());
812 }
813
814
815 // deletes and inserts again all paragaphs between the cursor
816 // and the specified par
817 // This function is needed after SetLayout and SetFont etc.
818 void LyXText::redoParagraphs(BufferView * bview, LyXCursor const & cur,
819                              Paragraph const * endpar) const
820 {
821         Row * tmprow2;
822         Paragraph * tmppar = 0;
823         Paragraph * first_phys_par = 0;
824
825         Row * tmprow = cur.row();
826
827         int y = cur.y() - tmprow->baseline();
828
829         if (!tmprow->previous()) {
830                 // a trick/hack for UNDO
831                 // This is needed because in an UNDO/REDO we could have changed
832                 // the ownerParagrah() so the paragraph inside the row is NOT
833                 // my really first par anymore. Got it Lars ;) (Jug 20011206)
834                 first_phys_par = ownerParagraph();
835         } else {
836                 first_phys_par = tmprow->par();
837                 while (tmprow->previous()
838                        && tmprow->previous()->par() == first_phys_par)
839                 {
840                         tmprow = tmprow->previous();
841                         y -= tmprow->height();
842                 }
843         }
844
845         // we can set the refreshing parameters now
846         status(bview, LyXText::NEED_MORE_REFRESH);
847         refresh_y = y;
848         refresh_row = tmprow->previous();        /* the real refresh row will
849                                                 be deleted, so I store
850                                                 the previous here */
851         // remove it
852         if (tmprow->next())
853                 tmppar = tmprow->next()->par();
854         else
855                 tmppar = 0;
856         while (tmprow->next() && tmppar != endpar) {
857                 removeRow(tmprow->next());
858                 if (tmprow->next()) {
859                         tmppar = tmprow->next()->par();
860                 } else {
861                         tmppar = 0;
862                 }
863         }
864
865         // remove the first one
866         tmprow2 = tmprow;     /* this is because tmprow->previous()
867                                  can be 0 */
868         tmprow = tmprow->previous();
869         removeRow(tmprow2);
870
871         tmppar = first_phys_par;
872
873         do {
874                 if (tmppar) {
875                         insertParagraph(bview, tmppar, tmprow);
876                         if (!tmprow) {
877                                 tmprow = firstrow;
878                         }
879                         while (tmprow->next()
880                                && tmprow->next()->par() == tmppar) {
881                                 tmprow = tmprow->next();
882                         }
883                         tmppar = tmppar->next();
884                 }
885         } while (tmppar && tmppar != endpar);
886
887         // this is because of layout changes
888         if (refresh_row) {
889                 refresh_y -= refresh_row->height();
890                 setHeightOfRow(bview, refresh_row);
891         } else {
892                 refresh_row = firstrow;
893                 refresh_y = 0;
894                 setHeightOfRow(bview, refresh_row);
895         }
896
897         if (tmprow && tmprow->next())
898                 setHeightOfRow(bview, tmprow->next());
899 }
900
901
902 void LyXText::fullRebreak(BufferView * bview)
903 {
904         if (!firstrow) {
905                 init(bview);
906                 return;
907         }
908         if (need_break_row) {
909                 breakAgain(bview, need_break_row);
910                 need_break_row = 0;
911                 return;
912         }
913 }
914
915
916 // important for the screen
917
918
919 /* the cursor set functions have a special mechanism. When they
920  * realize, that you left an empty paragraph, they will delete it.
921  * They also delete the corresponding row */
922
923 // need the selection cursor:
924 void LyXText::setSelection(BufferView * bview)
925 {
926         bool const lsel = selection.set();
927
928         if (!selection.set()) {
929                 last_sel_cursor = selection.cursor;
930                 selection.start = selection.cursor;
931                 selection.end = selection.cursor;
932         }
933
934         selection.set(true);
935
936         // first the toggling area
937         if (cursor.y() < last_sel_cursor.y()
938             || (cursor.y() == last_sel_cursor.y()
939                 && cursor.x() < last_sel_cursor.x())) {
940                 toggle_end_cursor = last_sel_cursor;
941                 toggle_cursor = cursor;
942         } else {
943                 toggle_end_cursor = cursor;
944                 toggle_cursor = last_sel_cursor;
945         }
946
947         last_sel_cursor = cursor;
948
949         // and now the whole selection
950
951         if (selection.cursor.par() == cursor.par())
952                 if (selection.cursor.pos() < cursor.pos()) {
953                         selection.end = cursor;
954                         selection.start = selection.cursor;
955                 } else {
956                         selection.end = selection.cursor;
957                         selection.start = cursor;
958                 }
959         else if (selection.cursor.y() < cursor.y() ||
960                  (selection.cursor.y() == cursor.y()
961                   && selection.cursor.x() < cursor.x())) {
962                 selection.end = cursor;
963                 selection.start = selection.cursor;
964         }
965         else {
966                 selection.end = selection.cursor;
967                 selection.start = cursor;
968         }
969
970         // a selection with no contents is not a selection
971         if (selection.start.par() == selection.end.par() &&
972             selection.start.pos() == selection.end.pos())
973                 selection.set(false);
974
975         if (inset_owner && (selection.set() || lsel))
976                 inset_owner->setUpdateStatus(bview, InsetText::SELECTION);
977 }
978
979
980 string const LyXText::selectionAsString(Buffer const * buffer,
981                                         bool label) const
982 {
983         if (!selection.set()) return string();
984         string result;
985
986         // Special handling if the whole selection is within one paragraph
987         if (selection.start.par() == selection.end.par()) {
988                 result += selection.start.par()->asString(buffer,
989                                                           selection.start.pos(),
990                                                           selection.end.pos(),
991                                                           label);
992                 return result;
993         }
994
995         // The selection spans more than one paragraph
996
997         // First paragraph in selection
998         result += selection.start.par()->asString(buffer,
999                                                   selection.start.pos(),
1000                                                   selection.start.par()->size(),
1001                                                   label)
1002                 + "\n\n";
1003
1004         // The paragraphs in between (if any)
1005         LyXCursor tmpcur(selection.start);
1006         tmpcur.par(tmpcur.par()->next());
1007         while (tmpcur.par() != selection.end.par()) {
1008                 result += tmpcur.par()->asString(buffer, 0,
1009                                                  tmpcur.par()->size(),
1010                                                  label) + "\n\n";
1011                 tmpcur.par(tmpcur.par()->next());
1012         }
1013
1014         // Last paragraph in selection
1015         result += selection.end.par()->asString(buffer, 0,
1016                                                 selection.end.pos(), label);
1017
1018         return result;
1019 }
1020
1021
1022 void LyXText::clearSelection() const
1023 {
1024         selection.set(false);
1025         selection.mark(false);
1026         last_sel_cursor = selection.end = selection.start = selection.cursor = cursor;
1027         // reset this in the bv_owner!
1028         if (bv_owner && bv_owner->text)
1029                 bv_owner->text->xsel_cache.set(false);
1030 }
1031
1032
1033 void LyXText::cursorHome(BufferView * bview) const
1034 {
1035         setCursor(bview, cursor.par(), cursor.row()->pos());
1036 }
1037
1038
1039 void LyXText::cursorEnd(BufferView * bview) const
1040 {
1041         if (!cursor.row()->next()
1042             || cursor.row()->next()->par() != cursor.row()->par()) {
1043                 setCursor(bview, cursor.par(), rowLast(cursor.row()) + 1);
1044         } else {
1045                 if (cursor.par()->size() &&
1046                     (cursor.par()->getChar(rowLast(cursor.row())) == ' '
1047                      || cursor.par()->isNewline(rowLast(cursor.row())))) {
1048                         setCursor(bview, cursor.par(), rowLast(cursor.row()));
1049                 } else {
1050                         setCursor(bview,cursor.par(),
1051                                   rowLast(cursor.row()) + 1);
1052                 }
1053         }
1054 }
1055
1056
1057 void LyXText::cursorTop(BufferView * bview) const
1058 {
1059         while (cursor.par()->previous())
1060                 cursor.par(cursor.par()->previous());
1061         setCursor(bview, cursor.par(), 0);
1062 }
1063
1064
1065 void LyXText::cursorBottom(BufferView * bview) const
1066 {
1067         while (cursor.par()->next())
1068                 cursor.par(cursor.par()->next());
1069         setCursor(bview, cursor.par(), cursor.par()->size());
1070 }
1071
1072
1073 void LyXText::toggleFree(BufferView * bview,
1074                          LyXFont const & font, bool toggleall)
1075 {
1076         // If the mask is completely neutral, tell user
1077         if (font == LyXFont(LyXFont::ALL_IGNORE)) {
1078                 // Could only happen with user style
1079                 bview->owner()->message(_("No font change defined. Use Character under the Layout menu to define font change."));
1080                 return;
1081         }
1082
1083         // Try implicit word selection
1084         // If there is a change in the language the implicit word selection
1085         // is disabled.
1086         LyXCursor resetCursor = cursor;
1087         bool implicitSelection = (font.language() == ignore_language
1088                                   && font.number() == LyXFont::IGNORE)
1089                 ? selectWordWhenUnderCursor(bview, WHOLE_WORD_STRICT) : false;
1090
1091         // Set font
1092         setFont(bview, font, toggleall);
1093
1094         // Implicit selections are cleared afterwards
1095         //and cursor is set to the original position.
1096         if (implicitSelection) {
1097                 clearSelection();
1098                 cursor = resetCursor;
1099                 setCursor(bview, cursor.par(), cursor.pos());
1100                 selection.cursor = cursor;
1101         }
1102         if (inset_owner)
1103                 inset_owner->setUpdateStatus(bview, InsetText::CURSOR_PAR);
1104 }
1105
1106
1107 string LyXText::getStringToIndex(BufferView * bview)
1108 {
1109         string idxstring;
1110
1111         // Try implicit word selection
1112         // If there is a change in the language the implicit word selection
1113         // is disabled.
1114         LyXCursor resetCursor = cursor;
1115         bool implicitSelection = selectWordWhenUnderCursor(bview, PREVIOUS_WORD);
1116
1117         if (!selection.set()) {
1118                 bview->owner()->message(_("Nothing to index!"));
1119                 return string();
1120         }
1121         if (selection.start.par() != selection.end.par()) {
1122                 bview->owner()->message(_("Cannot index more than one paragraph!"));
1123                 return string();
1124         }
1125
1126         idxstring = selectionAsString(bview->buffer(), false);
1127
1128         // Implicit selections are cleared afterwards
1129         //and cursor is set to the original position.
1130         if (implicitSelection) {
1131                 clearSelection();
1132                 cursor = resetCursor;
1133                 setCursor(bview, cursor.par(), cursor.pos());
1134                 selection.cursor = cursor;
1135         }
1136         return idxstring;
1137 }
1138
1139
1140 pos_type LyXText::beginningOfMainBody(Buffer const * buf,
1141                              Paragraph const * par) const
1142 {
1143         if (textclasslist[buf->params.textclass][
1144                                 par->layout()].labeltype != LABEL_MANUAL)
1145                 return 0;
1146         else
1147                 return par->beginningOfMainBody();
1148 }
1149
1150
1151 /* the DTP switches for paragraphs. LyX will store them in the
1152 * first physicla paragraph. When a paragraph is broken, the top settings
1153 * rest, the bottom settings are given to the new one. So I can make shure,
1154 * they do not duplicate themself and you cannnot make dirty things with
1155 * them!  */
1156
1157 void LyXText::setParagraph(BufferView * bview,
1158                            bool line_top, bool line_bottom,
1159                            bool pagebreak_top, bool pagebreak_bottom,
1160                            VSpace const & space_top,
1161                            VSpace const & space_bottom,
1162                            Spacing const & spacing,
1163                            LyXAlignment align,
1164                            string labelwidthstring,
1165                            bool noindent)
1166 {
1167         LyXCursor tmpcursor = cursor;
1168         if (!selection.set()) {
1169                 selection.start = cursor;
1170                 selection.end = cursor;
1171         }
1172
1173         // make sure that the depth behind the selection are restored, too
1174         Paragraph * endpar = selection.end.par()->next();
1175         Paragraph * undoendpar = endpar;
1176
1177         if (endpar && endpar->getDepth()) {
1178                 while (endpar && endpar->getDepth()) {
1179                         endpar = endpar->next();
1180                         undoendpar = endpar;
1181                 }
1182         }
1183         else if (endpar) {
1184                 // because of parindents etc.
1185                 endpar = endpar->next();
1186         }
1187
1188         setUndo(bview, Undo::EDIT, selection.start.par(), undoendpar);
1189
1190
1191         Paragraph * tmppar = selection.end.par();
1192         LyXTextClass const & tclass =
1193                 textclasslist[bview->buffer()->params.textclass];
1194
1195         while (tmppar != selection.start.par()->previous()) {
1196                 setCursor(bview, tmppar, 0);
1197                 status(bview, LyXText::NEED_MORE_REFRESH);
1198                 refresh_row = cursor.row();
1199                 refresh_y = cursor.y() - cursor.row()->baseline();
1200                 cursor.par()->params().lineTop(line_top);
1201                 cursor.par()->params().lineBottom(line_bottom);
1202                 cursor.par()->params().pagebreakTop(pagebreak_top);
1203                 cursor.par()->params().pagebreakBottom(pagebreak_bottom);
1204                 cursor.par()->params().spaceTop(space_top);
1205                 cursor.par()->params().spaceBottom(space_bottom);
1206                 cursor.par()->params().spacing(spacing);
1207                 // does the layout allow the new alignment?
1208                 LyXLayout const & layout = tclass[cursor.par()->layout()];
1209
1210                 if (align == LYX_ALIGN_LAYOUT)
1211                         align = layout.align;
1212                 if (align & layout.alignpossible) {
1213                         if (align == layout.align)
1214                                 cursor.par()->params().align(LYX_ALIGN_LAYOUT);
1215                         else
1216                                 cursor.par()->params().align(align);
1217                 }
1218                 cursor.par()->setLabelWidthString(labelwidthstring);
1219                 cursor.par()->params().noindent(noindent);
1220                 tmppar = cursor.par()->previous();
1221         }
1222
1223         redoParagraphs(bview, selection.start, endpar);
1224
1225         clearSelection();
1226         setCursor(bview, selection.start.par(), selection.start.pos());
1227         selection.cursor = cursor;
1228         setCursor(bview, selection.end.par(), selection.end.pos());
1229         setSelection(bview);
1230         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1231         if (inset_owner)
1232                 bview->updateInset(inset_owner, true);
1233 }
1234
1235
1236 char loweralphaCounter(int n)
1237 {
1238         if (n < 1 || n > 26)
1239                 return '?';
1240         else
1241                 return 'a' + n - 1;
1242 }
1243
1244
1245 namespace {
1246
1247 inline
1248 char alphaCounter(int n)
1249 {
1250         if (n < 1 || n > 26)
1251                 return '?';
1252         else
1253                 return 'A' + n - 1;
1254 }
1255
1256
1257 inline
1258 char hebrewCounter(int n)
1259 {
1260         static const char hebrew[22] = {
1261                 'à', 'á', 'â', 'ã', 'ä', 'Ã¥', 'æ', 'ç', 'è',
1262                 'é', 'ë', 'ì', 'î', 'ð', 'ñ', 'ò', 'ô', 'ö',
1263                 '÷', 'ø', 'ù', 'ú'
1264         };
1265         if (n < 1 || n > 22)
1266                 return '?';
1267         else
1268                 return hebrew[n-1];
1269 }
1270
1271
1272 inline
1273 string const romanCounter(int n)
1274 {
1275         static char const * roman[20] = {
1276                 "i",   "ii",  "iii", "iv", "v",
1277                 "vi",  "vii", "viii", "ix", "x",
1278                 "xi",  "xii", "xiii", "xiv", "xv",
1279                 "xvi", "xvii", "xviii", "xix", "xx"
1280         };
1281         if (n < 1 || n > 20)
1282                 return "??";
1283         else
1284                 return roman[n-1];
1285 }
1286
1287 } // namespace anon
1288
1289
1290 // set the counter of a paragraph. This includes the labels
1291 void LyXText::setCounter(Buffer const * buf, Paragraph * par) const
1292 {
1293         LyXTextClass const & textclass = textclasslist[buf->params.textclass];
1294         LyXLayout const & layout = textclass[par->layout()];
1295
1296
1297         // copy the prev-counters to this one,
1298         // unless this is the first paragraph
1299         if (par->previous()) {
1300                 for (int i = 0; i < 10; ++i) {
1301                         par->setCounter(i, par->previous()->getFirstCounter(i));
1302                 }
1303                 par->params().appendix(par->previous()->params().appendix());
1304                 if (!par->params().appendix() && par->params().startOfAppendix()) {
1305                         par->params().appendix(true);
1306                         for (int i = 0; i < 10; ++i) {
1307                                 par->setCounter(i, 0);
1308                         }
1309                 }
1310                 par->enumdepth = par->previous()->enumdepth;
1311                 par->itemdepth = par->previous()->itemdepth;
1312         } else {
1313                 for (int i = 0; i < 10; ++i) {
1314                         par->setCounter(i, 0);
1315                 }
1316                 par->params().appendix(par->params().startOfAppendix());
1317                 par->enumdepth = 0;
1318                 par->itemdepth = 0;
1319         }
1320
1321         /* Maybe we have to increment the enumeration depth.
1322          * BUT, enumeration in a footnote is considered in isolation from its
1323          *      surrounding paragraph so don't increment if this is the
1324          *      first line of the footnote
1325          * AND, bibliographies can't have their depth changed ie. they
1326          *      are always of depth 0
1327          */
1328         if (par->previous()
1329             && par->previous()->getDepth() < par->getDepth()
1330             && textclass[par->previous()->layout()].labeltype == LABEL_COUNTER_ENUMI
1331             && par->enumdepth < 3
1332             && layout.labeltype != LABEL_BIBLIO) {
1333                 par->enumdepth++;
1334         }
1335
1336         // Maybe we have to decrement the enumeration depth, see note above
1337         if (par->previous()
1338             && par->previous()->getDepth() > par->getDepth()
1339             && layout.labeltype != LABEL_BIBLIO) {
1340                 par->enumdepth = par->depthHook(par->getDepth())->enumdepth;
1341                 par->setCounter(6 + par->enumdepth,
1342                                 par->depthHook(par->getDepth())->getCounter(6 + par->enumdepth));
1343                 /* reset the counters.
1344                  * A depth change is like a breaking layout
1345                  */
1346                 for (int i = 6 + par->enumdepth + 1; i < 10; ++i)
1347                         par->setCounter(i, 0);
1348         }
1349
1350         if (!par->params().labelString().empty()) {
1351                 par->params().labelString(string());
1352         }
1353
1354         if (layout.margintype == MARGIN_MANUAL) {
1355                 if (par->params().labelWidthString().empty()) {
1356                         par->setLabelWidthString(layout.labelstring());
1357                 }
1358         } else {
1359                 par->setLabelWidthString(string());
1360         }
1361
1362         // is it a layout that has an automatic label?
1363         if (layout.labeltype >=  LABEL_COUNTER_CHAPTER) {
1364
1365                 int i = layout.labeltype - LABEL_COUNTER_CHAPTER;
1366                 if (i >= 0 && i<= buf->params.secnumdepth) {
1367                         par->incCounter(i);     // increment the counter
1368
1369                         // Is there a label? Useful for Chapter layout
1370                         if (!par->params().appendix()) {
1371                                 if (!layout.labelstring().empty())
1372                                         par->params().labelString(layout.labelstring());
1373                                 else
1374                                         par->params().labelString(string());
1375                         } else {
1376                                 if (!layout.labelstring_appendix().empty())
1377                                         par->params().labelString(layout.labelstring_appendix());
1378                                 else
1379                                         par->params().labelString(string());
1380                         }
1381
1382                         ostringstream s;
1383
1384                         if (!par->params().appendix()) {
1385                                 switch (2 * LABEL_COUNTER_CHAPTER -
1386                                         textclass.maxcounter() + i) {
1387                                 case LABEL_COUNTER_CHAPTER:
1388                                         s << par->getCounter(i);
1389                                         break;
1390                                 case LABEL_COUNTER_SECTION:
1391                                         s << par->getCounter(i - 1) << '.'
1392                                           << par->getCounter(i);
1393                                         break;
1394                                 case LABEL_COUNTER_SUBSECTION:
1395                                         s << par->getCounter(i - 2) << '.'
1396                                           << par->getCounter(i - 1) << '.'
1397                                           << par->getCounter(i);
1398                                         break;
1399                                 case LABEL_COUNTER_SUBSUBSECTION:
1400                                         s << par->getCounter(i - 3) << '.'
1401                                           << par->getCounter(i - 2) << '.'
1402                                           << par->getCounter(i - 1) << '.'
1403                                           << par->getCounter(i);
1404
1405                                         break;
1406                                 case LABEL_COUNTER_PARAGRAPH:
1407                                         s << par->getCounter(i - 4) << '.'
1408                                           << par->getCounter(i - 3) << '.'
1409                                           << par->getCounter(i - 2) << '.'
1410                                           << par->getCounter(i - 1) << '.'
1411                                           << par->getCounter(i);
1412                                         break;
1413                                 case LABEL_COUNTER_SUBPARAGRAPH:
1414                                         s << par->getCounter(i - 5) << '.'
1415                                           << par->getCounter(i - 4) << '.'
1416                                           << par->getCounter(i - 3) << '.'
1417                                           << par->getCounter(i - 2) << '.'
1418                                           << par->getCounter(i - 1) << '.'
1419                                           << par->getCounter(i);
1420
1421                                         break;
1422                                 default:
1423                                         // Can this ever be reached? And in the
1424                                         // case it is, how can this be correct?
1425                                         // (Lgb)
1426                                         s << par->getCounter(i) << '.';
1427                                         break;
1428                                 }
1429                         } else { // appendix
1430                                 switch (2 * LABEL_COUNTER_CHAPTER - textclass.maxcounter() + i) {
1431                                 case LABEL_COUNTER_CHAPTER:
1432                                         if (par->isRightToLeftPar(buf->params))
1433                                                 s << hebrewCounter(par->getCounter(i));
1434                                         else
1435                                                 s << alphaCounter(par->getCounter(i));
1436                                         break;
1437                                 case LABEL_COUNTER_SECTION:
1438                                         if (par->isRightToLeftPar(buf->params))
1439                                                 s << hebrewCounter(par->getCounter(i - 1));
1440                                         else
1441                                                 s << alphaCounter(par->getCounter(i - 1));
1442
1443                                         s << '.'
1444                                           << par->getCounter(i);
1445
1446                                         break;
1447                                 case LABEL_COUNTER_SUBSECTION:
1448                                         if (par->isRightToLeftPar(buf->params))
1449                                                 s << hebrewCounter(par->getCounter(i - 2));
1450                                         else
1451                                                 s << alphaCounter(par->getCounter(i - 2));
1452
1453                                         s << '.'
1454                                           << par->getCounter(i-1) << '.'
1455                                           << par->getCounter(i);
1456
1457                                         break;
1458                                 case LABEL_COUNTER_SUBSUBSECTION:
1459                                         if (par->isRightToLeftPar(buf->params))
1460                                                 s << hebrewCounter(par->getCounter(i-3));
1461                                         else
1462                                                 s << alphaCounter(par->getCounter(i-3));
1463
1464                                         s << '.'
1465                                           << par->getCounter(i-2) << '.'
1466                                           << par->getCounter(i-1) << '.'
1467                                           << par->getCounter(i);
1468
1469                                         break;
1470                                 case LABEL_COUNTER_PARAGRAPH:
1471                                         if (par->isRightToLeftPar(buf->params))
1472                                                 s << hebrewCounter(par->getCounter(i-4));
1473                                         else
1474                                                 s << alphaCounter(par->getCounter(i-4));
1475
1476                                         s << '.'
1477                                           << par->getCounter(i-3) << '.'
1478                                           << par->getCounter(i-2) << '.'
1479                                           << par->getCounter(i-1) << '.'
1480                                           << par->getCounter(i);
1481
1482                                         break;
1483                                 case LABEL_COUNTER_SUBPARAGRAPH:
1484                                         if (par->isRightToLeftPar(buf->params))
1485                                                 s << hebrewCounter(par->getCounter(i-5));
1486                                         else
1487                                                 s << alphaCounter(par->getCounter(i-5));
1488
1489                                         s << '.'
1490                                           << par->getCounter(i-4) << '.'
1491                                           << par->getCounter(i-3) << '.'
1492                                           << par->getCounter(i-2) << '.'
1493                                           << par->getCounter(i-1) << '.'
1494                                           << par->getCounter(i);
1495
1496                                         break;
1497                                 default:
1498                                         // Can this ever be reached? And in the
1499                                         // case it is, how can this be correct?
1500                                         // (Lgb)
1501                                         s << par->getCounter(i) << '.';
1502
1503                                         break;
1504                                 }
1505                         }
1506
1507                         par->params().labelString(par->params().labelString() +s.str().c_str());
1508                         // We really want to remove the c_str as soon as
1509                         // possible...
1510
1511                         for (i++; i < 10; ++i) {
1512                                 // reset the following counters
1513                                 par->setCounter(i, 0);
1514                         }
1515                 } else if (layout.labeltype < LABEL_COUNTER_ENUMI) {
1516                         for (i++; i < 10; ++i) {
1517                                 // reset the following counters
1518                                 par->setCounter(i, 0);
1519                         }
1520                 } else if (layout.labeltype == LABEL_COUNTER_ENUMI) {
1521                         par->incCounter(i + par->enumdepth);
1522                         int number = par->getCounter(i + par->enumdepth);
1523
1524                         ostringstream s;
1525
1526                         switch (par->enumdepth) {
1527                         case 1:
1528                                 if (par->isRightToLeftPar(buf->params))
1529                                         s << '('
1530                                           << hebrewCounter(number)
1531                                           << ')';
1532                                 else
1533                                         s << '('
1534                                           << loweralphaCounter(number)
1535                                           << ')';
1536                                 break;
1537                         case 2:
1538                                 if (par->isRightToLeftPar(buf->params))
1539                                         s << '.' << romanCounter(number);
1540                                 else
1541                                         s << romanCounter(number) << '.';
1542                                 break;
1543                         case 3:
1544                                 if (par->isRightToLeftPar(buf->params))
1545                                         s << '.'
1546                                           << alphaCounter(number);
1547                                 else
1548                                         s << alphaCounter(number)
1549                                           << '.';
1550                                 break;
1551                         default:
1552                                 if (par->isRightToLeftPar(buf->params))
1553                                         s << '.' << number;
1554                                 else
1555                                         s << number << '.';
1556                                 break;
1557                         }
1558
1559                         par->params().labelString(s.str().c_str());
1560
1561                         for (i += par->enumdepth + 1; i < 10; ++i) {
1562                                 // reset the following counters
1563                                 par->setCounter(i, 0);
1564                         }
1565
1566                 }
1567         } else if (layout.labeltype == LABEL_BIBLIO) {// ale970302
1568                 int i = LABEL_COUNTER_ENUMI - LABEL_COUNTER_CHAPTER + par->enumdepth;
1569                 par->incCounter(i);
1570                 int number = par->getCounter(i);
1571                 if (!par->bibkey) {
1572                         InsetCommandParams p("bibitem" );
1573                         par->bibkey = new InsetBibKey(p);
1574                 }
1575                 par->bibkey->setCounter(number);
1576                 par->params().labelString(layout.labelstring());
1577
1578                 // In biblio should't be following counters but...
1579         } else {
1580                 string s = layout.labelstring();
1581
1582                 // the caption hack:
1583                 if (layout.labeltype == LABEL_SENSITIVE) {
1584                         bool isOK (par->inInset() && par->inInset()->owner() &&
1585                                    (par->inInset()->owner()->lyxCode() == Inset::FLOAT_CODE));
1586
1587                         if (isOK) {
1588                                 InsetFloat * tmp = static_cast<InsetFloat*>(par->inInset()->owner());
1589                                 Floating const & fl
1590                                         = floatList.getType(tmp->type());
1591                                 // We should get the correct number here too.
1592                                 s = fl.name() + " #:";
1593                         } else {
1594                                 /* par->SetLayout(0);
1595                                    s = layout->labelstring;  */
1596                                 s = (par->getParLanguage(buf->params)->lang() == "hebrew")
1597                                         ? " :úåòîùî Ã¸Ã±Ã§" : "Senseless: ";
1598                         }
1599                 }
1600                 par->params().labelString(s);
1601
1602                 /* reset the enumeration counter. They are always resetted
1603                  * when there is any other layout between */
1604                 for (int i = 6 + par->enumdepth; i < 10; ++i)
1605                         par->setCounter(i, 0);
1606         }
1607 }
1608
1609
1610 // Updates all counters BEHIND the row. Changed paragraphs
1611 // with a dynamic left margin will be rebroken.
1612 void LyXText::updateCounters(BufferView * bview, Row * row) const
1613 {
1614         Paragraph * par;
1615
1616         if (!row) {
1617                 row = firstrow;
1618                 par = row->par();
1619         } else {
1620                 par = row->par()->next();
1621         }
1622
1623         while (par) {
1624                 while (row->par() != par)
1625                         row = row->next();
1626
1627                 setCounter(bview->buffer(), par);
1628
1629                 // now check for the headline layouts. remember that they
1630                 // have a dynamic left margin
1631                 LyXTextClass const & tclass =
1632                         textclasslist[bview->buffer()->params.textclass];
1633                 LyXLayout const & layout = tclass[par->layout()];
1634
1635                 if (layout.margintype == MARGIN_DYNAMIC
1636                     || layout.labeltype == LABEL_SENSITIVE) {
1637                         // Rebreak the paragraph
1638                         removeParagraph(row);
1639                         appendParagraph(bview, row);
1640                 }
1641                 par = par->next();
1642         }
1643 }
1644
1645
1646 void LyXText::insertInset(BufferView * bview, Inset * inset)
1647 {
1648         if (!cursor.par()->insetAllowed(inset->lyxCode()))
1649                 return;
1650         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1651         freezeUndo();
1652         cursor.par()->insertInset(cursor.pos(), inset);
1653         // Just to rebreak and refresh correctly.
1654         // The character will not be inserted a second time
1655         insertChar(bview, Paragraph::META_INSET);
1656         // If we enter a highly editable inset the cursor should be to before
1657         // the inset. This couldn't happen before as Undo was not handled inside
1658         // inset now after the Undo LyX tries to call inset->Edit(...) again
1659         // and cannot do this as the cursor is behind the inset and GetInset
1660         // does not return the inset!
1661         if (isHighlyEditableInset(inset)) {
1662                 cursorLeft(bview, true);
1663         }
1664         unFreezeUndo();
1665 }
1666
1667
1668 void LyXText::copyEnvironmentType()
1669 {
1670         copylayouttype = cursor.par()->layout();
1671 }
1672
1673
1674 void LyXText::pasteEnvironmentType(BufferView * bview)
1675 {
1676         setLayout(bview, copylayouttype);
1677 }
1678
1679
1680 void LyXText::cutSelection(BufferView * bview, bool doclear, bool realcut)
1681 {
1682         // Stuff what we got on the clipboard. Even if there is no selection.
1683
1684         // There is a problem with having the stuffing here in that the
1685         // larger the selection the slower LyX will get. This can be
1686         // solved by running the line below only when the selection has
1687         // finished. The solution used currently just works, to make it
1688         // faster we need to be more clever and probably also have more
1689         // calls to stuffClipboard. (Lgb)
1690         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1691
1692         // This doesn't make sense, if there is no selection
1693         if (!selection.set())
1694                 return;
1695
1696         // OK, we have a selection. This is always between selection.start
1697         // and selection.end
1698
1699         // make sure that the depth behind the selection are restored, too
1700         Paragraph * endpar = selection.end.par()->next();
1701         Paragraph * undoendpar = endpar;
1702
1703         if (endpar && endpar->getDepth()) {
1704                 while (endpar && endpar->getDepth()) {
1705                         endpar = endpar->next();
1706                         undoendpar = endpar;
1707                 }
1708         } else if (endpar) {
1709                 endpar = endpar->next(); // because of parindents etc.
1710         }
1711
1712         setUndo(bview, Undo::DELETE,
1713                 selection.start.par(), undoendpar);
1714
1715         // there are two cases: cut only within one paragraph or
1716         // more than one paragraph
1717         if (selection.start.par() == selection.end.par()) {
1718                 // only within one paragraph
1719                 endpar = selection.end.par();
1720                 int pos = selection.end.pos();
1721                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1722                                           selection.start.pos(), pos,
1723                                           bview->buffer()->params.textclass,
1724                                           doclear, realcut);
1725                 selection.end.pos(pos);
1726         } else {
1727                 endpar = selection.end.par();
1728                 int pos = selection.end.pos();
1729                 CutAndPaste::cutSelection(selection.start.par(), &endpar,
1730                                           selection.start.pos(), pos,
1731                                           bview->buffer()->params.textclass,
1732                                           doclear, realcut);
1733                 cursor.par(endpar);
1734                 selection.end.par(endpar);
1735                 selection.end.pos(pos);
1736                 cursor.pos(selection.end.pos());
1737         }
1738         endpar = endpar->next();
1739
1740         // sometimes necessary
1741         if (doclear)
1742                 selection.start.par()->stripLeadingSpaces(bview->buffer()->params.textclass);
1743
1744         redoParagraphs(bview, selection.start, endpar);
1745
1746         // cutSelection can invalidate the cursor so we need to set
1747         // it anew. (Lgb)
1748         cursor = selection.start;
1749
1750         // need a valid cursor. (Lgb)
1751         clearSelection();
1752
1753         setCursor(bview, cursor.par(), cursor.pos());
1754         selection.cursor = cursor;
1755         updateCounters(bview, cursor.row());
1756 }
1757
1758
1759 void LyXText::copySelection(BufferView * bview)
1760 {
1761         // stuff the selection onto the X clipboard, from an explicit copy request
1762         bview->stuffClipboard(selectionAsString(bview->buffer(), true));
1763
1764         // this doesnt make sense, if there is no selection
1765         if (!selection.set())
1766                 return;
1767
1768         // ok we have a selection. This is always between selection.start
1769         // and sel_end cursor
1770
1771         // copy behind a space if there is one
1772         while (selection.start.par()->size() > selection.start.pos()
1773                && selection.start.par()->isLineSeparator(selection.start.pos())
1774                && (selection.start.par() != selection.end.par()
1775                    || selection.start.pos() < selection.end.pos()))
1776                 selection.start.pos(selection.start.pos() + 1);
1777
1778         CutAndPaste::copySelection(selection.start.par(), selection.end.par(),
1779                                    selection.start.pos(), selection.end.pos(),
1780                                    bview->buffer()->params.textclass);
1781 }
1782
1783
1784 void LyXText::pasteSelection(BufferView * bview)
1785 {
1786         // this does not make sense, if there is nothing to paste
1787         if (!CutAndPaste::checkPastePossible(cursor.par()))
1788                 return;
1789
1790         setUndo(bview, Undo::INSERT,
1791                 cursor.par(), cursor.par()->next());
1792
1793         Paragraph * endpar;
1794         Paragraph * actpar = cursor.par();
1795         int pos = cursor.pos();
1796
1797         CutAndPaste::pasteSelection(&actpar, &endpar, pos,
1798                                     bview->buffer()->params.textclass);
1799
1800         redoParagraphs(bview, cursor, endpar);
1801
1802         setCursor(bview, cursor.par(), cursor.pos());
1803         clearSelection();
1804
1805         selection.cursor = cursor;
1806         setCursor(bview, actpar, pos);
1807         setSelection(bview);
1808         updateCounters(bview, cursor.row());
1809 }
1810
1811
1812 // sets the selection over the number of characters of string, no check!!
1813 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1814 {
1815         if (str.empty())
1816                 return;
1817
1818         selection.cursor = cursor;
1819         for (string::size_type i = 0; i < str.length(); ++i)
1820                 cursorRight(bview);
1821         setSelection(bview);
1822 }
1823
1824
1825 // simple replacing. The font of the first selected character is used
1826 void LyXText::replaceSelectionWithString(BufferView * bview,
1827                                          string const & str)
1828 {
1829         setCursorParUndo(bview);
1830         freezeUndo();
1831
1832         if (!selection.set()) { // create a dummy selection
1833                 selection.end = cursor;
1834                 selection.start = cursor;
1835         }
1836
1837         // Get font setting before we cut
1838         pos_type pos = selection.end.pos();
1839         LyXFont const font = selection.start.par()
1840                 ->getFontSettings(bview->buffer()->params,
1841                                   selection.start.pos());
1842
1843         // Insert the new string
1844         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1845                 selection.end.par()->insertChar(pos, (*cit), font);
1846                 ++pos;
1847         }
1848
1849         // Cut the selection
1850         cutSelection(bview, true, false);
1851
1852         unFreezeUndo();
1853 }
1854
1855
1856 // needed to insert the selection
1857 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1858 {
1859         Paragraph * par = cursor.par();
1860         pos_type pos = cursor.pos();
1861         Paragraph * endpar = cursor.par()->next();
1862
1863         setCursorParUndo(bview);
1864
1865         // only to be sure, should not be neccessary
1866         clearSelection();
1867
1868         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1869
1870         redoParagraphs(bview, cursor, endpar);
1871         setCursor(bview, cursor.par(), cursor.pos());
1872         selection.cursor = cursor;
1873         setCursor(bview, par, pos);
1874         setSelection(bview);
1875 }
1876
1877
1878 // turns double-CR to single CR, others where converted into one
1879 // blank. Then InsertStringAsLines is called
1880 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1881 {
1882         string linestr(str);
1883         bool newline_inserted = false;
1884         for (string::size_type i = 0; i < linestr.length(); ++i) {
1885                 if (linestr[i] == '\n') {
1886                         if (newline_inserted) {
1887                                 // we know that \r will be ignored by
1888                                 // InsertStringA. Of course, it is a dirty
1889                                 // trick, but it works...
1890                                 linestr[i - 1] = '\r';
1891                                 linestr[i] = '\n';
1892                         } else {
1893                                 linestr[i] = ' ';
1894                                 newline_inserted = true;
1895                         }
1896                 } else if (IsPrintable(linestr[i])) {
1897                         newline_inserted = false;
1898                 }
1899         }
1900         insertStringAsLines(bview, linestr);
1901 }
1902
1903
1904 bool LyXText::gotoNextInset(BufferView * bview,
1905                             vector<Inset::Code> const & codes,
1906                             string const & contents) const
1907 {
1908         LyXCursor res = cursor;
1909         Inset * inset;
1910         do {
1911                 if (res.pos() < res.par()->size() - 1) {
1912                         res.pos(res.pos() + 1);
1913                 } else  {
1914                         res.par(res.par()->next());
1915                         res.pos(0);
1916                 }
1917
1918         } while (res.par() &&
1919                  !(res.par()->isInset(res.pos())
1920                    && (inset = res.par()->getInset(res.pos())) != 0
1921                    && find(codes.begin(), codes.end(), inset->lyxCode())
1922                    != codes.end()
1923                    && (contents.empty() ||
1924                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1925                        == contents)));
1926
1927         if (res.par()) {
1928                 setCursor(bview, res.par(), res.pos(), false);
1929                 return true;
1930         }
1931         return false;
1932 }
1933
1934
1935 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1936                              pos_type pos)
1937 {
1938         LyXCursor tmpcursor;
1939
1940         int y = 0;
1941         pos_type z;
1942         Row * row = getRow(par, pos, y);
1943
1944         // is there a break one row above
1945         if (row->previous() && row->previous()->par() == row->par()) {
1946                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1947                 if (z >= row->pos()) {
1948                         // set the dimensions of the row above
1949                         y -= row->previous()->height();
1950                         refresh_y = y;
1951                         refresh_row = row->previous();
1952                         status(bview, LyXText::NEED_MORE_REFRESH);
1953
1954                         breakAgain(bview, row->previous());
1955
1956                         // set the cursor again. Otherwise
1957                         // dangling pointers are possible
1958                         setCursor(bview, cursor.par(), cursor.pos(),
1959                                   false, cursor.boundary());
1960                         selection.cursor = cursor;
1961                         return;
1962                 }
1963         }
1964
1965         int const tmpheight = row->height();
1966         pos_type const tmplast = rowLast(row);
1967         refresh_y = y;
1968         refresh_row = row;
1969
1970         breakAgain(bview, row);
1971         if (row->height() == tmpheight && rowLast(row) == tmplast)
1972                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1973         else
1974                 status(bview, LyXText::NEED_MORE_REFRESH);
1975
1976         // check the special right address boxes
1977         if (textclasslist[bview->buffer()->params.textclass][
1978                                 par->layout()].margintype
1979             == MARGIN_RIGHT_ADDRESS_BOX)
1980         {
1981                 tmpcursor.par(par);
1982                 tmpcursor.row(row);
1983                 tmpcursor.y(y);
1984                 tmpcursor.x(0);
1985                 tmpcursor.x_fix(0);
1986                 tmpcursor.pos(pos);
1987                 redoDrawingOfParagraph(bview, tmpcursor);
1988         }
1989
1990         // set the cursor again. Otherwise dangling pointers are possible
1991         // also set the selection
1992
1993         if (selection.set()) {
1994                 tmpcursor = cursor;
1995                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1996                                 false, selection.cursor.boundary());
1997                 selection.cursor = cursor;
1998                 setCursorIntern(bview, selection.start.par(),
1999                                 selection.start.pos(),
2000                                 false, selection.start.boundary());
2001                 selection.start = cursor;
2002                 setCursorIntern(bview, selection.end.par(),
2003                                 selection.end.pos(),
2004                                 false, selection.end.boundary());
2005                 selection.end = cursor;
2006                 setCursorIntern(bview, last_sel_cursor.par(),
2007                                 last_sel_cursor.pos(),
2008                                 false, last_sel_cursor.boundary());
2009                 last_sel_cursor = cursor;
2010                 cursor = tmpcursor;
2011         }
2012         setCursorIntern(bview, cursor.par(), cursor.pos(),
2013                         false, cursor.boundary());
2014 }
2015
2016
2017 // returns false if inset wasn't found
2018 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2019 {
2020         // first check the current paragraph
2021         int pos = cursor.par()->getPositionOfInset(inset);
2022         if (pos != -1) {
2023                 checkParagraph(bview, cursor.par(), pos);
2024                 return true;
2025         }
2026
2027         // check every paragraph
2028
2029         Paragraph * par = ownerParagraph();
2030         do {
2031                 pos = par->getPositionOfInset(inset);
2032                 if (pos != -1) {
2033                         checkParagraph(bview, par, pos);
2034                         return true;
2035                 }
2036                 par = par->next();
2037         } while (par);
2038
2039         return false;
2040 }
2041
2042
2043 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2044                         pos_type pos,
2045                         bool setfont, bool boundary) const
2046 {
2047         LyXCursor old_cursor = cursor;
2048         setCursorIntern(bview, par, pos, setfont, boundary);
2049         return deleteEmptyParagraphMechanism(bview, old_cursor);
2050 }
2051
2052
2053 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2054                         pos_type pos, bool boundary) const
2055 {
2056         lyx::Assert(par);
2057         lyx::Assert(bview);
2058
2059         cur.par(par);
2060         cur.pos(pos);
2061         cur.boundary(boundary);
2062
2063         // get the cursor y position in text
2064         int y = 0;
2065         Row * row = getRow(par, pos, y);
2066         Row * old_row = row;
2067         cur.irow(row);
2068         // if we are before the first char of this row and are still in the
2069         // same paragraph and there is a previous row then put the cursor on
2070         // the end of the previous row
2071         cur.iy(y + row->baseline());
2072         Inset * ins;
2073         if (row->previous() && pos &&
2074                 row->previous()->par() == row->par() &&
2075                 par->getChar(pos) == Paragraph::META_INSET &&
2076                 (ins=par->getInset(pos)) && (ins->needFullRow() || ins->display()))
2077         {
2078                 row = row->previous();
2079                 y -= row->height();
2080         }
2081
2082         cur.row(row);
2083         // y is now the beginning of the cursor row
2084         y += row->baseline();
2085         // y is now the cursor baseline
2086         cur.y(y);
2087
2088         pos_type last = rowLastPrintable(old_row);
2089
2090         if (pos > last + 1) {
2091                 // This shouldn't happen.
2092                 pos = last + 1;
2093                 cur.pos(pos);
2094         } else if (pos < row->pos()) {
2095                 pos = row->pos();
2096                 cur.pos(pos);
2097         }
2098
2099         // now get the cursors x position
2100         float x = getCursorX(bview, row, pos, last, boundary);
2101         cur.x(int(x));
2102         cur.x_fix(cur.x());
2103         if (old_row != row) {
2104                 x = getCursorX(bview, old_row, pos, last, boundary);
2105                 cur.ix(int(x));
2106         } else
2107                 cur.ix(cur.x());
2108 }
2109
2110
2111 float LyXText::getCursorX(BufferView * bview, Row * row,
2112                                                   pos_type pos, pos_type last, bool boundary) const
2113 {
2114         pos_type cursor_vpos = 0;
2115         float x;
2116         float fill_separator;
2117         float fill_hfill;
2118         float fill_label_hfill;
2119         // This call HAS to be here because of the BidiTables!!!
2120         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2121                        fill_label_hfill);
2122
2123         if (last < row->pos())
2124                 cursor_vpos = row->pos();
2125         else if (pos > last && !boundary)
2126                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2127                         ? row->pos() : last + 1;
2128         else if (pos > row->pos() &&
2129                  (pos > last || boundary))
2130                 /// Place cursor after char at (logical) position pos - 1
2131                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2132                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2133         else
2134                 /// Place cursor before char at (logical) position pos
2135                 cursor_vpos = (bidi_level(pos) % 2 == 0)
2136                         ? log2vis(pos) : log2vis(pos) + 1;
2137
2138         pos_type main_body =
2139                 beginningOfMainBody(bview->buffer(), row->par());
2140         if ((main_body > 0) &&
2141             ((main_body-1 > last) ||
2142              !row->par()->isLineSeparator(main_body-1)))
2143                 main_body = 0;
2144
2145         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2146                 pos_type pos = vis2log(vpos);
2147                 if (main_body > 0 && pos == main_body - 1) {
2148                         x += fill_label_hfill +
2149                                 lyxfont::width(textclasslist[
2150                                                        bview->buffer()->params.textclass][
2151                                         row->par()->layout()]
2152                                                .labelsep,
2153                                                getLabelFont(bview->buffer(), row->par()));
2154                         if (row->par()->isLineSeparator(main_body-1))
2155                                 x -= singleWidth(bview, row->par(),main_body-1);
2156                 }
2157                 if (hfillExpansion(bview->buffer(), row, pos)) {
2158                         x += singleWidth(bview, row->par(), pos);
2159                         if (pos >= main_body)
2160                                 x += fill_hfill;
2161                         else
2162                                 x += fill_label_hfill;
2163                 } else if (row->par()->isSeparator(pos)) {
2164                         x += singleWidth(bview, row->par(), pos);
2165                         if (pos >= main_body)
2166                                 x += fill_separator;
2167                 } else
2168                         x += singleWidth(bview, row->par(), pos);
2169         }
2170         return x;
2171 }
2172
2173
2174 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2175                               pos_type pos, bool setfont, bool boundary) const
2176 {
2177         InsetText * it = static_cast<InsetText *>(par->inInset());
2178         if (it) {
2179                 if (it != inset_owner) {
2180                         lyxerr[Debug::INSETS] << "InsetText   is " << it
2181                                               << endl
2182                                               << "inset_owner is "
2183                                               << inset_owner << endl;
2184 #ifdef WITH_WARNINGS
2185 #warning I believe this code is wrong. (Lgb)
2186 #warning Jürgen, have a look at this. (Lgb)
2187 #warning Hmmm, I guess you are right but we
2188 #warning should verify when this is needed
2189 #endif
2190                         // Jürgen, would you like to have a look?
2191                         // I guess we need to move the outer cursor
2192                         // and open and lock the inset (bla bla bla)
2193                         // stuff I don't know... so can you have a look?
2194                         // (Lgb)
2195                         // I moved the lyxerr stuff in here so we can see if
2196                         // this is actually really needed and where!
2197                         // (Jug)
2198                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2199                         return;
2200                 }
2201         }
2202
2203         setCursor(bview, cursor, par, pos, boundary);
2204         if (setfont)
2205                 setCurrentFont(bview);
2206 }
2207
2208
2209 void LyXText::setCurrentFont(BufferView * bview) const
2210 {
2211         pos_type pos = cursor.pos();
2212         if (cursor.boundary() && pos > 0)
2213                 --pos;
2214
2215         if (pos > 0) {
2216                 if (pos == cursor.par()->size())
2217                         --pos;
2218                 else // potentional bug... BUG (Lgb)
2219                         if (cursor.par()->isSeparator(pos)) {
2220                                 if (pos > cursor.row()->pos() &&
2221                                     bidi_level(pos) % 2 ==
2222                                     bidi_level(pos - 1) % 2)
2223                                         --pos;
2224                                 else if (pos + 1 < cursor.par()->size())
2225                                         ++pos;
2226                         }
2227         }
2228
2229         current_font =
2230                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2231         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2232
2233         if (cursor.pos() == cursor.par()->size() &&
2234             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2235             !cursor.boundary()) {
2236                 Language const * lang =
2237                         cursor.par()->getParLanguage(bview->buffer()->params);
2238                 current_font.setLanguage(lang);
2239                 current_font.setNumber(LyXFont::OFF);
2240                 real_current_font.setLanguage(lang);
2241                 real_current_font.setNumber(LyXFont::OFF);
2242         }
2243 }
2244
2245
2246 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2247 {
2248         LyXCursor old_cursor = cursor;
2249
2250         setCursorFromCoordinates(bview, cursor, x, y);
2251         setCurrentFont(bview);
2252         deleteEmptyParagraphMechanism(bview, old_cursor);
2253 }
2254
2255
2256 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2257                                        int x, int y) const
2258 {
2259         // Get the row first.
2260
2261         Row * row = getRowNearY(y);
2262         bool bound = false;
2263         pos_type const column = getColumnNearX(bview, row, x, bound);
2264         cur.par(row->par());
2265         cur.pos(row->pos() + column);
2266         cur.x(x);
2267         cur.y(y + row->baseline());
2268         cur.row(row);
2269         Inset * ins;
2270         if (row->next() && row->next()->pos() == cur.pos() &&
2271                 cur.par() == row->next()->par() &&
2272                 cur.par()->getChar(cur.pos()) == Paragraph::META_INSET &&
2273                 (ins=cur.par()->getInset(cur.pos())) &&
2274                 (ins->needFullRow() || ins->display()))
2275         {
2276                 // we enter here if we put the cursor on the end of the row before
2277                 // a inset which uses a full row and in that case we HAVE to calculate
2278                 // the right (i) values.
2279                 pos_type last = rowLastPrintable(row);
2280                 float x = getCursorX(bview, row->next(), cur.pos(), last, bound);
2281                 cur.ix(int(x));
2282                 cur.iy(y + row->height() + row->next()->baseline());
2283                 cur.irow(row->next());
2284         } else {
2285                 cur.iy(cur.y());
2286                 cur.ix(cur.x());
2287                 cur.irow(row);
2288         }
2289         cur.boundary(bound);
2290 }
2291
2292
2293 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2294 {
2295         if (cursor.pos() > 0) {
2296                 bool boundary = cursor.boundary();
2297                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2298                 if (!internal && !boundary &&
2299                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2300                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2301         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2302                 Paragraph * par = cursor.par()->previous();
2303                 setCursor(bview, par, par->size());
2304         }
2305 }
2306
2307
2308 void LyXText::cursorRight(BufferView * bview, bool internal) const
2309 {
2310         if (!internal && cursor.boundary() &&
2311             !cursor.par()->isNewline(cursor.pos()))
2312                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2313         else if (cursor.pos() < cursor.par()->size()) {
2314                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2315                 if (!internal &&
2316                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2317                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2318         } else if (cursor.par()->next())
2319                 setCursor(bview, cursor.par()->next(), 0);
2320 }
2321
2322
2323 void LyXText::cursorUp(BufferView * bview, bool selecting) const
2324 {
2325 #if 1
2326         int x = cursor.x_fix();
2327         int y = cursor.y() - cursor.row()->baseline() - 1;
2328         setCursorFromCoordinates(bview, x, y);
2329         if (!selecting) {
2330                 int y1 = cursor.iy() - first_y;
2331                 int y2 = y1;
2332                 y -= first_y;
2333                 Inset * inset_hit =
2334                         bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2335                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2336                         inset_hit->edit(bview, x, y - (y2 - y1), 0);
2337                 }
2338         }
2339 #else
2340         setCursorFromCoordinates(bview, cursor.x_fix(),
2341                                  cursor.y() - cursor.row()->baseline() - 1);
2342 #endif
2343 }
2344
2345
2346 void LyXText::cursorDown(BufferView * bview, bool selecting) const
2347 {
2348 #if 1
2349         int x = cursor.x_fix();
2350         int y = cursor.y() - cursor.row()->baseline() +
2351                 cursor.row()->height() + 1;
2352         setCursorFromCoordinates(bview, x, y);
2353         if (!selecting && cursor.row() == cursor.irow()) {
2354                 int y1 = cursor.iy() - first_y;
2355                 int y2 = y1;
2356                 y -= first_y;
2357                 Inset * inset_hit =
2358                         bview->checkInsetHit(const_cast<LyXText *>(this), x, y1);
2359                 if (inset_hit && isHighlyEditableInset(inset_hit)) {
2360                         inset_hit->edit(bview, x, y - (y2 - y1), 0);
2361                 }
2362         }
2363 #else
2364         setCursorFromCoordinates(bview, cursor.x_fix(),
2365                                  cursor.y() - cursor.row()->baseline()
2366                                  + cursor.row()->height() + 1);
2367 #endif
2368 }
2369
2370
2371 void LyXText::cursorUpParagraph(BufferView * bview) const
2372 {
2373         if (cursor.pos() > 0) {
2374                 setCursor(bview, cursor.par(), 0);
2375         }
2376         else if (cursor.par()->previous()) {
2377                 setCursor(bview, cursor.par()->previous(), 0);
2378         }
2379 }
2380
2381
2382 void LyXText::cursorDownParagraph(BufferView * bview) const
2383 {
2384         if (cursor.par()->next()) {
2385                 setCursor(bview, cursor.par()->next(), 0);
2386         } else {
2387                 setCursor(bview, cursor.par(), cursor.par()->size());
2388         }
2389 }
2390
2391 // fix the cursor `cur' after a characters has been deleted at `where'
2392 // position. Called by deleteEmptyParagraphMechanism
2393 void LyXText::fixCursorAfterDelete(BufferView * bview,
2394                                    LyXCursor & cur,
2395                                    LyXCursor const & where) const
2396 {
2397         // if cursor is not in the paragraph where the delete occured,
2398         // do nothing
2399         if (cur.par() != where.par())
2400                 return;
2401
2402         // if cursor position is after the place where the delete occured,
2403         // update it
2404         if (cur.pos() > where.pos())
2405                 cur.pos(cur.pos()-1);
2406
2407         // check also if we don't want to set the cursor on a spot behind the
2408         // pagragraph because we erased the last character.
2409         if (cur.pos() > cur.par()->size())
2410                 cur.pos(cur.par()->size());
2411         
2412         // recompute row et al. for this cursor
2413         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2414 }
2415
2416
2417 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2418                                             LyXCursor const & old_cursor) const
2419 {
2420         // Would be wrong to delete anything if we have a selection.
2421         if (selection.set())
2422                 return false;
2423
2424         // We allow all kinds of "mumbo-jumbo" when freespacing.
2425         if (textclasslist[bview->buffer()->params.textclass][
2426                                 old_cursor.par()->layout()].free_spacing
2427             || old_cursor.par()->isFreeSpacing())
2428         {
2429                 return false;
2430         }
2431
2432         /* Ok I'll put some comments here about what is missing.
2433            I have fixed BackSpace (and thus Delete) to not delete
2434            double-spaces automagically. I have also changed Cut,
2435            Copy and Paste to hopefully do some sensible things.
2436            There are still some small problems that can lead to
2437            double spaces stored in the document file or space at
2438            the beginning of paragraphs. This happens if you have
2439            the cursor betwenn to spaces and then save. Or if you
2440            cut and paste and the selection have a space at the
2441            beginning and then save right after the paste. I am
2442            sure none of these are very hard to fix, but I will
2443            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2444            that I can get some feedback. (Lgb)
2445         */
2446
2447         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2448         // delete the LineSeparator.
2449         // MISSING
2450
2451         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2452         // delete the LineSeparator.
2453         // MISSING
2454
2455         // If the pos around the old_cursor were spaces, delete one of them.
2456         if (old_cursor.par() != cursor.par()
2457             || old_cursor.pos() != cursor.pos()) {
2458                 // Only if the cursor has really moved
2459
2460                 if (old_cursor.pos() > 0
2461                     && old_cursor.pos() < old_cursor.par()->size()
2462                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2463                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2464                         old_cursor.par()->erase(old_cursor.pos() - 1);
2465                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2466
2467 #ifdef WITH_WARNINGS
2468 #warning This will not work anymore when we have multiple views of the same buffer
2469 // In this case, we will have to correct also the cursors held by
2470 // other bufferviews. It will probably be easier to do that in a more
2471 // automated way in LyXCursor code. (JMarc 26/09/2001)
2472 #endif
2473                         // correct all cursors held by the LyXText
2474                         fixCursorAfterDelete(bview, cursor, old_cursor);
2475                         fixCursorAfterDelete(bview, selection.cursor,
2476                                              old_cursor);
2477                         fixCursorAfterDelete(bview, selection.start,
2478                                              old_cursor);
2479                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2480                         fixCursorAfterDelete(bview, last_sel_cursor,
2481                                              old_cursor);
2482                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2483                         fixCursorAfterDelete(bview, toggle_end_cursor,
2484                                              old_cursor);
2485                         return false;
2486                 }
2487         }
2488
2489         // don't delete anything if this is the ONLY paragraph!
2490         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2491                 return false;
2492
2493         // Do not delete empty paragraphs with keepempty set.
2494         if (textclasslist
2495             [bview->buffer()->params.textclass]
2496             [old_cursor.par()->layout()].keepempty)
2497                 return false;
2498
2499         // only do our magic if we changed paragraph
2500         if (old_cursor.par() == cursor.par())
2501                 return false;
2502
2503         // record if we have deleted a paragraph
2504         // we can't possibly have deleted a paragraph before this point
2505         bool deleted = false;
2506
2507         if ((old_cursor.par()->size() == 0
2508              || (old_cursor.par()->size() == 1
2509                  && old_cursor.par()->isLineSeparator(0)))) {
2510                 // ok, we will delete anything
2511                 LyXCursor tmpcursor;
2512
2513                 // make sure that you do not delete any environments
2514                 status(bview, LyXText::NEED_MORE_REFRESH);
2515                 deleted = true;
2516
2517                 if (old_cursor.row()->previous()) {
2518                         refresh_row = old_cursor.row()->previous();
2519                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2520                         tmpcursor = cursor;
2521                         cursor = old_cursor; // that undo can restore the right cursor position
2522                         Paragraph * endpar = old_cursor.par()->next();
2523                         if (endpar && endpar->getDepth()) {
2524                                 while (endpar && endpar->getDepth()) {
2525                                         endpar = endpar->next();
2526                                 }
2527                         }
2528                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2529                         cursor = tmpcursor;
2530
2531                         // delete old row
2532                         removeRow(old_cursor.row());
2533                         if (ownerParagraph() == old_cursor.par()) {
2534                                 ownerParagraph(ownerParagraph()->next());
2535                         }
2536                         // delete old par
2537                         delete old_cursor.par();
2538
2539                         /* Breakagain the next par. Needed because of
2540                          * the parindent that can occur or dissappear.
2541                          * The next row can change its height, if
2542                          * there is another layout before */
2543                         if (refresh_row->next()) {
2544                                 breakAgain(bview, refresh_row->next());
2545                                 updateCounters(bview, refresh_row);
2546                         }
2547                         setHeightOfRow(bview, refresh_row);
2548                 } else {
2549                         refresh_row = old_cursor.row()->next();
2550                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2551
2552                         tmpcursor = cursor;
2553                         cursor = old_cursor; // that undo can restore the right cursor position
2554                         Paragraph * endpar = old_cursor.par()->next();
2555                         if (endpar && endpar->getDepth()) {
2556                                 while (endpar && endpar->getDepth()) {
2557                                         endpar = endpar->next();
2558                                 }
2559                         }
2560                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2561                         cursor = tmpcursor;
2562
2563                         // delete old row
2564                         removeRow(old_cursor.row());
2565                         // delete old par
2566                         if (ownerParagraph() == old_cursor.par()) {
2567                                 ownerParagraph(ownerParagraph()->next());
2568                         }
2569
2570                         delete old_cursor.par();
2571
2572                         /* Breakagain the next par. Needed because of
2573                            the parindent that can occur or dissappear.
2574                            The next row can change its height, if
2575                            there is another layout before */
2576                         if (refresh_row) {
2577                                 breakAgain(bview, refresh_row);
2578                                 updateCounters(bview, refresh_row->previous());
2579                         }
2580                 }
2581
2582                 // correct cursor y
2583                 setCursorIntern(bview, cursor.par(), cursor.pos());
2584
2585                 if (selection.cursor.par()  == old_cursor.par()
2586                     && selection.cursor.pos() == old_cursor.pos()) {
2587                         // correct selection
2588                         selection.cursor = cursor;
2589                 }
2590         }
2591         if (!deleted) {
2592                 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2593                         redoParagraphs(bview, old_cursor,
2594                                        old_cursor.par()->next());
2595                         // correct cursor y
2596                         setCursorIntern(bview, cursor.par(), cursor.pos());
2597                         selection.cursor = cursor;
2598                 }
2599         }
2600         return deleted;
2601 }
2602
2603
2604 void LyXText::toggleAppendix(BufferView * bview)
2605 {
2606         Paragraph * par = cursor.par();
2607         bool start = !par->params().startOfAppendix();
2608
2609         // ensure that we have only one start_of_appendix in this document
2610         Paragraph * tmp = ownerParagraph();
2611         for (; tmp; tmp = tmp->next()) {
2612                 tmp->params().startOfAppendix(false);
2613         }
2614
2615         par->params().startOfAppendix(start);
2616
2617         // we can set the refreshing parameters now
2618         status(bview, LyXText::NEED_MORE_REFRESH);
2619         refresh_y = 0;
2620         refresh_row = 0; // not needed for full update
2621         updateCounters(bview, 0);
2622         setCursor(bview, cursor.par(), cursor.pos());
2623 }
2624
2625
2626 Paragraph * LyXText::ownerParagraph() const
2627 {
2628         if (inset_owner) {
2629                 return inset_owner->paragraph();
2630         }
2631         return bv_owner->buffer()->paragraph;
2632 }
2633
2634
2635 void LyXText::ownerParagraph(Paragraph * p) const
2636 {
2637         if (inset_owner) {
2638                 inset_owner->paragraph(p);
2639         } else {
2640                 bv_owner->buffer()->paragraph = p;
2641         }
2642 }
2643
2644
2645 void LyXText::ownerParagraph(int id, Paragraph * p) const
2646 {
2647         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2648         if (op && op->inInset()) {
2649                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2650         } else {
2651                 ownerParagraph(p);
2652         }
2653 }
2654
2655
2656 LyXText::text_status LyXText::status() const
2657 {
2658         return status_;
2659 }
2660
2661
2662 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2663 {
2664         // We should only go up with refreshing code so this means that if
2665         // we have a MORE refresh we should never set it to LITTLE if we still
2666         // didn't handle it (and then it will be UNCHANGED. Now as long as
2667         // we stay inside one LyXText this may work but we need to tell the
2668         // outermost LyXText that it should REALLY draw us if there is some
2669         // change in a Inset::LyXText. So you see that when we are inside a
2670         // inset's LyXText we give the LITTLE to the outermost LyXText to
2671         // tell'em that it should redraw the actual row (where the inset
2672         // resides! Capito?!
2673
2674         if ((status_ != NEED_MORE_REFRESH)
2675             || (status_ == NEED_MORE_REFRESH
2676                 && st != NEED_VERY_LITTLE_REFRESH))
2677         {
2678                 status_ = st;
2679                 if (inset_owner && st != UNCHANGED) {
2680                         bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2681                         if (!bview->text->refresh_row) {
2682                                 bview->text->refresh_row = bview->text->cursor.row();
2683                                 bview->text->refresh_y = bview->text->cursor.y() -
2684                                         bview->text->cursor.row()->baseline();
2685                         }
2686                 }
2687         }
2688 }