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