]> git.lyx.org Git - lyx.git/blob - src/text.C
Fixed bug where delete/backspace like operations on selections filled the
[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 #include <cstdlib>
13 #include <cctype>
14 #include <algorithm>
15
16 #include "lyxtext.h"
17 #include "layout.h"
18 #include "paragraph.h"
19 #include "support/textutils.h"
20 #include "support/LAssert.h"
21 #include "support/lstrings.h"
22 #include "insets/insetbib.h"
23 #include "insets/insettext.h"
24 #include "insets/insetspecialchar.h"
25 #include "lyx_gui_misc.h"
26 #include "gettext.h"
27 #include "bufferparams.h"
28 #include "buffer.h"
29 #include "debug.h"
30 #include "lyxrc.h"
31 #include "LyXView.h"
32 #include "Painter.h"
33 #include "tracer.h"
34 #include "font.h"
35 #include "encoding.h"
36 #include "lyxscreen.h"
37 #include "bufferview_funcs.h"
38 #include "BufferView.h"
39 #include "language.h"
40 #include "ParagraphParameters.h"
41 #include "undo_funcs.h"
42 #include "font.h"
43
44 using std::max;
45 using std::min;
46 using std::endl;
47 using std::pair;
48
49 namespace {
50
51 int const LYX_PAPER_MARGIN = 20;
52
53 } // namespace anon
54
55 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
56
57
58 int LyXText::workWidth(BufferView * bview) const
59 {
60         if (inset_owner) {
61                 return inset_owner->textWidth(bview);
62         }
63         return bview->workWidth();
64 }
65
66
67 int LyXText::getRealCursorX(BufferView * bview) const
68 {
69         int x = cursor.x();
70         if (the_locking_inset && (the_locking_inset->getLyXText(bview)!=this))
71                 x = the_locking_inset->getLyXText(bview)->getRealCursorX(bview);
72         return x;
73 }
74
75
76 unsigned char LyXText::transformChar(unsigned char c, Paragraph * par,
77                         Paragraph::size_type pos) const
78 {
79         if (!Encodings::is_arabic(c))
80                 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && isdigit(c))
81                         return c + (0xb0 - '0');
82                 else
83                         return c;
84
85         unsigned char const prev_char = pos > 0 ? par->getChar(pos-1) : ' ';
86         unsigned char next_char = ' ';
87
88         for (Paragraph::size_type i = pos+1; i < par->size(); ++i)
89                 if (!Encodings::IsComposeChar_arabic(par->getChar(i))) {
90                         next_char = par->getChar(i);
91                         break;
92                 }
93
94         if (Encodings::is_arabic(next_char)) {
95                 if (Encodings::is_arabic(prev_char))
96                         return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
97                 else
98                         return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
99         } else {
100                 if (Encodings::is_arabic(prev_char))
101                         return Encodings::TransformChar(c, Encodings::FORM_FINAL);
102                 else
103                         return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
104         }
105 }
106
107 // This is the comments that some of the warnings below refers to.
108 // There are some issues in this file and I don't think they are
109 // really related to the FIX_DOUBLE_SPACE patch. I'd rather think that
110 // this is a problem that has been here almost from day one and that a
111 // larger userbase with differenct access patters triggers the bad
112 // behaviour. (segfaults.) What I think happen is: In several places
113 // we store the paragraph in the current cursor and then moves the
114 // cursor. This movement of the cursor will delete paragraph at the
115 // old position if it is now empty. This will make the temporary
116 // pointer to the old cursor paragraph invalid and dangerous to use.
117 // And is some cases this will trigger a segfault. I have marked some
118 // of the cases where this happens with a warning, but I am sure there
119 // are others in this file and in text2.C. There is also a note in
120 // Delete() that you should read. In Delete I store the paragraph->id
121 // instead of a pointer to the paragraph. I am pretty sure this faulty
122 // use of temporary pointers to paragraphs that might have gotten
123 // invalidated (through a cursor movement) before they are used, are
124 // the cause of the strange crashes we get reported often.
125 //
126 // It is very tiresom to change this code, especially when it is as
127 // hard to read as it is. Help to fix all the cases where this is done
128 // would be greately appreciated.
129 //
130 // Lgb
131
132 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
133                          Paragraph::size_type pos) const
134 {
135         char const c = par->getChar(pos);
136         return singleWidth(bview, par, pos, c);
137 }
138
139
140 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
141                          Paragraph::size_type pos, char c) const
142 {
143         LyXFont const font = getFont(bview->buffer(), par, pos);
144
145         // The most common case is handled first (Asger)
146         if (IsPrintable(c)) {
147                 if (font.language()->RightToLeft()) {
148                         if (font.language()->lang() == "arabic" &&
149                             (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
150                              lyxrc.font_norm_type == LyXRC::ISO_10646_1))
151                                 if (Encodings::IsComposeChar_arabic(c))
152                                         return 0;
153                                 else
154                                         c = transformChar(c, par, pos);
155                         else if (font.language()->lang() == "hebrew" &&
156                                  Encodings::IsComposeChar_hebrew(c))
157                                 return 0;
158                 }
159                 return lyxfont::width(c, font);
160
161         } else if (IsHfillChar(c)) {
162                 return 3;       /* Because of the representation
163                                  * as vertical lines */
164         } else if (c == Paragraph::META_INSET) {
165                 Inset * tmpinset = par->getInset(pos);
166                 if (tmpinset) {
167 #if 0 // seems not to be needed, but ...
168                         tmpinset->update(bview, font);
169 #endif
170                         return tmpinset->width(bview, font);
171                 } else
172                         return 0;
173
174         } else if (IsSeparatorChar(c))
175                 c = ' ';
176         else if (IsNewlineChar(c))
177                 c = 'n';
178         return lyxfont::width(c, font);
179 }
180
181
182 // Returns the paragraph position of the last character in the specified row
183 Paragraph::size_type LyXText::rowLast(Row const * row) const
184 {
185         if (row->next() == 0)
186                 return row->par()->size() - 1;
187         else if (row->next()->par() != row->par()) 
188                 return row->par()->size() - 1;
189         else 
190                 return row->next()->pos() - 1;
191 }
192
193
194 Paragraph::size_type LyXText::rowLastPrintable(Row const * row) const
195 {
196         Paragraph::size_type const last = rowLast(row);
197         if (last >= row->pos()
198             && row->next()
199             && row->next()->par() == row->par()
200             && row->par()->isSeparator(last))
201                 return last - 1;
202         else
203                 return last;
204 }
205
206
207 void LyXText::computeBidiTables(Buffer const * buf, Row * row) const
208 {
209         bidi_same_direction = true;
210         if (!lyxrc.rtl_support) {
211                 bidi_start = -1;
212                 return;
213         }
214
215         bidi_start = row->pos();
216         bidi_end = rowLastPrintable(row);
217
218         if (bidi_start > bidi_end) {
219                 bidi_start = -1;
220                 return;
221         }
222
223         if (bidi_end + 2 - bidi_start >
224             static_cast<Paragraph::size_type>(log2vis_list.size())) {
225                 Paragraph::size_type new_size = 
226                         (bidi_end + 2 - bidi_start < 500) ?
227                         500 : 2 * (bidi_end + 2 - bidi_start);
228                 log2vis_list.resize(new_size);
229                 vis2log_list.resize(new_size);
230                 bidi_levels.resize(new_size);
231         }
232
233         vis2log_list[bidi_end + 1 - bidi_start] = -1;
234         log2vis_list[bidi_end + 1 - bidi_start] = -1;
235
236         Paragraph::size_type    stack[2];
237         bool const rtl_par =
238                 row->par()->getParLanguage(buf->params)->RightToLeft();
239         int level = 0;
240         bool rtl = false;
241         bool rtl0 = false;
242         Paragraph::size_type const main_body =
243                 beginningOfMainBody(buf, row->par());
244
245         for (Paragraph::size_type lpos = bidi_start;
246              lpos <= bidi_end; ++lpos) {
247                 bool is_space = row->par()->isLineSeparator(lpos);
248                 Paragraph::size_type const pos =
249                         (is_space && lpos + 1 <= bidi_end &&
250                          !row->par()->isLineSeparator(lpos + 1) &&
251                          !row->par()->isNewline(lpos + 1))
252                         ? lpos + 1 : lpos;
253                 LyXFont font = row->par()->getFontSettings(buf->params, pos);
254                 if (pos != lpos && 0 < lpos && rtl0 && font.isRightToLeft() &&
255                     font.number() == LyXFont::ON &&
256                     row->par()->getFontSettings(buf->params, lpos - 1).number()
257                     == LyXFont::ON) {
258                         font = row->par()->getFontSettings(buf->params, lpos);
259                         is_space = false;
260                 }
261
262
263                 bool new_rtl = font.isVisibleRightToLeft();
264                 bool new_rtl0 = font.isRightToLeft();
265                 int new_level;
266
267                 if (lpos == main_body - 1
268                     && row->pos() < main_body - 1
269                     && is_space) {
270                         new_level = (rtl_par) ? 1 : 0;
271                         new_rtl = new_rtl0 = rtl_par;
272                 } else if (new_rtl0)
273                         new_level = (new_rtl) ? 1 : 2;
274                 else
275                         new_level = (rtl_par) ? 2 : 0;
276
277                 if (is_space && new_level >= level) {
278                         new_level = level;
279                         new_rtl = rtl;
280                         new_rtl0 = rtl0;
281                 }
282
283                 int new_level2 = new_level;
284
285                 if (level == new_level && rtl0 != new_rtl0) {
286                         --new_level2;
287                         log2vis_list[lpos - bidi_start] = (rtl) ? 1 : -1;
288                 } else if (level < new_level) {
289                         log2vis_list[lpos - bidi_start] =  (rtl) ? -1 : 1;
290                         if (new_level > rtl_par)
291                                 bidi_same_direction = false;
292                 } else
293                         log2vis_list[lpos - bidi_start] = (new_rtl) ? -1 : 1;
294                 rtl = new_rtl;
295                 rtl0 = new_rtl0;
296                 bidi_levels[lpos - bidi_start] = new_level;
297
298                 while (level > new_level2) {
299                         Paragraph::size_type old_lpos =
300                                 stack[--level];
301                         int delta = lpos - old_lpos - 1;
302                         if (level % 2)
303                                 delta = -delta;
304                         log2vis_list[lpos - bidi_start] += delta;
305                         log2vis_list[old_lpos - bidi_start] += delta;
306                 }
307                 while (level < new_level)
308                         stack[level++] = lpos;
309         }
310
311         while (level > 0) {
312                 Paragraph::size_type const old_lpos = stack[--level];
313                 int delta = bidi_end - old_lpos;
314                 if (level % 2)
315                         delta = -delta;
316                 log2vis_list[old_lpos - bidi_start] += delta;
317         }
318
319         Paragraph::size_type vpos = bidi_start - 1;
320         for (Paragraph::size_type lpos = bidi_start;
321              lpos <= bidi_end; ++lpos) {
322                 vpos += log2vis_list[lpos - bidi_start];
323                 vis2log_list[vpos - bidi_start] = lpos;
324                 log2vis_list[lpos - bidi_start] = vpos;
325         }
326 }
327
328
329 // This method requires a previous call to ComputeBidiTables()
330 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
331                          Paragraph::size_type pos) const
332 {
333         if (!lyxrc.rtl_support || pos == 0)
334                 return false;
335
336         if (!bidi_InRange(pos - 1)) {
337                 /// This can happen if pos is the first char of a row.
338                 /// Returning false in this case is incorrect!
339                 return false;
340         }
341
342         bool const rtl = bidi_level(pos - 1) % 2;
343         bool const rtl2 = bidi_InRange(pos)
344                 ? bidi_level(pos) % 2
345                 : par->isRightToLeftPar(buf->params);
346         return rtl != rtl2;
347 }
348
349
350 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
351                          Paragraph::size_type pos,
352                          LyXFont const & font) const
353 {
354         if (!lyxrc.rtl_support)
355                 return false;    // This is just for speedup
356
357         bool const rtl = font.isVisibleRightToLeft();
358         bool const rtl2 = bidi_InRange(pos)
359                 ? bidi_level(pos) % 2
360                 : par->isRightToLeftPar(buf->params);
361         return rtl != rtl2;
362 }
363
364
365 void LyXText::draw(BufferView * bview, Row const * row,
366                    Paragraph::size_type & vpos,
367                    int offset, float & x, bool cleared)
368 {
369         Painter & pain = bview->painter();
370         
371         Paragraph::size_type pos = vis2log(vpos);
372         char c = row->par()->getChar(pos);
373         float tmpx = x;
374
375         if (IsNewlineChar(c)) {
376                 ++vpos;
377                 // Draw end-of-line marker
378                 LyXFont const font = getFont(bview->buffer(), row->par(), pos);
379                 int const wid = lyxfont::width('n', font);
380                 int const asc = lyxfont::maxAscent(font);
381                 int const y = offset + row->baseline();
382                 int xp[3];
383                 int yp[3];
384                 
385                 if (bidi_level(pos) % 2 == 0) {
386                         xp[0] = int(x + wid * 0.375);
387                         yp[0] = int(y - 0.875 * asc * 0.75);
388                         
389                         xp[1] = int(x);
390                         yp[1] = int(y - 0.500 * asc * 0.75);
391                         
392                         xp[2] = int(x + wid * 0.375);
393                         yp[2] = int(y - 0.125 * asc * 0.75);
394                         
395                         pain.lines(xp, yp, 3, LColor::eolmarker);
396                         
397                         xp[0] = int(x);
398                         yp[0] = int(y - 0.500 * asc * 0.75);
399                         
400                         xp[1] = int(x + wid);
401                         yp[1] = int(y - 0.500 * asc * 0.75);
402                         
403                         xp[2] = int(x + wid);
404                         yp[2] = int(y - asc * 0.75);
405                         
406                         pain.lines(xp, yp, 3, LColor::eolmarker);
407                 } else {
408                         xp[0] = int(x + wid * 0.625);
409                         yp[0] = int(y - 0.875 * asc * 0.75);
410                         
411                         xp[1] = int(x + wid);
412                         yp[1] = int(y - 0.500 * asc * 0.75);
413                         
414                         xp[2] = int(x + wid * 0.625);
415                         yp[2] = int(y - 0.125 * asc * 0.75);
416                         
417                         pain.lines(xp, yp, 3, LColor::eolmarker);
418                         
419                         xp[0] = int(x + wid);
420                         yp[0] = int(y - 0.500 * asc * 0.75);
421                         
422                         xp[1] = int(x);
423                         yp[1] = int(y - 0.500 * asc * 0.75);
424                         
425                         xp[2] = int(x);
426                         yp[2] = int(y - asc * 0.75);
427                         
428                         pain.lines(xp, yp, 3, LColor::eolmarker);
429                 }
430                 x += wid;
431                 return;
432         }
433
434         LyXFont font = getFont(bview->buffer(), row->par(), pos);
435         LyXFont font2 = font;
436
437         if (c == Paragraph::META_INSET) {
438                 Inset * tmpinset = row->par()->getInset(pos);
439                 if (tmpinset) {
440                         tmpinset->update(bview, font, false);
441                         tmpinset->draw(bview, font, offset+row->baseline(), x,
442                                        cleared);
443                         if (!need_break_row && !inset_owner &&
444                             bview->text->status() == CHANGED_IN_DRAW)
445                         {
446                                 if (row->previous() && row->previous()->par() == row->par())
447                                         breakAgainOneRow(bview, row->previous());
448                                 setCursor(bview, cursor.par(), cursor.pos());
449                                 need_break_row = const_cast<Row *>(row);
450                         }
451                 }
452                 ++vpos;
453
454                 if (lyxrc.mark_foreign_language &&
455                         font.language() != ignore_language &&
456                     font.language() != bview->buffer()->params.language) {
457                         int const y = offset + row->height() - 1;
458                         pain.line(int(tmpx), y, int(x), y, LColor::language);
459                 }
460
461                 return;
462         }
463
464         /* usual characters, no insets */
465
466         // Collect character that we can draw in one command
467
468         // This is dirty, but fast. Notice that it will never be too small.
469         // For the record, I'll note that Microsoft Word has a limit
470         // of 768 here. We have none :-) (Asger)
471         // Ok. I am the first to admit that the use of std::string will be
472         // a tiny bit slower than using a POD char array. However, I claim
473         // that this slowdown is so small that it is close to inperceptive.
474         // So IMHO we should go with the easier and clearer implementation.
475         // And even if 1024 is a large number here it might overflow, string
476         // will only overflow if the machine is out of memory...
477         static string textstring;
478         textstring = c;
479         ++vpos;
480
481         Paragraph::size_type const last = rowLastPrintable(row);
482
483         if (font.language()->lang() == "hebrew") {
484                 if (Encodings::IsComposeChar_hebrew(c)) {
485                         int const width = lyxfont::width(c, font2);
486                         int dx = 0;
487                         for (Paragraph::size_type i = pos-1; i >= 0; --i) {
488                                 c = row->par()->getChar(i);
489                                 if (!Encodings::IsComposeChar_hebrew(c)) {
490                                         if (IsPrintableNonspace(c)) {
491                                                 int const width2 =
492                                                         singleWidth(bview,
493                                                                     row->par(),
494                                                                     i, c);
495                                                 dx = (c == 'ø' || c == 'ã') // dalet / resh
496                                                         ? width2 - width : (width2 - width) / 2;
497                                         }
498                                         break;
499                                 }
500                         }
501                         // Draw nikud
502                         pain.text(int(x) + dx, offset + row->baseline(),
503                                   textstring, font);
504                 } else {
505                         while (vpos <= last &&
506                                (pos = vis2log(vpos)) >= 0
507                                && IsPrintableNonspace(c = row->par()->getChar(pos))
508                                && !Encodings::IsComposeChar_hebrew(c)
509                                && font2 == getFont(bview->buffer(), row->par(), pos)) {
510                                 textstring += c;
511                                 ++vpos;
512                         }
513                         // Draw text and set the new x position
514                         pain.text(int(x), offset + row->baseline(),
515                                   textstring, font);
516                         x += lyxfont::width(textstring, font);
517                 }
518         } else if (font.language()->lang() == "arabic" &&
519                    (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
520                     lyxrc.font_norm_type == LyXRC::ISO_10646_1)) {
521                 if (Encodings::IsComposeChar_arabic(c)) {
522                         c = transformChar(c, row->par(), pos);
523                         textstring = c;
524                         int const width = lyxfont::width(c, font2);
525                         int dx = 0;
526                         for (Paragraph::size_type i = pos-1; i >= 0; --i) {
527                                 c = row->par()->getChar(i);
528                                 if (!Encodings::IsComposeChar_arabic(c)) {
529                                         if (IsPrintableNonspace(c)) {
530                                                 int const width2 =
531                                                         singleWidth(bview,
532                                                                     row->par(),
533                                                                     i, c);
534                                                 dx = (width2 - width) / 2;
535                                         }
536                                         break;
537                                 }
538                         }
539                         // Draw nikud
540                         pain.text(int(x) + dx, offset + row->baseline(), 
541                                   textstring, font);
542                 } else {
543                         textstring = transformChar(c, row->par(), pos);
544                         while (vpos <= last &&
545                                (pos = vis2log(vpos)) >= 0
546                                && IsPrintableNonspace(c = row->par()->getChar(pos))
547                                && !Encodings::IsComposeChar_arabic(c)
548                                && font2 == getFont(bview->buffer(), row->par(), pos)) {
549                                 c = transformChar(c, row->par(), pos);
550                                 textstring += c;
551                                 ++vpos;
552                         }
553                         // Draw text and set the new x position
554                         pain.text(int(x), offset + row->baseline(),
555                                   textstring, font);
556                         x += lyxfont::width(textstring, font);
557                 }
558         } else {
559                 while (vpos <= last &&
560                        (pos = vis2log(vpos)) >= 0
561                        && IsPrintableNonspace(c = row->par()->getChar(pos))
562                        && font2 == getFont(bview->buffer(), row->par(), pos)) {
563                         textstring += c;
564                         ++vpos;
565                 }
566                 // Draw text and set the new x position
567                 pain.text(int(x), offset + row->baseline(), textstring, font);
568                 x += lyxfont::width(textstring, font);
569         }
570
571 #ifdef WITH_WARNINGS
572         if ((font.language() == inherit_language) ||
573                 (font.language() == ignore_language))
574                 lyxerr << "No this shouldn't happen!\n";
575 #endif
576         if (lyxrc.mark_foreign_language &&
577             font.language() != bview->buffer()->params.language) {
578                 int const y = offset + row->height() - 1;
579                 pain.line(int(tmpx), y, int(x), y,
580                           LColor::language);
581         }
582
583         // If we want ulem.sty support, drawing
584         // routines should go here. (Asger)
585         // Why shouldn't LyXFont::drawText handle it internally?
586 }
587
588
589 // Returns the left beginning of the text. 
590 // This information cannot be taken from the layouts-objekt, because in 
591 // LaTeX the beginning of the text fits in some cases (for example sections)
592 // exactly the label-width.
593 int LyXText::leftMargin(BufferView * bview, Row const * row) const
594 {
595         LyXLayout const & layout =
596                 textclasslist.Style(bview->buffer()->params.textclass,
597                                     row->par()->getLayout());
598         
599         string parindent = layout.parindent; 
600
601         int x = LYX_PAPER_MARGIN;
602         
603         x += lyxfont::signedWidth(textclasslist
604                                   .TextClass(bview->buffer()->params.textclass)
605                                   .leftmargin(),
606                                   textclasslist
607                                   .TextClass(bview->buffer()->params.textclass)
608                                   .defaultfont());
609
610         // this is the way, LyX handles the LaTeX-Environments.
611         // I have had this idea very late, so it seems to be a
612         // later added hack and this is true
613         if (!row->par()->getDepth()) {
614                 if (!row->par()->getLayout()) {
615                         // find the previous same level paragraph
616                         if (row->par()->previous()) {
617                                 Paragraph * newpar = row->par()
618                                         ->depthHook(row->par()->getDepth());
619                                 if (newpar &&
620                                     textclasslist.Style(bview->buffer()->params.textclass,
621                                                         newpar->getLayout())
622                                     .nextnoindent)
623                                         parindent.erase();
624                         }
625                 }
626         } else {
627                 // find the next level paragraph
628                 
629                 Paragraph * newpar =
630                         row->par()->outerHook();
631                 
632                 // make a corresponding row. Needed to call LeftMargin()
633                 
634                 // check wether it is a sufficent paragraph 
635                 if (newpar
636                     && textclasslist
637                         .Style(bview->buffer()->params.textclass, 
638                                newpar->getLayout()).isEnvironment()) {
639                         Row dummyrow;
640                         dummyrow.par(newpar);
641                         dummyrow.pos(newpar->size());
642                         x = leftMargin(bview, &dummyrow);
643                 } else {
644                         // this is no longer an error, because this function
645                         // is used to clear impossible depths after changing
646                         // a layout. Since there is always a redo,
647                         // LeftMargin() is always called
648                         row->par()->params().depth(0);
649                 }
650                 
651                 if (newpar && !row->par()->getLayout()) {
652                         if (newpar->params().noindent())
653                                 parindent.erase();
654                         else
655                                 parindent = textclasslist
656                                         .Style(bview->buffer()->params.textclass, 
657                                                newpar->getLayout()).parindent;
658                 }
659                 
660         }
661         
662         LyXFont const labelfont = getLabelFont(bview->buffer(), row->par());
663         switch (layout.margintype) {
664         case MARGIN_DYNAMIC:
665                 if (!layout.leftmargin.empty()) {
666                         x += lyxfont::signedWidth(layout.leftmargin,
667                                                   textclasslist
668                                                   .TextClass(bview->buffer()->params.
669                                                              textclass)
670                                                   .defaultfont());
671                 }
672                 if (!row->par()->getLabelstring().empty()) {
673                         x += lyxfont::signedWidth(layout.labelindent,
674                                                   labelfont);
675                         x += lyxfont::width(row->par()->getLabelstring(),
676                                             labelfont);
677                         x += lyxfont::width(layout.labelsep, labelfont);
678                 }
679                 break;
680         case MARGIN_MANUAL:
681                 x += lyxfont::signedWidth(layout.labelindent, labelfont);
682                 if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
683                         if (!row->par()->getLabelWidthString().empty()) {
684                                 x += lyxfont::width(row->par()->getLabelWidthString(),
685                                                labelfont);
686                                 x += lyxfont::width(layout.labelsep, labelfont);
687                         }
688                 }
689                 break;
690         case MARGIN_STATIC:
691                 x += lyxfont::signedWidth(layout.leftmargin, textclasslist.TextClass(bview->buffer()->params.textclass).defaultfont()) * 4
692                         / (row->par()->getDepth() + 4);
693                 break;
694         case MARGIN_FIRST_DYNAMIC:
695                 if (layout.labeltype == LABEL_MANUAL) {
696                         if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
697                                 x += lyxfont::signedWidth(layout.leftmargin,
698                                                           labelfont);
699                         } else {
700                                 x += lyxfont::signedWidth(layout.labelindent,
701                                                           labelfont);
702                         }
703                 } else if (row->pos()
704                            // Special case to fix problems with
705                            // theorems (JMarc)
706                            || (layout.labeltype == LABEL_STATIC
707                                && layout.latextype == LATEX_ENVIRONMENT
708                                && ! row->par()->isFirstInSequence())) {
709                         x += lyxfont::signedWidth(layout.leftmargin,
710                                                   labelfont);
711                 } else if (layout.labeltype != LABEL_TOP_ENVIRONMENT
712                            && layout.labeltype != LABEL_BIBLIO
713                            && layout.labeltype !=
714                            LABEL_CENTERED_TOP_ENVIRONMENT) {
715                         x += lyxfont::signedWidth(layout.labelindent,
716                                                   labelfont);
717                         x += lyxfont::width(layout.labelsep, labelfont);
718                         x += lyxfont::width(row->par()->getLabelstring(),
719                                             labelfont);
720                 } 
721                 break;
722                 
723         case MARGIN_RIGHT_ADDRESS_BOX:
724         {
725                 // ok, a terrible hack. The left margin depends on the widest
726                 // row in this paragraph. Do not care about footnotes, they
727                 // are *NOT* allowed in the LaTeX realisation of this layout.
728                 
729                 // find the first row of this paragraph
730                 Row const * tmprow = row;
731                 while (tmprow->previous()
732                        && tmprow->previous()->par() == row->par())
733                         tmprow = tmprow->previous();
734                 
735                 int minfill = tmprow->fill();
736                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
737                         tmprow = tmprow->next();
738                         if (tmprow->fill() < minfill)
739                                 minfill = tmprow->fill();
740                 }
741                 
742                 x += lyxfont::signedWidth(layout.leftmargin,
743                                           textclasslist
744                                           .TextClass(bview->buffer()->params.textclass)
745                                           .defaultfont());
746                 x += minfill;
747         }
748         break;
749         }
750         
751         int align; // wrong type
752
753         if (row->par()->params().align() == LYX_ALIGN_LAYOUT)
754                 align = layout.align;
755         else
756                 align = row->par()->params().align();
757
758         // set the correct parindent
759         if (row->pos() == 0) {
760                 if ((layout.labeltype == LABEL_NO_LABEL 
761                      || layout.labeltype == LABEL_TOP_ENVIRONMENT 
762                      || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
763                      || (layout.labeltype == LABEL_STATIC
764                          && layout.latextype == LATEX_ENVIRONMENT
765                          && ! row->par()->isFirstInSequence()))
766                     && align == LYX_ALIGN_BLOCK
767                     && !row->par()->params().noindent()
768                     && (row->par()->layout ||
769                         bview->buffer()->params.paragraph_separation ==
770                         BufferParams::PARSEP_INDENT))
771                         x += lyxfont::signedWidth(parindent,
772                                                   textclasslist
773                                                   .TextClass(bview->buffer()->params
774                                                              .textclass)
775                                                   .defaultfont());
776                 else if (layout.labeltype == LABEL_BIBLIO) {
777                         // ale970405 Right width for bibitems
778                         x += bibitemMaxWidth(bview,textclasslist
779                                              .TextClass(bview->buffer()->params
780                                                         .textclass)
781                                              .defaultfont());
782                 }
783         }
784         return x;
785 }
786
787
788 int LyXText::rightMargin(Buffer const * buf, Row const * row) const
789 {
790         LyXLayout const & layout =
791                 textclasslist.Style(buf->params.textclass,
792                                     row->par()->getLayout());
793         
794         int x = LYX_PAPER_MARGIN
795                 + lyxfont::signedWidth(textclasslist
796                                        .TextClass(buf->params.textclass)
797                                        .rightmargin(),
798                                        textclasslist
799                                        .TextClass(buf->params.textclass)
800                                        .defaultfont());
801
802         // this is the way, LyX handles the LaTeX-Environments.
803         // I have had this idea very late, so it seems to be a
804         // later added hack and this is true
805         if (row->par()->getDepth()) {
806                 // find the next level paragraph
807                 
808                 Paragraph * newpar = row->par();
809                 
810                 do {
811                         newpar = newpar->previous();
812                 } while (newpar
813                          && newpar->getDepth() >= row->par()->getDepth());
814                 
815                 // make a corresponding row. Needed to call LeftMargin()
816                 
817                 // check wether it is a sufficent paragraph
818                 if (newpar
819                     && textclasslist.Style(buf->params.textclass,
820                                            newpar->getLayout())
821                        .isEnvironment()) {
822                         Row dummyrow;
823                         dummyrow.par(newpar);
824                         dummyrow.pos(0);
825                         x = rightMargin(buf, &dummyrow);
826                 } else {
827                         // this is no longer an error, because this function
828                         // is used to clear impossible depths after changing
829                         // a layout. Since there is always a redo,
830                         // LeftMargin() is always called
831                         row->par()->params().depth(0);
832                 }
833         }
834         
835         //lyxerr << "rightmargin: " << layout->rightmargin << endl;
836         x += lyxfont::signedWidth(layout.rightmargin, textclasslist
837                                   .TextClass(buf->params.textclass)
838                                   .defaultfont()) * 4 / (row->par()->getDepth()
839                                                          + 4);
840         return x;
841 }
842
843
844 int LyXText::labelEnd(BufferView * bview, Row const * row) const
845 {
846         if (textclasslist.Style(bview->buffer()->params.textclass,
847                                 row->par()->getLayout()).margintype
848             == MARGIN_MANUAL) {
849                 Row tmprow;
850                 tmprow = *row;
851                 tmprow.pos(row->par()->size());
852                 return leftMargin(bview, &tmprow);  /* just the beginning 
853                                                 of the main body */
854         } else
855                 return 0;  /* LabelEnd is only needed, if the  
856                               layout fills a flushleft
857                               label. */
858 }
859
860
861 // get the next breakpoint in a given paragraph
862 Paragraph::size_type
863 LyXText::nextBreakPoint(BufferView * bview, Row const * row, int width) const
864 {
865         Paragraph * par = row->par();
866
867         if (width < 0)
868                 return par->size();
869
870         Paragraph::size_type const pos = row->pos();
871
872
873         // position of the last possible breakpoint 
874         // -1 isn't a suitable value, but a flag
875         Paragraph::size_type last_separator = -1;
876         width -= rightMargin(bview->buffer(), row);
877         
878         Paragraph::size_type const main_body =
879                 beginningOfMainBody(bview->buffer(), par);
880         LyXLayout const & layout =
881                 textclasslist.Style(bview->buffer()->params.textclass, par->getLayout());
882         Paragraph::size_type i = pos;
883
884         if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
885                 /* special code for right address boxes, only newlines count */
886                 while (i < par->size()) {
887                         if (par->isNewline(i)) {
888                                 last_separator = i;
889                                 i = par->size() - 1; // this means break
890                                 //x = width;
891                         } else if (par->getChar(i) == Paragraph::META_INSET &&
892                                    par->getInset(i) && par->getInset(i)->display()){
893                                 par->getInset(i)->display(false);
894                         }
895                         ++i;
896                 }
897         } else {
898                 // Last position is an invariant
899                 Paragraph::size_type const last = 
900                         par->size();
901                 // this is the usual handling
902                 int x = leftMargin(bview, row);
903                 bool doitonetime = true;
904                 while (doitonetime || ((x < width) && (i < last))) {
905                         doitonetime = false;
906                         char const c = par->getChar(i);
907                         if (IsNewlineChar(c)) {
908                                 last_separator = i;
909                                 x = width; // this means break
910                         } else if (c == Paragraph::META_INSET &&
911                                    par->getInset(i)) {
912                                 
913                                 // check wether a Display() inset is
914                                 // valid here. if not, change it to
915                                 // non-display
916                                 if (par->getInset(i)->display() &&
917                                     (layout.isCommand() ||
918                                      (layout.labeltype == LABEL_MANUAL
919                                       && i < beginningOfMainBody(bview->buffer(), par)))) {
920                                         // display istn't allowd
921                                         par->getInset(i)->display(false);
922                                         x += singleWidth(bview, par, i, c);
923                                 } else if (par->getInset(i)->display() ||
924                                          par->getInset(i)->needFullRow()) {
925                                         // So break the line here
926                                         if (i == pos) {
927                                                 if (pos < last-1) {
928                                                         last_separator = i;
929                                                         if (IsLineSeparatorChar(par->getChar(i+1)))
930                                                                 ++last_separator;
931                                                 } else
932                                                         last_separator = last; // to avoid extra rows
933                                         } else
934                                                 last_separator = i - 1;
935                                         x = width;  // this means break
936                                 } else {
937                                         x += singleWidth(bview, par, i, c);
938                                 }
939                         } else  {
940                                 if (IsLineSeparatorChar(c))
941                                         last_separator = i;
942                                 x += singleWidth(bview, par, i, c);
943                         }
944                         ++i;
945                         if (i == main_body) {
946                                 x += lyxfont::width(layout.labelsep,
947                                                     getLabelFont(bview->buffer(), par));
948                                 if (par->isLineSeparator(i - 1))
949                                         x-= singleWidth(bview, par, i - 1);
950                                 int left_margin = labelEnd(bview, row);
951                                 if (x < left_margin)
952                                         x = left_margin;
953                         }
954                 }
955                 // end of paragraph is always a suitable separator
956                 if (i == last && x < width)
957                         last_separator = i;
958         }
959         
960         // well, if last_separator is still 0, the line isn't breakable. 
961         // don't care and cut simply at the end
962         if (last_separator < 0) {
963                 last_separator = i;
964         }
965         
966         // manual labels cannot be broken in LaTeX, do not care
967         if (main_body && last_separator < main_body)
968                 last_separator = main_body - 1;
969         
970         return last_separator;
971 }
972
973
974 // returns the minimum space a row needs on the screen in pixel
975 int LyXText::fill(BufferView * bview, Row * row, int paper_width) const
976 {
977         if (paper_width < 0)
978                 return 20;
979
980         int w;
981         // get the pure distance
982         Paragraph::size_type const last = rowLastPrintable(row);
983         
984         // special handling of the right address boxes
985         if (textclasslist.Style(bview->buffer()->params.textclass,
986                                 row->par()->getLayout()).margintype
987             == MARGIN_RIGHT_ADDRESS_BOX) {
988                 int const tmpfill = row->fill();
989                 row->fill(0); // the minfill in MarginLeft()
990                 w = leftMargin(bview, row);
991                 row->fill(tmpfill);
992         } else
993                 w = leftMargin(bview, row);
994         
995         LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
996                                                        row->par()->getLayout());
997         Paragraph::size_type const main_body = 
998                 beginningOfMainBody(bview->buffer(), row->par());
999         Paragraph::size_type i = row->pos();
1000
1001         while (i <= last) {
1002                 if (main_body > 0 && i == main_body) {
1003                         w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1004                         if (row->par()->isLineSeparator(i - 1))
1005                                 w -= singleWidth(bview, row->par(), i - 1);
1006                         int left_margin = labelEnd(bview, row);
1007                         if (w < left_margin)
1008                                 w = left_margin;
1009                 }
1010                 w += singleWidth(bview, row->par(), i);
1011                 ++i;
1012         }
1013         if (main_body > 0 && main_body > last) {
1014                 w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1015                 if (last >= 0 && row->par()->isLineSeparator(last))
1016                         w -= singleWidth(bview, row->par(), last);
1017                 int const left_margin = labelEnd(bview, row);
1018                 if (w < left_margin)
1019                         w = left_margin;
1020         }
1021         
1022         int const fill = paper_width - w - rightMargin(bview->buffer(), row);
1023 #ifdef WITH_WARNINGS
1024 #warning Please fix me (Jug!)
1025 #endif
1026 #if 0
1027         if (fill < 0)
1028                 return 0;
1029 #endif
1030         return fill;
1031 }
1032
1033
1034 // returns the minimum space a manual label needs on the screen in pixel
1035 int LyXText::labelFill(BufferView * bview, Row const * row) const
1036 {
1037         Paragraph::size_type last = beginningOfMainBody(bview->buffer(), row->par()) - 1;
1038         // -1 because a label ends either with a space that is in the label, 
1039         // or with the beginning of a footnote that is outside the label.
1040
1041         // I don't understand this code in depth, but sometimes "last" is
1042         // less than 0 and this causes a crash. This fix seems to work
1043         // correctly, but I bet the real error is elsewhere.  The bug is
1044         // triggered when you have an open footnote in a paragraph
1045         // environment with a manual label. (Asger)
1046         if (last < 0) last = 0;
1047         
1048         if (row->par()->isLineSeparator(last)) /* a sepearator at this end 
1049                                                 does not count */
1050                 --last;
1051         
1052         int w = 0;
1053         int i = row->pos();
1054         while (i <= last) {
1055                 w += singleWidth(bview, row->par(), i);
1056                 ++i;
1057         }
1058         
1059         int fill = 0;
1060         if (!row->par()->params().labelWidthString().empty()) {
1061                 fill = max(lyxfont::width(row->par()->params().labelWidthString(),
1062                                           getLabelFont(bview->buffer(), row->par())) - w,
1063                            0);
1064         }
1065         
1066         return fill;
1067 }
1068
1069
1070 // returns the number of separators in the specified row. The separator 
1071 // on the very last column doesnt count
1072 int LyXText::numberOfSeparators(Buffer const * buf, Row const * row) const
1073 {
1074         Paragraph::size_type const last = rowLast(row);
1075         Paragraph::size_type p =
1076                 max(row->pos(), beginningOfMainBody(buf, row->par()));
1077         int n = 0;
1078         for (; p < last; ++p) {
1079                 if (row->par()->isSeparator(p)) {
1080                         ++n;
1081                 }
1082         }
1083         return n;
1084 }
1085
1086
1087 // returns the number of hfills in the specified row. The LyX-Hfill is
1088 // a LaTeX \hfill so that the hfills at the beginning and at the end were 
1089 // ignored. This is *MUCH* more usefull than not to ignore!
1090 int LyXText::numberOfHfills(Buffer const * buf, Row const * row) const
1091 {
1092         Paragraph::size_type const last = rowLast(row);
1093         Paragraph::size_type first = row->pos();
1094         if (first) { /* hfill *DO* count at the beginning 
1095                       * of paragraphs! */
1096                 while(first <= last && row->par()->isHfill(first))
1097                         ++first;
1098         }
1099
1100         first = max(first, beginningOfMainBody(buf, row->par()));
1101         int n = 0;
1102         for (int p = first; p <= last; ++p) { // last, because the end is ignored!
1103                 if (row->par()->isHfill(p)) {
1104                         ++n;
1105                 }
1106         }
1107         return n;
1108 }
1109
1110
1111 // like NumberOfHfills, but only those in the manual label!
1112 int LyXText::numberOfLabelHfills(Buffer const * buf, Row const * row) const
1113 {
1114         Paragraph::size_type last = rowLast(row);
1115         Paragraph::size_type first = row->pos();
1116         if (first) { /* hfill *DO* count at the beginning 
1117                       * of paragraphs! */
1118                 while(first < last && row->par()->isHfill(first))
1119                         ++first;
1120         }
1121
1122         last = min(last, beginningOfMainBody(buf, row->par()));
1123         int n = 0;
1124         for (Paragraph::size_type p = first;
1125              p < last; ++p) {  // last, because the end is ignored!
1126                 if (row->par()->isHfill(p)) {
1127                         ++n;
1128                 }
1129         }
1130         return n;
1131 }
1132
1133
1134 // returns true, if a expansion is needed.
1135 // Rules are given by LaTeX
1136 bool LyXText::hfillExpansion(Buffer const * buf, Row const * row_ptr,
1137                              Paragraph::size_type pos) const
1138 {
1139         // by the way, is it a hfill?
1140         if (!row_ptr->par()->isHfill(pos))
1141                 return false;
1142         
1143         // at the end of a row it does not count
1144         if (pos >= rowLast(row_ptr))
1145                 return false;
1146         
1147         // at the beginning of a row it does not count, if it is not 
1148         // the first row of a paragaph
1149         if (!row_ptr->pos())
1150                 return true;
1151         
1152         // in some labels  it does not count
1153         if (textclasslist.Style(buf->params.textclass,
1154                                 row_ptr->par()->getLayout()).margintype
1155             != MARGIN_MANUAL
1156             && pos < beginningOfMainBody(buf, row_ptr->par()))
1157                 return false; 
1158         
1159         // if there is anything between the first char of the row and
1160         // the sepcified position that is not a newline and not a hfill,
1161         // the hfill will count, otherwise not
1162         Paragraph::size_type i = row_ptr->pos();
1163         while (i < pos && (row_ptr->par()->isNewline(i)
1164                            || row_ptr->par()->isHfill(i)))
1165                 ++i;
1166         
1167         return i != pos;
1168 }
1169
1170
1171 LColor::color LyXText::backgroundColor()
1172 {
1173         if (inset_owner)
1174                 return inset_owner->backgroundColor();
1175         else
1176                 return LColor::background;
1177 }
1178
1179 void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const
1180 {
1181     /* get the maximum ascent and the maximum descent */
1182         int asc = 0;
1183         int desc = 0;
1184         float layoutasc = 0;
1185         float layoutdesc = 0;
1186         float tmptop = 0;
1187         LyXFont tmpfont;
1188         Inset * tmpinset = 0;
1189
1190         /* this must not happen before the currentrow for clear reasons.
1191            so the trick is just to set the current row onto this row */
1192         int unused_y;
1193         getRow(row_ptr->par(), row_ptr->pos(), unused_y);
1194
1195         /* ok , let us initialize the maxasc and maxdesc value. 
1196          * This depends in LaTeX of the font of the last character
1197          * in the paragraph. The hack below is necessary because
1198          * of the possibility of open footnotes */
1199         
1200         /* Correction: only the fontsize count. The other properties
1201            are taken from the layoutfont. Nicer on the screen :) */
1202         Paragraph * par = row_ptr->par();
1203         Paragraph * firstpar = row_ptr->par();
1204    
1205         LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
1206                                                        firstpar->getLayout());
1207
1208         // as max get the first character of this row then it can increes but not
1209         // decrees the height. Just some point to start with so we don't have to
1210         // do the assignment below too often.
1211         LyXFont font = getFont(bview->buffer(), par, row_ptr->pos());
1212         LyXFont::FONT_SIZE const tmpsize = font.size();
1213         font = getLayoutFont(bview->buffer(), par);
1214         LyXFont::FONT_SIZE const size = font.size();
1215         font.setSize(tmpsize);
1216
1217         LyXFont labelfont = getLabelFont(bview->buffer(), par);
1218
1219         float spacing_val = 1.0;
1220         if (!row_ptr->par()->params().spacing().isDefault()) {
1221                 spacing_val = row_ptr->par()->params().spacing().getValue();
1222         } else {
1223                 spacing_val = bview->buffer()->params.spacing.getValue();
1224         }
1225         //lyxerr << "spacing_val = " << spacing_val << endl;
1226    
1227         int maxasc = int(lyxfont::maxAscent(font) *
1228                          layout.spacing.getValue() *
1229                          spacing_val);
1230         int maxdesc = int(lyxfont::maxDescent(font) *
1231                           layout.spacing.getValue() *
1232                           spacing_val);
1233         int const pos_end = rowLast(row_ptr);
1234         int labeladdon = 0;
1235         int maxwidth = 0;
1236
1237         // Check if any insets are larger
1238         for (int pos = row_ptr->pos(); pos <= pos_end; ++pos) {
1239                 if (row_ptr->par()->getChar(pos) == Paragraph::META_INSET) {
1240                         tmpfont = getFont(bview->buffer(), row_ptr->par(), pos);
1241                         tmpinset = row_ptr->par()->getInset(pos);
1242                         if (tmpinset) {
1243 #if 1 // this is needed for deep update on initialitation
1244                                 tmpinset->update(bview, tmpfont);
1245 #endif
1246                                 asc = tmpinset->ascent(bview, tmpfont);
1247                                 desc = tmpinset->descent(bview, tmpfont);
1248                                 maxwidth += tmpinset->width(bview, tmpfont);
1249                                 maxasc = max(maxasc, asc);
1250                                 maxdesc = max(maxdesc, desc);
1251                         }
1252                 } else {
1253                         maxwidth += singleWidth(bview, row_ptr->par(), pos);
1254                 }
1255         }
1256
1257         // Check if any custom fonts are larger (Asger)
1258         // This is not completely correct, but we can live with the small,
1259         // cosmetic error for now.
1260         LyXFont::FONT_SIZE maxsize =
1261                 row_ptr->par()->highestFontInRange(row_ptr->pos(), pos_end, size);
1262         if (maxsize > font.size()) {
1263                 font.setSize(maxsize);
1264
1265                 asc = lyxfont::maxAscent(font);
1266                 desc = lyxfont::maxDescent(font);
1267                 if (asc > maxasc) 
1268                         maxasc = asc;
1269                 if (desc > maxdesc)
1270                         maxdesc = desc;
1271         }
1272
1273         // This is nicer with box insets:
1274         ++maxasc;
1275         ++maxdesc;
1276
1277         row_ptr->ascent_of_text(maxasc);
1278    
1279         // is it a top line?
1280         if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
1281       
1282                 // some parksips VERY EASY IMPLEMENTATION
1283                 if (bview->buffer()->params.paragraph_separation ==
1284                         BufferParams::PARSEP_SKIP)
1285                 {
1286                         if (layout.isParagraph()
1287                                 && firstpar->getDepth() == 0
1288                                 && firstpar->previous())
1289                         {
1290                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1291                         } else if (firstpar->previous() &&
1292                                    textclasslist.Style(bview->buffer()->params.textclass,
1293                                                        firstpar->previous()->
1294                                                        getLayout()).isParagraph() &&
1295                                    firstpar->previous()->getDepth() == 0)
1296                         {
1297                                 // is it right to use defskip here too? (AS)
1298                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1299                         }
1300                 }
1301       
1302                 // the paper margins
1303                 if (!row_ptr->par()->previous() && bv_owner)
1304                         maxasc += LYX_PAPER_MARGIN;
1305       
1306                 // add the vertical spaces, that the user added
1307                 if (firstpar->params().spaceTop().kind() != VSpace::NONE)
1308                         maxasc += int(firstpar->params().spaceTop().inPixels(bview));
1309       
1310                 // do not forget the DTP-lines!
1311                 // there height depends on the font of the nearest character
1312                 if (firstpar->params().lineTop())
1313                         maxasc += 2 * lyxfont::ascent('x', getFont(bview->buffer(),
1314                                                                    firstpar, 0));
1315       
1316                 // and now the pagebreaks
1317                 if (firstpar->params().pagebreakTop())
1318                         maxasc += 3 * defaultHeight();
1319       
1320                 // This is special code for the chapter, since the label of this
1321                 // layout is printed in an extra row
1322                 if (layout.labeltype == LABEL_COUNTER_CHAPTER
1323                         && bview->buffer()->params.secnumdepth >= 0)
1324                 {
1325                         float spacing_val = 1.0;
1326                         if (!row_ptr->par()->params().spacing().isDefault()) {
1327                                 spacing_val = row_ptr->par()->params().spacing().getValue();
1328                         } else {
1329                                 spacing_val = bview->buffer()->params.spacing.getValue();
1330                         }
1331               
1332                         labeladdon = int(lyxfont::maxDescent(labelfont) *
1333                                          layout.spacing.getValue() *
1334                                          spacing_val)
1335                                 + int(lyxfont::maxAscent(labelfont) *
1336                                       layout.spacing.getValue() *
1337                                       spacing_val);
1338                 }
1339       
1340                 // special code for the top label
1341                 if ((layout.labeltype == LABEL_TOP_ENVIRONMENT
1342                      || layout.labeltype == LABEL_BIBLIO
1343                      || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1344                     && row_ptr->par()->isFirstInSequence()
1345                     && !row_ptr->par()->getLabelstring().empty())
1346                 {
1347                         float spacing_val = 1.0;
1348                         if (!row_ptr->par()->params().spacing().isDefault()) {
1349                                 spacing_val = row_ptr->par()->params().spacing().getValue();
1350                         } else {
1351                                 spacing_val = bview->buffer()->params.spacing.getValue();
1352                         }
1353               
1354                         labeladdon = int(
1355                                 (lyxfont::maxAscent(labelfont) *
1356                                  layout.spacing.getValue() *
1357                                  spacing_val)
1358                                 +(lyxfont::maxDescent(labelfont) *
1359                                   layout.spacing.getValue() *
1360                                   spacing_val)
1361                                 + layout.topsep * defaultHeight()
1362                                 + layout.labelbottomsep *  defaultHeight());
1363                 }
1364    
1365                 // and now the layout spaces, for example before and after a section, 
1366                 // or between the items of a itemize or enumerate environment
1367       
1368                 if (!firstpar->params().pagebreakTop()) {
1369                         Paragraph * prev = row_ptr->par()->previous();
1370                         if (prev)
1371                                 prev = row_ptr->par()->depthHook(row_ptr->par()->getDepth());
1372                         if (prev && prev->getLayout() == firstpar->getLayout() &&
1373                                 prev->getDepth() == firstpar->getDepth() &&
1374                                 prev->getLabelWidthString() == firstpar->getLabelWidthString())
1375                         {
1376                                 layoutasc = (layout.itemsep * defaultHeight());
1377                         } else if (row_ptr->previous()) {
1378                                 tmptop = layout.topsep;
1379             
1380                                 if (row_ptr->previous()->par()->getDepth() >= row_ptr->par()->getDepth())
1381                                         tmptop -= textclasslist.Style(bview->buffer()->params.textclass,
1382                                                                       row_ptr->previous()->par()->
1383                                                                       getLayout()).bottomsep;
1384             
1385                                 if (tmptop > 0)
1386                                         layoutasc = (tmptop * defaultHeight());
1387                         } else if (row_ptr->par()->params().lineTop()) {
1388                                 tmptop = layout.topsep;
1389             
1390                                 if (tmptop > 0)
1391                                         layoutasc = (tmptop * defaultHeight());
1392                         }
1393          
1394                         prev = row_ptr->par()->outerHook();
1395                         if (prev)  {
1396                                 maxasc += int(textclasslist.Style(bview->buffer()->params.textclass,
1397                                               prev->getLayout()).parsep * defaultHeight());
1398                         } else {
1399                                 if (firstpar->previous() &&
1400                                         firstpar->previous()->getDepth() == 0 &&
1401                                         firstpar->previous()->getLayout() !=
1402                                         firstpar->getLayout())
1403                                 {
1404                                         // avoid parsep
1405                                 } else if (firstpar->previous()) {
1406                                         maxasc += int(layout.parsep * defaultHeight());
1407                                 }
1408                         }
1409                 }
1410         }
1411    
1412         // is it a bottom line?
1413         if (row_ptr->par() == par
1414                 && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par()))
1415         {
1416                 // the paper margins
1417                 if (!par->next() && bv_owner)
1418                         maxdesc += LYX_PAPER_MARGIN;
1419           
1420                 // add the vertical spaces, that the user added
1421                 if (firstpar->params().spaceBottom().kind() != VSpace::NONE)
1422                         maxdesc += int(firstpar->params().spaceBottom().inPixels(bview));
1423           
1424                 // do not forget the DTP-lines!
1425                 // there height depends on the font of the nearest character
1426                 if (firstpar->params().lineBottom())
1427                         maxdesc += 2 * lyxfont::ascent('x',
1428                                                        getFont(bview->buffer(),
1429                                                        par, par->size() - 1));
1430           
1431                 // and now the pagebreaks
1432                 if (firstpar->params().pagebreakBottom())
1433                         maxdesc += 3 * defaultHeight();
1434           
1435                 // and now the layout spaces, for example before and after
1436                 // a section, or between the items of a itemize or enumerate
1437                 // environment
1438                 if (!firstpar->params().pagebreakBottom() && row_ptr->par()->next()) {
1439                         Paragraph * nextpar = row_ptr->par()->next();
1440                         Paragraph * comparepar = row_ptr->par();
1441                         float usual = 0;
1442                         float unusual = 0;
1443              
1444                         if (comparepar->getDepth() > nextpar->getDepth()) {
1445                                 usual = (textclasslist.Style(bview->buffer()->params.textclass,
1446                                          comparepar->getLayout()).bottomsep * defaultHeight());
1447                                 comparepar = comparepar->depthHook(nextpar->getDepth());
1448                                 if (comparepar->getLayout()!= nextpar->getLayout()
1449                                         || nextpar->getLabelWidthString() != 
1450                                         comparepar->getLabelWidthString())
1451                                 {
1452                                         unusual = (textclasslist.Style(bview->buffer()->params.textclass,
1453                                                    comparepar->getLayout()).bottomsep * defaultHeight());
1454                                 }
1455                                 if (unusual > usual)
1456                                         layoutdesc = unusual;
1457                                 else
1458                                         layoutdesc = usual;
1459                         } else if (comparepar->getDepth() ==  nextpar->getDepth()) {
1460                                 
1461                                 if (comparepar->getLayout()!= nextpar->getLayout()
1462                                         || nextpar->getLabelWidthString() != 
1463                                         comparepar->getLabelWidthString())
1464                                         layoutdesc = int(textclasslist.Style(bview->buffer()->params.textclass,
1465                                                                                                                  comparepar->getLayout()).bottomsep * defaultHeight());
1466                         }
1467                 }
1468         }
1469         
1470         // incalculate the layout spaces
1471         maxasc += int(layoutasc * 2 / (2 + firstpar->getDepth()));
1472         maxdesc += int(layoutdesc * 2 / (2 + firstpar->getDepth()));
1473         
1474         // calculate the new height of the text
1475         height -= row_ptr->height();
1476         
1477         row_ptr->height(maxasc + maxdesc + labeladdon);
1478         row_ptr->baseline(maxasc + labeladdon);
1479         
1480         height += row_ptr->height();
1481         float x;
1482         float dummy;
1483         prepareToPrint(bview, row_ptr, x, dummy, dummy, dummy, false);
1484         row_ptr->width(int(maxwidth + x));
1485         if (inset_owner) {
1486                 Row * r = firstrow;
1487                 width = max(0,workWidth(bview));
1488                 while(r) {
1489                         if (r->width() > width)
1490                                 width = r->width();
1491                         r = r->next();
1492                 }
1493         }
1494 }
1495
1496
1497 /* Appends the implicit specified paragraph behind the specified row,
1498  * start at the implicit given position */
1499 void LyXText::appendParagraph(BufferView * bview, Row * row) const
1500 {
1501    bool not_ready = true;
1502    
1503    // The last character position of a paragraph is an invariant so we can 
1504    // safely get it here. (Asger)
1505    int const lastposition = row->par()->size();
1506    do {
1507       // Get the next breakpoint
1508       int z = nextBreakPoint(bview, row, workWidth(bview));
1509       
1510       Row * tmprow = row;
1511
1512       // Insert the new row
1513       if (z < lastposition) {
1514          ++z;
1515          insertRow(row, row->par(), z);
1516          row = row->next();
1517
1518          row->height(0);
1519       } else
1520          not_ready = false;
1521       
1522       // Set the dimensions of the row
1523 #ifdef WITH_WARNINGS
1524 #warning Something is rotten here! (Jug)
1525 #endif
1526       tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1527       setHeightOfRow(bview, tmprow);
1528
1529    } while (not_ready);
1530 }
1531
1532
1533 void LyXText::breakAgain(BufferView * bview, Row * row) const
1534 {
1535         bool not_ready = true;
1536    
1537         do  {
1538                 // get the next breakpoint
1539                 Paragraph::size_type z = nextBreakPoint(bview, row, workWidth(bview));
1540                 Row * tmprow = row;
1541
1542                 if (z < row->par()->size()) {
1543                         if (!row->next() || (row->next() && row->next()->par() != row->par())) {
1544                                 // insert a new row
1545                                 ++z;
1546                                 insertRow(row, row->par(), z);
1547                                 row = row->next();
1548                                 row->height(0);
1549                         } else  {
1550                                 row = row->next();
1551                                 ++z;
1552                                 if (row->pos() == z)
1553                                         not_ready = false;     // the rest will not change
1554                                 else {
1555                                         row->pos(z);
1556                                 }
1557                         }
1558                 } else {
1559                         /* if there are some rows too much, delete them */
1560                         /* only if you broke the whole paragraph! */ 
1561                         Row * tmprow2 = row;
1562                         while (tmprow2->next() && tmprow2->next()->par() == row->par()) {
1563                                 tmprow2 = tmprow2->next();
1564                         }
1565                         while (tmprow2 != row) {
1566                                 tmprow2 = tmprow2->previous();
1567                                 removeRow(tmprow2->next());
1568                         }
1569                         not_ready = false;
1570                 }
1571                 
1572                 /* set the dimensions of the row */ 
1573                 tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1574                 setHeightOfRow(bview, tmprow);
1575         } while (not_ready);
1576 }
1577
1578
1579 // this is just a little changed version of break again
1580 void LyXText::breakAgainOneRow(BufferView * bview, Row * row)
1581 {
1582         // get the next breakpoint
1583         Paragraph::size_type z = nextBreakPoint(bview, row, workWidth(bview));
1584         Row * tmprow = row;
1585
1586         if (z < row->par()->size()) {
1587                 if (!row->next()
1588                     || (row->next() && row->next()->par() != row->par())) {
1589                         /* insert a new row */ 
1590                         ++z;
1591                         insertRow(row, row->par(), z);
1592                         row = row->next();
1593                         row->height(0);
1594                 } else  {
1595                         row= row->next();
1596                         ++z;
1597                         if (row->pos() != z)
1598                                 row->pos(z);
1599                 }
1600         } else {
1601                 // if there are some rows too much, delete them
1602                 // only if you broke the whole paragraph!
1603                 Row * tmprow2 = row;
1604                 while (tmprow2->next()
1605                        && tmprow2->next()->par() == row->par()) {
1606                         tmprow2 = tmprow2->next();
1607                 }
1608                 while (tmprow2 != row) {
1609                         tmprow2 = tmprow2->previous();
1610                         removeRow(tmprow2->next());
1611                 }
1612         }
1613         
1614         // set the dimensions of the row
1615         tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1616         setHeightOfRow(bview, tmprow);
1617 }
1618
1619
1620 void LyXText::breakParagraph(BufferView * bview, char keep_layout)
1621 {
1622    LyXLayout const & layout =
1623            textclasslist.Style(bview->buffer()->params.textclass,
1624                                cursor.par()->getLayout());
1625
1626    // this is only allowed, if the current paragraph is not empty or caption
1627    if ((cursor.par()->size() <= 0)
1628        && layout.labeltype!= LABEL_SENSITIVE)
1629            return;
1630    
1631    setUndo(bview, Undo::INSERT,cursor.par(),cursor.par()->next()); 
1632
1633    // Always break behind a space
1634    //
1635    // It is better to erase the space (Dekel)
1636    if (cursor.pos() < cursor.par()->size()
1637        && cursor.par()->isLineSeparator(cursor.pos()))
1638            cursor.par()->erase(cursor.pos());
1639            // cursor.pos(cursor.pos() + 1);
1640
1641    // break the paragraph
1642    if (keep_layout)
1643      keep_layout = 2;
1644    else 
1645      keep_layout = layout.isEnvironment();
1646    cursor.par()->breakParagraph(bview->buffer()->params, cursor.pos(),
1647                                 keep_layout);
1648
1649    // well this is the caption hack since one caption is really enough
1650    if (layout.labeltype == LABEL_SENSITIVE) {
1651      if (!cursor.pos())
1652              // set to standard-layout
1653              cursor.par()->setLayout(0);
1654      else
1655              // set to standard-layout
1656              cursor.par()->next()->setLayout(0);
1657    }
1658    
1659    /* if the cursor is at the beginning of a row without prior newline, 
1660     * move one row up! 
1661     * This touches only the screen-update. Otherwise we would may have
1662     * an empty row on the screen */
1663    if (cursor.pos() && !cursor.row()->par()->isNewline(cursor.row()->pos() - 1)
1664        && cursor.row()->pos() == cursor.pos()) {
1665            cursorLeft(bview);
1666    } 
1667    
1668    status(bview, LyXText::NEED_MORE_REFRESH);
1669    refresh_row = cursor.row();
1670    refresh_y = cursor.y() - cursor.row()->baseline();
1671    
1672    // Do not forget the special right address boxes
1673    if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1674       while (refresh_row->previous() &&
1675              refresh_row->previous()->par() == refresh_row->par()) {
1676               refresh_row = refresh_row->previous();
1677               refresh_y -= refresh_row->height();
1678       }
1679    }
1680    removeParagraph(cursor.row());
1681    
1682    // set the dimensions of the cursor row
1683    cursor.row()->fill(fill(bview, cursor.row(), workWidth(bview)));
1684
1685    setHeightOfRow(bview, cursor.row());
1686
1687    while (cursor.par()->next()->size()
1688           && cursor.par()->next()->isNewline(0))
1689            cursor.par()->next()->erase(0);
1690    
1691    insertParagraph(bview, cursor.par()->next(), cursor.row());
1692
1693    updateCounters(bview, cursor.row()->previous());
1694    
1695    /* This check is necessary. Otherwise the new empty paragraph will
1696     * be deleted automatically. And it is more friendly for the user! */ 
1697    if (cursor.pos())
1698            setCursor(bview, cursor.par()->next(), 0);
1699    else
1700            setCursor(bview, cursor.par(), 0);
1701    
1702    if (cursor.row()->next())
1703            breakAgain(bview, cursor.row()->next());
1704
1705    need_break_row = 0;
1706 }
1707
1708
1709 // Just a macro to make some thing easier. 
1710 void LyXText::redoParagraph(BufferView * bview) const
1711 {
1712         clearSelection();
1713         redoParagraphs(bview, cursor, cursor.par()->next());
1714         setCursorIntern(bview, cursor.par(), cursor.pos());
1715 }
1716
1717
1718 /* insert a character, moves all the following breaks in the 
1719  * same Paragraph one to the right and make a rebreak */
1720 void LyXText::insertChar(BufferView * bview, char c)
1721 {
1722         setUndo(bview, Undo::INSERT,
1723                 cursor.par(), cursor.par()->next());
1724
1725         // When the free-spacing option is set for the current layout,
1726         // disable the double-space checking
1727
1728         bool const freeSpacing = 
1729                 textclasslist.Style(bview->buffer()->params.textclass,
1730                                cursor.row()->par()->getLayout()).free_spacing;
1731
1732
1733         if (lyxrc.auto_number) {
1734                 static string const number_operators = "+-/*";
1735                 static string const number_unary_operators = "+-";
1736                 static string const number_seperators = ".,:";
1737
1738                 if (current_font.number() == LyXFont::ON) {
1739                         if (!isdigit(c) && !contains(number_operators, c) &&
1740                             !(contains(number_seperators, c) &&
1741                               cursor.pos() >= 1 &&
1742                               cursor.pos() < cursor.par()->size() &&
1743                               getFont(bview->buffer(),
1744                                       cursor.par(),
1745                                       cursor.pos()).number() == LyXFont::ON &&
1746                               getFont(bview->buffer(),
1747                                       cursor.par(),
1748                                       cursor.pos() - 1).number() == LyXFont::ON)
1749                             )
1750                                 number(bview); // Set current_font.number to OFF
1751                 } else if (isdigit(c) &&
1752                            real_current_font.isVisibleRightToLeft()) {
1753                         number(bview); // Set current_font.number to ON
1754
1755                         if (cursor.pos() > 0) {
1756                                 char const c = cursor.par()->getChar(cursor.pos() - 1);
1757                                 if (contains(number_unary_operators, c) &&
1758                                     (cursor.pos() == 1 ||
1759                                      cursor.par()->isSeparator(cursor.pos() - 2) ||
1760                                      cursor.par()->isNewline(cursor.pos() - 2) )
1761                                    ) {
1762                                         setCharFont(bview->buffer(),
1763                                                     cursor.par(),
1764                                                     cursor.pos() - 1,
1765                                                     current_font);
1766                                 } else if (contains(number_seperators, c) &&
1767                                            cursor.pos() >= 2 &&
1768                                            getFont(bview->buffer(),
1769                                                    cursor.par(),
1770                                                    cursor.pos() - 2).number() == LyXFont::ON) {
1771                                         setCharFont(bview->buffer(),
1772                                                     cursor.par(),
1773                                                     cursor.pos() - 1,
1774                                                     current_font);
1775                                 }
1776                         }
1777                 }
1778         }
1779
1780
1781         /* First check, if there will be two blanks together or a blank at 
1782           the beginning of a paragraph. 
1783           I decided to handle blanks like normal characters, the main 
1784           difference are the special checks when calculating the row.fill
1785           (blank does not count at the end of a row) and the check here */ 
1786
1787         // The bug is triggered when we type in a description environment:
1788         // The current_font is not changed when we go from label to main text
1789         // and it should (along with realtmpfont) when we type the space.
1790         // CHECK There is a bug here! (Asger)
1791         
1792         LyXFont realtmpfont = real_current_font;
1793         LyXFont rawtmpfont = current_font;  /* store the current font.
1794                                      * This is because of the use
1795                                      * of cursor movements. The moving
1796                                      * cursor would refresh the 
1797                                      * current font */
1798
1799         // Get the font that is used to calculate the baselineskip
1800         Paragraph::size_type const lastpos = cursor.par()->size();
1801         LyXFont rawparfont =
1802                 cursor.par()->getFontSettings(bview->buffer()->params,
1803                                               lastpos - 1);
1804
1805         bool jumped_over_space = false;
1806    
1807         if (!freeSpacing && IsLineSeparatorChar(c)) {
1808                 if ((cursor.pos() > 0 
1809                      && cursor.par()->isLineSeparator(cursor.pos() - 1))
1810                     || (cursor.pos() > 0
1811                         && cursor.par()->isNewline(cursor.pos() - 1))
1812                     || (cursor.pos() == 0)) {
1813                         static bool sent_space_message = false;
1814                         if (!sent_space_message) {
1815                                 if (cursor.pos() == 0) 
1816                                         bview->owner()->message(_("You cannot insert a space at the beginning of a paragraph.  Please read the Tutorial."));
1817                                 else
1818                                         bview->owner()->message(_("You cannot type two spaces this way.  Please read the Tutorial."));
1819                                 sent_space_message = true;
1820                         }
1821                         charInserted();
1822                         return;
1823                 }
1824         } else if (IsNewlineChar(c)) {
1825                 if (cursor.par() == cursor.par()
1826                     && cursor.pos() <= beginningOfMainBody(bview->buffer(), cursor.par())) {
1827                         charInserted();
1828                         return;
1829                 }
1830                 /* No newline at first position 
1831                  * of a paragraph or behind labels. 
1832                  * TeX does not allow that. */
1833
1834                 if (cursor.pos() < cursor.par()->size() &&
1835                     cursor.par()->isLineSeparator(cursor.pos()))
1836                         // newline always after a blank!
1837                         cursorRight(bview);
1838                 cursor.row()->fill(-1);        // to force a new break
1839         }
1840    
1841         // the display inset stuff
1842         if (cursor.row()->par()->getChar(cursor.row()->pos()) == Paragraph::META_INSET
1843             && cursor.row()->par()->getInset(cursor.row()->pos())
1844             && (cursor.row()->par()->getInset(cursor.row()->pos())->display() ||
1845                 cursor.row()->par()->getInset(cursor.row()->pos())->needFullRow()))
1846                 cursor.row()->fill(-1); // to force a new break  
1847
1848         // get the cursor row fist
1849         Row * row = cursor.row();
1850         int y = cursor.y() - row->baseline();
1851         if (c != Paragraph::META_INSET) /* Here case LyXText::InsertInset 
1852                                             * already insertet the character */
1853                 cursor.par()->insertChar(cursor.pos(), c);
1854         setCharFont(bview->buffer(), cursor.par(), cursor.pos(), rawtmpfont);
1855
1856         if (!jumped_over_space) {
1857                 // refresh the positions
1858                 Row * tmprow = row;
1859                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
1860                         tmprow = tmprow->next();
1861                         tmprow->pos(tmprow->pos() + 1);
1862                 }
1863         }
1864    
1865         // Is there a break one row above
1866         if ((cursor.par()->isLineSeparator(cursor.pos())
1867              || cursor.par()->isNewline(cursor.pos())
1868              || cursor.row()->fill() == -1)
1869             && row->previous() && row->previous()->par() == row->par()) {
1870                 Paragraph::size_type z = nextBreakPoint(bview,
1871                                                            row->previous(),
1872                                                            workWidth(bview));
1873                 if (z >= row->pos()) {
1874                         row->pos(z + 1);
1875                         
1876                         // set the dimensions of the row above
1877                         row->previous()->fill(fill(bview,
1878                                                    row->previous(),
1879                                                    workWidth(bview)));
1880
1881                         setHeightOfRow(bview, row->previous());
1882              
1883                         y -= row->previous()->height();
1884                         refresh_y = y;
1885                         refresh_row = row->previous();
1886                         status(bview, LyXText::NEED_MORE_REFRESH);
1887              
1888                         breakAgainOneRow(bview, row);
1889
1890                         current_font = rawtmpfont;
1891                         real_current_font = realtmpfont;
1892                         setCursor(bview, cursor.par(), cursor.pos() + 1,
1893                                   false, cursor.boundary());
1894                         // cursor MUST be in row now.
1895              
1896                         if (row->next() && row->next()->par() == row->par())
1897                                 need_break_row = row->next();
1898                         else
1899                                 need_break_row = 0;
1900              
1901                         // check, wether the last characters font has changed.
1902                         if (cursor.pos() && cursor.pos() == cursor.par()->size()
1903                             && rawparfont != rawtmpfont)
1904                                 redoHeightOfParagraph(bview, cursor);
1905                         
1906                         charInserted();
1907                         return;
1908                 }
1909         }
1910    
1911         // recalculate the fill of the row
1912         if (row->fill() >= 0)  /* needed because a newline
1913                               * will set fill to -1. Otherwise
1914                               * we would not get a rebreak! */
1915                 row->fill(fill(bview, row, workWidth(bview)));
1916         if (row->fill() < 0) {
1917                 refresh_y = y;
1918                 refresh_row = row; 
1919                 refresh_x = cursor.x();
1920                 refresh_pos = cursor.pos();
1921                 status(bview, LyXText::NEED_MORE_REFRESH);
1922                 breakAgainOneRow(bview, row); 
1923                 // will the cursor be in another row now?
1924                 if (rowLast(row) <= cursor.pos() + 1 && row->next()) {
1925                         if (row->next() && row->next()->par() == row->par())
1926                                 // this should always be true
1927                                 row = row->next();
1928                         breakAgainOneRow(bview, row);
1929                 }
1930                 current_font = rawtmpfont;
1931                 real_current_font = realtmpfont;
1932
1933                 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1934                           cursor.boundary());
1935                 if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
1936                     != cursor.boundary())
1937                         setCursor(bview, cursor.par(), cursor.pos(), false,
1938                           !cursor.boundary());
1939                 if (row->next() && row->next()->par() == row->par())
1940                         need_break_row = row->next();
1941                 else
1942                         need_break_row = 0;             
1943         } else {
1944                 refresh_y = y;
1945                 refresh_x = cursor.x();
1946                 refresh_row = row;
1947                 refresh_pos = cursor.pos();
1948                 
1949                 int const tmpheight = row->height();
1950                 setHeightOfRow(bview, row);
1951                 if (tmpheight == row->height())
1952                         status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1953                 else
1954                         status(bview, LyXText::NEED_MORE_REFRESH);
1955             
1956                 current_font = rawtmpfont;
1957                 real_current_font = realtmpfont;
1958                 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1959                           cursor.boundary());
1960         }
1961
1962         // check, wether the last characters font has changed.
1963         if (cursor.pos() && cursor.pos() == cursor.par()->size()
1964             && rawparfont != rawtmpfont) {
1965                 redoHeightOfParagraph(bview, cursor);
1966         } else {
1967                 // now the special right address boxes
1968                 if (textclasslist.Style(bview->buffer()->params.textclass,
1969                                    cursor.par()->getLayout()).margintype
1970                     == MARGIN_RIGHT_ADDRESS_BOX) {
1971                         redoDrawingOfParagraph(bview, cursor); 
1972                 }
1973         }
1974
1975         charInserted();
1976 }
1977    
1978
1979 void LyXText::charInserted()
1980 {
1981         // Here we could call FinishUndo for every 20 characters inserted.
1982         // This is from my experience how emacs does it.
1983         static unsigned int counter;
1984         if (counter < 20) {
1985                 ++counter;
1986         } else {
1987                 finishUndo();
1988                 counter = 0;
1989         }
1990 }
1991
1992
1993 void LyXText::prepareToPrint(BufferView * bview,
1994                              Row * row, float & x,
1995                              float & fill_separator, 
1996                              float & fill_hfill,
1997                              float & fill_label_hfill,
1998                              bool bidi) const
1999 {
2000         float nlh;
2001         float ns;
2002         
2003         float w = row->fill();
2004         fill_hfill = 0;
2005         fill_label_hfill = 0;
2006         fill_separator = 0;
2007         fill_label_hfill = 0;
2008
2009         bool const is_rtl =
2010                 row->par()->isRightToLeftPar(bview->buffer()->params);
2011         if (is_rtl) {
2012                 x = (workWidth(bview) > 0)
2013                         ? rightMargin(bview->buffer(), row) : 0;
2014         } else
2015                 x = (workWidth(bview) > 0) ? leftMargin(bview, row) : 0;
2016         
2017         // is there a manual margin with a manual label
2018         if (textclasslist.Style(bview->buffer()->params.textclass,
2019                            row->par()->getLayout()).margintype == MARGIN_MANUAL
2020             && textclasslist.Style(bview->buffer()->params.textclass,
2021                               row->par()->getLayout()).labeltype == LABEL_MANUAL) {
2022                
2023                 /* one more since labels are left aligned */ 
2024                 nlh = numberOfLabelHfills(bview->buffer(), row) + 1;
2025                 if (nlh && !row->par()->getLabelWidthString().empty()) {
2026                         fill_label_hfill = labelFill(bview, row) / nlh;
2027                 }
2028         }
2029                 
2030         // are there any hfills in the row?
2031         float const nh = numberOfHfills(bview->buffer(), row);
2032
2033         if (nh)
2034           fill_hfill = w / nh;
2035         else  {
2036                 // is it block, flushleft or flushright? 
2037                 // set x how you need it
2038         int align;
2039         if (row->par()->params().align() == LYX_ALIGN_LAYOUT)
2040           align = textclasslist.Style(bview->buffer()->params.textclass, row->par()->getLayout()).align;
2041         else
2042           align = row->par()->params().align();
2043            
2044         // center displayed insets 
2045         Inset * inset;
2046            if (row->par()->getChar(row->pos()) == Paragraph::META_INSET
2047                && (inset=row->par()->getInset(row->pos()))
2048                && (inset->display())) // || (inset->scroll() < 0)))
2049              align = (inset->lyxCode() == Inset::MATHMACRO_CODE)
2050                      ? LYX_ALIGN_BLOCK : LYX_ALIGN_CENTER;
2051
2052            switch (align) {
2053             case LYX_ALIGN_BLOCK:
2054               ns = numberOfSeparators(bview->buffer(), row);
2055               if (ns && row->next() && row->next()->par() == row->par() &&
2056                   !(row->next()->par()->isNewline(row->next()->pos() - 1))
2057                   && !(row->next()->par()->getChar(row->next()->pos()) == Paragraph::META_INSET
2058                        && row->next()->par()->getInset(row->next()->pos())
2059                        && row->next()->par()->getInset(row->next()->pos())->display())
2060                   )
2061                 fill_separator = w / ns;
2062               else if (is_rtl)
2063                 x += w;
2064               break;
2065             case LYX_ALIGN_RIGHT:
2066               x += w;
2067               break;
2068             case LYX_ALIGN_CENTER:
2069               x += w / 2;
2070               break;
2071            }
2072         }
2073         if (!bidi)
2074                 return;
2075
2076         computeBidiTables(bview->buffer(), row);
2077         if (is_rtl) {
2078                 Paragraph::size_type main_body = 
2079                         beginningOfMainBody(bview->buffer(), row->par());
2080                 Paragraph::size_type last = rowLast(row);
2081
2082                 if (main_body > 0 &&
2083                     (main_body-1 > last || 
2084                      !row->par()->isLineSeparator(main_body-1))) {
2085                         LyXLayout const & layout =
2086                                 textclasslist.Style(bview->buffer()->params.textclass,
2087                                                     row->par()->getLayout());
2088                         x += lyxfont::width(layout.labelsep,
2089                                             getLabelFont(bview->buffer(), row->par()));
2090                         if (main_body-1 <= last)
2091                                 x += fill_label_hfill;
2092                 }
2093         }
2094 }
2095       
2096 /* important for the screen */
2097
2098
2099 /* the cursor set functions have a special mechanism. When they
2100 * realize, that you left an empty paragraph, they will delete it.
2101 * They also delete the corresponding row */
2102
2103 void LyXText::cursorRightOneWord(BufferView * bview) const
2104 {
2105         // treat floats, HFills and Insets as words
2106         LyXCursor tmpcursor = cursor;
2107         // CHECK See comment on top of text.C
2108
2109         if (tmpcursor.pos() == tmpcursor.par()->size()
2110             && tmpcursor.par()->next()) {
2111                         tmpcursor.par(tmpcursor.par()->next());
2112                         tmpcursor.pos(0);
2113         } else {
2114                 int steps = 0;
2115
2116                 // Skip through initial nonword stuff.
2117                 while (tmpcursor.pos() < tmpcursor.par()->size() &&
2118                        ! tmpcursor.par()->isWord(tmpcursor.pos())) {
2119                   //    printf("Current pos1 %d", tmpcursor.pos()) ;
2120                         tmpcursor.pos(tmpcursor.pos() + 1);
2121                         ++steps;
2122                 }
2123                 // Advance through word.
2124                 while (tmpcursor.pos() < tmpcursor.par()->size() &&
2125                         tmpcursor.par()->isWord( tmpcursor.pos())) {
2126                   //     printf("Current pos2 %d", tmpcursor.pos()) ;
2127                         tmpcursor.pos(tmpcursor.pos() + 1);
2128                         ++steps;
2129                 }
2130         }
2131         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2132 }
2133
2134
2135 void LyXText::cursorTab(BufferView * bview) const
2136 {
2137     LyXCursor tmpcursor = cursor;
2138     while (tmpcursor.pos() < tmpcursor.par()->size()
2139            && !tmpcursor.par()->isNewline(tmpcursor.pos()))
2140         tmpcursor.pos(tmpcursor.pos() + 1);
2141
2142     if (tmpcursor.pos() == tmpcursor.par()->size()){
2143         if (tmpcursor.par()->next()) {
2144             tmpcursor.par(tmpcursor.par()->next());
2145             tmpcursor.pos(0);
2146         }
2147     } else
2148         tmpcursor.pos(tmpcursor.pos() + 1);
2149     setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2150 }
2151
2152
2153 /* -------> Skip initial whitespace at end of word and move cursor to *start*
2154             of prior word, not to end of next prior word. */
2155
2156 void LyXText::cursorLeftOneWord(BufferView * bview)  const
2157 {
2158         LyXCursor tmpcursor = cursor;
2159         cursorLeftOneWord(tmpcursor);
2160         setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2161 }
2162
2163 void LyXText::cursorLeftOneWord(LyXCursor  & cur)  const
2164 {
2165         // treat HFills, floats and Insets as words
2166         cur = cursor;
2167         while (cur.pos() 
2168                && (cur.par()->isSeparator(cur.pos() - 1) 
2169                    || cur.par()->isKomma(cur.pos() - 1))
2170                && !(cur.par()->isHfill(cur.pos() - 1)
2171                     || cur.par()->isInset(cur.pos() - 1)))
2172                 cur.pos(cur.pos() - 1);
2173
2174         if (cur.pos()
2175             && (cur.par()->isInset(cur.pos() - 1)
2176                 || cur.par()->isHfill(cur.pos() - 1))) {
2177                 cur.pos(cur.pos() - 1);
2178         } else if (!cur.pos()) {
2179                 if (cur.par()->previous()){
2180                         cur.par(cur.par()->previous());
2181                         cur.pos(cur.par()->size());
2182                 }
2183         } else {                // Here, cur != 0 
2184                 while (cur.pos() > 0 &&
2185                        cur.par()->isWord(cur.pos()-1) )
2186                         cur.pos(cur.pos() - 1);
2187         }
2188 }
2189
2190 /* -------> Select current word. This depends on behaviour of
2191 CursorLeftOneWord(), so it is patched as well. */
2192 void LyXText::getWord(LyXCursor & from, LyXCursor & to, 
2193                       word_location const loc) const
2194 {
2195         // first put the cursor where we wana start to select the word
2196         from = cursor;
2197         switch(loc) {
2198         case WHOLE_WORD_STRICT:
2199                 if (cursor.pos() == 0 || cursor.pos() == cursor.par()->size()
2200                     || cursor.par()->isSeparator(cursor.pos())
2201                     || cursor.par()->isKomma(cursor.pos())
2202                     || cursor.par()->isSeparator(cursor.pos() -1)
2203                     || cursor.par()->isKomma(cursor.pos() -1)) {
2204                         to = from;
2205                         return;
2206                 }
2207                 // no break here, we go to the next
2208                 
2209         case WHOLE_WORD:
2210                 // Move cursor to the beginning, when not already there.
2211                 if (from.pos() && !from.par()->isSeparator(from.pos() - 1)
2212                     && !from.par()->isKomma(from.pos() - 1))
2213                         cursorLeftOneWord(from);
2214                 break;
2215         case PREVIOUS_WORD:
2216                 // always move the cursor to the beginning of previous word
2217                 cursorLeftOneWord(from);
2218                 break;
2219         case NEXT_WORD:
2220                 lyxerr << "LyXText::getWord: NEXT_WORD not implemented yet\n";
2221                 break;
2222         case PARTIAL_WORD:
2223                 break;
2224         }
2225         to = from;
2226         while (to.pos() < to.par()->size()
2227                && !to.par()->isSeparator(to.pos())
2228                && !to.par()->isKomma(to.pos())
2229                && !to.par()->isHfill(to.pos()) )
2230         {
2231                 to.pos(to.pos() + 1);
2232         }
2233 }
2234
2235
2236 void LyXText::selectWord(BufferView * bview, word_location const loc) 
2237 {
2238         LyXCursor from;
2239         LyXCursor to;
2240         getWord(from, to, loc);
2241         if (cursor != from)
2242                 setCursor(bview, from.par(), from.pos());
2243         if (to == from)
2244                 return;
2245         selection.cursor = cursor;
2246         setCursor(bview, to.par(), to.pos() );
2247         setSelection(bview);
2248 }
2249
2250
2251 /* -------> Select the word currently under the cursor when no
2252         selection is currently set */
2253 bool LyXText::selectWordWhenUnderCursor(BufferView * bview, 
2254                                         word_location const loc) 
2255 {
2256         if (!selection.set()) {
2257                 selectWord(bview, loc);
2258                 return selection.set();
2259         }
2260         return false;
2261 }
2262
2263
2264 // This function is only used by the spellchecker for NextWord().
2265 // It doesn't handle LYX_ACCENTs and probably never will.
2266 string const LyXText::selectNextWord(BufferView * bview,
2267                                      float & value) const
2268 {
2269         if (the_locking_inset) {
2270                 string str = the_locking_inset->selectNextWord(bview, value);
2271                 if (!str.empty()) {
2272                         value += float(cursor.y())/float(height);
2273                         return str;
2274                 }
2275 #warning Dekel please have a look on this one RTL? (Jug)
2276                 // we have to go on checking so move cusor to the right
2277                 if (cursor.pos() == cursor.par()->size()) {
2278                         if (!cursor.par()->next())
2279                                 return str;
2280                         cursor.par(cursor.par()->next());
2281                         cursor.pos(0);
2282                 } else
2283                         cursor.pos(cursor.pos() + 1);
2284         }
2285         Paragraph * tmppar = cursor.par();
2286         
2287         // If this is not the very first word, skip rest of
2288         // current word because we are probably in the middle
2289         // of a word if there is text here.
2290         if (cursor.pos() || cursor.par()->previous()) {
2291                 while (cursor.pos() < cursor.par()->size()
2292                        && cursor.par()->isLetter(cursor.pos()))
2293                         cursor.pos(cursor.pos() + 1);
2294         }
2295         
2296         // Now, skip until we have real text (will jump paragraphs)
2297         while ((cursor.par()->size() > cursor.pos()
2298                && (!cursor.par()->isLetter(cursor.pos()))
2299                && (!cursor.par()->isInset(cursor.pos()) ||
2300                    !cursor.par()->getInset(cursor.pos())->isTextInset()))
2301                || (cursor.par()->size() == cursor.pos()
2302                    && cursor.par()->next()))
2303         {
2304                 if (cursor.pos() == cursor.par()->size()) {
2305                         cursor.par(cursor.par()->next());
2306                         cursor.pos(0);
2307                 } else
2308                         cursor.pos(cursor.pos() + 1);
2309         }
2310
2311         // now check if we hit an inset so it has to be a inset containing text!
2312         if (cursor.pos() < cursor.par()->size() &&
2313                 cursor.par()->isInset(cursor.pos()))
2314         {
2315                 // lock the inset!
2316                 cursor.par()->getInset(cursor.pos())->edit(bview);
2317                 // now call us again to do the above trick
2318                 // but obviously we have to start from down below ;)
2319                 return bview->text->selectNextWord(bview, value);
2320         }               
2321   
2322         // Update the value if we changed paragraphs
2323         if (cursor.par() != tmppar){
2324                 setCursor(bview, cursor.par(), cursor.pos());
2325                 value = float(cursor.y())/float(height);
2326         }
2327
2328         // Start the selection from here
2329         selection.cursor = cursor;
2330         
2331         Inset * inset;
2332
2333         // and find the end of the word 
2334         // (optional hyphens are part of a word)
2335         while (cursor.pos() < cursor.par()->size()
2336                && (cursor.par()->isLetter(cursor.pos())) 
2337                // assignment is intentional here
2338                || ((inset = getInset())
2339                    && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2340                    && static_cast<InsetSpecialChar *>(inset)->kind()
2341                         == InsetSpecialChar::HYPHENATION
2342                    ))
2343                 cursor.pos(cursor.pos() + 1);
2344
2345         // Finally, we copy the word to a string and return it
2346         string str;
2347         if (selection.cursor.pos() < cursor.pos()) {
2348                 Paragraph::size_type i;
2349                 for (i = selection.cursor.pos(); i < cursor.pos(); ++i) {
2350                         if (cursor.par()->getChar(i) != Paragraph::META_INSET)
2351                                 str += cursor.par()->getChar(i);
2352                 }
2353         }
2354         return str;
2355 }
2356
2357
2358 // This one is also only for the spellchecker
2359 void LyXText::selectSelectedWord(BufferView * bview)
2360 {
2361         if (the_locking_inset) {
2362                 the_locking_inset->selectSelectedWord(bview);
2363                 return;
2364         }
2365         // move cursor to the beginning
2366         setCursor(bview, selection.cursor.par(), selection.cursor.pos());
2367         
2368         // set the sel cursor
2369         selection.cursor = cursor;
2370         Inset * inset;
2371         
2372         // now find the end of the word
2373         while (cursor.pos() < cursor.par()->size()
2374                && (cursor.par()->isLetter(cursor.pos())
2375                    // assignment is intentional here
2376                    || ((inset = getInset())
2377                        && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2378                        && static_cast<InsetSpecialChar *>(inset)->kind()
2379                                 == InsetSpecialChar::HYPHENATION
2380                        )))
2381                 cursor.pos(cursor.pos() + 1);
2382         
2383         setCursor(bview, cursor.par(), cursor.pos());
2384         
2385         // finally set the selection
2386         setSelection(bview);
2387 }
2388
2389
2390 /* -------> Delete from cursor up to the end of the current or next word. */
2391 void LyXText::deleteWordForward(BufferView * bview)
2392 {
2393         if (!cursor.par()->size())
2394                 cursorRight(bview);
2395         else {
2396                 LyXCursor tmpcursor = cursor;
2397                 tmpcursor.row(0); // ??
2398                 selection.set(true); // to avoid deletion
2399                 cursorRightOneWord(bview);
2400                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2401                 selection.cursor = cursor;
2402                 cursor = tmpcursor;
2403                 setSelection(bview);
2404                 
2405                 /* -----> Great, CutSelection() gets rid of multiple spaces. */
2406                 cutSelection(bview, true, false);
2407         }
2408 }
2409
2410
2411 /* -------> Delete from cursor to start of current or prior word. */
2412 void LyXText::deleteWordBackward(BufferView * bview)
2413 {
2414        if (!cursor.par()->size())
2415                cursorLeft(bview);
2416        else {
2417                LyXCursor tmpcursor = cursor;
2418                tmpcursor.row(0); // ??
2419                selection.set(true); // to avoid deletion
2420                cursorLeftOneWord(bview);
2421                setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2422                selection.cursor = cursor;
2423                cursor = tmpcursor;
2424                setSelection(bview);
2425                cutSelection(bview, true, false);
2426        }
2427 }
2428
2429
2430 /* -------> Kill to end of line. */
2431 void LyXText::deleteLineForward(BufferView * bview)
2432 {
2433         if (!cursor.par()->size())
2434                 // Paragraph is empty, so we just go to the right
2435                 cursorRight(bview);
2436         else {
2437                 LyXCursor tmpcursor = cursor;
2438                 // We can't store the row over a regular setCursor
2439                 // so we set it to 0 and reset it afterwards.
2440                 tmpcursor.row(0); // ??
2441                 selection.set(true); // to avoid deletion
2442                 cursorEnd(bview);
2443                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2444                 selection.cursor = cursor;
2445                 cursor = tmpcursor;
2446                 setSelection(bview);
2447                 // What is this test for ??? (JMarc)
2448                 if (!selection.set()) {
2449                         deleteWordForward(bview);
2450                 } else {
2451                         cutSelection(bview, true, false);
2452                 }
2453         }
2454 }
2455
2456
2457 // Change the case of a word at cursor position. 
2458 // This function directly manipulates Paragraph::text because there
2459 // is no Paragraph::SetChar currently. I did what I could to ensure
2460 // that it is correct. I guess part of it should be moved to
2461 // Paragraph, but it will have to change for 1.1 anyway. At least
2462 // it does not access outside of the allocated array as the older
2463 // version did. (JMarc) 
2464 void LyXText::changeCase(BufferView * bview, LyXText::TextCase action)
2465 {
2466         LyXCursor from;
2467         LyXCursor to;
2468
2469         if (selection.set()) {
2470                 from = selection.start;
2471                 to = selection.end;
2472         } else {
2473                 getWord(from, to, PARTIAL_WORD);
2474                 setCursor(bview, to.par(), to.pos() + 1);
2475         }
2476
2477         changeRegionCase(bview, from, to, action);
2478 }
2479
2480
2481 void LyXText::changeRegionCase(BufferView * bview,
2482                                LyXCursor const & from,
2483                                LyXCursor const & to,
2484                                LyXText::TextCase action)
2485 {
2486         lyx::Assert(from <= to);
2487         
2488         setUndo(bview, Undo::FINISH,
2489                 from.par(), to.par()->next());
2490
2491         Paragraph::size_type pos = from.pos();
2492         Paragraph * par = from.par();
2493
2494         while (par && (pos != to.pos() || par != to.par())) {
2495                 unsigned char c = par->getChar(pos);
2496                 if (!IsInsetChar(c) && !IsHfillChar(c)) {
2497                         switch (action) {
2498                         case text_lowercase:
2499                                 c = tolower(c);
2500                                 break;
2501                         case text_capitalization:
2502                                 c = toupper(c);
2503                                 action = text_lowercase;
2504                                 break;
2505                         case text_uppercase:
2506                                 c = toupper(c);
2507                                 break;
2508                         }
2509                 }
2510                 par->setChar(pos, c);
2511                 checkParagraph(bview, par, pos);
2512
2513                 ++pos;
2514                 if (pos == par->size()) {
2515                         par = par->next();
2516                         pos = 0;
2517                 }
2518         }
2519         if (to.row() != from.row()) {
2520                 refresh_y = from.y() - from.row()->baseline();
2521                 refresh_row = from.row();
2522                 status(bview, LyXText::NEED_MORE_REFRESH);
2523         }
2524 }
2525
2526
2527 void LyXText::transposeChars(BufferView & bview)
2528 {
2529         Paragraph * tmppar = cursor.par();
2530
2531         setUndo(&bview, Undo::FINISH,
2532                 tmppar, tmppar->next()); 
2533
2534         Paragraph::size_type tmppos = cursor.pos();
2535
2536         // First decide if it is possible to transpose at all
2537
2538         // We are at the beginning of a paragraph.
2539         if (tmppos == 0) return;
2540
2541         // We are at the end of a paragraph.
2542         if (tmppos == tmppar->size() - 1) return;
2543
2544         unsigned char c1 = tmppar->getChar(tmppos);
2545         unsigned char c2 = tmppar->getChar(tmppos - 1);
2546
2547         if (c1 != Paragraph::META_INSET
2548             && c2 != Paragraph::META_INSET) {
2549                 tmppar->setChar(tmppos, c2);
2550                 tmppar->setChar(tmppos - 1, c1);
2551         }
2552         // We should have an implementation that handles insets
2553         // as well, but that will have to come later. (Lgb)
2554         checkParagraph(const_cast<BufferView*>(&bview), tmppar, tmppos);
2555 }
2556
2557
2558 void LyXText::Delete(BufferView * bview)
2559 {
2560         // this is a very easy implementation
2561
2562         LyXCursor old_cursor = cursor;
2563         int const old_cur_par_id = old_cursor.par()->id();
2564         int const old_cur_par_prev_id = old_cursor.par()->previous() ?
2565                 old_cursor.par()->previous()->id() : 0;
2566         
2567         // just move to the right
2568         cursorRight(bview);
2569
2570         // CHECK Look at the comment here.
2571         // This check is not very good...
2572         // The cursorRightIntern calls DeleteEmptyParagrapgMechanism
2573         // and that can very well delete the par or par->previous in
2574         // old_cursor. Will a solution where we compare paragraph id's
2575         //work better?
2576         if ((cursor.par()->previous() ? cursor.par()->previous()->id() : 0)
2577             == old_cur_par_prev_id
2578             && cursor.par()->id() != old_cur_par_id)
2579         {
2580                 return; // delete-empty-paragraph-mechanism has done it
2581         }
2582
2583         // if you had success make a backspace
2584         if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2585                 LyXCursor tmpcursor = cursor;
2586                 cursor = old_cursor; // to make sure undo gets the right cursor position
2587                 setUndo(bview, Undo::DELETE,
2588                         cursor.par(), cursor.par()->next()); 
2589                 cursor = tmpcursor;
2590                 backspace(bview);
2591         }
2592 }
2593
2594
2595 void LyXText::backspace(BufferView * bview)
2596 {
2597         // Get the font that is used to calculate the baselineskip
2598         Paragraph::size_type lastpos = cursor.par()->size();
2599         LyXFont rawparfont =
2600                 cursor.par()->getFontSettings(bview->buffer()->params,
2601                                               lastpos - 1);
2602
2603         if (cursor.pos() == 0) {
2604                 // The cursor is at the beginning of a paragraph,
2605                 // so the the backspace will collapse two paragraphs into one.
2606                 
2607                 // we may paste some paragraphs
2608       
2609                 // is it an empty paragraph?
2610       
2611                 if ((lastpos == 0
2612                      || (lastpos == 1 && cursor.par()->isSeparator(0)))) {
2613                         // This is an empty paragraph and we delete it just by moving the cursor one step
2614                         // left and let the DeleteEmptyParagraphMechanism handle the actual deletion
2615                         // of the paragraph.
2616                         
2617                         if (cursor.par()->previous()) {
2618                                 Paragraph * tmppar = cursor.par()->previous();
2619                                 if (cursor.par()->getLayout() == tmppar->getLayout()
2620                                     && cursor.par()->getAlign() == tmppar->getAlign()) {
2621                                         // Inherit bottom DTD from the paragraph below.
2622                                         // (the one we are deleting)
2623                                         tmppar->params().lineBottom(cursor.par()->params().lineBottom());
2624                                         tmppar->params().spaceBottom(cursor.par()->params().spaceBottom());
2625                                         tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom());
2626                                 }
2627                                 
2628                                 cursorLeft(bview);
2629                      
2630                                 // the layout things can change the height of a row !
2631                                 int const tmpheight = cursor.row()->height();
2632                                 setHeightOfRow(bview, cursor.row());
2633                                 if (cursor.row()->height() != tmpheight) {
2634                                         refresh_y = cursor.y() - cursor.row()->baseline();
2635                                         refresh_row = cursor.row();
2636                                         status(bview, LyXText::NEED_MORE_REFRESH);
2637                                 }
2638                                 return;
2639                         }
2640                 }
2641
2642                 if (cursor.par()->previous()) {
2643                         setUndo(bview, Undo::DELETE,
2644                                 cursor.par()->previous(), cursor.par()->next());
2645                 }
2646                 
2647                 Paragraph * tmppar = cursor.par();
2648                 Row * tmprow = cursor.row();
2649
2650                 // We used to do cursorLeftIntern() here, but it is
2651                 // not a good idea since it triggers the auto-delete
2652                 // mechanism. So we do a cursorLeftIntern()-lite,
2653                 // without the dreaded mechanism. (JMarc)
2654                 if (cursor.par()->previous()) { 
2655                         // steps into the above paragraph.
2656                         setCursorIntern(bview, cursor.par()->previous(),
2657                                         cursor.par()->previous()->size(),
2658                                         false);
2659                 }
2660
2661                 /* Pasting is not allowed, if the paragraphs have different
2662                    layout. I think it is a real bug of all other
2663                    word processors to allow it. It confuses the user.
2664                    Even so with a footnote paragraph and a non-footnote
2665                    paragraph. I will not allow pasting in this case, 
2666                    because the user would be confused if the footnote behaves 
2667                    different wether it is open or closed.
2668                   
2669                    Correction: Pasting is always allowed with standard-layout
2670                 */
2671                 if (cursor.par() != tmppar
2672                     && (cursor.par()->getLayout() == tmppar->getLayout()
2673                         || tmppar->getLayout() == 0 /*standard*/)
2674                     && cursor.par()->getAlign() == tmppar->getAlign())
2675                 {
2676                         removeParagraph(tmprow);
2677                         removeRow(tmprow);
2678                         cursor.par()->pasteParagraph(bview->buffer()->params);
2679                         
2680                         if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1))
2681                                 ; //cursor.par()->insertChar(cursor.pos(), ' ');
2682                         // strangely enough it seems that commenting out the line above removes
2683                         // most or all of the segfaults. I will however also try to move the
2684                         // two Remove... lines in front of the PasteParagraph too.
2685                         else
2686                                 if (cursor.pos())
2687                                         cursor.pos(cursor.pos() - 1);
2688                         
2689                         status(bview, LyXText::NEED_MORE_REFRESH);
2690                         refresh_row = cursor.row();
2691                         refresh_y = cursor.y() - cursor.row()->baseline();
2692                         
2693                         // remove the lost paragraph
2694                         // This one is not safe, since the paragraph that the tmprow and the
2695                         // following rows belong to has been deleted by the PasteParagraph
2696                         // above. The question is... could this be moved in front of the
2697                         // PasteParagraph?
2698                         //RemoveParagraph(tmprow);
2699                         //RemoveRow(tmprow);  
2700                         
2701                         // This rebuilds the rows.
2702                         appendParagraph(bview, cursor.row());
2703                         updateCounters(bview, cursor.row());
2704                         
2705                         // the row may have changed, block, hfills etc.
2706                         setCursor(bview, cursor.par(), cursor.pos(), false);
2707                 }
2708         } else {
2709                 /* this is the code for a normal backspace, not pasting
2710                  * any paragraphs */ 
2711                 setUndo(bview, Undo::DELETE,
2712                         cursor.par(), cursor.par()->next()); 
2713                 // We used to do cursorLeftIntern() here, but it is
2714                 // not a good idea since it triggers the auto-delete
2715                 // mechanism. So we do a cursorLeftIntern()-lite,
2716                 // without the dreaded mechanism. (JMarc)
2717                 setCursorIntern(bview, cursor.par(), cursor.pos()- 1,
2718                                 false, cursor.boundary());
2719                 
2720                 // some insets are undeletable here
2721                 if (cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
2722                         if (!cursor.par()->getInset(cursor.pos())->deletable())
2723                                 return; 
2724                         // force complete redo when erasing display insets
2725                         // this is a cruel method but safe..... Matthias 
2726                         if (cursor.par()->getInset(cursor.pos())->display() ||
2727                             cursor.par()->getInset(cursor.pos())->needFullRow()) {
2728                                 cursor.par()->erase(cursor.pos());
2729                                 redoParagraph(bview);
2730                                 return;
2731                         }
2732                 }
2733                 
2734                 Row * row = cursor.row();
2735                 int y = cursor.y() - row->baseline();
2736                 Paragraph::size_type z;
2737                 /* remember that a space at the end of a row doesnt count
2738                  * when calculating the fill */ 
2739                 if (cursor.pos() < rowLast(row) ||
2740                     !cursor.par()->isLineSeparator(cursor.pos())) {
2741                         row->fill(row->fill() + singleWidth(bview,
2742                                                             cursor.par(),
2743                                                             cursor.pos()));
2744                 }
2745                 
2746                 /* some special code when deleting a newline. This is similar
2747                  * to the behavior when pasting paragraphs */ 
2748                 if (cursor.pos() && cursor.par()->isNewline(cursor.pos())) {
2749                         cursor.par()->erase(cursor.pos());
2750                         // refresh the positions
2751                         Row * tmprow = row;
2752                         while (tmprow->next() && tmprow->next()->par() == row->par()) {
2753                                 tmprow = tmprow->next();
2754                                 tmprow->pos(tmprow->pos() - 1);
2755                         }
2756                         if (cursor.par()->isLineSeparator(cursor.pos() - 1))
2757                                 cursor.pos(cursor.pos() - 1);
2758
2759                         if (cursor.pos() < cursor.par()->size()
2760                             && !cursor.par()->isSeparator(cursor.pos())) {
2761                                 cursor.par()->insertChar(cursor.pos(), ' ');
2762                                 setCharFont(bview->buffer(), cursor.par(), 
2763                                             cursor.pos(), current_font);
2764                                 // refresh the positions
2765                                 tmprow = row;
2766                                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
2767                                         tmprow = tmprow->next();
2768                                         tmprow->pos(tmprow->pos() + 1);
2769                                 }
2770                         }
2771                 } else {
2772                         cursor.par()->erase(cursor.pos());
2773                         
2774                         // refresh the positions
2775                         Row * tmprow = row;
2776                         while (tmprow->next()
2777                                && tmprow->next()->par() == row->par()) {
2778                                 tmprow = tmprow->next();
2779                                 tmprow->pos(tmprow->pos() - 1);
2780                         }
2781
2782                         // delete newlines at the beginning of paragraphs
2783                         while (cursor.par()->size() &&
2784                                cursor.par()->isNewline(cursor.pos()) &&
2785                                cursor.pos() == beginningOfMainBody(bview->buffer(),
2786                                                                    cursor.par())) {
2787                                 cursor.par()->erase(cursor.pos());
2788                                 // refresh the positions
2789                                 tmprow = row;
2790                                 while (tmprow->next() && 
2791                                        tmprow->next()->par() == row->par()) {
2792                                         tmprow = tmprow->next();
2793                                         tmprow->pos(tmprow->pos() - 1);
2794                                 }
2795                         }
2796                 }
2797                 
2798                 // is there a break one row above
2799                 if (row->previous() && row->previous()->par() == row->par()) {
2800                         z = nextBreakPoint(bview, row->previous(),
2801                                            workWidth(bview));
2802                         if (z >= row->pos()) {
2803                                 row->pos(z + 1);
2804                                 
2805                                 Row * tmprow = row->previous();
2806                                 
2807                                 // maybe the current row is now empty
2808                                 if (row->pos() >= row->par()->size()) {
2809                                         // remove it
2810                                         removeRow(row);
2811                                         need_break_row = 0;
2812                                 } else {
2813                                         breakAgainOneRow(bview, row);
2814                                         if (row->next() && row->next()->par() == row->par())
2815                                                 need_break_row = row->next();
2816                                         else
2817                                                 need_break_row = 0;
2818                                 }
2819                                 
2820                                 // set the dimensions of the row above
2821                                 y -= tmprow->height();
2822                                 tmprow->fill(fill(bview, tmprow,
2823                                                   workWidth(bview)));
2824                                 setHeightOfRow(bview, tmprow);
2825                                 
2826                                 refresh_y = y;
2827                                 refresh_row = tmprow;
2828                                 status(bview, LyXText::NEED_MORE_REFRESH);
2829                                 setCursor(bview, cursor.par(), cursor.pos(),
2830                                           false, cursor.boundary());
2831                                 //current_font = rawtmpfont;
2832                                 //real_current_font = realtmpfont;
2833                                 // check, whether the last character's font has changed.
2834                                 if (rawparfont !=
2835                                     cursor.par()->getFontSettings(bview->buffer()->params,
2836                                                                   cursor.par()->size() - 1))
2837                                         redoHeightOfParagraph(bview, cursor);
2838                                 return;
2839                         }
2840                 }
2841                 
2842                 // break the cursor row again
2843                 if (row->next() && row->next()->par() == row->par() &&
2844                     (rowLast(row) == row->par()->size() - 1 ||
2845                      nextBreakPoint(bview, row, workWidth(bview)) != rowLast(row))) {
2846                         
2847                         /* it can happen that a paragraph loses one row
2848                          * without a real breakup. This is when a word
2849                          * is to long to be broken. Well, I don t care this 
2850                          * hack ;-) */
2851                         if (rowLast(row) == row->par()->size() - 1)
2852                                 removeRow(row->next());
2853                         
2854                         refresh_y = y;
2855                         refresh_row = row;
2856                         status(bview, LyXText::NEED_MORE_REFRESH);
2857                         
2858                         breakAgainOneRow(bview, row);
2859                         // will the cursor be in another row now?
2860                         if (row->next() && row->next()->par() == row->par() &&
2861                             rowLast(row) <= cursor.pos()) {
2862                                 row = row->next();
2863                                 breakAgainOneRow(bview, row);
2864                         }
2865
2866                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2867
2868                         if (row->next() && row->next()->par() == row->par())
2869                                 need_break_row = row->next();
2870                         else
2871                                 need_break_row = 0;
2872                 } else  {
2873                         // set the dimensions of the row
2874                         row->fill(fill(bview, row, workWidth(bview)));
2875                         int const tmpheight = row->height();
2876                         setHeightOfRow(bview, row);
2877                         if (tmpheight == row->height())
2878                                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2879                         else
2880                                 status(bview, LyXText::NEED_MORE_REFRESH);
2881                         refresh_y = y;
2882                         refresh_row = row;
2883                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2884                 }
2885         }
2886
2887         // current_font = rawtmpfont;
2888         // real_current_font = realtmpfont;
2889
2890         if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
2891             != cursor.boundary())
2892                 setCursor(bview, cursor.par(), cursor.pos(), false,
2893                           !cursor.boundary());
2894
2895         lastpos = cursor.par()->size();
2896         if (cursor.pos() == lastpos)
2897                 setCurrentFont(bview);
2898         
2899         // check, whether the last characters font has changed.
2900         if (rawparfont != 
2901             cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1)) {
2902                 redoHeightOfParagraph(bview, cursor);
2903         } else {
2904                 // now the special right address boxes
2905                 if (textclasslist.Style(bview->buffer()->params.textclass,
2906                                         cursor.par()->getLayout()).margintype == MARGIN_RIGHT_ADDRESS_BOX) {
2907                         redoDrawingOfParagraph(bview, cursor); 
2908                 }
2909         }
2910 }
2911
2912
2913 void LyXText::getVisibleRow(BufferView * bview, int y_offset, int x_offset,
2914                             Row * row_ptr, int y, bool cleared)
2915 {
2916         // returns a printed row
2917         Painter & pain = bview->painter();
2918         
2919         bool const is_rtl =
2920                 row_ptr->par()->isRightToLeftPar(bview->buffer()->params);
2921         
2922         Paragraph::size_type const last = rowLastPrintable(row_ptr);
2923
2924         Paragraph::size_type vpos;
2925         Paragraph::size_type pos;
2926
2927         float tmpx;
2928
2929         LyXFont font(LyXFont::ALL_SANE);
2930         int maxdesc;
2931         if (row_ptr->height() <= 0) {
2932                 lyxerr << "LYX_ERROR: row.height: "
2933                        << row_ptr->height() << endl;
2934                 return;
2935         }
2936
2937         float x;
2938         float fill_separator;
2939         float fill_hfill;
2940         float fill_label_hfill;
2941         prepareToPrint(bview, row_ptr, x, fill_separator,
2942                        fill_hfill, fill_label_hfill);
2943         
2944         if (inset_owner && (x < 0))
2945                 x = 0;
2946         x += x_offset;
2947         
2948         // clear the area where we want to paint/print
2949         int const ww = bview->workWidth();
2950
2951         bool clear_area = true;
2952         Inset * inset = 0;
2953
2954         if (!bview->screen()->forceClear() && last == row_ptr->pos()
2955             && row_ptr->par()->getChar(row_ptr->pos()) == Paragraph::META_INSET
2956             && (inset = row_ptr->par()->getInset(row_ptr->pos()))) {
2957                 clear_area = inset->doClearArea();
2958         }
2959         // we don't need to clear it's already done!!!
2960         if (cleared) {
2961                 clear_area = true;
2962         } else if (clear_area) {
2963                 int const y = y_offset < 0 ? 0 : y_offset;
2964                 int const h = y_offset < 0 ?
2965                         row_ptr->height() + y_offset : row_ptr->height();
2966                 int const w = inset_owner ?
2967                         inset_owner->textWidth(bview, true) : ww;
2968                 int const x = x_offset;
2969                 pain.fillRectangle(x, y, w, h, backgroundColor());
2970         } else if (inset != 0) {
2971                 int h = row_ptr->baseline() - inset->ascent(bview, font);
2972                 // first clear the whole row above the inset!
2973                 if (h > 0) {
2974                         int const w = (inset_owner ?
2975                                  inset_owner->textWidth(bview, true) : ww);
2976                         pain.fillRectangle(x_offset, y_offset, w, h,
2977                                            backgroundColor());
2978                 }
2979                 h += inset->ascent(bview, font) + inset->descent(bview, font);
2980                 // clear the space below the inset!
2981                 if ((row_ptr->height() - h) > 0) {
2982                         int const w = (inset_owner ?
2983                                        inset_owner->textWidth(bview, true) : ww);
2984                         pain.fillRectangle(x_offset, y_offset + h,
2985                                            w, row_ptr->height() - h,
2986                                            backgroundColor());
2987                 }
2988                 // clear the space behind the inset, if needed
2989                 if (!inset->display() && !inset->needFullRow()) {
2990                         int const w = (inset_owner ?
2991                                        inset_owner->textWidth(bview, true) : ww);
2992                         int const xp = int(x) + inset->width(bview, font);
2993                         if (w-xp > 0) {
2994                                 pain.fillRectangle(xp, y_offset,
2995                                                    w-xp, row_ptr->height(),
2996                                                    backgroundColor());
2997                         }
2998                 }
2999         }
3000
3001         if (selection.set()) {
3002                 int const w = (inset_owner ?
3003                                inset_owner->textWidth(bview, true) : ww);
3004                 // selection code
3005                 if (bidi_same_direction) {
3006                         if (selection.start.row() == row_ptr &&
3007                             selection.end.row() == row_ptr) {
3008                                 if (selection.start.x() < selection.end.x())
3009                                         pain.fillRectangle(x_offset + selection.start.x(),
3010                                                            y_offset,
3011                                                            selection.end.x() - selection.start.x(),
3012                                                            row_ptr->height(),
3013                                                            LColor::selection);
3014                                 else
3015                                         pain.fillRectangle(x_offset + selection.end.x(),
3016                                                            y_offset,
3017                                                            selection.start.x() - selection.end.x(),
3018                                                            row_ptr->height(),
3019                                                            LColor::selection);
3020                         } else if (selection.start.row() == row_ptr) {
3021                                 if (is_rtl)
3022                                         pain.fillRectangle(x_offset, y_offset,
3023                                                            selection.start.x(),
3024                                                            row_ptr->height(),
3025                                                            LColor::selection);
3026                                 else
3027                                         pain.fillRectangle(x_offset + selection.start.x(),
3028                                                            y_offset,
3029                                                            w - selection.start.x(),
3030                                                            row_ptr->height(),
3031                                                            LColor::selection);
3032                         } else if (selection.end.row() == row_ptr) {
3033                                 if (is_rtl)
3034                                         pain.fillRectangle(x_offset + selection.end.x(),
3035                                                            y_offset,
3036                                                            w - selection.end.x(),
3037                                                            row_ptr->height(),
3038                                                            LColor::selection);
3039                                 else
3040                                         pain.fillRectangle(x_offset, y_offset,
3041                                                            selection.end.x(),
3042                                                            row_ptr->height(),
3043                                                            LColor::selection);
3044                         } else if (y > selection.start.y()
3045                                    && y < selection.end.y()) {
3046                                 pain.fillRectangle(x_offset, y_offset, w,
3047                                                    row_ptr->height(),
3048                                                    LColor::selection);
3049                         }
3050                 } else if (selection.start.row() != row_ptr &&
3051                             selection.end.row() != row_ptr &&
3052                             y > selection.start.y()
3053                             && y < selection.end.y()) {
3054                         pain.fillRectangle(x_offset, y_offset, w,
3055                                            row_ptr->height(),
3056                                            LColor::selection);
3057                 } else if (selection.start.row() == row_ptr ||
3058                            selection.end.row() == row_ptr) {
3059                         float tmpx = x;
3060                         if ((selection.start.row() != row_ptr && !is_rtl) ||
3061                              (selection.end.row() != row_ptr && is_rtl))
3062                                 pain.fillRectangle(x_offset, y_offset,
3063                                                    int(tmpx),
3064                                                    row_ptr->height(),
3065                                                    LColor::selection);
3066                         Paragraph::size_type main_body =
3067                                 beginningOfMainBody(bview->buffer(),
3068                                                     row_ptr->par());
3069                         
3070                         for (vpos = row_ptr->pos(); vpos <= last; ++vpos)  {
3071                                 pos = vis2log(vpos);
3072                                 float const old_tmpx = tmpx;
3073                                 if (main_body > 0 && pos == main_body-1) {
3074                                         tmpx += fill_label_hfill +
3075                                                 lyxfont::width(textclasslist.Style(bview->buffer()->params.textclass,
3076                                                                                    row_ptr->par()->getLayout()).labelsep,
3077                                                                getLabelFont(bview->buffer(),row_ptr->par()));
3078                                         if (row_ptr->par()->isLineSeparator(main_body-1))
3079                                                 tmpx -= singleWidth(bview, row_ptr->par(), main_body-1);
3080                                 }
3081                                 if (hfillExpansion(bview->buffer(), row_ptr, pos)) {
3082                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3083                                         if (pos >= main_body)
3084                                                 tmpx += fill_hfill;
3085                                         else 
3086                                                 tmpx += fill_label_hfill;
3087                                 }
3088                                 else if (row_ptr->par()->isSeparator(pos)) {
3089                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3090                                         if (pos >= main_body)
3091                                                 tmpx += fill_separator;
3092                                 } else
3093                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3094                                 
3095                                 if ((selection.start.row() != row_ptr ||
3096                                       selection.start.pos() <= pos) &&
3097                                      (selection.end.row() != row_ptr ||
3098                                       pos < selection.end.pos()) )
3099                                         // Here we do not use x_offset as x_offset was
3100                                         // added to x.
3101                                         pain.fillRectangle(int(old_tmpx),
3102                                                            y_offset,
3103                                                            int(tmpx - old_tmpx + 1),
3104                                                            row_ptr->height(),
3105                                                            LColor::selection);
3106                         }
3107
3108                         if ((selection.start.row() != row_ptr && is_rtl) ||
3109                              (selection.end.row() != row_ptr && !is_rtl) )
3110                                 pain.fillRectangle(x_offset + int(tmpx),
3111                                                    y_offset,
3112                                                    int(ww - tmpx),
3113                                                    row_ptr->height(),
3114                                                    LColor::selection);
3115                 }
3116         }
3117
3118         int box_x = 0;
3119
3120         // Draw appendix lines
3121         Paragraph * firstpar = row_ptr->par();
3122
3123         if (firstpar->params().appendix()) {
3124                 pain.line(1, y_offset,
3125                           1, y_offset + row_ptr->height(),
3126                           LColor::appendixline);
3127                 pain.line(ww - 2, y_offset,
3128                           ww - 2, y_offset + row_ptr->height(),
3129                           LColor::appendixline);
3130         }
3131
3132         // Draw depth lines
3133         Paragraph::depth_type const depth = firstpar->getDepth();
3134         if (depth > 0) {
3135                 Paragraph::depth_type next_depth = 0;
3136                 Paragraph::depth_type prev_depth = 0;
3137                 if (row_ptr->next())
3138                                 next_depth = row_ptr->next()->par()->getDepth();
3139                 if (row_ptr->previous())
3140                                 prev_depth = row_ptr->previous()->par()->getDepth();
3141
3142                 for (Paragraph::depth_type i = 1; i <= depth; ++i) {
3143                         int const line_x = (LYX_PAPER_MARGIN / 5) *
3144                                 i + box_x + x_offset;
3145                         pain.line(line_x, y_offset, line_x,
3146                                   y_offset + row_ptr->height() - 1 - (i - next_depth - 1) * 3,
3147                                   LColor::depthbar);
3148                 
3149                         if (i > prev_depth)
3150                                 pain.fillRectangle(line_x, y_offset, LYX_PAPER_MARGIN / 5, 2,
3151                                                    LColor::depthbar);
3152                         if (i > next_depth)
3153                                 pain.fillRectangle(line_x,
3154                                                    y_offset + row_ptr->height() - 2 - (i - next_depth - 1) * 3,
3155                                                    LYX_PAPER_MARGIN / 5, 2,
3156                                                    LColor::depthbar);
3157                 }
3158         }
3159
3160         
3161         LyXLayout const & layout =
3162                 textclasslist.Style(bview->buffer()->params.textclass,
3163                                     row_ptr->par()->getLayout());
3164
3165         int y_top = 0;
3166         int y_bottom = row_ptr->height();
3167         
3168         // is it a first row?
3169         if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
3170                 
3171                 // start of appendix?
3172                 if (row_ptr->par()->params().startOfAppendix()) {
3173                         pain.line(1, y_offset,
3174                                   ww - 2, y_offset,
3175                                   LColor::appendixline);
3176                 }
3177                 
3178                 // think about the margins
3179                 if (!row_ptr->previous() && bv_owner)
3180                         y_top += LYX_PAPER_MARGIN;
3181                 
3182                 // draw a top pagebreak
3183                 if (row_ptr->par()->params().pagebreakTop()) {
3184                         LyXFont pb_font;
3185                         pb_font.setColor(LColor::pagebreak).decSize();
3186                         int w = 0;
3187                         int a = 0;
3188                         int d = 0;
3189                         pain.line(0, y_offset + y_top + 2*defaultHeight(),
3190                                   ww, 
3191                                   y_offset + y_top + 2 * defaultHeight(),
3192                                   LColor::pagebreak, 
3193                                   Painter::line_onoffdash);
3194                         lyxfont::rectText(_("Page Break (top)"), pb_font,
3195                                           w, a, d);
3196                         pain.rectText((ww - w)/2,
3197                                       y_offset + y_top + 2 * defaultHeight() + d,
3198                                       _("Page Break (top)"),
3199                                       pb_font,
3200                                       backgroundColor(),
3201                                       backgroundColor());
3202                         y_top += 3 * defaultHeight();
3203                 }
3204                 
3205                 if (row_ptr->par()->params().spaceTop().kind() == VSpace::VFILL) {
3206                         // draw a vfill top
3207                         pain.line(0, y_offset + 2 + y_top,
3208                                   LYX_PAPER_MARGIN, y_offset + 2 + y_top,
3209                                   LColor::vfillline);
3210                         
3211                         pain.line(0, y_offset + y_top + 3 * defaultHeight(),
3212                                   LYX_PAPER_MARGIN,
3213                                   y_offset + y_top + 3 * defaultHeight(),
3214                                   LColor::vfillline);
3215                         
3216                         pain.line(LYX_PAPER_MARGIN / 2, y_offset + 2 + y_top,
3217                                   LYX_PAPER_MARGIN / 2,
3218                                   y_offset + y_top + 3 * defaultHeight(),
3219                                   LColor::vfillline);
3220                         
3221                         y_top += 3 * defaultHeight();
3222                 }
3223                 
3224                 // think about user added space
3225                 y_top += int(row_ptr->par()->params().spaceTop().inPixels(bview));
3226                 
3227                 // think about the parskip
3228                 // some parskips VERY EASY IMPLEMENTATION
3229                 if (bview->buffer()->params.paragraph_separation == BufferParams::PARSEP_SKIP) {
3230                         if (layout.latextype == LATEX_PARAGRAPH
3231                             && firstpar->getDepth() == 0
3232                             && firstpar->previous())
3233                                 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3234                         else if (firstpar->previous()
3235                                  && textclasslist.Style(bview->buffer()->params.textclass,
3236                                                         firstpar->previous()->getLayout()).latextype == LATEX_PARAGRAPH
3237                                  && firstpar->previous()->getDepth() == 0)
3238                                 // is it right to use defskip here, too? (AS) 
3239                                 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3240                 }
3241                 
3242                 if (row_ptr->par()->params().lineTop()) {
3243                         // draw a top line
3244                         y_top +=  lyxfont::ascent('x',
3245                                                   getFont(bview->buffer(),
3246                                                           row_ptr->par(), 0));
3247                         int const w = (inset_owner ?
3248                                        inset_owner->width(bview, font) : ww);
3249                         int const xp = static_cast<int>(inset_owner ? x : 0);
3250                         pain.line(xp, y_offset + y_top,
3251                                   w, y_offset + y_top,
3252                                   LColor::topline,
3253                                   Painter::line_solid,
3254                                   Painter::line_thick);
3255                         
3256                         y_top +=  lyxfont::ascent('x',getFont(bview->buffer(),
3257                                                               row_ptr->par(), 0));
3258                 }
3259                 
3260                 // should we print a label?
3261                 if (layout.labeltype >= LABEL_STATIC
3262                     && (layout.labeltype != LABEL_STATIC
3263                         || layout.latextype != LATEX_ENVIRONMENT
3264                         || row_ptr->par()->isFirstInSequence())) {
3265                         font = getLabelFont(bview->buffer(), row_ptr->par());
3266                         if (!row_ptr->par()->getLabelstring().empty()) {
3267                                 tmpx = x;
3268                                 string const tmpstring =
3269                                         row_ptr->par()->getLabelstring();
3270                                 
3271                                 if (layout.labeltype == LABEL_COUNTER_CHAPTER) {
3272                                         if (bview->buffer()->params.secnumdepth >= 0) {
3273                                                 // this is special code for
3274                                                 // the chapter layout. This is
3275                                                 // printed in an extra row
3276                                                 // and has a pagebreak at
3277                                                 // the top.
3278                                                 float spacing_val = 1.0;
3279                                                 if (!row_ptr->par()->params().spacing().isDefault()) {
3280                                                         spacing_val = row_ptr->par()->params().spacing().getValue();
3281                                                 } else {
3282                                                         spacing_val = bview->buffer()->params.spacing.getValue();
3283                                                 }
3284    
3285                                                 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val)
3286                                                         + int(layout.parsep) * defaultHeight();
3287                                                 if (is_rtl)
3288                                                         tmpx = ww - leftMargin(bview, row_ptr) - 
3289                                                                 lyxfont::width(tmpstring, font);
3290                                                 pain.text(int(tmpx),
3291                                                           y_offset + row_ptr->baseline() - row_ptr->ascent_of_text() - maxdesc,
3292                                                           tmpstring, font);
3293                                         }
3294                                 } else {
3295                                         if (is_rtl) {
3296                                                 tmpx = ww - leftMargin(bview, row_ptr)
3297                                                         + lyxfont::width(layout.labelsep, font);
3298                                         } else
3299                                                 tmpx = x - lyxfont::width(layout.labelsep, font)
3300                                                         - lyxfont::width(tmpstring, font);
3301
3302                                         // draw it!
3303                                         pain.text(int(tmpx),
3304                                                   y_offset + row_ptr->baseline(),
3305                                                   tmpstring, font);
3306                                 }
3307                         }
3308                         // the labels at the top of an environment.
3309                         // More or less for bibliography
3310                 } else if (layout.labeltype == LABEL_TOP_ENVIRONMENT ||
3311                            layout.labeltype == LABEL_BIBLIO ||
3312                            layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
3313                         if (row_ptr->par()->isFirstInSequence()) {
3314                                 font = getLabelFont(bview->buffer(),
3315                                                     row_ptr->par());
3316                                 if (!row_ptr->par()->getLabelstring().empty()) {
3317                                         string const tmpstring =
3318                                                 row_ptr->par()->getLabelstring();
3319                                         float spacing_val = 1.0;
3320                                         if (!row_ptr->par()->params().spacing().isDefault()) {
3321                                                 spacing_val = row_ptr->par()->params().spacing().getValue();
3322                                         } else {
3323                                                 spacing_val = bview->buffer()->params.spacing.getValue();
3324                                         }
3325    
3326                                         maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val
3327                                                       + (layout.labelbottomsep * defaultHeight()));
3328                                         
3329                                         tmpx = x;
3330                                         if (layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT){
3331                                                 tmpx = ( (is_rtl ? leftMargin(bview, row_ptr) : x)
3332                                                          + ww - rightMargin(bview->buffer(), row_ptr) ) / 2; 
3333                                                 tmpx -= lyxfont::width(tmpstring, font) / 2;
3334                                         } else if (is_rtl)
3335                                                 tmpx = ww - leftMargin(bview, row_ptr) - 
3336                                                         lyxfont::width(tmpstring, font);
3337                                         pain.text(int(tmpx),
3338                                                   y_offset + row_ptr->baseline()
3339                                                   - row_ptr->ascent_of_text()
3340                                                   - maxdesc,
3341                                                   tmpstring, font);
3342                                 }
3343                         }
3344                 }
3345                 if (layout.labeltype == LABEL_BIBLIO && row_ptr->par()->bibkey) {
3346                         font = getLayoutFont(bview->buffer(), row_ptr->par());
3347                         if (is_rtl)
3348                                 tmpx = ww - leftMargin(bview, row_ptr)
3349                                         + lyxfont::width(layout.labelsep, font);
3350                         else
3351                                 tmpx = x - lyxfont::width(layout.labelsep, font)
3352                                         - row_ptr->par()->bibkey->width(bview, font);
3353                         row_ptr->par()->bibkey->draw(bview, font,
3354                                                    y_offset + row_ptr->baseline(), 
3355                                                    tmpx, clear_area);
3356                 }
3357         }
3358         
3359         // is it a last row?
3360         Paragraph * par = row_ptr->par();
3361         if (row_ptr->par() == par
3362             && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par())) {
3363                 // think about the margins
3364                 if (!row_ptr->next() && bv_owner)
3365                         y_bottom -= LYX_PAPER_MARGIN;
3366                 
3367                 // draw a bottom pagebreak
3368                 if (firstpar->params().pagebreakBottom()) {
3369                         LyXFont pb_font;
3370                         pb_font.setColor(LColor::pagebreak).decSize();
3371                         int const y_place = y_offset + y_bottom
3372                                 - 2 * defaultHeight();
3373                         
3374                         int w = 0;
3375                         int a = 0;
3376                         int d = 0;
3377                         pain
3378                                 .line(0, y_place, ww, y_place,
3379                                       LColor::pagebreak,
3380                                       Painter::line_onoffdash);
3381                         lyxfont::rectText(_("Page Break (bottom)"), pb_font,
3382                                           w, a, d);
3383                         pain.rectText((ww - w) / 2, y_place + d,
3384                                       _("Page Break (bottom)"),
3385                                       pb_font,
3386                                       backgroundColor(),
3387                                       backgroundColor());
3388                         y_bottom -= 3 * defaultHeight();
3389                 }
3390                 
3391                 if (firstpar->params().spaceBottom().kind() == VSpace::VFILL) {
3392                         // draw a vfill bottom
3393                         int const y_place = y_offset + y_bottom
3394                                 - 3 * defaultHeight();
3395                         
3396                         pain.line(0, y_place,
3397                                   LYX_PAPER_MARGIN, y_place,
3398                                   LColor::vfillline);
3399                         pain.line(0, y_offset + y_bottom - 2,
3400                                   LYX_PAPER_MARGIN,
3401                                   y_offset + y_bottom - 2,
3402                                   LColor::vfillline);
3403                         pain.line(LYX_PAPER_MARGIN / 2,
3404                                   y_place,
3405                                   LYX_PAPER_MARGIN / 2,
3406                                   y_offset + y_bottom - 2,
3407                                   LColor::vfillline);
3408                         y_bottom -= 3 * defaultHeight();
3409                 }
3410                 
3411                 // think about user added space
3412                 y_bottom -= int(firstpar->params().spaceBottom().inPixels(bview));
3413                 
3414                 if (firstpar->params().lineBottom()) {
3415                         // draw a bottom line
3416                         y_bottom -= lyxfont::ascent('x',
3417                                                     getFont(bview->buffer(),
3418                                                             par,
3419                                                             par->size() - 1));
3420                         int const w = (inset_owner ?
3421                                        inset_owner->width(bview, font) : ww);
3422                         int const xp = static_cast<int>(inset_owner ? x : 0);
3423                         pain.line(xp, y_offset + y_bottom,
3424                                   w, y_offset + y_bottom,
3425                                   LColor::topline, Painter::line_solid,
3426                                   Painter::line_thick);
3427                         y_bottom -= lyxfont::ascent('x',
3428                                                     getFont(bview->buffer(),
3429                                                             par,
3430                                                             par->size() - 1));
3431                 }
3432
3433                 // draw an endlabel
3434                 int const endlabel =
3435                         row_ptr->par()->getEndLabel(bview->buffer()->params);
3436                 switch (endlabel) {
3437                 case END_LABEL_BOX:
3438                 case END_LABEL_FILLED_BOX:
3439                 {
3440                         LyXFont const font = getFont(bview->buffer(),
3441                                                      row_ptr->par(), last);
3442                         int const size = int(0.75 * lyxfont::maxAscent(font));
3443                         int const y = (y_offset + row_ptr->baseline()) - size;
3444                         int x = is_rtl ? LYX_PAPER_MARGIN 
3445                                 : ww - LYX_PAPER_MARGIN - size;
3446
3447                         if (row_ptr->fill() <= size)
3448                                 x += (size - row_ptr->fill() + 1) * (is_rtl ? -1 : 1);
3449                         if (endlabel == END_LABEL_BOX) {
3450                                 pain.line(x, y, x, y + size,
3451                                           LColor::eolmarker);
3452                                 pain.line(x + size, y, x + size , y + size,
3453                                           LColor::eolmarker);
3454                                 pain.line(x, y, x + size, y,
3455                                           LColor::eolmarker);
3456                                 pain.line(x, y + size, x + size, y + size,
3457                                           LColor::eolmarker);
3458                         } else
3459                                 pain.fillRectangle(x, y, size, size,
3460                                                    LColor::eolmarker);
3461                         break;
3462                 }
3463                 case END_LABEL_STATIC:
3464                 {
3465                         LyXTextClass::LayoutList::size_type layout = row_ptr->par()->getLayout();
3466                         string const tmpstring = textclasslist.
3467                                 Style(bview->buffer()->params.textclass,
3468                                       layout).endlabelstring();
3469                         font = getLabelFont(bview->buffer(), row_ptr->par());
3470                         int const tmpx = is_rtl ?
3471                                 int(x) - lyxfont::width(tmpstring, font)
3472                                 : ww - rightMargin(bview->buffer(), row_ptr) - row_ptr->fill();
3473                         pain.text( tmpx, y_offset + row_ptr->baseline(), tmpstring, font);
3474                         break;
3475                 }
3476                 case END_LABEL_NO_LABEL:
3477                         break;
3478                 }
3479         }
3480         
3481         // draw the text in the pixmap
3482         
3483         vpos = row_ptr->pos();
3484
3485         Paragraph::size_type main_body = 
3486                 beginningOfMainBody(bview->buffer(), row_ptr->par());
3487         if (main_body > 0 &&
3488             (main_body-1 > last || 
3489              !row_ptr->par()->isLineSeparator(main_body - 1)))
3490                 main_body = 0;
3491         
3492         while (vpos <= last)  {
3493                 pos = vis2log(vpos);
3494                 if (main_body > 0 && pos == main_body - 1) {
3495                         x += fill_label_hfill
3496                                 + lyxfont::width(layout.labelsep,
3497                                                  getLabelFont(bview->buffer(),
3498                                                               row_ptr->par()))
3499                                 - singleWidth(bview,
3500                                               row_ptr->par(),
3501                                               main_body - 1);
3502                 }
3503                 
3504                 if (row_ptr->par() ->isHfill(pos)) {
3505                         x += 1;
3506                         pain.line(int(x),
3507                                   y_offset + row_ptr->baseline() - defaultHeight() / 2,
3508                                   int(x),
3509                                   y_offset + row_ptr->baseline(),
3510                                   LColor::vfillline);
3511                         
3512                         if (hfillExpansion(bview->buffer(),
3513                                            row_ptr, pos)) {
3514                                 if (pos >= main_body) {
3515                                         pain.line(int(x),
3516                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3517                                                   int(x + fill_hfill),
3518                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3519                                                   LColor::vfillline,
3520                                                   Painter::line_onoffdash);
3521                                         x += fill_hfill;
3522                                 } else {
3523                                         pain.line(int(x),
3524                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3525                                                   int(x + fill_label_hfill),
3526                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3527                                                   LColor::vfillline,
3528                                                   Painter::line_onoffdash);
3529                                         
3530                                         x += fill_label_hfill;
3531                                 }
3532                                 pain.line(int(x),
3533                                           y_offset + row_ptr->baseline() - defaultHeight() / 2,
3534                                           int(x),
3535                                           y_offset + row_ptr->baseline(),
3536                                           LColor::vfillline);
3537                         }
3538                         x += 2;
3539                         ++vpos;
3540                 } else if (row_ptr->par()->isSeparator(pos)) {
3541                         x += singleWidth(bview,
3542                                          row_ptr->par(), pos);
3543                         if (pos >= main_body)
3544                                 x += fill_separator;
3545                         ++vpos;
3546                 } else
3547                         draw(bview, row_ptr, vpos, y_offset, x, clear_area);
3548         }
3549 }
3550
3551
3552 int LyXText::defaultHeight() const
3553 {
3554         LyXFont font(LyXFont::ALL_SANE);
3555         return int(lyxfont::maxAscent(font) + lyxfont::maxDescent(font) * 1.5);
3556 }
3557
3558    
3559 /* returns the column near the specified x-coordinate of the row 
3560 * x is set to the real beginning of this column  */ 
3561 int LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
3562                             bool & boundary) const
3563 {
3564         float tmpx = 0.0;
3565         float fill_separator;
3566         float fill_hfill;
3567         float fill_label_hfill;
3568    
3569         prepareToPrint(bview, row, tmpx, fill_separator,
3570                        fill_hfill, fill_label_hfill);
3571
3572         Paragraph::size_type vc = row->pos();
3573         Paragraph::size_type last = rowLastPrintable(row);
3574         Paragraph::size_type c = 0;
3575         LyXLayout const & layout =
3576                 textclasslist.Style(bview->buffer()->params.textclass,
3577                                     row->par()->getLayout());
3578         bool left_side = false;
3579
3580         Paragraph::size_type
3581                 main_body = beginningOfMainBody(bview->buffer(), row->par());
3582         float last_tmpx = tmpx;
3583         
3584         if (main_body > 0 &&
3585             (main_body - 1 > last || 
3586              !row->par()->isLineSeparator(main_body - 1)))
3587                 main_body = 0;
3588         
3589         while (vc <= last && tmpx <= x) {
3590                 c = vis2log(vc);
3591                 last_tmpx = tmpx;
3592                 if (main_body > 0 && c == main_body-1) {
3593                         tmpx += fill_label_hfill +
3594                                 lyxfont::width(layout.labelsep,
3595                                                getLabelFont(bview->buffer(), row->par()));
3596                         if (row->par()->isLineSeparator(main_body - 1))
3597                                 tmpx -= singleWidth(bview, row->par(), main_body-1);
3598                 }
3599                 
3600                 if (hfillExpansion(bview->buffer(), row, c)) {
3601                         x += singleWidth(bview, row->par(), c);
3602                         if (c >= main_body)
3603                                 tmpx += fill_hfill;
3604                         else
3605                                 tmpx += fill_label_hfill;
3606                 }
3607                 else if (row->par()->isSeparator(c)) {
3608                         tmpx += singleWidth(bview, row->par(), c);
3609                         if (c >= main_body)
3610                                 tmpx+= fill_separator;
3611                 } else
3612                         tmpx += singleWidth(bview, row->par(), c);
3613                 ++vc;
3614         }
3615         
3616         if ((tmpx + last_tmpx) / 2 > x) {
3617                 tmpx = last_tmpx;
3618                 left_side = true;
3619         }
3620
3621         if (vc > last + 1)  // This shouldn't happen.
3622                 vc = last + 1;
3623
3624         boundary = false;
3625         bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
3626                                          // some speedup if rtl_support=false
3627                 && (!row->next() || row->next()->par() != row->par());
3628         bool const rtl = (lastrow)
3629                 ? row->par()->isRightToLeftPar(bview->buffer()->params)
3630                 : false; // If lastrow is false, we don't need to compute
3631                          // the value of rtl.
3632
3633         if (row->pos() > last)  // Row is empty?
3634                 c = row->pos();
3635         else if (lastrow &&
3636                  ( ( rtl &&  left_side && vc == row->pos() && x < tmpx - 5) ||
3637                    (!rtl && !left_side && vc == last + 1   && x > tmpx + 5) ))
3638                 c = last + 1;
3639         else if (vc == row->pos()) {
3640                 c = vis2log(vc);
3641                 if (bidi_level(c) % 2 == 1)
3642                         ++c;
3643         } else {
3644                 c = vis2log(vc - 1);
3645                 bool const rtl = (bidi_level(c) % 2 == 1);
3646                 if (left_side == rtl) {
3647                         ++c;
3648                         boundary = isBoundary(bview->buffer(), row->par(), c);
3649                 }
3650         }
3651
3652         if (row->pos() <= last && c > last
3653             && row->par()->isNewline(last)) {
3654                 if (bidi_level(last) % 2 == 0)
3655                         tmpx -= singleWidth(bview, row->par(), last);
3656                 else
3657                         tmpx += singleWidth(bview, row->par(), last);
3658                 c = last;
3659         }
3660
3661         c -= row->pos();
3662         x = int(tmpx);
3663         return c;
3664 }
3665
3666
3667 // returns pointer to a specified row
3668 Row * LyXText::getRow(Paragraph * par,
3669                       Paragraph::size_type pos, int & y) const
3670 {
3671         if (!firstrow)
3672                 return 0;
3673         
3674         Row * tmprow = firstrow;
3675         y = 0;
3676         
3677         // find the first row of the specified paragraph
3678         while (tmprow->next() && tmprow->par() != par) {
3679                 y += tmprow->height();
3680                 tmprow = tmprow->next();
3681         }
3682         
3683         // now find the wanted row
3684         while (tmprow->pos() < pos
3685                && tmprow->next()
3686                && tmprow->next()->par() == par
3687                && tmprow->next()->pos() <= pos) {
3688                 y += tmprow->height();
3689                 tmprow = tmprow->next();
3690         }
3691         
3692         return tmprow;
3693 }