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