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