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