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