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