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