1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright 1995 Matthias Ettrich
7 * Copyright 1995-2000 The LyX Team.
9 * ====================================================== */
16 #pragma implementation "table.h"
20 #include "lyxparagraph.h"
22 #include "support/textutils.h"
23 #include "insets/insetbib.h"
24 #include "lyx_gui_misc.h"
26 #include "bufferparams.h"
28 #include "minibuffer.h"
42 static const int LYX_PAPER_MARGIN = 20;
43 extern int bibitemMaxWidth(Painter &, LyXFont const &);
45 static int iso885968x[] = {
46 0xbc, // 0xa8 = fathatan
47 0xbd, // 0xa9 = dammatan
48 0xbe, // 0xaa = kasratan
52 0xde, // 0xae = shadda
55 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 0xb0-0xbf
59 0xc2, // 0xc2 = ligature madda
60 0xc3, // 0xc3 = ligature hamza on alef
61 0xc4, // 0xc4 = ligature hamza on waw
62 0xc5, // 0xc5 = ligature hamza under alef
63 0xc0, // 0xc6 = ligature hamza on ya
66 0xc9, // 0xc9 = taa marbuta
85 0,0,0,0,0, // 0xdb- 0xdf
96 0xe9, // 0xe9 = alef maksura
102 bool is_arabic(unsigned char c)
104 return 0xa8 <= c && c <= 0xea && iso885968x[c-0xa8];
109 bool is_nikud(unsigned char c)
111 return 192 <= c && c <= 210;
115 unsigned char LyXText::TransformChar(unsigned char c, Letter_Form form) const
118 (form == FORM_INITIAL || form == FORM_MEDIAL) )
119 return iso885968x[c-0xa8];
125 unsigned char LyXText::TransformChar(unsigned char c, LyXParagraph * par,
126 LyXParagraph::size_type pos) const
130 return c + (0xb0 - '0');
134 bool not_first = (pos > 0 && is_arabic(par->GetChar(pos-1)));
135 if (pos < par->Last()-1 && is_arabic(par->GetChar(pos+1)))
137 return TransformChar(c,FORM_MEDIAL);
139 return TransformChar(c,FORM_INITIAL);
142 return TransformChar(c,FORM_FINAL);
144 return TransformChar(c,FORM_ISOLATED);
147 // This is the comments that some of the warnings below refers to.
148 // There are some issues in this file and I don't think they are
149 // really related to the FIX_DOUBLE_SPACE patch. I'd rather think that
150 // this is a problem that has been here almost from day one and that a
151 // larger userbase with differenct access patters triggers the bad
152 // behaviour. (segfaults.) What I think happen is: In several places
153 // we store the paragraph in the current cursor and then moves the
154 // cursor. This movement of the cursor will delete paragraph at the
155 // old position if it is now empty. This will make the temporary
156 // pointer to the old cursor paragraph invalid and dangerous to use.
157 // And is some cases this will trigger a segfault. I have marked some
158 // of the cases where this happens with a warning, but I am sure there
159 // are others in this file and in text2.C. There is also a note in
160 // Delete() that you should read. In Delete I store the paragraph->id
161 // instead of a pointer to the paragraph. I am pretty sure this faulty
162 // use of temporary pointers to paragraphs that might have gotten
163 // invalidated (through a cursor movement) before they are used, are
164 // the cause of the strange crashes we get reported often.
166 // It is very tiresom to change this code, especially when it is as
167 // hard to read as it is. Help to fix all the cases where this is done
168 // would be greately appreciated.
172 int LyXText::SingleWidth(LyXParagraph * par,
173 LyXParagraph::size_type pos) const
175 char c = par->GetChar(pos);
176 return SingleWidth(par, pos, c);
180 int LyXText::SingleWidth(LyXParagraph * par,
181 LyXParagraph::size_type pos, char c) const
183 LyXFont font = GetFont(par, pos);
185 // The most common case is handled first (Asger)
186 if (IsPrintable(c)) {
187 if (font.language()->RightToLeft) {
188 if (font.language()->lang == "arabic" &&
189 lyxrc.font_norm == "iso8859-6.8x")
190 c = TransformChar(c, par, pos);
191 else if (font.language()->lang == "hebrew" &&
195 return lyxfont::width(c, font);
197 } else if (IsHfillChar(c)) {
198 return 3; /* Because of the representation
199 * as vertical lines */
200 } else if (c == LyXParagraph::META_FOOTNOTE ||
201 c == LyXParagraph::META_MARGIN ||
202 c == LyXParagraph::META_FIG ||
203 c == LyXParagraph::META_TAB ||
204 c == LyXParagraph::META_WIDE_FIG ||
205 c == LyXParagraph::META_WIDE_TAB ||
206 c == LyXParagraph::META_ALGORITHM) {
209 case LyXParagraph::META_MARGIN:
212 case LyXParagraph::META_FIG:
215 case LyXParagraph::META_TAB:
218 case LyXParagraph::META_ALGORITHM:
221 case LyXParagraph::META_WIDE_FIG:
224 case LyXParagraph::META_WIDE_TAB:
227 case LyXParagraph::META_FOOTNOTE:
233 return lyxfont::width(fs, font);
234 } else if (c == LyXParagraph::META_INSET) {
235 Inset * tmpinset= par->GetInset(pos);
237 return par->GetInset(pos)->width(owner_->painter(),
242 } else if (IsSeparatorChar(c))
244 else if (IsNewlineChar(c))
246 return lyxfont::width(c, font);
250 // Returns the paragraph position of the last character in the specified row
251 LyXParagraph::size_type LyXText::RowLast(Row const * row) const
254 return row->par->Last() - 1;
255 else if (row->next->par != row->par)
256 return row->par->Last() - 1;
258 return row->next->pos - 1;
262 LyXParagraph::size_type LyXText::RowLastPrintable(Row const * row) const
264 LyXParagraph::size_type last = RowLast(row);
265 if (last >= row->pos && row->next && row->next->par == row->par &&
266 row->par->IsSeparator(last))
273 void LyXText::ComputeBidiTables(Row * row) const
275 bidi_same_direction = true;
276 if (!lyxrc.rtl_support) {
280 bidi_start = row->pos;
281 bidi_end = RowLastPrintable(row);
283 if (bidi_start > bidi_end) {
288 if (bidi_end + 2 - bidi_start >
289 static_cast<LyXParagraph::size_type>(log2vis_list.size())) {
290 LyXParagraph::size_type new_size =
291 (bidi_end + 2 - bidi_start < 500) ?
292 500 : 2 * (bidi_end + 2 - bidi_start);
293 log2vis_list.resize(new_size);
294 vis2log_list.resize(new_size);
295 bidi_levels.resize(new_size);
298 vis2log_list[bidi_end + 1 - bidi_start] = -1;
299 log2vis_list[bidi_end + 1 - bidi_start] = -1;
301 LyXParagraph::size_type stack[2];
302 bool rtl_par = row->par->getParLanguage()->RightToLeft;
306 LyXParagraph::size_type main_body = BeginningOfMainBody(row->par);
308 for (LyXParagraph::size_type lpos = bidi_start; lpos <= bidi_end; ++lpos) {
309 bool is_space = row->par->IsLineSeparator(lpos);
310 LyXParagraph::size_type pos =
311 (is_space && lpos+1 <= bidi_end &&
312 !row->par->IsLineSeparator(lpos+1) &&
313 (!row->par->table || !row->par->IsNewline(lpos+1)) )
315 LyXFont font = row->par->GetFontSettings(pos);
316 bool new_rtl = font.isVisibleRightToLeft();
317 bool new_rtl0 = font.isRightToLeft();
320 if (row->par->table && row->par->IsNewline(lpos)) {
322 new_rtl = new_rtl0 = false;
323 } else if (lpos == main_body - 1 && row->pos < main_body - 1 &&
324 row->par->IsLineSeparator(lpos)) {
325 new_level = (rtl_par) ? 1 : 0;
326 new_rtl = new_rtl0 = rtl_par;
328 new_level = (new_rtl) ? 1 : 2;
330 new_level = (rtl_par) ? 2 : 0;
332 if (is_space && new_level >= level) {
338 int new_level2 = new_level;
340 if (level == new_level && rtl0 != new_rtl0) {
342 log2vis_list[lpos - bidi_start] = (rtl) ? 1 : -1;
343 } else if (level < new_level) {
344 log2vis_list[lpos - bidi_start] = (rtl) ? -1 : 1;
345 if (new_level > rtl_par)
346 bidi_same_direction = false;
348 log2vis_list[lpos - bidi_start] = (new_rtl) ? -1 : 1;
351 bidi_levels[lpos - bidi_start] = new_level;
353 while (level > new_level2) {
354 LyXParagraph::size_type old_lpos =
356 int delta = lpos - old_lpos - 1;
359 log2vis_list[lpos - bidi_start] += delta;
360 log2vis_list[old_lpos - bidi_start] += delta;
362 while (level < new_level)
363 stack[level++] = lpos;
367 LyXParagraph::size_type old_lpos = stack[--level];
368 int delta = bidi_end - old_lpos;
371 log2vis_list[old_lpos - bidi_start] += delta;
374 LyXParagraph::size_type vpos = bidi_start - 1;
375 for (LyXParagraph::size_type lpos = bidi_start; lpos <= bidi_end; ++lpos) {
376 vpos += log2vis_list[lpos - bidi_start];
377 vis2log_list[vpos - bidi_start] = lpos;
378 log2vis_list[lpos - bidi_start] = vpos;
383 // This method requires a previous call to ComputeBidiTables()
384 bool LyXText::IsBoundary(LyXParagraph * par, LyXParagraph::size_type pos) const
386 if (!lyxrc.rtl_support)
387 return false; // This is just for speedup
389 if (!bidi_InRange(pos - 1) ||
390 (par->table && par->IsNewline(pos-1)) )
393 bool rtl = bidi_level(pos - 1) % 2;
395 if (pos == par->Last() ||
396 (par->table && par->IsNewline(pos)))
397 rtl2 = par->isRightToLeftPar();
398 else if (bidi_InRange(pos))
399 rtl2 = bidi_level(pos) % 2;
403 bool LyXText::IsBoundary(LyXParagraph * par, LyXParagraph::size_type pos,
404 LyXFont const & font) const
406 if (!lyxrc.rtl_support)
407 return false; // This is just for speedup
409 bool rtl = font.isVisibleRightToLeft();
411 if (pos == par->Last() ||
412 (par->table && par->IsNewline(pos)))
413 rtl2 = par->isRightToLeftPar();
414 else if (bidi_InRange(pos))
415 rtl2 = bidi_level(pos) % 2;
420 void LyXText::draw(Row const * row,
421 LyXParagraph::size_type & vpos,
422 int offset, float & x)
424 Painter & pain = owner_->painter();
426 LyXParagraph::size_type pos = vis2log(vpos);
427 char c = row->par->GetChar(pos);
430 if (IsNewlineChar(c)) {
432 // Draw end-of-line marker
433 LyXFont font = GetFont(row->par, pos);
434 int wid = lyxfont::width('n', font);
435 int asc = lyxfont::maxAscent(font);
436 int y = offset + row->baseline;
439 if (bidi_level(pos) % 2 == 0) {
440 xp[0] = int(x + wid * 0.375);
441 yp[0] = int(y - 0.875 * asc * 0.75);
444 yp[1] = int(y - 0.500 * asc * 0.75);
446 xp[2] = int(x + wid * 0.375);
447 yp[2] = int(y - 0.125 * asc * 0.75);
449 pain.lines(xp, yp, 3, LColor::eolmarker);
452 yp[0] = int(y - 0.500 * asc * 0.75);
454 xp[1] = int(x + wid);
455 yp[1] = int(y - 0.500 * asc * 0.75);
457 xp[2] = int(x + wid);
458 yp[2] = int(y - asc * 0.75);
460 pain.lines(xp, yp, 3, LColor::eolmarker);
462 xp[0] = int(x + wid * 0.625);
463 yp[0] = int(y - 0.875 * asc * 0.75);
465 xp[1] = int(x + wid);
466 yp[1] = int(y - 0.500 * asc * 0.75);
468 xp[2] = int(x + wid * 0.625);
469 yp[2] = int(y - 0.125 * asc * 0.75);
471 pain.lines(xp, yp, 3, LColor::eolmarker);
473 xp[0] = int(x + wid);
474 yp[0] = int(y - 0.500 * asc * 0.75);
477 yp[1] = int(y - 0.500 * asc * 0.75);
480 yp[2] = int(y - asc * 0.75);
482 pain.lines(xp, yp, 3, LColor::eolmarker);
488 LyXFont font = GetFont(row->par, pos);
489 LyXFont font2 = font;
491 if (c == LyXParagraph::META_FOOTNOTE
492 || c == LyXParagraph::META_MARGIN
493 || c == LyXParagraph::META_FIG
494 || c == LyXParagraph::META_TAB
495 || c == LyXParagraph::META_WIDE_FIG
496 || c == LyXParagraph::META_WIDE_TAB
497 || c == LyXParagraph::META_ALGORITHM) {
500 case LyXParagraph::META_MARGIN:
503 case LyXParagraph::META_FIG:
506 case LyXParagraph::META_TAB:
509 case LyXParagraph::META_ALGORITHM:
512 case LyXParagraph::META_WIDE_FIG:
515 case LyXParagraph::META_WIDE_TAB:
518 case LyXParagraph::META_FOOTNOTE:
525 // calculate the position of the footnotemark
526 int y = (row->baseline - lyxfont::maxAscent(font2)
527 + lyxfont::maxAscent(font));
529 font.setColor(LColor::footnote);
531 // draw it and set new x position
533 pain.text(int(x), offset + y, fs, font);
534 x += lyxfont::width(fs, font);
535 pain.line(int(tmpx), offset + row->baseline,
536 int(x), offset + row->baseline,
541 } else if (c == LyXParagraph::META_INSET) {
542 Inset * tmpinset = row->par->GetInset(pos);
544 tmpinset->draw(owner_->painter(), font,
545 offset + row->baseline, x);
549 if (lyxrc.mark_foreign_language &&
550 font.language() != buffer->params.language_info) {
551 int y = offset + row->height - 1;
552 pain.line(int(tmpx), y, int(x), y,
559 /* usual characters, no insets */
561 // Collect character that we can draw in one command
563 // This is dirty, but fast. Notice that it will never be too small.
564 // For the record, I'll note that Microsoft Word has a limit
565 // of 768 here. We have none :-) (Asger)
566 // Ok. I am the first to admit that the use of std::string will be
567 // a tiny bit slower than using a POD char array. However, I claim
568 // that this slowdown is so small that it is close to inperceptive.
569 // So IMHO we should go with the easier and clearer implementation.
570 // And even if 1024 is a large number here it might overflow, string
571 // will only overflow if the machine is out of memory...
572 static string textstring;
576 LyXParagraph::size_type last = RowLastPrintable(row);
578 if (font.language()->lang == "hebrew") {
580 LyXParagraph::size_type vpos2 = vpos;
581 int width = lyxfont::width(c, font2);
583 while (vpos2 <= last &&
584 (pos = vis2log(vpos2)) >= 0
585 && static_cast<unsigned char>(c = row->par->GetChar(pos)) > ' '
588 if (static_cast<unsigned char>(c = row->par->GetChar(pos)) > ' '
590 int width2 = SingleWidth(row->par, pos, c);
591 dx = (c == 'ø' || c == 'ã')
592 ? width2-width : (width2-width)/2;
595 pain.text(int(x)+dx, offset + row->baseline, textstring, font);
597 while (vpos <= last &&
598 (pos = vis2log(vpos)) >= 0
599 && static_cast<unsigned char>(c = row->par->GetChar(pos)) > ' '
601 && font2 == GetFont(row->par, pos)) {
605 // Draw text and set the new x position
606 pain.text(int(x), offset + row->baseline, textstring, font);
607 x += lyxfont::width(textstring, font);
609 } else if (font.language()->lang == "arabic" &&
610 lyxrc.font_norm == "iso8859-6.8x") {
611 textstring = TransformChar(c, row->par, pos);
612 while (vpos <= last &&
613 (pos = vis2log(vpos)) >= 0
614 && static_cast<unsigned char>(c = row->par->GetChar(pos)) > ' '
615 && font2 == GetFont(row->par, pos)) {
616 c = TransformChar(c, row->par, pos);
620 // Draw text and set the new x position
621 pain.text(int(x), offset + row->baseline, textstring, font);
622 x += lyxfont::width(textstring, font);
624 while (vpos <= last &&
625 (pos = vis2log(vpos)) >= 0
626 && static_cast<unsigned char>(c = row->par->GetChar(pos)) > ' '
627 && font2 == GetFont(row->par, pos)) {
631 // Draw text and set the new x position
632 pain.text(int(x), offset + row->baseline, textstring, font);
633 x += lyxfont::width(textstring, font);
636 // what about underbars?
637 if (font.underbar() == LyXFont::ON && font.latex() != LyXFont::ON) {
638 pain.line(int(tmpx), offset + row->baseline + 2,
639 int(x), offset + row->baseline + 2);
641 } else if (lyxrc.mark_foreign_language &&
642 font.language() != buffer->params.language_info) {
643 int y = offset + row->height - 1;
644 pain.line(int(tmpx), y, int(x), y,
648 // If we want ulem.sty support, drawing
649 // routines should go here. (Asger)
650 // Why shouldn't LyXFont::drawText handle it internally?
654 // Returns the left beginning of the text.
655 // This information cannot be taken from the layouts-objekt, because in
656 // LaTeX the beginning of the text fits in some cases (for example sections)
657 // exactly the label-width.
658 int LyXText::LeftMargin(Row const * row) const
660 LyXLayout const & layout =
661 textclasslist.Style(buffer->params.textclass,
662 row->par->GetLayout());
664 string parindent = layout.parindent;
666 /* table stuff -- begin */
669 /* table stuff -- end */
671 int x = LYX_PAPER_MARGIN;
673 x += lyxfont::signedWidth(textclasslist
674 .TextClass(buffer->params.textclass)
677 .TextClass(buffer->params.textclass)
680 if (row->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
681 LyXFont font(LyXFont::ALL_SANE);
682 font.setSize(LyXFont::SIZE_SMALL);
683 x += lyxfont::width("Mwide-figM", font)
684 + LYX_PAPER_MARGIN/2;
687 // this is the way, LyX handles the LaTeX-Environments.
688 // I have had this idea very late, so it seems to be a
689 // later added hack and this is true
690 if (!row->par->GetDepth()) {
691 if (!row->par->GetLayout()) {
692 // find the previous same level paragraph
693 if (row->par->FirstPhysicalPar()->Previous()) {
694 LyXParagraph * newpar = row->par
695 ->DepthHook(row->par->GetDepth());
697 textclasslist.Style(buffer->params.textclass,
704 // find the next level paragraph
706 LyXParagraph * newpar = row->par->DepthHook(row->par->GetDepth()-1);
708 // make a corresponding row. Needed to call LeftMargin()
710 // check wether it is a sufficent paragraph
711 if (newpar && newpar->footnoteflag == row->par->footnoteflag
713 .Style(buffer->params.textclass,
714 newpar->GetLayout()).isEnvironment()) {
716 dummyrow.par = newpar;
717 dummyrow.pos = newpar->Last();
718 x = LeftMargin(&dummyrow);
720 // this is no longer an error, because this function
721 // is used to clear impossible depths after changing
722 // a layout. Since there is always a redo,
723 // LeftMargin() is always called
724 row->par->FirstPhysicalPar()->depth = 0;
727 if (newpar && !row->par->GetLayout()) {
728 if (newpar->FirstPhysicalPar()->noindent)
731 parindent = textclasslist
732 .Style(buffer->params.textclass,
733 newpar->GetLayout()).parindent;
738 LyXFont labelfont = GetFont(row->par, -2);
739 switch (layout.margintype) {
741 if (!layout.leftmargin.empty()) {
742 x += lyxfont::signedWidth(layout.leftmargin,
744 .TextClass(buffer->params.
748 if (!row->par->GetLabelstring().empty()) {
749 x += lyxfont::signedWidth(layout.labelindent,
751 x += lyxfont::width(row->par->GetLabelstring(), labelfont);
752 x += lyxfont::width(layout.labelsep, labelfont);
756 x += lyxfont::signedWidth(layout.labelindent, labelfont);
757 if (row->pos >= BeginningOfMainBody(row->par)) {
758 if (!row->par->GetLabelWidthString().empty()) {
759 x += lyxfont::width(row->par->GetLabelWidthString(),
761 x += lyxfont::width(layout.labelsep, labelfont);
766 x += lyxfont::signedWidth(layout.leftmargin, textclasslist.TextClass(buffer->params.textclass).defaultfont()) * 4
767 / (row->par->GetDepth() + 4);
769 case MARGIN_FIRST_DYNAMIC:
770 if (layout.labeltype == LABEL_MANUAL) {
771 if (row->pos >= BeginningOfMainBody(row->par)) {
772 x += lyxfont::signedWidth(layout.leftmargin, labelfont);
774 x += lyxfont::signedWidth(layout.labelindent, labelfont);
777 // Special case to fix problems with
779 || (layout.labeltype == LABEL_STATIC
780 && layout.latextype == LATEX_ENVIRONMENT
781 && ! row->par->IsFirstInSequence())) {
782 x += lyxfont::signedWidth(layout.leftmargin, labelfont);
783 } else if (layout.labeltype != LABEL_TOP_ENVIRONMENT
784 && layout.labeltype != LABEL_BIBLIO
785 && layout.labeltype !=
786 LABEL_CENTERED_TOP_ENVIRONMENT) {
787 x += lyxfont::signedWidth(layout.labelindent,
789 x += lyxfont::width(layout.labelsep, labelfont);
790 x += lyxfont::width(row->par->GetLabelstring(), labelfont);
794 case MARGIN_RIGHT_ADDRESS_BOX:
796 // ok, a terrible hack. The left margin depends on the widest
797 // row in this paragraph. Do not care about footnotes, they
798 // are *NOT* allowed in the LaTeX realisation of this layout.
800 // find the first row of this paragraph
801 Row const * tmprow = row;
802 while (tmprow->previous && tmprow->previous->par == row->par)
803 tmprow = tmprow->previous;
805 int minfill = tmprow->fill;
806 while (tmprow->next && tmprow->next->par == row->par) {
807 tmprow = tmprow->next;
808 if (tmprow->fill < minfill)
809 minfill = tmprow->fill;
812 x += lyxfont::signedWidth(layout.leftmargin,
814 .TextClass(buffer->params.textclass)
820 if (row->par->pextra_type == LyXParagraph::PEXTRA_INDENT) {
821 if (!row->par->pextra_widthp.empty()) {
823 atoi(row->par->pextra_widthp.c_str()) / 100;
824 } else if (!row->par->pextra_width.empty()) {
825 int xx = VSpace(row->par->pextra_width).inPixels(owner_);
827 xx = paperwidth * 80 / 100;
829 } else { // should not happen
830 LyXFont font(LyXFont::ALL_SANE);
831 x += lyxfont::width("XXXXXX", font);
835 int align; // wrong type
836 if (row->par->FirstPhysicalPar()->align == LYX_ALIGN_LAYOUT)
837 align = layout.align;
839 align = row->par->FirstPhysicalPar()->align;
841 // set the correct parindent
843 if ((layout.labeltype == LABEL_NO_LABEL
844 || layout.labeltype == LABEL_TOP_ENVIRONMENT
845 || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
846 || (layout.labeltype == LABEL_STATIC
847 && layout.latextype == LATEX_ENVIRONMENT
848 && ! row->par->IsFirstInSequence()))
849 && row->par == row->par->FirstPhysicalPar()
850 && align == LYX_ALIGN_BLOCK
851 && !row->par->noindent
852 && (row->par->layout ||
853 buffer->params.paragraph_separation ==
854 BufferParams::PARSEP_INDENT))
855 x += lyxfont::signedWidth(parindent,
857 .TextClass(buffer->params
860 else if (layout.labeltype == LABEL_BIBLIO) {
861 // ale970405 Right width for bibitems
862 x += bibitemMaxWidth(owner_->painter(),
864 .TextClass(buffer->params
873 int LyXText::RightMargin(Row const * row) const
875 LyXLayout const & layout =
876 textclasslist.Style(buffer->params.textclass,
877 row->par->GetLayout());
879 int x = LYX_PAPER_MARGIN
880 + lyxfont::signedWidth(textclasslist
881 .TextClass(buffer->params.textclass)
884 .TextClass(buffer->params.textclass)
887 if (row->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
888 x += LYX_PAPER_MARGIN / 2;
891 // this is the way, LyX handles the LaTeX-Environments.
892 // I have had this idea very late, so it seems to be a
893 // later added hack and this is true
894 if (row->par->GetDepth()) {
895 // find the next level paragraph
897 LyXParagraph * newpar = row->par;
900 newpar = newpar->FirstPhysicalPar()->Previous();
902 newpar = newpar->FirstPhysicalPar();
903 } while (newpar && newpar->GetDepth() >= row->par->GetDepth()
904 && newpar->footnoteflag == row->par->footnoteflag);
906 // make a corresponding row. Needed to call LeftMargin()
908 // check wether it is a sufficent paragraph
909 if (newpar && newpar->footnoteflag == row->par->footnoteflag
910 && textclasslist.Style(buffer->params.textclass,
914 dummyrow.par = newpar;
916 x = RightMargin(&dummyrow);
918 // this is no longer an error, because this function
919 // is used to clear impossible depths after changing
920 // a layout. Since there is always a redo,
921 // LeftMargin() is always called
922 row->par->FirstPhysicalPar()->depth = 0;
926 //lyxerr << "rightmargin: " << layout->rightmargin << endl;
927 x += lyxfont::signedWidth(layout.rightmargin, textclasslist.TextClass(buffer->params.textclass).defaultfont()) * 4
928 / (row->par->GetDepth() + 4);
933 int LyXText::LabelEnd (Row const * row) const
935 if (textclasslist.Style(buffer->params.textclass,
936 row->par->GetLayout()).margintype
940 tmprow.pos = row->par->Last();
941 return LeftMargin(&tmprow); /* just the beginning
944 return 0; /* LabelEnd is only needed, if the
945 layout fills a flushleft
950 /* table stuff -- begin*/
951 int LyXText::NumberOfCell(LyXParagraph * par,
952 LyXParagraph::size_type pos) const
955 LyXParagraph::size_type tmp_pos = 0;
956 while (tmp_pos < pos) {
957 if (par->IsNewline(tmp_pos))
965 int LyXText::WidthOfCell(LyXParagraph * par,
966 LyXParagraph::size_type & pos) const
969 while (pos < par->Last() && !par->IsNewline(pos)) {
970 w += SingleWidth(par, pos);
973 if (par->IsNewline(pos))
979 bool LyXText::HitInTable(Row * row, int x) const
982 float fill_separator, fill_hfill, fill_label_hfill;
983 if (!row->par->table)
985 PrepareToPrint(row, tmpx, fill_separator,
986 fill_hfill, fill_label_hfill, false);
987 return (x > tmpx && x < tmpx + row->par->table->WidthOfTable());
991 bool LyXText::MouseHitInTable(int x, long y) const
993 Row * row = GetRowNearY(y);
994 return HitInTable(row, x);
998 /* table stuff -- end*/
1001 // get the next breakpoint in a given paragraph
1002 LyXParagraph::size_type
1003 LyXText::NextBreakPoint(Row const * row, int width) const
1005 LyXParagraph * par = row->par;
1006 LyXParagraph::size_type pos = row->pos;
1008 /* table stuff -- begin*/
1010 while (pos < par->size()
1011 && (!par->IsNewline(pos)
1012 || !par->table->IsFirstCell(NumberOfCell(par, pos+1)))) {
1013 if (par->GetChar(pos) == LyXParagraph::META_INSET &&
1014 par->GetInset(pos) && par->GetInset(pos)->display()){
1015 par->GetInset(pos)->display(false);
1021 /* table stuff -- end*/
1023 // position of the last possible breakpoint
1024 // -1 isn't a suitable value, but a flag
1025 LyXParagraph::size_type last_separator = -1;
1026 width -= RightMargin(row);
1028 LyXParagraph::size_type main_body = BeginningOfMainBody(par);
1029 LyXLayout const & layout =
1030 textclasslist.Style(buffer->params.textclass, par->GetLayout());
1031 LyXParagraph::size_type i = pos;
1033 if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1034 /* special code for right address boxes, only newlines count */
1035 while (i < par->Last()) {
1036 if (par->IsNewline(i)) {
1038 i = par->Last() - 1; // this means break
1040 } else if (par->GetChar(i) == LyXParagraph::META_INSET &&
1041 par->GetInset(i) && par->GetInset(i)->display()){
1042 par->GetInset(i)->display(false);
1047 // Last position is an invariant
1048 LyXParagraph::size_type const last =
1050 // this is the usual handling
1051 int x = LeftMargin(row);
1052 while (x < width && i < last) {
1053 char c = par->GetChar(i);
1054 if (IsNewlineChar(c)) {
1056 x = width; // this means break
1057 } else if (c == LyXParagraph::META_INSET &&
1058 par->GetInset(i) && par->GetInset(i)->display()){
1059 // check wether a Display() inset is
1060 // valid here. if not, change it to
1062 if (layout.isCommand()
1063 || (layout.labeltype == LABEL_MANUAL
1064 && i < BeginningOfMainBody(par))){
1065 // display istn't allowd
1066 par->GetInset(i)->display(false);
1067 x += SingleWidth(par, i, c);
1069 // inset is display. So break the line here
1073 if (IsLineSeparatorChar(par->GetChar(i+1)))
1076 last_separator = last; // to avoid extra rows
1078 last_separator = i - 1;
1079 x = width; // this means break
1082 if (IsLineSeparatorChar(c))
1084 x += SingleWidth(par, i, c);
1087 if (i == main_body) {
1088 x += lyxfont::width(layout.labelsep,
1090 if (par->IsLineSeparator(i - 1))
1091 x-= SingleWidth(par, i - 1);
1092 int left_margin = LabelEnd(row);
1093 if (x < left_margin)
1097 // end of paragraph is always a suitable separator
1098 if (i == last && x < width)
1102 // well, if last_separator is still 0, the line isn't breakable.
1103 // don't care and cut simply at the end
1104 if (last_separator < 0) {
1108 // manual labels cannot be broken in LaTeX, do not care
1109 if (main_body && last_separator < main_body)
1110 last_separator = main_body - 1;
1112 return last_separator;
1116 // returns the minimum space a row needs on the screen in pixel
1117 int LyXText::Fill(Row const * row, int paper_width) const
1120 // get the pure distance
1121 LyXParagraph::size_type last = RowLastPrintable(row);
1122 /* table stuff -- begin */
1123 if (row->par->table) {
1124 // for tables FILL does calculate the widthes of each cell in
1126 LyXParagraph::size_type pos = row->pos;
1127 int cell = NumberOfCell(row->par, pos);
1130 row->par->table->SetWidthOfCell(cell,
1131 WidthOfCell(row->par,
1134 } while (pos <= last && !row->par->table->IsFirstCell(cell));
1135 // don't forget the very last table cell without characters
1136 if (cell == row->par->table->GetNumberOfCells() - 1)
1137 row->par->table->SetWidthOfCell(cell,
1138 WidthOfCell(row->par,
1141 return 0; /* width of table cannot be returned since
1142 * we cannot guarantee its correct value at
1145 /* table stuff -- end*/
1147 // special handling of the right address boxes
1148 if (textclasslist.Style(buffer->params.textclass,
1149 row->par->GetLayout()).margintype
1150 == MARGIN_RIGHT_ADDRESS_BOX) {
1151 int tmpfill = row->fill;
1152 row->fill = 0; // the minfill in MarginLeft()
1153 w = LeftMargin(row);
1154 row->fill = tmpfill;
1156 w = LeftMargin(row);
1158 LyXLayout const & layout = textclasslist.Style(buffer->params.textclass,
1159 row->par->GetLayout());
1160 LyXParagraph::size_type main_body =
1161 BeginningOfMainBody(row->par);
1162 LyXParagraph::size_type i = row->pos;
1165 if (main_body > 0 && i == main_body) {
1166 w += lyxfont::width(layout.labelsep, GetFont(row->par, -2));
1167 if (row->par->IsLineSeparator(i - 1))
1168 w -= SingleWidth(row->par, i - 1);
1169 int left_margin = LabelEnd(row);
1170 if (w < left_margin)
1173 w += SingleWidth(row->par, i);
1176 if (main_body > 0 && main_body > last) {
1177 w += lyxfont::width(layout.labelsep, GetFont(row->par, -2));
1178 if (last >= 0 && row->par->IsLineSeparator(last))
1179 w -= SingleWidth(row->par, last);
1180 int left_margin = LabelEnd(row);
1181 if (w < left_margin)
1185 fill = paper_width - w - RightMargin(row);
1190 // returns the minimum space a manual label needs on the screen in pixel
1191 int LyXText::LabelFill(Row const * row) const
1193 LyXParagraph::size_type last = BeginningOfMainBody(row->par) - 1;
1194 // -1 because a label ends either with a space that is in the label,
1195 // or with the beginning of a footnote that is outside the label.
1197 // I don't understand this code in depth, but sometimes "last" is
1198 // less than 0 and this causes a crash. This fix seems to work
1199 // correctly, but I bet the real error is elsewhere. The bug is
1200 // triggered when you have an open footnote in a paragraph
1201 // environment with a manual label. (Asger)
1202 if (last < 0) last = 0;
1204 if (row->par->IsLineSeparator(last)) /* a sepearator at this end
1211 w += SingleWidth(row->par, i);
1216 if (!row->par->labelwidthstring.empty()) {
1217 fill = max(lyxfont::width(row->par->labelwidthstring,
1218 GetFont(row->par, -2)) - w,
1226 // returns the number of separators in the specified row. The separator
1227 // on the very last column doesnt count
1228 int LyXText::NumberOfSeparators(Row const * row) const
1230 int last = RowLast(row);
1231 int p = max(row->pos, BeginningOfMainBody(row->par));
1233 for (; p < last; ++p) {
1234 if (row->par->IsSeparator(p)) {
1242 // returns the number of hfills in the specified row. The LyX-Hfill is
1243 // a LaTeX \hfill so that the hfills at the beginning and at the end were
1244 // ignored. This is *MUCH* more usefull than not to ignore!
1245 int LyXText::NumberOfHfills(Row const * row) const
1247 int last = RowLast(row);
1248 int first = row->pos;
1249 if (first) { /* hfill *DO* count at the beginning
1251 while(first <= last && row->par->IsHfill(first))
1255 first = max(first, BeginningOfMainBody(row->par));
1257 for (int p = first; p <= last; ++p) { // last, because the end is ignored!
1258 if (row->par->IsHfill(p)) {
1266 // like NumberOfHfills, but only those in the manual label!
1267 int LyXText::NumberOfLabelHfills(Row const * row) const
1269 LyXParagraph::size_type last = RowLast(row);
1270 LyXParagraph::size_type first = row->pos;
1271 if (first) { /* hfill *DO* count at the beginning
1273 while(first < last && row->par->IsHfill(first))
1277 last = min(last, BeginningOfMainBody(row->par));
1279 for (LyXParagraph::size_type p = first;
1280 p < last; ++p) { // last, because the end is ignored!
1281 if (row->par->IsHfill(p)) {
1289 // returns true, if a expansion is needed.
1290 // Rules are given by LaTeX
1291 bool LyXText::HfillExpansion(Row const * row_ptr,
1292 LyXParagraph::size_type pos) const
1294 // by the way, is it a hfill?
1295 if (!row_ptr->par->IsHfill(pos))
1298 // at the end of a row it does not count
1299 if (pos >= RowLast(row_ptr))
1302 // at the beginning of a row it does not count, if it is not
1303 // the first row of a paragaph
1307 // in some labels it does not count
1308 if (textclasslist.Style(buffer->params.textclass,
1309 row_ptr->par->GetLayout()).margintype
1311 && pos < BeginningOfMainBody(row_ptr->par))
1314 // if there is anything between the first char of the row and
1315 // the sepcified position that is not a newline and not a hfill,
1316 // the hfill will count, otherwise not
1317 LyXParagraph::size_type i = row_ptr->pos;
1318 while (i < pos && (row_ptr->par->IsNewline(i)
1319 || row_ptr->par->IsHfill(i)))
1326 void LyXText::SetHeightOfRow(Row * row_ptr) const
1328 /* get the maximum ascent and the maximum descent */
1330 float layoutasc = 0;
1331 float layoutdesc = 0;
1336 /* this must not happen before the currentrow for clear reasons.
1337 so the trick is just to set the current row onto this row */
1339 GetRow(row_ptr->par, row_ptr->pos, unused_y);
1341 /* ok , let us initialize the maxasc and maxdesc value.
1342 * This depends in LaTeX of the font of the last character
1343 * in the paragraph. The hack below is necessary because
1344 * of the possibility of open footnotes */
1346 /* Correction: only the fontsize count. The other properties
1347 are taken from the layoutfont. Nicer on the screen :) */
1349 LyXParagraph * par = row_ptr->par->LastPhysicalPar();
1350 LyXParagraph * firstpar = row_ptr->par->FirstPhysicalPar();
1352 LyXLayout const & layout = textclasslist.Style(buffer->params.textclass,
1353 firstpar->GetLayout());
1355 LyXFont font = GetFont(par, par->Last() - 1);
1356 LyXFont::FONT_SIZE size = font.size();
1357 font = GetFont(par, -1);
1360 LyXFont labelfont = GetFont(par, -2);
1362 float spacing_val = 1.0;
1363 if (!row_ptr->par->spacing.isDefault()) {
1364 spacing_val = row_ptr->par->spacing.getValue();
1366 spacing_val = buffer->params.spacing.getValue();
1368 //lyxerr << "spacing_val = " << spacing_val << endl;
1370 int maxasc = int(lyxfont::maxAscent(font) *
1371 layout.spacing.getValue() *
1373 int maxdesc = int(lyxfont::maxDescent(font) *
1374 layout.spacing.getValue() *
1377 int pos_end = RowLast(row_ptr);
1381 // Check if any insets are larger
1382 for (pos = row_ptr->pos; pos <= pos_end; ++pos) {
1383 if (row_ptr->par->GetChar(pos) == LyXParagraph::META_INSET) {
1384 tmpfont = GetFont(row_ptr->par, pos);
1385 tmpinset = row_ptr->par->GetInset(pos);
1387 asc = tmpinset->ascent(owner_->painter(), tmpfont);
1388 desc = tmpinset->descent(owner_->painter(), tmpfont);
1397 // Check if any custom fonts are larger (Asger)
1398 // This is not completely correct, but we can live with the small,
1399 // cosmetic error for now.
1400 LyXFont::FONT_SIZE maxsize = row_ptr->par->HighestFontInRange(row_ptr->pos,
1402 if (maxsize > font.size()) {
1403 font.setSize(maxsize);
1405 asc = lyxfont::maxAscent(font);
1406 desc = lyxfont::maxDescent(font);
1413 /* table stuff -- begin*/
1414 if (row_ptr->par->table){
1415 // stretch the rows a bit
1419 /* table stuff -- end*/
1421 // This is nicer with box insets:
1425 row_ptr->ascent_of_text = maxasc;
1427 /* is it a top line? */
1428 if (row_ptr->pos == 0
1429 && row_ptr->par == firstpar) {
1431 /* some parksips VERY EASY IMPLEMENTATION */
1432 if (buffer->params.paragraph_separation == BufferParams::PARSEP_SKIP) {
1433 if (layout.isParagraph()
1434 && firstpar->GetDepth() == 0
1435 && firstpar->Previous())
1436 maxasc += buffer->params.getDefSkip().inPixels(owner_);
1437 else if (firstpar->Previous()
1438 && textclasslist.Style(buffer->params.textclass,
1439 firstpar->Previous()->GetLayout()).isParagraph()
1440 && firstpar->Previous()->GetDepth() == 0)
1441 // is it right to use defskip here too? (AS)
1442 maxasc += buffer->params.getDefSkip().inPixels(owner_);
1445 /* the paper margins */
1446 if (!row_ptr->par->previous)
1447 maxasc += LYX_PAPER_MARGIN;
1449 /* add the vertical spaces, that the user added */
1450 if (firstpar->added_space_top.kind() != VSpace::NONE)
1451 maxasc += int(firstpar->added_space_top.inPixels(owner_));
1453 /* do not forget the DTP-lines!
1454 * there height depends on the font of the nearest character */
1455 if (firstpar->line_top)
1456 maxasc += 2 * lyxfont::ascent('x', GetFont(firstpar, 0));
1458 /* and now the pagebreaks */
1459 if (firstpar->pagebreak_top)
1460 maxasc += 3 * DefaultHeight();
1462 /* this is special code for the chapter, since the label of this
1463 * layout is printed in an extra row */
1464 if (layout.labeltype == LABEL_COUNTER_CHAPTER
1465 && buffer->params.secnumdepth >= 0) {
1466 float spacing_val = 1.0;
1467 if (!row_ptr->par->spacing.isDefault()) {
1468 spacing_val = row_ptr->par->spacing.getValue();
1470 spacing_val = buffer->params.spacing.getValue();
1473 labeladdon = int(lyxfont::maxDescent(labelfont) *
1474 layout.spacing.getValue() *
1476 + int(lyxfont::maxAscent(labelfont) *
1477 layout.spacing.getValue() *
1481 /* special code for the top label */
1482 if ((layout.labeltype == LABEL_TOP_ENVIRONMENT
1483 || layout.labeltype == LABEL_BIBLIO
1484 || layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1485 && row_ptr->par->IsFirstInSequence()
1486 && !row_ptr->par->GetLabelstring().empty()) {
1487 float spacing_val = 1.0;
1488 if (!row_ptr->par->spacing.isDefault()) {
1489 spacing_val = row_ptr->par->spacing.getValue();
1491 spacing_val = buffer->params.spacing.getValue();
1495 (lyxfont::maxAscent(labelfont) *
1496 layout.spacing.getValue() *
1498 +(lyxfont::maxDescent(labelfont) *
1499 layout.spacing.getValue() *
1501 + layout.topsep * DefaultHeight()
1502 + layout.labelbottomsep * DefaultHeight());
1505 /* and now the layout spaces, for example before and after a section,
1506 * or between the items of a itemize or enumerate environment */
1508 if (!firstpar->pagebreak_top) {
1509 LyXParagraph * prev = row_ptr->par->Previous();
1511 prev = row_ptr->par->DepthHook(row_ptr->par->GetDepth());
1512 if (prev && prev->GetLayout() == firstpar->GetLayout()
1513 && prev->GetDepth() == firstpar->GetDepth()
1514 && prev->GetLabelWidthString() == firstpar->GetLabelWidthString())
1516 layoutasc = (layout.itemsep * DefaultHeight());
1518 else if (row_ptr->previous) {
1519 tmptop = layout.topsep;
1521 if (row_ptr->previous->par->GetDepth() >= row_ptr->par->GetDepth())
1522 tmptop-= textclasslist.Style(buffer->params.textclass, row_ptr->previous->par->GetLayout()).bottomsep;
1525 layoutasc = (tmptop * DefaultHeight());
1527 else if (row_ptr->par->line_top){
1528 tmptop = layout.topsep;
1531 layoutasc = (tmptop * DefaultHeight());
1534 prev = row_ptr->par->DepthHook(row_ptr->par->GetDepth()-1);
1536 maxasc += int(textclasslist.Style(buffer->params.textclass,
1537 prev->GetLayout()).parsep * DefaultHeight());
1540 if (firstpar->Previous()
1541 && firstpar->Previous()->GetDepth() == 0
1542 && firstpar->Previous()->GetLayout() != firstpar->GetLayout()) {
1545 else if (firstpar->Previous()){
1546 maxasc += int(layout.parsep * DefaultHeight());
1552 /* is it a bottom line? */
1553 if (row_ptr->par->ParFromPos(RowLast(row_ptr) + 1) == par
1554 && (!row_ptr->next || row_ptr->next->par != row_ptr->par)) {
1556 /* the paper margins */
1558 maxdesc += LYX_PAPER_MARGIN;
1560 /* add the vertical spaces, that the user added */
1561 if (firstpar->added_space_bottom.kind() != VSpace::NONE)
1562 maxdesc += int(firstpar->added_space_bottom.inPixels(owner_));
1564 /* do not forget the DTP-lines!
1565 * there height depends on the font of the nearest character */
1566 if (firstpar->line_bottom)
1567 maxdesc += 2 * lyxfont::ascent('x', GetFont(par, par->Last() - 1));
1569 /* and now the pagebreaks */
1570 if (firstpar->pagebreak_bottom)
1571 maxdesc += 3 * DefaultHeight();
1573 /* and now the layout spaces, for example before and after a section,
1574 * or between the items of a itemize or enumerate environment */
1575 if (!firstpar->pagebreak_bottom && row_ptr->par->Next()) {
1576 LyXParagraph * nextpar = row_ptr->par->Next();
1577 LyXParagraph * comparepar = row_ptr->par;
1581 if (comparepar->GetDepth() > nextpar->GetDepth()) {
1582 usual = (textclasslist.Style(buffer->params.textclass, comparepar->GetLayout()).bottomsep * DefaultHeight());
1583 comparepar = comparepar->DepthHook(nextpar->GetDepth());
1584 if (comparepar->GetLayout()!= nextpar->GetLayout()
1585 || nextpar->GetLabelWidthString() !=
1586 comparepar->GetLabelWidthString())
1587 unusual = (textclasslist.Style(buffer->params.textclass, comparepar->GetLayout()).bottomsep * DefaultHeight());
1589 if (unusual > usual)
1590 layoutdesc = unusual;
1594 else if (comparepar->GetDepth() == nextpar->GetDepth()) {
1596 if (comparepar->GetLayout()!= nextpar->GetLayout()
1597 || nextpar->GetLabelWidthString() !=
1598 comparepar->GetLabelWidthString())
1599 layoutdesc = int(textclasslist.Style(buffer->params.textclass, comparepar->GetLayout()).bottomsep * DefaultHeight());
1604 /* incalculate the layout spaces */
1605 maxasc += int(layoutasc * 2 / (2 + firstpar->GetDepth()));
1606 maxdesc += int(layoutdesc * 2 / (2 + firstpar->GetDepth()));
1608 /* table stuff -- begin*/
1609 if (row_ptr->par->table){
1610 maxasc += row_ptr->par->table->
1611 AdditionalHeight(NumberOfCell(row_ptr->par, row_ptr->pos));
1613 /* table stuff -- end*/
1615 /* calculate the new height of the text */
1616 height -= row_ptr->height;
1618 row_ptr->height = maxasc + maxdesc + labeladdon;
1619 row_ptr->baseline = maxasc + labeladdon;
1621 height += row_ptr->height;
1625 /* Appends the implicit specified paragraph behind the specified row,
1626 * start at the implicit given position */
1627 void LyXText::AppendParagraph(Row * row) const
1629 bool not_ready = true;
1631 // The last character position of a paragraph is an invariant so we can
1632 // safely get it here. (Asger)
1633 int lastposition = row->par->Last();
1636 // Get the next breakpoint
1637 int z = NextBreakPoint(row, paperwidth);
1641 // Insert the new row
1642 if (z < lastposition) {
1644 InsertRow(row, row->par, z);
1651 // Set the dimensions of the row
1652 tmprow->fill = Fill(tmprow, paperwidth);
1653 SetHeightOfRow(tmprow);
1655 } while (not_ready);
1659 void LyXText::BreakAgain(Row * row) const
1661 bool not_ready = true;
1664 /* get the next breakpoint */
1665 LyXParagraph::size_type z =
1666 NextBreakPoint(row, paperwidth);
1669 if (z < row->par->Last() ) {
1670 if (!row->next || (row->next && row->next->par != row->par)) {
1673 InsertRow(row, row->par, z);
1680 not_ready = false; // the rest will not change
1686 /* if there are some rows too much, delete them */
1687 /* only if you broke the whole paragraph! */
1688 Row * tmprow2 = row;
1689 while (tmprow2->next && tmprow2->next->par == row->par) {
1690 tmprow2 = tmprow2->next;
1692 while (tmprow2 != row) {
1693 tmprow2 = tmprow2->previous;
1694 RemoveRow(tmprow2->next);
1699 /* set the dimensions of the row */
1700 tmprow->fill = Fill(tmprow, paperwidth);
1701 SetHeightOfRow(tmprow);
1702 } while (not_ready);
1706 /* this is just a little changed version of break again */
1707 void LyXText::BreakAgainOneRow(Row * row)
1709 /* get the next breakpoint */
1710 LyXParagraph::size_type z = NextBreakPoint(row, paperwidth);
1713 if (z < row->par->Last() ) {
1714 if (!row->next || (row->next && row->next->par != row->par)) {
1715 /* insert a new row */
1717 InsertRow(row, row->par, z);
1729 /* if there are some rows too much, delete them */
1730 /* only if you broke the whole paragraph! */
1731 Row * tmprow2 = row;
1732 while (tmprow2->next && tmprow2->next->par == row->par) {
1733 tmprow2 = tmprow2->next;
1735 while (tmprow2 != row) {
1736 tmprow2 = tmprow2->previous;
1737 RemoveRow(tmprow2->next);
1741 /* set the dimensions of the row */
1742 tmprow->fill = Fill(tmprow, paperwidth);
1743 SetHeightOfRow(tmprow);
1747 void LyXText::BreakParagraph(char keep_layout)
1749 LyXLayout const & layout = textclasslist.Style(buffer->params.textclass,
1750 cursor.par->GetLayout());
1752 /* table stuff -- begin */
1753 if (cursor.par->table) {
1754 // breaking of tables is only allowed at the beginning or the end */
1755 if (cursor.pos && cursor.pos < cursor.par->size() &&
1756 !cursor.par->table->ShouldBeVeryLastCell(NumberOfCell(cursor.par, cursor.pos)))
1757 return; // no breaking of tables allowed
1759 /* table stuff -- end */
1761 // this is only allowed, if the current paragraph is not empty or caption
1762 if ((cursor.par->Last() <= 0 && !cursor.par->IsDummy())
1764 layout.labeltype!= LABEL_SENSITIVE)
1767 SetUndo(Undo::INSERT,
1768 cursor.par->ParFromPos(cursor.pos)->previous,
1769 cursor.par->ParFromPos(cursor.pos)->next);
1771 /* table stuff -- begin */
1772 if (cursor.par->table) {
1773 int cell = NumberOfCell(cursor.par, cursor.pos);
1774 if (cursor.par->table->ShouldBeVeryLastCell(cell))
1775 SetCursor(cursor.par, cursor.par->size());
1777 /* table stuff -- end */
1779 // please break always behind a space
1780 if (cursor.pos < cursor.par->Last()
1781 && cursor.par->IsLineSeparator(cursor.pos))
1784 // break the paragraph
1788 keep_layout = layout.isEnvironment();
1789 cursor.par->BreakParagraph(cursor.pos, keep_layout);
1791 /* table stuff -- begin */
1792 if (cursor.par->table){
1793 // the table should stay with the contents
1795 cursor.par->Next()->table = cursor.par->table;
1796 cursor.par->table = 0;
1799 /* table stuff -- end */
1801 // well this is the caption hack since one caption is really enough
1802 if (layout.labeltype == LABEL_SENSITIVE) {
1804 cursor.par->SetLayout(0); // set to standard-layout
1806 cursor.par->Next()->SetLayout(0); // set to standard-layout
1809 /* if the cursor is at the beginning of a row without prior newline,
1811 * This touches only the screen-update. Otherwise we would may have
1812 * an empty row on the screen */
1813 if (cursor.pos && !cursor.row->par->IsNewline(cursor.row->pos -1) &&
1814 cursor.row->pos == cursor.pos) {
1818 status = LyXText::NEED_MORE_REFRESH;
1819 refresh_row = cursor.row;
1820 refresh_y = cursor.y - cursor.row->baseline;
1822 // Do not forget the special right address boxes
1823 if (layout.margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1824 while (refresh_row->previous &&
1825 refresh_row->previous->par == refresh_row->par) {
1826 refresh_row = refresh_row->previous;
1827 refresh_y -= refresh_row->height;
1830 RemoveParagraph(cursor.row);
1832 // set the dimensions of the cursor row
1833 cursor.row->fill = Fill(cursor.row, paperwidth);
1835 SetHeightOfRow(cursor.row);
1837 while (!cursor.par->Next()->table && cursor.par->Next()->Last()
1838 && cursor.par->Next()->IsNewline(0))
1839 cursor.par->Next()->Erase(0);
1841 InsertParagraph(cursor.par->Next(), cursor.row);
1843 UpdateCounters(cursor.row->previous);
1845 /* This check is necessary. Otherwise the new empty paragraph will
1846 * be deleted automatically. And it is more friendly for the user! */
1848 SetCursor(cursor.par->Next(), 0);
1850 SetCursor(cursor.par, 0);
1852 if (cursor.row->next)
1853 BreakAgain(cursor.row->next);
1859 void LyXText::OpenFootnote()
1861 LyXParagraph * endpar,* tmppar;
1864 LyXParagraph * par = cursor.par->ParFromPos(cursor.pos);
1866 /* if there is no footnote in this paragraph, just return. */
1868 || par->next->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
1871 /* ok, move the cursor right before the footnote */
1873 /* just a little faster than using CursorRight() */
1874 for (cursor.pos = 0;
1875 cursor.par->ParFromPos(cursor.pos) != par; cursor.pos++);
1876 /* now the cursor is at the beginning of the physical par */
1877 SetCursor(cursor.par,
1878 cursor.pos + cursor.par->ParFromPos(cursor.pos)->size());
1880 /* the cursor must be exactly before the footnote */
1881 par = cursor.par->ParFromPos(cursor.pos);
1883 status = LyXText::NEED_MORE_REFRESH;
1884 refresh_row = cursor.row;
1885 refresh_y = cursor.y - cursor.row->baseline;
1887 tmppar = cursor.par;
1888 endpar = cursor.par->Next();
1891 tmppar->OpenFootnote(cursor.pos);
1892 RemoveParagraph(row);
1893 /* set the dimensions of the cursor row */
1894 row->fill = Fill(row, paperwidth);
1895 SetHeightOfRow(row);
1896 // CHECK See comment on top of text.C
1897 tmppar = tmppar->Next();
1899 while (tmppar != endpar) {
1901 InsertParagraph(tmppar, row);
1902 while (row->next && row->next->par == tmppar)
1904 tmppar = tmppar->Next();
1907 SetCursor(par->next, 0);
1908 sel_cursor = cursor;
1912 /* table stuff -- begin*/
1914 void LyXText::TableFeatures(int feature, string const & val) const
1916 if (!cursor.par->table)
1917 return; /* this should never happen */
1919 int actCell = NumberOfCell(cursor.par, cursor.pos);
1920 SetUndo(Undo::FINISH,
1921 cursor.par->ParFromPos(cursor.pos)->previous,
1922 cursor.par->ParFromPos(cursor.pos)->next);
1925 case LyXTable::SET_PWIDTH:
1926 cursor.par->table->SetPWidth(actCell, val);
1928 case LyXTable::SET_SPECIAL_COLUMN:
1929 case LyXTable::SET_SPECIAL_MULTI:
1930 cursor.par->table->SetAlignSpecial(actCell, val, feature);
1939 void LyXText::TableFeatures(int feature) const
1942 int setAlign = LYX_ALIGN_LEFT;
1946 if (!cursor.par->table)
1947 return; /* this should never happen */
1949 int actCell = NumberOfCell(cursor.par, cursor.pos);
1950 SetUndo(Undo::FINISH,
1951 cursor.par->ParFromPos(cursor.pos)->previous,
1952 cursor.par->ParFromPos(cursor.pos)->next);
1955 case LyXTable::ALIGN_LEFT:
1956 setAlign= LYX_ALIGN_LEFT;
1958 case LyXTable::ALIGN_RIGHT:
1959 setAlign= LYX_ALIGN_RIGHT;
1961 case LyXTable::ALIGN_CENTER:
1962 setAlign= LYX_ALIGN_CENTER;
1968 case LyXTable::APPEND_ROW: {
1969 LyXParagraph::size_type pos = cursor.pos;
1971 /* move to the next row */
1972 int cell_org = actCell;
1973 int cell = cell_org;
1975 // if there is a ContRow following this row I have to add
1976 // the row after the ContRow's
1977 if ((pos < cursor.par->Last()) &&
1978 cursor.par->table->RowHasContRow(cell_org)) {
1979 while((pos < cursor.par->Last()) &&
1980 !cursor.par->table->IsContRow(cell)) {
1981 while (pos < cursor.par->Last() &&
1982 !cursor.par->IsNewline(pos))
1984 if (pos < cursor.par->Last())
1988 while((pos < cursor.par->Last()) &&
1989 cursor.par->table->IsContRow(cell)) {
1990 while (pos < cursor.par->Last() &&
1991 !cursor.par->IsNewline(pos))
1993 if (pos < cursor.par->Last())
1998 if (pos < cursor.par->Last())
2001 while (pos < cursor.par->Last() &&
2002 (cell == cell_org || !cursor.par->table->IsFirstCell(cell))){
2003 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
2005 if (pos < cursor.par->Last())
2010 /* insert the new cells */
2011 int number = cursor.par->table->NumberOfCellsInRow(cell_org);
2012 Language const * lang = cursor.par->getParLanguage();
2013 LyXFont font(LyXFont::ALL_INHERIT,lang);
2014 for (int i = 0; i < number; ++i) {
2015 cursor.par->InsertChar(pos, LyXParagraph::META_NEWLINE);
2016 cursor.par->SetFont(pos, font);
2019 /* append the row into the table */
2020 cursor.par->table->AppendRow(cell_org);
2024 case LyXTable::APPEND_CONT_ROW: {
2025 LyXParagraph::size_type pos = cursor.pos;
2026 /* move to the next row */
2027 int cell_org = actCell;
2028 int cell = cell_org;
2030 // if there is already a controw but not for this cell
2031 // the AppendContRow sets only the right values but does
2032 // not actually add a row
2033 if (cursor.par->table->RowHasContRow(cell_org) &&
2034 (cursor.par->table->CellHasContRow(cell_org)<0)) {
2035 cursor.par->table->AppendContRow(cell_org);
2039 while (pos < cursor.par->Last() &&
2041 || !cursor.par->table->IsFirstCell(cell))){
2042 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
2044 if (pos < cursor.par->Last())
2049 /* insert the new cells */
2050 int number = cursor.par->table->NumberOfCellsInRow(cell_org);
2051 Language const * lang = cursor.par->getParLanguage();
2052 LyXFont font(LyXFont::ALL_INHERIT,lang);
2053 for (int i = 0; i < number; ++i) {
2054 cursor.par->InsertChar(pos, LyXParagraph::META_NEWLINE);
2055 cursor.par->SetFont(pos, font);
2058 /* append the row into the table */
2059 cursor.par->table->AppendContRow(cell_org);
2063 case LyXTable::APPEND_COLUMN: {
2064 LyXParagraph::size_type pos = 0;
2065 int cell_org = actCell;
2067 Language const * lang = cursor.par->getParLanguage();
2068 LyXFont font(LyXFont::ALL_INHERIT,lang);
2070 if (pos && (cursor.par->IsNewline(pos-1))){
2071 if (cursor.par->table->AppendCellAfterCell(cell_org, cell)) {
2072 cursor.par->InsertChar(pos, LyXParagraph::META_NEWLINE);
2073 cursor.par->SetFont(pos, font);
2074 if (pos <= cursor.pos)
2081 } while (pos <= cursor.par->Last());
2082 /* remember that the very last cell doesn't end with a newline.
2083 This saves one byte memory per table ;-) */
2084 if (cursor.par->table->AppendCellAfterCell(cell_org, cell)) {
2085 LyXParagraph::size_type last = cursor.par->Last();
2086 cursor.par->InsertChar(last, LyXParagraph::META_NEWLINE);
2087 cursor.par->SetFont(last, font);
2090 /* append the column into the table */
2091 cursor.par->table->AppendColumn(cell_org);
2096 case LyXTable::DELETE_ROW:
2097 if (owner_->the_locking_inset)
2098 owner_->unlockInset(owner_->the_locking_inset);
2099 RemoveTableRow(&cursor);
2103 case LyXTable::DELETE_COLUMN: {
2104 LyXParagraph::size_type pos = 0;
2105 int cell_org = actCell;
2107 if (owner_->the_locking_inset)
2108 owner_->unlockInset(owner_->the_locking_inset);
2110 if (!pos || (cursor.par->IsNewline(pos-1))){
2111 if (cursor.par->table->DeleteCellIfColumnIsDeleted(cell, cell_org)){
2113 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
2114 cursor.par->Erase(pos);
2115 if (pos < cursor.par->Last())
2116 cursor.par->Erase(pos);
2118 cursor.par->Erase(pos - 1); // the missing newline at the end of a table
2119 --pos; // because of pos++ below
2124 } while (pos <= cursor.par->Last());
2126 /* delete the column from the table */
2127 cursor.par->table->DeleteColumn(cell_org);
2129 /* set the cursor to the beginning of the table, where else? */
2134 case LyXTable::TOGGLE_LINE_TOP:
2135 lineSet = !cursor.par->table->TopLine(actCell);
2137 cursor.par->table->SetTopLine(actCell, lineSet);
2139 LyXParagraph::size_type i;
2141 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i){
2142 if ((n = NumberOfCell(sel_start_cursor.par, i)) != m) {
2143 cursor.par->table->SetTopLine(n, lineSet);
2151 case LyXTable::TOGGLE_LINE_BOTTOM:
2152 lineSet = !cursor.par->table->BottomLine(actCell);
2154 cursor.par->table->SetBottomLine(actCell, lineSet);
2156 LyXParagraph::size_type i;
2158 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i) {
2159 if ((n = NumberOfCell(sel_start_cursor.par, i)) != m) {
2160 cursor.par->table->SetBottomLine(n, lineSet);
2168 case LyXTable::TOGGLE_LINE_LEFT:
2169 lineSet = !cursor.par->table->LeftLine(actCell);
2171 cursor.par->table->SetLeftLine(actCell, lineSet);
2173 LyXParagraph::size_type i;
2175 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i){
2176 if ((n= NumberOfCell(sel_start_cursor.par, i)) != m) {
2177 cursor.par->table->SetLeftLine(n, lineSet);
2185 case LyXTable::TOGGLE_LINE_RIGHT:
2186 lineSet = !cursor.par->table->RightLine(actCell);
2188 cursor.par->table->SetRightLine(actCell, lineSet);
2191 LyXParagraph::size_type i = sel_start_cursor.pos;
2192 for (; i <= sel_end_cursor.pos; ++i) {
2193 if ((n= NumberOfCell(sel_start_cursor.par, i)) != m) {
2194 cursor.par->table->SetRightLine(n, lineSet);
2202 case LyXTable::ALIGN_LEFT:
2203 case LyXTable::ALIGN_RIGHT:
2204 case LyXTable::ALIGN_CENTER:
2206 cursor.par->table->SetAlignment(actCell, setAlign);
2209 LyXParagraph::size_type i = sel_start_cursor.pos;
2210 for (; i <= sel_end_cursor.pos; ++i) {
2211 if ((n= NumberOfCell(sel_start_cursor.par, i)) != m) {
2212 cursor.par->table->SetAlignment(n, setAlign);
2220 case LyXTable::DELETE_TABLE:
2221 SetCursorIntern(cursor.par, 0);
2222 delete cursor.par->table;
2223 cursor.par->table = 0;
2224 // temporary: Should put table in simple_cut_buffer (with before and after
2225 // dummy-paragraph !!
2226 // not necessar anymore with UNDO :)
2227 for (LyXParagraph::size_type i =
2228 cursor.par->size() - 1; i >= 0; --i)
2229 cursor.par->Erase(i);
2233 case LyXTable::MULTICOLUMN: {
2235 // check wether we are completly in a multicol
2236 int multicol = cursor.par->table->IsMultiColumn(actCell);
2237 if (multicol && selection && sel_start_cursor.row == sel_end_cursor.row){
2238 multicol = NumberOfCell(sel_start_cursor.par, sel_start_cursor.pos)
2239 == NumberOfCell(sel_end_cursor.par, sel_end_cursor.pos);
2243 int newlines = cursor.par->table->UnsetMultiColumn(actCell);
2244 LyXParagraph::size_type pos = cursor.pos;
2245 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
2247 for (; newlines; --newlines)
2248 cursor.par->InsertChar(pos, LyXParagraph::META_NEWLINE);
2253 // selection must be in one row (or no selection)
2255 cursor.par->table->SetMultiColumn(NumberOfCell(cursor.par,
2262 if (sel_start_cursor.row == sel_end_cursor.row){
2263 LyXParagraph::size_type i;
2265 for (i = sel_start_cursor.pos;
2266 i < sel_end_cursor.pos; ++i){
2267 if (sel_start_cursor.par->IsNewline(i)){
2268 sel_start_cursor.par->Erase(i);
2269 // check for double-blanks
2270 if ((i && !sel_start_cursor.par->IsLineSeparator(i-1))
2272 (i < sel_start_cursor.par->Last()
2273 && !sel_start_cursor.par->IsLineSeparator(i)))
2274 sel_start_cursor.par->InsertChar(i, ' ');
2276 sel_end_cursor.pos--;
2283 SetMultiColumn(NumberOfCell(sel_start_cursor.par,
2284 sel_start_cursor.pos),
2286 cursor.pos = sel_start_cursor.pos;
2291 WriteAlert(_("Impossible Operation!"),
2292 _("Multicolumns can only be horizontally."),
2299 case LyXTable::SET_ALL_LINES:
2301 case LyXTable::UNSET_ALL_LINES:
2303 cursor.par->table->SetAllLines(NumberOfCell(cursor.par,
2307 LyXParagraph::size_type i;
2309 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i) {
2310 if ((n= NumberOfCell(sel_start_cursor.par, i)) != m) {
2311 cursor.par->table->SetAllLines(n, setLines);
2318 case LyXTable::SET_LONGTABLE:
2319 cursor.par->table->SetLongTable(true);
2321 case LyXTable::UNSET_LONGTABLE:
2322 cursor.par->table->SetLongTable(false);
2324 case LyXTable::SET_ROTATE_TABLE:
2325 cursor.par->table->SetRotateTable(true);
2327 case LyXTable::UNSET_ROTATE_TABLE:
2328 cursor.par->table->SetRotateTable(false);
2330 case LyXTable::SET_ROTATE_CELL:
2332 cursor.par->table->SetRotateCell(actCell, true);
2334 LyXParagraph::size_type i;
2336 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i){
2337 if ((n = NumberOfCell(sel_start_cursor.par, i)) != m) {
2338 cursor.par->table->SetRotateCell(n, true);
2344 case LyXTable::UNSET_ROTATE_CELL:
2346 cursor.par->table->SetRotateCell(actCell, false);
2349 LyXParagraph::size_type i = sel_start_cursor.pos;
2350 for (; i <= sel_end_cursor.pos; ++i) {
2351 if ((n= NumberOfCell(sel_start_cursor.par, i)) != m) {
2352 cursor.par->table->SetRotateCell(n, false);
2358 case LyXTable::SET_LINEBREAKS:
2359 what = !cursor.par->table->Linebreaks(cursor.par->table->FirstVirtualCell(actCell));
2361 cursor.par->table->SetLinebreaks(actCell, what);
2363 LyXParagraph::size_type i;
2365 for (i = sel_start_cursor.pos; i <= sel_end_cursor.pos; ++i) {
2366 if ((n = NumberOfCell(sel_start_cursor.par, i)) != m) {
2367 cursor.par->table->SetLinebreaks(n, what);
2373 case LyXTable::SET_LTFIRSTHEAD:
2374 cursor.par->table->SetLTHead(actCell, true);
2376 case LyXTable::SET_LTHEAD:
2377 cursor.par->table->SetLTHead(actCell, false);
2379 case LyXTable::SET_LTFOOT:
2380 cursor.par->table->SetLTFoot(actCell, false);
2382 case LyXTable::SET_LTLASTFOOT:
2383 cursor.par->table->SetLTFoot(actCell, true);
2385 case LyXTable::SET_LTNEWPAGE:
2386 what = !cursor.par->table->LTNewPage(actCell);
2387 cursor.par->table->SetLTNewPage(actCell, what);
2393 void LyXText::InsertCharInTable(char c)
2398 bool jumped_over_space;
2400 /* first check, if there will be two blanks together or a blank at
2401 * the beginning of a paragraph.
2402 * I decided to handle blanks like normal characters, the main
2403 * difference are the special checks when calculating the row.fill
2404 * (blank does not count at the end of a row) and the check here */
2406 LyXFont realtmpfont = real_current_font;
2407 LyXFont rawtmpfont = current_font; /* store the current font.
2408 * This is because of the use
2409 * of cursor movements. The moving
2410 * cursor would refresh the
2413 // Get the font that is used to calculate the baselineskip
2414 LyXParagraph::size_type const lastpos =
2416 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2418 jumped_over_space = false;
2419 if (IsLineSeparatorChar(c)) {
2420 if ((cursor.pos > 0 &&
2421 cursor.par->IsLineSeparator(cursor.pos - 1))
2422 || (cursor.pos > 0 && cursor.par->IsNewline(cursor.pos - 1))
2423 || (cursor.pos == 0 &&
2424 !(cursor.par->Previous()
2425 && cursor.par->Previous()->footnoteflag
2426 == LyXParagraph::OPEN_FOOTNOTE)))
2428 } else if (IsNewlineChar(c)) {
2429 if (!IsEmptyTableCell()) {
2430 TableFeatures(LyXTable::APPEND_CONT_ROW);
2437 y = cursor.y - row->baseline;
2438 if (c != LyXParagraph::META_INSET) /* in this case LyXText::InsertInset
2439 * already inserted the character */
2440 cursor.par->InsertChar(cursor.pos, c);
2441 SetCharFont(cursor.par, cursor.pos, rawtmpfont);
2443 if (!jumped_over_space) {
2444 /* refresh the positions */
2446 while (tmprow->next && tmprow->next->par == row->par) {
2447 tmprow = tmprow->next;
2454 CheckParagraphInTable(cursor.par, cursor.pos);
2456 current_font = rawtmpfont;
2457 real_current_font = realtmpfont;
2459 /* check, whether the last character's font has changed. */
2460 if (cursor.pos && cursor.pos == cursor.par->Last()
2461 && rawparfont != rawtmpfont)
2462 RedoHeightOfParagraph(cursor);
2466 void LyXText::CheckParagraphInTable(LyXParagraph * par,
2467 LyXParagraph::size_type pos)
2470 if (par->GetChar(pos) == LyXParagraph::META_INSET &&
2471 par->GetInset(pos) && par->GetInset(pos)->display()){
2472 par->GetInset(pos)->display(false);
2476 Row * row = GetRow(par, pos, y);
2478 int tmpheight = row->height;
2479 SetHeightOfRow(row);
2481 LyXParagraph::size_type tmp_pos = pos;
2482 /* update the table information */
2483 while (tmp_pos && !par->IsNewline(tmp_pos - 1))
2485 if (par->table->SetWidthOfCell(NumberOfCell(par, pos),
2486 WidthOfCell(par, tmp_pos))) {
2487 LyXCursor tmpcursor = cursor;
2488 SetCursorIntern(par, pos, false);
2489 /* make a complete redraw */
2490 RedoDrawingOfParagraph(cursor);
2494 /* redraw only the row */
2495 LyXCursor tmpcursor = cursor;
2496 SetCursorIntern(par, pos);
2497 //CHECK See comment on top of text.C
2499 refresh_x = cursor.x;
2501 refresh_pos = cursor.pos;
2504 if (tmpheight == row->height)
2505 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2507 status = LyXText::NEED_MORE_REFRESH;
2509 SetCursorIntern(cursor.par, cursor.pos, false, cursor.boundary);
2513 void LyXText::BackspaceInTable()
2515 Row * tmprow, * row;
2518 LyXFont rawtmpfont = current_font;
2519 LyXFont realtmpfont = real_current_font;
2521 // Get the font that is used to calculate the baselineskip
2522 int const lastpos = cursor.par->Last();
2523 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2525 if (cursor.pos == 0) {
2526 /* no pasting of table paragraphs */
2530 /* this is the code for a normal backspace, not pasting
2532 SetUndo(Undo::DELETE,
2533 cursor.par->ParFromPos(cursor.pos)->previous,
2534 cursor.par->ParFromPos(cursor.pos)->next);
2538 /* some insets are undeletable here */
2539 if (cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
2540 if (!cursor.par->GetInset(cursor.pos)->Deletable())
2545 y = cursor.y - row->baseline;
2547 /* some special code when deleting a newline. */
2548 if (cursor.par->IsNewline(cursor.pos)) {
2552 cursor.par->Erase(cursor.pos);
2554 /* refresh the positions */
2556 while (tmprow->next && tmprow->next->par == row->par) {
2557 tmprow = tmprow->next;
2562 CheckParagraphInTable(cursor.par, cursor.pos);
2564 /* check, wether the last characters font has changed. */
2565 if (cursor.pos && cursor.pos == cursor.par->Last()
2566 && rawparfont != rawtmpfont)
2567 RedoHeightOfParagraph(cursor);
2569 /* restore the current font
2570 * That is what a user expects! */
2571 current_font = rawtmpfont;
2572 real_current_font = realtmpfont;
2574 SetCursorIntern(cursor.par, cursor.pos, true, cursor.boundary);
2575 if (IsBoundary(cursor.par, cursor.pos) != cursor.boundary)
2576 SetCursor(cursor.par, cursor.pos, false, !cursor.boundary);
2579 /* table stuff -- end*/
2582 /* just a macro to make some thing easier. */
2583 void LyXText::RedoParagraph() const
2586 RedoParagraphs(cursor, cursor.par->Next());
2587 SetCursorIntern(cursor.par, cursor.pos);
2591 /* insert a character, moves all the following breaks in the
2592 * same Paragraph one to the right and make a rebreak */
2593 void LyXText::InsertChar(char c)
2595 SetUndo(Undo::INSERT,
2596 cursor.par->ParFromPos(cursor.pos)->previous,
2597 cursor.par->ParFromPos(cursor.pos)->next);
2599 /* When the free-spacing option is set for the current layout,
2600 * disable the double-space checking */
2603 textclasslist.Style(buffer->params.textclass,
2604 cursor.row->par->GetLayout()).free_spacing;
2606 /* table stuff -- begin*/
2607 if (cursor.par->table) {
2608 InsertCharInTable(c);
2612 /* table stuff -- end*/
2614 /* First check, if there will be two blanks together or a blank at
2615 the beginning of a paragraph.
2616 I decided to handle blanks like normal characters, the main
2617 difference are the special checks when calculating the row.fill
2618 (blank does not count at the end of a row) and the check here */
2620 // The bug is triggered when we type in a description environment:
2621 // The current_font is not changed when we go from label to main text
2622 // and it should (along with realtmpfont) when we type the space.
2623 // CHECK There is a bug here! (Asger)
2625 LyXFont realtmpfont = real_current_font;
2626 LyXFont rawtmpfont = current_font; /* store the current font.
2627 * This is because of the use
2628 * of cursor movements. The moving
2629 * cursor would refresh the
2632 // Get the font that is used to calculate the baselineskip
2633 LyXParagraph::size_type lastpos = cursor.par->Last();
2634 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2636 bool jumped_over_space = false;
2638 if (!freeSpacing && IsLineSeparatorChar(c)) {
2640 && cursor.par->IsLineSeparator(cursor.pos - 1))
2642 && cursor.par->IsNewline(cursor.pos - 1))
2644 && !(cursor.par->Previous()
2645 && cursor.par->Previous()->footnoteflag
2646 == LyXParagraph::OPEN_FOOTNOTE))) {
2647 if (cursor.pos == 0 )
2648 owner_->owner()->getMiniBuffer()->Set(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial."));
2650 owner_->owner()->getMiniBuffer()->Set(_("You cannot type two spaces this way. Please read the Tutorial."));
2654 } else if (IsNewlineChar(c)) {
2655 if (cursor.par->FirstPhysicalPar() == cursor.par
2656 && cursor.pos <= BeginningOfMainBody(cursor.par)) {
2660 /* No newline at first position
2661 * of a paragraph or behind labels.
2662 * TeX does not allow that. */
2664 if (cursor.pos < cursor.par->Last() &&
2665 cursor.par->IsLineSeparator(cursor.pos))
2666 CursorRightIntern(); // newline always after a blank!
2667 cursor.row->fill = -1; // to force a new break
2670 // the display inset stuff
2671 if (cursor.row->par->GetChar(cursor.row->pos) == LyXParagraph::META_INSET
2672 && cursor.row->par->GetInset(cursor.row->pos)
2673 && cursor.row->par->GetInset(cursor.row->pos)->display())
2674 cursor.row->fill = -1; // to force a new break
2676 // get the cursor row fist
2677 Row * row = cursor.row;
2678 long y = cursor.y - row->baseline;
2679 if (c != LyXParagraph::META_INSET) /* Here case LyXText::InsertInset
2680 * already insertet the character */
2681 cursor.par->InsertChar(cursor.pos, c);
2682 SetCharFont(cursor.par, cursor.pos, rawtmpfont);
2684 if (!jumped_over_space) {
2685 // refresh the positions
2687 while (tmprow->next && tmprow->next->par == row->par) {
2688 tmprow = tmprow->next;
2693 // Is there a break one row above
2694 if ((cursor.par->IsLineSeparator(cursor.pos)
2695 || cursor.par->IsNewline(cursor.pos)
2696 || cursor.row->fill == -1)
2697 && row->previous && row->previous->par == row->par) {
2698 LyXParagraph::size_type z = NextBreakPoint(row->previous,
2700 if ( z >= row->pos) {
2703 // set the dimensions of the row above
2704 row->previous->fill = Fill(row->previous, paperwidth);
2706 SetHeightOfRow(row->previous);
2708 y -= row->previous->height;
2710 refresh_row = row->previous;
2711 status = LyXText::NEED_MORE_REFRESH;
2713 BreakAgainOneRow(row);
2715 current_font = rawtmpfont;
2716 real_current_font = realtmpfont;
2717 SetCursor(cursor.par, cursor.pos + 1, false, cursor.boundary);
2718 /* cursor MUST be in row now */
2720 if (row->next && row->next->par == row->par)
2721 need_break_row = row->next;
2725 // check, wether the last characters font has changed.
2726 if (cursor.pos && cursor.pos == cursor.par->Last()
2727 && rawparfont != rawtmpfont)
2728 RedoHeightOfParagraph(cursor);
2735 /* recalculate the fill of the row */
2736 if (row->fill >= 0) /* needed because a newline
2737 * will set fill to -1. Otherwise
2738 * we would not get a rebreak! */
2739 row->fill = Fill(row, paperwidth);
2740 if (row->fill < 0 ) {
2743 refresh_x = cursor.x;
2744 refresh_pos = cursor.pos;
2745 status = LyXText::NEED_MORE_REFRESH;
2746 BreakAgainOneRow(row);
2747 /* will the cursor be in another row now? */
2748 if (RowLast(row) <= cursor.pos + 1 && row->next) {
2749 if (row->next && row->next->par == row->par)
2753 BreakAgainOneRow(row);
2755 current_font = rawtmpfont;
2756 real_current_font = realtmpfont;
2757 SetCursor(cursor.par, cursor.pos + 1, false, cursor.boundary);
2758 if (row->next && row->next->par == row->par)
2759 need_break_row = row->next;
2764 refresh_x = cursor.x;
2766 refresh_pos = cursor.pos;
2768 int tmpheight = row->height;
2769 SetHeightOfRow(row);
2770 if (tmpheight == row->height)
2771 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2773 status = LyXText::NEED_MORE_REFRESH;
2775 current_font = rawtmpfont;
2776 real_current_font = realtmpfont;
2777 SetCursor(cursor.par, cursor.pos + 1, false, cursor.boundary);
2780 /* check, wether the last characters font has changed. */
2781 if (cursor.pos && cursor.pos == cursor.par->Last()
2782 && rawparfont != rawtmpfont) {
2783 RedoHeightOfParagraph(cursor);
2785 /* now the special right address boxes */
2786 if (textclasslist.Style(buffer->params.textclass,
2787 cursor.par->GetLayout()).margintype
2788 == MARGIN_RIGHT_ADDRESS_BOX) {
2789 RedoDrawingOfParagraph(cursor);
2797 void LyXText::charInserted()
2799 // Here we could call FinishUndo for every 20 characters inserted.
2800 // This is from my experience how emacs does it.
2801 static unsigned int counter = 0;
2810 void LyXText::PrepareToPrint(Row * row, float & x,
2811 float & fill_separator,
2813 float & fill_label_hfill,
2818 float w = row->fill;
2820 fill_label_hfill = 0;
2822 fill_label_hfill = 0;
2824 bool is_rtl = row->par->isRightToLeftPar();
2827 x = RightMargin(row);
2828 if (row->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
2829 LyXFont font(LyXFont::ALL_SANE);
2830 font.setSize(LyXFont::SIZE_SMALL);
2831 x += lyxfont::width("Mwide-figM", font);
2834 x = LeftMargin(row);
2836 /* is there a manual margin with a manual label */
2837 if (textclasslist.Style(buffer->params.textclass,
2838 row->par->GetLayout()).margintype == MARGIN_MANUAL
2839 && textclasslist.Style(buffer->params.textclass,
2840 row->par->GetLayout()).labeltype == LABEL_MANUAL) {
2842 nlh = NumberOfLabelHfills(row) + 1; /* one more since labels
2843 * are left aligned*/
2844 if (nlh && !row->par->GetLabelWidthString().empty()) {
2845 fill_label_hfill = LabelFill(row) / nlh;
2849 /* are there any hfills in the row? */
2850 nh = NumberOfHfills(row);
2852 /* table stuff -- begin*/
2853 if (row->par->table) {
2854 w = paperwidth - row->par->table->WidthOfTable()
2855 - x - RightMargin(row);
2856 nh = 0; /* ignore hfills in tables */
2858 /* table stuff -- end*/
2863 /* is it block, flushleft or flushright?
2864 * set x how you need it */
2866 if (row->par->FirstPhysicalPar()->align == LYX_ALIGN_LAYOUT)
2867 align = textclasslist.Style(buffer->params.textclass, row->par->GetLayout()).align;
2869 align = row->par->FirstPhysicalPar()->align;
2871 /* center displayed insets */
2872 if (row->par->GetChar(row->pos) == LyXParagraph::META_INSET
2873 && row->par->GetInset(row->pos)
2874 && row->par->GetInset(row->pos)->display())
2875 align = LYX_ALIGN_CENTER;
2878 case LYX_ALIGN_BLOCK:
2879 ns = NumberOfSeparators(row);
2880 if (ns && row->next && row->next->par == row->par &&
2881 !(row->next->par->IsNewline(row->next->pos-1))
2882 && !(row->next->par->GetChar(row->next->pos) == LyXParagraph::META_INSET
2883 && row->next->par->GetInset(row->next->pos)
2884 && row->next->par->GetInset(row->next->pos)->display())
2886 fill_separator = w / ns;
2890 case LYX_ALIGN_RIGHT:
2893 case LYX_ALIGN_CENTER:
2901 ComputeBidiTables(row);
2903 LyXParagraph::size_type main_body =
2904 BeginningOfMainBody(row->par);
2905 LyXParagraph::size_type last = RowLast(row);
2907 if (main_body > 0 &&
2908 (main_body-1 > last ||
2909 !row->par->IsLineSeparator(main_body-1))) {
2910 LyXLayout const & layout = textclasslist.Style(buffer->params.textclass,
2911 row->par->GetLayout());
2912 x += lyxfont::width(layout.labelsep,
2913 GetFont(row->par, -2));
2914 if (main_body-1 <= last)
2915 x += fill_label_hfill;
2920 /* important for the screen */
2923 /* the cursor set functions have a special mechanism. When they
2924 * realize, that you left an empty paragraph, they will delete it.
2925 * They also delete the corresponding row */
2927 void LyXText::CursorRightOneWord() const
2929 // treat floats, HFills and Insets as words
2930 LyXCursor tmpcursor = cursor;
2931 // CHECK See comment on top of text.C
2933 if (tmpcursor.pos == tmpcursor.par->Last()
2934 && tmpcursor.par->Next())
2936 tmpcursor.par = tmpcursor.par->Next();
2941 // Skip through initial nonword stuff.
2942 while ( tmpcursor.pos < tmpcursor.par->Last() &&
2943 ! tmpcursor.par->IsWord( tmpcursor.pos ) )
2945 // printf("Current pos1 %d", tmpcursor.pos) ;
2949 // Advance through word.
2950 while ( tmpcursor.pos < tmpcursor.par->Last() &&
2951 tmpcursor.par->IsWord( tmpcursor.pos ) )
2953 // printf("Current pos2 %d", tmpcursor.pos) ;
2958 SetCursor(tmpcursor.par, tmpcursor.pos);
2962 void LyXText::CursorTab() const
2964 if (cursor.par->table) {
2965 int cell = NumberOfCell(cursor.par, cursor.pos);
2966 while(cursor.par->table->IsContRow(cell)) {
2968 cell = NumberOfCell(cursor.par, cursor.pos);
2970 if (cursor.par->table->ShouldBeVeryLastCell(cell))
2971 TableFeatures(LyXTable::APPEND_ROW);
2973 LyXCursor tmpcursor = cursor;
2974 while (tmpcursor.pos < tmpcursor.par->Last()
2975 && !tmpcursor.par->IsNewline(tmpcursor.pos))
2978 if (tmpcursor.pos == tmpcursor.par->Last()){
2979 if (tmpcursor.par->Next()) {
2980 tmpcursor.par = tmpcursor.par->Next();
2986 SetCursor(tmpcursor.par, tmpcursor.pos);
2987 if (cursor.par->table) {
2988 int cell = NumberOfCell(cursor.par, cursor.pos);
2989 while (cursor.par->table->IsContRow(cell) &&
2990 !cursor.par->table->ShouldBeVeryLastCell(cell)) {
2992 while (tmpcursor.pos < tmpcursor.par->Last()
2993 && !tmpcursor.par->IsNewline(tmpcursor.pos))
2996 if (tmpcursor.pos == tmpcursor.par->Last()){
2997 if (tmpcursor.par->Next()) {
2998 tmpcursor.par = tmpcursor.par->Next();
3004 SetCursor(tmpcursor.par, tmpcursor.pos);
3005 cell = NumberOfCell(cursor.par, cursor.pos);
3011 /* -------> Skip initial whitespace at end of word and move cursor to *start*
3012 of prior word, not to end of next prior word. */
3014 void LyXText::CursorLeftOneWord() const
3016 // treat HFills, floats and Insets as words
3017 LyXCursor tmpcursor = cursor;
3018 while (tmpcursor.pos
3019 && (tmpcursor.par->IsSeparator(tmpcursor.pos - 1)
3020 || tmpcursor.par->IsKomma(tmpcursor.pos - 1))
3021 && !(tmpcursor.par->IsHfill(tmpcursor.pos - 1)
3022 || tmpcursor.par->IsFloat(tmpcursor.pos - 1)
3023 || tmpcursor.par->IsInset(tmpcursor.pos - 1)))
3027 && (tmpcursor.par->IsInset(tmpcursor.pos - 1)
3028 || tmpcursor.par->IsFloat(tmpcursor.pos - 1)
3029 || tmpcursor.par->IsHfill(tmpcursor.pos - 1))) {
3031 } else if (!tmpcursor.pos) {
3032 if (tmpcursor.par->Previous()){
3033 tmpcursor.par = tmpcursor.par->Previous();
3034 tmpcursor.pos = tmpcursor.par->Last();
3036 } else { // Here, tmpcursor != 0
3037 while (tmpcursor.pos > 0 &&
3038 tmpcursor.par->IsWord(tmpcursor.pos-1) )
3041 SetCursor(tmpcursor.par, tmpcursor.pos);
3044 /* -------> Select current word. This depends on behaviour of CursorLeftOneWord(), so it is
3047 void LyXText::SelectWord()
3049 // Move cursor to the beginning, when not already there.
3051 && !cursor.par->IsSeparator(cursor.pos-1)
3052 && !cursor.par->IsKomma(cursor.pos-1) )
3053 CursorLeftOneWord();
3055 // set the sel cursor
3056 sel_cursor = cursor;
3058 while ( cursor.pos < cursor.par->Last()
3059 && !cursor.par->IsSeparator(cursor.pos)
3060 && !cursor.par->IsKomma(cursor.pos) )
3062 SetCursor( cursor.par, cursor.pos );
3064 // finally set the selection
3069 /* -------> Select the word currently under the cursor when:
3070 1: no selection is currently set,
3071 2: the cursor is not at the borders of the word. */
3073 bool LyXText::SelectWordWhenUnderCursor()
3076 cursor.pos > 0 && cursor.pos < cursor.par->Last()
3077 && !cursor.par->IsSeparator(cursor.pos)
3078 && !cursor.par->IsKomma(cursor.pos)
3079 && !cursor.par->IsSeparator(cursor.pos -1)
3080 && !cursor.par->IsKomma(cursor.pos -1) ) {
3088 // This function is only used by the spellchecker for NextWord().
3089 // It doesn't handle LYX_ACCENTs and probably never will.
3090 char * LyXText::SelectNextWord(float & value)
3092 LyXParagraph * tmppar = cursor.par;
3094 // If this is not the very first word, skip rest of
3095 // current word because we are probably in the middle
3096 // of a word if there is text here.
3097 if (cursor.pos || cursor.par->previous) {
3098 while (cursor.pos < cursor.par->Last()
3099 && cursor.par->IsLetter(cursor.pos))
3102 // Now, skip until we have real text (will jump paragraphs)
3103 while ((cursor.par->Last() > cursor.pos
3104 && (!cursor.par->IsLetter(cursor.pos)
3105 || cursor.par->getFont(cursor.pos).latex() == LyXFont::ON))
3106 || (cursor.par->Last() == cursor.pos
3107 && cursor.par->Next())){
3108 if (cursor.pos == cursor.par->Last()) {
3109 cursor.par = cursor.par->Next();
3116 // Update the value if we changed paragraphs
3117 if (cursor.par != tmppar){
3118 SetCursor(cursor.par, cursor.pos);
3119 value = float(cursor.y)/float(height);
3122 /* Start the selection from here */
3123 sel_cursor = cursor;
3126 std::ostringstream latex;
3130 /* and find the end of the word
3131 (optional hyphens are part of a word) */
3132 while (cursor.pos < cursor.par->Last()
3133 && (cursor.par->IsLetter(cursor.pos))
3134 || (cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
3135 && cursor.par->GetInset(cursor.pos) != 0
3136 && cursor.par->GetInset(cursor.pos)->Latex(latex, false, false) == 0
3138 && latex.str() == "\\-"
3140 && string(latex.str(), 3) == "\\-" // this is not nice at all
3145 #ifndef HAVE_SSTREAM
3146 delete [] latex.str();
3148 // Finally, we copy the word to a string and return it
3151 if (sel_cursor.pos < cursor.pos) {
3152 str = new char [cursor.pos - sel_cursor.pos + 2];
3153 LyXParagraph::size_type i, j;
3154 for (i = sel_cursor.pos, j = 0; i < cursor.pos; ++i) {
3155 if (cursor.par->GetChar(i) != LyXParagraph::META_INSET)
3156 str[j++] = cursor.par->GetChar(i);
3164 // This one is also only for the spellchecker
3165 void LyXText::SelectSelectedWord()
3167 /* move cursor to the beginning */
3168 SetCursor(sel_cursor.par, sel_cursor.pos);
3170 /* set the sel cursor */
3171 sel_cursor = cursor;
3174 std::ostringstream latex;
3179 /* now find the end of the word */
3180 while (cursor.pos < cursor.par->Last()
3181 && (cursor.par->IsLetter(cursor.pos)
3182 || (cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET
3183 && cursor.par->GetInset(cursor.pos) != 0
3184 && cursor.par->GetInset(cursor.pos)->Latex(latex, false, false) == 0
3186 && latex.str() == "\\-"
3188 && string(latex.str(), 3) == "\\-"
3193 #ifndef HAVE_SSTREAM
3194 delete [] latex.str();
3196 SetCursor(cursor.par, cursor.pos);
3198 /* finally set the selection */
3203 /* -------> Delete from cursor up to the end of the current or next word. */
3204 void LyXText::DeleteWordForward()
3206 if (!cursor.par->Last())
3209 LyXCursor tmpcursor = cursor;
3210 selection = true; // to avoid deletion
3211 CursorRightOneWord();
3212 sel_cursor = cursor;
3216 /* -----> Great, CutSelection() gets rid of multiple spaces. */
3222 /* -------> Delete from cursor to start of current or prior word. */
3223 void LyXText::DeleteWordBackward()
3225 if (!cursor.par->Last())
3228 LyXCursor tmpcursor = cursor;
3229 selection = true; // to avoid deletion
3230 CursorLeftOneWord();
3231 sel_cursor = cursor;
3239 /* -------> Kill to end of line. */
3240 void LyXText::DeleteLineForward()
3242 if (!cursor.par->Last())
3243 // Paragraph is empty, so we just go to the right
3246 LyXCursor tmpcursor = cursor;
3247 selection = true; // to avoid deletion
3249 sel_cursor = cursor;
3252 // What is this test for ??? (JMarc)
3254 DeleteWordForward();
3262 // Change the case of a word at cursor position.
3263 // This function directly manipulates LyXParagraph::text because there
3264 // is no LyXParagraph::SetChar currently. I did what I could to ensure
3265 // that it is correct. I guess part of it should be moved to
3266 // LyXParagraph, but it will have to change for 1.1 anyway. At least
3267 // it does not access outside of the allocated array as the older
3268 // version did. (JMarc)
3269 void LyXText::ChangeWordCase(LyXText::TextCase action)
3271 LyXParagraph * tmppar = cursor.par->ParFromPos(cursor.pos);
3273 SetUndo(Undo::FINISH, tmppar->previous, tmppar->next);
3275 LyXParagraph::size_type tmppos =
3276 cursor.par->PositionInParFromPos(cursor.pos);
3277 while (tmppos < tmppar->size()) {
3278 unsigned char c = tmppar->GetChar(tmppos);
3279 if (IsKommaChar(c) || IsLineSeparatorChar(c))
3281 if (c != LyXParagraph::META_INSET) {
3283 case text_lowercase:
3286 case text_capitalization:
3288 action = text_lowercase;
3290 case text_uppercase:
3296 //tmppar->text[tmppos] = c;
3297 tmppar->SetChar(tmppos, c);
3300 CheckParagraph(tmppar, tmppos);
3301 CursorRightOneWord();
3305 void LyXText::Delete()
3307 // this is a very easy implementation
3309 LyXCursor old_cursor = cursor;
3310 int old_cur_par_id = old_cursor.par->id();
3311 int old_cur_par_prev_id = old_cursor.par->previous ?
3312 old_cursor.par->previous->id() : 0;
3314 // just move to the right
3315 CursorRightIntern();
3317 // CHECK Look at the comment here.
3318 // This check is not very good...
3319 // The CursorRightIntern calls DeleteEmptyParagrapgMechanism
3320 // and that can very well delete the par or par->previous in
3321 // old_cursor. Will a solution where we compare paragraph id's
3323 if ((cursor.par->previous ? cursor.par->previous->id() : 0)
3324 == old_cur_par_prev_id
3325 && cursor.par->id() != old_cur_par_id)
3326 return; // delete-empty-paragraph-mechanism has done it
3328 // if you had success make a backspace
3329 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) {
3330 LyXCursor tmpcursor = cursor;
3331 cursor = old_cursor; // to make sure undo gets the right cursor position
3332 SetUndo(Undo::DELETE,
3333 cursor.par->ParFromPos(cursor.pos)->previous,
3334 cursor.par->ParFromPos(cursor.pos)->next);
3341 void LyXText::Backspace()
3343 /* table stuff -- begin */
3344 if (cursor.par->table) {
3348 /* table stuff -- end */
3350 // LyXFont rawtmpfont = current_font;
3351 // LyXFont realtmpfont = real_current_font;
3352 // We don't need the above variables as calling to SetCursor() with third
3353 // argument eqaul to false, will not change current_font & real_current_font
3355 // Get the font that is used to calculate the baselineskip
3356 LyXParagraph::size_type lastpos = cursor.par->Last();
3357 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
3359 if (cursor.pos == 0) {
3360 // The cursor is at the beginning of a paragraph, so the the backspace
3361 // will collapse two paragraphs into one.
3363 // we may paste some paragraphs
3365 // is it an empty paragraph?
3368 || (lastpos == 1 && cursor.par->IsSeparator(0)))
3369 && !(cursor.par->Next()
3370 && cursor.par->footnoteflag == LyXParagraph::NO_FOOTNOTE
3371 && cursor.par->Next()->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)) {
3372 // This is an empty paragraph and we delete it just by moving the cursor one step
3373 // left and let the DeleteEmptyParagraphMechanism handle the actual deletion
3374 // of the paragraph.
3376 if (cursor.par->previous) {
3377 LyXParagraph * tmppar = cursor.par->previous->FirstPhysicalPar();
3378 if (cursor.par->GetLayout() == tmppar->GetLayout()
3379 && cursor.par->footnoteflag == tmppar->footnoteflag
3380 && cursor.par->GetAlign() == tmppar->GetAlign()) {
3381 // Inherit botom DTD from the paragraph below.
3382 // (the one we are deleting)
3383 tmppar->line_bottom = cursor.par->line_bottom;
3384 tmppar->added_space_bottom = cursor.par->added_space_bottom;
3385 tmppar->pagebreak_bottom = cursor.par->pagebreak_bottom;
3390 // the layout things can change the height of a row !
3391 int tmpheight = cursor.row->height;
3392 SetHeightOfRow(cursor.row);
3393 if (cursor.row->height != tmpheight) {
3394 refresh_y = cursor.y - cursor.row->baseline;
3395 refresh_row = cursor.row;
3396 status = LyXText::NEED_MORE_REFRESH;
3402 if (cursor.par->ParFromPos(cursor.pos)->previous){
3403 SetUndo(Undo::DELETE,
3404 cursor.par->ParFromPos(cursor.pos)->previous->previous,
3405 cursor.par->ParFromPos(cursor.pos)->next);
3408 LyXParagraph * tmppar = cursor.par;
3409 Row * tmprow = cursor.row;
3411 // We used to do CursorLeftIntern() here, but it is
3412 // not a good idea since it triggers the auto-delete
3413 // mechanism. So we do a CursorLeftIntern()-lite,
3414 // without the dreaded mechanism. (JMarc)
3415 if (cursor.par->Previous()) {
3416 // steps into the above paragraph.
3417 SetCursorIntern(cursor.par->Previous(),
3418 cursor.par->Previous()->Last(), false);
3421 /* Pasting is not allowed, if the paragraphs have different
3422 layout. I think it is a real bug of all other
3423 word processors to allow it. It confuses the user.
3424 Even so with a footnote paragraph and a non-footnote
3425 paragraph. I will not allow pasting in this case,
3426 because the user would be confused if the footnote behaves
3427 different wether it is open or closed.
3429 Correction: Pasting is always allowed with standard-layout
3431 if (cursor.par != tmppar
3432 && (cursor.par->GetLayout() == tmppar->GetLayout()
3433 || tmppar->GetLayout() == 0 /*standard*/)
3434 && cursor.par->footnoteflag == tmppar->footnoteflag
3435 /* table stuff -- begin*/
3436 && !cursor.par->table /* no pasting of tables */
3437 /* table stuff -- end*/
3438 && cursor.par->GetAlign() == tmppar->GetAlign()) {
3440 RemoveParagraph(tmprow);
3442 cursor.par->PasteParagraph();
3444 if (!cursor.pos || !cursor.par->IsSeparator(cursor.pos - 1))
3445 ; //cursor.par->InsertChar(cursor.pos, ' ');
3446 // strangely enough it seems that commenting out the line above removes
3447 // most or all of the segfaults. I will however also try to move the
3448 // two Remove... lines in front of the PasteParagraph too.
3453 status = LyXText::NEED_MORE_REFRESH;
3454 refresh_row = cursor.row;
3455 refresh_y = cursor.y - cursor.row->baseline;
3457 // remove the lost paragraph
3458 // This one is not safe, since the paragraph that the tmprow and the
3459 // following rows belong to has been deleted by the PasteParagraph
3460 // above. The question is... could this be moved in front of the
3462 //RemoveParagraph(tmprow);
3463 //RemoveRow(tmprow);
3465 AppendParagraph(cursor.row); // This rebuilds the rows.
3466 UpdateCounters(cursor.row);
3468 // the row may have changed, block, hfills etc.
3469 SetCursor(cursor.par, cursor.pos, false);
3472 /* this is the code for a normal backspace, not pasting
3474 SetUndo(Undo::DELETE,
3475 cursor.par->ParFromPos(cursor.pos)->previous,
3476 cursor.par->ParFromPos(cursor.pos)->next);
3477 // We used to do CursorLeftIntern() here, but it is
3478 // not a good idea since it triggers the auto-delete
3479 // mechanism. So we do a CursorLeftIntern()-lite,
3480 // without the dreaded mechanism. (JMarc)
3481 SetCursorIntern(cursor.par, cursor.pos - 1, false, cursor.boundary);
3483 // some insets are undeletable here
3484 if (cursor.par->GetChar(cursor.pos) == LyXParagraph::META_INSET) {
3485 if (!cursor.par->GetInset(cursor.pos)->Deletable())
3487 // force complete redo when erasing display insets
3488 // this is a cruel method but safe..... Matthias
3489 if (cursor.par->GetInset(cursor.pos)->display()){
3490 cursor.par->Erase(cursor.pos);
3496 Row * row = cursor.row;
3497 long y = cursor.y - row->baseline;
3498 LyXParagraph::size_type z;
3499 /* remember that a space at the end of a row doesnt count
3500 * when calculating the fill */
3501 if (cursor.pos < RowLast(row) ||
3502 !cursor.par->IsLineSeparator(cursor.pos)) {
3503 row->fill += SingleWidth(cursor.par, cursor.pos);
3506 /* some special code when deleting a newline. This is similar
3507 * to the behavior when pasting paragraphs */
3508 if (cursor.pos && cursor.par->IsNewline(cursor.pos)) {
3509 cursor.par->Erase(cursor.pos);
3510 // refresh the positions
3512 while (tmprow->next && tmprow->next->par == row->par) {
3513 tmprow = tmprow->next;
3516 if (cursor.par->IsLineSeparator(cursor.pos - 1))
3519 if (cursor.pos < cursor.par->Last() && !cursor.par->IsSeparator(cursor.pos)) {
3520 cursor.par->InsertChar(cursor.pos, ' ');
3521 // refresh the positions
3523 while (tmprow->next && tmprow->next->par == row->par) {
3524 tmprow = tmprow->next;
3529 cursor.par->Erase(cursor.pos);
3531 // refresh the positions
3533 while (tmprow->next && tmprow->next->par == row->par) {
3534 tmprow = tmprow->next;
3538 // delete newlines at the beginning of paragraphs
3539 while (cursor.par->Last() &&
3540 cursor.par->IsNewline(cursor.pos) &&
3541 cursor.pos == BeginningOfMainBody(cursor.par)) {
3542 cursor.par->Erase(cursor.pos);
3543 // refresh the positions
3545 while (tmprow->next &&
3546 tmprow->next->par == row->par) {
3547 tmprow = tmprow->next;
3553 // is there a break one row above
3554 if (row->previous && row->previous->par == row->par) {
3555 z = NextBreakPoint(row->previous, paperwidth);
3556 if ( z >= row->pos) {
3559 Row * tmprow = row->previous;
3561 // maybe the current row is now empty
3562 if (row->pos >= row->par->Last()) {
3567 BreakAgainOneRow(row);
3568 if (row->next && row->next->par == row->par)
3569 need_break_row = row->next;
3574 // set the dimensions of the row above
3575 y -= tmprow->height;
3576 tmprow->fill = Fill(tmprow, paperwidth);
3577 SetHeightOfRow(tmprow);
3580 refresh_row = tmprow;
3581 status = LyXText::NEED_MORE_REFRESH;
3582 SetCursor(cursor.par, cursor.pos, false, cursor.boundary);
3583 //current_font = rawtmpfont;
3584 //real_current_font = realtmpfont;
3585 // check, whether the last character's font has changed.
3587 cursor.par->GetFontSettings(cursor.par->Last() - 1))
3588 RedoHeightOfParagraph(cursor);
3593 // break the cursor row again
3594 z = NextBreakPoint(row, paperwidth);
3596 if (z != RowLast(row) ||
3597 (row->next && row->next->par == row->par &&
3598 RowLast(row) == row->par->Last() - 1)){
3600 /* it can happen that a paragraph loses one row
3601 * without a real breakup. This is when a word
3602 * is to long to be broken. Well, I don t care this
3604 if (row->next && row->next->par == row->par &&
3605 RowLast(row) == row->par->Last() - 1)
3606 RemoveRow(row->next);
3610 status = LyXText::NEED_MORE_REFRESH;
3612 BreakAgainOneRow(row);
3613 SetCursor(cursor.par, cursor.pos, false, cursor.boundary);
3614 // cursor MUST be in row now
3616 if (row->next && row->next->par == row->par)
3617 need_break_row = row->next;
3621 // set the dimensions of the row
3622 row->fill = Fill(row, paperwidth);
3623 int tmpheight = row->height;
3624 SetHeightOfRow(row);
3625 if (tmpheight == row->height)
3626 status = LyXText::NEED_VERY_LITTLE_REFRESH;
3628 status = LyXText::NEED_MORE_REFRESH;
3631 SetCursor(cursor.par, cursor.pos, false, cursor.boundary);
3635 // current_font = rawtmpfont;
3636 // real_current_font = realtmpfont;
3638 lastpos = cursor.par->Last();
3639 if (cursor.pos == lastpos) {
3641 if (IsBoundary(cursor.par, cursor.pos) != cursor.boundary)
3642 SetCursor(cursor.par, cursor.pos, false, !cursor.boundary);
3645 // check, wether the last characters font has changed.
3647 cursor.par->GetFontSettings(lastpos - 1)) {
3648 RedoHeightOfParagraph(cursor);
3650 // now the special right address boxes
3651 if (textclasslist.Style(buffer->params.textclass,
3652 cursor.par->GetLayout()).margintype == MARGIN_RIGHT_ADDRESS_BOX) {
3653 RedoDrawingOfParagraph(cursor);
3659 void LyXText::GetVisibleRow(int offset, Row * row_ptr, long y)
3661 /* returns a printed row */
3662 Painter & pain = owner_->painter();
3664 bool is_rtl = row_ptr->par->isRightToLeftPar();
3665 LyXParagraph::size_type last = RowLastPrintable(row_ptr);
3667 LyXParagraph::size_type vpos, pos;
3669 int y_top, y_bottom;
3670 float fill_separator, fill_hfill, fill_label_hfill;
3674 if (row_ptr->height <= 0) {
3675 lyxerr << "LYX_ERROR: row.height: " << row_ptr->height << endl;
3678 PrepareToPrint(row_ptr, x, fill_separator,
3679 fill_hfill, fill_label_hfill);
3681 // clear the area where we want to paint/print
3682 pain.fillRectangle(0, offset, paperwidth, row_ptr->height);
3685 /* selection code */
3686 if (bidi_same_direction) {
3687 if (sel_start_cursor.row == row_ptr &&
3688 sel_end_cursor.row == row_ptr) {
3689 if (sel_start_cursor.x < sel_end_cursor.x)
3690 pain.fillRectangle(sel_start_cursor.x, offset,
3691 sel_end_cursor.x - sel_start_cursor.x,
3695 pain.fillRectangle(sel_end_cursor.x, offset,
3696 sel_start_cursor.x - sel_end_cursor.x,
3699 } else if (sel_start_cursor.row == row_ptr) {
3701 pain.fillRectangle(0, offset,
3706 pain.fillRectangle(sel_start_cursor.x, offset,
3707 paperwidth - sel_start_cursor.x,
3710 } else if (sel_end_cursor.row == row_ptr) {
3712 pain.fillRectangle(sel_end_cursor.x, offset,
3713 paperwidth - sel_end_cursor.x,
3717 pain.fillRectangle(0, offset,
3721 } else if (y > long(sel_start_cursor.y)
3722 && y < long(sel_end_cursor.y)) {
3723 pain.fillRectangle(0, offset,
3724 paperwidth, row_ptr->height,
3727 } else if ( sel_start_cursor.row != row_ptr &&
3728 sel_end_cursor.row != row_ptr &&
3729 y > long(sel_start_cursor.y)
3730 && y < long(sel_end_cursor.y)) {
3731 pain.fillRectangle(0, offset,
3732 paperwidth, row_ptr->height,
3734 } else if (sel_start_cursor.row == row_ptr ||
3735 sel_end_cursor.row == row_ptr) {
3738 if (row_ptr->par->table) {
3739 cell = NumberOfCell(row_ptr->par, row_ptr->pos);
3740 tmpx += row_ptr->par->table->GetBeginningOfTextInCell(cell);
3742 if ( (sel_start_cursor.row != row_ptr && !is_rtl) ||
3743 (sel_end_cursor.row != row_ptr && is_rtl))
3744 pain.fillRectangle(0, offset,
3745 int(tmpx), row_ptr->height,
3747 if (row_ptr->par->table) {
3749 for (vpos = row_ptr->pos; vpos <= last; ++vpos) {
3750 pos = vis2log(vpos);
3751 float old_tmpx = tmpx;
3752 if (row_ptr->par->IsNewline(pos)) {
3753 tmpx = x_old + row_ptr->par->table->WidthOfColumn(cell);
3756 tmpx += row_ptr->par->table->GetBeginningOfTextInCell(cell);
3758 tmpx += SingleWidth(row_ptr->par, pos);
3760 if ( (sel_start_cursor.row != row_ptr ||
3761 sel_start_cursor.pos <= pos) &&
3762 (sel_end_cursor.row != row_ptr ||
3763 pos < sel_end_cursor.pos) )
3764 pain.fillRectangle(int(old_tmpx), offset,
3765 int(tmpx - old_tmpx + 1),
3770 LyXParagraph::size_type main_body =
3771 BeginningOfMainBody(row_ptr->par);
3773 for (vpos = row_ptr->pos; vpos <= last; ++vpos) {
3774 pos = vis2log(vpos);
3775 float old_tmpx = tmpx;
3776 if (main_body > 0 && pos == main_body-1) {
3777 tmpx += fill_label_hfill +
3778 lyxfont::width(textclasslist.Style(buffer->params.textclass,
3779 row_ptr->par->GetLayout()).labelsep,
3780 GetFont(row_ptr->par, -2));
3781 if (row_ptr->par->IsLineSeparator(main_body-1))
3782 tmpx -= SingleWidth(row_ptr->par, main_body-1);
3784 if (HfillExpansion(row_ptr, pos)) {
3785 tmpx += SingleWidth(row_ptr->par, pos);
3786 if (pos >= main_body)
3789 tmpx += fill_label_hfill;
3791 else if (row_ptr->par->IsSeparator(pos)) {
3792 tmpx += SingleWidth(row_ptr->par, pos);
3793 if (pos >= main_body)
3794 tmpx += fill_separator;
3796 tmpx += SingleWidth(row_ptr->par, pos);
3798 if ( (sel_start_cursor.row != row_ptr ||
3799 sel_start_cursor.pos <= pos) &&
3800 (sel_end_cursor.row != row_ptr ||
3801 pos < sel_end_cursor.pos) )
3802 pain.fillRectangle(int(old_tmpx), offset,
3803 int(tmpx - old_tmpx + 1),
3808 if ( (sel_start_cursor.row != row_ptr && is_rtl) ||
3809 (sel_end_cursor.row != row_ptr && !is_rtl) )
3810 pain.fillRectangle(int(tmpx), offset,
3811 int(paperwidth - tmpx),
3818 if (row_ptr->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
3819 LyXFont font(LyXFont::ALL_SANE);
3820 font.setSize(LyXFont::SIZE_FOOTNOTE);
3821 font.setColor(LColor::footnote);
3823 box_x = LYX_PAPER_MARGIN + lyxfont::width(" wide-tab ", font);
3824 if (row_ptr->previous &&
3825 row_ptr->previous->par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
3827 switch (row_ptr->par->footnotekind) {
3828 case LyXParagraph::MARGIN:
3831 case LyXParagraph::FIG:
3834 case LyXParagraph::TAB:
3837 case LyXParagraph::WIDE_FIG:
3840 case LyXParagraph::WIDE_TAB:
3843 case LyXParagraph::ALGORITHM:
3846 case LyXParagraph::FOOTNOTE:
3851 pain.fillRectangle(LYX_PAPER_MARGIN,
3853 box_x - LYX_PAPER_MARGIN,
3854 int(lyxfont::maxAscent(font)
3855 + lyxfont::maxDescent(font)),
3856 LColor::footnotebg);
3858 pain.line(LYX_PAPER_MARGIN, offset,
3859 paperwidth - LYX_PAPER_MARGIN, offset,
3860 LColor::footnoteframe);
3862 pain.text(LYX_PAPER_MARGIN,
3863 offset + int(lyxfont::maxAscent(font)) + 1,
3866 pain.line(LYX_PAPER_MARGIN, offset,
3868 offset + int(lyxfont::maxAscent(font)
3869 + lyxfont::maxDescent(font)),
3870 LColor::footnoteframe);
3872 pain.line(LYX_PAPER_MARGIN,
3873 offset + int(lyxfont::maxAscent(font)
3874 + lyxfont::maxDescent(font)) + 1,
3876 offset + int(lyxfont::maxAscent(font)
3877 + lyxfont::maxDescent(font)) + 1,
3878 LColor::footnoteframe);
3882 /* draw the open floats in a red box */
3883 pain.line(box_x, offset,
3884 box_x, offset + row_ptr->height,
3885 LColor::footnoteframe);
3887 pain.line(paperwidth - LYX_PAPER_MARGIN,
3889 paperwidth - LYX_PAPER_MARGIN,
3890 offset + row_ptr->height,
3891 LColor::footnoteframe);
3894 // Draw appendix lines
3895 LyXParagraph * p = row_ptr->par->PreviousBeforeFootnote()->FirstPhysicalPar();
3897 pain.line(1, offset,
3898 1, offset + row_ptr->height,
3899 LColor::appendixline);
3900 pain.line(paperwidth - 2, offset,
3901 paperwidth - 2, offset + row_ptr->height,
3902 LColor::appendixline);
3905 // Draw minipage line
3906 bool minipage = p->pextra_type == LyXParagraph::PEXTRA_MINIPAGE;
3908 pain.line(LYX_PAPER_MARGIN/5, offset,
3910 offset + row_ptr->height - 1,
3911 LColor::minipageline);
3914 int depth = p->GetDepth();
3915 for (int i = 1; i <= depth; ++i) {
3916 int line_x = (LYX_PAPER_MARGIN/5)*(i+minipage);
3917 pain.line(line_x, offset, line_x,
3918 offset + row_ptr->height - 1,
3921 } else if (row_ptr->previous &&
3922 row_ptr->previous->par->footnoteflag
3923 == LyXParagraph::OPEN_FOOTNOTE) {
3924 LyXFont font(LyXFont::ALL_SANE);
3925 font.setSize(LyXFont::SIZE_FOOTNOTE);
3927 int box_x = LYX_PAPER_MARGIN;
3928 box_x += lyxfont::width(" wide-tab ", font);
3930 pain.line(box_x, offset,
3931 paperwidth - LYX_PAPER_MARGIN,
3932 offset, LColor::footnote);
3935 // Draw appendix lines
3936 LyXParagraph * firstpar = row_ptr->par->FirstPhysicalPar();
3937 if (firstpar->appendix){
3938 pain.line(1, offset,
3939 1, offset + row_ptr->height,
3940 LColor::appendixline);
3941 pain.line(paperwidth - 2, offset,
3942 paperwidth - 2, offset + row_ptr->height,
3943 LColor::appendixline);
3946 // Draw minipage line
3947 bool minipage = firstpar->pextra_type == LyXParagraph::PEXTRA_MINIPAGE;
3949 pain.line(LYX_PAPER_MARGIN/5 + box_x, offset,
3950 LYX_PAPER_MARGIN/5 + box_x,
3951 offset + row_ptr->height - 1,
3952 LColor::minipageline);
3955 int depth = firstpar->GetDepth();
3960 if (row_ptr->par->footnoteflag ==
3961 row_ptr->next->par->footnoteflag)
3962 next_depth = row_ptr->next->par->GetDepth();
3963 else if (row_ptr->par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
3966 if (row_ptr->previous)
3967 if (row_ptr->par->footnoteflag ==
3968 row_ptr->previous->par->footnoteflag)
3969 prev_depth = row_ptr->previous->par->GetDepth();
3970 else if (row_ptr->par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE)
3973 for (int i = 1; i <= depth; ++i) {
3974 int line_x = (LYX_PAPER_MARGIN/5)*(i+minipage)+box_x;
3975 pain.line(line_x, offset, line_x,
3976 offset + row_ptr->height - 1 - (i-next_depth-1)*3,
3980 pain.fillRectangle(line_x, offset, LYX_PAPER_MARGIN/5, 2,
3983 pain.fillRectangle(line_x,
3984 offset + row_ptr->height - 2 - (i-next_depth-1)*3,
3985 LYX_PAPER_MARGIN/5, 2,
3991 LyXLayout const & layout =
3992 textclasslist.Style(buffer->params.textclass,
3993 row_ptr->par->GetLayout());
3996 y_bottom = row_ptr->height;
3998 /* is it a first row? */
3999 if (row_ptr->pos == 0
4000 && row_ptr->par == firstpar) {
4002 /* start of appendix? */
4003 if (row_ptr->par->start_of_appendix){
4004 pain.line(1, offset,
4005 paperwidth - 2, offset,
4006 LColor::appendixline);
4009 /* think about the margins */
4010 if (!row_ptr->previous)
4011 y_top += LYX_PAPER_MARGIN;
4013 if (row_ptr->par->pagebreak_top){ /* draw a top pagebreak */
4015 pb_font.setColor(LColor::pagebreak).decSize();
4016 int w = 0, a = 0, d = 0;
4017 pain.line(0, offset + y_top + 2*DefaultHeight(),
4019 offset + y_top + 2*DefaultHeight(),
4021 Painter::line_onoffdash)
4024 _("Page Break (top)"),
4027 LColor::background, false, w, a, d);
4028 pain.rectText((paperwidth - w)/2,
4029 offset +y_top + 2*DefaultHeight() +d,
4030 _("Page Break (top)"),
4033 LColor::background);
4034 y_top += 3 * DefaultHeight();
4037 if (row_ptr->par->added_space_top.kind() == VSpace::VFILL) {
4038 /* draw a vfill top */
4039 pain.line(0, offset + 2 + y_top,
4040 LYX_PAPER_MARGIN, offset + 2 + y_top,
4043 pain.line(0, offset + y_top + 3 * DefaultHeight(),
4045 offset + y_top + 3 * DefaultHeight(),
4048 pain.line(LYX_PAPER_MARGIN / 2, offset + 2 + y_top,
4049 LYX_PAPER_MARGIN / 2,
4050 offset + y_top + 3 * DefaultHeight(),
4053 y_top += 3 * DefaultHeight();
4056 /* think about user added space */
4057 y_top += int(row_ptr->par->added_space_top.inPixels(owner_));
4059 /* think about the parskip */
4060 /* some parskips VERY EASY IMPLEMENTATION */
4061 if (buffer->params.paragraph_separation == BufferParams::PARSEP_SKIP) {
4062 if (layout.latextype == LATEX_PARAGRAPH
4063 && firstpar->GetDepth() == 0
4064 && firstpar->Previous())
4065 y_top += buffer->params.getDefSkip().inPixels(owner_);
4066 else if (firstpar->Previous()
4067 && textclasslist.Style(buffer->params.textclass,
4068 firstpar->Previous()->GetLayout()).latextype == LATEX_PARAGRAPH
4069 && firstpar->Previous()->GetDepth() == 0)
4070 // is it right to use defskip here, too? (AS)
4071 y_top += buffer->params.getDefSkip().inPixels(owner_);
4074 if (row_ptr->par->line_top) { /* draw a top line */
4075 y_top += lyxfont::ascent('x', GetFont(row_ptr->par, 0));
4077 pain.line(0, offset + y_top,
4078 paperwidth, offset + y_top,
4080 Painter::line_solid,
4081 Painter::line_thick);
4083 y_top += lyxfont::ascent('x', GetFont(row_ptr->par, 0));
4086 /* should we print a label? */
4087 if (layout.labeltype >= LABEL_STATIC
4088 && (layout.labeltype != LABEL_STATIC
4089 || layout.latextype != LATEX_ENVIRONMENT
4090 || row_ptr->par->IsFirstInSequence())) {
4091 font = GetFont(row_ptr->par, -2);
4092 if (!row_ptr->par->GetLabelstring().empty()) {
4094 string tmpstring = row_ptr->par->GetLabelstring();
4096 if (layout.labeltype == LABEL_COUNTER_CHAPTER) {
4097 if (buffer->params.secnumdepth >= 0) {
4098 /* this is special code for the chapter layout. This is printed in
4099 * an extra row and has a pagebreak at the top. */
4100 float spacing_val = 1.0;
4101 if (!row_ptr->par->spacing.isDefault()) {
4102 spacing_val = row_ptr->par->spacing.getValue();
4104 spacing_val = buffer->params.spacing.getValue();
4107 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val)
4108 + int(layout.parsep) * DefaultHeight();
4110 tmpx = paperwidth - LeftMargin(row_ptr) -
4111 lyxfont::width(tmpstring, font);
4112 pain.text(int(tmpx),
4113 offset + row_ptr->baseline - row_ptr->ascent_of_text - maxdesc,
4118 tmpx = paperwidth - LeftMargin(row_ptr)
4119 + lyxfont::width(layout.labelsep, font);
4120 if (row_ptr->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
4121 LyXFont font(LyXFont::ALL_SANE);
4122 font.setSize(LyXFont::SIZE_SMALL);
4123 tmpx += lyxfont::width("Mwide-fixM", font);
4126 tmpx = x - lyxfont::width(layout.labelsep, font)
4127 - lyxfont::width(tmpstring, font);
4130 pain.text(int(tmpx),
4131 offset + row_ptr->baseline,
4135 /* the labels at the top of an environment. More or less for bibliography */
4136 } else if (layout.labeltype == LABEL_TOP_ENVIRONMENT ||
4137 layout.labeltype == LABEL_BIBLIO ||
4138 layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
4139 if (row_ptr->par->IsFirstInSequence()) {
4140 font = GetFont(row_ptr->par, -2);
4141 if (!row_ptr->par->GetLabelstring().empty()) {
4142 string tmpstring = row_ptr->par->GetLabelstring();
4143 float spacing_val = 1.0;
4144 if (!row_ptr->par->spacing.isDefault()) {
4145 spacing_val = row_ptr->par->spacing.getValue();
4147 spacing_val = buffer->params.spacing.getValue();
4150 maxdesc = int(lyxfont::maxDescent(font) * layout.spacing.getValue() * spacing_val
4151 + (layout.labelbottomsep * DefaultHeight()));
4154 if (layout.labeltype == LABEL_CENTERED_TOP_ENVIRONMENT){
4155 tmpx = ( (is_rtl ? LeftMargin(row_ptr) : x)
4156 + paperwidth - RightMargin(row_ptr) ) / 2;
4157 tmpx -= lyxfont::width(tmpstring, font) / 2;
4159 tmpx = paperwidth - LeftMargin(row_ptr) -
4160 lyxfont::width(tmpstring, font);
4161 pain.text(int(tmpx),
4162 offset + row_ptr->baseline
4163 - row_ptr->ascent_of_text
4169 if (layout.labeltype == LABEL_BIBLIO && row_ptr->par->bibkey) {
4170 font = GetFont(row_ptr->par, -1);
4172 tmpx = paperwidth - LeftMargin(row_ptr)
4173 + lyxfont::width(layout.labelsep, font);
4175 tmpx = x - lyxfont::width(layout.labelsep, font)
4176 - row_ptr->par->bibkey->width(owner_->painter(), font);
4177 row_ptr->par->bibkey->draw(pain,
4179 offset + row_ptr->baseline,
4184 /* is it a last row? */
4185 LyXParagraph * par = row_ptr->par->LastPhysicalPar();
4186 if (row_ptr->par->ParFromPos(last + 1) == par
4187 && (!row_ptr->next || row_ptr->next->par != row_ptr->par)) {
4189 /* think about the margins */
4191 y_bottom -= LYX_PAPER_MARGIN;
4193 /* draw a bottom pagebreak */
4194 if (firstpar->pagebreak_bottom) {
4196 pb_font.setColor(LColor::pagebreak).decSize();
4197 int w = 0, a = 0, d = 0;
4199 offset + y_bottom - 2 * DefaultHeight(),
4201 offset + y_bottom - 2 * DefaultHeight(),
4203 Painter::line_onoffdash)
4206 _("Page Break (bottom)"),
4209 LColor::background, false, w, a, d);
4210 pain.rectText((paperwidth - w)/2,
4211 offset +y_top + 2*DefaultHeight() +d,
4212 _("Page Break (bottom)"),
4215 LColor::background);
4216 y_bottom -= 3 * DefaultHeight();
4219 if (firstpar->added_space_bottom.kind() == VSpace::VFILL) {
4220 /* draw a vfill bottom */
4221 pain.line(0, offset + y_bottom - 3 * DefaultHeight(),
4223 offset + y_bottom - 3 * DefaultHeight(),
4225 pain.line(0, offset + y_bottom - 2,
4227 offset + y_bottom - 2,
4229 pain.line(LYX_PAPER_MARGIN / 2,
4230 offset + y_bottom - 3 * DefaultHeight(),
4231 LYX_PAPER_MARGIN / 2,
4232 offset + y_bottom - 2,
4234 y_bottom -= 3* DefaultHeight();
4237 /* think about user added space */
4238 y_bottom -= int(firstpar->added_space_bottom.inPixels(owner_));
4240 if (firstpar->line_bottom) {
4241 /* draw a bottom line */
4242 y_bottom -= lyxfont::ascent('x', GetFont(par, par->Last() - 1));
4243 pain.line(0, offset + y_bottom,
4244 paperwidth, offset + y_bottom,
4245 LColor::topline, Painter::line_solid,
4246 Painter::line_thick);
4247 y_bottom -= lyxfont::ascent('x', GetFont(par, par->Last() - 1));
4251 int endlabel = row_ptr->par->GetEndLabel();
4254 case END_LABEL_FILLED_BOX:
4256 LyXFont font = GetFont(row_ptr->par, last);
4257 int size = int(0.75 * lyxfont::maxAscent(font));
4258 int y = (offset + row_ptr->baseline) - size;
4259 int x = is_rtl ? LYX_PAPER_MARGIN
4260 : paperwidth - LYX_PAPER_MARGIN - size;
4261 if (row_ptr->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE)
4263 LyXFont font(LyXFont::ALL_SANE);
4264 font.setSize(LyXFont::SIZE_SMALL);
4265 x += lyxfont::width("Mwide-figM", font);
4267 x -= LYX_PAPER_MARGIN/2;
4268 if (row_ptr->fill <= size)
4269 x += (size - row_ptr->fill + 1) * (is_rtl ? -1 : 1);
4270 if (endlabel == END_LABEL_BOX) {
4271 pain.line(x, y, x, y + size,
4273 pain.line(x + size, y, x + size , y + size,
4275 pain.line(x, y, x + size, y,
4277 pain.line(x, y + size, x + size, y + size,
4280 pain.fillRectangle(x, y, size, size,
4284 case END_LABEL_STATIC:
4286 LyXTextClass::LayoutList::size_type layout = row_ptr->par->GetLayout();
4287 string tmpstring = textclasslist.Style(buffer->params.textclass,
4288 layout).endlabelstring();
4289 font = GetFont(row_ptr->par, -2);
4290 int tmpx = is_rtl ? int(x) - lyxfont::width(tmpstring, font)
4291 : paperwidth - RightMargin(row_ptr) - row_ptr->fill;
4292 pain.text( tmpx, offset + row_ptr->baseline, tmpstring, font);
4295 case END_LABEL_NO_LABEL:
4300 /* draw the text in the pixmap */
4302 vpos = row_ptr->pos;
4303 /* table stuff -- begin*/
4304 if (row_ptr->par->table) {
4306 int cell = NumberOfCell(row_ptr->par, row_ptr->pos);
4308 x += row_ptr->par->table->GetBeginningOfTextInCell(cell);
4310 while (vpos <= last) {
4311 pos = vis2log(vpos);
4312 if (row_ptr->par->IsNewline(pos)) {
4314 x = x_old + row_ptr->par->table->WidthOfColumn(cell);
4315 /* draw the table lines, still very simple */
4316 on_off = !row_ptr->par->table->TopLine(cell);
4318 !row_ptr->par->table->TopAlreadyDrawed(cell)) &&
4319 !row_ptr->par->table->IsContRow(cell))
4320 pain.line(int(x_old),
4321 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4323 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4325 on_off ? Painter::line_onoffdash : Painter::line_solid);
4327 on_off = !row_ptr->par->table->BottomLine(cell);
4328 if ((!on_off && !row_ptr->par->table->RowHasContRow(cell)) ||
4329 row_ptr->par->table->VeryLastRow(cell))
4331 pain.line(int(x_old),
4332 offset + y_bottom - 1,
4334 offset + y_bottom - 1,
4336 on_off ? Painter::line_onoffdash : Painter::line_solid);
4338 on_off = !row_ptr->par->table->LeftLine(cell);
4340 pain.line(int(x_old),
4341 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4343 offset + y_bottom - 1,
4345 on_off ? Painter::line_onoffdash : Painter::line_solid);
4347 on_off = !row_ptr->par->table->RightLine(cell);
4349 pain.line(int(x) - row_ptr->par->table->AdditionalWidth(cell),
4350 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4351 int(x) - row_ptr->par->table->AdditionalWidth(cell),
4352 offset + y_bottom - 1,
4354 on_off ? Painter::line_onoffdash : Painter::line_solid);
4357 /* take care about the alignment and other spaces */
4359 x += row_ptr->par->table->GetBeginningOfTextInCell(cell);
4360 if (row_ptr->par->table->IsFirstCell(cell))
4361 --cell; // little hack, sorry
4363 } else if (row_ptr->par->IsHfill(pos)) {
4367 offset + row_ptr->baseline - DefaultHeight() / 2,
4369 offset + row_ptr->baseline,
4374 } else if (row_ptr->par->IsSeparator(pos)) {
4376 x+= SingleWidth(row_ptr->par, pos);
4379 draw(row_ptr, vpos, offset, x);
4382 /* do not forget the very last cell. This has no NEWLINE so
4383 * ignored by the code above*/
4384 if (cell == row_ptr->par->table->GetNumberOfCells()-1){
4385 x = x_old + row_ptr->par->table->WidthOfColumn(cell);
4386 on_off = !row_ptr->par->table->TopLine(cell);
4388 !row_ptr->par->table->TopAlreadyDrawed(cell)) &&
4389 !row_ptr->par->table->IsContRow(cell))
4391 pain.line(int(x_old),
4392 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4394 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4396 on_off ? Painter::line_onoffdash : Painter::line_solid);
4397 on_off = !row_ptr->par->table->BottomLine(cell);
4398 if ((!on_off && !row_ptr->par->table->RowHasContRow(cell)) ||
4399 row_ptr->par->table->VeryLastRow(cell))
4401 pain.line(int(x_old),
4402 offset + y_bottom - 1,
4404 offset + y_bottom - 1,
4406 on_off ? Painter::line_onoffdash : Painter::line_solid);
4408 on_off = !row_ptr->par->table->LeftLine(cell);
4410 pain.line(int(x_old),
4411 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4413 offset + y_bottom - 1,
4415 on_off ? Painter::line_onoffdash : Painter::line_solid);
4417 on_off = !row_ptr->par->table->RightLine(cell);
4419 pain.line(int(x) - row_ptr->par->table->AdditionalWidth(cell),
4420 offset + row_ptr->baseline - row_ptr->ascent_of_text,
4421 int(x) - row_ptr->par->table->AdditionalWidth(cell),
4422 offset + y_bottom - 1,
4424 on_off ? Painter::line_onoffdash : Painter::line_solid);
4427 /* table stuff -- end*/
4428 LyXParagraph::size_type main_body =
4429 BeginningOfMainBody(row_ptr->par);
4430 if (main_body > 0 &&
4431 (main_body-1 > last ||
4432 !row_ptr->par->IsLineSeparator(main_body-1)))
4435 while (vpos <= last) {
4436 pos = vis2log(vpos);
4437 if (main_body > 0 && pos == main_body-1) {
4438 x += fill_label_hfill
4439 + lyxfont::width(layout.labelsep, GetFont(row_ptr->par, -2))
4440 - SingleWidth(row_ptr->par, main_body-1);
4443 if (row_ptr->par->IsHfill(pos)) {
4446 offset + row_ptr->baseline - DefaultHeight() / 2,
4448 offset + row_ptr->baseline,
4451 if (HfillExpansion(row_ptr, pos)) {
4452 if (pos >= main_body) {
4454 offset + row_ptr->baseline - DefaultHeight() / 4,
4455 int(x + fill_hfill),
4456 offset + row_ptr->baseline - DefaultHeight() / 4,
4458 Painter::line_onoffdash);
4462 offset + row_ptr->baseline - DefaultHeight() / 4,
4463 int(x + fill_label_hfill),
4464 offset + row_ptr->baseline - DefaultHeight() / 4,
4466 Painter::line_onoffdash);
4468 x += fill_label_hfill;
4471 offset + row_ptr->baseline - DefaultHeight() / 2,
4473 offset + row_ptr->baseline,
4478 } else if (row_ptr->par->IsSeparator(pos)) {
4479 x += SingleWidth(row_ptr->par, pos);
4480 if (pos >= main_body)
4481 x += fill_separator;
4484 draw(row_ptr, vpos, offset, x);
4490 int LyXText::DefaultHeight() const
4492 LyXFont font(LyXFont::ALL_SANE);
4493 return int(lyxfont::maxAscent(font) + lyxfont::maxDescent(font) * 1.5);
4497 /* returns the column near the specified x-coordinate of the row
4498 * x is set to the real beginning of this column */
4499 int LyXText::GetColumnNearX(Row * row, int & x, bool & boundary) const
4502 float fill_separator, fill_hfill, fill_label_hfill;
4504 PrepareToPrint(row, tmpx, fill_separator,
4505 fill_hfill, fill_label_hfill);
4507 LyXParagraph::size_type vc = row->pos;
4508 LyXParagraph::size_type last = RowLastPrintable(row);
4509 LyXParagraph::size_type c = 0;
4510 LyXLayout const & layout = textclasslist.Style(buffer->params.textclass,
4511 row->par->GetLayout());
4512 bool left_side = false;
4514 /* table stuff -- begin */
4515 if (row->par->table) {
4516 //the last row doesn't need a newline at the end
4517 if (row->next && row->next->par == row->par
4518 && row->par->IsNewline(last))
4520 int cell = NumberOfCell(row->par, row->pos);
4521 float cell_x = tmpx + row->par->table->WidthOfColumn(cell);
4522 tmpx += row->par->table->GetBeginningOfTextInCell(cell);
4523 float last_tmpx = tmpx;
4524 while (vc <= last && tmpx <= x) {
4527 if (row->par->IsNewline(c)) {
4530 tmpx = cell_x + row->par->table->GetBeginningOfTextInCell(cell);
4531 cell_x += row->par->table->WidthOfColumn(cell);
4536 tmpx += SingleWidth(row->par, c);
4540 if (vc > row->pos && !row->par->IsNewline(c) &&
4541 (tmpx+last_tmpx)/2 > x) {
4546 /* table stuff -- end*/
4547 LyXParagraph::size_type main_body = BeginningOfMainBody(row->par);
4548 float last_tmpx = tmpx;
4550 if (main_body > 0 &&
4551 (main_body-1 > last ||
4552 !row->par->IsLineSeparator(main_body-1)))
4555 while (vc <= last && tmpx <= x) {
4558 if (main_body > 0 && c == main_body-1) {
4559 tmpx += fill_label_hfill +
4560 lyxfont::width(layout.labelsep,
4561 GetFont(row->par, -2));
4562 if (row->par->IsLineSeparator(main_body-1))
4563 tmpx -= SingleWidth(row->par, main_body-1);
4566 if (HfillExpansion(row, c)) {
4567 x += SingleWidth(row->par, c);
4571 tmpx += fill_label_hfill;
4573 else if (row->par->IsSeparator(c)) {
4574 tmpx += SingleWidth(row->par, c);
4576 tmpx+= fill_separator;
4578 tmpx += SingleWidth(row->par, c);
4582 if (vc > row->pos && (tmpx+last_tmpx)/2 > x) {
4589 if (vc > last + 1) // This shouldn't happen.
4594 if (row->pos > last) // Row is empty?
4596 else if (vc == row->pos ||
4597 (row->par->table && vc <= last && row->par->IsNewline(vc-1)) ) {
4599 if (bidi_level(c) % 2 == 1)
4602 c = vis2log(vc - 1);
4603 bool rtl = (bidi_level(c) % 2 == 1);
4604 if (left_side == rtl) {
4606 boundary = IsBoundary(row->par, c);
4610 if (!row->par->table && row->pos <= last && c > last
4611 && row->par->IsNewline(last)) {
4612 if (bidi_level(last) % 2 == 0)
4613 tmpx -= SingleWidth(row->par, last);
4615 tmpx += SingleWidth(row->par, last);
4625 /* turn the selection into a new environment. If there is no selection,
4626 * create an empty environment */
4627 void LyXText::InsertFootnoteEnvironment(LyXParagraph::footnote_kind kind)
4629 /* no footnoteenvironment in a footnoteenvironment */
4630 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
4631 WriteAlert(_("Impossible operation"),
4632 _("You can't insert a float in a float!"),
4636 /* no marginpars in minipages */
4637 if (kind == LyXParagraph::MARGIN
4638 && cursor.par->pextra_type == LyXParagraph::PEXTRA_MINIPAGE) {
4639 WriteAlert(_("Impossible operation"),
4640 _("You can't insert a marginpar in a minipage!"),
4645 /* this doesnt make sense, if there is no selection */
4646 bool dummy_selection = false;
4648 sel_start_cursor = cursor; /* dummy selection */
4649 sel_end_cursor = cursor;
4650 dummy_selection = true;
4653 LyXParagraph *tmppar;
4655 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
4656 WriteAlert(_("Impossible operation"), _("Cannot cut table."), _("Sorry."));
4660 /* a test to make sure there is not already a footnote
4661 * in the selection. */
4663 tmppar = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
4665 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos) &&
4666 tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
4667 tmppar = tmppar->next;
4669 if (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
4670 || tmppar->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
4671 WriteAlert(_("Impossible operation"),
4672 _("Float would include float!"),
4677 /* ok we have a selection. This is always between sel_start_cursor
4678 * and sel_end cursor */
4680 SetUndo(Undo::FINISH,
4681 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
4682 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
4684 if (sel_end_cursor.pos > 0
4685 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1))
4686 sel_end_cursor.pos--; /* please break before a space at
4688 if (sel_start_cursor.par == sel_end_cursor.par
4689 && sel_start_cursor.pos > sel_end_cursor.pos)
4690 sel_start_cursor.pos--;
4692 sel_end_cursor.par->BreakParagraphConservative(sel_end_cursor.pos);
4694 sel_end_cursor.par = sel_end_cursor.par->Next();
4695 sel_end_cursor.pos = 0;
4697 // don't forget to insert a dummy layout paragraph if necessary
4698 if (sel_start_cursor.par->GetLayout() != sel_end_cursor.par->layout){
4699 sel_end_cursor.par->BreakParagraphConservative(0);
4700 sel_end_cursor.par->layout = LYX_DUMMY_LAYOUT;
4701 sel_end_cursor.par = sel_end_cursor.par->next;
4704 sel_end_cursor.par->layout = LYX_DUMMY_LAYOUT;
4706 cursor = sel_end_cursor;
4708 /* please break behind a space, if there is one. The space should
4710 if (sel_start_cursor.pos > 0
4711 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos - 1))
4712 sel_start_cursor.pos--;
4713 if (sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)) {
4714 sel_start_cursor.par->Erase(sel_start_cursor.pos);
4717 sel_start_cursor.par->BreakParagraphConservative(sel_start_cursor.pos);
4718 tmppar = sel_start_cursor.par->Next();
4720 if (dummy_selection) {
4722 if (kind == LyXParagraph::TAB
4723 || kind == LyXParagraph::FIG
4724 || kind == LyXParagraph::WIDE_TAB
4725 || kind == LyXParagraph::WIDE_FIG
4726 || kind == LyXParagraph::ALGORITHM) {
4727 pair<bool, LyXTextClass::size_type> lres =
4728 textclasslist.NumberOfLayout(buffer->params.textclass,
4730 LyXTextClass::size_type lay;
4736 lay = 0; // use default layout "Standard" (0)
4738 tmppar->SetLayout(lay);
4742 if (sel_start_cursor.pos > 0) {
4743 /* the footnote-environment should begin with a standard layout.
4744 * Imagine you insert a footnote within an enumeration, you
4745 * certainly do not want an enumerated footnote! */
4749 /* this is a exception the user would sometimes expect, I hope */
4750 sel_start_cursor.par->Clear();
4754 while (tmppar != sel_end_cursor.par) {
4755 tmppar->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
4756 tmppar->footnotekind = kind;
4757 tmppar = tmppar->Next();
4760 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
4762 SetCursor(sel_start_cursor.par->Next(), 0);
4768 // returns pointer to a specified row
4769 Row * LyXText::GetRow(LyXParagraph * par,
4770 LyXParagraph::size_type pos, long & y) const
4772 Row * tmprow = firstrow;
4775 // find the first row of the specified paragraph
4776 while (tmprow->next && tmprow->par != par) {
4777 y += tmprow->height;
4778 tmprow = tmprow->next;
4781 // now find the wanted row
4782 while (tmprow->pos < pos
4784 && tmprow->next->par == par
4785 && tmprow->next->pos <= pos) {
4786 y += tmprow->height;
4787 tmprow = tmprow->next;