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