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