]> git.lyx.org Git - lyx.git/blob - src/text2.C
861f4c363ae8f315d40636532c5e4324fe2639f1
[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         selection.cursor = cursor;
1804         setCursor(bview, actpar, pos);
1805         setSelection(bview);
1806         updateCounters(bview, cursor.row());
1807 }
1808
1809
1810 // sets the selection over the number of characters of string, no check!!
1811 void LyXText::setSelectionOverString(BufferView * bview, string const & str)
1812 {
1813         if (str.empty())
1814                 return;
1815         
1816         selection.cursor = cursor;
1817         for (string::size_type i = 0; i < str.length(); ++i)
1818                 cursorRight(bview);
1819         setSelection(bview);
1820 }
1821
1822
1823 // simple replacing. The font of the first selected character is used
1824 void LyXText::replaceSelectionWithString(BufferView * bview,
1825                                          string const & str)
1826 {
1827         setCursorParUndo(bview);
1828         freezeUndo();
1829
1830         if (!selection.set()) { // create a dummy selection
1831                 selection.end = cursor;
1832                 selection.start = cursor;
1833         }
1834
1835         // Get font setting before we cut
1836         pos_type pos = selection.end.pos();
1837         LyXFont const font = selection.start.par()
1838                 ->getFontSettings(bview->buffer()->params,
1839                                   selection.start.pos());
1840
1841         // Insert the new string
1842         for (string::const_iterator cit = str.begin(); cit != str.end(); ++cit) {
1843                 selection.end.par()->insertChar(pos, (*cit), font);
1844                 ++pos;
1845         }
1846         
1847         // Cut the selection
1848         cutSelection(bview, true, false);
1849
1850         unFreezeUndo();
1851 }
1852
1853
1854 // needed to insert the selection
1855 void LyXText::insertStringAsLines(BufferView * bview, string const & str)
1856 {
1857         Paragraph * par = cursor.par();
1858         pos_type pos = cursor.pos();
1859         Paragraph * endpar = cursor.par()->next();
1860         
1861         setCursorParUndo(bview);
1862         
1863         // only to be sure, should not be neccessary
1864         clearSelection();
1865         
1866         bview->buffer()->insertStringAsLines(par, pos, current_font, str);
1867
1868         redoParagraphs(bview, cursor, endpar);
1869         setCursor(bview, cursor.par(), cursor.pos());
1870         selection.cursor = cursor;
1871         setCursor(bview, par, pos);
1872         setSelection(bview);
1873 }
1874
1875
1876 // turns double-CR to single CR, others where converted into one
1877 // blank. Then InsertStringAsLines is called
1878 void LyXText::insertStringAsParagraphs(BufferView * bview, string const & str)
1879 {
1880         string linestr(str);
1881         bool newline_inserted = false;
1882         for (string::size_type i = 0; i < linestr.length(); ++i) {
1883                 if (linestr[i] == '\n') {
1884                         if (newline_inserted) {
1885                                 // we know that \r will be ignored by
1886                                 // InsertStringA. Of course, it is a dirty
1887                                 // trick, but it works...
1888                                 linestr[i - 1] = '\r';
1889                                 linestr[i] = '\n';
1890                         } else {
1891                                 linestr[i] = ' ';
1892                                 newline_inserted = true;
1893                         } 
1894                 } else if (IsPrintable(linestr[i])) {
1895                         newline_inserted = false;
1896                 }
1897         }
1898         insertStringAsLines(bview, linestr);
1899 }
1900
1901
1902 bool LyXText::gotoNextInset(BufferView * bview,
1903                             vector<Inset::Code> const & codes,
1904                             string const & contents) const
1905 {
1906         LyXCursor res = cursor;
1907         Inset * inset;
1908         do {
1909                 if (res.pos() < res.par()->size() - 1) {
1910                         res.pos(res.pos() + 1);
1911                 } else  {
1912                         res.par(res.par()->next());
1913                         res.pos(0);
1914                 }
1915       
1916         } while (res.par() && 
1917                  !(res.par()->isInset(res.pos())
1918                    && (inset = res.par()->getInset(res.pos())) != 0
1919                    && find(codes.begin(), codes.end(), inset->lyxCode())
1920                    != codes.end()
1921                    && (contents.empty() ||
1922                        static_cast<InsetCommand *>(res.par()->getInset(res.pos()))->getContents()
1923                        == contents)));
1924
1925         if (res.par()) {
1926                 setCursor(bview, res.par(), res.pos(), false);
1927                 return true;
1928         }
1929         return false;
1930 }
1931
1932
1933 void LyXText::checkParagraph(BufferView * bview, Paragraph * par,
1934                              pos_type pos)
1935 {
1936         LyXCursor tmpcursor;                    
1937
1938         int y = 0;
1939         pos_type z;
1940         Row * row = getRow(par, pos, y);
1941         
1942         // is there a break one row above
1943         if (row->previous() && row->previous()->par() == row->par()) {
1944                 z = nextBreakPoint(bview, row->previous(), workWidth(bview));
1945                 if (z >= row->pos()) {
1946                         // set the dimensions of the row above
1947                         y -= row->previous()->height();
1948                         refresh_y = y;
1949                         refresh_row = row->previous();
1950                         status(bview, LyXText::NEED_MORE_REFRESH);
1951                         
1952                         breakAgain(bview, row->previous());
1953                         
1954                         // set the cursor again. Otherwise
1955                         // dangling pointers are possible
1956                         setCursor(bview, cursor.par(), cursor.pos(),
1957                                   false, cursor.boundary());
1958                         selection.cursor = cursor;
1959                         return;
1960                 }
1961         }
1962
1963         int const tmpheight = row->height();
1964         pos_type const tmplast = rowLast(row);
1965         refresh_y = y;
1966         refresh_row = row;
1967         
1968         breakAgain(bview, row);
1969         if (row->height() == tmpheight && rowLast(row) == tmplast)
1970                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1971         else
1972                 status(bview, LyXText::NEED_MORE_REFRESH); 
1973         
1974         // check the special right address boxes
1975         if (textclasslist[bview->buffer()->params.textclass][
1976                                 par->layout()].margintype
1977             == MARGIN_RIGHT_ADDRESS_BOX)
1978         {
1979                 tmpcursor.par(par);
1980                 tmpcursor.row(row);
1981                 tmpcursor.y(y);
1982                 tmpcursor.x(0);
1983                 tmpcursor.x_fix(0);
1984                 tmpcursor.pos(pos);
1985                 redoDrawingOfParagraph(bview, tmpcursor); 
1986         }
1987
1988         // set the cursor again. Otherwise dangling pointers are possible
1989         // also set the selection
1990    
1991         if (selection.set()) {
1992                 tmpcursor = cursor;
1993                 setCursorIntern(bview, selection.cursor.par(), selection.cursor.pos(),
1994                                 false, selection.cursor.boundary());
1995                 selection.cursor = cursor; 
1996                 setCursorIntern(bview, selection.start.par(),
1997                                 selection.start.pos(),
1998                                 false, selection.start.boundary());
1999                 selection.start = cursor; 
2000                 setCursorIntern(bview, selection.end.par(),
2001                                 selection.end.pos(),
2002                                 false, selection.end.boundary());
2003                 selection.end = cursor; 
2004                 setCursorIntern(bview, last_sel_cursor.par(),
2005                                 last_sel_cursor.pos(),
2006                                 false, last_sel_cursor.boundary());
2007                 last_sel_cursor = cursor; 
2008                 cursor = tmpcursor;
2009         }
2010         setCursorIntern(bview, cursor.par(), cursor.pos(),
2011                         false, cursor.boundary());
2012 }
2013
2014
2015 // returns false if inset wasn't found
2016 bool LyXText::updateInset(BufferView * bview, Inset * inset)
2017 {
2018         // first check the current paragraph
2019         int pos = cursor.par()->getPositionOfInset(inset);
2020         if (pos != -1) {
2021                 checkParagraph(bview, cursor.par(), pos);
2022                 return true;
2023         }
2024   
2025         // check every paragraph
2026   
2027         Paragraph * par = ownerParagraph();
2028         do {
2029                 pos = par->getPositionOfInset(inset);
2030                 if (pos != -1) {
2031                         checkParagraph(bview, par, pos);
2032                         return true;
2033                 }
2034                 par = par->next();
2035         } while (par);
2036   
2037         return false;
2038 }
2039
2040
2041 bool LyXText::setCursor(BufferView * bview, Paragraph * par,
2042                         pos_type pos, 
2043                         bool setfont, bool boundary) const
2044 {
2045         LyXCursor old_cursor = cursor;
2046         setCursorIntern(bview, par, pos, setfont, boundary);
2047         return deleteEmptyParagraphMechanism(bview, old_cursor);
2048 }
2049
2050
2051 void LyXText::setCursor(BufferView * bview, LyXCursor & cur, Paragraph * par,
2052                         pos_type pos, bool boundary) const
2053 {
2054         lyx::Assert(par);
2055         lyx::Assert(bview);
2056
2057         cur.par(par);
2058         cur.pos(pos);
2059         cur.boundary(boundary);
2060
2061 #if 0
2062         if (pos && par->getChar(pos) == Paragraph::META_INSET &&
2063                 par->getInset(pos)) {
2064                 Inset * ins = par->getInset(pos);
2065                 if (ins->needFullRow() || ins->display()) {
2066                         --pos;
2067                         boundary = true;
2068                 }
2069         }
2070 #endif
2071
2072         // get the cursor y position in text
2073         int y = 0;
2074         Row * row = getRow(par, pos, y);
2075         // y is now the beginning of the cursor row
2076         y += row->baseline();
2077         // y is now the cursor baseline 
2078         cur.y(y);
2079
2080         // now get the cursors x position
2081         float x;
2082         float fill_separator;
2083         float fill_hfill;
2084         float fill_label_hfill;
2085         prepareToPrint(bview, row, x, fill_separator, fill_hfill,
2086                        fill_label_hfill);
2087         pos_type cursor_vpos = 0;
2088         pos_type last = rowLastPrintable(row);
2089
2090         if (pos > last + 1) {
2091                 // This shouldn't happen.
2092                 pos = last + 1;
2093                 cur.pos(pos);
2094         } else if (pos < row->pos()) {
2095                 pos = row->pos();
2096                 cur.pos(pos);
2097         }
2098         
2099         if (last < row->pos())
2100                 cursor_vpos = row->pos();
2101         else if (pos > last && !boundary)
2102                 cursor_vpos = (row->par()->isRightToLeftPar(bview->buffer()->params))
2103                         ? row->pos() : last + 1; 
2104         else if (pos > row->pos() &&
2105                  (pos > last || boundary))
2106                 /// Place cursor after char at (logical) position pos - 1
2107                 cursor_vpos = (bidi_level(pos - 1) % 2 == 0)
2108                         ? log2vis(pos - 1) + 1 : log2vis(pos - 1);
2109         else
2110                 /// Place cursor before char at (logical) position pos
2111                 cursor_vpos = (bidi_level(pos) % 2 == 0)
2112                         ? log2vis(pos) : log2vis(pos) + 1;
2113         
2114         pos_type main_body =
2115                 beginningOfMainBody(bview->buffer(), row->par());
2116         if ((main_body > 0) &&
2117             ((main_body-1 > last) || 
2118              !row->par()->isLineSeparator(main_body-1)))
2119                 main_body = 0;
2120         
2121         for (pos_type vpos = row->pos(); vpos < cursor_vpos; ++vpos) {
2122                 pos = vis2log(vpos);
2123                 if (main_body > 0 && pos == main_body - 1) {
2124                         x += fill_label_hfill +
2125                                 lyxfont::width(textclasslist[
2126                                                        bview->buffer()->params.textclass][
2127                                         row->par()->layout()]
2128                                                .labelsep,
2129                                                getLabelFont(bview->buffer(), row->par()));
2130                         if (row->par()->isLineSeparator(main_body-1))
2131                                 x -= singleWidth(bview, row->par(),main_body-1);
2132                 }
2133                 if (hfillExpansion(bview->buffer(), row, pos)) {
2134                         x += singleWidth(bview, row->par(), pos);
2135                         if (pos >= main_body)
2136                                 x += fill_hfill;
2137                         else 
2138                                 x += fill_label_hfill;
2139                 } else if (row->par()->isSeparator(pos)) {
2140                         x += singleWidth(bview, row->par(), pos);
2141                         if (pos >= main_body)
2142                                 x += fill_separator;
2143                 } else
2144                         x += singleWidth(bview, row->par(), pos);
2145         }
2146         
2147         cur.x(int(x));
2148         cur.x_fix(cur.x());
2149         cur.row(row);
2150 }
2151
2152
2153 void LyXText::setCursorIntern(BufferView * bview, Paragraph * par,
2154                               pos_type pos, bool setfont, bool boundary) const
2155 {
2156         InsetText * it = static_cast<InsetText *>(par->inInset());
2157         if (it) {
2158                 if (it != inset_owner) {
2159                         lyxerr[Debug::INSETS] << "InsetText   is " << it
2160                                               << endl
2161                                               << "inset_owner is "
2162                                               << inset_owner << endl;
2163 #ifdef WITH_WARNINGS
2164 #warning I believe this code is wrong. (Lgb)
2165 #warning Jürgen, have a look at this. (Lgb)
2166 #warning Hmmm, I guess you are right but we
2167 #warning should verify when this is needed
2168 #endif
2169                         // Jürgen, would you like to have a look?
2170                         // I guess we need to move the outer cursor
2171                         // and open and lock the inset (bla bla bla)
2172                         // stuff I don't know... so can you have a look?
2173                         // (Lgb)
2174                         // I moved the lyxerr stuff in here so we can see if
2175                         // this is actually really needed and where!
2176                         // (Jug)
2177                         // it->getLyXText(bview)->setCursorIntern(bview, par, pos, setfont, boundary);
2178                         return;
2179                 }
2180         }
2181         
2182         setCursor(bview, cursor, par, pos, boundary);
2183         if (setfont)
2184                 setCurrentFont(bview);
2185 }
2186
2187
2188 void LyXText::setCurrentFont(BufferView * bview) const
2189 {
2190         pos_type pos = cursor.pos();
2191         if (cursor.boundary() && pos > 0)
2192                 --pos;
2193
2194         if (pos > 0) {
2195                 if (pos == cursor.par()->size())
2196                         --pos;
2197                 else // potentional bug... BUG (Lgb)
2198                         if (cursor.par()->isSeparator(pos)) {
2199                                 if (pos > cursor.row()->pos() &&
2200                                     bidi_level(pos) % 2 == 
2201                                     bidi_level(pos - 1) % 2)
2202                                         --pos;
2203                                 else if (pos + 1 < cursor.par()->size())
2204                                         ++pos;
2205                         }
2206         }
2207
2208         current_font =
2209                 cursor.par()->getFontSettings(bview->buffer()->params, pos);
2210         real_current_font = getFont(bview->buffer(), cursor.par(), pos);
2211
2212         if (cursor.pos() == cursor.par()->size() &&
2213             isBoundary(bview->buffer(), cursor.par(), cursor.pos()) &&
2214             !cursor.boundary()) {
2215                 Language const * lang =
2216                         cursor.par()->getParLanguage(bview->buffer()->params);
2217                 current_font.setLanguage(lang);
2218                 current_font.setNumber(LyXFont::OFF);
2219                 real_current_font.setLanguage(lang);
2220                 real_current_font.setNumber(LyXFont::OFF);
2221         }
2222 }
2223
2224
2225 void LyXText::setCursorFromCoordinates(BufferView * bview, int x, int y) const
2226 {
2227         LyXCursor old_cursor = cursor;
2228
2229         setCursorFromCoordinates(bview, cursor, x, y);
2230         setCurrentFont(bview);
2231         deleteEmptyParagraphMechanism(bview, old_cursor);
2232 }
2233
2234
2235 void LyXText::setCursorFromCoordinates(BufferView * bview, LyXCursor & cur,
2236                                        int x, int y) const
2237 {
2238         // Get the row first.
2239    
2240         Row * row = getRowNearY(y);
2241         bool bound = false;
2242         pos_type const column = getColumnNearX(bview, row, x, bound);
2243         cur.par(row->par());
2244         cur.pos(row->pos() + column);
2245         cur.x(x);
2246         cur.y(y + row->baseline());
2247         cur.row(row);
2248         cur.boundary(bound);
2249 }
2250
2251
2252 void LyXText::cursorLeft(BufferView * bview, bool internal) const
2253 {
2254         if (cursor.pos() > 0) {
2255                 bool boundary = cursor.boundary();
2256                 setCursor(bview, cursor.par(), cursor.pos() - 1, true, false);
2257                 if (!internal && !boundary &&
2258                     isBoundary(bview->buffer(), cursor.par(), cursor.pos() + 1))
2259                         setCursor(bview, cursor.par(), cursor.pos() + 1, true, true);
2260         } else if (cursor.par()->previous()) { // steps into the above paragraph.
2261                 Paragraph * par = cursor.par()->previous();
2262                 setCursor(bview, par, par->size());
2263         }
2264 }
2265
2266
2267 void LyXText::cursorRight(BufferView * bview, bool internal) const
2268 {
2269         if (!internal && cursor.boundary() &&
2270             !cursor.par()->isNewline(cursor.pos()))
2271                 setCursor(bview, cursor.par(), cursor.pos(), true, false);
2272         else if (cursor.pos() < cursor.par()->size()) {
2273                 setCursor(bview, cursor.par(), cursor.pos() + 1, true, false);
2274                 if (!internal &&
2275                     isBoundary(bview->buffer(), cursor.par(), cursor.pos()))
2276                         setCursor(bview, cursor.par(), cursor.pos(), true, true);
2277         } else if (cursor.par()->next())
2278                 setCursor(bview, cursor.par()->next(), 0);
2279 }
2280
2281
2282 void LyXText::cursorUp(BufferView * bview) const
2283 {
2284         setCursorFromCoordinates(bview, cursor.x_fix(), 
2285                                  cursor.y() - cursor.row()->baseline() - 1);
2286 }
2287
2288
2289 void LyXText::cursorDown(BufferView * bview) const
2290 {
2291         setCursorFromCoordinates(bview, cursor.x_fix(), 
2292                                  cursor.y() - cursor.row()->baseline()
2293                                  + cursor.row()->height() + 1);
2294 }
2295
2296
2297 void LyXText::cursorUpParagraph(BufferView * bview) const
2298 {
2299         if (cursor.pos() > 0) {
2300                 setCursor(bview, cursor.par(), 0);
2301         }
2302         else if (cursor.par()->previous()) {
2303                 setCursor(bview, cursor.par()->previous(), 0);
2304         }
2305 }
2306
2307
2308 void LyXText::cursorDownParagraph(BufferView * bview) const
2309 {
2310         if (cursor.par()->next()) {
2311                 setCursor(bview, cursor.par()->next(), 0);
2312         } else {
2313                 setCursor(bview, cursor.par(), cursor.par()->size());
2314         }
2315 }
2316
2317 // fix the cursor `cur' after a characters has been deleted at `where'
2318 // position. Called by deleteEmptyParagraphMechanism
2319 void LyXText::fixCursorAfterDelete(BufferView * bview,
2320                                    LyXCursor & cur,
2321                                    LyXCursor const & where) const
2322 {
2323         // if cursor is not in the paragraph where the delete occured,
2324         // do nothing
2325         if (cur.par() != where.par())
2326                 return;
2327
2328         // if cursor position is after the place where the delete occured,
2329         // update it
2330         if (cur.pos() > where.pos())
2331                 cur.pos(cur.pos()-1);
2332
2333         // recompute row et al. for this cursor
2334         setCursor(bview, cur, cur.par(), cur.pos(), cur.boundary());
2335 }
2336
2337
2338 bool LyXText::deleteEmptyParagraphMechanism(BufferView * bview,
2339                                             LyXCursor const & old_cursor) const
2340 {
2341         // Would be wrong to delete anything if we have a selection.
2342         if (selection.set())
2343                 return false;
2344
2345         // We allow all kinds of "mumbo-jumbo" when freespacing.
2346         if (textclasslist[bview->buffer()->params.textclass][
2347                                 old_cursor.par()->layout()].free_spacing
2348             || old_cursor.par()->isFreeSpacing())
2349         {
2350                 return false;
2351         }
2352         
2353         /* Ok I'll put some comments here about what is missing.
2354            I have fixed BackSpace (and thus Delete) to not delete
2355            double-spaces automagically. I have also changed Cut,
2356            Copy and Paste to hopefully do some sensible things.
2357            There are still some small problems that can lead to
2358            double spaces stored in the document file or space at
2359            the beginning of paragraphs. This happens if you have
2360            the cursor betwenn to spaces and then save. Or if you
2361            cut and paste and the selection have a space at the
2362            beginning and then save right after the paste. I am
2363            sure none of these are very hard to fix, but I will
2364            put out 1.1.4pre2 with FIX_DOUBLE_SPACE defined so
2365            that I can get some feedback. (Lgb)
2366         */
2367
2368         // If old_cursor.pos() == 0 and old_cursor.pos()(1) == LineSeparator
2369         // delete the LineSeparator.
2370         // MISSING
2371
2372         // If old_cursor.pos() == 1 and old_cursor.pos()(0) == LineSeparator
2373         // delete the LineSeparator.
2374         // MISSING
2375
2376         // If the pos around the old_cursor were spaces, delete one of them.
2377         if (old_cursor.par() != cursor.par()
2378             || old_cursor.pos() != cursor.pos()) { 
2379                 // Only if the cursor has really moved
2380                 
2381                 if (old_cursor.pos() > 0
2382                     && old_cursor.pos() < old_cursor.par()->size()
2383                     && old_cursor.par()->isLineSeparator(old_cursor.pos())
2384                     && old_cursor.par()->isLineSeparator(old_cursor.pos() - 1)) {
2385                         old_cursor.par()->erase(old_cursor.pos() - 1);
2386                         redoParagraphs(bview, old_cursor, old_cursor.par()->next());
2387
2388 #ifdef WITH_WARNINGS
2389 #warning This will not work anymore when we have multiple views of the same buffer
2390 // In this case, we will have to correct also the cursors held by
2391 // other bufferviews. It will probably be easier to do that in a more
2392 // automated way in LyXCursor code. (JMarc 26/09/2001)
2393 #endif
2394                         // correct all cursors held by the LyXText
2395                         fixCursorAfterDelete(bview, cursor, old_cursor);
2396                         fixCursorAfterDelete(bview, selection.cursor,
2397                                              old_cursor);
2398                         fixCursorAfterDelete(bview, selection.start,
2399                                              old_cursor);
2400                         fixCursorAfterDelete(bview, selection.end, old_cursor);
2401                         fixCursorAfterDelete(bview, last_sel_cursor,
2402                                              old_cursor);
2403                         fixCursorAfterDelete(bview, toggle_cursor, old_cursor);
2404                         fixCursorAfterDelete(bview, toggle_end_cursor,
2405                                              old_cursor);
2406                         return false;
2407                 }
2408         }
2409
2410         // don't delete anything if this is the ONLY paragraph!
2411         if (!old_cursor.par()->next() && !old_cursor.par()->previous())
2412                 return false;
2413         
2414         // Do not delete empty paragraphs with keepempty set.
2415         if (textclasslist
2416             [bview->buffer()->params.textclass]
2417             [old_cursor.par()->layout()].keepempty)
2418                 return false;
2419
2420         // only do our magic if we changed paragraph
2421         if (old_cursor.par() == cursor.par()) 
2422                 return false;
2423         
2424         // record if we have deleted a paragraph
2425         // we can't possibly have deleted a paragraph before this point
2426         bool deleted = false;
2427
2428         if ((old_cursor.par()->size() == 0
2429              || (old_cursor.par()->size() == 1
2430                  && old_cursor.par()->isLineSeparator(0)))) {
2431                 // ok, we will delete anything
2432                 LyXCursor tmpcursor;
2433                 
2434                 // make sure that you do not delete any environments
2435                 status(bview, LyXText::NEED_MORE_REFRESH);
2436                 deleted = true;
2437                                 
2438                 if (old_cursor.row()->previous()) {
2439                         refresh_row = old_cursor.row()->previous();
2440                         refresh_y = old_cursor.y() - old_cursor.row()->baseline() - refresh_row->height();
2441                         tmpcursor = cursor;
2442                         cursor = old_cursor; // that undo can restore the right cursor position
2443                         Paragraph * endpar = old_cursor.par()->next();
2444                         if (endpar && endpar->getDepth()) {
2445                                 while (endpar && endpar->getDepth()) {
2446                                         endpar = endpar->next();
2447                                 }
2448                         }
2449                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2450                         cursor = tmpcursor;
2451
2452                         // delete old row
2453                         removeRow(old_cursor.row());
2454                         if (ownerParagraph() == old_cursor.par()) {
2455                                 ownerParagraph(ownerParagraph()->next());
2456                         }
2457                         // delete old par
2458                         delete old_cursor.par();
2459                                         
2460                         /* Breakagain the next par. Needed because of
2461                          * the parindent that can occur or dissappear.
2462                          * The next row can change its height, if
2463                          * there is another layout before */
2464                         if (refresh_row->next()) {
2465                                 breakAgain(bview, refresh_row->next());
2466                                 updateCounters(bview, refresh_row);
2467                         }
2468                         setHeightOfRow(bview, refresh_row);
2469                 } else {
2470                         refresh_row = old_cursor.row()->next();
2471                         refresh_y = old_cursor.y() - old_cursor.row()->baseline();
2472                                         
2473                         tmpcursor = cursor;
2474                         cursor = old_cursor; // that undo can restore the right cursor position
2475                         Paragraph * endpar = old_cursor.par()->next();
2476                         if (endpar && endpar->getDepth()) {
2477                                 while (endpar && endpar->getDepth()) {
2478                                         endpar = endpar->next();
2479                                 }
2480                         }
2481                         setUndo(bview, Undo::DELETE, old_cursor.par(), endpar);
2482                         cursor = tmpcursor;
2483
2484                         // delete old row
2485                         removeRow(old_cursor.row());
2486                         // delete old par
2487                         if (ownerParagraph() == old_cursor.par()) {
2488                                 ownerParagraph(ownerParagraph()->next());
2489                         }
2490
2491                         delete old_cursor.par();
2492                                         
2493                         /* Breakagain the next par. Needed because of
2494                            the parindent that can occur or dissappear.
2495                            The next row can change its height, if
2496                            there is another layout before */
2497                         if (refresh_row) {
2498                                 breakAgain(bview, refresh_row);
2499                                 updateCounters(bview, refresh_row->previous());
2500                         }
2501                 }
2502                                 
2503                 // correct cursor y
2504                 setCursorIntern(bview, cursor.par(), cursor.pos());
2505
2506                 if (selection.cursor.par()  == old_cursor.par()
2507                     && selection.cursor.pos() == old_cursor.pos()) {
2508                         // correct selection
2509                         selection.cursor = cursor;
2510                 }
2511         }
2512         if (!deleted) {
2513                 if (old_cursor.par()->stripLeadingSpaces(bview->buffer()->params.textclass)) {
2514                         redoParagraphs(bview, old_cursor,
2515                                        old_cursor.par()->next());
2516                         // correct cursor y
2517                         setCursorIntern(bview, cursor.par(), cursor.pos());
2518                         selection.cursor = cursor;
2519                 }
2520         }
2521         return deleted;
2522 }
2523
2524
2525 void LyXText::toggleAppendix(BufferView * bview)
2526 {
2527         Paragraph * par = cursor.par();
2528         bool start = !par->params().startOfAppendix();
2529
2530         // ensure that we have only one start_of_appendix in this document
2531         Paragraph * tmp = ownerParagraph();
2532         for (; tmp; tmp = tmp->next()) {
2533                 tmp->params().startOfAppendix(false);
2534         }
2535         
2536         par->params().startOfAppendix(start);
2537
2538         // we can set the refreshing parameters now
2539         status(bview, LyXText::NEED_MORE_REFRESH);
2540         refresh_y = 0;
2541         refresh_row = 0; // not needed for full update
2542         updateCounters(bview, 0);
2543         setCursor(bview, cursor.par(), cursor.pos());
2544 }
2545
2546
2547 Paragraph * LyXText::ownerParagraph() const
2548 {
2549         if (inset_owner) {
2550                 return inset_owner->paragraph();
2551         }
2552         return bv_owner->buffer()->paragraph;
2553 }
2554
2555
2556 void LyXText::ownerParagraph(Paragraph * p) const
2557 {
2558         if (inset_owner) {
2559                 inset_owner->paragraph(p);
2560         } else {
2561                 bv_owner->buffer()->paragraph = p;
2562         }
2563 }
2564
2565
2566 void LyXText::ownerParagraph(int id, Paragraph * p) const
2567 {
2568         Paragraph * op = bv_owner->buffer()->getParFromID(id);
2569         if (op && op->inInset()) {
2570                 static_cast<InsetText *>(op->inInset())->paragraph(p);
2571         } else {
2572                 ownerParagraph(p);
2573         }
2574 }
2575
2576
2577 LyXText::text_status LyXText::status() const
2578 {
2579         return status_;
2580 }
2581
2582
2583 void LyXText::status(BufferView * bview, LyXText::text_status st) const
2584 {
2585         // well as much as I know && binds more then || so the above and the
2586         // below are identical (this for your known use of parentesis!)
2587         // Now some explanation:
2588         // We should only go up with refreshing code so this means that if
2589         // we have a MORE refresh we should never set it to LITTLE if we still
2590         // didn't handle it (and then it will be UNCHANGED. Now as long as
2591         // we stay inside one LyXText this may work but we need to tell the
2592         // outermost LyXText that it should REALLY draw us if there is some
2593         // change in a Inset::LyXText. So you see that when we are inside a
2594         // inset's LyXText we give the LITTLE to the outermost LyXText to
2595         // tell'em that it should redraw the actual row (where the inset
2596         // resides! Capito?!
2597
2598         if ((status_ != NEED_MORE_REFRESH)
2599             || (status_ == NEED_MORE_REFRESH
2600                 && st != NEED_VERY_LITTLE_REFRESH))
2601         {
2602                 status_ = st;
2603                 if (inset_owner && st != UNCHANGED) {
2604                         bview->text->status(bview, NEED_VERY_LITTLE_REFRESH);
2605                         if (!bview->text->refresh_row) {
2606                                 bview->text->refresh_row = bview->text->cursor.row();
2607                                 bview->text->refresh_y = bview->text->cursor.y() -
2608                                         bview->text->cursor.row()->baseline();
2609                         }
2610                 }
2611         }
2612 }