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