]> git.lyx.org Git - lyx.git/blob - src/text.C
lyxserver cleanup patch + andre's small patches
[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 "insets/insetspecialchar.h"
25 #include "lyx_gui_misc.h"
26 #include "gettext.h"
27 #include "bufferparams.h"
28 #include "buffer.h"
29 #include "debug.h"
30 #include "lyxrc.h"
31 #include "LyXView.h"
32 #include "Painter.h"
33 #include "tracer.h"
34 #include "font.h"
35 #include "encoding.h"
36 #include "lyxscreen.h"
37 #include "bufferview_funcs.h"
38 #include "BufferView.h"
39 #include "language.h"
40 #include "ParagraphParameters.h"
41 #include "undo_funcs.h"
42 #include "font.h"
43
44 using std::max;
45 using std::min;
46 using std::endl;
47 using std::pair;
48
49 namespace {
50
51 int const LYX_PAPER_MARGIN = 20;
52
53 } // namespace anon
54
55 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
56
57
58 int LyXText::workWidth(BufferView * bview) const
59 {
60         if (inset_owner) {
61                 return inset_owner->textWidth(bview);
62         }
63         return bview->workWidth();
64 }
65
66
67 int LyXText::workWidth(BufferView * bview, Inset * inset) const
68 {
69         Buffer::inset_iterator it;
70         Paragraph * par = 0;
71         Paragraph::size_type pos = 0;
72
73         for(it=bview->buffer()->inset_iterator_begin();
74             it != bview->buffer()->inset_iterator_end();
75             ++it)
76         {
77                 if (*it == inset) {
78                         par = it.getPar();
79                         pos = it.getPos();
80                         break;
81                 }
82         }
83         
84         if (!par) {
85                 lyxerr << "LyXText::workWidth: cannot find inset!" <<endl;
86                 return workWidth(bview);
87         }
88         
89         LyXLayout const & layout =
90                 textclasslist.Style(bview->buffer()->params.textclass,
91                                     par->getLayout());
92
93         if (layout.margintype != MARGIN_RIGHT_ADDRESS_BOX) {
94                 // Optimization here: in most cases, the real row is
95                 // not needed, but only the par/pos values. So we just
96                 // construct a dummy row for leftMargin. (JMarc)
97                 Row dummyrow;
98                 dummyrow.par(par);
99                 dummyrow.pos(pos);
100                 return workWidth(bview) - leftMargin(bview, &dummyrow);
101         } else {
102                 Row * row = firstrow;
103                 for(; row; row = row->next()) {
104                         if ((row->par() == par && row->pos() >= pos)) {
105                                 if (!row->next())
106                                         break;
107                                 else if ((row->next()->par() == par) &&
108                                          (row->next()->pos() >= pos))
109                                         continue;
110                         }
111                 }
112                 if (row) {
113                         return workWidth(bview) - leftMargin(bview, row);
114                 } 
115         }
116         return workWidth(bview);
117 }
118
119
120 int LyXText::getRealCursorX(BufferView * bview) const
121 {
122         int x = cursor.x();
123         if (the_locking_inset && (the_locking_inset->getLyXText(bview)!=this))
124                 x = the_locking_inset->getLyXText(bview)->getRealCursorX(bview);
125         return x;
126 }
127
128
129 unsigned char LyXText::transformChar(unsigned char c, Paragraph * par,
130                         Paragraph::size_type pos) const
131 {
132         if (!Encodings::is_arabic(c))
133                 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && isdigit(c))
134                         return c + (0xb0 - '0');
135                 else
136                         return c;
137
138         unsigned char const prev_char = pos > 0 ? par->getChar(pos-1) : ' ';
139         unsigned char next_char = ' ';
140
141         for (Paragraph::size_type i = pos+1; i < par->size(); ++i)
142                 if (!Encodings::IsComposeChar_arabic(par->getChar(i))) {
143                         next_char = par->getChar(i);
144                         break;
145                 }
146
147         if (Encodings::is_arabic(next_char)) {
148                 if (Encodings::is_arabic(prev_char))
149                         return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
150                 else
151                         return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
152         } else {
153                 if (Encodings::is_arabic(prev_char))
154                         return Encodings::TransformChar(c, Encodings::FORM_FINAL);
155                 else
156                         return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
157         }
158 }
159
160 // This is the comments that some of the warnings below refers to.
161 // There are some issues in this file and I don't think they are
162 // really related to the FIX_DOUBLE_SPACE patch. I'd rather think that
163 // this is a problem that has been here almost from day one and that a
164 // larger userbase with differenct access patters triggers the bad
165 // behaviour. (segfaults.) What I think happen is: In several places
166 // we store the paragraph in the current cursor and then moves the
167 // cursor. This movement of the cursor will delete paragraph at the
168 // old position if it is now empty. This will make the temporary
169 // pointer to the old cursor paragraph invalid and dangerous to use.
170 // And is some cases this will trigger a segfault. I have marked some
171 // of the cases where this happens with a warning, but I am sure there
172 // are others in this file and in text2.C. There is also a note in
173 // Delete() that you should read. In Delete I store the paragraph->id
174 // instead of a pointer to the paragraph. I am pretty sure this faulty
175 // use of temporary pointers to paragraphs that might have gotten
176 // invalidated (through a cursor movement) before they are used, are
177 // the cause of the strange crashes we get reported often.
178 //
179 // It is very tiresom to change this code, especially when it is as
180 // hard to read as it is. Help to fix all the cases where this is done
181 // would be greately appreciated.
182 //
183 // Lgb
184
185 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
186                          Paragraph::size_type pos) const
187 {
188         char const c = par->getChar(pos);
189         return singleWidth(bview, par, pos, c);
190 }
191
192
193 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
194                          Paragraph::size_type pos, char c) const
195 {
196         LyXFont const font = getFont(bview->buffer(), par, pos);
197
198         // The most common case is handled first (Asger)
199         if (IsPrintable(c)) {
200                 if (font.language()->RightToLeft()) {
201                         if (font.language()->lang() == "arabic" &&
202                             (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
203                              lyxrc.font_norm_type == LyXRC::ISO_10646_1))
204                                 if (Encodings::IsComposeChar_arabic(c))
205                                         return 0;
206                                 else
207                                         c = transformChar(c, par, pos);
208                         else if (font.language()->lang() == "hebrew" &&
209                                  Encodings::IsComposeChar_hebrew(c))
210                                 return 0;
211                 }
212                 return lyxfont::width(c, font);
213
214         } else if (IsHfillChar(c)) {
215                 return 3;       /* Because of the representation
216                                  * as vertical lines */
217         } else if (c == Paragraph::META_INSET) {
218                 Inset * tmpinset = par->getInset(pos);
219                 if (tmpinset) {
220 #if 0 // seems not to be needed, but ...
221                         tmpinset->update(bview, font);
222 #endif
223                         return tmpinset->width(bview, font);
224                 } else
225                         return 0;
226
227         } else if (IsSeparatorChar(c))
228                 c = ' ';
229         else if (IsNewlineChar(c))
230                 c = 'n';
231         return lyxfont::width(c, font);
232 }
233
234
235 // Returns the paragraph position of the last character in the specified row
236 Paragraph::size_type LyXText::rowLast(Row const * row) const
237 {
238         if (row->next() == 0)
239                 return row->par()->size() - 1;
240         else if (row->next()->par() != row->par()) 
241                 return row->par()->size() - 1;
242         else 
243                 return row->next()->pos() - 1;
244 }
245
246
247 Paragraph::size_type LyXText::rowLastPrintable(Row const * row) const
248 {
249         Paragraph::size_type const last = rowLast(row);
250         if (last >= row->pos()
251             && row->next()
252             && row->next()->par() == row->par()
253             && row->par()->isSeparator(last))
254                 return last - 1;
255         else
256                 return last;
257 }
258
259
260 void LyXText::computeBidiTables(Buffer const * buf, Row * row) const
261 {
262         bidi_same_direction = true;
263         if (!lyxrc.rtl_support) {
264                 bidi_start = -1;
265                 return;
266         }
267
268         bidi_start = row->pos();
269         bidi_end = rowLastPrintable(row);
270
271         if (bidi_start > bidi_end) {
272                 bidi_start = -1;
273                 return;
274         }
275
276         if (bidi_end + 2 - bidi_start >
277             static_cast<Paragraph::size_type>(log2vis_list.size())) {
278                 Paragraph::size_type new_size = 
279                         (bidi_end + 2 - bidi_start < 500) ?
280                         500 : 2 * (bidi_end + 2 - bidi_start);
281                 log2vis_list.resize(new_size);
282                 vis2log_list.resize(new_size);
283                 bidi_levels.resize(new_size);
284         }
285
286         vis2log_list[bidi_end + 1 - bidi_start] = -1;
287         log2vis_list[bidi_end + 1 - bidi_start] = -1;
288
289         Paragraph::size_type    stack[2];
290         bool const rtl_par =
291                 row->par()->getParLanguage(buf->params)->RightToLeft();
292         int level = 0;
293         bool rtl = false;
294         bool rtl0 = false;
295         Paragraph::size_type const main_body =
296                 beginningOfMainBody(buf, row->par());
297
298         for (Paragraph::size_type lpos = bidi_start;
299              lpos <= bidi_end; ++lpos) {
300                 bool is_space = row->par()->isLineSeparator(lpos);
301                 Paragraph::size_type const pos =
302                         (is_space && lpos + 1 <= bidi_end &&
303                          !row->par()->isLineSeparator(lpos + 1) &&
304                          !row->par()->isNewline(lpos + 1))
305                         ? lpos + 1 : lpos;
306                 LyXFont font = row->par()->getFontSettings(buf->params, pos);
307                 if (pos != lpos && 0 < lpos && rtl0 && font.isRightToLeft() &&
308                     font.number() == LyXFont::ON &&
309                     row->par()->getFontSettings(buf->params, lpos - 1).number()
310                     == LyXFont::ON) {
311                         font = row->par()->getFontSettings(buf->params, lpos);
312                         is_space = false;
313                 }
314
315
316                 bool new_rtl = font.isVisibleRightToLeft();
317                 bool new_rtl0 = font.isRightToLeft();
318                 int new_level;
319
320                 if (lpos == main_body - 1
321                     && row->pos() < main_body - 1
322                     && is_space) {
323                         new_level = (rtl_par) ? 1 : 0;
324                         new_rtl = new_rtl0 = rtl_par;
325                 } else if (new_rtl0)
326                         new_level = (new_rtl) ? 1 : 2;
327                 else
328                         new_level = (rtl_par) ? 2 : 0;
329
330                 if (is_space && new_level >= level) {
331                         new_level = level;
332                         new_rtl = rtl;
333                         new_rtl0 = rtl0;
334                 }
335
336                 int new_level2 = new_level;
337
338                 if (level == new_level && rtl0 != new_rtl0) {
339                         --new_level2;
340                         log2vis_list[lpos - bidi_start] = (rtl) ? 1 : -1;
341                 } else if (level < new_level) {
342                         log2vis_list[lpos - bidi_start] =  (rtl) ? -1 : 1;
343                         if (new_level > rtl_par)
344                                 bidi_same_direction = false;
345                 } else
346                         log2vis_list[lpos - bidi_start] = (new_rtl) ? -1 : 1;
347                 rtl = new_rtl;
348                 rtl0 = new_rtl0;
349                 bidi_levels[lpos - bidi_start] = new_level;
350
351                 while (level > new_level2) {
352                         Paragraph::size_type old_lpos =
353                                 stack[--level];
354                         int delta = lpos - old_lpos - 1;
355                         if (level % 2)
356                                 delta = -delta;
357                         log2vis_list[lpos - bidi_start] += delta;
358                         log2vis_list[old_lpos - bidi_start] += delta;
359                 }
360                 while (level < new_level)
361                         stack[level++] = lpos;
362         }
363
364         while (level > 0) {
365                 Paragraph::size_type const old_lpos = stack[--level];
366                 int delta = bidi_end - old_lpos;
367                 if (level % 2)
368                         delta = -delta;
369                 log2vis_list[old_lpos - bidi_start] += delta;
370         }
371
372         Paragraph::size_type vpos = bidi_start - 1;
373         for (Paragraph::size_type lpos = bidi_start;
374              lpos <= bidi_end; ++lpos) {
375                 vpos += log2vis_list[lpos - bidi_start];
376                 vis2log_list[vpos - bidi_start] = lpos;
377                 log2vis_list[lpos - bidi_start] = vpos;
378         }
379 }
380
381
382 // This method requires a previous call to ComputeBidiTables()
383 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
384                          Paragraph::size_type pos) const
385 {
386         if (!lyxrc.rtl_support || pos == 0)
387                 return false;
388
389         if (!bidi_InRange(pos - 1)) {
390                 /// This can happen if pos is the first char of a row.
391                 /// Returning false in this case is incorrect!
392                 return false;
393         }
394
395         bool const rtl = bidi_level(pos - 1) % 2;
396         bool const rtl2 = bidi_InRange(pos)
397                 ? bidi_level(pos) % 2
398                 : par->isRightToLeftPar(buf->params);
399         return rtl != rtl2;
400 }
401
402
403 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
404                          Paragraph::size_type pos,
405                          LyXFont const & font) const
406 {
407         if (!lyxrc.rtl_support)
408                 return false;    // This is just for speedup
409
410         bool const rtl = font.isVisibleRightToLeft();
411         bool const rtl2 = bidi_InRange(pos)
412                 ? bidi_level(pos) % 2
413                 : par->isRightToLeftPar(buf->params);
414         return rtl != rtl2;
415 }
416
417
418 void LyXText::draw(BufferView * bview, Row const * row,
419                    Paragraph::size_type & vpos,
420                    int offset, float & x, bool cleared)
421 {
422         Painter & pain = bview->painter();
423         
424         Paragraph::size_type pos = vis2log(vpos);
425         char c = row->par()->getChar(pos);
426         float tmpx = x;
427
428         if (IsNewlineChar(c)) {
429                 ++vpos;
430                 // Draw end-of-line marker
431                 LyXFont const font = getFont(bview->buffer(), row->par(), pos);
432                 int const wid = lyxfont::width('n', font);
433                 int const asc = lyxfont::maxAscent(font);
434                 int const y = offset + row->baseline();
435                 int xp[3];
436                 int yp[3];
437                 
438                 if (bidi_level(pos) % 2 == 0) {
439                         xp[0] = int(x + wid * 0.375);
440                         yp[0] = int(y - 0.875 * asc * 0.75);
441                         
442                         xp[1] = int(x);
443                         yp[1] = int(y - 0.500 * asc * 0.75);
444                         
445                         xp[2] = int(x + wid * 0.375);
446                         yp[2] = int(y - 0.125 * asc * 0.75);
447                         
448                         pain.lines(xp, yp, 3, LColor::eolmarker);
449                         
450                         xp[0] = int(x);
451                         yp[0] = int(y - 0.500 * asc * 0.75);
452                         
453                         xp[1] = int(x + wid);
454                         yp[1] = int(y - 0.500 * asc * 0.75);
455                         
456                         xp[2] = int(x + wid);
457                         yp[2] = int(y - asc * 0.75);
458                         
459                         pain.lines(xp, yp, 3, LColor::eolmarker);
460                 } else {
461                         xp[0] = int(x + wid * 0.625);
462                         yp[0] = int(y - 0.875 * asc * 0.75);
463                         
464                         xp[1] = int(x + wid);
465                         yp[1] = int(y - 0.500 * asc * 0.75);
466                         
467                         xp[2] = int(x + wid * 0.625);
468                         yp[2] = int(y - 0.125 * asc * 0.75);
469                         
470                         pain.lines(xp, yp, 3, LColor::eolmarker);
471                         
472                         xp[0] = int(x + wid);
473                         yp[0] = int(y - 0.500 * asc * 0.75);
474                         
475                         xp[1] = int(x);
476                         yp[1] = int(y - 0.500 * asc * 0.75);
477                         
478                         xp[2] = int(x);
479                         yp[2] = int(y - asc * 0.75);
480                         
481                         pain.lines(xp, yp, 3, LColor::eolmarker);
482                 }
483                 x += wid;
484                 return;
485         }
486
487         LyXFont font = getFont(bview->buffer(), row->par(), pos);
488         LyXFont font2 = font;
489
490         if (c == Paragraph::META_INSET) {
491                 Inset * tmpinset = row->par()->getInset(pos);
492                 if (tmpinset) {
493                         tmpinset->update(bview, font, false);
494                         tmpinset->draw(bview, font, offset+row->baseline(), x,
495                                        cleared);
496                         if (!need_break_row && !inset_owner &&
497                             bview->text->status() == CHANGED_IN_DRAW)
498                         {
499                                 if (row->previous() && row->previous()->par() == row->par())
500                                         breakAgainOneRow(bview, row->previous());
501                                 setCursor(bview, cursor.par(), cursor.pos());
502                                 need_break_row = const_cast<Row *>(row);
503                         }
504                 }
505                 ++vpos;
506
507                 if (lyxrc.mark_foreign_language &&
508                         font.language() != latex_language &&
509                     font.language() != bview->buffer()->params.language) {
510                         int const y = offset + row->height() - 1;
511                         pain.line(int(tmpx), y, int(x), y, LColor::language);
512                 }
513
514                 return;
515         }
516
517         /* usual characters, no insets */
518
519         // Collect character that we can draw in one command
520
521         // This is dirty, but fast. Notice that it will never be too small.
522         // For the record, I'll note that Microsoft Word has a limit
523         // of 768 here. We have none :-) (Asger)
524         // Ok. I am the first to admit that the use of std::string will be
525         // a tiny bit slower than using a POD char array. However, I claim
526         // that this slowdown is so small that it is close to inperceptive.
527         // So IMHO we should go with the easier and clearer implementation.
528         // And even if 1024 is a large number here it might overflow, string
529         // will only overflow if the machine is out of memory...
530         static string textstring;
531         textstring = c;
532         ++vpos;
533
534         Paragraph::size_type const last = rowLastPrintable(row);
535
536         if (font.language()->lang() == "hebrew") {
537                 if (Encodings::IsComposeChar_hebrew(c)) {
538                         int const width = lyxfont::width(c, font2);
539                         int dx = 0;
540                         for (Paragraph::size_type i = pos-1; i >= 0; --i) {
541                                 c = row->par()->getChar(i);
542                                 if (!Encodings::IsComposeChar_hebrew(c)) {
543                                         if (IsPrintableNonspace(c)) {
544                                                 int const width2 =
545                                                         singleWidth(bview,
546                                                                     row->par(),
547                                                                     i, c);
548                                                 dx = (c == 'ø' || c == 'ã') // dalet / resh
549                                                         ? width2 - width : (width2 - width) / 2;
550                                         }
551                                         break;
552                                 }
553                         }
554                         // Draw nikud
555                         pain.text(int(x) + dx, offset + row->baseline(),
556                                   textstring, font);
557                 } else {
558                         while (vpos <= last &&
559                                (pos = vis2log(vpos)) >= 0
560                                && IsPrintableNonspace(c = row->par()->getChar(pos))
561                                && !Encodings::IsComposeChar_hebrew(c)
562                                && font2 == getFont(bview->buffer(), row->par(), pos)) {
563                                 textstring += c;
564                                 ++vpos;
565                         }
566                         // Draw text and set the new x position
567                         pain.text(int(x), offset + row->baseline(),
568                                   textstring, font);
569                         x += lyxfont::width(textstring, font);
570                 }
571         } else if (font.language()->lang() == "arabic" &&
572                    (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
573                     lyxrc.font_norm_type == LyXRC::ISO_10646_1)) {
574                 if (Encodings::IsComposeChar_arabic(c)) {
575                         c = transformChar(c, row->par(), pos);
576                         textstring = c;
577                         int const width = lyxfont::width(c, font2);
578                         int dx = 0;
579                         for (Paragraph::size_type i = pos-1; i >= 0; --i) {
580                                 c = row->par()->getChar(i);
581                                 if (!Encodings::IsComposeChar_arabic(c)) {
582                                         if (IsPrintableNonspace(c)) {
583                                                 int const width2 =
584                                                         singleWidth(bview,
585                                                                     row->par(),
586                                                                     i, c);
587                                                 dx = (width2 - width) / 2;
588                                         }
589                                         break;
590                                 }
591                         }
592                         // Draw nikud
593                         pain.text(int(x) + dx, offset + row->baseline(), 
594                                   textstring, font);
595                 } else {
596                         textstring = transformChar(c, row->par(), pos);
597                         while (vpos <= last &&
598                                (pos = vis2log(vpos)) >= 0
599                                && IsPrintableNonspace(c = row->par()->getChar(pos))
600                                && !Encodings::IsComposeChar_arabic(c)
601                                && font2 == getFont(bview->buffer(), row->par(), pos)) {
602                                 c = transformChar(c, row->par(), pos);
603                                 textstring += c;
604                                 ++vpos;
605                         }
606                         // Draw text and set the new x position
607                         pain.text(int(x), offset + row->baseline(),
608                                   textstring, font);
609                         x += lyxfont::width(textstring, font);
610                 }
611         } else {
612                 while (vpos <= last &&
613                        (pos = vis2log(vpos)) >= 0
614                        && IsPrintableNonspace(c = row->par()->getChar(pos))
615                        && font2 == getFont(bview->buffer(), row->par(), pos)) {
616                         textstring += c;
617                         ++vpos;
618                 }
619                 // Draw text and set the new x position
620                 pain.text(int(x), offset + row->baseline(), textstring, font);
621                 x += lyxfont::width(textstring, font);
622         }
623
624 #ifdef INHERIT_LANGUAGE
625 #ifdef WITH_WARNINGS
626         if ((font.language() == inherit_language) ||
627                 (font.language() == ignore_language))
628                 lyxerr << "No this shouldn't happen!\n";
629 #endif
630 #endif
631         if (lyxrc.mark_foreign_language &&
632             font.language() != latex_language &&
633             font.language() != bview->buffer()->params.language) {
634                 int const y = offset + row->height() - 1;
635                 pain.line(int(tmpx), y, int(x), y,
636                           LColor::language);
637         }
638
639         // If we want ulem.sty support, drawing
640         // routines should go here. (Asger)
641         // Why shouldn't LyXFont::drawText handle it internally?
642 }
643
644
645 // Returns the left beginning of the text. 
646 // This information cannot be taken from the layouts-objekt, because in 
647 // LaTeX the beginning of the text fits in some cases (for example sections)
648 // exactly the label-width.
649 int LyXText::leftMargin(BufferView * bview, Row const * row) const
650 {
651         LyXTextClass const & tclass =
652                 textclasslist.TextClass(bview->buffer()->params.textclass);
653         LyXLayout const & layout = tclass[row->par()->getLayout()];
654         
655         string parindent = layout.parindent; 
656
657         int x = LYX_PAPER_MARGIN;
658         
659         x += lyxfont::signedWidth(tclass.leftmargin(), tclass.defaultfont());
660
661         // this is the way, LyX handles the LaTeX-Environments.
662         // I have had this idea very late, so it seems to be a
663         // later added hack and this is true
664         if (!row->par()->getDepth()) {
665                 if (!row->par()->getLayout()) {
666                         // find the previous same level paragraph
667                         if (row->par()->previous()) {
668                                 Paragraph * newpar = row->par()
669                                         ->depthHook(row->par()->getDepth());
670                                 if (newpar &&
671                                     tclass[newpar->getLayout()].nextnoindent)
672                                         parindent.erase();
673                         }
674                 }
675         } else {
676                 // find the next level paragraph
677                 
678                 Paragraph * newpar =
679                         row->par()->outerHook();
680                 
681                 // make a corresponding row. Needed to call LeftMargin()
682                 
683                 // check wether it is a sufficent paragraph 
684                 if (newpar
685                     && tclass[newpar->getLayout()].isEnvironment()) {
686                         Row dummyrow;
687                         dummyrow.par(newpar);
688                         dummyrow.pos(newpar->size());
689                         x = leftMargin(bview, &dummyrow);
690                 } else {
691                         // this is no longer an error, because this function
692                         // is used to clear impossible depths after changing
693                         // a layout. Since there is always a redo,
694                         // LeftMargin() is always called
695                         row->par()->params().depth(0);
696                 }
697                 
698                 if (newpar && !row->par()->getLayout()) {
699                         if (newpar->params().noindent())
700                                 parindent.erase();
701                         else
702                                 parindent = tclass[newpar->getLayout()].parindent;
703                 }
704                 
705         }
706         
707         LyXFont const labelfont = getLabelFont(bview->buffer(), row->par());
708         switch (layout.margintype) {
709         case MARGIN_DYNAMIC:
710                 if (!layout.leftmargin.empty()) {
711                         x += lyxfont::signedWidth(layout.leftmargin,
712                                                   tclass.defaultfont());
713                 }
714                 if (!row->par()->getLabelstring().empty()) {
715                         x += lyxfont::signedWidth(layout.labelindent,
716                                                   labelfont);
717                         x += lyxfont::width(row->par()->getLabelstring(),
718                                             labelfont);
719                         x += lyxfont::width(layout.labelsep, labelfont);
720                 }
721                 break;
722         case MARGIN_MANUAL:
723                 x += lyxfont::signedWidth(layout.labelindent, labelfont);
724                 if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
725                         if (!row->par()->getLabelWidthString().empty()) {
726                                 x += lyxfont::width(row->par()->getLabelWidthString(),
727                                                labelfont);
728                                 x += lyxfont::width(layout.labelsep, labelfont);
729                         }
730                 }
731                 break;
732         case MARGIN_STATIC:
733                 x += lyxfont::signedWidth(layout.leftmargin, tclass.defaultfont()) * 4
734                         / (row->par()->getDepth() + 4);
735                 break;
736         case MARGIN_FIRST_DYNAMIC:
737                 if (layout.labeltype == LABEL_MANUAL) {
738                         if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
739                                 x += lyxfont::signedWidth(layout.leftmargin,
740                                                           labelfont);
741                         } else {
742                                 x += lyxfont::signedWidth(layout.labelindent,
743                                                           labelfont);
744                         }
745                 } else if (row->pos()
746                            // Special case to fix problems with
747                            // theorems (JMarc)
748                            || (layout.labeltype == LABEL_STATIC
749                                && layout.latextype == LATEX_ENVIRONMENT
750                                && ! row->par()->isFirstInSequence())) {
751                         x += lyxfont::signedWidth(layout.leftmargin,
752                                                   labelfont);
753                 } else if (layout.labeltype != LABEL_TOP_ENVIRONMENT
754                            && layout.labeltype != LABEL_BIBLIO
755                            && layout.labeltype !=
756                            LABEL_CENTERED_TOP_ENVIRONMENT) {
757                         x += lyxfont::signedWidth(layout.labelindent,
758                                                   labelfont);
759                         x += lyxfont::width(layout.labelsep, labelfont);
760                         x += lyxfont::width(row->par()->getLabelstring(),
761                                             labelfont);
762                 } 
763                 break;
764                 
765         case MARGIN_RIGHT_ADDRESS_BOX:
766         {
767                 // ok, a terrible hack. The left margin depends on the widest
768                 // row in this paragraph. Do not care about footnotes, they
769                 // are *NOT* allowed in the LaTeX realisation of this layout.
770                 
771                 // find the first row of this paragraph
772                 Row const * tmprow = row;
773                 while (tmprow->previous()
774                        && tmprow->previous()->par() == row->par())
775                         tmprow = tmprow->previous();
776                 
777                 int minfill = tmprow->fill();
778                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
779                         tmprow = tmprow->next();
780                         if (tmprow->fill() < minfill)
781                                 minfill = tmprow->fill();
782                 }
783                 
784                 x += lyxfont::signedWidth(layout.leftmargin,
785                                           tclass.defaultfont());
786                 x += minfill;
787         }
788         break;
789         }
790         
791         LyXAlignment align; // wrong type
792
793         if (row->par()->params().align() == LYX_ALIGN_LAYOUT)
794                 align = layout.align;
795         else
796                 align = row->par()->params().align();
797
798         // set the correct parindent
799         if (row->pos() == 0) {
800                 if ((layout.labeltype == LABEL_NO_LABEL 
801                      || layout.labeltype == LABEL_TOP_ENVIRONMENT 
802                      || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
803                      || (layout.labeltype == LABEL_STATIC
804                          && layout.latextype == LATEX_ENVIRONMENT
805                          && ! row->par()->isFirstInSequence()))
806                     && align == LYX_ALIGN_BLOCK
807                     && !row->par()->params().noindent()
808                     && (row->par()->layout ||
809                         bview->buffer()->params.paragraph_separation ==
810                         BufferParams::PARSEP_INDENT))
811                         x += lyxfont::signedWidth(parindent,
812                                                   tclass.defaultfont());
813                 else if (layout.labeltype == LABEL_BIBLIO) {
814                         // ale970405 Right width for bibitems
815                         x += bibitemMaxWidth(bview, tclass.defaultfont());
816                 }
817         }
818         return x;
819 }
820
821
822 int LyXText::rightMargin(Buffer const * buf, Row const * row) const
823 {
824         LyXTextClass const & tclass =
825                 textclasslist.TextClass(buf->params.textclass);
826         LyXLayout const & layout = tclass[row->par()->getLayout()];
827                 
828         int x = LYX_PAPER_MARGIN
829                 + lyxfont::signedWidth(tclass.rightmargin(),
830                                        tclass.defaultfont());
831
832         // this is the way, LyX handles the LaTeX-Environments.
833         // I have had this idea very late, so it seems to be a
834         // later added hack and this is true
835         if (row->par()->getDepth()) {
836                 // find the next level paragraph
837                 
838                 Paragraph * newpar = row->par();
839                 
840                 do {
841                         newpar = newpar->previous();
842                 } while (newpar
843                          && newpar->getDepth() >= row->par()->getDepth());
844                 
845                 // make a corresponding row. Needed to call LeftMargin()
846                 
847                 // check wether it is a sufficent paragraph
848                 if (newpar
849                     && tclass[newpar->getLayout()].isEnvironment()) {
850                         Row dummyrow;
851                         dummyrow.par(newpar);
852                         dummyrow.pos(0);
853                         x = rightMargin(buf, &dummyrow);
854                 } else {
855                         // this is no longer an error, because this function
856                         // is used to clear impossible depths after changing
857                         // a layout. Since there is always a redo,
858                         // LeftMargin() is always called
859                         row->par()->params().depth(0);
860                 }
861         }
862         
863         //lyxerr << "rightmargin: " << layout->rightmargin << endl;
864         x += lyxfont::signedWidth(layout.rightmargin, tclass.defaultfont())
865                 * 4 / (row->par()->getDepth() + 4);
866         return x;
867 }
868
869
870 int LyXText::labelEnd(BufferView * bview, Row const * row) const
871 {
872         if (textclasslist.Style(bview->buffer()->params.textclass,
873                                 row->par()->getLayout()).margintype
874             == MARGIN_MANUAL) {
875                 Row tmprow;
876                 tmprow = *row;
877                 tmprow.pos(row->par()->size());
878                 return leftMargin(bview, &tmprow);  /* just the beginning 
879                                                 of the main body */
880         } else
881                 return 0;  /* LabelEnd is only needed, if the  
882                               layout fills a flushleft
883                               label. */
884 }
885
886
887 // get the next breakpoint in a given paragraph
888 Paragraph::size_type
889 LyXText::nextBreakPoint(BufferView * bview, Row const * row, int width) const
890 {
891         Paragraph * par = row->par();
892
893         if (width < 0)
894                 return par->size();
895
896         Paragraph::size_type const pos = row->pos();
897
898
899         // position of the last possible breakpoint 
900         // -1 isn't a suitable value, but a flag
901         Paragraph::size_type last_separator = -1;
902         width -= rightMargin(bview->buffer(), row);
903         
904         Paragraph::size_type const main_body =
905                 beginningOfMainBody(bview->buffer(), par);
906         LyXLayout const & layout =
907                 textclasslist.Style(bview->buffer()->params.textclass,
908                                     par->getLayout());
909         Paragraph::size_type i = pos;
910
911         if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
912                 /* special code for right address boxes, only newlines count */
913                 while (i < par->size()) {
914                         if (par->isNewline(i)) {
915                                 last_separator = i;
916                                 i = par->size() - 1; // this means break
917                                 //x = width;
918                         } else if (par->getChar(i) == Paragraph::META_INSET &&
919                                    par->getInset(i) && par->getInset(i)->display()){
920                                 par->getInset(i)->display(false);
921                         }
922                         ++i;
923                 }
924         } else {
925                 // Last position is an invariant
926                 Paragraph::size_type const last = 
927                         par->size();
928                 // this is the usual handling
929                 int x = leftMargin(bview, row);
930                 bool doitonetime = true;
931                 while (doitonetime || ((x < width) && (i < last))) {
932                         doitonetime = false;
933                         char const c = par->getChar(i);
934                         if (IsNewlineChar(c)) {
935                                 last_separator = i;
936                                 x = width; // this means break
937                         } else if (c == Paragraph::META_INSET &&
938                                    par->getInset(i)) {
939                                 
940                                 // check wether a Display() inset is
941                                 // valid here. if not, change it to
942                                 // non-display
943                                 if (par->getInset(i)->display() &&
944                                     (layout.isCommand() ||
945                                      (layout.labeltype == LABEL_MANUAL
946                                       && i < beginningOfMainBody(bview->buffer(), par)))) {
947                                         // display istn't allowd
948                                         par->getInset(i)->display(false);
949                                         x += singleWidth(bview, par, i, c);
950                                 } else if (par->getInset(i)->display() ||
951                                          par->getInset(i)->needFullRow()) {
952                                         // So break the line here
953                                         if (i == pos) {
954                                                 if (pos < last-1) {
955                                                         last_separator = i;
956                                                         if (IsLineSeparatorChar(par->getChar(i+1)))
957                                                                 ++last_separator;
958                                                 } else
959                                                         last_separator = last; // to avoid extra rows
960                                         } else
961                                                 last_separator = i - 1;
962                                         x = width;  // this means break
963                                 } else {
964                                         x += singleWidth(bview, par, i, c);
965                                 }
966                         } else  {
967                                 if (IsLineSeparatorChar(c))
968                                         last_separator = i;
969                                 x += singleWidth(bview, par, i, c);
970                         }
971                         ++i;
972                         if (i == main_body) {
973                                 x += lyxfont::width(layout.labelsep,
974                                                     getLabelFont(bview->buffer(), par));
975                                 if (par->isLineSeparator(i - 1))
976                                         x-= singleWidth(bview, par, i - 1);
977                                 int left_margin = labelEnd(bview, row);
978                                 if (x < left_margin)
979                                         x = left_margin;
980                         }
981                 }
982                 // end of paragraph is always a suitable separator
983                 if (i == last && x < width)
984                         last_separator = i;
985         }
986         
987         // well, if last_separator is still 0, the line isn't breakable. 
988         // don't care and cut simply at the end
989         if (last_separator < 0) {
990                 last_separator = i;
991         }
992         
993         // manual labels cannot be broken in LaTeX, do not care
994         if (main_body && last_separator < main_body)
995                 last_separator = main_body - 1;
996         
997         return last_separator;
998 }
999
1000
1001 // returns the minimum space a row needs on the screen in pixel
1002 int LyXText::fill(BufferView * bview, Row * row, int paper_width) const
1003 {
1004         if (paper_width < 0)
1005                 return 0;
1006
1007         int w;
1008         // get the pure distance
1009         Paragraph::size_type const last = rowLastPrintable(row);
1010         
1011         // special handling of the right address boxes
1012         if (textclasslist.Style(bview->buffer()->params.textclass,
1013                                 row->par()->getLayout()).margintype
1014             == MARGIN_RIGHT_ADDRESS_BOX) {
1015                 int const tmpfill = row->fill();
1016                 row->fill(0); // the minfill in MarginLeft()
1017                 w = leftMargin(bview, row);
1018                 row->fill(tmpfill);
1019         } else
1020                 w = leftMargin(bview, row);
1021         
1022         LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
1023                                                        row->par()->getLayout());
1024         Paragraph::size_type const main_body = 
1025                 beginningOfMainBody(bview->buffer(), row->par());
1026         Paragraph::size_type i = row->pos();
1027
1028         while (i <= last) {
1029                 if (main_body > 0 && i == main_body) {
1030                         w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1031                         if (row->par()->isLineSeparator(i - 1))
1032                                 w -= singleWidth(bview, row->par(), i - 1);
1033                         int left_margin = labelEnd(bview, row);
1034                         if (w < left_margin)
1035                                 w = left_margin;
1036                 }
1037                 w += singleWidth(bview, row->par(), i);
1038                 ++i;
1039         }
1040         if (main_body > 0 && main_body > last) {
1041                 w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1042                 if (last >= 0 && row->par()->isLineSeparator(last))
1043                         w -= singleWidth(bview, row->par(), last);
1044                 int const left_margin = labelEnd(bview, row);
1045                 if (w < left_margin)
1046                         w = left_margin;
1047         }
1048         
1049         int const fill = paper_width - w - rightMargin(bview->buffer(), row);
1050 #ifdef WITH_WARNINGS
1051 #warning Please fix me (Jug!)
1052 #endif
1053 #if 0
1054         if (fill < 0)
1055                 return 0;
1056 #endif
1057         return fill;
1058 }
1059
1060
1061 // returns the minimum space a manual label needs on the screen in pixel
1062 int LyXText::labelFill(BufferView * bview, Row const * row) const
1063 {
1064         Paragraph::size_type last = beginningOfMainBody(bview->buffer(), row->par()) - 1;
1065         // -1 because a label ends either with a space that is in the label, 
1066         // or with the beginning of a footnote that is outside the label.
1067
1068         // I don't understand this code in depth, but sometimes "last" is
1069         // less than 0 and this causes a crash. This fix seems to work
1070         // correctly, but I bet the real error is elsewhere.  The bug is
1071         // triggered when you have an open footnote in a paragraph
1072         // environment with a manual label. (Asger)
1073         if (last < 0) last = 0;
1074         
1075         if (row->par()->isLineSeparator(last)) /* a sepearator at this end 
1076                                                 does not count */
1077                 --last;
1078         
1079         int w = 0;
1080         Paragraph::size_type i = row->pos();
1081         while (i <= last) {
1082                 w += singleWidth(bview, row->par(), i);
1083                 ++i;
1084         }
1085         
1086         int fill = 0;
1087         if (!row->par()->params().labelWidthString().empty()) {
1088                 fill = max(lyxfont::width(row->par()->params().labelWidthString(),
1089                                           getLabelFont(bview->buffer(), row->par())) - w,
1090                            0);
1091         }
1092         
1093         return fill;
1094 }
1095
1096
1097 // returns the number of separators in the specified row. The separator 
1098 // on the very last column doesnt count
1099 int LyXText::numberOfSeparators(Buffer const * buf, Row const * row) const
1100 {
1101         Paragraph::size_type const last = rowLast(row);
1102         Paragraph::size_type p =
1103                 max(row->pos(), beginningOfMainBody(buf, row->par()));
1104         int n = 0;
1105         for (; p < last; ++p) {
1106                 if (row->par()->isSeparator(p)) {
1107                         ++n;
1108                 }
1109         }
1110         return n;
1111 }
1112
1113
1114 // returns the number of hfills in the specified row. The LyX-Hfill is
1115 // a LaTeX \hfill so that the hfills at the beginning and at the end were 
1116 // ignored. This is *MUCH* more usefull than not to ignore!
1117 int LyXText::numberOfHfills(Buffer const * buf, Row const * row) const
1118 {
1119         Paragraph::size_type const last = rowLast(row);
1120         Paragraph::size_type first = row->pos();
1121         if (first) { /* hfill *DO* count at the beginning 
1122                       * of paragraphs! */
1123                 while(first <= last && row->par()->isHfill(first))
1124                         ++first;
1125         }
1126
1127         first = max(first, beginningOfMainBody(buf, row->par()));
1128         int n = 0;
1129         for (Paragraph::size_type p = first; p <= last; ++p) {
1130         // last, because the end is ignored!
1131                 if (row->par()->isHfill(p)) {
1132                         ++n;
1133                 }
1134         }
1135         return n;
1136 }
1137
1138
1139 // like NumberOfHfills, but only those in the manual label!
1140 int LyXText::numberOfLabelHfills(Buffer const * buf, Row const * row) const
1141 {
1142         Paragraph::size_type last = rowLast(row);
1143         Paragraph::size_type first = row->pos();
1144         if (first) { /* hfill *DO* count at the beginning 
1145                       * of paragraphs! */
1146                 while(first < last && row->par()->isHfill(first))
1147                         ++first;
1148         }
1149
1150         last = min(last, beginningOfMainBody(buf, row->par()));
1151         int n = 0;
1152         for (Paragraph::size_type p = first;
1153              p < last; ++p) {  // last, because the end is ignored!
1154                 if (row->par()->isHfill(p)) {
1155                         ++n;
1156                 }
1157         }
1158         return n;
1159 }
1160
1161
1162 // returns true, if a expansion is needed.
1163 // Rules are given by LaTeX
1164 bool LyXText::hfillExpansion(Buffer const * buf, Row const * row_ptr,
1165                              Paragraph::size_type pos) const
1166 {
1167         // by the way, is it a hfill?
1168         if (!row_ptr->par()->isHfill(pos))
1169                 return false;
1170         
1171         // at the end of a row it does not count
1172         if (pos >= rowLast(row_ptr))
1173                 return false;
1174         
1175         // at the beginning of a row it does not count, if it is not 
1176         // the first row of a paragaph
1177         if (!row_ptr->pos())
1178                 return true;
1179         
1180         // in some labels  it does not count
1181         if (textclasslist.Style(buf->params.textclass,
1182                                 row_ptr->par()->getLayout()).margintype
1183             != MARGIN_MANUAL
1184             && pos < beginningOfMainBody(buf, row_ptr->par()))
1185                 return false; 
1186         
1187         // if there is anything between the first char of the row and
1188         // the sepcified position that is not a newline and not a hfill,
1189         // the hfill will count, otherwise not
1190         Paragraph::size_type i = row_ptr->pos();
1191         while (i < pos && (row_ptr->par()->isNewline(i)
1192                            || row_ptr->par()->isHfill(i)))
1193                 ++i;
1194         
1195         return i != pos;
1196 }
1197
1198
1199 LColor::color LyXText::backgroundColor()
1200 {
1201         if (inset_owner)
1202                 return inset_owner->backgroundColor();
1203         else
1204                 return LColor::background;
1205 }
1206
1207 void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const
1208 {
1209     /* get the maximum ascent and the maximum descent */
1210         int asc = 0;
1211         int desc = 0;
1212         float layoutasc = 0;
1213         float layoutdesc = 0;
1214         float tmptop = 0;
1215         LyXFont tmpfont;
1216         Inset * tmpinset = 0;
1217
1218         /* this must not happen before the currentrow for clear reasons.
1219            so the trick is just to set the current row onto this row */
1220         int unused_y;
1221         getRow(row_ptr->par(), row_ptr->pos(), unused_y);
1222
1223         /* ok , let us initialize the maxasc and maxdesc value. 
1224          * This depends in LaTeX of the font of the last character
1225          * in the paragraph. The hack below is necessary because
1226          * of the possibility of open footnotes */
1227         
1228         /* Correction: only the fontsize count. The other properties
1229            are taken from the layoutfont. Nicer on the screen :) */
1230         Paragraph * par = row_ptr->par();
1231         Paragraph * firstpar = row_ptr->par();
1232    
1233         LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
1234                                                        firstpar->getLayout());
1235
1236         // as max get the first character of this row then it can increes but not
1237         // decrees the height. Just some point to start with so we don't have to
1238         // do the assignment below too often.
1239         LyXFont font = getFont(bview->buffer(), par, row_ptr->pos());
1240         LyXFont::FONT_SIZE const tmpsize = font.size();
1241         font = getLayoutFont(bview->buffer(), par);
1242         LyXFont::FONT_SIZE const size = font.size();
1243         font.setSize(tmpsize);
1244
1245         LyXFont labelfont = getLabelFont(bview->buffer(), par);
1246
1247         float spacing_val = 1.0;
1248         if (!row_ptr->par()->params().spacing().isDefault()) {
1249                 spacing_val = row_ptr->par()->params().spacing().getValue();
1250         } else {
1251                 spacing_val = bview->buffer()->params.spacing.getValue();
1252         }
1253         //lyxerr << "spacing_val = " << spacing_val << endl;
1254    
1255         int maxasc = int(lyxfont::maxAscent(font) *
1256                          layout.spacing.getValue() *
1257                          spacing_val);
1258         int maxdesc = int(lyxfont::maxDescent(font) *
1259                           layout.spacing.getValue() *
1260                           spacing_val);
1261         Paragraph::size_type const pos_end = rowLast(row_ptr);
1262         int labeladdon = 0;
1263         int maxwidth = 0;
1264
1265         // Check if any insets are larger
1266         for (Paragraph::size_type pos = row_ptr->pos(); pos <= pos_end; ++pos) {
1267                 if (row_ptr->par()->getChar(pos) == Paragraph::META_INSET) {
1268                         tmpfont = getFont(bview->buffer(), row_ptr->par(), pos);
1269                         tmpinset = row_ptr->par()->getInset(pos);
1270                         if (tmpinset) {
1271 #if 1 // this is needed for deep update on initialitation
1272                                 tmpinset->update(bview, tmpfont);
1273 #endif
1274                                 asc = tmpinset->ascent(bview, tmpfont);
1275                                 desc = tmpinset->descent(bview, tmpfont);
1276                                 maxwidth += tmpinset->width(bview, tmpfont);
1277                                 maxasc = max(maxasc, asc);
1278                                 maxdesc = max(maxdesc, desc);
1279                         }
1280                 } else {
1281                         maxwidth += singleWidth(bview, row_ptr->par(), pos);
1282                 }
1283         }
1284
1285         // Check if any custom fonts are larger (Asger)
1286         // This is not completely correct, but we can live with the small,
1287         // cosmetic error for now.
1288         LyXFont::FONT_SIZE maxsize =
1289                 row_ptr->par()->highestFontInRange(row_ptr->pos(), pos_end, size);
1290         if (maxsize > font.size()) {
1291                 font.setSize(maxsize);
1292
1293                 asc = lyxfont::maxAscent(font);
1294                 desc = lyxfont::maxDescent(font);
1295                 if (asc > maxasc) 
1296                         maxasc = asc;
1297                 if (desc > maxdesc)
1298                         maxdesc = desc;
1299         }
1300
1301         // This is nicer with box insets:
1302         ++maxasc;
1303         ++maxdesc;
1304
1305         row_ptr->ascent_of_text(maxasc);
1306    
1307         // is it a top line?
1308         if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
1309       
1310                 // some parksips VERY EASY IMPLEMENTATION
1311                 if (bview->buffer()->params.paragraph_separation ==
1312                         BufferParams::PARSEP_SKIP)
1313                 {
1314                         if (layout.isParagraph()
1315                                 && firstpar->getDepth() == 0
1316                                 && firstpar->previous())
1317                         {
1318                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1319                         } else if (firstpar->previous() &&
1320                                    textclasslist.Style(bview->buffer()->params.textclass,
1321                                                        firstpar->previous()->
1322                                                        getLayout()).isParagraph() &&
1323                                    firstpar->previous()->getDepth() == 0)
1324                         {
1325                                 // is it right to use defskip here too? (AS)
1326                                 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1327                         }
1328                 }
1329       
1330                 // the paper margins
1331                 if (!row_ptr->par()->previous() && bv_owner)
1332                         maxasc += LYX_PAPER_MARGIN;
1333       
1334                 // add the vertical spaces, that the user added
1335                 if (firstpar->params().spaceTop().kind() != VSpace::NONE)
1336                         maxasc += int(firstpar->params().spaceTop().inPixels(bview));
1337       
1338                 // do not forget the DTP-lines!
1339                 // there height depends on the font of the nearest character
1340                 if (firstpar->params().lineTop())
1341                         maxasc += 2 * lyxfont::ascent('x', getFont(bview->buffer(),
1342                                                                    firstpar, 0));
1343       
1344                 // and now the pagebreaks
1345                 if (firstpar->params().pagebreakTop())
1346                         maxasc += 3 * defaultHeight();
1347       
1348                 // This is special code for the chapter, since the label of this
1349                 // layout is printed in an extra row
1350                 if (layout.labeltype == LABEL_COUNTER_CHAPTER
1351                         && bview->buffer()->params.secnumdepth >= 0)
1352                 {
1353                         float spacing_val = 1.0;
1354                         if (!row_ptr->par()->params().spacing().isDefault()) {
1355                                 spacing_val = row_ptr->par()->params().spacing().getValue();
1356                         } else {
1357                                 spacing_val = bview->buffer()->params.spacing.getValue();
1358                         }
1359               
1360                         labeladdon = int(lyxfont::maxDescent(labelfont) *
1361                                          layout.spacing.getValue() *
1362                                          spacing_val)
1363                                 + int(lyxfont::maxAscent(labelfont) *
1364                                       layout.spacing.getValue() *
1365                                       spacing_val);
1366                 }
1367       
1368                 // special code for the top label
1369                 if ((layout.labeltype == LABEL_TOP_ENVIRONMENT
1370                      || layout.labeltype == LABEL_BIBLIO
1371                      || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1372                     && row_ptr->par()->isFirstInSequence()
1373                     && !row_ptr->par()->getLabelstring().empty())
1374                 {
1375                         float spacing_val = 1.0;
1376                         if (!row_ptr->par()->params().spacing().isDefault()) {
1377                                 spacing_val = row_ptr->par()->params().spacing().getValue();
1378                         } else {
1379                                 spacing_val = bview->buffer()->params.spacing.getValue();
1380                         }
1381               
1382                         labeladdon = int(
1383                                 (lyxfont::maxAscent(labelfont) *
1384                                  layout.spacing.getValue() *
1385                                  spacing_val)
1386                                 +(lyxfont::maxDescent(labelfont) *
1387                                   layout.spacing.getValue() *
1388                                   spacing_val)
1389                                 + layout.topsep * defaultHeight()
1390                                 + layout.labelbottomsep *  defaultHeight());
1391                 }
1392    
1393                 // and now the layout spaces, for example before and after a section, 
1394                 // or between the items of a itemize or enumerate environment
1395       
1396                 if (!firstpar->params().pagebreakTop()) {
1397                         Paragraph * prev = row_ptr->par()->previous();
1398                         if (prev)
1399                                 prev = row_ptr->par()->depthHook(row_ptr->par()->getDepth());
1400                         if (prev && prev->getLayout() == firstpar->getLayout() &&
1401                                 prev->getDepth() == firstpar->getDepth() &&
1402                                 prev->getLabelWidthString() == firstpar->getLabelWidthString())
1403                         {
1404                                 layoutasc = (layout.itemsep * defaultHeight());
1405                         } else if (row_ptr->previous()) {
1406                                 tmptop = layout.topsep;
1407             
1408                                 if (row_ptr->previous()->par()->getDepth() >= row_ptr->par()->getDepth())
1409                                         tmptop -= textclasslist.Style(bview->buffer()->params.textclass,
1410                                                                       row_ptr->previous()->par()->
1411                                                                       getLayout()).bottomsep;
1412             
1413                                 if (tmptop > 0)
1414                                         layoutasc = (tmptop * defaultHeight());
1415                         } else if (row_ptr->par()->params().lineTop()) {
1416                                 tmptop = layout.topsep;
1417             
1418                                 if (tmptop > 0)
1419                                         layoutasc = (tmptop * defaultHeight());
1420                         }
1421          
1422                         prev = row_ptr->par()->outerHook();
1423                         if (prev)  {
1424                                 maxasc += int(textclasslist.Style(bview->buffer()->params.textclass,
1425                                               prev->getLayout()).parsep * defaultHeight());
1426                         } else {
1427                                 if (firstpar->previous() &&
1428                                         firstpar->previous()->getDepth() == 0 &&
1429                                         firstpar->previous()->getLayout() !=
1430                                         firstpar->getLayout())
1431                                 {
1432                                         // avoid parsep
1433                                 } else if (firstpar->previous()) {
1434                                         maxasc += int(layout.parsep * defaultHeight());
1435                                 }
1436                         }
1437                 }
1438         }
1439    
1440         // is it a bottom line?
1441         if (row_ptr->par() == par
1442                 && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par()))
1443         {
1444                 // the paper margins
1445                 if (!par->next() && bv_owner)
1446                         maxdesc += LYX_PAPER_MARGIN;
1447           
1448                 // add the vertical spaces, that the user added
1449                 if (firstpar->params().spaceBottom().kind() != VSpace::NONE)
1450                         maxdesc += int(firstpar->params().spaceBottom().inPixels(bview));
1451           
1452                 // do not forget the DTP-lines!
1453                 // there height depends on the font of the nearest character
1454                 if (firstpar->params().lineBottom())
1455                         maxdesc += 2 * lyxfont::ascent('x',
1456                                                        getFont(bview->buffer(),
1457                                                                par,
1458                                                                max(Paragraph::size_type(0), par->size() - 1)));
1459           
1460                 // and now the pagebreaks
1461                 if (firstpar->params().pagebreakBottom())
1462                         maxdesc += 3 * defaultHeight();
1463           
1464                 // and now the layout spaces, for example before and after
1465                 // a section, or between the items of a itemize or enumerate
1466                 // environment
1467                 if (!firstpar->params().pagebreakBottom() && row_ptr->par()->next()) {
1468                         Paragraph * nextpar = row_ptr->par()->next();
1469                         Paragraph * comparepar = row_ptr->par();
1470                         float usual = 0;
1471                         float unusual = 0;
1472              
1473                         if (comparepar->getDepth() > nextpar->getDepth()) {
1474                                 usual = (textclasslist.Style(bview->buffer()->params.textclass,
1475                                          comparepar->getLayout()).bottomsep * defaultHeight());
1476                                 comparepar = comparepar->depthHook(nextpar->getDepth());
1477                                 if (comparepar->getLayout()!= nextpar->getLayout()
1478                                         || nextpar->getLabelWidthString() != 
1479                                         comparepar->getLabelWidthString())
1480                                 {
1481                                         unusual = (textclasslist.Style(bview->buffer()->params.textclass,
1482                                                    comparepar->getLayout()).bottomsep * defaultHeight());
1483                                 }
1484                                 if (unusual > usual)
1485                                         layoutdesc = unusual;
1486                                 else
1487                                         layoutdesc = usual;
1488                         } else if (comparepar->getDepth() ==  nextpar->getDepth()) {
1489                                 
1490                                 if (comparepar->getLayout()!= nextpar->getLayout()
1491                                         || nextpar->getLabelWidthString() != 
1492                                         comparepar->getLabelWidthString())
1493                                         layoutdesc = int(textclasslist.Style(bview->buffer()->params.textclass,
1494                                                                                                                  comparepar->getLayout()).bottomsep * defaultHeight());
1495                         }
1496                 }
1497         }
1498         
1499         // incalculate the layout spaces
1500         maxasc += int(layoutasc * 2 / (2 + firstpar->getDepth()));
1501         maxdesc += int(layoutdesc * 2 / (2 + firstpar->getDepth()));
1502         
1503         // calculate the new height of the text
1504         height -= row_ptr->height();
1505         
1506         row_ptr->height(maxasc + maxdesc + labeladdon);
1507         row_ptr->baseline(maxasc + labeladdon);
1508         
1509         height += row_ptr->height();
1510         float x;
1511         float dummy;
1512         prepareToPrint(bview, row_ptr, x, dummy, dummy, dummy, false);
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())->isTextInset()))
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 
2368         // (optional hyphens are part of a word)
2369         while (cursor.pos() < cursor.par()->size()
2370                && (cursor.par()->isLetter(cursor.pos())) 
2371                // assignment is intentional here
2372                || ((inset = getInset())
2373                    && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2374                    && static_cast<InsetSpecialChar *>(inset)->kind()
2375                         == InsetSpecialChar::HYPHENATION
2376                    ))
2377                 cursor.pos(cursor.pos() + 1);
2378
2379         // Finally, we copy the word to a string and return it
2380         string str;
2381         if (selection.cursor.pos() < cursor.pos()) {
2382                 Paragraph::size_type i;
2383                 for (i = selection.cursor.pos(); i < cursor.pos(); ++i) {
2384                         if (cursor.par()->getChar(i) != Paragraph::META_INSET)
2385                                 str += cursor.par()->getChar(i);
2386                 }
2387         }
2388         return str;
2389 }
2390
2391
2392 // This one is also only for the spellchecker
2393 void LyXText::selectSelectedWord(BufferView * bview)
2394 {
2395         if (the_locking_inset) {
2396                 the_locking_inset->selectSelectedWord(bview);
2397                 return;
2398         }
2399         // move cursor to the beginning
2400         setCursor(bview, selection.cursor.par(), selection.cursor.pos());
2401         
2402         // set the sel cursor
2403         selection.cursor = cursor;
2404         Inset * inset;
2405         
2406         // now find the end of the word
2407         while (cursor.pos() < cursor.par()->size()
2408                && (cursor.par()->isLetter(cursor.pos())
2409                    // assignment is intentional here
2410                    || ((inset = getInset())
2411                        && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2412                        && static_cast<InsetSpecialChar *>(inset)->kind()
2413                                 == InsetSpecialChar::HYPHENATION
2414                        )))
2415                 cursor.pos(cursor.pos() + 1);
2416         
2417         setCursor(bview, cursor.par(), cursor.pos());
2418         
2419         // finally set the selection
2420         setSelection(bview);
2421 }
2422
2423
2424 /* -------> Delete from cursor up to the end of the current or next word. */
2425 void LyXText::deleteWordForward(BufferView * bview)
2426 {
2427         if (!cursor.par()->size())
2428                 cursorRight(bview);
2429         else {
2430                 LyXCursor tmpcursor = cursor;
2431                 tmpcursor.row(0); // ??
2432                 selection.set(true); // to avoid deletion
2433                 cursorRightOneWord(bview);
2434                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2435                 selection.cursor = cursor;
2436                 cursor = tmpcursor;
2437                 setSelection(bview);
2438                 
2439                 /* -----> Great, CutSelection() gets rid of multiple spaces. */
2440                 cutSelection(bview, true, false);
2441         }
2442 }
2443
2444
2445 /* -------> Delete from cursor to start of current or prior word. */
2446 void LyXText::deleteWordBackward(BufferView * bview)
2447 {
2448        if (!cursor.par()->size())
2449                cursorLeft(bview);
2450        else {
2451                LyXCursor tmpcursor = cursor;
2452                tmpcursor.row(0); // ??
2453                selection.set(true); // to avoid deletion
2454                cursorLeftOneWord(bview);
2455                setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2456                selection.cursor = cursor;
2457                cursor = tmpcursor;
2458                setSelection(bview);
2459                cutSelection(bview, true, false);
2460        }
2461 }
2462
2463
2464 /* -------> Kill to end of line. */
2465 void LyXText::deleteLineForward(BufferView * bview)
2466 {
2467         if (!cursor.par()->size())
2468                 // Paragraph is empty, so we just go to the right
2469                 cursorRight(bview);
2470         else {
2471                 LyXCursor tmpcursor = cursor;
2472                 // We can't store the row over a regular setCursor
2473                 // so we set it to 0 and reset it afterwards.
2474                 tmpcursor.row(0); // ??
2475                 selection.set(true); // to avoid deletion
2476                 cursorEnd(bview);
2477                 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2478                 selection.cursor = cursor;
2479                 cursor = tmpcursor;
2480                 setSelection(bview);
2481                 // What is this test for ??? (JMarc)
2482                 if (!selection.set()) {
2483                         deleteWordForward(bview);
2484                 } else {
2485                         cutSelection(bview, true, false);
2486                 }
2487         }
2488 }
2489
2490
2491 // Change the case of a word at cursor position. 
2492 // This function directly manipulates Paragraph::text because there
2493 // is no Paragraph::SetChar currently. I did what I could to ensure
2494 // that it is correct. I guess part of it should be moved to
2495 // Paragraph, but it will have to change for 1.1 anyway. At least
2496 // it does not access outside of the allocated array as the older
2497 // version did. (JMarc) 
2498 void LyXText::changeCase(BufferView * bview, LyXText::TextCase action)
2499 {
2500         LyXCursor from;
2501         LyXCursor to;
2502
2503         if (selection.set()) {
2504                 from = selection.start;
2505                 to = selection.end;
2506         } else {
2507                 getWord(from, to, PARTIAL_WORD);
2508                 setCursor(bview, to.par(), to.pos() + 1);
2509         }
2510
2511         changeRegionCase(bview, from, to, action);
2512 }
2513
2514
2515 void LyXText::changeRegionCase(BufferView * bview,
2516                                LyXCursor const & from,
2517                                LyXCursor const & to,
2518                                LyXText::TextCase action)
2519 {
2520         lyx::Assert(from <= to);
2521         
2522         setUndo(bview, Undo::FINISH,
2523                 from.par(), to.par()->next());
2524
2525         Paragraph::size_type pos = from.pos();
2526         Paragraph * par = from.par();
2527
2528         while (par && (pos != to.pos() || par != to.par())) {
2529                 unsigned char c = par->getChar(pos);
2530                 if (!IsInsetChar(c) && !IsHfillChar(c)) {
2531                         switch (action) {
2532                         case text_lowercase:
2533                                 c = lowercase(c);
2534                                 break;
2535                         case text_capitalization:
2536                                 c = uppercase(c);
2537                                 action = text_lowercase;
2538                                 break;
2539                         case text_uppercase:
2540                                 c = uppercase(c);
2541                                 break;
2542                         }
2543                 }
2544                 par->setChar(pos, c);
2545                 checkParagraph(bview, par, pos);
2546
2547                 ++pos;
2548                 if (pos == par->size()) {
2549                         par = par->next();
2550                         pos = 0;
2551                 }
2552         }
2553         if (to.row() != from.row()) {
2554                 refresh_y = from.y() - from.row()->baseline();
2555                 refresh_row = from.row();
2556                 status(bview, LyXText::NEED_MORE_REFRESH);
2557         }
2558 }
2559
2560
2561 void LyXText::transposeChars(BufferView & bview)
2562 {
2563         Paragraph * tmppar = cursor.par();
2564
2565         setUndo(&bview, Undo::FINISH,
2566                 tmppar, tmppar->next()); 
2567
2568         Paragraph::size_type tmppos = cursor.pos();
2569
2570         // First decide if it is possible to transpose at all
2571
2572         // We are at the beginning of a paragraph.
2573         if (tmppos == 0) return;
2574
2575         // We are at the end of a paragraph.
2576         if (tmppos == tmppar->size() - 1) return;
2577
2578         unsigned char c1 = tmppar->getChar(tmppos);
2579         unsigned char c2 = tmppar->getChar(tmppos - 1);
2580
2581         if (c1 != Paragraph::META_INSET
2582             && c2 != Paragraph::META_INSET) {
2583                 tmppar->setChar(tmppos, c2);
2584                 tmppar->setChar(tmppos - 1, c1);
2585         }
2586         // We should have an implementation that handles insets
2587         // as well, but that will have to come later. (Lgb)
2588         checkParagraph(const_cast<BufferView*>(&bview), tmppar, tmppos);
2589 }
2590
2591
2592 void LyXText::Delete(BufferView * bview)
2593 {
2594         // this is a very easy implementation
2595
2596         LyXCursor old_cursor = cursor;
2597         int const old_cur_par_id = old_cursor.par()->id();
2598         int const old_cur_par_prev_id = old_cursor.par()->previous() ?
2599                 old_cursor.par()->previous()->id() : 0;
2600         
2601         // just move to the right
2602         cursorRight(bview);
2603
2604         // CHECK Look at the comment here.
2605         // This check is not very good...
2606         // The cursorRightIntern calls DeleteEmptyParagrapgMechanism
2607         // and that can very well delete the par or par->previous in
2608         // old_cursor. Will a solution where we compare paragraph id's
2609         //work better?
2610         if ((cursor.par()->previous() ? cursor.par()->previous()->id() : 0)
2611             == old_cur_par_prev_id
2612             && cursor.par()->id() != old_cur_par_id)
2613         {
2614                 return; // delete-empty-paragraph-mechanism has done it
2615         }
2616
2617         // if you had success make a backspace
2618         if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2619                 LyXCursor tmpcursor = cursor;
2620                 cursor = old_cursor; // to make sure undo gets the right cursor position
2621                 setUndo(bview, Undo::DELETE,
2622                         cursor.par(), cursor.par()->next()); 
2623                 cursor = tmpcursor;
2624                 backspace(bview);
2625         }
2626 }
2627
2628
2629 void LyXText::backspace(BufferView * bview)
2630 {
2631         // Get the font that is used to calculate the baselineskip
2632         Paragraph::size_type lastpos = cursor.par()->size();
2633         LyXFont rawparfont =
2634                 cursor.par()->getFontSettings(bview->buffer()->params,
2635                                               lastpos - 1);
2636
2637         if (cursor.pos() == 0) {
2638                 // The cursor is at the beginning of a paragraph,
2639                 // so the the backspace will collapse two paragraphs into one.
2640                 
2641                 // we may paste some paragraphs
2642       
2643                 // is it an empty paragraph?
2644       
2645                 if ((lastpos == 0
2646                      || (lastpos == 1 && cursor.par()->isSeparator(0)))) {
2647                         // This is an empty paragraph and we delete it just by moving the cursor one step
2648                         // left and let the DeleteEmptyParagraphMechanism handle the actual deletion
2649                         // of the paragraph.
2650                         
2651                         if (cursor.par()->previous()) {
2652                                 Paragraph * tmppar = cursor.par()->previous();
2653                                 if (cursor.par()->getLayout() == tmppar->getLayout()
2654                                     && cursor.par()->getAlign() == tmppar->getAlign()) {
2655                                         // Inherit bottom DTD from the paragraph below.
2656                                         // (the one we are deleting)
2657                                         tmppar->params().lineBottom(cursor.par()->params().lineBottom());
2658                                         tmppar->params().spaceBottom(cursor.par()->params().spaceBottom());
2659                                         tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom());
2660                                 }
2661                                 
2662                                 cursorLeft(bview);
2663                      
2664                                 // the layout things can change the height of a row !
2665                                 int const tmpheight = cursor.row()->height();
2666                                 setHeightOfRow(bview, cursor.row());
2667                                 if (cursor.row()->height() != tmpheight) {
2668                                         refresh_y = cursor.y() - cursor.row()->baseline();
2669                                         refresh_row = cursor.row();
2670                                         status(bview, LyXText::NEED_MORE_REFRESH);
2671                                 }
2672                                 return;
2673                         }
2674                 }
2675
2676                 if (cursor.par()->previous()) {
2677                         setUndo(bview, Undo::DELETE,
2678                                 cursor.par()->previous(), cursor.par()->next());
2679                 }
2680                 
2681                 Paragraph * tmppar = cursor.par();
2682                 Row * tmprow = cursor.row();
2683
2684                 // We used to do cursorLeftIntern() here, but it is
2685                 // not a good idea since it triggers the auto-delete
2686                 // mechanism. So we do a cursorLeftIntern()-lite,
2687                 // without the dreaded mechanism. (JMarc)
2688                 if (cursor.par()->previous()) { 
2689                         // steps into the above paragraph.
2690                         setCursorIntern(bview, cursor.par()->previous(),
2691                                         cursor.par()->previous()->size(),
2692                                         false);
2693                 }
2694
2695                 /* Pasting is not allowed, if the paragraphs have different
2696                    layout. I think it is a real bug of all other
2697                    word processors to allow it. It confuses the user.
2698                    Even so with a footnote paragraph and a non-footnote
2699                    paragraph. I will not allow pasting in this case, 
2700                    because the user would be confused if the footnote behaves 
2701                    different wether it is open or closed.
2702                   
2703                    Correction: Pasting is always allowed with standard-layout
2704                 */
2705                 if (cursor.par() != tmppar
2706                     && (cursor.par()->getLayout() == tmppar->getLayout()
2707                         || tmppar->getLayout() == 0 /*standard*/)
2708                     && cursor.par()->getAlign() == tmppar->getAlign())
2709                 {
2710                         removeParagraph(tmprow);
2711                         removeRow(tmprow);
2712                         cursor.par()->pasteParagraph(bview->buffer()->params);
2713                         
2714                         if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1))
2715                                 ; //cursor.par()->insertChar(cursor.pos(), ' ');
2716                         // strangely enough it seems that commenting out the line above removes
2717                         // most or all of the segfaults. I will however also try to move the
2718                         // two Remove... lines in front of the PasteParagraph too.
2719                         else
2720                                 if (cursor.pos())
2721                                         cursor.pos(cursor.pos() - 1);
2722                         
2723                         status(bview, LyXText::NEED_MORE_REFRESH);
2724                         refresh_row = cursor.row();
2725                         refresh_y = cursor.y() - cursor.row()->baseline();
2726                         
2727                         // remove the lost paragraph
2728                         // This one is not safe, since the paragraph that the tmprow and the
2729                         // following rows belong to has been deleted by the PasteParagraph
2730                         // above. The question is... could this be moved in front of the
2731                         // PasteParagraph?
2732                         //RemoveParagraph(tmprow);
2733                         //RemoveRow(tmprow);  
2734                         
2735                         // This rebuilds the rows.
2736                         appendParagraph(bview, cursor.row());
2737                         updateCounters(bview, cursor.row());
2738                         
2739                         // the row may have changed, block, hfills etc.
2740                         setCursor(bview, cursor.par(), cursor.pos(), false);
2741                 }
2742         } else {
2743                 /* this is the code for a normal backspace, not pasting
2744                  * any paragraphs */ 
2745                 setUndo(bview, Undo::DELETE,
2746                         cursor.par(), cursor.par()->next()); 
2747                 // We used to do cursorLeftIntern() here, but it is
2748                 // not a good idea since it triggers the auto-delete
2749                 // mechanism. So we do a cursorLeftIntern()-lite,
2750                 // without the dreaded mechanism. (JMarc)
2751                 setCursorIntern(bview, cursor.par(), cursor.pos()- 1,
2752                                 false, cursor.boundary());
2753                 
2754                 // some insets are undeletable here
2755                 if (cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
2756                         if (!cursor.par()->getInset(cursor.pos())->deletable())
2757                                 return; 
2758                         // force complete redo when erasing display insets
2759                         // this is a cruel method but safe..... Matthias 
2760                         if (cursor.par()->getInset(cursor.pos())->display() ||
2761                             cursor.par()->getInset(cursor.pos())->needFullRow()) {
2762                                 cursor.par()->erase(cursor.pos());
2763                                 redoParagraph(bview);
2764                                 return;
2765                         }
2766                 }
2767                 
2768                 Row * row = cursor.row();
2769                 int y = cursor.y() - row->baseline();
2770                 Paragraph::size_type z;
2771                 /* remember that a space at the end of a row doesnt count
2772                  * when calculating the fill */ 
2773                 if (cursor.pos() < rowLast(row) ||
2774                     !cursor.par()->isLineSeparator(cursor.pos())) {
2775                         row->fill(row->fill() + singleWidth(bview,
2776                                                             cursor.par(),
2777                                                             cursor.pos()));
2778                 }
2779                 
2780                 /* some special code when deleting a newline. This is similar
2781                  * to the behavior when pasting paragraphs */ 
2782                 if (cursor.pos() && cursor.par()->isNewline(cursor.pos())) {
2783                         cursor.par()->erase(cursor.pos());
2784                         // refresh the positions
2785                         Row * tmprow = row;
2786                         while (tmprow->next() && tmprow->next()->par() == row->par()) {
2787                                 tmprow = tmprow->next();
2788                                 tmprow->pos(tmprow->pos() - 1);
2789                         }
2790                         if (cursor.par()->isLineSeparator(cursor.pos() - 1))
2791                                 cursor.pos(cursor.pos() - 1);
2792
2793                         if (cursor.pos() < cursor.par()->size()
2794                             && !cursor.par()->isSeparator(cursor.pos())) {
2795                                 cursor.par()->insertChar(cursor.pos(), ' ');
2796                                 setCharFont(bview->buffer(), cursor.par(), 
2797                                             cursor.pos(), current_font);
2798                                 // refresh the positions
2799                                 tmprow = row;
2800                                 while (tmprow->next() && tmprow->next()->par() == row->par()) {
2801                                         tmprow = tmprow->next();
2802                                         tmprow->pos(tmprow->pos() + 1);
2803                                 }
2804                         }
2805                 } else {
2806                         cursor.par()->erase(cursor.pos());
2807                         
2808                         // refresh the positions
2809                         Row * tmprow = row;
2810                         while (tmprow->next()
2811                                && tmprow->next()->par() == row->par()) {
2812                                 tmprow = tmprow->next();
2813                                 tmprow->pos(tmprow->pos() - 1);
2814                         }
2815
2816                         // delete newlines at the beginning of paragraphs
2817                         while (cursor.par()->size() &&
2818                                cursor.par()->isNewline(cursor.pos()) &&
2819                                cursor.pos() == beginningOfMainBody(bview->buffer(),
2820                                                                    cursor.par())) {
2821                                 cursor.par()->erase(cursor.pos());
2822                                 // refresh the positions
2823                                 tmprow = row;
2824                                 while (tmprow->next() && 
2825                                        tmprow->next()->par() == row->par()) {
2826                                         tmprow = tmprow->next();
2827                                         tmprow->pos(tmprow->pos() - 1);
2828                                 }
2829                         }
2830                 }
2831                 
2832                 // is there a break one row above
2833                 if (row->previous() && row->previous()->par() == row->par()) {
2834                         z = nextBreakPoint(bview, row->previous(),
2835                                            workWidth(bview));
2836                         if (z >= row->pos()) {
2837                                 row->pos(z + 1);
2838                                 
2839                                 Row * tmprow = row->previous();
2840                                 
2841                                 // maybe the current row is now empty
2842                                 if (row->pos() >= row->par()->size()) {
2843                                         // remove it
2844                                         removeRow(row);
2845                                         need_break_row = 0;
2846                                 } else {
2847                                         breakAgainOneRow(bview, row);
2848                                         if (row->next() && row->next()->par() == row->par())
2849                                                 need_break_row = row->next();
2850                                         else
2851                                                 need_break_row = 0;
2852                                 }
2853                                 
2854                                 // set the dimensions of the row above
2855                                 y -= tmprow->height();
2856                                 tmprow->fill(fill(bview, tmprow,
2857                                                   workWidth(bview)));
2858                                 setHeightOfRow(bview, tmprow);
2859                                 
2860                                 refresh_y = y;
2861                                 refresh_row = tmprow;
2862                                 status(bview, LyXText::NEED_MORE_REFRESH);
2863                                 setCursor(bview, cursor.par(), cursor.pos(),
2864                                           false, cursor.boundary());
2865                                 //current_font = rawtmpfont;
2866                                 //real_current_font = realtmpfont;
2867                                 // check, whether the last character's font has changed.
2868                                 if (rawparfont !=
2869                                     cursor.par()->getFontSettings(bview->buffer()->params,
2870                                                                   cursor.par()->size() - 1))
2871                                         redoHeightOfParagraph(bview, cursor);
2872                                 return;
2873                         }
2874                 }
2875                 
2876                 // break the cursor row again
2877                 if (row->next() && row->next()->par() == row->par() &&
2878                     (rowLast(row) == row->par()->size() - 1 ||
2879                      nextBreakPoint(bview, row, workWidth(bview)) != rowLast(row))) {
2880                         
2881                         /* it can happen that a paragraph loses one row
2882                          * without a real breakup. This is when a word
2883                          * is to long to be broken. Well, I don t care this 
2884                          * hack ;-) */
2885                         if (rowLast(row) == row->par()->size() - 1)
2886                                 removeRow(row->next());
2887                         
2888                         refresh_y = y;
2889                         refresh_row = row;
2890                         status(bview, LyXText::NEED_MORE_REFRESH);
2891                         
2892                         breakAgainOneRow(bview, row);
2893                         // will the cursor be in another row now?
2894                         if (row->next() && row->next()->par() == row->par() &&
2895                             rowLast(row) <= cursor.pos()) {
2896                                 row = row->next();
2897                                 breakAgainOneRow(bview, row);
2898                         }
2899
2900                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2901
2902                         if (row->next() && row->next()->par() == row->par())
2903                                 need_break_row = row->next();
2904                         else
2905                                 need_break_row = 0;
2906                 } else  {
2907                         // set the dimensions of the row
2908                         row->fill(fill(bview, row, workWidth(bview)));
2909                         int const tmpheight = row->height();
2910                         setHeightOfRow(bview, row);
2911                         if (tmpheight == row->height())
2912                                 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2913                         else
2914                                 status(bview, LyXText::NEED_MORE_REFRESH);
2915                         refresh_y = y;
2916                         refresh_row = row;
2917                         setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2918                 }
2919         }
2920
2921         // current_font = rawtmpfont;
2922         // real_current_font = realtmpfont;
2923
2924         if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
2925             != cursor.boundary())
2926                 setCursor(bview, cursor.par(), cursor.pos(), false,
2927                           !cursor.boundary());
2928
2929         lastpos = cursor.par()->size();
2930         if (cursor.pos() == lastpos)
2931                 setCurrentFont(bview);
2932         
2933         // check, whether the last characters font has changed.
2934         if (rawparfont != 
2935             cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1)) {
2936                 redoHeightOfParagraph(bview, cursor);
2937         } else {
2938                 // now the special right address boxes
2939                 if (textclasslist.Style(bview->buffer()->params.textclass,
2940                                         cursor.par()->getLayout()).margintype == MARGIN_RIGHT_ADDRESS_BOX) {
2941                         redoDrawingOfParagraph(bview, cursor); 
2942                 }
2943         }
2944 }
2945
2946
2947 void LyXText::getVisibleRow(BufferView * bview, int y_offset, int x_offset,
2948                             Row * row_ptr, int y, bool cleared)
2949 {
2950         // returns a printed row
2951         Painter & pain = bview->painter();
2952         
2953         bool const is_rtl =
2954                 row_ptr->par()->isRightToLeftPar(bview->buffer()->params);
2955         
2956         Paragraph::size_type const last = rowLastPrintable(row_ptr);
2957
2958         Paragraph::size_type vpos;
2959         Paragraph::size_type pos;
2960
2961         float tmpx;
2962
2963         LyXFont font(LyXFont::ALL_SANE);
2964         int maxdesc;
2965         if (row_ptr->height() <= 0) {
2966                 lyxerr << "LYX_ERROR: row.height: "
2967                        << row_ptr->height() << endl;
2968                 return;
2969         }
2970
2971         float x;
2972         float fill_separator;
2973         float fill_hfill;
2974         float fill_label_hfill;
2975         prepareToPrint(bview, row_ptr, x, fill_separator,
2976                        fill_hfill, fill_label_hfill);
2977         
2978         if (inset_owner && (x < 0))
2979                 x = 0;
2980         x += x_offset;
2981         
2982         // clear the area where we want to paint/print
2983         int const ww = bview->workWidth();
2984
2985         bool clear_area = true;
2986         Inset * inset = 0;
2987
2988         if (!bview->screen()->forceClear() && last == row_ptr->pos()
2989             && row_ptr->par()->getChar(row_ptr->pos()) == Paragraph::META_INSET
2990             && (inset = row_ptr->par()->getInset(row_ptr->pos()))) {
2991                 clear_area = inset->doClearArea();
2992         }
2993         // we don't need to clear it's already done!!!
2994         if (cleared) {
2995                 clear_area = true;
2996         } else if (clear_area) {
2997                 int const y = y_offset < 0 ? 0 : y_offset;
2998                 int const h = y_offset < 0 ?
2999                         row_ptr->height() + y_offset : row_ptr->height();
3000                 int const w = inset_owner ?
3001                         inset_owner->textWidth(bview, true) : ww;
3002                 int const x = x_offset;
3003                 pain.fillRectangle(x, y, w, h, backgroundColor());
3004         } else if (inset != 0) {
3005                 int h = row_ptr->baseline() - inset->ascent(bview, font);
3006                 // first clear the whole row above the inset!
3007                 if (h > 0) {
3008                         int const w = (inset_owner ?
3009                                  inset_owner->textWidth(bview, true) : ww);
3010                         pain.fillRectangle(x_offset, y_offset, w, h,
3011                                            backgroundColor());
3012                 }
3013                 h += inset->ascent(bview, font) + inset->descent(bview, font);
3014                 // clear the space below the inset!
3015                 if ((row_ptr->height() - h) > 0) {
3016                         int const w = (inset_owner ?
3017                                        inset_owner->textWidth(bview, true) : ww);
3018                         pain.fillRectangle(x_offset, y_offset + h,
3019                                            w, row_ptr->height() - h,
3020                                            backgroundColor());
3021                 }
3022                 // clear the space behind the inset, if needed
3023                 if (!inset->display() && !inset->needFullRow()) {
3024                         int const w = (inset_owner ?
3025                                        inset_owner->textWidth(bview, true) : ww);
3026                         int const xp = int(x) + inset->width(bview, font);
3027                         if (w-xp > 0) {
3028                                 pain.fillRectangle(xp, y_offset,
3029                                                    w-xp, row_ptr->height(),
3030                                                    backgroundColor());
3031                         }
3032                 }
3033         }
3034
3035         if (selection.set()) {
3036                 int const w = (inset_owner ?
3037                                inset_owner->textWidth(bview, true) : ww);
3038                 // selection code
3039                 if (bidi_same_direction) {
3040                         if (selection.start.row() == row_ptr &&
3041                             selection.end.row() == row_ptr) {
3042                                 if (selection.start.x() < selection.end.x())
3043                                         pain.fillRectangle(x_offset + selection.start.x(),
3044                                                            y_offset,
3045                                                            selection.end.x() - selection.start.x(),
3046                                                            row_ptr->height(),
3047                                                            LColor::selection);
3048                                 else
3049                                         pain.fillRectangle(x_offset + selection.end.x(),
3050                                                            y_offset,
3051                                                            selection.start.x() - selection.end.x(),
3052                                                            row_ptr->height(),
3053                                                            LColor::selection);
3054                         } else if (selection.start.row() == row_ptr) {
3055                                 if (is_rtl)
3056                                         pain.fillRectangle(x_offset, y_offset,
3057                                                            selection.start.x(),
3058                                                            row_ptr->height(),
3059                                                            LColor::selection);
3060                                 else
3061                                         pain.fillRectangle(x_offset + selection.start.x(),
3062                                                            y_offset,
3063                                                            w - selection.start.x(),
3064                                                            row_ptr->height(),
3065                                                            LColor::selection);
3066                         } else if (selection.end.row() == row_ptr) {
3067                                 if (is_rtl)
3068                                         pain.fillRectangle(x_offset + selection.end.x(),
3069                                                            y_offset,
3070                                                            w - selection.end.x(),
3071                                                            row_ptr->height(),
3072                                                            LColor::selection);
3073                                 else
3074                                         pain.fillRectangle(x_offset, y_offset,
3075                                                            selection.end.x(),
3076                                                            row_ptr->height(),
3077                                                            LColor::selection);
3078                         } else if (y > selection.start.y()
3079                                    && y < selection.end.y()) {
3080                                 pain.fillRectangle(x_offset, y_offset, w,
3081                                                    row_ptr->height(),
3082                                                    LColor::selection);
3083                         }
3084                 } else if (selection.start.row() != row_ptr &&
3085                             selection.end.row() != row_ptr &&
3086                             y > selection.start.y()
3087                             && y < selection.end.y()) {
3088                         pain.fillRectangle(x_offset, y_offset, w,
3089                                            row_ptr->height(),
3090                                            LColor::selection);
3091                 } else if (selection.start.row() == row_ptr ||
3092                            selection.end.row() == row_ptr) {
3093                         float tmpx = x;
3094                         if ((selection.start.row() != row_ptr && !is_rtl) ||
3095                              (selection.end.row() != row_ptr && is_rtl))
3096                                 pain.fillRectangle(x_offset, y_offset,
3097                                                    int(tmpx),
3098                                                    row_ptr->height(),
3099                                                    LColor::selection);
3100                         Paragraph::size_type main_body =
3101                                 beginningOfMainBody(bview->buffer(),
3102                                                     row_ptr->par());
3103                         
3104                         for (vpos = row_ptr->pos(); vpos <= last; ++vpos)  {
3105                                 pos = vis2log(vpos);
3106                                 float const old_tmpx = tmpx;
3107                                 if (main_body > 0 && pos == main_body-1) {
3108                                         tmpx += fill_label_hfill +
3109                                                 lyxfont::width(textclasslist.Style(bview->buffer()->params.textclass,
3110                                                                                    row_ptr->par()->getLayout()).labelsep,
3111                                                                getLabelFont(bview->buffer(),row_ptr->par()));
3112                                         if (row_ptr->par()->isLineSeparator(main_body-1))
3113                                                 tmpx -= singleWidth(bview, row_ptr->par(), main_body-1);
3114                                 }
3115                                 if (hfillExpansion(bview->buffer(), row_ptr, pos)) {
3116                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3117                                         if (pos >= main_body)
3118                                                 tmpx += fill_hfill;
3119                                         else 
3120                                                 tmpx += fill_label_hfill;
3121                                 }
3122                                 else if (row_ptr->par()->isSeparator(pos)) {
3123                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3124                                         if (pos >= main_body)
3125                                                 tmpx += fill_separator;
3126                                 } else
3127                                         tmpx += singleWidth(bview, row_ptr->par(), pos);
3128                                 
3129                                 if ((selection.start.row() != row_ptr ||
3130                                       selection.start.pos() <= pos) &&
3131                                      (selection.end.row() != row_ptr ||
3132                                       pos < selection.end.pos()) )
3133                                         // Here we do not use x_offset as x_offset was
3134                                         // added to x.
3135                                         pain.fillRectangle(int(old_tmpx),
3136                                                            y_offset,
3137                                                            int(tmpx - old_tmpx + 1),
3138                                                            row_ptr->height(),
3139                                                            LColor::selection);
3140                         }
3141
3142                         if ((selection.start.row() != row_ptr && is_rtl) ||
3143                              (selection.end.row() != row_ptr && !is_rtl) )
3144                                 pain.fillRectangle(x_offset + int(tmpx),
3145                                                    y_offset,
3146                                                    int(ww - tmpx),
3147                                                    row_ptr->height(),
3148                                                    LColor::selection);
3149                 }
3150         }
3151
3152         int box_x = 0;
3153
3154         // Draw appendix lines
3155         Paragraph * firstpar = row_ptr->par();
3156
3157         if (firstpar->params().appendix()) {
3158                 pain.line(1, y_offset,
3159                           1, y_offset + row_ptr->height(),
3160                           LColor::appendixline);
3161                 pain.line(ww - 2, y_offset,
3162                           ww - 2, y_offset + row_ptr->height(),
3163                           LColor::appendixline);
3164         }
3165
3166         // Draw depth lines
3167         Paragraph::depth_type const depth = firstpar->getDepth();
3168         if (depth > 0) {
3169                 Paragraph::depth_type next_depth = 0;
3170                 Paragraph::depth_type prev_depth = 0;
3171                 if (row_ptr->next())
3172                                 next_depth = row_ptr->next()->par()->getDepth();
3173                 if (row_ptr->previous())
3174                                 prev_depth = row_ptr->previous()->par()->getDepth();
3175
3176                 for (Paragraph::depth_type i = 1; i <= depth; ++i) {
3177                         int const line_x = (LYX_PAPER_MARGIN / 5) *
3178                                 i + box_x + x_offset;
3179                         pain.line(line_x, y_offset, line_x,
3180                                   y_offset + row_ptr->height() - 1 - (i - next_depth - 1) * 3,
3181                                   LColor::depthbar);
3182                 
3183                         if (i > prev_depth)
3184                                 pain.fillRectangle(line_x, y_offset, LYX_PAPER_MARGIN / 5, 2,
3185                                                    LColor::depthbar);
3186                         if (i > next_depth)
3187                                 pain.fillRectangle(line_x,
3188                                                    y_offset + row_ptr->height() - 2 - (i - next_depth - 1) * 3,
3189                                                    LYX_PAPER_MARGIN / 5, 2,
3190                                                    LColor::depthbar);
3191                 }
3192         }
3193
3194         
3195         LyXLayout const & layout =
3196                 textclasslist.Style(bview->buffer()->params.textclass,
3197                                     row_ptr->par()->getLayout());
3198
3199         int y_top = 0;
3200         int y_bottom = row_ptr->height();
3201         
3202         // is it a first row?
3203         if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
3204                 
3205                 // start of appendix?
3206                 if (row_ptr->par()->params().startOfAppendix()) {
3207                         pain.line(1, y_offset,
3208                                   ww - 2, y_offset,
3209                                   LColor::appendixline);
3210                 }
3211                 
3212                 // think about the margins
3213                 if (!row_ptr->previous() && bv_owner)
3214                         y_top += LYX_PAPER_MARGIN;
3215                 
3216                 // draw a top pagebreak
3217                 if (row_ptr->par()->params().pagebreakTop()) {
3218                         LyXFont pb_font;
3219                         pb_font.setColor(LColor::pagebreak).decSize();
3220                         int w = 0;
3221                         int a = 0;
3222                         int d = 0;
3223                         pain.line(0, y_offset + y_top + 2*defaultHeight(),
3224                                   ww, 
3225                                   y_offset + y_top + 2 * defaultHeight(),
3226                                   LColor::pagebreak, 
3227                                   Painter::line_onoffdash);
3228                         lyxfont::rectText(_("Page Break (top)"), pb_font,
3229                                           w, a, d);
3230                         pain.rectText((ww - w)/2,
3231                                       y_offset + y_top + 2 * defaultHeight() + d,
3232                                       _("Page Break (top)"),
3233                                       pb_font,
3234                                       backgroundColor(),
3235                                       backgroundColor());
3236                         y_top += 3 * defaultHeight();
3237                 }
3238                 
3239                 if (row_ptr->par()->params().spaceTop().kind() == VSpace::VFILL) {
3240                         // draw a vfill top
3241                         pain.line(0, y_offset + 2 + y_top,
3242                                   LYX_PAPER_MARGIN, y_offset + 2 + y_top,
3243                                   LColor::vfillline);
3244                         
3245                         pain.line(0, y_offset + y_top + 3 * defaultHeight(),
3246                                   LYX_PAPER_MARGIN,
3247                                   y_offset + y_top + 3 * defaultHeight(),
3248                                   LColor::vfillline);
3249                         
3250                         pain.line(LYX_PAPER_MARGIN / 2, y_offset + 2 + y_top,
3251                                   LYX_PAPER_MARGIN / 2,
3252                                   y_offset + y_top + 3 * defaultHeight(),
3253                                   LColor::vfillline);
3254                         
3255                         y_top += 3 * defaultHeight();
3256                 }
3257                 
3258                 // think about user added space
3259                 y_top += int(row_ptr->par()->params().spaceTop().inPixels(bview));
3260                 
3261                 // think about the parskip
3262                 // some parskips VERY EASY IMPLEMENTATION
3263                 if (bview->buffer()->params.paragraph_separation == BufferParams::PARSEP_SKIP) {
3264                         if (layout.latextype == LATEX_PARAGRAPH
3265                             && firstpar->getDepth() == 0
3266                             && firstpar->previous())
3267                                 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3268                         else if (firstpar->previous()
3269                                  && textclasslist.Style(bview->buffer()->params.textclass,
3270                                                         firstpar->previous()->getLayout()).latextype == LATEX_PARAGRAPH
3271                                  && firstpar->previous()->getDepth() == 0)
3272                                 // is it right to use defskip here, too? (AS) 
3273                                 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3274                 }
3275                 
3276                 if (row_ptr->par()->params().lineTop()) {
3277                         // draw a top line
3278                         y_top +=  lyxfont::ascent('x',
3279                                                   getFont(bview->buffer(),
3280                                                           row_ptr->par(), 0));
3281                         int const w = (inset_owner ?
3282                                        inset_owner->width(bview, font) : ww);
3283                         int const xp = static_cast<int>(inset_owner ? x : 0);
3284                         pain.line(xp, y_offset + y_top,
3285                                   w, y_offset + y_top,
3286                                   LColor::topline,
3287                                   Painter::line_solid,
3288                                   Painter::line_thick);
3289                         
3290                         y_top +=  lyxfont::ascent('x',getFont(bview->buffer(),
3291                                                               row_ptr->par(), 0));
3292                 }
3293                 
3294                 // should we print a label?
3295                 if (layout.labeltype >= LABEL_STATIC
3296                     && (layout.labeltype != LABEL_STATIC
3297                         || layout.latextype != LATEX_ENVIRONMENT
3298                         || row_ptr->par()->isFirstInSequence())) {
3299                         font = getLabelFont(bview->buffer(), row_ptr->par());
3300                         if (!row_ptr->par()->getLabelstring().empty()) {
3301                                 tmpx = x;
3302                                 string const tmpstring =
3303                                         row_ptr->par()->getLabelstring();
3304                                 
3305                                 if (layout.labeltype == LABEL_COUNTER_CHAPTER) {
3306                                         if (bview->buffer()->params.secnumdepth >= 0) {
3307                                                 // this is special code for
3308                                                 // the chapter layout. This is
3309                                                 // printed in an extra row
3310                                                 // and has a pagebreak at
3311                                                 // the top.
3312                                                 float spacing_val = 1.0;
3313                                                 if (!row_ptr->par()->params().spacing().isDefault()) {
3314                                                         spacing_val = row_ptr->par()->params().spacing().getValue();
3315                                                 } else {
3316                                                         spacing_val = bview->buffer()->params.spacing.getValue();
3317                                                 }
3318    
3319                                                 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val)
3320                                                         + int(layout.parsep) * defaultHeight();
3321                                                 if (is_rtl)
3322                                                         tmpx = ww - leftMargin(bview, row_ptr) - 
3323                                                                 lyxfont::width(tmpstring, font);
3324                                                 pain.text(int(tmpx),
3325                                                           y_offset + row_ptr->baseline() - row_ptr->ascent_of_text() - maxdesc,
3326                                                           tmpstring, font);
3327                                         }
3328                                 } else {
3329                                         if (is_rtl) {
3330                                                 tmpx = ww - leftMargin(bview, row_ptr)
3331                                                         + lyxfont::width(layout.labelsep, font);
3332                                         } else
3333                                                 tmpx = x - lyxfont::width(layout.labelsep, font)
3334                                                         - lyxfont::width(tmpstring, font);
3335
3336                                         // draw it!
3337                                         pain.text(int(tmpx),
3338                                                   y_offset + row_ptr->baseline(),
3339                                                   tmpstring, font);
3340                                 }
3341                         }
3342                         // the labels at the top of an environment.
3343                         // More or less for bibliography
3344                 } else if (layout.labeltype == LABEL_TOP_ENVIRONMENT ||
3345                            layout.labeltype == LABEL_BIBLIO ||
3346                            layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
3347                         if (row_ptr->par()->isFirstInSequence()) {
3348                                 font = getLabelFont(bview->buffer(),
3349                                                     row_ptr->par());
3350                                 if (!row_ptr->par()->getLabelstring().empty()) {
3351                                         string const tmpstring =
3352                                                 row_ptr->par()->getLabelstring();
3353                                         float spacing_val = 1.0;
3354                                         if (!row_ptr->par()->params().spacing().isDefault()) {
3355                                                 spacing_val = row_ptr->par()->params().spacing().getValue();
3356                                         } else {
3357                                                 spacing_val = bview->buffer()->params.spacing.getValue();
3358                                         }
3359    
3360                                         maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val
3361                                                       + (layout.labelbottomsep * defaultHeight()));
3362                                         
3363                                         tmpx = x;
3364                                         if (layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT){
3365                                                 tmpx = ( (is_rtl ? leftMargin(bview, row_ptr) : x)
3366                                                          + ww - rightMargin(bview->buffer(), row_ptr) ) / 2; 
3367                                                 tmpx -= lyxfont::width(tmpstring, font) / 2;
3368                                         } else if (is_rtl)
3369                                                 tmpx = ww - leftMargin(bview, row_ptr) - 
3370                                                         lyxfont::width(tmpstring, font);
3371                                         pain.text(int(tmpx),
3372                                                   y_offset + row_ptr->baseline()
3373                                                   - row_ptr->ascent_of_text()
3374                                                   - maxdesc,
3375                                                   tmpstring, font);
3376                                 }
3377                         }
3378                 }
3379                 if (layout.labeltype == LABEL_BIBLIO && row_ptr->par()->bibkey) {
3380                         font = getLayoutFont(bview->buffer(), row_ptr->par());
3381                         if (is_rtl)
3382                                 tmpx = ww - leftMargin(bview, row_ptr)
3383                                         + lyxfont::width(layout.labelsep, font);
3384                         else
3385                                 tmpx = x - lyxfont::width(layout.labelsep, font)
3386                                         - row_ptr->par()->bibkey->width(bview, font);
3387                         row_ptr->par()->bibkey->draw(bview, font,
3388                                                    y_offset + row_ptr->baseline(), 
3389                                                    tmpx, clear_area);
3390                 }
3391         }
3392         
3393         // is it a last row?
3394         Paragraph * par = row_ptr->par();
3395         if (row_ptr->par() == par
3396             && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par())) {
3397                 // think about the margins
3398                 if (!row_ptr->next() && bv_owner)
3399                         y_bottom -= LYX_PAPER_MARGIN;
3400                 
3401                 // draw a bottom pagebreak
3402                 if (firstpar->params().pagebreakBottom()) {
3403                         LyXFont pb_font;
3404                         pb_font.setColor(LColor::pagebreak).decSize();
3405                         int const y_place = y_offset + y_bottom
3406                                 - 2 * defaultHeight();
3407                         
3408                         int w = 0;
3409                         int a = 0;
3410                         int d = 0;
3411                         pain
3412                                 .line(0, y_place, ww, y_place,
3413                                       LColor::pagebreak,
3414                                       Painter::line_onoffdash);
3415                         lyxfont::rectText(_("Page Break (bottom)"), pb_font,
3416                                           w, a, d);
3417                         pain.rectText((ww - w) / 2, y_place + d,
3418                                       _("Page Break (bottom)"),
3419                                       pb_font,
3420                                       backgroundColor(),
3421                                       backgroundColor());
3422                         y_bottom -= 3 * defaultHeight();
3423                 }
3424                 
3425                 if (firstpar->params().spaceBottom().kind() == VSpace::VFILL) {
3426                         // draw a vfill bottom
3427                         int const y_place = y_offset + y_bottom
3428                                 - 3 * defaultHeight();
3429                         
3430                         pain.line(0, y_place,
3431                                   LYX_PAPER_MARGIN, y_place,
3432                                   LColor::vfillline);
3433                         pain.line(0, y_offset + y_bottom - 2,
3434                                   LYX_PAPER_MARGIN,
3435                                   y_offset + y_bottom - 2,
3436                                   LColor::vfillline);
3437                         pain.line(LYX_PAPER_MARGIN / 2,
3438                                   y_place,
3439                                   LYX_PAPER_MARGIN / 2,
3440                                   y_offset + y_bottom - 2,
3441                                   LColor::vfillline);
3442                         y_bottom -= 3 * defaultHeight();
3443                 }
3444                 
3445                 // think about user added space
3446                 y_bottom -= int(firstpar->params().spaceBottom().inPixels(bview));
3447                 
3448                 if (firstpar->params().lineBottom()) {
3449                         // draw a bottom line
3450                         y_bottom -= lyxfont::ascent('x',
3451                                                     getFont(bview->buffer(),
3452                                                             par,
3453                                                             max(Paragraph::size_type(0), par->size() - 1)));
3454                         int const w = (inset_owner ?
3455                                        inset_owner->width(bview, font) : ww);
3456                         int const xp = static_cast<int>(inset_owner ? x : 0);
3457                         pain.line(xp, y_offset + y_bottom,
3458                                   w, y_offset + y_bottom,
3459                                   LColor::topline, Painter::line_solid,
3460                                   Painter::line_thick);
3461                         y_bottom -= lyxfont::ascent('x',
3462                                                     getFont(bview->buffer(),
3463                                                             par,
3464                                                             max(Paragraph::size_type(0), par->size() - 1)));
3465                 }
3466
3467                 // draw an endlabel
3468                 int const endlabel =
3469                         row_ptr->par()->getEndLabel(bview->buffer()->params);
3470                 switch (endlabel) {
3471                 case END_LABEL_BOX:
3472                 case END_LABEL_FILLED_BOX:
3473                 {
3474                         LyXFont const font = getFont(bview->buffer(),
3475                                                      row_ptr->par(), last);
3476                         int const size = int(0.75 * lyxfont::maxAscent(font));
3477                         int const y = (y_offset + row_ptr->baseline()) - size;
3478                         int x = is_rtl ? LYX_PAPER_MARGIN 
3479                                 : ww - LYX_PAPER_MARGIN - size;
3480
3481                         if (row_ptr->fill() <= size)
3482                                 x += (size - row_ptr->fill() + 1) * (is_rtl ? -1 : 1);
3483                         if (endlabel == END_LABEL_BOX) {
3484                                 pain.line(x, y, x, y + size,
3485                                           LColor::eolmarker);
3486                                 pain.line(x + size, y, x + size , y + size,
3487                                           LColor::eolmarker);
3488                                 pain.line(x, y, x + size, y,
3489                                           LColor::eolmarker);
3490                                 pain.line(x, y + size, x + size, y + size,
3491                                           LColor::eolmarker);
3492                         } else
3493                                 pain.fillRectangle(x, y, size, size,
3494                                                    LColor::eolmarker);
3495                         break;
3496                 }
3497                 case END_LABEL_STATIC:
3498                 {
3499                         LyXTextClass::LayoutList::size_type layout = row_ptr->par()->getLayout();
3500                         string const tmpstring = textclasslist.
3501                                 Style(bview->buffer()->params.textclass,
3502                                       layout).endlabelstring();
3503                         font = getLabelFont(bview->buffer(), row_ptr->par());
3504                         int const tmpx = is_rtl ?
3505                                 int(x) - lyxfont::width(tmpstring, font)
3506                                 : ww - rightMargin(bview->buffer(), row_ptr) - row_ptr->fill();
3507                         pain.text( tmpx, y_offset + row_ptr->baseline(), tmpstring, font);
3508                         break;
3509                 }
3510                 case END_LABEL_NO_LABEL:
3511                         break;
3512                 }
3513         }
3514         
3515         // draw the text in the pixmap
3516         
3517         vpos = row_ptr->pos();
3518
3519         Paragraph::size_type main_body = 
3520                 beginningOfMainBody(bview->buffer(), row_ptr->par());
3521         if (main_body > 0 &&
3522             (main_body-1 > last || 
3523              !row_ptr->par()->isLineSeparator(main_body - 1)))
3524                 main_body = 0;
3525         
3526         while (vpos <= last)  {
3527                 pos = vis2log(vpos);
3528                 if (main_body > 0 && pos == main_body - 1) {
3529                         x += fill_label_hfill
3530                                 + lyxfont::width(layout.labelsep,
3531                                                  getLabelFont(bview->buffer(),
3532                                                               row_ptr->par()))
3533                                 - singleWidth(bview,
3534                                               row_ptr->par(),
3535                                               main_body - 1);
3536                 }
3537                 
3538                 if (row_ptr->par() ->isHfill(pos)) {
3539                         x += 1;
3540                         pain.line(int(x),
3541                                   y_offset + row_ptr->baseline() - defaultHeight() / 2,
3542                                   int(x),
3543                                   y_offset + row_ptr->baseline(),
3544                                   LColor::vfillline);
3545                         
3546                         if (hfillExpansion(bview->buffer(),
3547                                            row_ptr, pos)) {
3548                                 if (pos >= main_body) {
3549                                         pain.line(int(x),
3550                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3551                                                   int(x + fill_hfill),
3552                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3553                                                   LColor::vfillline,
3554                                                   Painter::line_onoffdash);
3555                                         x += fill_hfill;
3556                                 } else {
3557                                         pain.line(int(x),
3558                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3559                                                   int(x + fill_label_hfill),
3560                                                   y_offset + row_ptr->baseline() - defaultHeight() / 4,
3561                                                   LColor::vfillline,
3562                                                   Painter::line_onoffdash);
3563                                         
3564                                         x += fill_label_hfill;
3565                                 }
3566                                 pain.line(int(x),
3567                                           y_offset + row_ptr->baseline() - defaultHeight() / 2,
3568                                           int(x),
3569                                           y_offset + row_ptr->baseline(),
3570                                           LColor::vfillline);
3571                         }
3572                         x += 2;
3573                         ++vpos;
3574                 } else if (row_ptr->par()->isSeparator(pos)) {
3575                         x += singleWidth(bview,
3576                                          row_ptr->par(), pos);
3577                         if (pos >= main_body)
3578                                 x += fill_separator;
3579                         ++vpos;
3580                 } else
3581                         draw(bview, row_ptr, vpos, y_offset, x, clear_area);
3582         }
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 }