]> git.lyx.org Git - lyx.git/blob - src/text.C
Alfredo's patch.
[lyx.git] / src / text.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 #include "lyxtext.h"
14 #include "lyxrow.h"
15 #include "paragraph.h"
16 #include "gettext.h"
17 #include "bufferparams.h"
18 #include "buffer.h"
19 #include "debug.h"
20 #include "intl.h"
21 #include "lyxrc.h"
22 #include "encoding.h"
23 #include "frontends/LyXView.h"
24 #include "frontends/Painter.h"
25 #include "frontends/font_metrics.h"
26 #include "frontends/screen.h"
27 #include "frontends/WorkArea.h"
28 #include "bufferview_funcs.h"
29 #include "BufferView.h"
30 #include "language.h"
31 #include "ParagraphParameters.h"
32 #include "undo_funcs.h"
33 #include "WordLangTuple.h"
34 #include "paragraph_funcs.h"
35 #include "rowpainter.h"
36
37 #include "insets/insettext.h"
38
39 #include "support/textutils.h"
40 #include "support/LAssert.h"
41 #include "support/lstrings.h"
42
43 #include <algorithm>
44
45 using std::max;
46 using std::min;
47 using std::endl;
48 using std::pair;
49 using lyx::pos_type;
50
51 /// top, right, bottom pixel margin
52 extern int const PAPER_MARGIN = 20;
53 /// margin for changebar
54 extern int const CHANGEBAR_MARGIN = 10;
55 /// left margin
56 extern int const LEFT_MARGIN = PAPER_MARGIN + CHANGEBAR_MARGIN;
57
58 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
59
60
61 int LyXText::top_y() const
62 {
63         if (!top_row_)
64                 return 0;
65         
66         int y = 0;
67         for (Row * row = firstrow; row && row != top_row_; row = row->next()) {
68                 y += row->height();
69         }
70         return y + top_row_offset_;
71 }
72
73
74 void LyXText::top_y(int newy)
75 {
76         if (!firstrow)
77                 return;
78         lyxerr[Debug::GUI] << "setting top y = " << newy << endl;
79         
80         int y = newy;
81         top_row_ = getRowNearY(y);
82         top_row_offset_ = newy - y;
83         lyxerr[Debug::GUI] << "changing reference to row: " << top_row_
84                << " offset: " << top_row_offset_ << endl;
85 }
86
87
88 int LyXText::workWidth(BufferView & bview) const
89 {
90         if (inset_owner) {
91                 // FIXME: pass (const ?) ref
92                 return inset_owner->textWidth(&bview);
93         }
94         return bview.workWidth();
95 }
96
97
98 int LyXText::workWidth(BufferView & bview, Inset * inset) const
99 {
100         Paragraph * par = inset->parOwner();
101         lyx::Assert(par);
102
103         pos_type pos = par->getPositionOfInset(inset);
104         lyx::Assert(pos != -1);
105
106         LyXLayout_ptr const & layout = par->layout();
107
108         if (layout->margintype != MARGIN_RIGHT_ADDRESS_BOX) {
109                 // Optimization here: in most cases, the real row is
110                 // not needed, but only the par/pos values. So we just
111                 // construct a dummy row for leftMargin. (JMarc)
112                 Row dummyrow;
113                 dummyrow.par(par);
114                 dummyrow.pos(pos);
115                 return workWidth(bview) - leftMargin(&bview, &dummyrow);
116         } else {
117                 int dummy_y;
118                 Row * row = getRow(par, pos, dummy_y);
119                 Row * frow = row;
120                 while (frow->previous() && frow->par() == frow->previous()->par())
121                         frow = frow->previous();
122
123                 // FIXME: I don't understand this code - jbl
124
125                 unsigned int maxw = 0;
126                 while (!frow->isParEnd()) {
127                         if ((frow != row) && (maxw < frow->width()))
128                                 maxw = frow->width();
129                         frow = frow->next();
130                 }
131                 if (maxw)
132                         return maxw;
133
134         }
135         return workWidth(bview);
136 }
137
138
139 int LyXText::getRealCursorX(BufferView * bview) const
140 {
141         int x = cursor.x();
142         if (the_locking_inset && (the_locking_inset->getLyXText(bview)!=this))
143                 x = the_locking_inset->getLyXText(bview)->getRealCursorX(bview);
144         return x;
145 }
146
147
148 unsigned char LyXText::transformChar(unsigned char c, Paragraph * par,
149                         pos_type pos) const
150 {
151         if (!Encodings::is_arabic(c))
152                 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && IsDigit(c))
153                         return c + (0xb0 - '0');
154                 else
155                         return c;
156
157         unsigned char const prev_char = pos > 0 ? par->getChar(pos-1) : ' ';
158         unsigned char next_char = ' ';
159
160         for (pos_type i = pos+1; i < par->size(); ++i)
161                 if (!Encodings::IsComposeChar_arabic(par->getChar(i))) {
162                         next_char = par->getChar(i);
163                         break;
164                 }
165
166         if (Encodings::is_arabic(next_char)) {
167                 if (Encodings::is_arabic(prev_char) &&
168                         !Encodings::is_arabic_special(prev_char))
169                         return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
170                 else
171                         return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
172         } else {
173                 if (Encodings::is_arabic(prev_char) &&
174                         !Encodings::is_arabic_special(prev_char))
175                         return Encodings::TransformChar(c, Encodings::FORM_FINAL);
176                 else
177                         return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
178         }
179 }
180
181 // This is the comments that some of the warnings below refers to.
182 // There are some issues in this file and I don't think they are
183 // really related to the FIX_DOUBLE_SPACE patch. I'd rather think that
184 // this is a problem that has been here almost from day one and that a
185 // larger userbase with differenct access patters triggers the bad
186 // behaviour. (segfaults.) What I think happen is: In several places
187 // we store the paragraph in the current cursor and then moves the
188 // cursor. This movement of the cursor will delete paragraph at the
189 // old position if it is now empty. This will make the temporary
190 // pointer to the old cursor paragraph invalid and dangerous to use.
191 // And is some cases this will trigger a segfault. I have marked some
192 // of the cases where this happens with a warning, but I am sure there
193 // are others in this file and in text2.C. There is also a note in
194 // Delete() that you should read. In Delete I store the paragraph->id
195 // instead of a pointer to the paragraph. I am pretty sure this faulty
196 // use of temporary pointers to paragraphs that might have gotten
197 // invalidated (through a cursor movement) before they are used, are
198 // the cause of the strange crashes we get reported often.
199 //
200 // It is very tiresom to change this code, especially when it is as
201 // hard to read as it is. Help to fix all the cases where this is done
202 // would be greately appreciated.
203 //
204 // Lgb
205
206 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
207                          pos_type pos) const
208 {
209         char const c = par->getChar(pos);
210         return singleWidth(bview, par, pos, c);
211 }
212
213
214 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
215                          pos_type pos, char c) const
216 {
217         LyXFont const font = getFont(bview->buffer(), par, pos);
218
219         // The most common case is handled first (Asger)
220         if (IsPrintable(c)) {
221                 if (font.language()->RightToLeft()) {
222                         if (font.language()->lang() == "arabic" &&
223                             (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
224                              lyxrc.font_norm_type == LyXRC::ISO_10646_1))
225                                 if (Encodings::IsComposeChar_arabic(c))
226                                         return 0;
227                                 else
228                                         c = transformChar(c, par, pos);
229                         else if (font.language()->lang() == "hebrew" &&
230                                  Encodings::IsComposeChar_hebrew(c))
231                                 return 0;
232                 }
233                 return font_metrics::width(c, font);
234
235         } else if (IsHfillChar(c)) {
236                 // Because of the representation as vertical lines
237                 return 3;
238         } else if (c == Paragraph::META_INSET) {
239                 Inset * tmpinset = par->getInset(pos);
240                 if (tmpinset) {
241 #if 1
242                         // this IS needed otherwise on initialitation we don't get the fill
243                         // of the row right (ONLY on initialization if we read a file!)
244                         // should be changed! (Jug 20011204)
245                         tmpinset->update(bview, font);
246 #endif
247                         return tmpinset->width(bview, font);
248                 } else
249                         return 0;
250
251         } else if (IsSeparatorChar(c))
252                 c = ' ';
253         else if (IsNewlineChar(c))
254                 c = 'n';
255         return font_metrics::width(c, font);
256 }
257
258
259 void LyXText::computeBidiTables(Buffer const * buf, Row * row) const
260 {
261         bidi_same_direction = true;
262         if (!lyxrc.rtl_support) {
263                 bidi_start = -1;
264                 return;
265         }
266
267         Inset * inset = row->par()->inInset();
268         if (inset && inset->owner() &&
269             inset->owner()->lyxCode() == Inset::ERT_CODE) {
270                 bidi_start = -1;
271                 return;
272         }
273
274         bidi_start = row->pos();
275         bidi_end = row->lastPrintablePos();
276
277         if (bidi_start > bidi_end) {
278                 bidi_start = -1;
279                 return;
280         }
281
282         if (bidi_end + 2 - bidi_start >
283             static_cast<pos_type>(log2vis_list.size())) {
284                 pos_type new_size =
285                         (bidi_end + 2 - bidi_start < 500) ?
286                         500 : 2 * (bidi_end + 2 - bidi_start);
287                 log2vis_list.resize(new_size);
288                 vis2log_list.resize(new_size);
289                 bidi_levels.resize(new_size);
290         }
291
292         vis2log_list[bidi_end + 1 - bidi_start] = -1;
293         log2vis_list[bidi_end + 1 - bidi_start] = -1;
294
295         pos_type stack[2];
296         bool const rtl_par =
297                 row->par()->isRightToLeftPar(buf->params);
298         int level = 0;
299         bool rtl = false;
300         bool rtl0 = false;
301         pos_type const main_body = row->par()->beginningOfMainBody();
302
303         for (pos_type lpos = bidi_start; lpos <= bidi_end; ++lpos) {
304                 bool is_space = row->par()->isLineSeparator(lpos);
305                 pos_type const pos =
306                         (is_space && lpos + 1 <= bidi_end &&
307                          !row->par()->isLineSeparator(lpos + 1) &&
308                          !row->par()->isNewline(lpos + 1))
309                         ? lpos + 1 : lpos;
310                 LyXFont font = row->par()->getFontSettings(buf->params, pos);
311                 if (pos != lpos && 0 < lpos && rtl0 && font.isRightToLeft() &&
312                     font.number() == LyXFont::ON &&
313                     row->par()->getFontSettings(buf->params, lpos - 1).number()
314                     == LyXFont::ON) {
315                         font = row->par()->getFontSettings(buf->params, lpos);
316                         is_space = false;
317                 }
318
319
320                 bool new_rtl = font.isVisibleRightToLeft();
321                 bool new_rtl0 = font.isRightToLeft();
322                 int new_level;
323
324                 if (lpos == main_body - 1
325                     && row->pos() < main_body - 1
326                     && is_space) {
327                         new_level = (rtl_par) ? 1 : 0;
328                         new_rtl = new_rtl0 = rtl_par;
329                 } else if (new_rtl0)
330                         new_level = (new_rtl) ? 1 : 2;
331                 else
332                         new_level = (rtl_par) ? 2 : 0;
333
334                 if (is_space && new_level >= level) {
335                         new_level = level;
336                         new_rtl = rtl;
337                         new_rtl0 = rtl0;
338                 }
339
340                 int new_level2 = new_level;
341
342                 if (level == new_level && rtl0 != new_rtl0) {
343                         --new_level2;
344                         log2vis_list[lpos - bidi_start] = (rtl) ? 1 : -1;
345                 } else if (level < new_level) {
346                         log2vis_list[lpos - bidi_start] =  (rtl) ? -1 : 1;
347                         if (new_level > rtl_par)
348                                 bidi_same_direction = false;
349                 } else
350                         log2vis_list[lpos - bidi_start] = (new_rtl) ? -1 : 1;
351                 rtl = new_rtl;
352                 rtl0 = new_rtl0;
353                 bidi_levels[lpos - bidi_start] = new_level;
354
355                 while (level > new_level2) {
356                         pos_type old_lpos = stack[--level];
357                         int delta = lpos - old_lpos - 1;
358                         if (level % 2)
359                                 delta = -delta;
360                         log2vis_list[lpos - bidi_start] += delta;
361                         log2vis_list[old_lpos - bidi_start] += delta;
362                 }
363                 while (level < new_level)
364                         stack[level++] = lpos;
365         }
366
367         while (level > 0) {
368                 pos_type const old_lpos = stack[--level];
369                 int delta = bidi_end - old_lpos;
370                 if (level % 2)
371                         delta = -delta;
372                 log2vis_list[old_lpos - bidi_start] += delta;
373         }
374
375         pos_type vpos = bidi_start - 1;
376         for (pos_type lpos = bidi_start;
377              lpos <= bidi_end; ++lpos) {
378                 vpos += log2vis_list[lpos - bidi_start];
379                 vis2log_list[vpos - bidi_start] = lpos;
380                 log2vis_list[lpos - bidi_start] = vpos;
381         }
382 }
383
384
385 // This method requires a previous call to ComputeBidiTables()
386 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
387                          pos_type pos) const
388 {
389         if (!lyxrc.rtl_support || pos == 0)
390                 return false;
391
392         if (!bidi_InRange(pos - 1)) {
393                 /// This can happen if pos is the first char of a row.
394                 /// Returning false in this case is incorrect!
395                 return false;
396         }
397
398         bool const rtl = bidi_level(pos - 1) % 2;
399         bool const rtl2 = bidi_InRange(pos)
400                 ? bidi_level(pos) % 2
401                 : par->isRightToLeftPar(buf->params);
402         return rtl != rtl2;
403 }
404
405
406 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
407                          pos_type pos, LyXFont const & font) const
408 {
409         if (!lyxrc.rtl_support)
410                 return false;    // This is just for speedup
411
412         bool const rtl = font.isVisibleRightToLeft();
413         bool const rtl2 = bidi_InRange(pos)
414                 ? bidi_level(pos) % 2
415                 : par->isRightToLeftPar(buf->params);
416         return rtl != rtl2;
417 }
418
419
420 int LyXText::leftMargin(BufferView * bview, Row const * row) const
421 {
422         Inset * ins;
423         if ((row->par()->getChar(row->pos()) == Paragraph::META_INSET) &&
424                 (ins=row->par()->getInset(row->pos())) &&
425                 (ins->needFullRow() || ins->display()))
426                 return LEFT_MARGIN;
427
428         LyXTextClass const & tclass =
429                 bview->buffer()->params.getLyXTextClass();
430         LyXLayout_ptr const & layout = row->par()->layout();
431
432         string parindent = layout->parindent;
433
434         int x = LEFT_MARGIN;
435
436         x += font_metrics::signedWidth(tclass.leftmargin(), tclass.defaultfont());
437
438         // this is the way, LyX handles the LaTeX-Environments.
439         // I have had this idea very late, so it seems to be a
440         // later added hack and this is true
441         if (!row->par()->getDepth()) {
442                 if (row->par()->layout() == tclass.defaultLayout()) {
443                         // find the previous same level paragraph
444                         if (row->par()->previous()) {
445                                 Paragraph * newpar = row->par()
446                                         ->depthHook(row->par()->getDepth());
447                                 if (newpar &&
448                                     newpar->layout()->nextnoindent)
449                                         parindent.erase();
450                         }
451                 }
452         } else {
453                 // find the next level paragraph
454
455                 Paragraph * newpar = row->par()->outerHook();
456
457                 // make a corresponding row. Needed to call LeftMargin()
458
459                 // check wether it is a sufficent paragraph
460                 if (newpar && newpar->layout()->isEnvironment()) {
461                         Row dummyrow;
462                         dummyrow.par(newpar);
463                         dummyrow.pos(newpar->size());
464                         x = leftMargin(bview, &dummyrow);
465                 } else {
466                         // this is no longer an error, because this function
467                         // is used to clear impossible depths after changing
468                         // a layout. Since there is always a redo,
469                         // LeftMargin() is always called
470                         row->par()->params().depth(0);
471                 }
472
473                 if (newpar && row->par()->layout() == tclass.defaultLayout()) {
474                         if (newpar->params().noindent())
475                                 parindent.erase();
476                         else {
477                                 parindent = newpar->layout()->parindent;
478                         }
479
480                 }
481         }
482
483         LyXFont const labelfont = getLabelFont(bview->buffer(), row->par());
484         switch (layout->margintype) {
485         case MARGIN_DYNAMIC:
486                 if (!layout->leftmargin.empty()) {
487                         x += font_metrics::signedWidth(layout->leftmargin,
488                                                   tclass.defaultfont());
489                 }
490                 if (!row->par()->getLabelstring().empty()) {
491                         x += font_metrics::signedWidth(layout->labelindent,
492                                                   labelfont);
493                         x += font_metrics::width(row->par()->getLabelstring(),
494                                             labelfont);
495                         x += font_metrics::width(layout->labelsep, labelfont);
496                 }
497                 break;
498         case MARGIN_MANUAL:
499                 x += font_metrics::signedWidth(layout->labelindent, labelfont);
500                 if (row->pos() >= row->par()->beginningOfMainBody()) {
501                         if (!row->par()->getLabelWidthString().empty()) {
502                                 x += font_metrics::width(row->par()->getLabelWidthString(),
503                                                labelfont);
504                                 x += font_metrics::width(layout->labelsep, labelfont);
505                         }
506                 }
507                 break;
508         case MARGIN_STATIC:
509                 x += font_metrics::signedWidth(layout->leftmargin, tclass.defaultfont()) * 4
510                         / (row->par()->getDepth() + 4);
511                 break;
512         case MARGIN_FIRST_DYNAMIC:
513                 if (layout->labeltype == LABEL_MANUAL) {
514                         if (row->pos() >= row->par()->beginningOfMainBody()) {
515                                 x += font_metrics::signedWidth(layout->leftmargin,
516                                                           labelfont);
517                         } else {
518                                 x += font_metrics::signedWidth(layout->labelindent,
519                                                           labelfont);
520                         }
521                 } else if (row->pos()
522                            // Special case to fix problems with
523                            // theorems (JMarc)
524                            || (layout->labeltype == LABEL_STATIC
525                                && layout->latextype == LATEX_ENVIRONMENT
526                                && ! row->par()->isFirstInSequence())) {
527                         x += font_metrics::signedWidth(layout->leftmargin,
528                                                   labelfont);
529                 } else if (layout->labeltype != LABEL_TOP_ENVIRONMENT
530                            && layout->labeltype != LABEL_BIBLIO
531                            && layout->labeltype !=
532                            LABEL_CENTERED_TOP_ENVIRONMENT) {
533                         x += font_metrics::signedWidth(layout->labelindent,
534                                                   labelfont);
535                         x += font_metrics::width(layout->labelsep, labelfont);
536                         x += font_metrics::width(row->par()->getLabelstring(),
537                                             labelfont);
538                 }
539                 break;
540
541         case MARGIN_RIGHT_ADDRESS_BOX:
542         {
543                 // ok, a terrible hack. The left margin depends on the widest
544                 // row in this paragraph. Do not care about footnotes, they
545                 // are *NOT* allowed in the LaTeX realisation of this layout.
546
547                 // find the first row of this paragraph
548                 Row const * tmprow = row;
549                 while (tmprow->previous()
550                        && tmprow->previous()->par() == row->par())
551                         tmprow = tmprow->previous();
552
553                 int minfill = tmprow->fill();
554                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
555                         tmprow = tmprow->next();
556                         if (tmprow->fill() < minfill)
557                                 minfill = tmprow->fill();
558                 }
559
560                 x += font_metrics::signedWidth(layout->leftmargin,
561                         tclass.defaultfont());
562                 x += minfill;
563         }
564         break;
565         }
566
567         if ((workWidth(*bview) > 0) &&
568                 !row->par()->params().leftIndent().zero())
569         {
570                 LyXLength const len = row->par()->params().leftIndent();
571                 int const tw = inset_owner ?
572                         inset_owner->latexTextWidth(bview) : workWidth(*bview);
573                 x += len.inPixels(tw);
574         }
575
576         LyXAlignment align; // wrong type
577
578         if (row->par()->params().align() == LYX_ALIGN_LAYOUT)
579                 align = layout->align;
580         else
581                 align = row->par()->params().align();
582
583         // set the correct parindent
584         if (row->pos() == 0) {
585                 if ((layout->labeltype == LABEL_NO_LABEL
586                      || layout->labeltype == LABEL_TOP_ENVIRONMENT
587                      || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
588                      || (layout->labeltype == LABEL_STATIC
589                          && layout->latextype == LATEX_ENVIRONMENT
590                          && ! row->par()->isFirstInSequence()))
591                     && align == LYX_ALIGN_BLOCK
592                     && !row->par()->params().noindent()
593                         // in tabulars and ert paragraphs are never indented!
594                         && (!row->par()->inInset() || !row->par()->inInset()->owner() ||
595                                 (row->par()->inInset()->owner()->lyxCode() != Inset::TABULAR_CODE &&
596                                  row->par()->inInset()->owner()->lyxCode() != Inset::ERT_CODE))
597                     && (row->par()->layout() != tclass.defaultLayout() ||
598                         bview->buffer()->params.paragraph_separation ==
599                         BufferParams::PARSEP_INDENT)) {
600                         x += font_metrics::signedWidth(parindent,
601                                                   tclass.defaultfont());
602                 } else if (layout->labeltype == LABEL_BIBLIO) {
603                         // ale970405 Right width for bibitems
604                         x += bibitemMaxWidth(bview, tclass.defaultfont());
605                 }
606         }
607
608         return x;
609 }
610
611
612 int LyXText::rightMargin(Buffer const & buf, Row const & row) const
613 {
614         Inset * ins;
615         if ((row.par()->getChar(row.pos()) == Paragraph::META_INSET) &&
616                 (ins=row.par()->getInset(row.pos())) &&
617                 (ins->needFullRow() || ins->display()))
618                 return PAPER_MARGIN;
619
620         LyXTextClass const & tclass = buf.params.getLyXTextClass();
621         LyXLayout_ptr const & layout = row.par()->layout();
622
623         int x = PAPER_MARGIN
624                 + font_metrics::signedWidth(tclass.rightmargin(),
625                                        tclass.defaultfont());
626
627         // this is the way, LyX handles the LaTeX-Environments.
628         // I have had this idea very late, so it seems to be a
629         // later added hack and this is true
630         if (row.par()->getDepth()) {
631                 // find the next level paragraph
632
633                 Paragraph const * newpar = row.par();
634
635                 do {
636                         newpar = newpar->previous();
637                 } while (newpar
638                          && newpar->getDepth() >= row.par()->getDepth());
639
640                 // make a corresponding row. Needed to call LeftMargin()
641
642                 // check wether it is a sufficent paragraph
643                 if (newpar && newpar->layout()->isEnvironment()) {
644                         Row dummyrow;
645                         dummyrow.par(const_cast<Paragraph *>(newpar));
646                         dummyrow.pos(0);
647                         x = rightMargin(buf, dummyrow);
648                 } else {
649                         // this is no longer an error, because this function
650                         // is used to clear impossible depths after changing
651                         // a layout. Since there is always a redo,
652                         // LeftMargin() is always called
653                         row.par()->params().depth(0);
654                 }
655         }
656
657         //lyxerr << "rightmargin: " << layout->rightmargin << endl;
658         x += font_metrics::signedWidth(layout->rightmargin,
659                                        tclass.defaultfont())
660                 * 4 / (row.par()->getDepth() + 4);
661         return x;
662 }
663
664
665 int LyXText::labelEnd(BufferView & bview, Row const & row) const
666 {
667         if (row.par()->layout()->margintype == MARGIN_MANUAL) {
668                 Row tmprow = row;
669                 tmprow.pos(row.par()->size());
670                 // just the beginning of the main body
671                 return leftMargin(&bview, &tmprow);
672         }
673
674         // LabelEnd is only needed if the layout
675         // fills a flushleft label.
676         return 0;
677 }
678
679
680 // get the next breakpoint in a given paragraph
681 pos_type
682 LyXText::nextBreakPoint(BufferView * bview, Row const * row, int width) const
683 {
684         Paragraph * par = row->par();
685
686         if (width < 0)
687                 return par->size();
688
689         pos_type const pos = row->pos();
690
691         // position of the last possible breakpoint
692         // -1 isn't a suitable value, but a flag
693         pos_type last_separator = -1;
694         width -= rightMargin(*bview->buffer(), *row);
695
696         pos_type const main_body = par->beginningOfMainBody();
697         LyXLayout_ptr const & layout = par->layout();
698
699         pos_type i = pos;
700
701         if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
702                 // special code for right address boxes, only newlines count
703                 while (i < par->size()) {
704                         if (par->isNewline(i)) {
705                                 last_separator = i;
706                                 i = par->size() - 1; // this means break
707                                 //x = width;
708                         } else if (par->isInset(i) && par->getInset(i)
709                                 && par->getInset(i)->display()) {
710                                 par->getInset(i)->display(false);
711                         }
712                         ++i;
713                 }
714         } else {
715                 // Last position is an invariant
716                 pos_type const last = par->size();
717                 // this is the usual handling
718                 int x = leftMargin(bview, row);
719                 bool doitonetime = true;
720                 while (doitonetime || ((x < width) && (i < last))) {
721                         doitonetime = false;
722                         char const c = par->getChar(i);
723                         Inset * in = 0;
724                         if (c == Paragraph::META_INSET)
725                                 in = par->getInset(i);
726                         if (IsNewlineChar(c)) {
727                                 last_separator = i;
728                                 x = width; // this means break
729                         } else if (in && !in->isChar()) {
730                                 // check wether a Display() inset is
731                                 // valid here. if not, change it to
732                                 // non-display
733                                 if (in->display() &&
734                                     (layout->isCommand() ||
735                                      (layout->labeltype == LABEL_MANUAL
736                                       && i < par->beginningOfMainBody())))
737                                 {
738                                         // display istn't allowd
739                                         in->display(false);
740                                         x += singleWidth(bview, par, i, c);
741                                 } else if (in->display() || in->needFullRow()) {
742                                         // So break the line here
743                                         if (i == pos) {
744                                                 if (pos < last-1) {
745                                                         last_separator = i;
746                                                         if (par->isLineSeparator(i+1))
747                                                                 ++last_separator;
748                                                 } else
749                                                         last_separator = last; // to avoid extra rows
750                                         } else
751                                                 last_separator = i - 1;
752                                         x = width;  // this means break
753                                 } else {
754                                         x += singleWidth(bview, par, i, c);
755                                         // we have to check this separately as we could have a
756                                         // lineseparator and then the algorithm below would prefer
757                                         // that which IS wrong! We should always break on an inset
758                                         // if it's too long and not on the last separator.
759                                         // Maybe the only exeption is insets used as chars but
760                                         // then we would have to have a special function inside
761                                         // the inset to tell us this. Till then we leave it as
762                                         // it is now. (Jug 20020106)
763                                         if (pos < i && x >= width && last_separator >= 0)
764                                                 last_separator = i - 1;
765                                 }
766                         } else  {
767                                 if (par->isLineSeparator(i))
768                                         last_separator = i;
769                                 x += singleWidth(bview, par, i, c);
770                         }
771                         ++i;
772                         if (i == main_body) {
773                                 x += font_metrics::width(layout->labelsep,
774                                                     getLabelFont(bview->buffer(), par));
775                                 if (par->isLineSeparator(i - 1))
776                                         x-= singleWidth(bview, par, i - 1);
777                                 int left_margin = labelEnd(*bview, *row);
778                                 if (x < left_margin)
779                                         x = left_margin;
780                         }
781                 }
782                 if ((pos+1 < i) && (last_separator < 0) && (x >= width))
783                         last_separator = i - 2;
784                 else if ((pos < i) && (last_separator < 0) && (x >= width))
785                         last_separator = i - 1;
786                 // end of paragraph is always a suitable separator
787                 else if (i == last && x < width)
788                         last_separator = i;
789         }
790
791         // well, if last_separator is still 0, the line isn't breakable.
792         // don't care and cut simply at the end
793         if (last_separator < 0) {
794                 last_separator = i;
795         }
796
797         // manual labels cannot be broken in LaTeX, do not care
798         if (main_body && last_separator < main_body)
799                 last_separator = main_body - 1;
800
801         return last_separator;
802 }
803
804
805 // returns the minimum space a row needs on the screen in pixel
806 int LyXText::fill(BufferView & bview, Row & row, int paper_width) const
807 {
808         if (paper_width < 0)
809                 return 0;
810
811         int w;
812         // get the pure distance
813         pos_type const last = row.lastPrintablePos();
814
815         // special handling of the right address boxes
816         if (row.par()->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
817                 int const tmpfill = row.fill();
818                 row.fill(0); // the minfill in MarginLeft()
819                 w = leftMargin(&bview, &row);
820                 row.fill(tmpfill);
821         } else
822                 w = leftMargin(&bview, &row);
823
824         Paragraph * par = row.par();
825         LyXLayout_ptr const & layout = par->layout();
826
827         pos_type const main_body = par->beginningOfMainBody();
828         pos_type i = row.pos();
829
830         while (i <= last) {
831                 if (main_body > 0 && i == main_body) {
832                         w += font_metrics::width(layout->labelsep, getLabelFont(bview.buffer(), par));
833                         if (par->isLineSeparator(i - 1))
834                                 w -= singleWidth(&bview, par, i - 1);
835                         int left_margin = labelEnd(bview, row);
836                         if (w < left_margin)
837                                 w = left_margin;
838                 }
839                 w += singleWidth(&bview, par, i);
840                 ++i;
841         }
842         if (main_body > 0 && main_body > last) {
843                 w += font_metrics::width(layout->labelsep, getLabelFont(bview.buffer(), par));
844                 if (last >= 0 && par->isLineSeparator(last))
845                         w -= singleWidth(&bview, par, last);
846                 int const left_margin = labelEnd(bview, row);
847                 if (w < left_margin)
848                         w = left_margin;
849         }
850
851         int const fill = paper_width - w - rightMargin(*bview.buffer(), row);
852         return fill;
853 }
854
855
856 // returns the minimum space a manual label needs on the screen in pixel
857 int LyXText::labelFill(BufferView & bview, Row const & row) const
858 {
859         pos_type last = row.par()->beginningOfMainBody();
860
861         lyx::Assert(last > 0);
862
863         // -1 because a label ends either with a space that is in the label,
864         // or with the beginning of a footnote that is outside the label.
865         --last;
866
867         // a separator at this end does not count
868         if (row.par()->isLineSeparator(last))
869                 --last;
870
871         int w = 0;
872         pos_type i = row.pos();
873         while (i <= last) {
874                 w += singleWidth(&bview, row.par(), i);
875                 ++i;
876         }
877
878         int fill = 0;
879         string const & labwidstr = row.par()->params().labelWidthString();
880         if (!labwidstr.empty()) {
881                 LyXFont const labfont = getLabelFont(bview.buffer(), row.par());
882                 int const labwidth = font_metrics::width(labwidstr, labfont);
883                 fill = max(labwidth - w, 0);
884         }
885
886         return fill;
887 }
888
889
890 LColor::color LyXText::backgroundColor() const
891 {
892         if (inset_owner)
893                 return inset_owner->backgroundColor();
894         else
895                 return LColor::background;
896 }
897
898 void LyXText::setHeightOfRow(BufferView * bview, Row * row) const
899 {
900         // get the maximum ascent and the maximum descent
901         int asc = 0;
902         int desc = 0;
903         float layoutasc = 0;
904         float layoutdesc = 0;
905         float tmptop = 0;
906         LyXFont tmpfont;
907         Inset * tmpinset = 0;
908
909         // ok , let us initialize the maxasc and maxdesc value.
910         // This depends in LaTeX of the font of the last character
911         // in the paragraph. The hack below is necessary because
912         // of the possibility of open footnotes
913
914         // Correction: only the fontsize count. The other properties
915         //  are taken from the layoutfont. Nicer on the screen :)
916         Paragraph * par = row->par();
917         Paragraph * firstpar = row->par();
918
919         LyXLayout_ptr const & layout = firstpar->layout();
920
921         // as max get the first character of this row then it can increase but not
922         // decrease the height. Just some point to start with so we don't have to
923         // do the assignment below too often.
924         LyXFont font = getFont(bview->buffer(), par, row->pos());
925         LyXFont::FONT_SIZE const tmpsize = font.size();
926         font = getLayoutFont(bview->buffer(), par);
927         LyXFont::FONT_SIZE const size = font.size();
928         font.setSize(tmpsize);
929
930         LyXFont labelfont = getLabelFont(bview->buffer(), par);
931
932         float spacing_val = 1.0;
933         if (!row->par()->params().spacing().isDefault()) {
934                 spacing_val = row->par()->params().spacing().getValue();
935         } else {
936                 spacing_val = bview->buffer()->params.spacing.getValue();
937         }
938         //lyxerr << "spacing_val = " << spacing_val << endl;
939
940         int maxasc = int(font_metrics::maxAscent(font) *
941                          layout->spacing.getValue() *
942                          spacing_val);
943         int maxdesc = int(font_metrics::maxDescent(font) *
944                           layout->spacing.getValue() *
945                           spacing_val);
946
947         pos_type const pos_end = row->lastPos();
948         int labeladdon = 0;
949         int maxwidth = 0;
950
951         // Check if any insets are larger
952         for (pos_type pos = row->pos(); pos <= pos_end; ++pos) {
953                 if (row->par()->isInset(pos)) {
954                         tmpfont = getFont(bview->buffer(), row->par(), pos);
955                         tmpinset = row->par()->getInset(pos);
956                         if (tmpinset) {
957 #if 1 // this is needed for deep update on initialitation
958                                 tmpinset->update(bview, tmpfont);
959 #endif
960                                 asc = tmpinset->ascent(bview, tmpfont);
961                                 desc = tmpinset->descent(bview, tmpfont);
962                                 maxwidth += tmpinset->width(bview, tmpfont);
963                                 maxasc = max(maxasc, asc);
964                                 maxdesc = max(maxdesc, desc);
965                         }
966                 } else {
967                         maxwidth += singleWidth(bview, row->par(), pos);
968                 }
969         }
970
971         // Check if any custom fonts are larger (Asger)
972         // This is not completely correct, but we can live with the small,
973         // cosmetic error for now.
974         LyXFont::FONT_SIZE maxsize =
975                 row->par()->highestFontInRange(row->pos(), pos_end, size);
976         if (maxsize > font.size()) {
977                 font.setSize(maxsize);
978
979                 asc = font_metrics::maxAscent(font);
980                 desc = font_metrics::maxDescent(font);
981                 if (asc > maxasc)
982                         maxasc = asc;
983                 if (desc > maxdesc)
984                         maxdesc = desc;
985         }
986
987         // This is nicer with box insets:
988         ++maxasc;
989         ++maxdesc;
990
991         row->ascent_of_text(maxasc);
992
993         // is it a top line?
994         if (!row->pos() && (row->par() == firstpar)) {
995
996                 // some parksips VERY EASY IMPLEMENTATION
997                 if (bview->buffer()->params.paragraph_separation ==
998                         BufferParams::PARSEP_SKIP)
999                 {
1000                         if (layout->isParagraph()
1001                                 && firstpar->getDepth() == 0
1002                                 && firstpar->previous())
1003                         {
1004                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(*bview);
1005                         } else if (firstpar->previous() &&
1006                                    firstpar->previous()->layout()->isParagraph() &&
1007                                    firstpar->previous()->getDepth() == 0)
1008                         {
1009                                 // is it right to use defskip here too? (AS)
1010                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(*bview);
1011                         }
1012                 }
1013
1014                 // the top margin
1015                 if (!row->par()->previous() && isTopLevel())
1016                         maxasc += PAPER_MARGIN;
1017
1018                 // add the vertical spaces, that the user added
1019                 maxasc += getLengthMarkerHeight(*bview, firstpar->params().spaceTop());
1020
1021                 // do not forget the DTP-lines!
1022                 // there height depends on the font of the nearest character
1023                 if (firstpar->params().lineTop())
1024
1025                         maxasc += 2 * font_metrics::ascent('x', getFont(bview->buffer(),
1026                                         firstpar, 0));
1027                 // and now the pagebreaks
1028                 if (firstpar->params().pagebreakTop())
1029                         maxasc += 3 * defaultRowHeight();
1030
1031                 // This is special code for the chapter, since the label of this
1032                 // layout is printed in an extra row
1033                 if (layout->labeltype == LABEL_COUNTER_CHAPTER
1034                         && bview->buffer()->params.secnumdepth >= 0)
1035                 {
1036                         float spacing_val = 1.0;
1037                         if (!row->par()->params().spacing().isDefault()) {
1038                                 spacing_val = row->par()->params().spacing().getValue();
1039                         } else {
1040                                 spacing_val = bview->buffer()->params.spacing.getValue();
1041                         }
1042
1043                         labeladdon = int(font_metrics::maxDescent(labelfont) *
1044                                          layout->spacing.getValue() *
1045                                          spacing_val)
1046                                 + int(font_metrics::maxAscent(labelfont) *
1047                                       layout->spacing.getValue() *
1048                                       spacing_val);
1049                 }
1050
1051                 // special code for the top label
1052                 if ((layout->labeltype == LABEL_TOP_ENVIRONMENT
1053                      || layout->labeltype == LABEL_BIBLIO
1054                      || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1055                     && row->par()->isFirstInSequence()
1056                     && !row->par()->getLabelstring().empty())
1057                 {
1058                         float spacing_val = 1.0;
1059                         if (!row->par()->params().spacing().isDefault()) {
1060                                 spacing_val = row->par()->params().spacing().getValue();
1061                         } else {
1062                                 spacing_val = bview->buffer()->params.spacing.getValue();
1063                         }
1064
1065                         labeladdon = int(
1066                                 (font_metrics::maxAscent(labelfont) *
1067                                  layout->spacing.getValue() *
1068                                  spacing_val)
1069                                 +(font_metrics::maxDescent(labelfont) *
1070                                   layout->spacing.getValue() *
1071                                   spacing_val)
1072                                 + layout->topsep * defaultRowHeight()
1073                                 + layout->labelbottomsep * defaultRowHeight());
1074                 }
1075
1076                 // and now the layout spaces, for example before and after a section,
1077                 // or between the items of a itemize or enumerate environment
1078
1079                 if (!firstpar->params().pagebreakTop()) {
1080                         Paragraph * prev = row->par()->previous();
1081                         if (prev)
1082                                 prev = row->par()->depthHook(row->par()->getDepth());
1083                         if (prev && prev->layout() == firstpar->layout() &&
1084                                 prev->getDepth() == firstpar->getDepth() &&
1085                                 prev->getLabelWidthString() == firstpar->getLabelWidthString())
1086                         {
1087                                 layoutasc = (layout->itemsep * defaultRowHeight());
1088                         } else if (row->previous()) {
1089                                 tmptop = layout->topsep;
1090
1091                                 if (row->previous()->par()->getDepth() >= row->par()->getDepth())
1092                                         tmptop -= row->previous()->par()->layout()->bottomsep;
1093
1094                                 if (tmptop > 0)
1095                                         layoutasc = (tmptop * defaultRowHeight());
1096                         } else if (row->par()->params().lineTop()) {
1097                                 tmptop = layout->topsep;
1098
1099                                 if (tmptop > 0)
1100                                         layoutasc = (tmptop * defaultRowHeight());
1101                         }
1102
1103                         prev = row->par()->outerHook();
1104                         if (prev)  {
1105                                 maxasc += int(prev->layout()->parsep * defaultRowHeight());
1106                         } else {
1107                                 if (firstpar->previous() &&
1108                                         firstpar->previous()->getDepth() == 0 &&
1109                                         firstpar->previous()->layout() !=
1110                                         firstpar->layout())
1111                                 {
1112                                         // avoid parsep
1113                                 } else if (firstpar->previous()) {
1114                                         maxasc += int(layout->parsep * defaultRowHeight());
1115                                 }
1116                         }
1117                 }
1118         }
1119
1120         // is it a bottom line?
1121         if (row->par() == par
1122                 && (!row->next() || row->next()->par() != row->par())) {
1123                 // the bottom margin
1124                 if (!par->next() && isTopLevel())
1125                         maxdesc += PAPER_MARGIN;
1126
1127                 // add the vertical spaces, that the user added
1128                 maxdesc += getLengthMarkerHeight(*bview, firstpar->params().spaceBottom());
1129
1130                 // do not forget the DTP-lines!
1131                 // there height depends on the font of the nearest character
1132                 if (firstpar->params().lineBottom())
1133                         maxdesc += 2 * font_metrics::ascent('x',
1134                                                        getFont(bview->buffer(),
1135                                                                par,
1136                                                                max(pos_type(0), par->size() - 1)));
1137
1138                 // and now the pagebreaks
1139                 if (firstpar->params().pagebreakBottom())
1140                         maxdesc += 3 * defaultRowHeight();
1141
1142                 // and now the layout spaces, for example before and after
1143                 // a section, or between the items of a itemize or enumerate
1144                 // environment
1145                 if (!firstpar->params().pagebreakBottom()
1146                     && row->par()->next()) {
1147                         Paragraph * nextpar = row->par()->next();
1148                         Paragraph * comparepar = row->par();
1149                         float usual = 0;
1150                         float unusual = 0;
1151
1152                         if (comparepar->getDepth() > nextpar->getDepth()) {
1153                                 usual = (comparepar->layout()->bottomsep * defaultRowHeight());
1154                                 comparepar = comparepar->depthHook(nextpar->getDepth());
1155                                 if (comparepar->layout()!= nextpar->layout()
1156                                         || nextpar->getLabelWidthString() !=
1157                                         comparepar->getLabelWidthString())
1158                                 {
1159                                         unusual = (comparepar->layout()->bottomsep * defaultRowHeight());
1160                                 }
1161                                 if (unusual > usual)
1162                                         layoutdesc = unusual;
1163                                 else
1164                                         layoutdesc = usual;
1165                         } else if (comparepar->getDepth() ==  nextpar->getDepth()) {
1166
1167                                 if (comparepar->layout() != nextpar->layout()
1168                                         || nextpar->getLabelWidthString() !=
1169                                         comparepar->getLabelWidthString())
1170                                         layoutdesc = int(comparepar->layout()->bottomsep * defaultRowHeight());
1171                         }
1172                 }
1173         }
1174
1175         // incalculate the layout spaces
1176         maxasc += int(layoutasc * 2 / (2 + firstpar->getDepth()));
1177         maxdesc += int(layoutdesc * 2 / (2 + firstpar->getDepth()));
1178
1179         // calculate the new height of the text
1180         height -= row->height();
1181
1182         row->height(maxasc + maxdesc + labeladdon);
1183         row->baseline(maxasc + labeladdon);
1184
1185         height += row->height();
1186
1187         row->top_of_text(row->baseline() - font_metrics::maxAscent(font));
1188
1189         float x = 0;
1190         if (layout->margintype != MARGIN_RIGHT_ADDRESS_BOX) {
1191                 float dummy;
1192                 // this IS needed
1193                 row->width(maxwidth);
1194                 prepareToPrint(bview, row, x, dummy, dummy, dummy, false);
1195         }
1196         row->width(int(maxwidth + x));
1197         if (inset_owner) {
1198                 Row * r = firstrow;
1199                 width = max(0, workWidth(*bview));
1200                 while (r) {
1201                         if (r->width() > width)
1202                                 width = r->width();
1203                         r = r->next();
1204                 }
1205         }
1206 }
1207
1208
1209 // Appends the implicit specified paragraph behind the specified row,
1210 // start at the implicit given position
1211 void LyXText::appendParagraph(BufferView * bview, Row * row) const
1212 {
1213         bool not_ready = true;
1214
1215         // The last character position of a paragraph is an invariant so we can
1216         // safely get it here. (Asger)
1217         pos_type const lastposition = row->par()->size();
1218         do {
1219                 // Get the next breakpoint
1220                 pos_type z = nextBreakPoint(bview, row, workWidth(*bview));
1221
1222                 Row * tmprow = row;
1223
1224                 // Insert the new row
1225                 if (z < lastposition) {
1226                         ++z;
1227                         insertRow(row, row->par(), z);
1228                         row = row->next();
1229
1230                         row->height(0);
1231                 } else
1232                         not_ready = false;
1233
1234                 // Set the dimensions of the row
1235                 // fixed fill setting now by calling inset->update() in
1236                 // SingleWidth when needed!
1237                 tmprow->fill(fill(*bview, *tmprow, workWidth(*bview)));
1238                 setHeightOfRow(bview, tmprow);
1239
1240         } while (not_ready);
1241 }
1242
1243
1244 // Do we even need this at all ? Code that uses  RowPainter *already*
1245 // sets need_break_row when it sees a CHANGED_IN_DRAW, though not
1246 // quite like this
1247 void LyXText::markChangeInDraw(BufferView * bv, Row * row, Row * prev)
1248 {
1249         if (prev && prev->par() == row->par()) {
1250                 breakAgainOneRow(bv, prev);
1251                 if (prev->next() != row) {
1252                         // breakAgainOneRow() has removed row_
1253                         need_break_row = prev;
1254                 } else {
1255                         need_break_row = row;
1256                 }
1257         } else if (!prev) {
1258                 need_break_row = firstrow;
1259         } else {
1260                 need_break_row = prev->next();
1261         }
1262         setCursor(bv, cursor.par(), cursor.pos());
1263         /* FIXME */
1264 }
1265
1266
1267 void LyXText::breakAgain(BufferView * bview, Row * row) const
1268 {
1269         bool not_ready = true;
1270
1271         do  {
1272                 // get the next breakpoint
1273                 pos_type z = nextBreakPoint(bview, row, workWidth(*bview));
1274                 Row * tmprow = row;
1275
1276                 if (z < row->par()->size()) {
1277                         if (!row->next() || (row->next() && row->next()->par() != row->par())) {
1278                                 // insert a new row
1279                                 ++z;
1280                                 insertRow(row, row->par(), z);
1281                                 row = row->next();
1282                                 row->height(0);
1283                         } else  {
1284                                 row = row->next();
1285                                 ++z;
1286                                 if (row->pos() == z)
1287                                         not_ready = false;     // the rest will not change
1288                                 else {
1289                                         row->pos(z);
1290                                 }
1291                         }
1292                 } else {
1293                         // if there are some rows too much, delete them
1294                         // only if you broke the whole paragraph!
1295                         Row * tmprow2 = row;
1296                         while (tmprow2->next() && tmprow2->next()->par() == row->par()) {
1297                                 tmprow2 = tmprow2->next();
1298                         }
1299                         while (tmprow2 != row) {
1300                                 tmprow2 = tmprow2->previous();
1301                                 removeRow(tmprow2->next());
1302                         }
1303                         not_ready = false;
1304                 }
1305
1306                 // set the dimensions of the row
1307                 tmprow->fill(fill(*bview, *tmprow, workWidth(*bview)));
1308                 setHeightOfRow(bview, tmprow);
1309         } while (not_ready);
1310 }
1311
1312
1313 // this is just a little changed version of break again
1314 void LyXText::breakAgainOneRow(BufferView * bview, Row * row)
1315 {
1316         // get the next breakpoint
1317         pos_type z = nextBreakPoint(bview, row, workWidth(*bview));
1318         Row * tmprow = row;
1319
1320         if (z < row->par()->size()) {
1321                 if (!row->next()
1322                     || (row->next() && row->next()->par() != row->par())) {
1323                         // insert a new row
1324                         ++z;
1325                         insertRow(row, row->par(), z);
1326                         row = row->next();
1327                         row->height(0);
1328                 } else  {
1329                         row = row->next();
1330                         ++z;
1331                         if (row->pos() != z)
1332                                 row->pos(z);
1333                 }
1334         } else {
1335                 // if there are some rows too much, delete them
1336                 // only if you broke the whole paragraph!
1337                 Row * tmprow2 = row;
1338                 while (tmprow2->next()
1339                        && tmprow2->next()->par() == row->par()) {
1340                         tmprow2 = tmprow2->next();
1341                 }
1342                 while (tmprow2 != row) {
1343                         tmprow2 = tmprow2->previous();
1344                         removeRow(tmprow2->next());
1345                 }
1346         }
1347
1348         // set the dimensions of the row
1349         tmprow->fill(fill(*bview, *tmprow, workWidth(*bview)));
1350         setHeightOfRow(bview, tmprow);
1351 }
1352
1353
1354 void LyXText::breakParagraph(BufferView * bview,
1355                              ParagraphList & paragraphs, char keep_layout)
1356 {
1357         // allow only if at start or end, or all previous is new text
1358         if (cursor.pos() && cursor.pos() != cursor.par()->size()
1359                 && cursor.par()->isChangeEdited(0, cursor.pos()))
1360                 return;
1361
1362         LyXTextClass const & tclass =
1363                 bview->buffer()->params.getLyXTextClass();
1364         LyXLayout_ptr const & layout = cursor.par()->layout();
1365
1366         // this is only allowed, if the current paragraph is not empty or caption
1367         // and if it has not the keepempty flag aktive
1368         if (cursor.par()->empty()
1369            && layout->labeltype != LABEL_SENSITIVE
1370            && !layout->keepempty)
1371                 return;
1372
1373         setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next());
1374
1375         // Always break behind a space
1376         //
1377         // It is better to erase the space (Dekel)
1378         if (cursor.pos() < cursor.par()->size()
1379              && cursor.par()->isLineSeparator(cursor.pos()))
1380            cursor.par()->erase(cursor.pos());
1381         // cursor.pos(cursor.pos() + 1);
1382
1383         // break the paragraph
1384         if (keep_layout)
1385                 keep_layout = 2;
1386         else
1387                 keep_layout = layout->isEnvironment();
1388
1389         // we need to set this before we insert the paragraph. IMO the
1390         // breakParagraph call should return a bool if it inserts the
1391         // paragraph before or behind and we should react on that one
1392         // but we can fix this in 1.3.0 (Jug 20020509)
1393         bool const isempty = (layout->keepempty && cursor.par()->empty());
1394         ::breakParagraph(bview->buffer()->params, paragraphs, cursor.par(), cursor.pos(),
1395                        keep_layout);
1396
1397         // well this is the caption hack since one caption is really enough
1398         if (layout->labeltype == LABEL_SENSITIVE) {
1399                 if (!cursor.pos())
1400                         // set to standard-layout
1401                         cursor.par()->applyLayout(tclass.defaultLayout());
1402                 else
1403                         // set to standard-layout
1404                         cursor.par()->next()->applyLayout(tclass.defaultLayout());
1405         }
1406
1407         // if the cursor is at the beginning of a row without prior newline,
1408         // move one row up!
1409         // This touches only the screen-update. Otherwise we would may have
1410         // an empty row on the screen
1411         if (cursor.pos() && !cursor.row()->par()->isNewline(cursor.row()->pos() - 1)
1412                          && cursor.row()->pos() == cursor.pos())
1413         {
1414                 cursorLeft(bview);
1415         }
1416
1417         status(bview, LyXText::NEED_MORE_REFRESH);
1418         refresh_row = cursor.row();
1419         refresh_y = cursor.y() - cursor.row()->baseline();
1420
1421         // Do not forget the special right address boxes
1422         if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1423                 while (refresh_row->previous() &&
1424                        refresh_row->previous()->par() == refresh_row->par())
1425                 {
1426                         refresh_row = refresh_row->previous();
1427                         refresh_y -= refresh_row->height();
1428                 }
1429         }
1430         removeParagraph(cursor.row());
1431
1432         // set the dimensions of the cursor row
1433         cursor.row()->fill(fill(*bview, *cursor.row(), workWidth(*bview)));
1434
1435         setHeightOfRow(bview, cursor.row());
1436
1437 #warning Trouble Point! (Lgb)
1438         // When ::breakParagraph is called from within an inset we must
1439         // ensure that the correct ParagraphList is used. Today that is not
1440         // the case and the Buffer::paragraphs is used. Not good. (Lgb)
1441         while (!cursor.par()->next()->empty()
1442           && cursor.par()->next()->isNewline(0))
1443            cursor.par()->next()->erase(0);
1444
1445         insertParagraph(bview, cursor.par()->next(), cursor.row());
1446
1447         updateCounters(bview);
1448
1449         // This check is necessary. Otherwise the new empty paragraph will
1450         // be deleted automatically. And it is more friendly for the user!
1451         if (cursor.pos() || isempty)
1452                 setCursor(bview, cursor.par()->next(), 0);
1453         else
1454                 setCursor(bview, cursor.par(), 0);
1455
1456         if (cursor.row()->next())
1457                 breakAgain(bview, cursor.row()->next());
1458
1459         need_break_row = 0;
1460 }
1461
1462
1463 // Just a macro to make some thing easier.
1464 void LyXText::redoParagraph(BufferView * bview) const
1465 {
1466         clearSelection();
1467         redoParagraphs(bview, cursor, cursor.par()->next());
1468         setCursorIntern(bview, cursor.par(), cursor.pos());
1469 }
1470
1471
1472 // insert a character, moves all the following breaks in the
1473 // same Paragraph one to the right and make a rebreak
1474 void LyXText::insertChar(BufferView * bview, char c)
1475 {
1476         setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next());
1477
1478         // When the free-spacing option is set for the current layout,
1479         // disable the double-space checking
1480
1481         bool const freeSpacing = cursor.row()->par()->layout()->free_spacing ||
1482                 cursor.row()->par()->isFreeSpacing();
1483
1484         if (lyxrc.auto_number) {
1485                 static string const number_operators = "+-/*";
1486                 static string const number_unary_operators = "+-";
1487                 static string const number_seperators = ".,:";
1488
1489                 if (current_font.number() == LyXFont::ON) {
1490                         if (!IsDigit(c) && !contains(number_operators, c) &&
1491                             !(contains(number_seperators, c) &&
1492                               cursor.pos() >= 1 &&
1493                               cursor.pos() < cursor.par()->size() &&
1494                               getFont(bview->buffer(),
1495                                       cursor.par(),
1496                                       cursor.pos()).number() == LyXFont::ON &&
1497                               getFont(bview->buffer(),
1498                                       cursor.par(),
1499                                       cursor.pos() - 1).number() == LyXFont::ON)
1500                            )
1501                                 number(bview); // Set current_font.number to OFF
1502                 } else if (IsDigit(c) &&
1503                            real_current_font.isVisibleRightToLeft()) {
1504                         number(bview); // Set current_font.number to ON
1505
1506                         if (cursor.pos() > 0) {
1507                                 char const c = cursor.par()->getChar(cursor.pos() - 1);
1508                                 if (contains(number_unary_operators, c) &&
1509                                     (cursor.pos() == 1 ||
1510                                      cursor.par()->isSeparator(cursor.pos() - 2) ||
1511                                      cursor.par()->isNewline(cursor.pos() - 2))
1512                                   ) {
1513                                         setCharFont(bview->buffer(),
1514                                                     cursor.par(),
1515                                                     cursor.pos() - 1,
1516                                                     current_font);
1517                                 } else if (contains(number_seperators, c) &&
1518                                            cursor.pos() >= 2 &&
1519                                            getFont(bview->buffer(),
1520                                                    cursor.par(),
1521                                                    cursor.pos() - 2).number() == LyXFont::ON) {
1522                                         setCharFont(bview->buffer(),
1523                                                     cursor.par(),
1524                                                     cursor.pos() - 1,
1525                                                     current_font);
1526                                 }
1527                         }
1528                 }
1529         }
1530
1531
1532         // First check, if there will be two blanks together or a blank at
1533         // the beginning of a paragraph.
1534         // I decided to handle blanks like normal characters, the main
1535         // difference are the special checks when calculating the row.fill
1536         // (blank does not count at the end of a row) and the check here
1537
1538         // The bug is triggered when we type in a description environment:
1539         // The current_font is not changed when we go from label to main text
1540         // and it should (along with realtmpfont) when we type the space.
1541         // CHECK There is a bug here! (Asger)
1542
1543         LyXFont realtmpfont = real_current_font;
1544         LyXFont rawtmpfont = current_font;
1545         // store the current font.  This is because of the use of cursor
1546         // movements. The moving cursor would refresh the current font
1547
1548         // Get the font that is used to calculate the baselineskip
1549         pos_type const lastpos = cursor.par()->size();
1550         LyXFont rawparfont =
1551                 cursor.par()->getFontSettings(bview->buffer()->params,
1552                                               lastpos - 1);
1553
1554         bool jumped_over_space = false;
1555
1556         if (!freeSpacing && IsLineSeparatorChar(c)) {
1557                 if ((cursor.pos() > 0
1558                      && cursor.par()->isLineSeparator(cursor.pos() - 1))
1559                     || (cursor.pos() > 0
1560                         && cursor.par()->isNewline(cursor.pos() - 1))
1561                     || (cursor.pos() == 0)) {
1562                         static bool sent_space_message = false;
1563                         if (!sent_space_message) {
1564                                 if (cursor.pos() == 0)
1565                                         bview->owner()->message(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial."));
1566                                 else
1567                                         bview->owner()->message(_("You cannot type two spaces this way. Please read the Tutorial."));
1568                                 sent_space_message = true;
1569                         }
1570                         charInserted();
1571                         return;
1572                 }
1573         } else if (IsNewlineChar(c)) {
1574                 if (cursor.pos() <= cursor.par()->beginningOfMainBody()) {
1575                         charInserted();
1576                         return;
1577                 }
1578                 // No newline at first position of a paragraph or behind labels.
1579                 // TeX does not allow that
1580
1581                 if (cursor.pos() < cursor.par()->size() &&
1582                     cursor.par()->isLineSeparator(cursor.pos()))
1583                         // newline always after a blank!
1584                         cursorRight(bview);
1585                 cursor.row()->fill(-1);        // to force a new break
1586         }
1587
1588         // the display inset stuff
1589         if (cursor.row()->par()->isInset(cursor.row()->pos())) {
1590                 Inset * inset = cursor.row()->par()->getInset(cursor.row()->pos());
1591                 if (inset && (inset->display() || inset->needFullRow())) {
1592                         // force a new break
1593                         cursor.row()->fill(-1); // to force a new break
1594                 }
1595         }
1596
1597         // get the cursor row fist
1598         Row * row = cursor.row();
1599         int y = cursor.y() - row->baseline();
1600         if (c != Paragraph::META_INSET) {
1601                 // Here case LyXText::InsertInset  already insertet the character
1602                 cursor.par()->insertChar(cursor.pos(), c);
1603         }
1604         setCharFont(bview->buffer(), cursor.par(), cursor.pos(), rawtmpfont);
1605
1606         if (!jumped_over_space) {
1607                 // refresh the positions
1608                 Row * tmprow = row;
1609                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
1610                         tmprow = tmprow->next();
1611                         tmprow->pos(tmprow->pos() + 1);
1612                 }
1613         }
1614
1615         // Is there a break one row above
1616         if (row->previous() && row->previous()->par() == row->par()
1617             && (cursor.par()->isLineSeparator(cursor.pos())
1618                 || cursor.par()->isNewline(cursor.pos())
1619                 || ((cursor.pos() < cursor.par()->size()) &&
1620                     cursor.par()->isInset(cursor.pos()+1))
1621                 || cursor.row()->fill() == -1))
1622         {
1623                 pos_type z = nextBreakPoint(bview,
1624                                                            row->previous(),
1625                                                            workWidth(*bview));
1626                 if (z >= row->pos()) {
1627                         row->pos(z + 1);
1628
1629                         // set the dimensions of the row above
1630                         row->previous()->fill(fill(*bview,
1631                                                    *row->previous(),
1632                                                    workWidth(*bview)));
1633
1634                         setHeightOfRow(bview, row->previous());
1635
1636                         y -= row->previous()->height();
1637                         refresh_y = y;
1638                         refresh_row = row->previous();
1639                         status(bview, LyXText::NEED_MORE_REFRESH);
1640
1641                         breakAgainOneRow(bview, row);
1642
1643                         current_font = rawtmpfont;
1644                         real_current_font = realtmpfont;
1645                         setCursor(bview, cursor.par(), cursor.pos() + 1,
1646                                   false, cursor.boundary());
1647                         // cursor MUST be in row now.
1648
1649                         if (row->next() && row->next()->par() == row->par())
1650                                 need_break_row = row->next();
1651                         else
1652                                 need_break_row = 0;
1653
1654                         // check, wether the last characters font has changed.
1655                         if (cursor.pos() && cursor.pos() == cursor.par()->size()
1656                             && rawparfont != rawtmpfont)
1657                                 redoHeightOfParagraph(bview, cursor);
1658
1659                         charInserted();
1660                         return;
1661                 }
1662         }
1663
1664         // recalculate the fill of the row
1665         if (row->fill() >= 0) {
1666                 // needed because a newline will set fill to -1. Otherwise
1667                 // we would not get a rebreak!
1668                 row->fill(fill(*bview, *row, workWidth(*bview)));
1669         }
1670
1671         if (c == Paragraph::META_INSET || row->fill() < 0) {
1672                 refresh_y = y;
1673                 refresh_row = row;
1674                 status(bview, LyXText::NEED_MORE_REFRESH);
1675                 breakAgainOneRow(bview, row);
1676                 // will the cursor be in another row now?
1677                 if (row->lastPos() <= cursor.pos() + 1 && row->next()) {
1678                         if (row->next() && row->next()->par() == row->par())
1679                                 // this should always be true
1680                                 row = row->next();
1681                         breakAgainOneRow(bview, row);
1682                 }
1683                 current_font = rawtmpfont;
1684                 real_current_font = realtmpfont;
1685
1686                 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1687                           cursor.boundary());
1688                 if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
1689                     != cursor.boundary())
1690                         setCursor(bview, cursor.par(), cursor.pos(), false,
1691                           !cursor.boundary());
1692                 if (row->next() && row->next()->par() == row->par())
1693                         need_break_row = row->next();
1694                 else
1695                         need_break_row = 0;
1696         } else {
1697                 refresh_y = y;
1698                 refresh_row = row;
1699
1700                 int const tmpheight = row->height();
1701                 setHeightOfRow(bview, row);
1702                 if (tmpheight == row->height())
1703                         status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1704                 else
1705                         status(bview, LyXText::NEED_MORE_REFRESH);
1706
1707                 current_font = rawtmpfont;
1708                 real_current_font = realtmpfont;
1709                 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1710                           cursor.boundary());
1711         }
1712
1713         // check, wether the last characters font has changed.
1714         if (cursor.pos() && cursor.pos() == cursor.par()->size()
1715             && rawparfont != rawtmpfont) {
1716                 redoHeightOfParagraph(bview, cursor);
1717         } else {
1718                 // now the special right address boxes
1719                 if (cursor.par()->layout()->margintype
1720                     == MARGIN_RIGHT_ADDRESS_BOX) {
1721                         redoDrawingOfParagraph(bview, cursor);
1722                 }
1723         }
1724
1725         charInserted();
1726 }
1727
1728
1729 void LyXText::charInserted()
1730 {
1731         // Here we could call FinishUndo for every 20 characters inserted.
1732         // This is from my experience how emacs does it.
1733         static unsigned int counter;
1734         if (counter < 20) {
1735                 ++counter;
1736         } else {
1737                 finishUndo();
1738                 counter = 0;
1739         }
1740 }
1741
1742
1743 void LyXText::prepareToPrint(BufferView * bview,
1744                              Row * row, float & x,
1745                              float & fill_separator,
1746                              float & fill_hfill,
1747                              float & fill_label_hfill,
1748                              bool bidi) const
1749 {
1750         float nlh;
1751         float ns;
1752
1753         float w = row->fill();
1754         fill_hfill = 0;
1755         fill_label_hfill = 0;
1756         fill_separator = 0;
1757         fill_label_hfill = 0;
1758
1759         bool const is_rtl =
1760                 row->par()->isRightToLeftPar(bview->buffer()->params);
1761         if (is_rtl) {
1762                 x = (workWidth(*bview) > 0)
1763                         ? rightMargin(*bview->buffer(), *row) : 0;
1764         } else
1765                 x = (workWidth(*bview) > 0)
1766                         ? leftMargin(bview, row) : 0;
1767
1768         // is there a manual margin with a manual label
1769         LyXLayout_ptr const & layout = row->par()->layout();
1770
1771         if (layout->margintype == MARGIN_MANUAL
1772             && layout->labeltype == LABEL_MANUAL) {
1773                 // one more since labels are left aligned
1774                 nlh = row->numberOfLabelHfills() + 1;
1775                 if (nlh && !row->par()->getLabelWidthString().empty()) {
1776                         fill_label_hfill = labelFill(*bview, *row) / nlh;
1777                 }
1778         }
1779
1780         // are there any hfills in the row?
1781         float const nh = row->numberOfHfills();
1782
1783         if (nh) {
1784                 if (w > 0)
1785                         fill_hfill = w / nh;
1786         // we don't have to look at the alignment if it is ALIGN_LEFT and
1787         // if the row is already larger then the permitted width as then
1788         // we force the LEFT_ALIGN'edness!
1789         } else if (static_cast<int>(row->width()) < workWidth(*bview)) {
1790                 // is it block, flushleft or flushright?
1791                 // set x how you need it
1792                 int align;
1793                 if (row->par()->params().align() == LYX_ALIGN_LAYOUT) {
1794                         align = layout->align;
1795                 } else {
1796                         align = row->par()->params().align();
1797                 }
1798
1799                 // center displayed insets
1800                 Inset * inset;
1801                 if (row->par()->isInset(row->pos())
1802                     && (inset=row->par()->getInset(row->pos()))
1803                     && (inset->display())) // || (inset->scroll() < 0)))
1804                     align = (inset->lyxCode() == Inset::MATHMACRO_CODE)
1805                         ? LYX_ALIGN_BLOCK : LYX_ALIGN_CENTER;
1806                 // ERT insets should always be LEFT ALIGNED on screen
1807                 inset = row->par()->inInset();
1808                 if (inset && inset->owner() &&
1809                         inset->owner()->lyxCode() == Inset::ERT_CODE)
1810                 {
1811                         align = LYX_ALIGN_LEFT;
1812                 }
1813
1814                 switch (align) {
1815             case LYX_ALIGN_BLOCK:
1816                         ns = row->numberOfSeparators();
1817                         if (ns && row->next() && row->next()->par() == row->par() &&
1818                             !(row->next()->par()->isNewline(row->next()->pos() - 1))
1819                             && !(row->next()->par()->isInset(row->next()->pos())
1820                                  && row->next()->par()->getInset(row->next()->pos())
1821                                  && row->next()->par()->getInset(row->next()->pos())->display())
1822                                 )
1823                         {
1824                                 fill_separator = w / ns;
1825                         } else if (is_rtl) {
1826                                 x += w;
1827                         }
1828                         break;
1829             case LYX_ALIGN_RIGHT:
1830                         x += w;
1831                         break;
1832             case LYX_ALIGN_CENTER:
1833                         x += w / 2;
1834                         break;
1835                 }
1836         }
1837         if (!bidi)
1838                 return;
1839
1840         computeBidiTables(bview->buffer(), row);
1841         if (is_rtl) {
1842                 pos_type main_body = row->par()->beginningOfMainBody();
1843                 pos_type last = row->lastPos();
1844
1845                 if (main_body > 0 &&
1846                     (main_body - 1 > last ||
1847                      !row->par()->isLineSeparator(main_body - 1))) {
1848                         x += font_metrics::width(layout->labelsep,
1849                                             getLabelFont(bview->buffer(), row->par()));
1850                         if (main_body - 1 <= last)
1851                                 x += fill_label_hfill;
1852                 }
1853         }
1854 }
1855
1856
1857 // important for the screen
1858
1859
1860 // the cursor set functions have a special mechanism. When they
1861 // realize, that you left an empty paragraph, they will delete it.
1862 // They also delete the corresponding row
1863
1864 void LyXText::cursorRightOneWord(BufferView * bview) const
1865 {
1866         // treat floats, HFills and Insets as words
1867         LyXCursor tmpcursor = cursor;
1868         // CHECK See comment on top of text.C
1869
1870         if (tmpcursor.pos() == tmpcursor.par()->size()
1871             && tmpcursor.par()->next()) {
1872                         tmpcursor.par(tmpcursor.par()->next());
1873                         tmpcursor.pos(0);
1874         } else {
1875                 int steps = 0;
1876
1877                 // Skip through initial nonword stuff.
1878                 while (tmpcursor.pos() < tmpcursor.par()->size() &&
1879                        ! tmpcursor.par()->isWord(tmpcursor.pos())) {
1880                   //    printf("Current pos1 %d", tmpcursor.pos()) ;
1881                         tmpcursor.pos(tmpcursor.pos() + 1);
1882                         ++steps;
1883                 }
1884                 // Advance through word.
1885                 while (tmpcursor.pos() < tmpcursor.par()->size() &&
1886                         tmpcursor.par()->isWord(tmpcursor.pos())) {
1887                   //     printf("Current pos2 %d", tmpcursor.pos()) ;
1888                         tmpcursor.pos(tmpcursor.pos() + 1);
1889                         ++steps;
1890                 }
1891         }
1892         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1893 }
1894
1895
1896 void LyXText::cursorTab(BufferView * bview) const
1897 {
1898         LyXCursor tmpcursor = cursor;
1899         while (tmpcursor.pos() < tmpcursor.par()->size()
1900            && !tmpcursor.par()->isNewline(tmpcursor.pos()))
1901         tmpcursor.pos(tmpcursor.pos() + 1);
1902
1903         if (tmpcursor.pos() == tmpcursor.par()->size()) {
1904                 if (tmpcursor.par()->next()) {
1905                         tmpcursor.par(tmpcursor.par()->next());
1906                         tmpcursor.pos(0);
1907                 }
1908         } else
1909                 tmpcursor.pos(tmpcursor.pos() + 1);
1910         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1911 }
1912
1913
1914 // Skip initial whitespace at end of word and move cursor to *start*
1915 // of prior word, not to end of next prior word.
1916 void LyXText::cursorLeftOneWord(BufferView * bview)  const
1917 {
1918         LyXCursor tmpcursor = cursor;
1919         cursorLeftOneWord(tmpcursor);
1920         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
1921 }
1922
1923
1924 void LyXText::cursorLeftOneWord(LyXCursor & cur) const
1925 {
1926         // treat HFills, floats and Insets as words
1927         cur = cursor;
1928         while (cur.pos()
1929                && (cur.par()->isSeparator(cur.pos() - 1)
1930                    || cur.par()->isKomma(cur.pos() - 1))
1931                && !(cur.par()->isHfill(cur.pos() - 1)
1932                     || cur.par()->isInset(cur.pos() - 1)))
1933                 cur.pos(cur.pos() - 1);
1934
1935         if (cur.pos()
1936             && (cur.par()->isInset(cur.pos() - 1)
1937                 || cur.par()->isHfill(cur.pos() - 1))) {
1938                 cur.pos(cur.pos() - 1);
1939         } else if (!cur.pos()) {
1940                 if (cur.par()->previous()) {
1941                         cur.par(cur.par()->previous());
1942                         cur.pos(cur.par()->size());
1943                 }
1944         } else {                // Here, cur != 0
1945                 while (cur.pos() > 0 &&
1946                        cur.par()->isWord(cur.pos() - 1))
1947                         cur.pos(cur.pos() - 1);
1948         }
1949 }
1950
1951
1952 // Select current word. This depends on behaviour of
1953 // CursorLeftOneWord(), so it is patched as well.
1954 void LyXText::getWord(LyXCursor & from, LyXCursor & to,
1955                       word_location const loc) const
1956 {
1957         // first put the cursor where we wana start to select the word
1958         from = cursor;
1959         switch (loc) {
1960         case WHOLE_WORD_STRICT:
1961                 if (cursor.pos() == 0 || cursor.pos() == cursor.par()->size()
1962                     || cursor.par()->isSeparator(cursor.pos())
1963                     || cursor.par()->isKomma(cursor.pos())
1964                     || cursor.par()->isSeparator(cursor.pos() - 1)
1965                     || cursor.par()->isKomma(cursor.pos() - 1)) {
1966                         to = from;
1967                         return;
1968                 }
1969                 // no break here, we go to the next
1970
1971         case WHOLE_WORD:
1972                 // Move cursor to the beginning, when not already there.
1973                 if (from.pos() && !from.par()->isSeparator(from.pos() - 1)
1974                     && !from.par()->isKomma(from.pos() - 1))
1975                         cursorLeftOneWord(from);
1976                 break;
1977         case PREVIOUS_WORD:
1978                 // always move the cursor to the beginning of previous word
1979                 cursorLeftOneWord(from);
1980                 break;
1981         case NEXT_WORD:
1982                 lyxerr << "LyXText::getWord: NEXT_WORD not implemented yet\n";
1983                 break;
1984         case PARTIAL_WORD:
1985                 break;
1986         }
1987         to = from;
1988         while (to.pos() < to.par()->size()
1989                && !to.par()->isSeparator(to.pos())
1990                && !to.par()->isKomma(to.pos())
1991                && !to.par()->isHfill(to.pos())
1992                && !to.par()->isInset(to.pos()))
1993         {
1994                 to.pos(to.pos() + 1);
1995         }
1996 }
1997
1998
1999 void LyXText::selectWord(BufferView * bview, word_location const loc)
2000 {
2001         LyXCursor from;
2002         LyXCursor to;
2003         getWord(from, to, loc);
2004         if (cursor != from)
2005                 setCursor(bview, from.par(), from.pos());
2006         if (to == from)
2007                 return;
2008         selection.cursor = cursor;
2009         setCursor(bview, to.par(), to.pos());
2010         setSelection(bview);
2011 }
2012
2013
2014 // Select the word currently under the cursor when no
2015 // selection is currently set
2016 bool LyXText::selectWordWhenUnderCursor(BufferView * bview,
2017                                         word_location const loc)
2018 {
2019         if (!selection.set()) {
2020                 selectWord(bview, loc);
2021                 return selection.set();
2022         }
2023         return false;
2024 }
2025
2026
2027 void LyXText::acceptChange(BufferView * bv)
2028 {
2029         if (!selection.set() && cursor.par()->size())
2030                 return;
2031
2032         bv->hideCursor();
2033
2034         if (selection.start.par() == selection.end.par()) {
2035                 LyXCursor & startc = selection.start;
2036                 LyXCursor & endc = selection.end;
2037                 setUndo(bv, Undo::INSERT, startc.par(), startc.par()->next());
2038                 startc.par()->acceptChange(startc.pos(), endc.pos());
2039                 finishUndo();
2040                 clearSelection();
2041                 redoParagraphs(bv, startc, startc.par()->next());
2042                 setCursorIntern(bv, startc.par(), 0);
2043         }
2044 #warning handle multi par selection
2045 }
2046
2047
2048 void LyXText::rejectChange(BufferView * bv)
2049 {
2050         if (!selection.set() && cursor.par()->size())
2051                 return;
2052
2053         bv->hideCursor();
2054
2055         if (selection.start.par() == selection.end.par()) {
2056                 LyXCursor & startc = selection.start;
2057                 LyXCursor & endc = selection.end;
2058                 setUndo(bv, Undo::INSERT, startc.par(), startc.par()->next());
2059                 startc.par()->rejectChange(startc.pos(), endc.pos());
2060                 finishUndo();
2061                 clearSelection();
2062                 redoParagraphs(bv, startc, startc.par()->next());
2063                 setCursorIntern(bv, startc.par(), 0);
2064         }
2065 #warning handle multi par selection
2066 }
2067
2068
2069 // This function is only used by the spellchecker for NextWord().
2070 // It doesn't handle LYX_ACCENTs and probably never will.
2071 WordLangTuple const
2072 LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const
2073 {
2074         if (the_locking_inset) {
2075                 WordLangTuple word = the_locking_inset->selectNextWordToSpellcheck(bview, value);
2076                 if (!word.word().empty()) {
2077                         value += float(cursor.y());
2078                         value /= float(height);
2079                         return word;
2080                 }
2081                 // we have to go on checking so move cursor to the next char
2082                 if (cursor.pos() == cursor.par()->size()) {
2083                         if (!cursor.par()->next())
2084                                 return word;
2085                         cursor.par(cursor.par()->next());
2086                         cursor.pos(0);
2087                 } else
2088                         cursor.pos(cursor.pos() + 1);
2089         }
2090         Paragraph * tmppar = cursor.par();
2091
2092         // If this is not the very first word, skip rest of
2093         // current word because we are probably in the middle
2094         // of a word if there is text here.
2095         if (cursor.pos() || cursor.par()->previous()) {
2096                 while (cursor.pos() < cursor.par()->size()
2097                        && cursor.par()->isLetter(cursor.pos()))
2098                         cursor.pos(cursor.pos() + 1);
2099         }
2100
2101         // Now, skip until we have real text (will jump paragraphs)
2102         while (1) {
2103                 Paragraph * cpar(cursor.par());
2104                 pos_type const cpos(cursor.pos());
2105
2106                 if (cpos == cpar->size()) {
2107                         if (cpar->next()) {
2108                                 cursor.par(cpar->next());
2109                                 cursor.pos(0);
2110                                 continue;
2111                         }
2112                         break;
2113                 }
2114
2115                 bool const is_bad_inset(cpar->isInset(cpos)
2116                         && !cpar->getInset(cpos)->allowSpellcheck());
2117
2118                 if (cpar->isLetter(cpos) && !isDeletedText(*cpar, cpos)
2119                         && !is_bad_inset)
2120                         break;
2121
2122                 cursor.pos(cpos + 1);
2123         }
2124
2125         // now check if we hit an inset so it has to be a inset containing text!
2126         if (cursor.pos() < cursor.par()->size() &&
2127             cursor.par()->isInset(cursor.pos())) {
2128                 // lock the inset!
2129                 cursor.par()->getInset(cursor.pos())->edit(bview);
2130                 // now call us again to do the above trick
2131                 // but obviously we have to start from down below ;)
2132                 return bview->text->selectNextWordToSpellcheck(bview, value);
2133         }
2134
2135         // Update the value if we changed paragraphs
2136         if (cursor.par() != tmppar) {
2137                 setCursor(bview, cursor.par(), cursor.pos());
2138                 value = float(cursor.y())/float(height);
2139         }
2140
2141         // Start the selection from here
2142         selection.cursor = cursor;
2143
2144         string lang_code(
2145                 getFont(bview->buffer(), cursor.par(), cursor.pos())
2146                         .language()->code());
2147         // and find the end of the word (insets like optional hyphens
2148         // and ligature break are part of a word)
2149         while (cursor.pos() < cursor.par()->size()
2150                && cursor.par()->isLetter(cursor.pos())
2151                && !isDeletedText(*cursor.par(), cursor.pos()))
2152                 cursor.pos(cursor.pos() + 1);
2153
2154         // Finally, we copy the word to a string and return it
2155         string str;
2156         if (selection.cursor.pos() < cursor.pos()) {
2157                 pos_type i;
2158                 for (i = selection.cursor.pos(); i < cursor.pos(); ++i) {
2159                         if (!cursor.par()->isInset(i))
2160                                 str += cursor.par()->getChar(i);
2161                 }
2162         }
2163         return WordLangTuple(str, lang_code);
2164 }
2165
2166
2167 // This one is also only for the spellchecker
2168 void LyXText::selectSelectedWord(BufferView * bview)
2169 {
2170         if (the_locking_inset) {
2171                 the_locking_inset->selectSelectedWord(bview);
2172                 return;
2173         }
2174         // move cursor to the beginning
2175         setCursor(bview, selection.cursor.par(), selection.cursor.pos());
2176
2177         // set the sel cursor
2178         selection.cursor = cursor;
2179
2180         // now find the end of the word
2181         while (cursor.pos() < cursor.par()->size()
2182                && (cursor.par()->isLetter(cursor.pos())))
2183                 cursor.pos(cursor.pos() + 1);
2184
2185         setCursor(bview, cursor.par(), cursor.pos());
2186
2187         // finally set the selection
2188         setSelection(bview);
2189 }
2190
2191
2192 // Delete from cursor up to the end of the current or next word.
2193 void LyXText::deleteWordForward(BufferView * bview)
2194 {
2195         if (cursor.par()->empty())
2196                 cursorRight(bview);
2197         else {
2198                 LyXCursor tmpcursor = cursor;
2199                 tmpcursor.row(0); // ??
2200                 selection.set(true); // to avoid deletion
2201                 cursorRightOneWord(bview);
2202                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2203                 selection.cursor = cursor;
2204                 cursor = tmpcursor;
2205                 setSelection(bview);
2206
2207                 // Great, CutSelection() gets rid of multiple spaces.
2208                 cutSelection(bview, true, false);
2209         }
2210 }
2211
2212
2213 // Delete from cursor to start of current or prior word.
2214 void LyXText::deleteWordBackward(BufferView * bview)
2215 {
2216         if (cursor.par()->empty())
2217                 cursorLeft(bview);
2218         else {
2219                 LyXCursor tmpcursor = cursor;
2220                 tmpcursor.row(0); // ??
2221                 selection.set(true); // to avoid deletion
2222                 cursorLeftOneWord(bview);
2223                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2224                 selection.cursor = cursor;
2225                 cursor = tmpcursor;
2226                 setSelection(bview);
2227                 cutSelection(bview, true, false);
2228         }
2229 }
2230
2231
2232 // Kill to end of line.
2233 void LyXText::deleteLineForward(BufferView * bview)
2234 {
2235         if (cursor.par()->empty())
2236                 // Paragraph is empty, so we just go to the right
2237                 cursorRight(bview);
2238         else {
2239                 LyXCursor tmpcursor = cursor;
2240                 // We can't store the row over a regular setCursor
2241                 // so we set it to 0 and reset it afterwards.
2242                 tmpcursor.row(0); // ??
2243                 selection.set(true); // to avoid deletion
2244                 cursorEnd(bview);
2245                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2246                 selection.cursor = cursor;
2247                 cursor = tmpcursor;
2248                 setSelection(bview);
2249                 // What is this test for ??? (JMarc)
2250                 if (!selection.set()) {
2251                         deleteWordForward(bview);
2252                 } else {
2253                         cutSelection(bview, true, false);
2254                 }
2255         }
2256 }
2257
2258
2259 void LyXText::changeCase(BufferView & bview, LyXText::TextCase action)
2260 {
2261         LyXCursor from;
2262         LyXCursor to;
2263
2264         if (selection.set()) {
2265                 from = selection.start;
2266                 to = selection.end;
2267         } else {
2268                 getWord(from, to, PARTIAL_WORD);
2269                 setCursor(&bview, to.par(), to.pos() + 1);
2270         }
2271
2272         lyx::Assert(from <= to);
2273
2274         setUndo(&bview, Undo::FINISH, from.par(), to.par()->next());
2275
2276         pos_type pos = from.pos();
2277         Paragraph * par = from.par();
2278
2279         while (par && (pos != to.pos() || par != to.par())) {
2280                 if (pos == par->size()) {
2281                         par = par->next();
2282                         pos = 0;
2283                         continue;
2284                 }
2285                 unsigned char c = par->getChar(pos);
2286                 if (!IsInsetChar(c) && !IsHfillChar(c)) {
2287                         switch (action) {
2288                         case text_lowercase:
2289                                 c = lowercase(c);
2290                                 break;
2291                         case text_capitalization:
2292                                 c = uppercase(c);
2293                                 action = text_lowercase;
2294                                 break;
2295                         case text_uppercase:
2296                                 c = uppercase(c);
2297                                 break;
2298                         }
2299                 }
2300 #warning changes
2301                 par->setChar(pos, c);
2302                 checkParagraph(&bview, par, pos);
2303
2304                 ++pos;
2305         }
2306         if (to.row() != from.row()) {
2307                 refresh_y = from.y() - from.row()->baseline();
2308                 refresh_row = from.row();
2309                 status(&bview, LyXText::NEED_MORE_REFRESH);
2310         }
2311 }
2312
2313
2314 void LyXText::transposeChars(BufferView & bview)
2315 {
2316         Paragraph * tmppar = cursor.par();
2317
2318         setUndo(&bview, Undo::FINISH, tmppar, tmppar->next());
2319
2320         pos_type tmppos = cursor.pos();
2321
2322         // First decide if it is possible to transpose at all
2323
2324         if (tmppos == 0 || tmppos == tmppar->size())
2325                 return;
2326
2327         if (isDeletedText(*tmppar, tmppos - 1)
2328                 || isDeletedText(*tmppar, tmppos))
2329                 return;
2330
2331         unsigned char c1 = tmppar->getChar(tmppos);
2332         unsigned char c2 = tmppar->getChar(tmppos - 1);
2333
2334         // We should have an implementation that handles insets
2335         // as well, but that will have to come later. (Lgb)
2336         if (c1 == Paragraph::META_INSET || c2 == Paragraph::META_INSET)
2337                 return;
2338
2339         bool const erased = tmppar->erase(tmppos - 1, tmppos + 1);
2340         pos_type const ipos(erased ? tmppos - 1 : tmppos + 1);
2341
2342         tmppar->insertChar(ipos, c1);
2343         tmppar->insertChar(ipos + 1, c2);
2344
2345         checkParagraph(&bview, tmppar, tmppos);
2346 }
2347
2348
2349 void LyXText::Delete(BufferView * bview)
2350 {
2351         // this is a very easy implementation
2352
2353         LyXCursor old_cursor = cursor;
2354         int const old_cur_par_id = old_cursor.par()->id();
2355         int const old_cur_par_prev_id = old_cursor.par()->previous() ?
2356                 old_cursor.par()->previous()->id() : -1;
2357
2358         // just move to the right
2359         cursorRight(bview);
2360
2361         // CHECK Look at the comment here.
2362         // This check is not very good...
2363         // The cursorRightIntern calls DeleteEmptyParagrapgMechanism
2364         // and that can very well delete the par or par->previous in
2365         // old_cursor. Will a solution where we compare paragraph id's
2366         //work better?
2367         if ((cursor.par()->previous() ? cursor.par()->previous()->id() : -1)
2368             == old_cur_par_prev_id
2369             && cursor.par()->id() != old_cur_par_id) {
2370                 // delete-empty-paragraph-mechanism has done it
2371                 return;
2372         }
2373
2374         // if you had success make a backspace
2375         if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2376                 LyXCursor tmpcursor = cursor;
2377                 // to make sure undo gets the right cursor position
2378                 cursor = old_cursor;
2379                 setUndo(bview, Undo::DELETE,
2380                         cursor.par(), cursor.par()->next());
2381                 cursor = tmpcursor;
2382                 backspace(bview);
2383         }
2384 }
2385
2386
2387 void LyXText::backspace(BufferView * bview)
2388 {
2389         // Get the font that is used to calculate the baselineskip
2390         pos_type lastpos = cursor.par()->size();
2391         LyXFont rawparfont =
2392                 cursor.par()->getFontSettings(bview->buffer()->params,
2393                                               lastpos - 1);
2394
2395         if (cursor.pos() == 0) {
2396                 // The cursor is at the beginning of a paragraph,
2397                 // so the the backspace will collapse two paragraphs into one.
2398
2399                 // but it's not allowed unless it's new
2400                 if (cursor.par()->isChangeEdited(0, cursor.par()->size()))
2401                         return;
2402
2403                 // we may paste some paragraphs
2404
2405                 // is it an empty paragraph?
2406
2407                 if ((lastpos == 0
2408                      || (lastpos == 1 && cursor.par()->isSeparator(0)))) {
2409                         // This is an empty paragraph and we delete it just by moving the cursor one step
2410                         // left and let the DeleteEmptyParagraphMechanism handle the actual deletion
2411                         // of the paragraph.
2412
2413                         if (cursor.par()->previous()) {
2414                                 Paragraph * tmppar = cursor.par()->previous();
2415                                 if (cursor.par()->layout() == tmppar->layout()
2416                                     && cursor.par()->getAlign() == tmppar->getAlign()) {
2417                                         // Inherit bottom DTD from the paragraph below.
2418                                         // (the one we are deleting)
2419                                         tmppar->params().lineBottom(cursor.par()->params().lineBottom());
2420                                         tmppar->params().spaceBottom(cursor.par()->params().spaceBottom());
2421                                         tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom());
2422                                 }
2423
2424                                 cursorLeft(bview);
2425
2426                                 // the layout things can change the height of a row !
2427                                 int const tmpheight = cursor.row()->height();
2428                                 setHeightOfRow(bview, cursor.row());
2429                                 if (cursor.row()->height() != tmpheight) {
2430                                         refresh_y = cursor.y() - cursor.row()->baseline();
2431                                         refresh_row = cursor.row();
2432                                         status(bview, LyXText::NEED_MORE_REFRESH);
2433                                 }
2434                                 return;
2435                         }
2436                 }
2437
2438                 if (cursor.par()->previous()) {
2439                         setUndo(bview, Undo::DELETE,
2440                                 cursor.par()->previous(), cursor.par()->next());
2441                 }
2442
2443                 Paragraph * tmppar = cursor.par();
2444                 Row * tmprow = cursor.row();
2445
2446                 // We used to do cursorLeftIntern() here, but it is
2447                 // not a good idea since it triggers the auto-delete
2448                 // mechanism. So we do a cursorLeftIntern()-lite,
2449                 // without the dreaded mechanism. (JMarc)
2450                 if (cursor.par()->previous()) {
2451                         // steps into the above paragraph.
2452                         setCursorIntern(bview, cursor.par()->previous(),
2453                                         cursor.par()->previous()->size(),
2454                                         false);
2455                 }
2456
2457                 // Pasting is not allowed, if the paragraphs have different
2458                 // layout. I think it is a real bug of all other
2459                 // word processors to allow it. It confuses the user.
2460                 // Even so with a footnote paragraph and a non-footnote
2461                 // paragraph. I will not allow pasting in this case,
2462                 // because the user would be confused if the footnote behaves
2463                 // different wether it is open or closed.
2464
2465                 //      Correction: Pasting is always allowed with standard-layout
2466                 LyXTextClass const & tclass =
2467                         bview->buffer()->params.getLyXTextClass();
2468
2469                 if (cursor.par() != tmppar
2470                     && (cursor.par()->layout() == tmppar->layout()
2471                         || tmppar->layout() == tclass.defaultLayout())
2472                     && cursor.par()->getAlign() == tmppar->getAlign()) {
2473                         removeParagraph(tmprow);
2474                         removeRow(tmprow);
2475                         mergeParagraph(bview->buffer()->params, bview->buffer()->paragraphs, cursor.par());
2476
2477                         if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1))
2478                                 ; //cursor.par()->insertChar(cursor.pos(), ' ');
2479                         // strangely enough it seems that commenting out the line above removes
2480                         // most or all of the segfaults. I will however also try to move the
2481                         // two Remove... lines in front of the PasteParagraph too.
2482                         else
2483                                 if (cursor.pos())
2484                                         cursor.pos(cursor.pos() - 1);
2485
2486                         status(bview, LyXText::NEED_MORE_REFRESH);
2487                         refresh_row = cursor.row();
2488                         refresh_y = cursor.y() - cursor.row()->baseline();
2489
2490                         // remove the lost paragraph
2491                         // This one is not safe, since the paragraph that the tmprow and the
2492                         // following rows belong to has been deleted by the PasteParagraph
2493                         // above. The question is... could this be moved in front of the
2494                         // PasteParagraph?
2495                         //RemoveParagraph(tmprow);
2496                         //RemoveRow(tmprow);
2497
2498                         // This rebuilds the rows.
2499                         appendParagraph(bview, cursor.row());
2500                         updateCounters(bview);
2501
2502                         // the row may have changed, block, hfills etc.
2503                         setCursor(bview, cursor.par(), cursor.pos(), false);
2504                 }
2505         } else {
2506                 // this is the code for a normal backspace, not pasting
2507                 // any paragraphs
2508                 setUndo(bview, Undo::DELETE,
2509                         cursor.par(), cursor.par()->next());
2510                 // We used to do cursorLeftIntern() here, but it is
2511                 // not a good idea since it triggers the auto-delete
2512                 // mechanism. So we do a cursorLeftIntern()-lite,
2513                 // without the dreaded mechanism. (JMarc)
2514                 setCursorIntern(bview, cursor.par(), cursor.pos()- 1,
2515                                 false, cursor.boundary());
2516
2517                 if (cursor.par()->isInset(cursor.pos())) {
2518                         // force complete redo when erasing display insets
2519                         // this is a cruel method but safe..... Matthias
2520                         if (cursor.par()->getInset(cursor.pos())->display() ||
2521                             cursor.par()->getInset(cursor.pos())->needFullRow()) {
2522                                 cursor.par()->erase(cursor.pos());
2523                                 redoParagraph(bview);
2524                                 return;
2525                         }
2526                 }
2527
2528                 Row * row = cursor.row();
2529                 int y = cursor.y() - row->baseline();
2530                 pos_type z;
2531                 // remember that a space at the end of a row doesnt count
2532                 // when calculating the fill
2533                 if (cursor.pos() < row->lastPos() ||
2534                     !cursor.par()->isLineSeparator(cursor.pos())) {
2535                         row->fill(row->fill() + singleWidth(bview,
2536                                                             cursor.par(),
2537                                                             cursor.pos()));
2538                 }
2539
2540                 // some special code when deleting a newline. This is similar
2541                 // to the behavior when pasting paragraphs
2542                 if (cursor.pos() && cursor.par()->isNewline(cursor.pos())) {
2543                         cursor.par()->erase(cursor.pos());
2544                         // refresh the positions
2545                         Row * tmprow = row;
2546                         while (tmprow->next() && tmprow->next()->par() == row->par()) {
2547                                 tmprow = tmprow->next();
2548                                 tmprow->pos(tmprow->pos() - 1);
2549                         }
2550                         if (cursor.par()->isLineSeparator(cursor.pos() - 1))
2551                                 cursor.pos(cursor.pos() - 1);
2552
2553                         if (cursor.pos() < cursor.par()->size()
2554                             && !cursor.par()->isSeparator(cursor.pos())) {
2555                                 cursor.par()->insertChar(cursor.pos(), ' ');
2556                                 setCharFont(bview->buffer(), cursor.par(),
2557                                             cursor.pos(), current_font);
2558                                 // refresh the positions
2559                                 tmprow = row;
2560                                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
2561                                         tmprow = tmprow->next();
2562                                         tmprow->pos(tmprow->pos() + 1);
2563                                 }
2564                         }
2565                 } else {
2566                         cursor.par()->erase(cursor.pos());
2567
2568                         // refresh the positions
2569                         Row * tmprow = row;
2570                         while (tmprow->next()
2571                                && tmprow->next()->par() == row->par()) {
2572                                 tmprow = tmprow->next();
2573                                 tmprow->pos(tmprow->pos() - 1);
2574                         }
2575
2576                         // delete newlines at the beginning of paragraphs
2577                         while (!cursor.par()->empty() &&
2578                                cursor.par()->isNewline(cursor.pos()) &&
2579                                cursor.pos() == cursor.par()->beginningOfMainBody()) {
2580                                 cursor.par()->erase(cursor.pos());
2581                                 // refresh the positions
2582                                 tmprow = row;
2583                                 while (tmprow->next() &&
2584                                        tmprow->next()->par() == row->par()) {
2585                                         tmprow = tmprow->next();
2586                                         tmprow->pos(tmprow->pos() - 1);
2587                                 }
2588                         }
2589                 }
2590
2591                 // is there a break one row above
2592                 if (row->previous() && row->previous()->par() == row->par()) {
2593                         z = nextBreakPoint(bview, row->previous(),
2594                                            workWidth(*bview));
2595                         if (z >= row->pos()) {
2596                                 row->pos(z + 1);
2597
2598                                 Row * tmprow = row->previous();
2599
2600                                 // maybe the current row is now empty
2601                                 if (row->pos() >= row->par()->size()) {
2602                                         // remove it
2603                                         removeRow(row);
2604                                         need_break_row = 0;
2605                                 } else {
2606                                         breakAgainOneRow(bview, row);
2607                                         if (row->next() && row->next()->par() == row->par())
2608                                                 need_break_row = row->next();
2609                                         else
2610                                                 need_break_row = 0;
2611                                 }
2612
2613                                 // set the dimensions of the row above
2614                                 y -= tmprow->height();
2615                                 tmprow->fill(fill(*bview, *tmprow, workWidth(*bview)));
2616                                 setHeightOfRow(bview, tmprow);
2617
2618                                 refresh_y = y;
2619                                 refresh_row = tmprow;
2620                                 status(bview, LyXText::NEED_MORE_REFRESH);
2621                                 setCursor(bview, cursor.par(), cursor.pos(),
2622                                           false, cursor.boundary());
2623                                 //current_font = rawtmpfont;
2624                                 //real_current_font = realtmpfont;
2625                                 // check, whether the last character's font has changed.
2626                                 if (rawparfont !=
2627                                     cursor.par()->getFontSettings(bview->buffer()->params,
2628                                                                   cursor.par()->size() - 1))
2629                                         redoHeightOfParagraph(bview, cursor);
2630                                 return;
2631                         }
2632                 }
2633
2634                 // break the cursor row again
2635                 if (row->next() && row->next()->par() == row->par() &&
2636                     (row->lastPos() == row->par()->size() - 1 ||
2637                      nextBreakPoint(bview, row, workWidth(*bview)) != row->lastPos())) {
2638
2639                         // it can happen that a paragraph loses one row
2640                         // without a real breakup. This is when a word
2641                         // is to long to be broken. Well, I don t care this
2642                         // hack ;-)
2643                         if (row->lastPos() == row->par()->size() - 1)
2644                                 removeRow(row->next());
2645
2646                         refresh_y = y;
2647                         refresh_row = row;
2648                         status(bview, LyXText::NEED_MORE_REFRESH);
2649
2650                         breakAgainOneRow(bview, row);
2651                         // will the cursor be in another row now?
2652                         if (row->next() && row->next()->par() == row->par() &&
2653                             row->lastPos() <= cursor.pos()) {
2654                                 row = row->next();
2655                                 breakAgainOneRow(bview, row);
2656                         }
2657
2658                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2659
2660                         if (row->next() && row->next()->par() == row->par())
2661                                 need_break_row = row->next();
2662                         else
2663                                 need_break_row = 0;
2664                 } else  {
2665                         // set the dimensions of the row
2666                         row->fill(fill(*bview, *row, workWidth(*bview)));
2667                         int const tmpheight = row->height();
2668                         setHeightOfRow(bview, row);
2669                         if (tmpheight == row->height())
2670                                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2671                         else
2672                                 status(bview, LyXText::NEED_MORE_REFRESH);
2673                         refresh_y = y;
2674                         refresh_row = row;
2675                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2676                 }
2677         }
2678
2679         // current_font = rawtmpfont;
2680         // real_current_font = realtmpfont;
2681
2682         if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
2683             != cursor.boundary())
2684                 setCursor(bview, cursor.par(), cursor.pos(), false,
2685                           !cursor.boundary());
2686
2687         lastpos = cursor.par()->size();
2688         if (cursor.pos() == lastpos)
2689                 setCurrentFont(bview);
2690
2691         // check, whether the last characters font has changed.
2692         if (rawparfont !=
2693             cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1)) {
2694                 redoHeightOfParagraph(bview, cursor);
2695         } else {
2696                 // now the special right address boxes
2697                 if (cursor.par()->layout()->margintype
2698                     == MARGIN_RIGHT_ADDRESS_BOX) {
2699                         redoDrawingOfParagraph(bview, cursor);
2700                 }
2701         }
2702 }
2703
2704
2705 // returns the column near the specified x-coordinate of the row
2706 // x is set to the real beginning of this column
2707 pos_type
2708 LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
2709                         bool & boundary) const
2710 {
2711         float tmpx = 0.0;
2712         float fill_separator;
2713         float fill_hfill;
2714         float fill_label_hfill;
2715
2716         prepareToPrint(bview, row, tmpx, fill_separator,
2717                        fill_hfill, fill_label_hfill);
2718
2719         pos_type vc = row->pos();
2720         pos_type last = row->lastPrintablePos();
2721         pos_type c = 0;
2722
2723         LyXLayout_ptr const & layout = row->par()->layout();
2724
2725         bool left_side = false;
2726
2727         pos_type main_body = row->par()->beginningOfMainBody();
2728         float last_tmpx = tmpx;
2729
2730         if (main_body > 0 &&
2731             (main_body - 1 > last ||
2732              !row->par()->isLineSeparator(main_body - 1)))
2733                 main_body = 0;
2734
2735         // check for empty row
2736         if (!row->par()->size()) {
2737                 x = int(tmpx);
2738                 return 0;
2739         }
2740
2741         while (vc <= last && tmpx <= x) {
2742                 c = vis2log(vc);
2743                 last_tmpx = tmpx;
2744                 if (main_body > 0 && c == main_body-1) {
2745                         tmpx += fill_label_hfill +
2746                                 font_metrics::width(layout->labelsep,
2747                                                getLabelFont(bview->buffer(), row->par()));
2748                         if (row->par()->isLineSeparator(main_body - 1))
2749                                 tmpx -= singleWidth(bview, row->par(), main_body-1);
2750                 }
2751
2752                 if (row->hfillExpansion(c)) {
2753                         tmpx += singleWidth(bview, row->par(), c);
2754                         if (c >= main_body)
2755                                 tmpx += fill_hfill;
2756                         else
2757                                 tmpx += fill_label_hfill;
2758                 } else if (row->par()->isSeparator(c)) {
2759                         tmpx += singleWidth(bview, row->par(), c);
2760                         if (c >= main_body)
2761                                 tmpx+= fill_separator;
2762                 } else {
2763                         tmpx += singleWidth(bview, row->par(), c);
2764                 }
2765                 ++vc;
2766         }
2767
2768         if ((tmpx + last_tmpx) / 2 > x) {
2769                 tmpx = last_tmpx;
2770                 left_side = true;
2771         }
2772
2773         if (vc > last + 1)  // This shouldn't happen.
2774                 vc = last + 1;
2775
2776         boundary = false;
2777         bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
2778                                          // some speedup if rtl_support=false
2779                 && (!row->next() || row->next()->par() != row->par());
2780         bool const rtl = (lastrow)
2781                 ? row->par()->isRightToLeftPar(bview->buffer()->params)
2782                 : false; // If lastrow is false, we don't need to compute
2783                          // the value of rtl.
2784
2785         if (lastrow &&
2786                  ((rtl &&  left_side && vc == row->pos() && x < tmpx - 5) ||
2787                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5)))
2788                 c = last + 1;
2789         else if (vc == row->pos()) {
2790                 c = vis2log(vc);
2791                 if (bidi_level(c) % 2 == 1)
2792                         ++c;
2793         } else {
2794                 c = vis2log(vc - 1);
2795                 bool const rtl = (bidi_level(c) % 2 == 1);
2796                 if (left_side == rtl) {
2797                         ++c;
2798                         boundary = isBoundary(bview->buffer(), row->par(), c);
2799                 }
2800         }
2801
2802         if (row->pos() <= last && c > last
2803             && row->par()->isNewline(last)) {
2804                 if (bidi_level(last) % 2 == 0)
2805                         tmpx -= singleWidth(bview, row->par(), last);
2806                 else
2807                         tmpx += singleWidth(bview, row->par(), last);
2808                 c = last;
2809         }
2810
2811         c -= row->pos();
2812         x = int(tmpx);
2813         return c;
2814 }
2815
2816
2817 // returns pointer to a specified row
2818 Row * LyXText::getRow(Paragraph * par, pos_type pos, int & y) const
2819 {
2820         if (!firstrow)
2821                 return 0;
2822
2823         Row * tmprow = firstrow;
2824         y = 0;
2825
2826         // find the first row of the specified paragraph
2827         while (tmprow->next() && tmprow->par() != par) {
2828                 y += tmprow->height();
2829                 tmprow = tmprow->next();
2830         }
2831
2832         // now find the wanted row
2833         while (tmprow->pos() < pos
2834                && tmprow->next()
2835                && tmprow->next()->par() == par
2836                && tmprow->next()->pos() <= pos) {
2837                 y += tmprow->height();
2838                 tmprow = tmprow->next();
2839         }
2840
2841         return tmprow;
2842 }
2843
2844
2845 Row * LyXText::getRowNearY(int & y) const
2846 {
2847 #if 1
2848         // If possible we should optimize this method. (Lgb)
2849         Row * tmprow = firstrow;
2850         int tmpy = 0;
2851
2852         while (tmprow->next() && tmpy + tmprow->height() <= y) {
2853                 tmpy += tmprow->height();
2854                 tmprow = tmprow->next();
2855         }
2856
2857         y = tmpy;   // return the real y
2858
2859         //lyxerr << "returned y = " << y << endl;
2860
2861         return tmprow;
2862 #else
2863         // Search from the current cursor position.
2864
2865         Row * tmprow = cursor.row();
2866         int tmpy = cursor.y() - tmprow->baseline();
2867
2868         lyxerr << "cursor.y() = " << tmpy << endl;
2869         lyxerr << "tmprow->height() = " << tmprow->height() << endl;
2870         lyxerr << "tmprow->baseline() = " << tmprow->baseline() << endl;
2871         lyxerr << "first = " << first << endl;
2872         lyxerr << "y = " << y << endl;
2873
2874         if (y < tmpy) {
2875                 lyxerr << "up" << endl;
2876                 do {
2877                         tmpy -= tmprow->height();
2878                         tmprow = tmprow->previous();
2879                 } while (tmprow && tmpy - tmprow->height() >= y);
2880         } else if (y > tmpy) {
2881                 lyxerr << "down" << endl;
2882
2883                 while (tmprow->next() && tmpy + tmprow->height() <= y) {
2884                         tmpy += tmprow->height();
2885                         tmprow = tmprow->next();
2886                 }
2887         } else {
2888                 lyxerr << "equal" << endl;
2889         }
2890
2891         y = tmpy; // return the real y
2892
2893         lyxerr << "returned y = " << y << endl;
2894
2895         return tmprow;
2896
2897 #endif
2898 }
2899
2900
2901 int LyXText::getDepth() const
2902 {
2903         return cursor.par()->getDepth();
2904 }