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