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