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