1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2001 The LyX Team.
9 * ====================================================== */
12 //#include <cstdlib> //these two do not seem useful anymore (JMarc)
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"
27 #include "bufferparams.h"
36 #include "lyxscreen.h"
37 #include "bufferview_funcs.h"
38 #include "BufferView.h"
40 #include "ParagraphParameters.h"
41 #include "undo_funcs.h"
51 int const LYX_PAPER_MARGIN = 20;
55 extern int bibitemMaxWidth(BufferView *, LyXFont const &);
58 int LyXText::workWidth(BufferView * bview) const
61 return inset_owner->textWidth(bview);
63 return bview->workWidth();
67 int LyXText::workWidth(BufferView * bview, Inset * inset) const
69 Buffer::inset_iterator it;
71 Paragraph::size_type pos;
73 for(it=bview->buffer()->inset_iterator_begin();
74 it != bview->buffer()->inset_iterator_end();
85 for(; row; row = row->next()) {
86 if ((row->par() == par && row->pos() >= pos)) {
89 else if ((row->next()->par() == par) &&
90 (row->next()->pos() >= pos))
95 return workWidth(bview) - leftMargin(bview, row);
98 return workWidth(bview);
102 int LyXText::getRealCursorX(BufferView * bview) const
105 if (the_locking_inset && (the_locking_inset->getLyXText(bview)!=this))
106 x = the_locking_inset->getLyXText(bview)->getRealCursorX(bview);
111 unsigned char LyXText::transformChar(unsigned char c, Paragraph * par,
112 Paragraph::size_type pos) const
114 if (!Encodings::is_arabic(c))
115 if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && isdigit(c))
116 return c + (0xb0 - '0');
120 unsigned char const prev_char = pos > 0 ? par->getChar(pos-1) : ' ';
121 unsigned char next_char = ' ';
123 for (Paragraph::size_type i = pos+1; i < par->size(); ++i)
124 if (!Encodings::IsComposeChar_arabic(par->getChar(i))) {
125 next_char = par->getChar(i);
129 if (Encodings::is_arabic(next_char)) {
130 if (Encodings::is_arabic(prev_char))
131 return Encodings::TransformChar(c, Encodings::FORM_MEDIAL);
133 return Encodings::TransformChar(c, Encodings::FORM_INITIAL);
135 if (Encodings::is_arabic(prev_char))
136 return Encodings::TransformChar(c, Encodings::FORM_FINAL);
138 return Encodings::TransformChar(c, Encodings::FORM_ISOLATED);
142 // This is the comments that some of the warnings below refers to.
143 // There are some issues in this file and I don't think they are
144 // really related to the FIX_DOUBLE_SPACE patch. I'd rather think that
145 // this is a problem that has been here almost from day one and that a
146 // larger userbase with differenct access patters triggers the bad
147 // behaviour. (segfaults.) What I think happen is: In several places
148 // we store the paragraph in the current cursor and then moves the
149 // cursor. This movement of the cursor will delete paragraph at the
150 // old position if it is now empty. This will make the temporary
151 // pointer to the old cursor paragraph invalid and dangerous to use.
152 // And is some cases this will trigger a segfault. I have marked some
153 // of the cases where this happens with a warning, but I am sure there
154 // are others in this file and in text2.C. There is also a note in
155 // Delete() that you should read. In Delete I store the paragraph->id
156 // instead of a pointer to the paragraph. I am pretty sure this faulty
157 // use of temporary pointers to paragraphs that might have gotten
158 // invalidated (through a cursor movement) before they are used, are
159 // the cause of the strange crashes we get reported often.
161 // It is very tiresom to change this code, especially when it is as
162 // hard to read as it is. Help to fix all the cases where this is done
163 // would be greately appreciated.
167 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
168 Paragraph::size_type pos) const
170 char const c = par->getChar(pos);
171 return singleWidth(bview, par, pos, c);
175 int LyXText::singleWidth(BufferView * bview, Paragraph * par,
176 Paragraph::size_type pos, char c) const
178 LyXFont const font = getFont(bview->buffer(), par, pos);
180 // The most common case is handled first (Asger)
181 if (IsPrintable(c)) {
182 if (font.language()->RightToLeft()) {
183 if (font.language()->lang() == "arabic" &&
184 (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
185 lyxrc.font_norm_type == LyXRC::ISO_10646_1))
186 if (Encodings::IsComposeChar_arabic(c))
189 c = transformChar(c, par, pos);
190 else if (font.language()->lang() == "hebrew" &&
191 Encodings::IsComposeChar_hebrew(c))
194 return lyxfont::width(c, font);
196 } else if (IsHfillChar(c)) {
197 return 3; /* Because of the representation
198 * as vertical lines */
199 } else if (c == Paragraph::META_INSET) {
200 Inset * tmpinset = par->getInset(pos);
202 #if 0 // seems not to be needed, but ...
203 tmpinset->update(bview, font);
205 return tmpinset->width(bview, font);
209 } else if (IsSeparatorChar(c))
211 else if (IsNewlineChar(c))
213 return lyxfont::width(c, font);
217 // Returns the paragraph position of the last character in the specified row
218 Paragraph::size_type LyXText::rowLast(Row const * row) const
220 if (row->next() == 0)
221 return row->par()->size() - 1;
222 else if (row->next()->par() != row->par())
223 return row->par()->size() - 1;
225 return row->next()->pos() - 1;
229 Paragraph::size_type LyXText::rowLastPrintable(Row const * row) const
231 Paragraph::size_type const last = rowLast(row);
232 if (last >= row->pos()
234 && row->next()->par() == row->par()
235 && row->par()->isSeparator(last))
242 void LyXText::computeBidiTables(Buffer const * buf, Row * row) const
244 bidi_same_direction = true;
245 if (!lyxrc.rtl_support) {
250 bidi_start = row->pos();
251 bidi_end = rowLastPrintable(row);
253 if (bidi_start > bidi_end) {
258 if (bidi_end + 2 - bidi_start >
259 static_cast<Paragraph::size_type>(log2vis_list.size())) {
260 Paragraph::size_type new_size =
261 (bidi_end + 2 - bidi_start < 500) ?
262 500 : 2 * (bidi_end + 2 - bidi_start);
263 log2vis_list.resize(new_size);
264 vis2log_list.resize(new_size);
265 bidi_levels.resize(new_size);
268 vis2log_list[bidi_end + 1 - bidi_start] = -1;
269 log2vis_list[bidi_end + 1 - bidi_start] = -1;
271 Paragraph::size_type stack[2];
273 row->par()->getParLanguage(buf->params)->RightToLeft();
277 Paragraph::size_type const main_body =
278 beginningOfMainBody(buf, row->par());
280 for (Paragraph::size_type lpos = bidi_start;
281 lpos <= bidi_end; ++lpos) {
282 bool is_space = row->par()->isLineSeparator(lpos);
283 Paragraph::size_type const pos =
284 (is_space && lpos + 1 <= bidi_end &&
285 !row->par()->isLineSeparator(lpos + 1) &&
286 !row->par()->isNewline(lpos + 1))
288 LyXFont font = row->par()->getFontSettings(buf->params, pos);
289 if (pos != lpos && 0 < lpos && rtl0 && font.isRightToLeft() &&
290 font.number() == LyXFont::ON &&
291 row->par()->getFontSettings(buf->params, lpos - 1).number()
293 font = row->par()->getFontSettings(buf->params, lpos);
298 bool new_rtl = font.isVisibleRightToLeft();
299 bool new_rtl0 = font.isRightToLeft();
302 if (lpos == main_body - 1
303 && row->pos() < main_body - 1
305 new_level = (rtl_par) ? 1 : 0;
306 new_rtl = new_rtl0 = rtl_par;
308 new_level = (new_rtl) ? 1 : 2;
310 new_level = (rtl_par) ? 2 : 0;
312 if (is_space && new_level >= level) {
318 int new_level2 = new_level;
320 if (level == new_level && rtl0 != new_rtl0) {
322 log2vis_list[lpos - bidi_start] = (rtl) ? 1 : -1;
323 } else if (level < new_level) {
324 log2vis_list[lpos - bidi_start] = (rtl) ? -1 : 1;
325 if (new_level > rtl_par)
326 bidi_same_direction = false;
328 log2vis_list[lpos - bidi_start] = (new_rtl) ? -1 : 1;
331 bidi_levels[lpos - bidi_start] = new_level;
333 while (level > new_level2) {
334 Paragraph::size_type old_lpos =
336 int delta = lpos - old_lpos - 1;
339 log2vis_list[lpos - bidi_start] += delta;
340 log2vis_list[old_lpos - bidi_start] += delta;
342 while (level < new_level)
343 stack[level++] = lpos;
347 Paragraph::size_type const old_lpos = stack[--level];
348 int delta = bidi_end - old_lpos;
351 log2vis_list[old_lpos - bidi_start] += delta;
354 Paragraph::size_type vpos = bidi_start - 1;
355 for (Paragraph::size_type lpos = bidi_start;
356 lpos <= bidi_end; ++lpos) {
357 vpos += log2vis_list[lpos - bidi_start];
358 vis2log_list[vpos - bidi_start] = lpos;
359 log2vis_list[lpos - bidi_start] = vpos;
364 // This method requires a previous call to ComputeBidiTables()
365 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
366 Paragraph::size_type pos) const
368 if (!lyxrc.rtl_support || pos == 0)
371 if (!bidi_InRange(pos - 1)) {
372 /// This can happen if pos is the first char of a row.
373 /// Returning false in this case is incorrect!
377 bool const rtl = bidi_level(pos - 1) % 2;
378 bool const rtl2 = bidi_InRange(pos)
379 ? bidi_level(pos) % 2
380 : par->isRightToLeftPar(buf->params);
385 bool LyXText::isBoundary(Buffer const * buf, Paragraph * par,
386 Paragraph::size_type pos,
387 LyXFont const & font) const
389 if (!lyxrc.rtl_support)
390 return false; // This is just for speedup
392 bool const rtl = font.isVisibleRightToLeft();
393 bool const rtl2 = bidi_InRange(pos)
394 ? bidi_level(pos) % 2
395 : par->isRightToLeftPar(buf->params);
400 void LyXText::draw(BufferView * bview, Row const * row,
401 Paragraph::size_type & vpos,
402 int offset, float & x, bool cleared)
404 Painter & pain = bview->painter();
406 Paragraph::size_type pos = vis2log(vpos);
407 char c = row->par()->getChar(pos);
410 if (IsNewlineChar(c)) {
412 // Draw end-of-line marker
413 LyXFont const font = getFont(bview->buffer(), row->par(), pos);
414 int const wid = lyxfont::width('n', font);
415 int const asc = lyxfont::maxAscent(font);
416 int const y = offset + row->baseline();
420 if (bidi_level(pos) % 2 == 0) {
421 xp[0] = int(x + wid * 0.375);
422 yp[0] = int(y - 0.875 * asc * 0.75);
425 yp[1] = int(y - 0.500 * asc * 0.75);
427 xp[2] = int(x + wid * 0.375);
428 yp[2] = int(y - 0.125 * asc * 0.75);
430 pain.lines(xp, yp, 3, LColor::eolmarker);
433 yp[0] = int(y - 0.500 * asc * 0.75);
435 xp[1] = int(x + wid);
436 yp[1] = int(y - 0.500 * asc * 0.75);
438 xp[2] = int(x + wid);
439 yp[2] = int(y - asc * 0.75);
441 pain.lines(xp, yp, 3, LColor::eolmarker);
443 xp[0] = int(x + wid * 0.625);
444 yp[0] = int(y - 0.875 * asc * 0.75);
446 xp[1] = int(x + wid);
447 yp[1] = int(y - 0.500 * asc * 0.75);
449 xp[2] = int(x + wid * 0.625);
450 yp[2] = int(y - 0.125 * asc * 0.75);
452 pain.lines(xp, yp, 3, LColor::eolmarker);
454 xp[0] = int(x + wid);
455 yp[0] = int(y - 0.500 * asc * 0.75);
458 yp[1] = int(y - 0.500 * asc * 0.75);
461 yp[2] = int(y - asc * 0.75);
463 pain.lines(xp, yp, 3, LColor::eolmarker);
469 LyXFont font = getFont(bview->buffer(), row->par(), pos);
470 LyXFont font2 = font;
472 if (c == Paragraph::META_INSET) {
473 Inset * tmpinset = row->par()->getInset(pos);
475 tmpinset->update(bview, font, false);
476 tmpinset->draw(bview, font, offset+row->baseline(), x,
478 if (!need_break_row && !inset_owner &&
479 bview->text->status() == CHANGED_IN_DRAW)
481 if (row->previous() && row->previous()->par() == row->par())
482 breakAgainOneRow(bview, row->previous());
483 setCursor(bview, cursor.par(), cursor.pos());
484 need_break_row = const_cast<Row *>(row);
489 if (lyxrc.mark_foreign_language &&
490 font.language() != latex_language &&
491 font.language() != bview->buffer()->params.language) {
492 int const y = offset + row->height() - 1;
493 pain.line(int(tmpx), y, int(x), y, LColor::language);
499 /* usual characters, no insets */
501 // Collect character that we can draw in one command
503 // This is dirty, but fast. Notice that it will never be too small.
504 // For the record, I'll note that Microsoft Word has a limit
505 // of 768 here. We have none :-) (Asger)
506 // Ok. I am the first to admit that the use of std::string will be
507 // a tiny bit slower than using a POD char array. However, I claim
508 // that this slowdown is so small that it is close to inperceptive.
509 // So IMHO we should go with the easier and clearer implementation.
510 // And even if 1024 is a large number here it might overflow, string
511 // will only overflow if the machine is out of memory...
512 static string textstring;
516 Paragraph::size_type const last = rowLastPrintable(row);
518 if (font.language()->lang() == "hebrew") {
519 if (Encodings::IsComposeChar_hebrew(c)) {
520 int const width = lyxfont::width(c, font2);
522 for (Paragraph::size_type i = pos-1; i >= 0; --i) {
523 c = row->par()->getChar(i);
524 if (!Encodings::IsComposeChar_hebrew(c)) {
525 if (IsPrintableNonspace(c)) {
530 dx = (c == 'ø' || c == 'ã') // dalet / resh
531 ? width2 - width : (width2 - width) / 2;
537 pain.text(int(x) + dx, offset + row->baseline(),
540 while (vpos <= last &&
541 (pos = vis2log(vpos)) >= 0
542 && IsPrintableNonspace(c = row->par()->getChar(pos))
543 && !Encodings::IsComposeChar_hebrew(c)
544 && font2 == getFont(bview->buffer(), row->par(), pos)) {
548 // Draw text and set the new x position
549 pain.text(int(x), offset + row->baseline(),
551 x += lyxfont::width(textstring, font);
553 } else if (font.language()->lang() == "arabic" &&
554 (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 ||
555 lyxrc.font_norm_type == LyXRC::ISO_10646_1)) {
556 if (Encodings::IsComposeChar_arabic(c)) {
557 c = transformChar(c, row->par(), pos);
559 int const width = lyxfont::width(c, font2);
561 for (Paragraph::size_type i = pos-1; i >= 0; --i) {
562 c = row->par()->getChar(i);
563 if (!Encodings::IsComposeChar_arabic(c)) {
564 if (IsPrintableNonspace(c)) {
569 dx = (width2 - width) / 2;
575 pain.text(int(x) + dx, offset + row->baseline(),
578 textstring = transformChar(c, row->par(), pos);
579 while (vpos <= last &&
580 (pos = vis2log(vpos)) >= 0
581 && IsPrintableNonspace(c = row->par()->getChar(pos))
582 && !Encodings::IsComposeChar_arabic(c)
583 && font2 == getFont(bview->buffer(), row->par(), pos)) {
584 c = transformChar(c, row->par(), pos);
588 // Draw text and set the new x position
589 pain.text(int(x), offset + row->baseline(),
591 x += lyxfont::width(textstring, font);
594 while (vpos <= last &&
595 (pos = vis2log(vpos)) >= 0
596 && IsPrintableNonspace(c = row->par()->getChar(pos))
597 && font2 == getFont(bview->buffer(), row->par(), pos)) {
601 // Draw text and set the new x position
602 pain.text(int(x), offset + row->baseline(), textstring, font);
603 x += lyxfont::width(textstring, font);
606 #ifdef INHERIT_LANGUAGE
608 if ((font.language() == inherit_language) ||
609 (font.language() == ignore_language))
610 lyxerr << "No this shouldn't happen!\n";
613 if (lyxrc.mark_foreign_language &&
614 font.language() != latex_language &&
615 font.language() != bview->buffer()->params.language) {
616 int const y = offset + row->height() - 1;
617 pain.line(int(tmpx), y, int(x), y,
621 // If we want ulem.sty support, drawing
622 // routines should go here. (Asger)
623 // Why shouldn't LyXFont::drawText handle it internally?
627 // Returns the left beginning of the text.
628 // This information cannot be taken from the layouts-objekt, because in
629 // LaTeX the beginning of the text fits in some cases (for example sections)
630 // exactly the label-width.
631 int LyXText::leftMargin(BufferView * bview, Row const * row) const
633 LyXTextClass const & tclass =
634 textclasslist.TextClass(bview->buffer()->params.textclass);
635 LyXLayout const & layout = tclass[row->par()->getLayout()];
637 string parindent = layout.parindent;
639 int x = LYX_PAPER_MARGIN;
641 x += lyxfont::signedWidth(tclass.leftmargin(), tclass.defaultfont());
643 // this is the way, LyX handles the LaTeX-Environments.
644 // I have had this idea very late, so it seems to be a
645 // later added hack and this is true
646 if (!row->par()->getDepth()) {
647 if (!row->par()->getLayout()) {
648 // find the previous same level paragraph
649 if (row->par()->previous()) {
650 Paragraph * newpar = row->par()
651 ->depthHook(row->par()->getDepth());
653 tclass[newpar->getLayout()].nextnoindent)
658 // find the next level paragraph
661 row->par()->outerHook();
663 // make a corresponding row. Needed to call LeftMargin()
665 // check wether it is a sufficent paragraph
667 && tclass[newpar->getLayout()].isEnvironment()) {
669 dummyrow.par(newpar);
670 dummyrow.pos(newpar->size());
671 x = leftMargin(bview, &dummyrow);
673 // this is no longer an error, because this function
674 // is used to clear impossible depths after changing
675 // a layout. Since there is always a redo,
676 // LeftMargin() is always called
677 row->par()->params().depth(0);
680 if (newpar && !row->par()->getLayout()) {
681 if (newpar->params().noindent())
684 parindent = tclass[newpar->getLayout()].parindent;
689 LyXFont const labelfont = getLabelFont(bview->buffer(), row->par());
690 switch (layout.margintype) {
692 if (!layout.leftmargin.empty()) {
693 x += lyxfont::signedWidth(layout.leftmargin,
694 tclass.defaultfont());
696 if (!row->par()->getLabelstring().empty()) {
697 x += lyxfont::signedWidth(layout.labelindent,
699 x += lyxfont::width(row->par()->getLabelstring(),
701 x += lyxfont::width(layout.labelsep, labelfont);
705 x += lyxfont::signedWidth(layout.labelindent, labelfont);
706 if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
707 if (!row->par()->getLabelWidthString().empty()) {
708 x += lyxfont::width(row->par()->getLabelWidthString(),
710 x += lyxfont::width(layout.labelsep, labelfont);
715 x += lyxfont::signedWidth(layout.leftmargin, tclass.defaultfont()) * 4
716 / (row->par()->getDepth() + 4);
718 case MARGIN_FIRST_DYNAMIC:
719 if (layout.labeltype == LABEL_MANUAL) {
720 if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) {
721 x += lyxfont::signedWidth(layout.leftmargin,
724 x += lyxfont::signedWidth(layout.labelindent,
727 } else if (row->pos()
728 // Special case to fix problems with
730 || (layout.labeltype == LABEL_STATIC
731 && layout.latextype == LATEX_ENVIRONMENT
732 && ! row->par()->isFirstInSequence())) {
733 x += lyxfont::signedWidth(layout.leftmargin,
735 } else if (layout.labeltype != LABEL_TOP_ENVIRONMENT
736 && layout.labeltype != LABEL_BIBLIO
737 && layout.labeltype !=
738 LABEL_CENTERED_TOP_ENVIRONMENT) {
739 x += lyxfont::signedWidth(layout.labelindent,
741 x += lyxfont::width(layout.labelsep, labelfont);
742 x += lyxfont::width(row->par()->getLabelstring(),
747 case MARGIN_RIGHT_ADDRESS_BOX:
749 // ok, a terrible hack. The left margin depends on the widest
750 // row in this paragraph. Do not care about footnotes, they
751 // are *NOT* allowed in the LaTeX realisation of this layout.
753 // find the first row of this paragraph
754 Row const * tmprow = row;
755 while (tmprow->previous()
756 && tmprow->previous()->par() == row->par())
757 tmprow = tmprow->previous();
759 int minfill = tmprow->fill();
760 while (tmprow->next() && tmprow->next()->par() == row->par()) {
761 tmprow = tmprow->next();
762 if (tmprow->fill() < minfill)
763 minfill = tmprow->fill();
766 x += lyxfont::signedWidth(layout.leftmargin,
767 tclass.defaultfont());
773 LyXAlignment align; // wrong type
775 if (row->par()->params().align() == LYX_ALIGN_LAYOUT)
776 align = layout.align;
778 align = row->par()->params().align();
780 // set the correct parindent
781 if (row->pos() == 0) {
782 if ((layout.labeltype == LABEL_NO_LABEL
783 || layout.labeltype == LABEL_TOP_ENVIRONMENT
784 || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
785 || (layout.labeltype == LABEL_STATIC
786 && layout.latextype == LATEX_ENVIRONMENT
787 && ! row->par()->isFirstInSequence()))
788 && align == LYX_ALIGN_BLOCK
789 && !row->par()->params().noindent()
790 && (row->par()->layout ||
791 bview->buffer()->params.paragraph_separation ==
792 BufferParams::PARSEP_INDENT))
793 x += lyxfont::signedWidth(parindent,
794 tclass.defaultfont());
795 else if (layout.labeltype == LABEL_BIBLIO) {
796 // ale970405 Right width for bibitems
797 x += bibitemMaxWidth(bview, tclass.defaultfont());
804 int LyXText::rightMargin(Buffer const * buf, Row const * row) const
806 LyXTextClass const & tclass =
807 textclasslist.TextClass(buf->params.textclass);
808 LyXLayout const & layout = tclass[row->par()->getLayout()];
810 int x = LYX_PAPER_MARGIN
811 + lyxfont::signedWidth(tclass.rightmargin(),
812 tclass.defaultfont());
814 // this is the way, LyX handles the LaTeX-Environments.
815 // I have had this idea very late, so it seems to be a
816 // later added hack and this is true
817 if (row->par()->getDepth()) {
818 // find the next level paragraph
820 Paragraph * newpar = row->par();
823 newpar = newpar->previous();
825 && newpar->getDepth() >= row->par()->getDepth());
827 // make a corresponding row. Needed to call LeftMargin()
829 // check wether it is a sufficent paragraph
831 && tclass[newpar->getLayout()].isEnvironment()) {
833 dummyrow.par(newpar);
835 x = rightMargin(buf, &dummyrow);
837 // this is no longer an error, because this function
838 // is used to clear impossible depths after changing
839 // a layout. Since there is always a redo,
840 // LeftMargin() is always called
841 row->par()->params().depth(0);
845 //lyxerr << "rightmargin: " << layout->rightmargin << endl;
846 x += lyxfont::signedWidth(layout.rightmargin, tclass.defaultfont())
847 * 4 / (row->par()->getDepth() + 4);
852 int LyXText::labelEnd(BufferView * bview, Row const * row) const
854 if (textclasslist.Style(bview->buffer()->params.textclass,
855 row->par()->getLayout()).margintype
859 tmprow.pos(row->par()->size());
860 return leftMargin(bview, &tmprow); /* just the beginning
863 return 0; /* LabelEnd is only needed, if the
864 layout fills a flushleft
869 // get the next breakpoint in a given paragraph
871 LyXText::nextBreakPoint(BufferView * bview, Row const * row, int width) const
873 Paragraph * par = row->par();
878 Paragraph::size_type const pos = row->pos();
881 // position of the last possible breakpoint
882 // -1 isn't a suitable value, but a flag
883 Paragraph::size_type last_separator = -1;
884 width -= rightMargin(bview->buffer(), row);
886 Paragraph::size_type const main_body =
887 beginningOfMainBody(bview->buffer(), par);
888 LyXLayout const & layout =
889 textclasslist.Style(bview->buffer()->params.textclass,
891 Paragraph::size_type i = pos;
893 if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
894 /* special code for right address boxes, only newlines count */
895 while (i < par->size()) {
896 if (par->isNewline(i)) {
898 i = par->size() - 1; // this means break
900 } else if (par->getChar(i) == Paragraph::META_INSET &&
901 par->getInset(i) && par->getInset(i)->display()){
902 par->getInset(i)->display(false);
907 // Last position is an invariant
908 Paragraph::size_type const last =
910 // this is the usual handling
911 int x = leftMargin(bview, row);
912 bool doitonetime = true;
913 while (doitonetime || ((x < width) && (i < last))) {
915 char const c = par->getChar(i);
916 if (IsNewlineChar(c)) {
918 x = width; // this means break
919 } else if (c == Paragraph::META_INSET &&
922 // check wether a Display() inset is
923 // valid here. if not, change it to
925 if (par->getInset(i)->display() &&
926 (layout.isCommand() ||
927 (layout.labeltype == LABEL_MANUAL
928 && i < beginningOfMainBody(bview->buffer(), par)))) {
929 // display istn't allowd
930 par->getInset(i)->display(false);
931 x += singleWidth(bview, par, i, c);
932 } else if (par->getInset(i)->display() ||
933 par->getInset(i)->needFullRow()) {
934 // So break the line here
938 if (IsLineSeparatorChar(par->getChar(i+1)))
941 last_separator = last; // to avoid extra rows
943 last_separator = i - 1;
944 x = width; // this means break
946 x += singleWidth(bview, par, i, c);
949 if (IsLineSeparatorChar(c))
951 x += singleWidth(bview, par, i, c);
954 if (i == main_body) {
955 x += lyxfont::width(layout.labelsep,
956 getLabelFont(bview->buffer(), par));
957 if (par->isLineSeparator(i - 1))
958 x-= singleWidth(bview, par, i - 1);
959 int left_margin = labelEnd(bview, row);
964 // end of paragraph is always a suitable separator
965 if (i == last && x < width)
969 // well, if last_separator is still 0, the line isn't breakable.
970 // don't care and cut simply at the end
971 if (last_separator < 0) {
975 // manual labels cannot be broken in LaTeX, do not care
976 if (main_body && last_separator < main_body)
977 last_separator = main_body - 1;
979 return last_separator;
983 // returns the minimum space a row needs on the screen in pixel
984 int LyXText::fill(BufferView * bview, Row * row, int paper_width) const
990 // get the pure distance
991 Paragraph::size_type const last = rowLastPrintable(row);
993 // special handling of the right address boxes
994 if (textclasslist.Style(bview->buffer()->params.textclass,
995 row->par()->getLayout()).margintype
996 == MARGIN_RIGHT_ADDRESS_BOX) {
997 int const tmpfill = row->fill();
998 row->fill(0); // the minfill in MarginLeft()
999 w = leftMargin(bview, row);
1002 w = leftMargin(bview, row);
1004 LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
1005 row->par()->getLayout());
1006 Paragraph::size_type const main_body =
1007 beginningOfMainBody(bview->buffer(), row->par());
1008 Paragraph::size_type i = row->pos();
1011 if (main_body > 0 && i == main_body) {
1012 w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1013 if (row->par()->isLineSeparator(i - 1))
1014 w -= singleWidth(bview, row->par(), i - 1);
1015 int left_margin = labelEnd(bview, row);
1016 if (w < left_margin)
1019 w += singleWidth(bview, row->par(), i);
1022 if (main_body > 0 && main_body > last) {
1023 w += lyxfont::width(layout.labelsep, getLabelFont(bview->buffer(), row->par()));
1024 if (last >= 0 && row->par()->isLineSeparator(last))
1025 w -= singleWidth(bview, row->par(), last);
1026 int const left_margin = labelEnd(bview, row);
1027 if (w < left_margin)
1031 int const fill = paper_width - w - rightMargin(bview->buffer(), row);
1032 #ifdef WITH_WARNINGS
1033 #warning Please fix me (Jug!)
1043 // returns the minimum space a manual label needs on the screen in pixel
1044 int LyXText::labelFill(BufferView * bview, Row const * row) const
1046 Paragraph::size_type last = beginningOfMainBody(bview->buffer(), row->par()) - 1;
1047 // -1 because a label ends either with a space that is in the label,
1048 // or with the beginning of a footnote that is outside the label.
1050 // I don't understand this code in depth, but sometimes "last" is
1051 // less than 0 and this causes a crash. This fix seems to work
1052 // correctly, but I bet the real error is elsewhere. The bug is
1053 // triggered when you have an open footnote in a paragraph
1054 // environment with a manual label. (Asger)
1055 if (last < 0) last = 0;
1057 if (row->par()->isLineSeparator(last)) /* a sepearator at this end
1062 Paragraph::size_type i = row->pos();
1064 w += singleWidth(bview, row->par(), i);
1069 if (!row->par()->params().labelWidthString().empty()) {
1070 fill = max(lyxfont::width(row->par()->params().labelWidthString(),
1071 getLabelFont(bview->buffer(), row->par())) - w,
1079 // returns the number of separators in the specified row. The separator
1080 // on the very last column doesnt count
1081 int LyXText::numberOfSeparators(Buffer const * buf, Row const * row) const
1083 Paragraph::size_type const last = rowLast(row);
1084 Paragraph::size_type p =
1085 max(row->pos(), beginningOfMainBody(buf, row->par()));
1087 for (; p < last; ++p) {
1088 if (row->par()->isSeparator(p)) {
1096 // returns the number of hfills in the specified row. The LyX-Hfill is
1097 // a LaTeX \hfill so that the hfills at the beginning and at the end were
1098 // ignored. This is *MUCH* more usefull than not to ignore!
1099 int LyXText::numberOfHfills(Buffer const * buf, Row const * row) const
1101 Paragraph::size_type const last = rowLast(row);
1102 Paragraph::size_type first = row->pos();
1103 if (first) { /* hfill *DO* count at the beginning
1105 while(first <= last && row->par()->isHfill(first))
1109 first = max(first, beginningOfMainBody(buf, row->par()));
1111 for (Paragraph::size_type p = first; p <= last; ++p) {
1112 // last, because the end is ignored!
1113 if (row->par()->isHfill(p)) {
1121 // like NumberOfHfills, but only those in the manual label!
1122 int LyXText::numberOfLabelHfills(Buffer const * buf, Row const * row) const
1124 Paragraph::size_type last = rowLast(row);
1125 Paragraph::size_type first = row->pos();
1126 if (first) { /* hfill *DO* count at the beginning
1128 while(first < last && row->par()->isHfill(first))
1132 last = min(last, beginningOfMainBody(buf, row->par()));
1134 for (Paragraph::size_type p = first;
1135 p < last; ++p) { // last, because the end is ignored!
1136 if (row->par()->isHfill(p)) {
1144 // returns true, if a expansion is needed.
1145 // Rules are given by LaTeX
1146 bool LyXText::hfillExpansion(Buffer const * buf, Row const * row_ptr,
1147 Paragraph::size_type pos) const
1149 // by the way, is it a hfill?
1150 if (!row_ptr->par()->isHfill(pos))
1153 // at the end of a row it does not count
1154 if (pos >= rowLast(row_ptr))
1157 // at the beginning of a row it does not count, if it is not
1158 // the first row of a paragaph
1159 if (!row_ptr->pos())
1162 // in some labels it does not count
1163 if (textclasslist.Style(buf->params.textclass,
1164 row_ptr->par()->getLayout()).margintype
1166 && pos < beginningOfMainBody(buf, row_ptr->par()))
1169 // if there is anything between the first char of the row and
1170 // the sepcified position that is not a newline and not a hfill,
1171 // the hfill will count, otherwise not
1172 Paragraph::size_type i = row_ptr->pos();
1173 while (i < pos && (row_ptr->par()->isNewline(i)
1174 || row_ptr->par()->isHfill(i)))
1181 LColor::color LyXText::backgroundColor()
1184 return inset_owner->backgroundColor();
1186 return LColor::background;
1189 void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const
1191 /* get the maximum ascent and the maximum descent */
1194 float layoutasc = 0;
1195 float layoutdesc = 0;
1198 Inset * tmpinset = 0;
1200 /* this must not happen before the currentrow for clear reasons.
1201 so the trick is just to set the current row onto this row */
1203 getRow(row_ptr->par(), row_ptr->pos(), unused_y);
1205 /* ok , let us initialize the maxasc and maxdesc value.
1206 * This depends in LaTeX of the font of the last character
1207 * in the paragraph. The hack below is necessary because
1208 * of the possibility of open footnotes */
1210 /* Correction: only the fontsize count. The other properties
1211 are taken from the layoutfont. Nicer on the screen :) */
1212 Paragraph * par = row_ptr->par();
1213 Paragraph * firstpar = row_ptr->par();
1215 LyXLayout const & layout = textclasslist.Style(bview->buffer()->params.textclass,
1216 firstpar->getLayout());
1218 // as max get the first character of this row then it can increes but not
1219 // decrees the height. Just some point to start with so we don't have to
1220 // do the assignment below too often.
1221 LyXFont font = getFont(bview->buffer(), par, row_ptr->pos());
1222 LyXFont::FONT_SIZE const tmpsize = font.size();
1223 font = getLayoutFont(bview->buffer(), par);
1224 LyXFont::FONT_SIZE const size = font.size();
1225 font.setSize(tmpsize);
1227 LyXFont labelfont = getLabelFont(bview->buffer(), par);
1229 float spacing_val = 1.0;
1230 if (!row_ptr->par()->params().spacing().isDefault()) {
1231 spacing_val = row_ptr->par()->params().spacing().getValue();
1233 spacing_val = bview->buffer()->params.spacing.getValue();
1235 //lyxerr << "spacing_val = " << spacing_val << endl;
1237 int maxasc = int(lyxfont::maxAscent(font) *
1238 layout.spacing.getValue() *
1240 int maxdesc = int(lyxfont::maxDescent(font) *
1241 layout.spacing.getValue() *
1243 Paragraph::size_type const pos_end = rowLast(row_ptr);
1247 // Check if any insets are larger
1248 for (Paragraph::size_type pos = row_ptr->pos(); pos <= pos_end; ++pos) {
1249 if (row_ptr->par()->getChar(pos) == Paragraph::META_INSET) {
1250 tmpfont = getFont(bview->buffer(), row_ptr->par(), pos);
1251 tmpinset = row_ptr->par()->getInset(pos);
1253 #if 1 // this is needed for deep update on initialitation
1254 tmpinset->update(bview, tmpfont);
1256 asc = tmpinset->ascent(bview, tmpfont);
1257 desc = tmpinset->descent(bview, tmpfont);
1258 maxwidth += tmpinset->width(bview, tmpfont);
1259 maxasc = max(maxasc, asc);
1260 maxdesc = max(maxdesc, desc);
1263 maxwidth += singleWidth(bview, row_ptr->par(), pos);
1267 // Check if any custom fonts are larger (Asger)
1268 // This is not completely correct, but we can live with the small,
1269 // cosmetic error for now.
1270 LyXFont::FONT_SIZE maxsize =
1271 row_ptr->par()->highestFontInRange(row_ptr->pos(), pos_end, size);
1272 if (maxsize > font.size()) {
1273 font.setSize(maxsize);
1275 asc = lyxfont::maxAscent(font);
1276 desc = lyxfont::maxDescent(font);
1283 // This is nicer with box insets:
1287 row_ptr->ascent_of_text(maxasc);
1289 // is it a top line?
1290 if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
1292 // some parksips VERY EASY IMPLEMENTATION
1293 if (bview->buffer()->params.paragraph_separation ==
1294 BufferParams::PARSEP_SKIP)
1296 if (layout.isParagraph()
1297 && firstpar->getDepth() == 0
1298 && firstpar->previous())
1300 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1301 } else if (firstpar->previous() &&
1302 textclasslist.Style(bview->buffer()->params.textclass,
1303 firstpar->previous()->
1304 getLayout()).isParagraph() &&
1305 firstpar->previous()->getDepth() == 0)
1307 // is it right to use defskip here too? (AS)
1308 maxasc += bview->buffer()->params.getDefSkip().inPixels(bview);
1312 // the paper margins
1313 if (!row_ptr->par()->previous() && bv_owner)
1314 maxasc += LYX_PAPER_MARGIN;
1316 // add the vertical spaces, that the user added
1317 if (firstpar->params().spaceTop().kind() != VSpace::NONE)
1318 maxasc += int(firstpar->params().spaceTop().inPixels(bview));
1320 // do not forget the DTP-lines!
1321 // there height depends on the font of the nearest character
1322 if (firstpar->params().lineTop())
1323 maxasc += 2 * lyxfont::ascent('x', getFont(bview->buffer(),
1326 // and now the pagebreaks
1327 if (firstpar->params().pagebreakTop())
1328 maxasc += 3 * defaultHeight();
1330 // This is special code for the chapter, since the label of this
1331 // layout is printed in an extra row
1332 if (layout.labeltype == LABEL_COUNTER_CHAPTER
1333 && bview->buffer()->params.secnumdepth >= 0)
1335 float spacing_val = 1.0;
1336 if (!row_ptr->par()->params().spacing().isDefault()) {
1337 spacing_val = row_ptr->par()->params().spacing().getValue();
1339 spacing_val = bview->buffer()->params.spacing.getValue();
1342 labeladdon = int(lyxfont::maxDescent(labelfont) *
1343 layout.spacing.getValue() *
1345 + int(lyxfont::maxAscent(labelfont) *
1346 layout.spacing.getValue() *
1350 // special code for the top label
1351 if ((layout.labeltype == LABEL_TOP_ENVIRONMENT
1352 || layout.labeltype == LABEL_BIBLIO
1353 || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1354 && row_ptr->par()->isFirstInSequence()
1355 && !row_ptr->par()->getLabelstring().empty())
1357 float spacing_val = 1.0;
1358 if (!row_ptr->par()->params().spacing().isDefault()) {
1359 spacing_val = row_ptr->par()->params().spacing().getValue();
1361 spacing_val = bview->buffer()->params.spacing.getValue();
1365 (lyxfont::maxAscent(labelfont) *
1366 layout.spacing.getValue() *
1368 +(lyxfont::maxDescent(labelfont) *
1369 layout.spacing.getValue() *
1371 + layout.topsep * defaultHeight()
1372 + layout.labelbottomsep * defaultHeight());
1375 // and now the layout spaces, for example before and after a section,
1376 // or between the items of a itemize or enumerate environment
1378 if (!firstpar->params().pagebreakTop()) {
1379 Paragraph * prev = row_ptr->par()->previous();
1381 prev = row_ptr->par()->depthHook(row_ptr->par()->getDepth());
1382 if (prev && prev->getLayout() == firstpar->getLayout() &&
1383 prev->getDepth() == firstpar->getDepth() &&
1384 prev->getLabelWidthString() == firstpar->getLabelWidthString())
1386 layoutasc = (layout.itemsep * defaultHeight());
1387 } else if (row_ptr->previous()) {
1388 tmptop = layout.topsep;
1390 if (row_ptr->previous()->par()->getDepth() >= row_ptr->par()->getDepth())
1391 tmptop -= textclasslist.Style(bview->buffer()->params.textclass,
1392 row_ptr->previous()->par()->
1393 getLayout()).bottomsep;
1396 layoutasc = (tmptop * defaultHeight());
1397 } else if (row_ptr->par()->params().lineTop()) {
1398 tmptop = layout.topsep;
1401 layoutasc = (tmptop * defaultHeight());
1404 prev = row_ptr->par()->outerHook();
1406 maxasc += int(textclasslist.Style(bview->buffer()->params.textclass,
1407 prev->getLayout()).parsep * defaultHeight());
1409 if (firstpar->previous() &&
1410 firstpar->previous()->getDepth() == 0 &&
1411 firstpar->previous()->getLayout() !=
1412 firstpar->getLayout())
1415 } else if (firstpar->previous()) {
1416 maxasc += int(layout.parsep * defaultHeight());
1422 // is it a bottom line?
1423 if (row_ptr->par() == par
1424 && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par()))
1426 // the paper margins
1427 if (!par->next() && bv_owner)
1428 maxdesc += LYX_PAPER_MARGIN;
1430 // add the vertical spaces, that the user added
1431 if (firstpar->params().spaceBottom().kind() != VSpace::NONE)
1432 maxdesc += int(firstpar->params().spaceBottom().inPixels(bview));
1434 // do not forget the DTP-lines!
1435 // there height depends on the font of the nearest character
1436 if (firstpar->params().lineBottom())
1437 maxdesc += 2 * lyxfont::ascent('x',
1438 getFont(bview->buffer(),
1440 max(Paragraph::size_type(0), par->size() - 1)));
1442 // and now the pagebreaks
1443 if (firstpar->params().pagebreakBottom())
1444 maxdesc += 3 * defaultHeight();
1446 // and now the layout spaces, for example before and after
1447 // a section, or between the items of a itemize or enumerate
1449 if (!firstpar->params().pagebreakBottom() && row_ptr->par()->next()) {
1450 Paragraph * nextpar = row_ptr->par()->next();
1451 Paragraph * comparepar = row_ptr->par();
1455 if (comparepar->getDepth() > nextpar->getDepth()) {
1456 usual = (textclasslist.Style(bview->buffer()->params.textclass,
1457 comparepar->getLayout()).bottomsep * defaultHeight());
1458 comparepar = comparepar->depthHook(nextpar->getDepth());
1459 if (comparepar->getLayout()!= nextpar->getLayout()
1460 || nextpar->getLabelWidthString() !=
1461 comparepar->getLabelWidthString())
1463 unusual = (textclasslist.Style(bview->buffer()->params.textclass,
1464 comparepar->getLayout()).bottomsep * defaultHeight());
1466 if (unusual > usual)
1467 layoutdesc = unusual;
1470 } else if (comparepar->getDepth() == nextpar->getDepth()) {
1472 if (comparepar->getLayout()!= nextpar->getLayout()
1473 || nextpar->getLabelWidthString() !=
1474 comparepar->getLabelWidthString())
1475 layoutdesc = int(textclasslist.Style(bview->buffer()->params.textclass,
1476 comparepar->getLayout()).bottomsep * defaultHeight());
1481 // incalculate the layout spaces
1482 maxasc += int(layoutasc * 2 / (2 + firstpar->getDepth()));
1483 maxdesc += int(layoutdesc * 2 / (2 + firstpar->getDepth()));
1485 // calculate the new height of the text
1486 height -= row_ptr->height();
1488 row_ptr->height(maxasc + maxdesc + labeladdon);
1489 row_ptr->baseline(maxasc + labeladdon);
1491 height += row_ptr->height();
1494 prepareToPrint(bview, row_ptr, x, dummy, dummy, dummy, false);
1495 row_ptr->width(int(maxwidth + x));
1498 width = max(0,workWidth(bview));
1500 if (r->width() > width)
1508 /* Appends the implicit specified paragraph behind the specified row,
1509 * start at the implicit given position */
1510 void LyXText::appendParagraph(BufferView * bview, Row * row) const
1512 bool not_ready = true;
1514 // The last character position of a paragraph is an invariant so we can
1515 // safely get it here. (Asger)
1516 Paragraph::size_type const lastposition = row->par()->size();
1518 // Get the next breakpoint
1519 Paragraph::size_type z = nextBreakPoint(bview, row, workWidth(bview));
1523 // Insert the new row
1524 if (z < lastposition) {
1526 insertRow(row, row->par(), z);
1533 // Set the dimensions of the row
1534 #ifdef WITH_WARNINGS
1535 #warning Something is rotten here! (Jug)
1537 tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1538 setHeightOfRow(bview, tmprow);
1540 } while (not_ready);
1544 void LyXText::breakAgain(BufferView * bview, Row * row) const
1546 bool not_ready = true;
1549 // get the next breakpoint
1550 Paragraph::size_type z = nextBreakPoint(bview, row, workWidth(bview));
1553 if (z < row->par()->size()) {
1554 if (!row->next() || (row->next() && row->next()->par() != row->par())) {
1557 insertRow(row, row->par(), z);
1563 if (row->pos() == z)
1564 not_ready = false; // the rest will not change
1570 /* if there are some rows too much, delete them */
1571 /* only if you broke the whole paragraph! */
1572 Row * tmprow2 = row;
1573 while (tmprow2->next() && tmprow2->next()->par() == row->par()) {
1574 tmprow2 = tmprow2->next();
1576 while (tmprow2 != row) {
1577 tmprow2 = tmprow2->previous();
1578 removeRow(tmprow2->next());
1583 /* set the dimensions of the row */
1584 tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1585 setHeightOfRow(bview, tmprow);
1586 } while (not_ready);
1590 // this is just a little changed version of break again
1591 void LyXText::breakAgainOneRow(BufferView * bview, Row * row)
1593 // get the next breakpoint
1594 Paragraph::size_type z = nextBreakPoint(bview, row, workWidth(bview));
1597 if (z < row->par()->size()) {
1599 || (row->next() && row->next()->par() != row->par())) {
1600 /* insert a new row */
1602 insertRow(row, row->par(), z);
1608 if (row->pos() != z)
1612 // if there are some rows too much, delete them
1613 // only if you broke the whole paragraph!
1614 Row * tmprow2 = row;
1615 while (tmprow2->next()
1616 && tmprow2->next()->par() == row->par()) {
1617 tmprow2 = tmprow2->next();
1619 while (tmprow2 != row) {
1620 tmprow2 = tmprow2->previous();
1621 removeRow(tmprow2->next());
1625 // set the dimensions of the row
1626 tmprow->fill(fill(bview, tmprow, workWidth(bview)));
1627 setHeightOfRow(bview, tmprow);
1631 void LyXText::breakParagraph(BufferView * bview, char keep_layout)
1633 LyXLayout const & layout =
1634 textclasslist.Style(bview->buffer()->params.textclass,
1635 cursor.par()->getLayout());
1637 // this is only allowed, if the current paragraph is not empty or caption
1638 if ((cursor.par()->size() <= 0)
1639 && layout.labeltype!= LABEL_SENSITIVE)
1642 setUndo(bview, Undo::INSERT,cursor.par(),cursor.par()->next());
1644 // Always break behind a space
1646 // It is better to erase the space (Dekel)
1647 if (cursor.pos() < cursor.par()->size()
1648 && cursor.par()->isLineSeparator(cursor.pos()))
1649 cursor.par()->erase(cursor.pos());
1650 // cursor.pos(cursor.pos() + 1);
1652 // break the paragraph
1656 keep_layout = layout.isEnvironment();
1657 cursor.par()->breakParagraph(bview->buffer()->params, cursor.pos(),
1660 // well this is the caption hack since one caption is really enough
1661 if (layout.labeltype == LABEL_SENSITIVE) {
1663 // set to standard-layout
1664 cursor.par()->setLayout(0);
1666 // set to standard-layout
1667 cursor.par()->next()->setLayout(0);
1670 /* if the cursor is at the beginning of a row without prior newline,
1672 * This touches only the screen-update. Otherwise we would may have
1673 * an empty row on the screen */
1674 if (cursor.pos() && !cursor.row()->par()->isNewline(cursor.row()->pos() - 1)
1675 && cursor.row()->pos() == cursor.pos()) {
1679 status(bview, LyXText::NEED_MORE_REFRESH);
1680 refresh_row = cursor.row();
1681 refresh_y = cursor.y() - cursor.row()->baseline();
1683 // Do not forget the special right address boxes
1684 if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1685 while (refresh_row->previous() &&
1686 refresh_row->previous()->par() == refresh_row->par()) {
1687 refresh_row = refresh_row->previous();
1688 refresh_y -= refresh_row->height();
1691 removeParagraph(cursor.row());
1693 // set the dimensions of the cursor row
1694 cursor.row()->fill(fill(bview, cursor.row(), workWidth(bview)));
1696 setHeightOfRow(bview, cursor.row());
1698 while (cursor.par()->next()->size()
1699 && cursor.par()->next()->isNewline(0))
1700 cursor.par()->next()->erase(0);
1702 insertParagraph(bview, cursor.par()->next(), cursor.row());
1704 updateCounters(bview, cursor.row()->previous());
1706 /* This check is necessary. Otherwise the new empty paragraph will
1707 * be deleted automatically. And it is more friendly for the user! */
1709 setCursor(bview, cursor.par()->next(), 0);
1711 setCursor(bview, cursor.par(), 0);
1713 if (cursor.row()->next())
1714 breakAgain(bview, cursor.row()->next());
1720 // Just a macro to make some thing easier.
1721 void LyXText::redoParagraph(BufferView * bview) const
1724 redoParagraphs(bview, cursor, cursor.par()->next());
1725 setCursorIntern(bview, cursor.par(), cursor.pos());
1729 /* insert a character, moves all the following breaks in the
1730 * same Paragraph one to the right and make a rebreak */
1731 void LyXText::insertChar(BufferView * bview, char c)
1733 setUndo(bview, Undo::INSERT,
1734 cursor.par(), cursor.par()->next());
1736 // When the free-spacing option is set for the current layout,
1737 // disable the double-space checking
1739 bool const freeSpacing =
1740 textclasslist.Style(bview->buffer()->params.textclass,
1741 cursor.row()->par()->getLayout()).free_spacing;
1744 if (lyxrc.auto_number) {
1745 static string const number_operators = "+-/*";
1746 static string const number_unary_operators = "+-";
1747 static string const number_seperators = ".,:";
1749 if (current_font.number() == LyXFont::ON) {
1750 if (!isdigit(c) && !contains(number_operators, c) &&
1751 !(contains(number_seperators, c) &&
1752 cursor.pos() >= 1 &&
1753 cursor.pos() < cursor.par()->size() &&
1754 getFont(bview->buffer(),
1756 cursor.pos()).number() == LyXFont::ON &&
1757 getFont(bview->buffer(),
1759 cursor.pos() - 1).number() == LyXFont::ON)
1761 number(bview); // Set current_font.number to OFF
1762 } else if (isdigit(c) &&
1763 real_current_font.isVisibleRightToLeft()) {
1764 number(bview); // Set current_font.number to ON
1766 if (cursor.pos() > 0) {
1767 char const c = cursor.par()->getChar(cursor.pos() - 1);
1768 if (contains(number_unary_operators, c) &&
1769 (cursor.pos() == 1 ||
1770 cursor.par()->isSeparator(cursor.pos() - 2) ||
1771 cursor.par()->isNewline(cursor.pos() - 2) )
1773 setCharFont(bview->buffer(),
1777 } else if (contains(number_seperators, c) &&
1778 cursor.pos() >= 2 &&
1779 getFont(bview->buffer(),
1781 cursor.pos() - 2).number() == LyXFont::ON) {
1782 setCharFont(bview->buffer(),
1792 /* First check, if there will be two blanks together or a blank at
1793 the beginning of a paragraph.
1794 I decided to handle blanks like normal characters, the main
1795 difference are the special checks when calculating the row.fill
1796 (blank does not count at the end of a row) and the check here */
1798 // The bug is triggered when we type in a description environment:
1799 // The current_font is not changed when we go from label to main text
1800 // and it should (along with realtmpfont) when we type the space.
1801 // CHECK There is a bug here! (Asger)
1803 LyXFont realtmpfont = real_current_font;
1804 LyXFont rawtmpfont = current_font; /* store the current font.
1805 * This is because of the use
1806 * of cursor movements. The moving
1807 * cursor would refresh the
1810 // Get the font that is used to calculate the baselineskip
1811 Paragraph::size_type const lastpos = cursor.par()->size();
1812 LyXFont rawparfont =
1813 cursor.par()->getFontSettings(bview->buffer()->params,
1816 bool jumped_over_space = false;
1818 if (!freeSpacing && IsLineSeparatorChar(c)) {
1819 if ((cursor.pos() > 0
1820 && cursor.par()->isLineSeparator(cursor.pos() - 1))
1821 || (cursor.pos() > 0
1822 && cursor.par()->isNewline(cursor.pos() - 1))
1823 || (cursor.pos() == 0)) {
1824 static bool sent_space_message = false;
1825 if (!sent_space_message) {
1826 if (cursor.pos() == 0)
1827 bview->owner()->message(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial."));
1829 bview->owner()->message(_("You cannot type two spaces this way. Please read the Tutorial."));
1830 sent_space_message = true;
1835 } else if (IsNewlineChar(c)) {
1836 if (cursor.par() == cursor.par()
1837 && cursor.pos() <= beginningOfMainBody(bview->buffer(), cursor.par())) {
1841 /* No newline at first position
1842 * of a paragraph or behind labels.
1843 * TeX does not allow that. */
1845 if (cursor.pos() < cursor.par()->size() &&
1846 cursor.par()->isLineSeparator(cursor.pos()))
1847 // newline always after a blank!
1849 cursor.row()->fill(-1); // to force a new break
1852 // the display inset stuff
1853 if (cursor.row()->par()->getChar(cursor.row()->pos()) == Paragraph::META_INSET
1854 && cursor.row()->par()->getInset(cursor.row()->pos())
1855 && (cursor.row()->par()->getInset(cursor.row()->pos())->display() ||
1856 cursor.row()->par()->getInset(cursor.row()->pos())->needFullRow()))
1857 cursor.row()->fill(-1); // to force a new break
1859 // get the cursor row fist
1860 Row * row = cursor.row();
1861 int y = cursor.y() - row->baseline();
1862 if (c != Paragraph::META_INSET) /* Here case LyXText::InsertInset
1863 * already insertet the character */
1864 cursor.par()->insertChar(cursor.pos(), c);
1865 setCharFont(bview->buffer(), cursor.par(), cursor.pos(), rawtmpfont);
1867 if (!jumped_over_space) {
1868 // refresh the positions
1870 while (tmprow->next() && tmprow->next()->par() == row->par()) {
1871 tmprow = tmprow->next();
1872 tmprow->pos(tmprow->pos() + 1);
1876 // Is there a break one row above
1877 if ((cursor.par()->isLineSeparator(cursor.pos())
1878 || cursor.par()->isNewline(cursor.pos())
1879 || cursor.row()->fill() == -1)
1880 && row->previous() && row->previous()->par() == row->par()) {
1881 Paragraph::size_type z = nextBreakPoint(bview,
1884 if (z >= row->pos()) {
1887 // set the dimensions of the row above
1888 row->previous()->fill(fill(bview,
1892 setHeightOfRow(bview, row->previous());
1894 y -= row->previous()->height();
1896 refresh_row = row->previous();
1897 status(bview, LyXText::NEED_MORE_REFRESH);
1899 breakAgainOneRow(bview, row);
1901 current_font = rawtmpfont;
1902 real_current_font = realtmpfont;
1903 setCursor(bview, cursor.par(), cursor.pos() + 1,
1904 false, cursor.boundary());
1905 // cursor MUST be in row now.
1907 if (row->next() && row->next()->par() == row->par())
1908 need_break_row = row->next();
1912 // check, wether the last characters font has changed.
1913 if (cursor.pos() && cursor.pos() == cursor.par()->size()
1914 && rawparfont != rawtmpfont)
1915 redoHeightOfParagraph(bview, cursor);
1922 // recalculate the fill of the row
1923 if (row->fill() >= 0) /* needed because a newline
1924 * will set fill to -1. Otherwise
1925 * we would not get a rebreak! */
1926 row->fill(fill(bview, row, workWidth(bview)));
1927 if (row->fill() < 0) {
1930 refresh_x = cursor.x();
1931 refresh_pos = cursor.pos();
1932 status(bview, LyXText::NEED_MORE_REFRESH);
1933 breakAgainOneRow(bview, row);
1934 // will the cursor be in another row now?
1935 if (rowLast(row) <= cursor.pos() + 1 && row->next()) {
1936 if (row->next() && row->next()->par() == row->par())
1937 // this should always be true
1939 breakAgainOneRow(bview, row);
1941 current_font = rawtmpfont;
1942 real_current_font = realtmpfont;
1944 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1946 if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
1947 != cursor.boundary())
1948 setCursor(bview, cursor.par(), cursor.pos(), false,
1949 !cursor.boundary());
1950 if (row->next() && row->next()->par() == row->par())
1951 need_break_row = row->next();
1956 refresh_x = cursor.x();
1958 refresh_pos = cursor.pos();
1960 int const tmpheight = row->height();
1961 setHeightOfRow(bview, row);
1962 if (tmpheight == row->height())
1963 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
1965 status(bview, LyXText::NEED_MORE_REFRESH);
1967 current_font = rawtmpfont;
1968 real_current_font = realtmpfont;
1969 setCursor(bview, cursor.par(), cursor.pos() + 1, false,
1973 // check, wether the last characters font has changed.
1974 if (cursor.pos() && cursor.pos() == cursor.par()->size()
1975 && rawparfont != rawtmpfont) {
1976 redoHeightOfParagraph(bview, cursor);
1978 // now the special right address boxes
1979 if (textclasslist.Style(bview->buffer()->params.textclass,
1980 cursor.par()->getLayout()).margintype
1981 == MARGIN_RIGHT_ADDRESS_BOX) {
1982 redoDrawingOfParagraph(bview, cursor);
1990 void LyXText::charInserted()
1992 // Here we could call FinishUndo for every 20 characters inserted.
1993 // This is from my experience how emacs does it.
1994 static unsigned int counter;
2004 void LyXText::prepareToPrint(BufferView * bview,
2005 Row * row, float & x,
2006 float & fill_separator,
2008 float & fill_label_hfill,
2014 float w = row->fill();
2016 fill_label_hfill = 0;
2018 fill_label_hfill = 0;
2021 row->par()->isRightToLeftPar(bview->buffer()->params);
2023 x = (workWidth(bview) > 0)
2024 ? rightMargin(bview->buffer(), row) : 0;
2026 x = (workWidth(bview) > 0) ? leftMargin(bview, row) : 0;
2028 // is there a manual margin with a manual label
2029 if (textclasslist.Style(bview->buffer()->params.textclass,
2030 row->par()->getLayout()).margintype == MARGIN_MANUAL
2031 && textclasslist.Style(bview->buffer()->params.textclass,
2032 row->par()->getLayout()).labeltype == LABEL_MANUAL) {
2034 /* one more since labels are left aligned */
2035 nlh = numberOfLabelHfills(bview->buffer(), row) + 1;
2036 if (nlh && !row->par()->getLabelWidthString().empty()) {
2037 fill_label_hfill = labelFill(bview, row) / nlh;
2041 // are there any hfills in the row?
2042 float const nh = numberOfHfills(bview->buffer(), row);
2046 fill_hfill = w / nh;
2048 // is it block, flushleft or flushright?
2049 // set x how you need it
2051 if (row->par()->params().align() == LYX_ALIGN_LAYOUT) {
2052 align = textclasslist.Style(bview->buffer()->params.textclass, row->par()->getLayout()).align;
2054 align = row->par()->params().align();
2057 // center displayed insets
2059 if (row->par()->getChar(row->pos()) == Paragraph::META_INSET
2060 && (inset=row->par()->getInset(row->pos()))
2061 && (inset->display())) // || (inset->scroll() < 0)))
2062 align = (inset->lyxCode() == Inset::MATHMACRO_CODE)
2063 ? LYX_ALIGN_BLOCK : LYX_ALIGN_CENTER;
2066 case LYX_ALIGN_BLOCK:
2067 ns = numberOfSeparators(bview->buffer(), row);
2068 if (ns && row->next() && row->next()->par() == row->par() &&
2069 !(row->next()->par()->isNewline(row->next()->pos() - 1))
2070 && !(row->next()->par()->getChar(row->next()->pos()) == Paragraph::META_INSET
2071 && row->next()->par()->getInset(row->next()->pos())
2072 && row->next()->par()->getInset(row->next()->pos())->display())
2075 fill_separator = w / ns;
2076 } else if (is_rtl) {
2080 case LYX_ALIGN_RIGHT:
2083 case LYX_ALIGN_CENTER:
2091 computeBidiTables(bview->buffer(), row);
2093 Paragraph::size_type main_body =
2094 beginningOfMainBody(bview->buffer(), row->par());
2095 Paragraph::size_type last = rowLast(row);
2097 if (main_body > 0 &&
2098 (main_body-1 > last ||
2099 !row->par()->isLineSeparator(main_body-1))) {
2100 LyXLayout const & layout =
2101 textclasslist.Style(bview->buffer()->params.textclass,
2102 row->par()->getLayout());
2103 x += lyxfont::width(layout.labelsep,
2104 getLabelFont(bview->buffer(), row->par()));
2105 if (main_body-1 <= last)
2106 x += fill_label_hfill;
2111 /* important for the screen */
2114 /* the cursor set functions have a special mechanism. When they
2115 * realize, that you left an empty paragraph, they will delete it.
2116 * They also delete the corresponding row */
2118 void LyXText::cursorRightOneWord(BufferView * bview) const
2120 // treat floats, HFills and Insets as words
2121 LyXCursor tmpcursor = cursor;
2122 // CHECK See comment on top of text.C
2124 if (tmpcursor.pos() == tmpcursor.par()->size()
2125 && tmpcursor.par()->next()) {
2126 tmpcursor.par(tmpcursor.par()->next());
2131 // Skip through initial nonword stuff.
2132 while (tmpcursor.pos() < tmpcursor.par()->size() &&
2133 ! tmpcursor.par()->isWord(tmpcursor.pos())) {
2134 // printf("Current pos1 %d", tmpcursor.pos()) ;
2135 tmpcursor.pos(tmpcursor.pos() + 1);
2138 // Advance through word.
2139 while (tmpcursor.pos() < tmpcursor.par()->size() &&
2140 tmpcursor.par()->isWord( tmpcursor.pos())) {
2141 // printf("Current pos2 %d", tmpcursor.pos()) ;
2142 tmpcursor.pos(tmpcursor.pos() + 1);
2146 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2150 void LyXText::cursorTab(BufferView * bview) const
2152 LyXCursor tmpcursor = cursor;
2153 while (tmpcursor.pos() < tmpcursor.par()->size()
2154 && !tmpcursor.par()->isNewline(tmpcursor.pos()))
2155 tmpcursor.pos(tmpcursor.pos() + 1);
2157 if (tmpcursor.pos() == tmpcursor.par()->size()){
2158 if (tmpcursor.par()->next()) {
2159 tmpcursor.par(tmpcursor.par()->next());
2163 tmpcursor.pos(tmpcursor.pos() + 1);
2164 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2168 /* -------> Skip initial whitespace at end of word and move cursor to *start*
2169 of prior word, not to end of next prior word. */
2171 void LyXText::cursorLeftOneWord(BufferView * bview) const
2173 LyXCursor tmpcursor = cursor;
2174 cursorLeftOneWord(tmpcursor);
2175 setCursor(bview, tmpcursor.par(), tmpcursor.pos());
2178 void LyXText::cursorLeftOneWord(LyXCursor & cur) const
2180 // treat HFills, floats and Insets as words
2183 && (cur.par()->isSeparator(cur.pos() - 1)
2184 || cur.par()->isKomma(cur.pos() - 1))
2185 && !(cur.par()->isHfill(cur.pos() - 1)
2186 || cur.par()->isInset(cur.pos() - 1)))
2187 cur.pos(cur.pos() - 1);
2190 && (cur.par()->isInset(cur.pos() - 1)
2191 || cur.par()->isHfill(cur.pos() - 1))) {
2192 cur.pos(cur.pos() - 1);
2193 } else if (!cur.pos()) {
2194 if (cur.par()->previous()){
2195 cur.par(cur.par()->previous());
2196 cur.pos(cur.par()->size());
2198 } else { // Here, cur != 0
2199 while (cur.pos() > 0 &&
2200 cur.par()->isWord(cur.pos()-1) )
2201 cur.pos(cur.pos() - 1);
2205 /* -------> Select current word. This depends on behaviour of
2206 CursorLeftOneWord(), so it is patched as well. */
2207 void LyXText::getWord(LyXCursor & from, LyXCursor & to,
2208 word_location const loc) const
2210 // first put the cursor where we wana start to select the word
2213 case WHOLE_WORD_STRICT:
2214 if (cursor.pos() == 0 || cursor.pos() == cursor.par()->size()
2215 || cursor.par()->isSeparator(cursor.pos())
2216 || cursor.par()->isKomma(cursor.pos())
2217 || cursor.par()->isSeparator(cursor.pos() -1)
2218 || cursor.par()->isKomma(cursor.pos() -1)) {
2222 // no break here, we go to the next
2225 // Move cursor to the beginning, when not already there.
2226 if (from.pos() && !from.par()->isSeparator(from.pos() - 1)
2227 && !from.par()->isKomma(from.pos() - 1))
2228 cursorLeftOneWord(from);
2231 // always move the cursor to the beginning of previous word
2232 cursorLeftOneWord(from);
2235 lyxerr << "LyXText::getWord: NEXT_WORD not implemented yet\n";
2241 while (to.pos() < to.par()->size()
2242 && !to.par()->isSeparator(to.pos())
2243 && !to.par()->isKomma(to.pos())
2244 && !to.par()->isHfill(to.pos()) )
2246 to.pos(to.pos() + 1);
2251 void LyXText::selectWord(BufferView * bview, word_location const loc)
2255 getWord(from, to, loc);
2257 setCursor(bview, from.par(), from.pos());
2260 selection.cursor = cursor;
2261 setCursor(bview, to.par(), to.pos() );
2262 setSelection(bview);
2266 /* -------> Select the word currently under the cursor when no
2267 selection is currently set */
2268 bool LyXText::selectWordWhenUnderCursor(BufferView * bview,
2269 word_location const loc)
2271 if (!selection.set()) {
2272 selectWord(bview, loc);
2273 return selection.set();
2279 // This function is only used by the spellchecker for NextWord().
2280 // It doesn't handle LYX_ACCENTs and probably never will.
2281 string const LyXText::selectNextWordToSpellcheck(BufferView * bview,
2282 float & value) const
2284 if (the_locking_inset) {
2285 string str = the_locking_inset->selectNextWordToSpellcheck(bview, value);
2287 value += float(cursor.y())/float(height);
2290 #warning Dekel please have a look on this one RTL? (Jug)
2292 // we have to go on checking so move cusor to the right
2293 if (cursor.pos() == cursor.par()->size()) {
2294 if (!cursor.par()->next())
2296 cursor.par(cursor.par()->next());
2299 cursor.pos(cursor.pos() + 1);
2301 Paragraph * tmppar = cursor.par();
2303 // If this is not the very first word, skip rest of
2304 // current word because we are probably in the middle
2305 // of a word if there is text here.
2306 if (cursor.pos() || cursor.par()->previous()) {
2307 while (cursor.pos() < cursor.par()->size()
2308 && cursor.par()->isLetter(cursor.pos()))
2309 cursor.pos(cursor.pos() + 1);
2312 // Now, skip until we have real text (will jump paragraphs)
2313 while ((cursor.par()->size() > cursor.pos()
2314 && (!cursor.par()->isLetter(cursor.pos()))
2315 && (!cursor.par()->isInset(cursor.pos()) ||
2316 !cursor.par()->getInset(cursor.pos())->isTextInset()))
2317 || (cursor.par()->size() == cursor.pos()
2318 && cursor.par()->next()))
2320 if (cursor.pos() == cursor.par()->size()) {
2321 cursor.par(cursor.par()->next());
2324 cursor.pos(cursor.pos() + 1);
2327 // now check if we hit an inset so it has to be a inset containing text!
2328 if (cursor.pos() < cursor.par()->size() &&
2329 cursor.par()->isInset(cursor.pos()))
2332 cursor.par()->getInset(cursor.pos())->edit(bview);
2333 // now call us again to do the above trick
2334 // but obviously we have to start from down below ;)
2335 return bview->text->selectNextWordToSpellcheck(bview, value);
2338 // Update the value if we changed paragraphs
2339 if (cursor.par() != tmppar){
2340 setCursor(bview, cursor.par(), cursor.pos());
2341 value = float(cursor.y())/float(height);
2344 // Start the selection from here
2345 selection.cursor = cursor;
2349 // and find the end of the word
2350 // (optional hyphens are part of a word)
2351 while (cursor.pos() < cursor.par()->size()
2352 && (cursor.par()->isLetter(cursor.pos()))
2353 // assignment is intentional here
2354 || ((inset = getInset())
2355 && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2356 && static_cast<InsetSpecialChar *>(inset)->kind()
2357 == InsetSpecialChar::HYPHENATION
2359 cursor.pos(cursor.pos() + 1);
2361 // Finally, we copy the word to a string and return it
2363 if (selection.cursor.pos() < cursor.pos()) {
2364 Paragraph::size_type i;
2365 for (i = selection.cursor.pos(); i < cursor.pos(); ++i) {
2366 if (cursor.par()->getChar(i) != Paragraph::META_INSET)
2367 str += cursor.par()->getChar(i);
2374 // This one is also only for the spellchecker
2375 void LyXText::selectSelectedWord(BufferView * bview)
2377 if (the_locking_inset) {
2378 the_locking_inset->selectSelectedWord(bview);
2381 // move cursor to the beginning
2382 setCursor(bview, selection.cursor.par(), selection.cursor.pos());
2384 // set the sel cursor
2385 selection.cursor = cursor;
2388 // now find the end of the word
2389 while (cursor.pos() < cursor.par()->size()
2390 && (cursor.par()->isLetter(cursor.pos())
2391 // assignment is intentional here
2392 || ((inset = getInset())
2393 && inset->lyxCode() == Inset::SPECIALCHAR_CODE
2394 && static_cast<InsetSpecialChar *>(inset)->kind()
2395 == InsetSpecialChar::HYPHENATION
2397 cursor.pos(cursor.pos() + 1);
2399 setCursor(bview, cursor.par(), cursor.pos());
2401 // finally set the selection
2402 setSelection(bview);
2406 /* -------> Delete from cursor up to the end of the current or next word. */
2407 void LyXText::deleteWordForward(BufferView * bview)
2409 if (!cursor.par()->size())
2412 LyXCursor tmpcursor = cursor;
2413 tmpcursor.row(0); //Â ??
2414 selection.set(true); // to avoid deletion
2415 cursorRightOneWord(bview);
2416 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2417 selection.cursor = cursor;
2419 setSelection(bview);
2421 /* -----> Great, CutSelection() gets rid of multiple spaces. */
2422 cutSelection(bview, true, false);
2427 /* -------> Delete from cursor to start of current or prior word. */
2428 void LyXText::deleteWordBackward(BufferView * bview)
2430 if (!cursor.par()->size())
2433 LyXCursor tmpcursor = cursor;
2434 tmpcursor.row(0); // ??
2435 selection.set(true); // to avoid deletion
2436 cursorLeftOneWord(bview);
2437 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2438 selection.cursor = cursor;
2440 setSelection(bview);
2441 cutSelection(bview, true, false);
2446 /* -------> Kill to end of line. */
2447 void LyXText::deleteLineForward(BufferView * bview)
2449 if (!cursor.par()->size())
2450 // Paragraph is empty, so we just go to the right
2453 LyXCursor tmpcursor = cursor;
2454 // We can't store the row over a regular setCursor
2455 // so we set it to 0 and reset it afterwards.
2456 tmpcursor.row(0); //Â ??
2457 selection.set(true); // to avoid deletion
2459 setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos());
2460 selection.cursor = cursor;
2462 setSelection(bview);
2463 // What is this test for ??? (JMarc)
2464 if (!selection.set()) {
2465 deleteWordForward(bview);
2467 cutSelection(bview, true, false);
2473 // Change the case of a word at cursor position.
2474 // This function directly manipulates Paragraph::text because there
2475 // is no Paragraph::SetChar currently. I did what I could to ensure
2476 // that it is correct. I guess part of it should be moved to
2477 // Paragraph, but it will have to change for 1.1 anyway. At least
2478 // it does not access outside of the allocated array as the older
2479 // version did. (JMarc)
2480 void LyXText::changeCase(BufferView * bview, LyXText::TextCase action)
2485 if (selection.set()) {
2486 from = selection.start;
2489 getWord(from, to, PARTIAL_WORD);
2490 setCursor(bview, to.par(), to.pos() + 1);
2493 changeRegionCase(bview, from, to, action);
2497 void LyXText::changeRegionCase(BufferView * bview,
2498 LyXCursor const & from,
2499 LyXCursor const & to,
2500 LyXText::TextCase action)
2502 lyx::Assert(from <= to);
2504 setUndo(bview, Undo::FINISH,
2505 from.par(), to.par()->next());
2507 Paragraph::size_type pos = from.pos();
2508 Paragraph * par = from.par();
2510 while (par && (pos != to.pos() || par != to.par())) {
2511 unsigned char c = par->getChar(pos);
2512 if (!IsInsetChar(c) && !IsHfillChar(c)) {
2514 case text_lowercase:
2517 case text_capitalization:
2519 action = text_lowercase;
2521 case text_uppercase:
2526 par->setChar(pos, c);
2527 checkParagraph(bview, par, pos);
2530 if (pos == par->size()) {
2535 if (to.row() != from.row()) {
2536 refresh_y = from.y() - from.row()->baseline();
2537 refresh_row = from.row();
2538 status(bview, LyXText::NEED_MORE_REFRESH);
2543 void LyXText::transposeChars(BufferView & bview)
2545 Paragraph * tmppar = cursor.par();
2547 setUndo(&bview, Undo::FINISH,
2548 tmppar, tmppar->next());
2550 Paragraph::size_type tmppos = cursor.pos();
2552 // First decide if it is possible to transpose at all
2554 // We are at the beginning of a paragraph.
2555 if (tmppos == 0) return;
2557 // We are at the end of a paragraph.
2558 if (tmppos == tmppar->size() - 1) return;
2560 unsigned char c1 = tmppar->getChar(tmppos);
2561 unsigned char c2 = tmppar->getChar(tmppos - 1);
2563 if (c1 != Paragraph::META_INSET
2564 && c2 != Paragraph::META_INSET) {
2565 tmppar->setChar(tmppos, c2);
2566 tmppar->setChar(tmppos - 1, c1);
2568 // We should have an implementation that handles insets
2569 // as well, but that will have to come later. (Lgb)
2570 checkParagraph(const_cast<BufferView*>(&bview), tmppar, tmppos);
2574 void LyXText::Delete(BufferView * bview)
2576 // this is a very easy implementation
2578 LyXCursor old_cursor = cursor;
2579 int const old_cur_par_id = old_cursor.par()->id();
2580 int const old_cur_par_prev_id = old_cursor.par()->previous() ?
2581 old_cursor.par()->previous()->id() : 0;
2583 // just move to the right
2586 // CHECK Look at the comment here.
2587 // This check is not very good...
2588 // The cursorRightIntern calls DeleteEmptyParagrapgMechanism
2589 // and that can very well delete the par or par->previous in
2590 // old_cursor. Will a solution where we compare paragraph id's
2592 if ((cursor.par()->previous() ? cursor.par()->previous()->id() : 0)
2593 == old_cur_par_prev_id
2594 && cursor.par()->id() != old_cur_par_id)
2596 return; // delete-empty-paragraph-mechanism has done it
2599 // if you had success make a backspace
2600 if (old_cursor.par() != cursor.par() || old_cursor.pos() != cursor.pos()) {
2601 LyXCursor tmpcursor = cursor;
2602 cursor = old_cursor; // to make sure undo gets the right cursor position
2603 setUndo(bview, Undo::DELETE,
2604 cursor.par(), cursor.par()->next());
2611 void LyXText::backspace(BufferView * bview)
2613 // Get the font that is used to calculate the baselineskip
2614 Paragraph::size_type lastpos = cursor.par()->size();
2615 LyXFont rawparfont =
2616 cursor.par()->getFontSettings(bview->buffer()->params,
2619 if (cursor.pos() == 0) {
2620 // The cursor is at the beginning of a paragraph,
2621 // so the the backspace will collapse two paragraphs into one.
2623 // we may paste some paragraphs
2625 // is it an empty paragraph?
2628 || (lastpos == 1 && cursor.par()->isSeparator(0)))) {
2629 // This is an empty paragraph and we delete it just by moving the cursor one step
2630 // left and let the DeleteEmptyParagraphMechanism handle the actual deletion
2631 // of the paragraph.
2633 if (cursor.par()->previous()) {
2634 Paragraph * tmppar = cursor.par()->previous();
2635 if (cursor.par()->getLayout() == tmppar->getLayout()
2636 && cursor.par()->getAlign() == tmppar->getAlign()) {
2637 // Inherit bottom DTD from the paragraph below.
2638 // (the one we are deleting)
2639 tmppar->params().lineBottom(cursor.par()->params().lineBottom());
2640 tmppar->params().spaceBottom(cursor.par()->params().spaceBottom());
2641 tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom());
2646 // the layout things can change the height of a row !
2647 int const tmpheight = cursor.row()->height();
2648 setHeightOfRow(bview, cursor.row());
2649 if (cursor.row()->height() != tmpheight) {
2650 refresh_y = cursor.y() - cursor.row()->baseline();
2651 refresh_row = cursor.row();
2652 status(bview, LyXText::NEED_MORE_REFRESH);
2658 if (cursor.par()->previous()) {
2659 setUndo(bview, Undo::DELETE,
2660 cursor.par()->previous(), cursor.par()->next());
2663 Paragraph * tmppar = cursor.par();
2664 Row * tmprow = cursor.row();
2666 // We used to do cursorLeftIntern() here, but it is
2667 // not a good idea since it triggers the auto-delete
2668 // mechanism. So we do a cursorLeftIntern()-lite,
2669 // without the dreaded mechanism. (JMarc)
2670 if (cursor.par()->previous()) {
2671 // steps into the above paragraph.
2672 setCursorIntern(bview, cursor.par()->previous(),
2673 cursor.par()->previous()->size(),
2677 /* Pasting is not allowed, if the paragraphs have different
2678 layout. I think it is a real bug of all other
2679 word processors to allow it. It confuses the user.
2680 Even so with a footnote paragraph and a non-footnote
2681 paragraph. I will not allow pasting in this case,
2682 because the user would be confused if the footnote behaves
2683 different wether it is open or closed.
2685 Correction: Pasting is always allowed with standard-layout
2687 if (cursor.par() != tmppar
2688 && (cursor.par()->getLayout() == tmppar->getLayout()
2689 || tmppar->getLayout() == 0 /*standard*/)
2690 && cursor.par()->getAlign() == tmppar->getAlign())
2692 removeParagraph(tmprow);
2694 cursor.par()->pasteParagraph(bview->buffer()->params);
2696 if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1))
2697 ; //cursor.par()->insertChar(cursor.pos(), ' ');
2698 // strangely enough it seems that commenting out the line above removes
2699 // most or all of the segfaults. I will however also try to move the
2700 // two Remove... lines in front of the PasteParagraph too.
2703 cursor.pos(cursor.pos() - 1);
2705 status(bview, LyXText::NEED_MORE_REFRESH);
2706 refresh_row = cursor.row();
2707 refresh_y = cursor.y() - cursor.row()->baseline();
2709 // remove the lost paragraph
2710 // This one is not safe, since the paragraph that the tmprow and the
2711 // following rows belong to has been deleted by the PasteParagraph
2712 // above. The question is... could this be moved in front of the
2714 //RemoveParagraph(tmprow);
2715 //RemoveRow(tmprow);
2717 // This rebuilds the rows.
2718 appendParagraph(bview, cursor.row());
2719 updateCounters(bview, cursor.row());
2721 // the row may have changed, block, hfills etc.
2722 setCursor(bview, cursor.par(), cursor.pos(), false);
2725 /* this is the code for a normal backspace, not pasting
2727 setUndo(bview, Undo::DELETE,
2728 cursor.par(), cursor.par()->next());
2729 // We used to do cursorLeftIntern() here, but it is
2730 // not a good idea since it triggers the auto-delete
2731 // mechanism. So we do a cursorLeftIntern()-lite,
2732 // without the dreaded mechanism. (JMarc)
2733 setCursorIntern(bview, cursor.par(), cursor.pos()- 1,
2734 false, cursor.boundary());
2736 // some insets are undeletable here
2737 if (cursor.par()->getChar(cursor.pos()) == Paragraph::META_INSET) {
2738 if (!cursor.par()->getInset(cursor.pos())->deletable())
2740 // force complete redo when erasing display insets
2741 // this is a cruel method but safe..... Matthias
2742 if (cursor.par()->getInset(cursor.pos())->display() ||
2743 cursor.par()->getInset(cursor.pos())->needFullRow()) {
2744 cursor.par()->erase(cursor.pos());
2745 redoParagraph(bview);
2750 Row * row = cursor.row();
2751 int y = cursor.y() - row->baseline();
2752 Paragraph::size_type z;
2753 /* remember that a space at the end of a row doesnt count
2754 * when calculating the fill */
2755 if (cursor.pos() < rowLast(row) ||
2756 !cursor.par()->isLineSeparator(cursor.pos())) {
2757 row->fill(row->fill() + singleWidth(bview,
2762 /* some special code when deleting a newline. This is similar
2763 * to the behavior when pasting paragraphs */
2764 if (cursor.pos() && cursor.par()->isNewline(cursor.pos())) {
2765 cursor.par()->erase(cursor.pos());
2766 // refresh the positions
2768 while (tmprow->next() && tmprow->next()->par() == row->par()) {
2769 tmprow = tmprow->next();
2770 tmprow->pos(tmprow->pos() - 1);
2772 if (cursor.par()->isLineSeparator(cursor.pos() - 1))
2773 cursor.pos(cursor.pos() - 1);
2775 if (cursor.pos() < cursor.par()->size()
2776 && !cursor.par()->isSeparator(cursor.pos())) {
2777 cursor.par()->insertChar(cursor.pos(), ' ');
2778 setCharFont(bview->buffer(), cursor.par(),
2779 cursor.pos(), current_font);
2780 // refresh the positions
2782 while (tmprow->next() && tmprow->next()->par() == row->par()) {
2783 tmprow = tmprow->next();
2784 tmprow->pos(tmprow->pos() + 1);
2788 cursor.par()->erase(cursor.pos());
2790 // refresh the positions
2792 while (tmprow->next()
2793 && tmprow->next()->par() == row->par()) {
2794 tmprow = tmprow->next();
2795 tmprow->pos(tmprow->pos() - 1);
2798 // delete newlines at the beginning of paragraphs
2799 while (cursor.par()->size() &&
2800 cursor.par()->isNewline(cursor.pos()) &&
2801 cursor.pos() == beginningOfMainBody(bview->buffer(),
2803 cursor.par()->erase(cursor.pos());
2804 // refresh the positions
2806 while (tmprow->next() &&
2807 tmprow->next()->par() == row->par()) {
2808 tmprow = tmprow->next();
2809 tmprow->pos(tmprow->pos() - 1);
2814 // is there a break one row above
2815 if (row->previous() && row->previous()->par() == row->par()) {
2816 z = nextBreakPoint(bview, row->previous(),
2818 if (z >= row->pos()) {
2821 Row * tmprow = row->previous();
2823 // maybe the current row is now empty
2824 if (row->pos() >= row->par()->size()) {
2829 breakAgainOneRow(bview, row);
2830 if (row->next() && row->next()->par() == row->par())
2831 need_break_row = row->next();
2836 // set the dimensions of the row above
2837 y -= tmprow->height();
2838 tmprow->fill(fill(bview, tmprow,
2840 setHeightOfRow(bview, tmprow);
2843 refresh_row = tmprow;
2844 status(bview, LyXText::NEED_MORE_REFRESH);
2845 setCursor(bview, cursor.par(), cursor.pos(),
2846 false, cursor.boundary());
2847 //current_font = rawtmpfont;
2848 //real_current_font = realtmpfont;
2849 // check, whether the last character's font has changed.
2851 cursor.par()->getFontSettings(bview->buffer()->params,
2852 cursor.par()->size() - 1))
2853 redoHeightOfParagraph(bview, cursor);
2858 // break the cursor row again
2859 if (row->next() && row->next()->par() == row->par() &&
2860 (rowLast(row) == row->par()->size() - 1 ||
2861 nextBreakPoint(bview, row, workWidth(bview)) != rowLast(row))) {
2863 /* it can happen that a paragraph loses one row
2864 * without a real breakup. This is when a word
2865 * is to long to be broken. Well, I don t care this
2867 if (rowLast(row) == row->par()->size() - 1)
2868 removeRow(row->next());
2872 status(bview, LyXText::NEED_MORE_REFRESH);
2874 breakAgainOneRow(bview, row);
2875 // will the cursor be in another row now?
2876 if (row->next() && row->next()->par() == row->par() &&
2877 rowLast(row) <= cursor.pos()) {
2879 breakAgainOneRow(bview, row);
2882 setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2884 if (row->next() && row->next()->par() == row->par())
2885 need_break_row = row->next();
2889 // set the dimensions of the row
2890 row->fill(fill(bview, row, workWidth(bview)));
2891 int const tmpheight = row->height();
2892 setHeightOfRow(bview, row);
2893 if (tmpheight == row->height())
2894 status(bview, LyXText::NEED_VERY_LITTLE_REFRESH);
2896 status(bview, LyXText::NEED_MORE_REFRESH);
2899 setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary());
2903 // current_font = rawtmpfont;
2904 // real_current_font = realtmpfont;
2906 if (isBoundary(bview->buffer(), cursor.par(), cursor.pos())
2907 != cursor.boundary())
2908 setCursor(bview, cursor.par(), cursor.pos(), false,
2909 !cursor.boundary());
2911 lastpos = cursor.par()->size();
2912 if (cursor.pos() == lastpos)
2913 setCurrentFont(bview);
2915 // check, whether the last characters font has changed.
2917 cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1)) {
2918 redoHeightOfParagraph(bview, cursor);
2920 // now the special right address boxes
2921 if (textclasslist.Style(bview->buffer()->params.textclass,
2922 cursor.par()->getLayout()).margintype == MARGIN_RIGHT_ADDRESS_BOX) {
2923 redoDrawingOfParagraph(bview, cursor);
2929 void LyXText::getVisibleRow(BufferView * bview, int y_offset, int x_offset,
2930 Row * row_ptr, int y, bool cleared)
2932 // returns a printed row
2933 Painter & pain = bview->painter();
2936 row_ptr->par()->isRightToLeftPar(bview->buffer()->params);
2938 Paragraph::size_type const last = rowLastPrintable(row_ptr);
2940 Paragraph::size_type vpos;
2941 Paragraph::size_type pos;
2945 LyXFont font(LyXFont::ALL_SANE);
2947 if (row_ptr->height() <= 0) {
2948 lyxerr << "LYX_ERROR: row.height: "
2949 << row_ptr->height() << endl;
2954 float fill_separator;
2956 float fill_label_hfill;
2957 prepareToPrint(bview, row_ptr, x, fill_separator,
2958 fill_hfill, fill_label_hfill);
2960 if (inset_owner && (x < 0))
2964 // clear the area where we want to paint/print
2965 int const ww = bview->workWidth();
2967 bool clear_area = true;
2970 if (!bview->screen()->forceClear() && last == row_ptr->pos()
2971 && row_ptr->par()->getChar(row_ptr->pos()) == Paragraph::META_INSET
2972 && (inset = row_ptr->par()->getInset(row_ptr->pos()))) {
2973 clear_area = inset->doClearArea();
2975 // we don't need to clear it's already done!!!
2978 } else if (clear_area) {
2979 int const y = y_offset < 0 ? 0 : y_offset;
2980 int const h = y_offset < 0 ?
2981 row_ptr->height() + y_offset : row_ptr->height();
2982 int const w = inset_owner ?
2983 inset_owner->textWidth(bview, true) : ww;
2984 int const x = x_offset;
2985 pain.fillRectangle(x, y, w, h, backgroundColor());
2986 } else if (inset != 0) {
2987 int h = row_ptr->baseline() - inset->ascent(bview, font);
2988 // first clear the whole row above the inset!
2990 int const w = (inset_owner ?
2991 inset_owner->textWidth(bview, true) : ww);
2992 pain.fillRectangle(x_offset, y_offset, w, h,
2995 h += inset->ascent(bview, font) + inset->descent(bview, font);
2996 // clear the space below the inset!
2997 if ((row_ptr->height() - h) > 0) {
2998 int const w = (inset_owner ?
2999 inset_owner->textWidth(bview, true) : ww);
3000 pain.fillRectangle(x_offset, y_offset + h,
3001 w, row_ptr->height() - h,
3004 // clear the space behind the inset, if needed
3005 if (!inset->display() && !inset->needFullRow()) {
3006 int const w = (inset_owner ?
3007 inset_owner->textWidth(bview, true) : ww);
3008 int const xp = int(x) + inset->width(bview, font);
3010 pain.fillRectangle(xp, y_offset,
3011 w-xp, row_ptr->height(),
3017 if (selection.set()) {
3018 int const w = (inset_owner ?
3019 inset_owner->textWidth(bview, true) : ww);
3021 if (bidi_same_direction) {
3022 if (selection.start.row() == row_ptr &&
3023 selection.end.row() == row_ptr) {
3024 if (selection.start.x() < selection.end.x())
3025 pain.fillRectangle(x_offset + selection.start.x(),
3027 selection.end.x() - selection.start.x(),
3031 pain.fillRectangle(x_offset + selection.end.x(),
3033 selection.start.x() - selection.end.x(),
3036 } else if (selection.start.row() == row_ptr) {
3038 pain.fillRectangle(x_offset, y_offset,
3039 selection.start.x(),
3043 pain.fillRectangle(x_offset + selection.start.x(),
3045 w - selection.start.x(),
3048 } else if (selection.end.row() == row_ptr) {
3050 pain.fillRectangle(x_offset + selection.end.x(),
3052 w - selection.end.x(),
3056 pain.fillRectangle(x_offset, y_offset,
3060 } else if (y > selection.start.y()
3061 && y < selection.end.y()) {
3062 pain.fillRectangle(x_offset, y_offset, w,
3066 } else if (selection.start.row() != row_ptr &&
3067 selection.end.row() != row_ptr &&
3068 y > selection.start.y()
3069 && y < selection.end.y()) {
3070 pain.fillRectangle(x_offset, y_offset, w,
3073 } else if (selection.start.row() == row_ptr ||
3074 selection.end.row() == row_ptr) {
3076 if ((selection.start.row() != row_ptr && !is_rtl) ||
3077 (selection.end.row() != row_ptr && is_rtl))
3078 pain.fillRectangle(x_offset, y_offset,
3082 Paragraph::size_type main_body =
3083 beginningOfMainBody(bview->buffer(),
3086 for (vpos = row_ptr->pos(); vpos <= last; ++vpos) {
3087 pos = vis2log(vpos);
3088 float const old_tmpx = tmpx;
3089 if (main_body > 0 && pos == main_body-1) {
3090 tmpx += fill_label_hfill +
3091 lyxfont::width(textclasslist.Style(bview->buffer()->params.textclass,
3092 row_ptr->par()->getLayout()).labelsep,
3093 getLabelFont(bview->buffer(),row_ptr->par()));
3094 if (row_ptr->par()->isLineSeparator(main_body-1))
3095 tmpx -= singleWidth(bview, row_ptr->par(), main_body-1);
3097 if (hfillExpansion(bview->buffer(), row_ptr, pos)) {
3098 tmpx += singleWidth(bview, row_ptr->par(), pos);
3099 if (pos >= main_body)
3102 tmpx += fill_label_hfill;
3104 else if (row_ptr->par()->isSeparator(pos)) {
3105 tmpx += singleWidth(bview, row_ptr->par(), pos);
3106 if (pos >= main_body)
3107 tmpx += fill_separator;
3109 tmpx += singleWidth(bview, row_ptr->par(), pos);
3111 if ((selection.start.row() != row_ptr ||
3112 selection.start.pos() <= pos) &&
3113 (selection.end.row() != row_ptr ||
3114 pos < selection.end.pos()) )
3115 // Here we do not use x_offset as x_offset was
3117 pain.fillRectangle(int(old_tmpx),
3119 int(tmpx - old_tmpx + 1),
3124 if ((selection.start.row() != row_ptr && is_rtl) ||
3125 (selection.end.row() != row_ptr && !is_rtl) )
3126 pain.fillRectangle(x_offset + int(tmpx),
3136 // Draw appendix lines
3137 Paragraph * firstpar = row_ptr->par();
3139 if (firstpar->params().appendix()) {
3140 pain.line(1, y_offset,
3141 1, y_offset + row_ptr->height(),
3142 LColor::appendixline);
3143 pain.line(ww - 2, y_offset,
3144 ww - 2, y_offset + row_ptr->height(),
3145 LColor::appendixline);
3149 Paragraph::depth_type const depth = firstpar->getDepth();
3151 Paragraph::depth_type next_depth = 0;
3152 Paragraph::depth_type prev_depth = 0;
3153 if (row_ptr->next())
3154 next_depth = row_ptr->next()->par()->getDepth();
3155 if (row_ptr->previous())
3156 prev_depth = row_ptr->previous()->par()->getDepth();
3158 for (Paragraph::depth_type i = 1; i <= depth; ++i) {
3159 int const line_x = (LYX_PAPER_MARGIN / 5) *
3160 i + box_x + x_offset;
3161 pain.line(line_x, y_offset, line_x,
3162 y_offset + row_ptr->height() - 1 - (i - next_depth - 1) * 3,
3166 pain.fillRectangle(line_x, y_offset, LYX_PAPER_MARGIN / 5, 2,
3169 pain.fillRectangle(line_x,
3170 y_offset + row_ptr->height() - 2 - (i - next_depth - 1) * 3,
3171 LYX_PAPER_MARGIN / 5, 2,
3177 LyXLayout const & layout =
3178 textclasslist.Style(bview->buffer()->params.textclass,
3179 row_ptr->par()->getLayout());
3182 int y_bottom = row_ptr->height();
3184 // is it a first row?
3185 if (!row_ptr->pos() && (row_ptr->par() == firstpar)) {
3187 // start of appendix?
3188 if (row_ptr->par()->params().startOfAppendix()) {
3189 pain.line(1, y_offset,
3191 LColor::appendixline);
3194 // think about the margins
3195 if (!row_ptr->previous() && bv_owner)
3196 y_top += LYX_PAPER_MARGIN;
3198 // draw a top pagebreak
3199 if (row_ptr->par()->params().pagebreakTop()) {
3201 pb_font.setColor(LColor::pagebreak).decSize();
3205 pain.line(0, y_offset + y_top + 2*defaultHeight(),
3207 y_offset + y_top + 2 * defaultHeight(),
3209 Painter::line_onoffdash);
3210 lyxfont::rectText(_("Page Break (top)"), pb_font,
3212 pain.rectText((ww - w)/2,
3213 y_offset + y_top + 2 * defaultHeight() + d,
3214 _("Page Break (top)"),
3218 y_top += 3 * defaultHeight();
3221 if (row_ptr->par()->params().spaceTop().kind() == VSpace::VFILL) {
3223 pain.line(0, y_offset + 2 + y_top,
3224 LYX_PAPER_MARGIN, y_offset + 2 + y_top,
3227 pain.line(0, y_offset + y_top + 3 * defaultHeight(),
3229 y_offset + y_top + 3 * defaultHeight(),
3232 pain.line(LYX_PAPER_MARGIN / 2, y_offset + 2 + y_top,
3233 LYX_PAPER_MARGIN / 2,
3234 y_offset + y_top + 3 * defaultHeight(),
3237 y_top += 3 * defaultHeight();
3240 // think about user added space
3241 y_top += int(row_ptr->par()->params().spaceTop().inPixels(bview));
3243 // think about the parskip
3244 // some parskips VERY EASY IMPLEMENTATION
3245 if (bview->buffer()->params.paragraph_separation == BufferParams::PARSEP_SKIP) {
3246 if (layout.latextype == LATEX_PARAGRAPH
3247 && firstpar->getDepth() == 0
3248 && firstpar->previous())
3249 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3250 else if (firstpar->previous()
3251 && textclasslist.Style(bview->buffer()->params.textclass,
3252 firstpar->previous()->getLayout()).latextype == LATEX_PARAGRAPH
3253 && firstpar->previous()->getDepth() == 0)
3254 // is it right to use defskip here, too? (AS)
3255 y_top += bview->buffer()->params.getDefSkip().inPixels(bview);
3258 if (row_ptr->par()->params().lineTop()) {
3260 y_top += lyxfont::ascent('x',
3261 getFont(bview->buffer(),
3262 row_ptr->par(), 0));
3263 int const w = (inset_owner ?
3264 inset_owner->width(bview, font) : ww);
3265 int const xp = static_cast<int>(inset_owner ? x : 0);
3266 pain.line(xp, y_offset + y_top,
3267 w, y_offset + y_top,
3269 Painter::line_solid,
3270 Painter::line_thick);
3272 y_top += lyxfont::ascent('x',getFont(bview->buffer(),
3273 row_ptr->par(), 0));
3276 // should we print a label?
3277 if (layout.labeltype >= LABEL_STATIC
3278 && (layout.labeltype != LABEL_STATIC
3279 || layout.latextype != LATEX_ENVIRONMENT
3280 || row_ptr->par()->isFirstInSequence())) {
3281 font = getLabelFont(bview->buffer(), row_ptr->par());
3282 if (!row_ptr->par()->getLabelstring().empty()) {
3284 string const tmpstring =
3285 row_ptr->par()->getLabelstring();
3287 if (layout.labeltype == LABEL_COUNTER_CHAPTER) {
3288 if (bview->buffer()->params.secnumdepth >= 0) {
3289 // this is special code for
3290 // the chapter layout. This is
3291 // printed in an extra row
3292 // and has a pagebreak at
3294 float spacing_val = 1.0;
3295 if (!row_ptr->par()->params().spacing().isDefault()) {
3296 spacing_val = row_ptr->par()->params().spacing().getValue();
3298 spacing_val = bview->buffer()->params.spacing.getValue();
3301 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val)
3302 + int(layout.parsep) * defaultHeight();
3304 tmpx = ww - leftMargin(bview, row_ptr) -
3305 lyxfont::width(tmpstring, font);
3306 pain.text(int(tmpx),
3307 y_offset + row_ptr->baseline() - row_ptr->ascent_of_text() - maxdesc,
3312 tmpx = ww - leftMargin(bview, row_ptr)
3313 + lyxfont::width(layout.labelsep, font);
3315 tmpx = x - lyxfont::width(layout.labelsep, font)
3316 - lyxfont::width(tmpstring, font);
3319 pain.text(int(tmpx),
3320 y_offset + row_ptr->baseline(),
3324 // the labels at the top of an environment.
3325 // More or less for bibliography
3326 } else if (layout.labeltype == LABEL_TOP_ENVIRONMENT ||
3327 layout.labeltype == LABEL_BIBLIO ||
3328 layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
3329 if (row_ptr->par()->isFirstInSequence()) {
3330 font = getLabelFont(bview->buffer(),
3332 if (!row_ptr->par()->getLabelstring().empty()) {
3333 string const tmpstring =
3334 row_ptr->par()->getLabelstring();
3335 float spacing_val = 1.0;
3336 if (!row_ptr->par()->params().spacing().isDefault()) {
3337 spacing_val = row_ptr->par()->params().spacing().getValue();
3339 spacing_val = bview->buffer()->params.spacing.getValue();
3342 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val
3343 + (layout.labelbottomsep * defaultHeight()));
3346 if (layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT){
3347 tmpx = ( (is_rtl ? leftMargin(bview, row_ptr) : x)
3348 + ww - rightMargin(bview->buffer(), row_ptr) ) / 2;
3349 tmpx -= lyxfont::width(tmpstring, font) / 2;
3351 tmpx = ww - leftMargin(bview, row_ptr) -
3352 lyxfont::width(tmpstring, font);
3353 pain.text(int(tmpx),
3354 y_offset + row_ptr->baseline()
3355 - row_ptr->ascent_of_text()
3361 if (layout.labeltype == LABEL_BIBLIO && row_ptr->par()->bibkey) {
3362 font = getLayoutFont(bview->buffer(), row_ptr->par());
3364 tmpx = ww - leftMargin(bview, row_ptr)
3365 + lyxfont::width(layout.labelsep, font);
3367 tmpx = x - lyxfont::width(layout.labelsep, font)
3368 - row_ptr->par()->bibkey->width(bview, font);
3369 row_ptr->par()->bibkey->draw(bview, font,
3370 y_offset + row_ptr->baseline(),
3375 // is it a last row?
3376 Paragraph * par = row_ptr->par();
3377 if (row_ptr->par() == par
3378 && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par())) {
3379 // think about the margins
3380 if (!row_ptr->next() && bv_owner)
3381 y_bottom -= LYX_PAPER_MARGIN;
3383 // draw a bottom pagebreak
3384 if (firstpar->params().pagebreakBottom()) {
3386 pb_font.setColor(LColor::pagebreak).decSize();
3387 int const y_place = y_offset + y_bottom
3388 - 2 * defaultHeight();
3394 .line(0, y_place, ww, y_place,
3396 Painter::line_onoffdash);
3397 lyxfont::rectText(_("Page Break (bottom)"), pb_font,
3399 pain.rectText((ww - w) / 2, y_place + d,
3400 _("Page Break (bottom)"),
3404 y_bottom -= 3 * defaultHeight();
3407 if (firstpar->params().spaceBottom().kind() == VSpace::VFILL) {
3408 // draw a vfill bottom
3409 int const y_place = y_offset + y_bottom
3410 - 3 * defaultHeight();
3412 pain.line(0, y_place,
3413 LYX_PAPER_MARGIN, y_place,
3415 pain.line(0, y_offset + y_bottom - 2,
3417 y_offset + y_bottom - 2,
3419 pain.line(LYX_PAPER_MARGIN / 2,
3421 LYX_PAPER_MARGIN / 2,
3422 y_offset + y_bottom - 2,
3424 y_bottom -= 3 * defaultHeight();
3427 // think about user added space
3428 y_bottom -= int(firstpar->params().spaceBottom().inPixels(bview));
3430 if (firstpar->params().lineBottom()) {
3431 // draw a bottom line
3432 y_bottom -= lyxfont::ascent('x',
3433 getFont(bview->buffer(),
3435 max(Paragraph::size_type(0), par->size() - 1)));
3436 int const w = (inset_owner ?
3437 inset_owner->width(bview, font) : ww);
3438 int const xp = static_cast<int>(inset_owner ? x : 0);
3439 pain.line(xp, y_offset + y_bottom,
3440 w, y_offset + y_bottom,
3441 LColor::topline, Painter::line_solid,
3442 Painter::line_thick);
3443 y_bottom -= lyxfont::ascent('x',
3444 getFont(bview->buffer(),
3446 max(Paragraph::size_type(0), par->size() - 1)));
3450 int const endlabel =
3451 row_ptr->par()->getEndLabel(bview->buffer()->params);
3454 case END_LABEL_FILLED_BOX:
3456 LyXFont const font = getFont(bview->buffer(),
3457 row_ptr->par(), last);
3458 int const size = int(0.75 * lyxfont::maxAscent(font));
3459 int const y = (y_offset + row_ptr->baseline()) - size;
3460 int x = is_rtl ? LYX_PAPER_MARGIN
3461 : ww - LYX_PAPER_MARGIN - size;
3463 if (row_ptr->fill() <= size)
3464 x += (size - row_ptr->fill() + 1) * (is_rtl ? -1 : 1);
3465 if (endlabel == END_LABEL_BOX) {
3466 pain.line(x, y, x, y + size,
3468 pain.line(x + size, y, x + size , y + size,
3470 pain.line(x, y, x + size, y,
3472 pain.line(x, y + size, x + size, y + size,
3475 pain.fillRectangle(x, y, size, size,
3479 case END_LABEL_STATIC:
3481 LyXTextClass::LayoutList::size_type layout = row_ptr->par()->getLayout();
3482 string const tmpstring = textclasslist.
3483 Style(bview->buffer()->params.textclass,
3484 layout).endlabelstring();
3485 font = getLabelFont(bview->buffer(), row_ptr->par());
3486 int const tmpx = is_rtl ?
3487 int(x) - lyxfont::width(tmpstring, font)
3488 : ww - rightMargin(bview->buffer(), row_ptr) - row_ptr->fill();
3489 pain.text( tmpx, y_offset + row_ptr->baseline(), tmpstring, font);
3492 case END_LABEL_NO_LABEL:
3497 // draw the text in the pixmap
3499 vpos = row_ptr->pos();
3501 Paragraph::size_type main_body =
3502 beginningOfMainBody(bview->buffer(), row_ptr->par());
3503 if (main_body > 0 &&
3504 (main_body-1 > last ||
3505 !row_ptr->par()->isLineSeparator(main_body - 1)))
3508 while (vpos <= last) {
3509 pos = vis2log(vpos);
3510 if (main_body > 0 && pos == main_body - 1) {
3511 x += fill_label_hfill
3512 + lyxfont::width(layout.labelsep,
3513 getLabelFont(bview->buffer(),
3515 - singleWidth(bview,
3520 if (row_ptr->par() ->isHfill(pos)) {
3523 y_offset + row_ptr->baseline() - defaultHeight() / 2,
3525 y_offset + row_ptr->baseline(),
3528 if (hfillExpansion(bview->buffer(),
3530 if (pos >= main_body) {
3532 y_offset + row_ptr->baseline() - defaultHeight() / 4,
3533 int(x + fill_hfill),
3534 y_offset + row_ptr->baseline() - defaultHeight() / 4,
3536 Painter::line_onoffdash);
3540 y_offset + row_ptr->baseline() - defaultHeight() / 4,
3541 int(x + fill_label_hfill),
3542 y_offset + row_ptr->baseline() - defaultHeight() / 4,
3544 Painter::line_onoffdash);
3546 x += fill_label_hfill;
3549 y_offset + row_ptr->baseline() - defaultHeight() / 2,
3551 y_offset + row_ptr->baseline(),
3556 } else if (row_ptr->par()->isSeparator(pos)) {
3557 x += singleWidth(bview,
3558 row_ptr->par(), pos);
3559 if (pos >= main_body)
3560 x += fill_separator;
3563 draw(bview, row_ptr, vpos, y_offset, x, clear_area);
3568 int LyXText::defaultHeight() const
3570 LyXFont font(LyXFont::ALL_SANE);
3571 return int(lyxfont::maxAscent(font) + lyxfont::maxDescent(font) * 1.5);
3575 /* returns the column near the specified x-coordinate of the row
3576 * x is set to the real beginning of this column */
3577 Paragraph::size_type
3578 LyXText::getColumnNearX(BufferView * bview, Row * row, int & x,
3579 bool & boundary) const
3582 float fill_separator;
3584 float fill_label_hfill;
3586 prepareToPrint(bview, row, tmpx, fill_separator,
3587 fill_hfill, fill_label_hfill);
3589 Paragraph::size_type vc = row->pos();
3590 Paragraph::size_type last = rowLastPrintable(row);
3591 Paragraph::size_type c = 0;
3592 LyXLayout const & layout =
3593 textclasslist.Style(bview->buffer()->params.textclass,
3594 row->par()->getLayout());
3595 bool left_side = false;
3597 Paragraph::size_type
3598 main_body = beginningOfMainBody(bview->buffer(), row->par());
3599 float last_tmpx = tmpx;
3601 if (main_body > 0 &&
3602 (main_body - 1 > last ||
3603 !row->par()->isLineSeparator(main_body - 1)))
3606 while (vc <= last && tmpx <= x) {
3609 if (main_body > 0 && c == main_body-1) {
3610 tmpx += fill_label_hfill +
3611 lyxfont::width(layout.labelsep,
3612 getLabelFont(bview->buffer(), row->par()));
3613 if (row->par()->isLineSeparator(main_body - 1))
3614 tmpx -= singleWidth(bview, row->par(), main_body-1);
3617 if (hfillExpansion(bview->buffer(), row, c)) {
3618 x += singleWidth(bview, row->par(), c);
3622 tmpx += fill_label_hfill;
3624 else if (row->par()->isSeparator(c)) {
3625 tmpx += singleWidth(bview, row->par(), c);
3627 tmpx+= fill_separator;
3629 tmpx += singleWidth(bview, row->par(), c);
3633 if ((tmpx + last_tmpx) / 2 > x) {
3638 if (vc > last + 1) // This shouldn't happen.
3642 bool const lastrow = lyxrc.rtl_support // This is not needed, but gives
3643 // some speedup if rtl_support=false
3644 && (!row->next() || row->next()->par() != row->par());
3645 bool const rtl = (lastrow)
3646 ? row->par()->isRightToLeftPar(bview->buffer()->params)
3647 : false; // If lastrow is false, we don't need to compute
3648 // the value of rtl.
3650 if (row->pos() > last) // Row is empty?
3653 ( ( rtl && left_side && vc == row->pos() && x < tmpx - 5) ||
3654 (!rtl && !left_side && vc == last + 1 && x > tmpx + 5) ))
3656 else if (vc == row->pos()) {
3658 if (bidi_level(c) % 2 == 1)
3661 c = vis2log(vc - 1);
3662 bool const rtl = (bidi_level(c) % 2 == 1);
3663 if (left_side == rtl) {
3665 boundary = isBoundary(bview->buffer(), row->par(), c);
3669 if (row->pos() <= last && c > last
3670 && row->par()->isNewline(last)) {
3671 if (bidi_level(last) % 2 == 0)
3672 tmpx -= singleWidth(bview, row->par(), last);
3674 tmpx += singleWidth(bview, row->par(), last);
3684 // returns pointer to a specified row
3685 Row * LyXText::getRow(Paragraph * par,
3686 Paragraph::size_type pos, int & y) const
3691 Row * tmprow = firstrow;
3694 // find the first row of the specified paragraph
3695 while (tmprow->next() && tmprow->par() != par) {
3696 y += tmprow->height();
3697 tmprow = tmprow->next();
3700 // now find the wanted row
3701 while (tmprow->pos() < pos
3703 && tmprow->next()->par() == par
3704 && tmprow->next()->pos() <= pos) {
3705 y += tmprow->height();
3706 tmprow = tmprow->next();