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