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