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