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