1 /* This file is part of
2 * ======================================================
4 * LyX, The Document Processor
6 * Copyright (C) 1995 Matthias Ettrich
7 * Copyright (C) 1995-1998 The LyX Team.
9 *======================================================*/
16 #pragma implementation "table.h"
20 #include "lyxparagraph.h"
22 #include "support/textutils.h"
23 #include "lyx_gui_misc.h"
26 #include "bufferparams.h"
28 #include "lyxscreen.h"
29 #include "minibuffer.h"
32 static const int LYX_PAPER_MARGIN = 20;
34 extern int mono_video;
35 extern int reverse_video;
36 extern int fast_selection;
37 extern BufferView *current_view;
38 extern int UnlockInset(UpdatableInset* inset);
41 extern int bibitemMaxWidth(const class LyXFont &);
44 extern MiniBuffer *minibuffer;
46 int LyXText::SingleWidth(LyXParagraph *par, int pos)
48 char c = par->GetChar(pos);
49 return SingleWidth(par, pos, c);
53 int LyXText::SingleWidth(LyXParagraph *par, int pos, char c)
55 LyXFont font = GetFont(par, pos);
57 // The most common case is handled first (Asger)
61 } else if (IsHfillChar(c)) {
62 return 3; /* because of the representation
63 * as vertical lines */
65 } else if (c == LYX_META_FOOTNOTE ||
66 c == LYX_META_MARGIN ||
69 c == LYX_META_WIDE_FIG ||
70 c == LYX_META_WIDE_TAB ||
71 c == LYX_META_ALGORITHM)
84 case LYX_META_ALGORITHM:
87 case LYX_META_WIDE_FIG:
90 case LYX_META_WIDE_TAB:
93 case LYX_META_FOOTNOTE:
99 return font.stringWidth(fs);
102 else if (c == LYX_META_INSET) {
103 Inset *tmpinset=par->GetInset(pos);
105 return par->GetInset(pos)->Width(font);
109 } else if (IsSeparatorChar(c))
111 else if (IsNewlineChar(c))
113 return font.width(c);
117 /* returns the paragraph position of the last character in the
119 int LyXText::RowLast(Row *row)
122 return row->par->Last()-1;
123 else if (row->next->par != row->par)
124 return row->par->Last()-1;
126 return row->next->pos - 1;
130 void LyXText::Draw(Row *row, int &pos, LyXScreen &scr, int offset, float &x)
132 char c = row->par->GetChar(pos);
134 if (IsNewlineChar(c)) {
136 // Draw end-of-line marker
138 LyXFont font = GetFont(row->par, pos);
139 int asc = font.maxAscent();
140 int wid = font.width('n');
141 int y = (offset + row->baseline);
143 p[0].x = int(x + wid*0.375); p[0].y = int(y - 0.875*asc*0.75);
144 p[1].x = int(x); p[1].y = int(y - 0.500*asc*0.75);
145 p[2].x = int(x + wid*0.375); p[2].y = int(y - 0.125*asc*0.75);
146 scr.drawLines(::getGC(gc_new_line),p, 3);
148 p[0].x = int(x); p[0].y = int(y - 0.500*asc*0.75);
149 p[1].x = int(x + wid); p[1].y = int(y - 0.500*asc*0.75);
150 p[2].x = int(x + wid); p[2].y = int(y - asc*0.75);
151 scr.drawLines(::getGC(gc_new_line),p, 3);
155 LyXFont font = GetFont(row->par, pos);
156 LyXFont font2 = font;
158 if (c == LYX_META_FOOTNOTE ||
159 c == LYX_META_MARGIN ||
162 c == LYX_META_WIDE_FIG ||
163 c == LYX_META_WIDE_TAB ||
164 c == LYX_META_ALGORITHM) {
167 case LYX_META_MARGIN:
169 /* draw a sign at the left margin! */
170 scr.drawText(font, "!", 1, offset + row->baseline,
171 (LYX_PAPER_MARGIN - font.width('!'))/2);
179 case LYX_META_ALGORITHM:
182 case LYX_META_WIDE_FIG:
185 case LYX_META_WIDE_TAB:
188 case LYX_META_FOOTNOTE:
195 /* calculate the position of the footnotemark */
196 int y = (row->baseline - font2.maxAscent()
199 font.setColor(LyXFont::INSET);
203 /* draw it and set new x position */
204 x += scr.drawString(font, fs, offset + y, int(x));
206 scr.drawLine(gc_foot, offset + row->baseline,
207 int(tmpx), int(x - tmpx));
211 } else if (c == LYX_META_INSET) {
212 Inset *tmpinset = row->par->GetInset(pos);
214 tmpinset->Draw(font, scr, offset + row->baseline, x);
219 /* usual characters, no insets */
221 // Collect character that we can draw in one command
223 // This is dirty, but fast. Notice that it will never be too small.
224 // For the record, I'll note that Microsoft Word has a limit
225 // of 768 here. We have none :-) (Asger)
226 static char textstring[1024];
228 int last = RowLast(row);
229 // Prevent crash in the extremely unlikely event
230 // that our array is too small
231 if (last > pos+1020) last = pos + 1020;
238 while (pos <= last &&
239 (unsigned char) (c = row->par->GetChar(pos)) > ' ' &&
240 font2 == GetFont(row->par, pos)) {
248 // If monochrome and LaTeX mode, provide reverse background
249 if (mono_video && font.latex() == LyXFont::ON) {
250 int a=font.maxAscent(), d=font.maxDescent();
251 scr.fillRectangle(gc_copy, int(tmpx), offset + row->baseline-a,
252 font.textWidth(textstring, i), a+d);
255 /* Draw text and set the new x position */
256 x += scr.drawText(font, textstring, i, offset + row->baseline,
258 /* what about underbars? */
259 if (font.underbar() == LyXFont::ON && font.latex() != LyXFont::ON) {
260 scr.drawLine(gc_copy, offset + row->baseline + 2,
261 int(tmpx), int(x - tmpx));
264 // If we want ulem.sty support, drawing
265 // routines should go here. (Asger)
266 // Why shouldn't LyXFont::drawText handle it internally?
270 /* Returns the left beginning of the text.
271 * This information cannot be taken from the layouts-objekt, because in
272 * LaTeX the beginning of the text fits in some cases (for example sections)
273 * exactly the label-width. */
274 int LyXText::LeftMargin(Row* row)
279 LyXParagraph *newpar;
281 layout = lyxstyle.Style(parameters->textclass, row->par->GetLayout());
283 string parindent = layout->parindent;
285 /* table stuff -- begin*/
288 /* table stuff -- end*/
290 x = LYX_PAPER_MARGIN;
292 x += lyxstyle.TextClass(parameters->textclass)->
293 defaultfont.signedStringWidth(lyxstyle.TextClass(parameters->textclass)->leftmargin);
295 if (row->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
296 LyXFont font(LyXFont::ALL_SANE);
297 font.setSize(LyXFont::SIZE_SMALL);
298 x += font.textWidth("Mwide-figM", 10) + LYX_PAPER_MARGIN/2;
301 /* this is the way, LyX handles the LaTeX-Environments.
302 * I have had this idea very late, so it seems to be a
303 * later added hack and this is true */
304 if (!row->par->GetDepth()) {
305 if (!row->par->GetLayout()) {
306 /* find the previous same level paragraph */
307 if (row->par->FirstPhysicalPar()->Previous()) {
308 newpar = row->par->DepthHook(row->par->GetDepth());
309 if (newpar && lyxstyle.Style(parameters->textclass, newpar->GetLayout())->nextnoindent)
315 /* find the next level paragraph */
317 newpar = row->par->DepthHook(row->par->GetDepth()-1);
319 /* make a corresponding row. Needed to call LeftMargin() */
321 /* check wether it is a sufficent paragraph */
322 if (newpar && newpar->footnoteflag == row->par->footnoteflag
323 && lyxstyle.Style(parameters->textclass,
324 newpar->GetLayout())->isEnvironment()) {
325 dummyrow.par = newpar;
326 dummyrow.pos = newpar->Last();
327 x = LeftMargin(&dummyrow);
330 /* this is no longer an error, because this function is used
331 * to clear impossible depths after changing a layout. Since there
332 * is always a redo, LeftMargin() is always called */
334 /* printf("LYX_ERROR (text, LeftMargin()) impossible depth \n");*/
335 row->par->FirstPhysicalPar()->depth = 0;
338 if (newpar && !row->par->GetLayout()) {
339 if (newpar->FirstPhysicalPar()->noindent)
342 parindent = lyxstyle.Style(parameters->textclass,
343 newpar->GetLayout())->parindent;
348 labelfont = GetFont(row->par, -2);
349 switch (layout->margintype) {
351 if (!layout->leftmargin.empty()) {
352 x += lyxstyle.TextClass(parameters->textclass)->defaultfont.signedStringWidth(layout->leftmargin);
354 if (!row->par->GetLabestring().empty()) {
355 x += labelfont.signedStringWidth(layout->labelindent);
356 x += labelfont.stringWidth(row->par->GetLabestring());
357 x += labelfont.stringWidth(layout->labelsep);
361 x += labelfont.signedStringWidth(layout->labelindent);
362 if (row->pos >= BeginningOfMainBody(row->par)) {
363 if (!row->par->GetLabelWidthString().empty()) {
364 x += labelfont.stringWidth(row->par->GetLabelWidthString());
365 x += labelfont.stringWidth(layout->labelsep);
370 x += ( lyxstyle.TextClass(parameters->textclass)->defaultfont.signedStringWidth(layout->leftmargin) * 4
371 / (row->par->GetDepth() + 4));
373 case MARGIN_FIRST_DYNAMIC:
374 if (layout->labeltype == LABEL_MANUAL) {
375 if (row->pos >= BeginningOfMainBody(row->par)) {
376 x += labelfont.signedStringWidth(layout->leftmargin);
378 x += labelfont.signedStringWidth(layout->labelindent);
382 // Special case to fix problems with theorems (JMarc)
383 || (layout->labeltype == LABEL_STATIC
384 && layout->latextype == LATEX_ENVIRONMENT
385 && ! row->par->IsFirstInSequence())) {
386 x += labelfont.signedStringWidth(layout->leftmargin);
387 } else if (layout->labeltype != LABEL_TOP_ENVIRONMENT
388 && layout->labeltype != LABEL_BIBLIO
389 && layout->labeltype != LABEL_CENTERED_TOP_ENVIRONMENT) {
390 x += labelfont.signedStringWidth(layout->labelindent);
391 x += labelfont.stringWidth(layout->labelsep);
392 x += labelfont.stringWidth(row->par->GetLabestring());
397 case MARGIN_RIGHT_ADDRESS_BOX:
399 /* ok, a terrible hack. The left margin depends on the widest row
400 * in this paragraph. Do not care about footnotes, they are *NOT*
401 * allowed in the LaTeX realisation of this layout. */
403 /* find the first row of this paragraph */
405 while (tmprow->previous && tmprow->previous->par == row->par)
406 tmprow = tmprow->previous;
408 int minfill = tmprow->fill;
409 while (tmprow-> next && tmprow->next->par == row->par) {
410 tmprow = tmprow->next;
411 if (tmprow->fill < minfill)
412 minfill = tmprow->fill;
415 x += lyxstyle.TextClass(parameters->textclass)->defaultfont.signedStringWidth(layout->leftmargin);
420 if (row->par->pextra_type == PEXTRA_INDENT) {
421 if (!row->par->pextra_widthp.empty()) {
422 x += paperwidth * atoi(row->par->pextra_widthp.c_str()) / 100;
423 } else if (!row->par->pextra_width.empty()) {
424 int xx = VSpace(row->par->pextra_width).inPixels();
427 xx = paperwidth * 80 / 100;
429 } else { // should not happen
430 LyXFont font(LyXFont::ALL_SANE);
431 x += font.stringWidth("XXXXXX");
436 if (row->par->FirstPhysicalPar()->align == LYX_ALIGN_LAYOUT)
437 align = layout->align;
439 align = row->par->FirstPhysicalPar()->align;
441 /* set the correct parindent */
443 if ((layout->labeltype == LABEL_NO_LABEL
444 || layout->labeltype == LABEL_TOP_ENVIRONMENT
445 || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT
446 || (layout->labeltype == LABEL_STATIC
447 && layout->latextype == LATEX_ENVIRONMENT
448 && ! row->par->IsFirstInSequence()))
449 && row->par == row->par->FirstPhysicalPar()
450 && align == LYX_ALIGN_BLOCK
451 && !row->par->noindent
452 && (row->par->layout ||
453 parameters->paragraph_separation == LYX_PARSEP_INDENT))
454 x += lyxstyle.TextClass(parameters->textclass)->defaultfont.stringWidth(parindent);
456 if (layout->labeltype==LABEL_BIBLIO) { // ale970405 Right width for bibitems
457 x += bibitemMaxWidth(lyxstyle.TextClass(parameters->textclass)->defaultfont);
465 int LyXText::RightMargin(Row *row)
470 LyXParagraph *newpar;
472 layout = lyxstyle.Style(parameters->textclass, row->par->GetLayout());
474 x = LYX_PAPER_MARGIN;
476 x += lyxstyle.TextClass(parameters->textclass)->
477 defaultfont.signedStringWidth(lyxstyle.TextClass(parameters->textclass)->rightmargin);
478 if (row->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
479 x += LYX_PAPER_MARGIN/2;
482 /* this is the way, LyX handles the LaTeX-Environments.
483 * I have had this idea very late, so it seems to be a
484 * later added hack and this is true */
485 if (row->par->GetDepth()) {
486 /* find the next level paragraph */
491 newpar = newpar->FirstPhysicalPar()->Previous();
493 newpar = newpar->FirstPhysicalPar();
494 } while (newpar && newpar->GetDepth() >= row->par->GetDepth()
495 && newpar->footnoteflag == row->par->footnoteflag);
497 /* make a corresponding row. Needed to call LeftMargin() */
499 /* check wether it is a sufficent paragraph */
500 if (newpar && newpar->footnoteflag == row->par->footnoteflag
501 && lyxstyle.Style(parameters->textclass,
502 newpar->GetLayout())->isEnvironment()) {
503 dummyrow.par = newpar;
505 x = RightMargin(&dummyrow);
508 /* this is no longer an error, because this function is used
509 * to clear impossible depths after changing a layout. Since there
510 * is always a redo, LeftMargin() is always called */
512 /* printf("LYX_ERROR (text, LeftMargin()) impossible depth \n");*/
513 row->par->FirstPhysicalPar()->depth = 0;
517 //lyxerr << "rightmargin: " << layout->rightmargin << endl;
518 x += (lyxstyle.TextClass(parameters->textclass)->defaultfont.signedStringWidth(layout->rightmargin) * 4
519 / (row->par->GetDepth() + 4));
525 int LyXText::LabelEnd (Row *row)
527 if (lyxstyle.Style(parameters->textclass, row->par->GetLayout())->margintype == MARGIN_MANUAL) {
530 tmprow.pos = row->par->Last();
531 return LeftMargin(&tmprow); /* just the beginning
532 * of the main body */
535 return 0; /* LabelEnd is only needed, if the
536 * layout fills a flushleft
541 /* table stuff -- begin*/
542 int LyXText::NumberOfCell(LyXParagraph *par, int pos)
546 while (tmp_pos < pos) {
547 if (par->IsNewline(tmp_pos))
555 int LyXText::WidthOfCell(LyXParagraph *par, int &pos)
558 while (pos < par->Last() && !par->IsNewline(pos)) {
559 w += SingleWidth(par, pos);
562 if (par->IsNewline(pos))
568 char LyXText::HitInTable(Row* row, int x)
571 float fill_separator, fill_hfill, fill_label_hfill;
572 if (!row->par->table)
574 PrepareToPrint(row, tmpx, fill_separator, fill_hfill, fill_label_hfill);
575 return (x > tmpx && x < tmpx + row->par->table->WidthOfTable());
579 bool LyXText::MouseHitInTable(int x, long y)
581 Row *row = GetRowNearY(y);
582 return HitInTable(row, x);
586 /* table stuff -- end*/
589 /* get the next breakpoint in a given paragraph */
590 int LyXText::NextBreakPoint(Row* row, int width)
593 int last_separator = -1; /* position of the last possible breakpoint
594 * -1 isn't a suitable value, but a flag */
598 LyXParagraph *par = row->par;
601 /* table stuff -- begin*/
604 && (!par->IsNewline(pos)
605 || !par->table->IsFirstCell(NumberOfCell(par, pos+1)))) {
606 if (par->GetChar(pos) == LYX_META_INSET &&
607 par->GetInset(pos) && par->GetInset(pos)->Display()){
608 par->GetInset(pos)->SetDisplay(false);
614 /* table stuff -- end*/
616 left_margin = LabelEnd(row);
617 width -= RightMargin(row);
618 int main_body = BeginningOfMainBody(par);
619 LyXLayout* layout = lyxstyle.Style(parameters->textclass, par->GetLayout());
623 if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
624 /* special code for right address boxes, only newlines count */
625 while (i < par->Last()) {
626 if (par->IsNewline(i)) {
628 i = par->Last() - 1;/* this means break */
630 } else if (par->GetChar(i) == LYX_META_INSET &&
631 par->GetInset(i) && par->GetInset(i)->Display()){
632 par->GetInset(i)->SetDisplay(false);
637 // Last position is an invariant
638 int const last = par->Last();
639 /* this is the usual handling */
641 while (x < width && i < last) {
643 if (IsNewlineChar(c)) {
645 x = width; /* this means break */
646 } else if (c == LYX_META_INSET &&
647 par->GetInset(i) && par->GetInset(i)->Display()){
648 /* check wether a Display() inset is valid here .
649 if not, change it to non-display*/
650 if (layout->isCommand()
651 || (layout->labeltype == LABEL_MANUAL
652 && i < BeginningOfMainBody(par))){
653 /* display istn't allowd */
654 par->GetInset(i)->SetDisplay(false);
655 x += SingleWidth(par, i, c);
657 /* inset is display. So break the line here */
661 if (IsLineSeparatorChar(par->GetChar(i+1)))
664 last_separator = last; // to avoid extra rows
666 last_separator = i - 1;
667 x = width; /* this means break */
670 if (IsLineSeparatorChar(c))
672 x += SingleWidth(par, i, c);
675 if (i == main_body) {
676 x += GetFont(par, -2).stringWidth(layout->labelsep);
677 if (par->IsLineSeparator(i - 1))
678 x-= SingleWidth(par, i - 1);
683 /* end of paragraph is always a suitable separator */
684 if (i == last && x < width)
688 /* well, if last_separator is still 0, the line isn't breakable.
689 * don't care and cut simply at the end */
690 if (last_separator < 0) {
694 /* manual labels cannot be broken in LaTeX, do not care */
695 if (main_body && last_separator < main_body)
696 last_separator = main_body - 1;
698 return last_separator;
702 /* returns the minimum space a row needs on the screen in pixel */
703 int LyXText::Fill(Row *row, int paperwidth)
705 int w,i, last, fill, left_margin;
706 /* get the pure distance */
709 /* table stuff -- begin*/
710 if (row->par->table) {
711 /* for tables FILL does calculate the widthes of each cell in
714 int cell = NumberOfCell(row->par, pos);
717 row->par->table->SetWidthOfCell(cell, WidthOfCell(row->par, pos));
719 } while (pos <= last && !row->par->table->IsFirstCell(cell));
720 /* don't forget the very last table cell without characters */
721 if (cell == row->par->table->GetNumberOfCells()-1)
722 row->par->table->SetWidthOfCell(cell, WidthOfCell(row->par, pos));
724 return 0; /* width of table cannot be returned since
725 * we cannot guarantee its correct value at
728 /* table stuff -- end*/
730 left_margin = LabelEnd(row);
732 /* if the row ends with newline, this newline will not be relevant */
733 if (last >= 0 && row->par->IsNewline(last))
736 /* if the row ends with a space, this space will not be relevant */
737 if (last >= 0 && row->par->IsLineSeparator(last))
740 /* special handling of the right address boxes */
741 if (lyxstyle.Style(parameters->textclass, row->par->GetLayout())->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
742 int tmpfill = row->fill;
743 row->fill = 0; /* the minfill in MarginLeft() */
747 /* the old way, impossible when using environments: */
748 /* w = LyXStringWidth(lyxstyle.Style(parameters->textclass, row->par->GetLayout())->font, */
749 /* lyxstyle.Style(parameters->textclass, row->par->GetLayout())->leftmargin); */
754 int main_body = BeginningOfMainBody(row->par);
755 LyXLayout *layout = lyxstyle.Style(parameters->textclass,
756 row->par->GetLayout());
759 w += SingleWidth(row->par, i);
761 if (i == main_body) {
762 w += GetFont(row->par, -2).stringWidth(layout->labelsep);
763 if (row->par->IsLineSeparator(i - 1))
764 w-= SingleWidth(row->par, i - 1);
770 fill = paperwidth - w - RightMargin(row);
775 /* returns the minimum space a manual label needs on the screen in pixel */
776 int LyXText::LabelFill(Row *row)
781 last = BeginningOfMainBody(row->par) - 1;
782 /* -1 because a label ends either with a space that is in the label,
783 * or with the beginning of a footnote that is outside the label. */
785 // I don't understand this code in depth, but sometimes "last" is less than
786 // 0 and this causes a crash. This fix seems to work correctly, but I
787 // bet the real error is elsewhere. The bug is triggered when you have an
788 // open footnote in a paragraph environment with a manual label. (Asger)
789 if (last<0) last = 0;
791 if (row->par->IsLineSeparator(last)) /* a sepearator at this end
798 w += SingleWidth(row->par, i);
802 if (!row->par->labelwidthstring.empty()) {
803 fill = GetFont(row->par, -2).stringWidth(row->par->labelwidthstring) - w;
813 /* returns the number of separators in the specified row. The separator
814 * on the very last column doesnt count */
815 int LyXText::NumberOfSeparators(Row *row)
822 int main_body = BeginningOfMainBody(row->par);
825 for (; p < last; p++) {
826 if (row->par->IsSeparator(p)) {
834 /* returns the number of hfills in the specified row. The LyX-Hfill is
835 * a LaTeX \hfill so that the hfills at the beginning and at the end were
836 * ignored. This is *MUCH* more usefull than not to ignore! */
837 int LyXText::NumberOfHfills(Row *row)
843 if (first) { /* hfill *DO* count at the beginning
845 while(first <= last && row->par->IsHfill(first))
850 int main_body = BeginningOfMainBody(row->par);
851 if (first < main_body)
853 for (p = first; p <= last; p++) { /* last, because the end is ignored! */
854 if (row->par->IsHfill(p)) {
862 /* like NumberOfHfills, but only those in the manual label! */
863 int LyXText::NumberOfLabelHfills(Row *row)
869 if (first) { /* hfill *DO* count at the beginning
871 while(first < last && row->par->IsHfill(first))
876 int main_body = BeginningOfMainBody(row->par);
878 if (last > main_body)
881 for (p = first; p < last; p++) { /* last, because the end is ignored! */
882 if (row->par->IsHfill(p)) {
890 /* returns true, if a expansion is needed.
891 * Rules are given by LaTeX */
892 bool LyXText::HfillExpansion(Row *row_ptr, int pos)
894 /* by the way, is it a hfill? */
895 if (!row_ptr->par->IsHfill(pos))
898 /* at the end of a row it does not count */
899 if (pos >= RowLast(row_ptr))
902 /* at the beginning of a row it does not count, if it is not
903 * the first row of a paragaph */
907 /* in some labels it does not count */
908 if ( lyxstyle.Style(parameters->textclass, row_ptr->par->GetLayout())->margintype != MARGIN_MANUAL &&
909 pos < BeginningOfMainBody(row_ptr->par))
912 /* if there is anything between the first char of the row and
913 * the sepcified position that is not a newline and not a hfill,
914 * the hfill will count, otherwise not */
916 int i = row_ptr->pos;
917 while (i < pos && (row_ptr->par->IsNewline(i)
918 || row_ptr->par->IsHfill(i)))
925 void LyXText::SetHeightOfRow(Row *row_ptr)
927 /* get the maximum ascent and the maximum descent */
928 int asc, maxasc, desc, maxdesc, pos_end, pos, labeladdon;
930 float layoutdesc = 0;
932 LyXParagraph *par, *firstpar;
936 /* this must not happen before the currentrow for clear reasons.
937 so the trick is just to set the current row onto this row */
939 GetRow(row_ptr->par, row_ptr->pos, unused_y);
941 /* ok , let us initialize the maxasc and maxdesc value.
942 * This depends in LaTeX of the font of the last character
943 * in the paragraph. The hack below is necessary because
944 * of the possibility of open footnotes */
946 /* Correction: only the fontsize count. The other properties
947 are taken from the layoutfont. Nicer on the screen :) */
949 par = row_ptr->par->LastPhysicalPar();
950 firstpar = row_ptr->par->FirstPhysicalPar();
952 LyXLayout *layout = lyxstyle.Style(parameters->textclass, firstpar->GetLayout());
954 LyXFont font = GetFont(par, par->Last()-1);
955 LyXFont::FONT_SIZE size = font.size();
956 font = GetFont(par, -1);
959 LyXFont labelfont = GetFont(par, -2);
961 maxasc = int(font.maxAscent() *
962 layout->spacing.getValue() *
963 parameters->spacing.getValue());
964 maxdesc = int(font.maxDescent() *
965 layout->spacing.getValue() *
966 parameters->spacing.getValue());
968 pos_end = RowLast(row_ptr);
972 // Check if any insets are larger
973 for (pos = row_ptr->pos; pos <= pos_end; pos++) {
974 if (row_ptr->par->GetChar(pos) == LYX_META_INSET) {
975 tmpfont = GetFont(row_ptr->par, pos);
976 tmpinset = row_ptr->par->GetInset(pos);
978 asc = tmpinset->Ascent(tmpfont);
979 desc = tmpinset->Descent(tmpfont);
988 // Check if any custom fonts are larger (Asger)
989 // This is not completely correct, but we can live with the small,
990 // cosmetic error for now.
991 LyXFont::FONT_SIZE maxsize = row_ptr->par->HighestFontInRange(row_ptr->pos, pos_end);
992 if (maxsize > font.size()) {
993 font.setSize(maxsize);
995 asc = font.maxAscent();
996 desc = font.maxDescent();
1003 /* table stuff -- begin*/
1004 if (row_ptr->par->table){
1005 // stretch the rows a bit
1009 /* table stuff -- end*/
1011 // This is nicer with box insets:
1015 row_ptr->ascent_of_text = maxasc;
1017 /* is it a top line? */
1018 if (row_ptr->pos == 0
1019 && row_ptr->par == firstpar) {
1021 /* some parksips VERY EASY IMPLEMENTATION */
1022 if (parameters->paragraph_separation == LYX_PARSEP_SKIP) {
1023 if (layout->isParagraph()
1024 && firstpar->GetDepth() == 0
1025 && firstpar->Previous())
1026 maxasc += parameters->getDefSkip().inPixels();
1027 else if (firstpar->Previous()
1028 && lyxstyle.Style(parameters->textclass,
1029 firstpar->Previous()->GetLayout())->isParagraph()
1030 && firstpar->Previous()->GetDepth() == 0)
1031 // is it right to use defskip here too? (AS)
1032 maxasc += parameters->getDefSkip().inPixels();
1035 /* the paper margins */
1036 if (!row_ptr->par->previous)
1037 maxasc += LYX_PAPER_MARGIN;
1039 /* add the vertical spaces, that the user added */
1040 if (firstpar->added_space_top.kind() != VSpace::NONE)
1041 maxasc += int(firstpar->added_space_top.inPixels());
1043 /* do not forget the DTP-lines!
1044 * there height depends on the font of the nearest character */
1045 if (firstpar->line_top)
1046 maxasc += 2 * GetFont(firstpar, 0).ascent('x');
1048 /* and now the pagebreaks */
1049 if (firstpar->pagebreak_top)
1050 maxasc += 3 * DefaultHeight();
1052 /* this is special code for the chapter, since the label of this
1053 * layout is printed in an extra row */
1054 if (layout->labeltype == LABEL_COUNTER_CHAPTER
1055 && parameters->secnumdepth>=0) {
1056 labeladdon = int(labelfont.maxDescent() *
1057 layout->spacing.getValue() *
1058 parameters->spacing.getValue())
1059 + int(labelfont.maxAscent() *
1060 layout->spacing.getValue() *
1061 parameters->spacing.getValue());
1064 /* special code for the top label */
1065 if ((layout->labeltype == LABEL_TOP_ENVIRONMENT
1066 || layout->labeltype == LABEL_BIBLIO
1067 || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)
1068 && row_ptr->par->IsFirstInSequence()
1069 && !row_ptr->par->GetLabestring().empty()) {
1071 (labelfont.maxAscent() *
1072 layout->spacing.getValue() *
1073 parameters->spacing.getValue())
1074 +(labelfont.maxDescent() *
1075 layout->spacing.getValue() *
1076 parameters->spacing.getValue())
1077 + layout->topsep * DefaultHeight()
1078 + layout->labelbottomsep * DefaultHeight());
1081 /* and now the layout spaces, for example before and after a section,
1082 * or between the items of a itemize or enumerate environment */
1084 if (!firstpar->pagebreak_top) {
1085 LyXParagraph *prev = row_ptr->par->Previous();
1087 prev = row_ptr->par->DepthHook(row_ptr->par->GetDepth());
1088 if (prev && prev->GetLayout() == firstpar->GetLayout()
1089 && prev->GetDepth() == firstpar->GetDepth()
1090 && prev->GetLabelWidthString() == firstpar->GetLabelWidthString())
1092 layoutasc = (layout->itemsep * DefaultHeight());
1094 else if (row_ptr->previous) {
1095 tmptop = layout->topsep;
1097 if (row_ptr->previous->par->GetDepth() >= row_ptr->par->GetDepth())
1098 tmptop-=lyxstyle.Style(parameters->textclass, row_ptr->previous->par->GetLayout())->bottomsep;
1101 layoutasc = (tmptop * DefaultHeight());
1103 else if (row_ptr->par->line_top){
1104 tmptop = layout->topsep;
1107 layoutasc = (tmptop * DefaultHeight());
1110 prev = row_ptr->par->DepthHook(row_ptr->par->GetDepth()-1);
1112 maxasc += int(lyxstyle.Style(parameters->textclass,
1113 prev->GetLayout())->parsep * DefaultHeight());
1116 if (firstpar->Previous()
1117 && firstpar->Previous()->GetDepth() == 0
1118 && firstpar->Previous()->GetLayout() != firstpar->GetLayout()) {
1121 else if (firstpar->Previous()){
1122 maxasc += int(layout->parsep * DefaultHeight());
1128 /* is it a bottom line? */
1129 if (row_ptr->par->ParFromPos(RowLast(row_ptr) + 1) == par
1130 && (!row_ptr->next || row_ptr->next->par != row_ptr->par)) {
1132 /* the paper margins */
1134 maxdesc += LYX_PAPER_MARGIN;
1136 /* add the vertical spaces, that the user added */
1137 if (firstpar->added_space_bottom.kind() != VSpace::NONE)
1138 maxdesc += int(firstpar->added_space_bottom.inPixels());
1140 /* do not forget the DTP-lines!
1141 * there height depends on the font of the nearest character */
1142 if (firstpar->line_bottom)
1143 maxdesc += 2 * (GetFont(par, par->Last()-1).ascent('x'));
1145 /* and now the pagebreaks */
1146 if (firstpar->pagebreak_bottom)
1147 maxdesc += 3 * DefaultHeight();
1149 /* and now the layout spaces, for example before and after a section,
1150 * or between the items of a itemize or enumerate environment */
1151 if (!firstpar->pagebreak_bottom && row_ptr->par->Next()) {
1152 LyXParagraph *nextpar = row_ptr->par->Next();
1153 LyXParagraph *comparepar = row_ptr->par;
1157 if (comparepar->GetDepth() > nextpar->GetDepth()) {
1158 usual = (lyxstyle.Style(parameters->textclass, comparepar->GetLayout())->bottomsep * DefaultHeight());
1159 comparepar = comparepar->DepthHook(nextpar->GetDepth());
1160 if (comparepar->GetLayout()!=nextpar->GetLayout()
1161 || nextpar->GetLabelWidthString() !=
1162 comparepar->GetLabelWidthString())
1163 unusual = (lyxstyle.Style(parameters->textclass, comparepar->GetLayout())->bottomsep * DefaultHeight());
1165 if (unusual > usual)
1166 layoutdesc = unusual;
1170 else if (comparepar->GetDepth() == nextpar->GetDepth()) {
1172 if (comparepar->GetLayout()!= nextpar->GetLayout()
1173 || nextpar->GetLabelWidthString() !=
1174 comparepar->GetLabelWidthString())
1175 layoutdesc = int(lyxstyle.Style(parameters->textclass, comparepar->GetLayout())->bottomsep * DefaultHeight());
1180 /* incalculate the layout spaces */
1181 maxasc += int(layoutasc * 2 / (2 + firstpar->GetDepth()));
1182 maxdesc += int(layoutdesc * 2 / (2 + firstpar->GetDepth()));
1184 /* table stuff -- begin*/
1185 if (row_ptr->par->table){
1186 maxasc += row_ptr->par->table->
1187 AdditionalHeight(NumberOfCell(row_ptr->par, row_ptr->pos));
1189 /* table stuff -- end*/
1191 /* calculate the new height of the text */
1192 height -= row_ptr->height;
1194 row_ptr->height=maxasc+maxdesc+labeladdon;
1195 row_ptr->baseline=maxasc+labeladdon;
1197 height += row_ptr->height;
1201 /* Appends the implicit specified paragraph behind the specified row,
1202 * start at the implicit given position */
1203 void LyXText::AppendParagraph(Row *row)
1207 bool not_ready = true;
1209 // Get the width of the row
1212 // The last character position of a paragraph is an invariant so we can
1213 // safely get it here. (Asger)
1214 int lastposition = row->par->Last();
1217 // Get the next breakpoint
1218 z = NextBreakPoint(row, paperwidth);
1222 // Insert the new row
1223 if (z < lastposition) {
1225 InsertRow(row, row->par, z);
1232 // Set the dimensions of the row
1233 tmprow->fill = Fill(tmprow, paperwidth);
1234 SetHeightOfRow(tmprow);
1236 } while (not_ready);
1240 void LyXText::BreakAgain(Row *row)
1245 Row *tmprow, *tmprow2;
1250 /* get the next breakpoint */
1251 z = NextBreakPoint(row, paperwidth);
1255 if (z < row->par->Last() ) {
1256 if (!row->next || (row->next && row->next->par != row->par)) {
1257 /* insert a new row */
1259 InsertRow(row, row->par, z);
1267 not_ready = 0; /* the rest will not change */
1274 /* if there are some rows too much, delete them */
1275 /* only if you broke the whole paragraph! */
1277 while (tmprow2->next && tmprow2->next->par == row->par) {
1278 tmprow2 = tmprow2->next;
1280 while (tmprow2 != row) {
1281 tmprow2 = tmprow2->previous;
1282 RemoveRow(tmprow2->next);
1287 /* set the dimensions of the row */
1288 tmprow->fill = Fill(tmprow, paperwidth);
1289 SetHeightOfRow(tmprow);
1290 } while (not_ready);
1294 /* this is just a little changed version of break again */
1295 void LyXText::BreakAgainOneRow(Row *row)
1300 Row *tmprow, *tmprow2;
1304 /* get the next breakpoint */
1305 z = NextBreakPoint(row, paperwidth);
1309 if (z < row->par->Last() ) {
1310 if (!row->next || (row->next && row->next->par != row->par)) {
1311 /* insert a new row */
1313 InsertRow(row, row->par, z);
1321 not_ready = 0; /* the rest will not change */
1328 /* if there are some rows too much, delete them */
1329 /* only if you broke the whole paragraph! */
1331 while (tmprow2->next && tmprow2->next->par == row->par) {
1332 tmprow2 = tmprow2->next;
1334 while (tmprow2 != row) {
1335 tmprow2 = tmprow2->previous;
1336 RemoveRow(tmprow2->next);
1341 /* set the dimensions of the row */
1342 tmprow->fill = Fill(tmprow, paperwidth);
1343 SetHeightOfRow(tmprow);
1347 void LyXText::BreakParagraph(char keep_layout)
1349 LyXLayout *layout = lyxstyle.Style(parameters->textclass,
1350 cursor.par->GetLayout());
1352 /* table stuff -- begin*/
1353 if (cursor.par->table) {
1354 // breaking of tables is only allowed at the beginning or the end */
1355 if (cursor.pos && cursor.pos < cursor.par->last &&
1356 !cursor.par->table->ShouldBeVeryLastCell(NumberOfCell(cursor.par, cursor.pos)))
1357 return; /* no breaking of tables allowed */
1359 /* table stuff -- end*/
1361 /* this is only allowed, if the current paragraph is not empty or caption*/
1362 if ((cursor.par->Last() <= 0 && !cursor.par->IsDummy())
1364 layout->labeltype!=LABEL_SENSITIVE)
1367 SetUndo(Undo::INSERT,
1368 cursor.par->ParFromPos(cursor.pos)->previous,
1369 cursor.par->ParFromPos(cursor.pos)->next);
1371 /* table stuff -- begin*/
1372 if (cursor.par->table) {
1373 int cell = NumberOfCell(cursor.par, cursor.pos);
1374 if (cursor.par->table->ShouldBeVeryLastCell(cell))
1375 SetCursor(cursor.par,cursor.par->last);
1377 /* table stuff -- end*/
1378 /* please break alway behind a space */
1379 if (cursor.pos < cursor.par->Last()
1380 && cursor.par->IsLineSeparator(cursor.pos))
1383 /* break the paragraph */
1387 keep_layout = layout->isEnvironment();
1388 cursor.par->BreakParagraph(cursor.pos, keep_layout);
1390 /* table stuff -- begin*/
1391 if (cursor.par->table){
1392 // the table should stay with the contents
1394 cursor.par->Next()->table = cursor.par->table;
1395 cursor.par->table = 0;
1398 /* table stuff -- end*/
1400 /* well this is the caption hack since one caption is really enough */
1401 if (layout->labeltype == LABEL_SENSITIVE){
1403 cursor.par->SetLayout(0); /* set the new paragraph to standard-layout */
1405 cursor.par->Next()->SetLayout(0); /* set the new paragraph to standard-layout */
1409 /* if the cursor is at the beginning of a row without prior newline,
1411 * This touches only the screen-update. Otherwise we would may have
1412 * an empty row on the screen */
1413 if (cursor.pos && !cursor.row->par->IsNewline(cursor.row->pos -1) &&
1414 cursor.row->pos == cursor.pos) {
1418 status = LyXText::NEED_MORE_REFRESH;
1419 refresh_row = cursor.row;
1420 refresh_y = cursor.y - cursor.row->baseline;
1422 /* Do not forget the special right address boxes */
1423 if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
1424 while (refresh_row->previous &&
1425 refresh_row->previous->par == refresh_row->par) {
1426 refresh_row = refresh_row->previous;
1427 refresh_y -= refresh_row->height;
1430 RemoveParagraph(cursor.row);
1432 /* set the dimensions of the cursor row */
1433 cursor.row->fill = Fill(cursor.row, paperwidth);
1435 SetHeightOfRow(cursor.row);
1437 while (!cursor.par->Next()->table && cursor.par->Next()->Last()
1438 && cursor.par->Next()->IsNewline(0))
1439 cursor.par->Next()->Erase(0);
1441 InsertParagraph(cursor.par->Next(), cursor.row);
1443 UpdateCounters(cursor.row->previous);
1445 /* this check is necessary. Otherwise the new empty paragraph will
1446 * be deleted automatically. And it is more friendly for the user! */
1448 SetCursor(cursor.par->Next(), 0);
1450 SetCursor(cursor.par, 0);
1452 if (cursor.row->next)
1453 BreakAgain(cursor.row->next);
1459 void LyXText::OpenFootnote()
1461 LyXParagraph *par, *endpar,*tmppar;
1464 par = cursor.par->ParFromPos(cursor.pos);
1466 /* if there is no footnote in this paragraph, just return. */
1468 || par->next->footnoteflag != LyXParagraph::CLOSED_FOOTNOTE)
1471 /* ok, move the cursor right before the footnote */
1473 /* just a little faster than using CursorRight() */
1474 for (cursor.pos=0; cursor.par->ParFromPos(cursor.pos)!=par; cursor.pos++);
1475 /* now the cursor is at the beginning of the physical par */
1476 SetCursor(cursor.par, cursor.pos + cursor.par->ParFromPos(cursor.pos)->last);
1478 /* the cursor must be exactly before the footnote */
1479 par = cursor.par->ParFromPos(cursor.pos);
1481 status = LyXText::NEED_MORE_REFRESH;
1482 refresh_row = cursor.row;
1483 refresh_y = cursor.y - cursor.row->baseline;
1485 tmppar = cursor.par;
1486 endpar = cursor.par->Next();
1489 tmppar->OpenFootnote(cursor.pos);
1490 RemoveParagraph(row);
1491 /* set the dimensions of the cursor row */
1492 row->fill = Fill(row, paperwidth);
1493 SetHeightOfRow(row);
1494 tmppar = tmppar->Next();
1496 while (tmppar != endpar) {
1498 InsertParagraph(tmppar, row);
1499 while (row->next && row->next->par == tmppar)
1501 tmppar = tmppar->Next();
1504 SetCursor(par->next, 0);
1505 sel_cursor = cursor;
1509 /* table stuff -- begin*/
1511 void LyXText::TableFeatures(int feature, string val)
1516 if (!cursor.par->table)
1517 return; /* this should never happen */
1519 actCell = NumberOfCell(cursor.par, cursor.pos);
1520 SetUndo(Undo::FINISH,
1521 cursor.par->ParFromPos(cursor.pos)->previous,
1522 cursor.par->ParFromPos(cursor.pos)->next);
1525 case LyXTable::SET_PWIDTH:
1526 cursor.par->table->SetPWidth(actCell,val);
1528 case LyXTable::SET_SPECIAL_COLUMN:
1529 case LyXTable::SET_SPECIAL_MULTI:
1530 cursor.par->table->SetAlignSpecial(actCell,val,feature);
1539 void LyXText::TableFeatures(int feature)
1543 setAlign = LYX_ALIGN_LEFT,
1549 if (!cursor.par->table)
1550 return; /* this should never happen */
1552 actCell = NumberOfCell(cursor.par, cursor.pos);
1553 SetUndo(Undo::FINISH,
1554 cursor.par->ParFromPos(cursor.pos)->previous,
1555 cursor.par->ParFromPos(cursor.pos)->next);
1558 case LyXTable::ALIGN_LEFT:
1559 setAlign=LYX_ALIGN_LEFT;
1561 case LyXTable::ALIGN_RIGHT:
1562 setAlign=LYX_ALIGN_RIGHT;
1564 case LyXTable::ALIGN_CENTER:
1565 setAlign=LYX_ALIGN_CENTER;
1571 case LyXTable::APPEND_ROW: {
1572 int pos = cursor.pos;
1573 /* move to the next row */
1574 int cell_org = actCell;
1575 int cell = cell_org;
1577 // if there is a ContRow following this row I have to add
1578 // the row after the ContRow's
1579 if ((pos < cursor.par->Last()) &&
1580 cursor.par->table->RowHasContRow(cell_org)) {
1581 while((pos < cursor.par->Last()) &&
1582 !cursor.par->table->IsContRow(cell)) {
1583 while (pos < cursor.par->Last() &&
1584 !cursor.par->IsNewline(pos))
1586 if (pos < cursor.par->Last())
1590 while((pos < cursor.par->Last()) &&
1591 cursor.par->table->IsContRow(cell)) {
1592 while (pos < cursor.par->Last() &&
1593 !cursor.par->IsNewline(pos))
1595 if (pos < cursor.par->Last())
1600 if (pos < cursor.par->Last())
1603 while (pos < cursor.par->Last() &&
1604 (cell == cell_org || !cursor.par->table->IsFirstCell(cell))){
1605 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
1607 if (pos < cursor.par->Last())
1612 /* insert the new cells */
1613 int number = cursor.par->table->NumberOfCellsInRow(cell_org);
1615 for (i=0; i<number; i++)
1616 cursor.par->InsertChar(pos, LYX_META_NEWLINE);
1618 /* append the row into the table */
1619 cursor.par->table->AppendRow(cell_org);
1623 case LyXTable::APPEND_CONT_ROW: {
1624 int pos = cursor.pos;
1625 /* move to the next row */
1626 int cell_org = actCell;
1627 int cell = cell_org;
1629 // if there is already a controw but not for this cell
1630 // the AppendContRow sets only the right values but does
1631 // not actually add a row
1632 if (cursor.par->table->RowHasContRow(cell_org) &&
1633 (cursor.par->table->CellHasContRow(cell_org)<0)) {
1634 cursor.par->table->AppendContRow(cell_org);
1638 while (pos < cursor.par->Last() &&
1640 || !cursor.par->table->IsFirstCell(cell))){
1641 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
1643 if (pos < cursor.par->Last())
1648 /* insert the new cells */
1649 int number = cursor.par->table->NumberOfCellsInRow(cell_org);
1651 for (i=0; i<number; i++)
1652 cursor.par->InsertChar(pos, LYX_META_NEWLINE);
1654 /* append the row into the table */
1655 cursor.par->table->AppendContRow(cell_org);
1659 case LyXTable::APPEND_COLUMN: {
1661 int cell_org = actCell;
1664 if (pos && (cursor.par->IsNewline(pos-1))){
1665 if (cursor.par->table->AppendCellAfterCell(cell_org, cell)){
1666 cursor.par->InsertChar(pos, LYX_META_NEWLINE);
1667 if (pos<=cursor.pos)
1674 } while (pos<= cursor.par->Last());
1675 /* remember that the very last cell doesn't end with a newline.
1676 This saves one byte memory per table ;-) */
1677 if (cursor.par->table->AppendCellAfterCell(cell_org, cell))
1678 cursor.par->InsertChar(cursor.par->Last(), LYX_META_NEWLINE);
1680 /* append the column into the table */
1681 cursor.par->table->AppendColumn(cell_org);
1686 case LyXTable::DELETE_ROW:
1687 if (current_view->currentBuffer()->the_locking_inset)
1688 UnlockInset(current_view->currentBuffer()->the_locking_inset);
1689 RemoveTableRow(&cursor);
1693 case LyXTable::DELETE_COLUMN: {
1695 int cell_org = actCell;
1697 if (current_view->currentBuffer()->the_locking_inset)
1698 UnlockInset(current_view->currentBuffer()->the_locking_inset);
1700 if (!pos || (cursor.par->IsNewline(pos-1))){
1701 if (cursor.par->table->DeleteCellIfColumnIsDeleted(cell, cell_org)){
1703 while (pos < cursor.par->Last() && !cursor.par->IsNewline(pos))
1704 cursor.par->Erase(pos);
1705 if (pos < cursor.par->Last())
1706 cursor.par->Erase(pos);
1708 cursor.par->Erase(pos - 1); // the missing newline at the end of a table
1709 pos--; // because of pos++ below
1714 } while (pos<= cursor.par->Last());
1716 /* delete the column from the table */
1717 cursor.par->table->DeleteColumn(cell_org);
1719 /* set the cursor to the beginning of the table, where else? */
1724 case LyXTable::TOGGLE_LINE_TOP:
1725 lineSet = !cursor.par->table->TopLine(actCell);
1727 cursor.par->table->SetTopLine(actCell,lineSet);
1730 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1731 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1732 cursor.par->table->SetTopLine(n,lineSet);
1740 case LyXTable::TOGGLE_LINE_BOTTOM:
1741 lineSet = !cursor.par->table->BottomLine(actCell);
1743 cursor.par->table->SetBottomLine(actCell,lineSet);
1746 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1747 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1748 cursor.par->table->SetBottomLine(n,lineSet);
1756 case LyXTable::TOGGLE_LINE_LEFT:
1757 lineSet = !cursor.par->table->LeftLine(actCell);
1759 cursor.par->table->SetLeftLine(actCell,lineSet);
1762 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1763 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1764 cursor.par->table->SetLeftLine(n,lineSet);
1772 case LyXTable::TOGGLE_LINE_RIGHT:
1773 lineSet = !cursor.par->table->RightLine(actCell);
1775 cursor.par->table->SetRightLine(actCell,lineSet);
1778 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1779 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1780 cursor.par->table->SetRightLine(n,lineSet);
1788 case LyXTable::ALIGN_LEFT:
1789 case LyXTable::ALIGN_RIGHT:
1790 case LyXTable::ALIGN_CENTER:
1792 cursor.par->table->SetAlignment(actCell,setAlign);
1795 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1796 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1797 cursor.par->table->SetAlignment(n,setAlign);
1805 case LyXTable::DELETE_TABLE:
1806 SetCursorIntern(cursor.par, 0);
1807 delete cursor.par->table;
1808 cursor.par->table = 0;
1809 // temporary: Should put table in simple_cut_buffer (with before and after
1810 // dummy-paragraph !!
1811 // not necessar anymore with UNDO :)
1813 for (i = cursor.par->last-1; i>=0; i--)
1814 cursor.par->Erase(i);
1818 case LyXTable::MULTICOLUMN: {
1820 // check wether we are completly in a multicol
1821 int multicol = cursor.par->table->IsMultiColumn(actCell);
1822 if (multicol && selection && sel_start_cursor.row == sel_end_cursor.row){
1823 multicol = NumberOfCell(sel_start_cursor.par, sel_start_cursor.pos)
1824 == NumberOfCell(sel_end_cursor.par, sel_end_cursor.pos);
1828 int newlines = cursor.par->table->UnsetMultiColumn(actCell);
1829 int pos = cursor.pos;
1830 while (pos<cursor.par->Last() && !cursor.par->IsNewline(pos))
1832 for (;newlines;newlines--)
1833 cursor.par->InsertChar(pos, LYX_META_NEWLINE);
1838 // selection must be in one row (or no selection)
1840 cursor.par->table->SetMultiColumn(NumberOfCell(cursor.par,
1847 if (sel_start_cursor.row == sel_end_cursor.row){
1850 for (i=sel_start_cursor.pos; i<sel_end_cursor.pos; i++){
1851 if (sel_start_cursor.par->IsNewline(i)){
1852 sel_start_cursor.par->Erase(i);
1853 // check for double-blanks
1854 if ((i && !sel_start_cursor.par->IsLineSeparator(i-1))
1856 (i<sel_start_cursor.par->Last()
1857 && !sel_start_cursor.par->IsLineSeparator(i)))
1858 sel_start_cursor.par->InsertChar(i, ' ');
1860 sel_end_cursor.pos--;
1867 SetMultiColumn(NumberOfCell(sel_start_cursor.par,
1868 sel_start_cursor.pos),
1870 cursor.pos = sel_start_cursor.pos;
1875 WriteAlert(_("Impossible Operation!"),
1876 _("Multicolumns can only be horizontally."),
1883 case LyXTable::SET_ALL_LINES:
1885 case LyXTable::UNSET_ALL_LINES:
1887 cursor.par->table->SetAllLines(NumberOfCell(cursor.par,
1892 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1893 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1894 cursor.par->table->SetAllLines(n,setLines);
1901 case LyXTable::SET_LONGTABLE:
1902 cursor.par->table->SetLongTable(true);
1904 case LyXTable::UNSET_LONGTABLE:
1905 cursor.par->table->SetLongTable(false);
1907 case LyXTable::SET_ROTATE_TABLE:
1908 cursor.par->table->SetRotateTable(true);
1910 case LyXTable::UNSET_ROTATE_TABLE:
1911 cursor.par->table->SetRotateTable(false);
1913 case LyXTable::SET_ROTATE_CELL:
1915 cursor.par->table->SetRotateCell(actCell,true);
1918 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1919 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1920 cursor.par->table->SetRotateCell(n,true);
1926 case LyXTable::UNSET_ROTATE_CELL:
1928 cursor.par->table->SetRotateCell(actCell,false);
1931 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1932 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1933 cursor.par->table->SetRotateCell(n,false);
1939 case LyXTable::SET_LINEBREAKS:
1940 what = !cursor.par->table->Linebreaks(cursor.par->table->FirstVirtualCell(actCell));
1942 cursor.par->table->SetLinebreaks(actCell,what);
1945 for (i=sel_start_cursor.pos; i<=sel_end_cursor.pos; i++){
1946 if ((n=NumberOfCell(sel_start_cursor.par,i)) != m) {
1947 cursor.par->table->SetLinebreaks(n,what);
1953 case LyXTable::SET_LTFIRSTHEAD:
1954 cursor.par->table->SetLTHead(actCell,true);
1956 case LyXTable::SET_LTHEAD:
1957 cursor.par->table->SetLTHead(actCell,false);
1959 case LyXTable::SET_LTFOOT:
1960 cursor.par->table->SetLTFoot(actCell,false);
1962 case LyXTable::SET_LTLASTFOOT:
1963 cursor.par->table->SetLTFoot(actCell,true);
1965 case LyXTable::SET_LTNEWPAGE:
1966 what = !cursor.par->table->LTNewPage(actCell);
1967 cursor.par->table->SetLTNewPage(actCell,what);
1973 void LyXText::InsertCharInTable(char c)
1978 bool jumped_over_space;
1980 /* first check, if there will be two blanks together or a blank at
1981 * the beginning of a paragraph.
1982 * I decided to handle blanks like normal characters, the main
1983 * difference are the special checks when calculating the row.fill
1984 * (blank does not count at the end of a row) and the check here */
1986 LyXFont realtmpfont = real_current_font;
1987 LyXFont rawtmpfont = current_font; /* store the current font.
1988 * This is because of the use
1989 * of cursor movements. The moving
1990 * cursor would refresh the
1993 // Get the font that is used to calculate the baselineskip
1994 int const lastpos = cursor.par->Last();
1995 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
1997 jumped_over_space = false;
1998 if (IsLineSeparatorChar(c)) {
2000 /* avoid double blanks but insert the new blank because
2001 * of a possible font change */
2002 if (cursor.pos < lastpos &&
2003 cursor.par->IsLineSeparator(cursor.pos))
2005 cursor.par->Erase(cursor.pos);
2006 jumped_over_space = true;
2008 else if ((cursor.pos > 0 &&
2009 cursor.par->IsLineSeparator(cursor.pos - 1))
2010 || (cursor.pos > 0 && cursor.par->IsNewline(cursor.pos - 1))
2011 || (cursor.pos == 0 &&
2012 !(cursor.par->Previous()
2013 && cursor.par->Previous()->footnoteflag
2014 == LyXParagraph::OPEN_FOOTNOTE)))
2017 else if (IsNewlineChar(c)) {
2018 if (!IsEmptyTableCell()) {
2019 TableFeatures(LyXTable::APPEND_CONT_ROW);
2022 /* the newline character is the separator of the cells */
2023 // cursor.par->InsertChar(cursor.pos, c);
2024 // SetCharFont(cursor.par, cursor.pos, rawtmpfont);
2025 // RedoParagraphs(cursor, cursor.par->Next());
2026 // SetCursor(cursor.par, cursor.pos+1);
2031 y = cursor.y - row->baseline;
2032 if (c != LYX_META_INSET) /* in this case LyXText::InsertInset
2033 * already inserted the character */
2034 cursor.par->InsertChar(cursor.pos, c);
2035 SetCharFont(cursor.par, cursor.pos, rawtmpfont);
2037 if (!jumped_over_space) {
2038 /* refresh the positions */
2040 while (tmprow->next && tmprow->next->par == row->par) {
2041 tmprow = tmprow->next;
2048 CheckParagraphInTable(cursor.par, cursor.pos);
2050 current_font = rawtmpfont;
2051 real_current_font = realtmpfont;
2053 /* check, whether the last character's font has changed. */
2054 if (cursor.pos && cursor.pos == cursor.par->Last()
2055 && rawparfont != rawtmpfont)
2056 RedoHeightOfParagraph(cursor);
2060 void LyXText::CheckParagraphInTable(LyXParagraph* par, int pos)
2065 if (par->GetChar(pos) == LYX_META_INSET &&
2066 par->GetInset(pos) && par->GetInset(pos)->Display()){
2067 par->GetInset(pos)->SetDisplay(false);
2070 row = GetRow(par, pos, y);
2072 int tmpheight = row->height;
2073 SetHeightOfRow(row);
2076 /* update the table information */
2077 while (tmp_pos && !par->IsNewline(tmp_pos - 1))
2079 if (par->table->SetWidthOfCell(NumberOfCell(par, pos),
2080 WidthOfCell(par, tmp_pos))) {
2081 LyXCursor tmpcursor = cursor;
2082 SetCursorIntern(par, pos);
2083 /* make a complete redraw */
2084 RedoDrawingOfParagraph(cursor);
2088 /* redraw only the row */
2089 LyXCursor tmpcursor = cursor;
2090 SetCursorIntern(par, pos);
2092 refresh_x = cursor.x;
2094 refresh_pos = cursor.pos;
2097 if (tmpheight == row->height)
2098 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2100 status = LyXText::NEED_MORE_REFRESH;
2102 SetCursorIntern(cursor.par, cursor.pos);
2106 void LyXText::BackspaceInTable()
2111 LyXFont rawtmpfont = current_font;
2112 LyXFont realtmpfont = real_current_font;
2114 // Get the font that is used to calculate the baselineskip
2115 int const lastpos = cursor.par->Last();
2116 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2118 if (cursor.pos == 0) {
2119 /* no pasting of table paragraphs */
2124 /* this is the code for a normal backspace, not pasting
2126 SetUndo(Undo::DELETE,
2127 cursor.par->ParFromPos(cursor.pos)->previous,
2128 cursor.par->ParFromPos(cursor.pos)->next);
2132 /* some insets are undeletable here */
2133 if (cursor.par->GetChar(cursor.pos)==LYX_META_INSET) {
2134 if (!cursor.par->GetInset(cursor.pos)->Deletable())
2139 y = cursor.y - row->baseline;
2141 /* some special code when deleting a newline. */
2142 if (cursor.par->IsNewline(cursor.pos)) {
2147 cursor.par->Erase(cursor.pos);
2149 /* refresh the positions */
2151 while (tmprow->next && tmprow->next->par == row->par) {
2152 tmprow = tmprow->next;
2156 /* delete superfluous blanks */
2157 if (cursor.pos < cursor.par->Last() - 1 &&
2158 (cursor.par->IsLineSeparator(cursor.pos))) {
2160 if (cursor.pos == BeginningOfMainBody(cursor.par)
2162 || cursor.par->IsLineSeparator(cursor.pos - 1)) {
2163 cursor.par->Erase(cursor.pos);
2164 /* refresh the positions */
2166 while (tmprow->next &&
2167 tmprow->next->par == row->par) {
2168 tmprow = tmprow->next;
2171 if (cursor.pos) /* move one character left */
2177 CheckParagraphInTable(cursor.par, cursor.pos);
2179 /* check, wether the last characters font has changed. */
2180 if (cursor.pos && cursor.pos == cursor.par->Last()
2181 && rawparfont != rawtmpfont)
2182 RedoHeightOfParagraph(cursor);
2184 /* restore the current font
2185 * That is what a user expects! */
2186 current_font = rawtmpfont;
2187 real_current_font = realtmpfont;
2189 SetCursorIntern(cursor.par, cursor.pos);
2192 /* table stuff -- end*/
2195 /* just a macro to make some thing easier. */
2196 void LyXText::RedoParagraph()
2198 LyXCursor tmpcursor = cursor;
2200 RedoParagraphs(cursor, cursor.par->Next());;
2201 SetCursorIntern(tmpcursor.par, tmpcursor.pos);
2205 /* insert a character, moves all the following breaks in the
2206 * same Paragraph one to the right and make a rebreak */
2207 void LyXText::InsertChar(char c)
2213 bool jumped_over_space;
2214 LyXFont realtmpfont;
2219 SetUndo(Undo::INSERT,
2220 cursor.par->ParFromPos(cursor.pos)->previous,
2221 cursor.par->ParFromPos(cursor.pos)->next);
2223 /* When the free-spacing option is set for the current layout,
2224 * all spaces are converted to protected spaces. */
2225 bool freeSpacingBo =
2226 lyxstyle.Style(parameters->textclass,
2227 cursor.row->par->GetLayout())->free_spacing;
2229 // Is this wanted? There cannot be a line break between protected
2230 // separators. Therefore I suggest the way implemented below
2233 // if ( freeSpacingBo && IsLineSeparatorChar(c) )
2234 // c = LYX_META_PROTECTED_SEPARATOR;
2236 if (freeSpacingBo && IsLineSeparatorChar(c)
2237 && (!cursor.pos || cursor.par->IsLineSeparator(cursor.pos-1)))
2238 c = LYX_META_PROTECTED_SEPARATOR;
2240 /* table stuff -- begin*/
2241 if (cursor.par->table) {
2242 InsertCharInTable(c);
2245 /* table stuff -- end*/
2247 /* first check, if there will be two blanks together or a blank at
2248 * the beginning of a paragraph.
2249 * I decided to handle blanks like normal characters, the main
2250 * difference are the special checks when calculating the row.fill
2251 * (blank does not count at the end of a row) and the check here */
2253 // The bug is triggered when we type in a description environment:
2254 // The current_font is not changed when we go from label to main text
2255 // and it should (along with realtmpfont) when we type the space.
2256 #ifdef WITH_WARNINGS
2257 #warning There is a bug here! (Asger)
2260 realtmpfont = real_current_font;
2261 rawtmpfont = current_font; /* store the current font.
2262 * This is because of the use
2263 * of cursor movements. The moving
2264 * cursor would refresh the
2267 // Get the font that is used to calculate the baselineskip
2268 lastpos = cursor.par->Last();
2269 rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2271 jumped_over_space = false;
2273 if (IsLineSeparatorChar(c)) {
2275 if (cursor.pos < lastpos
2276 && cursor.par->IsLineSeparator(cursor.pos)) {
2277 /* the user inserted a space before a space. So we
2278 * will just make a CursorRight. BUT: The font of this
2279 * space should be set to current font. That is why
2280 * we need to rebreak perhaps. If there is a protected
2281 * blank at the end of a row we have to force
2284 minibuffer->Set(_("You cannot type two spaces this way. Please read the Tutorial."));
2285 if (cursor.pos == RowLast(cursor.row)
2286 && !IsLineSeparatorChar(c))
2287 cursor.row->fill = -1; /* force rebreak */
2289 cursor.par->Erase(cursor.pos);
2290 jumped_over_space = true;
2292 } else if ((cursor.pos > 0
2293 && cursor.par->IsLineSeparator(cursor.pos - 1))
2295 && cursor.par->IsNewline(cursor.pos - 1))
2297 && !(cursor.par->Previous()
2298 && cursor.par->Previous()->footnoteflag
2299 == LyXParagraph::OPEN_FOOTNOTE))) {
2300 if (cursor.pos == 0 )
2301 minibuffer->Set(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial."));
2303 minibuffer->Set(_("You cannot type two spaces this way. Please read the Tutorial."));
2306 } else if (IsNewlineChar(c)) {
2307 if (cursor.par->FirstPhysicalPar() == cursor.par
2308 && cursor.pos <= BeginningOfMainBody(cursor.par))
2310 /* no newline at first position
2311 * of a paragraph or behind labels.
2312 * TeX does not allow that. */
2314 if (cursor.pos < cursor.par->Last() &&
2315 cursor.par->IsLineSeparator(cursor.pos))
2316 CursorRightIntern(); // newline always after a blank!
2317 cursor.row->fill = -1; // to force a new break
2320 /* the display inset stuff */
2321 if (cursor.row->par->GetChar(cursor.row->pos) == LYX_META_INSET
2322 && cursor.row->par->GetInset(cursor.row->pos)
2323 && cursor.row->par->GetInset(cursor.row->pos)->Display())
2324 cursor.row->fill = -1; // to force a new break
2326 /* get the cursor row fist */
2327 /* this is a dumb solution, i will try to hold the cursor.row
2329 /* row = GetRow(cursor.par, cursor.pos, y);*/
2330 /* ok, heres a better way: */
2332 y = cursor.y - row->baseline;
2333 if (c != LYX_META_INSET) /* in this case LyXText::InsertInset
2334 * already insertet the character */
2335 cursor.par->InsertChar(cursor.pos, c);
2336 SetCharFont(cursor.par, cursor.pos, rawtmpfont);
2338 if (!jumped_over_space) {
2339 /* refresh the positions */
2341 while (tmprow->next && tmprow->next->par == row->par) {
2342 tmprow = tmprow->next;
2347 /* Is there a break one row above */
2348 if ((cursor.par->IsLineSeparator(cursor.pos)
2349 || cursor.par->IsNewline(cursor.pos)
2350 || cursor.row->fill == -1)
2351 && row->previous && row->previous->par == row->par) {
2352 z = NextBreakPoint(row->previous, paperwidth);
2353 if ( z >= row->pos) {
2356 /* set the dimensions of the row above */
2357 row->previous->fill = Fill(row->previous, paperwidth);
2359 SetHeightOfRow(row->previous);
2361 y -= row->previous->height;
2363 refresh_row = row->previous;
2364 status = LyXText::NEED_MORE_REFRESH;
2366 BreakAgainOneRow(row);
2367 SetCursor(cursor.par, cursor.pos + 1);
2368 /* cursor MUST be in row now */
2370 if (row->next && row->next->par == row->par)
2371 need_break_row = row->next;
2375 current_font = rawtmpfont;
2376 real_current_font = realtmpfont;
2378 // check, wether the last characters font has changed.
2379 if (cursor.pos && cursor.pos == cursor.par->Last()
2380 && rawparfont != rawtmpfont)
2381 RedoHeightOfParagraph(cursor);
2387 /* recalculate the fill of the row */
2388 if (row->fill >= 0) /* needed because a newline
2389 * will set fill to -1. Otherwise
2390 * we would not get a rebreak! */
2391 row->fill = Fill(row, paperwidth);
2392 if (row->fill < 0 ) {
2395 refresh_x = cursor.x;
2396 refresh_pos = cursor.pos;
2397 status = LyXText::NEED_MORE_REFRESH;
2398 BreakAgainOneRow(row);
2399 /* will the cursor be in another row now? */
2400 if (RowLast(row) <= cursor.pos + 1 && row->next) {
2401 if (row->next && row->next->par == row->par)
2405 BreakAgainOneRow(row);
2407 SetCursor(cursor.par, cursor.pos + 1);
2408 if (row->next && row->next->par == row->par)
2409 need_break_row = row->next;
2413 current_font = rawtmpfont;
2414 real_current_font = realtmpfont;
2417 refresh_x = cursor.x;
2419 refresh_pos = cursor.pos;
2421 int tmpheight = row->height;
2422 SetHeightOfRow(row);
2423 if (tmpheight == row->height)
2424 status = LyXText::NEED_VERY_LITTLE_REFRESH;
2426 status = LyXText::NEED_MORE_REFRESH;
2428 SetCursor(cursor.par, cursor.pos + 1);
2429 current_font = rawtmpfont;
2430 real_current_font = realtmpfont;
2433 /* check, wether the last characters font has changed. */
2434 if (cursor.pos && cursor.pos == cursor.par->Last()
2435 && rawparfont != rawtmpfont) {
2436 RedoHeightOfParagraph(cursor);
2438 /* now the special right address boxes */
2439 if (lyxstyle.Style(parameters->textclass,
2440 cursor.par->GetLayout())->margintype
2441 == MARGIN_RIGHT_ADDRESS_BOX) {
2442 RedoDrawingOfParagraph(cursor);
2447 // Here we could call FinishUndo for every 20 characters inserted.
2448 // This is from my experience how emacs does it.
2449 static unsigned short counter = 0;
2460 void LyXText::PrepareToPrint(Row *row, float &x, float &fill_separator,
2461 float &fill_hfill, float &fill_label_hfill)
2463 float w, nh, nlh, ns;
2467 fill_label_hfill = 0;
2469 fill_label_hfill = 0;
2471 x = LeftMargin(row);
2473 /* is there a manual margin with a manual label */
2474 if (lyxstyle.Style(parameters->textclass,
2475 row->par->GetLayout())->margintype == MARGIN_MANUAL
2476 && lyxstyle.Style(parameters->textclass,
2477 row->par->GetLayout())->labeltype == LABEL_MANUAL) {
2479 nlh = NumberOfLabelHfills(row) + 1; /* one more since labels
2480 * are left aligned*/
2481 if (nlh && !row->par->GetLabelWidthString().empty()) {
2482 fill_label_hfill = LabelFill(row) / nlh;
2486 /* are there any hfills in the row? */
2487 nh = NumberOfHfills(row);
2489 /* table stuff -- begin*/
2490 if (row->par->table) {
2491 w = paperwidth - row->par->table->WidthOfTable()
2492 - x - RightMargin(row);
2493 nh = 0; /* ignore hfills in tables */
2495 /* table stuff -- end*/
2500 /* is it block, flushleft or flushright?
2501 * set x how you need it */
2503 if (row->par->FirstPhysicalPar()->align == LYX_ALIGN_LAYOUT)
2504 align = lyxstyle.Style(parameters->textclass, row->par->GetLayout())->align;
2506 align = row->par->FirstPhysicalPar()->align;
2508 /* center displayed insets */
2509 if (row->par->GetChar(row->pos) == LYX_META_INSET
2510 && row->par->GetInset(row->pos)
2511 && row->par->GetInset(row->pos)->Display())
2512 align = LYX_ALIGN_CENTER;
2515 case LYX_ALIGN_BLOCK:
2516 ns = NumberOfSeparators(row);
2517 if (ns && row->next && row->next->par == row->par &&
2518 !(row->next->par->IsNewline(row->next->pos-1))
2519 && !(row->next->par->GetChar(row->next->pos) == LYX_META_INSET
2520 && row->next->par->GetInset(row->next->pos)
2521 && row->next->par->GetInset(row->next->pos)->Display())
2523 fill_separator = w / ns;
2525 case LYX_ALIGN_RIGHT:
2528 case LYX_ALIGN_CENTER:
2536 /* important for the screen */
2539 /* the cursor set functions have a special mechanism. When they
2540 * realize, that you left an empty paragraph, they will delete it.
2541 * They also delete the corresponding row */
2543 void LyXText::CursorRightOneWord()
2545 // treat floats, HFills and Insets as words
2546 LyXCursor tmpcursor = cursor;
2548 if (tmpcursor.pos == tmpcursor.par->Last()
2549 && tmpcursor.par->Next())
2551 tmpcursor.par = tmpcursor.par->Next();
2556 // Skip through initial nonword stuff.
2557 while ( tmpcursor.pos < tmpcursor.par->Last() &&
2558 ! tmpcursor.par->IsWord( tmpcursor.pos ) )
2560 // printf("Current pos1 %d",tmpcursor.pos) ;
2564 // Advance through word.
2565 while ( tmpcursor.pos < tmpcursor.par->Last() &&
2566 tmpcursor.par->IsWord( tmpcursor.pos ) )
2568 // printf("Current pos2 %d",tmpcursor.pos) ;
2573 SetCursor(tmpcursor.par, tmpcursor.pos);
2577 void LyXText::CursorTab()
2579 if (cursor.par->table) {
2580 int cell = NumberOfCell(cursor.par, cursor.pos);
2581 while(cursor.par->table->IsContRow(cell)) {
2583 cell = NumberOfCell(cursor.par, cursor.pos);
2585 if (cursor.par->table->ShouldBeVeryLastCell(cell))
2586 TableFeatures(LyXTable::APPEND_ROW);
2588 LyXCursor tmpcursor = cursor;
2589 while (tmpcursor.pos < tmpcursor.par->Last()
2590 && !tmpcursor.par->IsNewline(tmpcursor.pos))
2593 if (tmpcursor.pos == tmpcursor.par->Last()){
2594 if (tmpcursor.par->Next()) {
2595 tmpcursor.par = tmpcursor.par->Next();
2601 SetCursor(tmpcursor.par, tmpcursor.pos);
2602 if (cursor.par->table) {
2603 int cell = NumberOfCell(cursor.par, cursor.pos);
2604 while (cursor.par->table->IsContRow(cell) &&
2605 !cursor.par->table->ShouldBeVeryLastCell(cell)) {
2607 while (tmpcursor.pos < tmpcursor.par->Last()
2608 && !tmpcursor.par->IsNewline(tmpcursor.pos))
2611 if (tmpcursor.pos == tmpcursor.par->Last()){
2612 if (tmpcursor.par->Next()) {
2613 tmpcursor.par = tmpcursor.par->Next();
2619 SetCursor(tmpcursor.par, tmpcursor.pos);
2620 cell = NumberOfCell(cursor.par, cursor.pos);
2626 /* -------> Skip initial whitespace at end of word and move cursor to *start*
2627 of prior word, not to end of next prior word. */
2629 void LyXText::CursorLeftOneWord()
2631 // treat HFills, floats and Insets as words
2632 LyXCursor tmpcursor = cursor;
2633 while (tmpcursor.pos
2634 && (tmpcursor.par->IsSeparator(tmpcursor.pos - 1)
2635 || tmpcursor.par->IsKomma(tmpcursor.pos - 1))
2636 && !(tmpcursor.par->IsHfill(tmpcursor.pos - 1)
2637 || tmpcursor.par->IsFloat(tmpcursor.pos - 1)
2638 || tmpcursor.par->IsInset(tmpcursor.pos - 1)))
2642 && (tmpcursor.par->IsInset(tmpcursor.pos - 1)
2643 || tmpcursor.par->IsFloat(tmpcursor.pos - 1)
2644 || tmpcursor.par->IsHfill(tmpcursor.pos - 1))) {
2646 } else if (!tmpcursor.pos) {
2647 if (tmpcursor.par->Previous()){
2648 tmpcursor.par = tmpcursor.par->Previous();
2649 tmpcursor.pos = tmpcursor.par->Last();
2651 } else { // Here, tmpcursor != 0
2652 while (tmpcursor.pos > 0 &&
2653 tmpcursor.par->IsWord(tmpcursor.pos-1) )
2656 SetCursor(tmpcursor.par, tmpcursor.pos);
2659 /* -------> Select current word. This depends on behaviour of CursorLeftOneWord(), so it is
2662 void LyXText::SelectWord()
2664 /* Move cursor to the beginning, when not already there. */
2666 && !cursor.par->IsSeparator(cursor.pos-1)
2667 && !cursor.par->IsKomma(cursor.pos-1) )
2668 CursorLeftOneWord();
2670 /* set the sel cursor */
2671 sel_cursor = cursor;
2673 while ( cursor.pos < cursor.par->Last()
2674 && !cursor.par->IsSeparator(cursor.pos)
2675 && !cursor.par->IsKomma(cursor.pos) )
2677 SetCursor( cursor.par, cursor.pos );
2679 /* finally set the selection */
2684 /* -------> Select the word currently under the cursor when:
2685 1: no selection is currently set,
2686 2: the cursor is not at the borders of the word. */
2688 int LyXText::SelectWordWhenUnderCursor()
2690 if ( selection ) return 0;
2691 if ( cursor.pos < cursor.par->Last()
2692 && !cursor.par->IsSeparator(cursor.pos)
2693 && !cursor.par->IsKomma(cursor.pos)
2695 && !cursor.par->IsSeparator(cursor.pos -1)
2696 && !cursor.par->IsKomma(cursor.pos -1) ) {
2704 // This function is only used by the spellchecker for NextWord().
2705 // It doesn't handle LYX_ACCENTs and probably never will.
2706 char* LyXText::SelectNextWord(float &value)
2708 LyXParagraph* tmppar = cursor.par;
2710 // If this is not the very first word, skip rest of
2711 // current word because we are probably in the middle
2712 // of a word if there is text here.
2713 if (cursor.pos || cursor.par->previous) {
2714 while (cursor.pos < cursor.par->Last()
2715 && cursor.par->IsLetter(cursor.pos))
2718 // Now, skip until we have real text (will jump paragraphs)
2719 while ((cursor.par->Last() > cursor.pos
2720 && (!cursor.par->IsLetter(cursor.pos)
2721 || cursor.par->getFont(cursor.pos).latex() == LyXFont::ON))
2722 || (cursor.par->Last() == cursor.pos
2723 && cursor.par->Next())){
2724 if (cursor.pos == cursor.par->Last()) {
2725 cursor.par = cursor.par->Next();
2732 // Update the value if we changed paragraphs
2733 if (cursor.par != tmppar){
2734 SetCursor(cursor.par, cursor.pos);
2735 value = float(cursor.y)/float(height);
2738 /* Start the selection from here */
2739 sel_cursor = cursor;
2743 /* and find the end of the word
2744 (optional hyphens are part of a word) */
2745 while (cursor.pos < cursor.par->Last()
2746 && (cursor.par->IsLetter(cursor.pos))
2747 || (cursor.par->GetChar(cursor.pos) == LYX_META_INSET &&
2748 cursor.par->GetInset(cursor.pos) != 0 &&
2749 cursor.par->GetInset(cursor.pos)->Latex(latex,0)==0 &&
2753 // Finally, we copy the word to a string and return it
2756 if (sel_cursor.pos < cursor.pos) {
2757 string = new char [cursor.pos - sel_cursor.pos + 2];
2760 for (i=sel_cursor.pos, j=0; i<cursor.pos; i++) {
2761 if (cursor.par->GetChar(i) != LYX_META_INSET)
2762 string[j++] = cursor.par->GetChar(i);
2770 // This one is also only for the spellchecker
2771 void LyXText::SelectSelectedWord()
2773 /* move cursor to the beginning */
2774 SetCursor(sel_cursor.par, sel_cursor.pos);
2776 /* set the sel cursor */
2777 sel_cursor = cursor;
2781 /* now find the end of the word */
2782 while (cursor.pos < cursor.par->Last()
2783 && (cursor.par->IsLetter(cursor.pos)
2784 || (cursor.par->GetChar(cursor.pos) == LYX_META_INSET &&
2785 cursor.par->GetInset(cursor.pos) != 0 &&
2786 cursor.par->GetInset(cursor.pos)->Latex(latex,0)==0 &&
2790 SetCursor(cursor.par, cursor.pos);
2792 /* finally set the selection */
2797 /* -------> Delete from cursor up to the end of the current or next word. */
2798 void LyXText::DeleteWordForward()
2800 LyXCursor tmpcursor = cursor;
2802 if (!cursor.par->Last())
2805 /* -------> Skip initial non-word stuff. */
2806 while ( cursor.pos < cursor.par->Last()
2807 && (cursor.par->IsSeparator(cursor.pos)
2808 || cursor.par->IsKomma(cursor.pos)) )
2811 SetCursorIntern(cursor.par, cursor.pos);
2812 selection = True; // to avoid deletion
2813 CursorRightOneWord();
2814 sel_cursor = cursor;
2818 /* -----> Great, CutSelection() gets rid of multiple spaces. */
2824 /* -------> Delete from cursor to start of current or prior word. */
2825 void LyXText::DeleteWordBackward()
2827 LyXCursor tmpcursor = cursor;
2828 if (!cursor.par->Last())
2831 selection = True; // to avoid deletion
2832 CursorLeftOneWord();
2833 sel_cursor = cursor;
2841 /* -------> Kill to end of line. */
2842 void LyXText::DeleteLineForward()
2844 LyXCursor tmpcursor = cursor;
2845 if (!cursor.par->Last())
2849 sel_cursor = cursor;
2852 if (selection == false) {
2853 DeleteWordForward();
2860 // Change the case of a word at cursor position. The meaning of action
2862 // 0 change to lowercase
2863 // 1 capitalize word
2864 // 2 change to uppercase
2865 // This function directly manipulates LyXParagraph::text because there
2866 // is no LyXParagraph::SetChar currently. I did what I could to ensure
2867 // that it is correct. I guess part of it should be moved to
2868 // LyXParagraph, but it will have to change for 1.1 anyway. At least
2869 // it does not access outside of the allocated array as the older
2870 // version did. (JMarc)
2871 void LyXText::ChangeWordCase(int action)
2873 LyXParagraph *tmppar = cursor.par->ParFromPos(cursor.pos);
2874 int tmppos = cursor.par->PositionInParFromPos(cursor.pos);
2876 SetUndo(Undo::FINISH, tmppar->previous, tmppar->next);
2878 while (tmppos < tmppar->last) {
2879 unsigned char c = tmppar->text[tmppos];
2880 if (IsKommaChar(c) || IsLineSeparatorChar(c))
2882 if (c != LYX_META_INSET) {
2897 tmppar->text[tmppos] = c;
2900 CheckParagraph(tmppar, tmppos);
2901 CursorRightOneWord();
2905 void LyXText::Delete()
2907 LyXCursor old_cursor = cursor;
2908 LyXCursor tmpcursor;
2909 /* this is a very easy implementation*/
2911 /* just move to the right */
2912 CursorRightIntern();
2914 if (cursor.par->previous == old_cursor.par->previous
2915 && cursor.par != old_cursor.par)
2916 return; // delete-emty-paragraph-mechanism has done it
2918 /* if you had success make a backspace */
2919 if (old_cursor.par != cursor.par || old_cursor.pos != cursor.pos) {
2921 cursor = old_cursor; // to make sure undo gets the right cursor position
2922 SetUndo(Undo::DELETE,
2923 cursor.par->ParFromPos(cursor.pos)->previous,
2924 cursor.par->ParFromPos(cursor.pos)->next);
2931 void LyXText::Backspace()
2933 LyXParagraph *tmppar;
2938 /* table stuff -- begin*/
2940 if (cursor.par->table) {
2944 /* table stuff -- end*/
2946 LyXFont rawtmpfont = current_font;
2947 LyXFont realtmpfont = real_current_font;
2949 // Get the font that is used to calculate the baselineskip
2950 int const lastpos = cursor.par->Last();
2951 LyXFont rawparfont = cursor.par->GetFontSettings(lastpos - 1);
2953 if (cursor.pos == 0) {
2954 /* we may paste some paragraphs */
2956 /* is it an empty paragraph? */
2959 || (lastpos == 1 && cursor.par->IsSeparator(0)))
2960 && !(cursor.par->Next()
2961 && cursor.par->footnoteflag ==
2962 LyXParagraph::NO_FOOTNOTE
2963 && cursor.par->Next()->footnoteflag ==
2964 LyXParagraph::OPEN_FOOTNOTE)) {
2966 if (cursor.par->previous) {
2967 tmppar = cursor.par->previous->FirstPhysicalPar();
2968 if (cursor.par->GetLayout() == tmppar->GetLayout()
2969 && cursor.par->footnoteflag == tmppar->footnoteflag
2970 && cursor.par->GetAlign() == tmppar->GetAlign()) {
2972 tmppar->line_bottom = cursor.par->line_bottom;
2973 tmppar->added_space_bottom = cursor.par->added_space_bottom;
2974 tmppar->pagebreak_bottom = cursor.par->pagebreak_bottom;
2979 /* the layout things can change the height of a row ! */
2980 tmpheight = cursor.row->height;
2981 SetHeightOfRow(cursor.row);
2982 if (cursor.row->height != tmpheight) {
2983 refresh_y = cursor.y - cursor.row->baseline;
2984 refresh_row = cursor.row;
2985 status = LyXText::NEED_MORE_REFRESH;
2990 if (cursor.par->ParFromPos(cursor.pos)->previous){
2991 SetUndo(Undo::DELETE,
2992 cursor.par->ParFromPos(cursor.pos)->previous->previous,
2993 cursor.par->ParFromPos(cursor.pos)->next);
2995 tmppar = cursor.par;
2996 tmprow = cursor.row;
2998 /* Pasting is not allowed, if the paragraphs have different layout.
2999 * I think it is a real bug of all other word processors to allow
3000 * it. It confuses the user. Even so with a footnote paragraph and
3001 * a non-footnote paragraph. I will not allow pasting in this case,
3002 * because the user would be confused if the footnote behaves
3003 * different wether it is open or closed.
3005 * Correction: Pasting is always allowed with standard-layout */
3006 if (cursor.par != tmppar
3007 && (cursor.par->GetLayout() == tmppar->GetLayout()
3008 || !tmppar->GetLayout())
3009 && cursor.par->footnoteflag == tmppar->footnoteflag
3010 /* table stuff -- begin*/
3011 && !cursor.par->table /* no pasting of tables */
3012 /* table stuff -- end*/
3013 && cursor.par->GetAlign() == tmppar->GetAlign()) {
3015 cursor.par->PasteParagraph();
3018 cursor.par->IsSeparator(cursor.pos - 1)))
3019 cursor.par->InsertChar(cursor.pos, ' ');
3024 status = LyXText::NEED_MORE_REFRESH;
3025 refresh_row = cursor.row;
3026 refresh_y = cursor.y - cursor.row->baseline;
3028 /* remove the lost paragraph */
3029 RemoveParagraph(tmprow);
3032 /* break the paragraph again */
3033 /* BreakAgain(cursor.row); */
3035 AppendParagraph(cursor.row);
3036 UpdateCounters(cursor.row);
3038 /* the row may have changed, block, hfills etc. */
3039 SetCursor(cursor.par, cursor.pos);
3042 /* this is the code for a normal backspace, not pasting
3044 SetUndo(Undo::DELETE,
3045 cursor.par->ParFromPos(cursor.pos)->previous,
3046 cursor.par->ParFromPos(cursor.pos)->next);
3049 /* some insets are undeletable here */
3050 if (cursor.par->GetChar(cursor.pos)==LYX_META_INSET) {
3051 if (!cursor.par->GetInset(cursor.pos)->Deletable())
3053 /* force complete redo when erasing display insets */
3054 /* this is a cruel mathod but save..... Matthias */
3055 if (cursor.par->GetInset(cursor.pos)->Display()){
3056 cursor.par->Erase(cursor.pos);
3063 y = cursor.y - row->baseline;
3066 /* remember that a space at the end of a row doesnt count
3067 * when calculating the fill */
3068 if (cursor.pos < RowLast(row) ||
3069 !cursor.par->IsLineSeparator(cursor.pos)) {
3070 row->fill += SingleWidth(cursor.par, cursor.pos);
3073 /* some special code when deleting a newline. This is similar
3074 * to the behavior when pasting paragraphs */
3075 if (cursor.pos && cursor.par->IsNewline(cursor.pos)) {
3076 cursor.par->Erase(cursor.pos);
3077 /* refresh the positions */
3079 while (tmprow->next && tmprow->next->par == row->par) {
3080 tmprow = tmprow->next;
3083 if (cursor.par->IsLineSeparator(cursor.pos - 1))
3086 if (cursor.pos < cursor.par->Last() && !cursor.par->IsSeparator(cursor.pos)) {
3087 cursor.par->InsertChar(cursor.pos, ' ');
3088 /* refresh the positions */
3090 while (tmprow->next && tmprow->next->par == row->par) {
3091 tmprow = tmprow->next;
3096 cursor.par->Erase(cursor.pos);
3098 /* refresh the positions */
3100 while (tmprow->next && tmprow->next->par == row->par) {
3101 tmprow = tmprow->next;
3105 /* delete superfluous blanks */
3106 if (cursor.pos < cursor.par->Last() - 1 &&
3107 (cursor.par->IsLineSeparator(cursor.pos))) {
3109 if (cursor.pos == BeginningOfMainBody(cursor.par)
3111 || cursor.par->IsLineSeparator(cursor.pos - 1)) {
3112 cursor.par->Erase(cursor.pos);
3113 /* refresh the positions */
3115 while (tmprow->next &&
3116 tmprow->next->par == row->par) {
3117 tmprow = tmprow->next;
3120 if (cursor.pos) /* move one character left */
3125 /* delete newlines at the beginning of paragraphs */
3126 while (cursor.par->Last() &&
3127 cursor.par->IsNewline(cursor.pos) &&
3128 cursor.pos == BeginningOfMainBody(cursor.par)
3130 cursor.par->Erase(cursor.pos);
3131 /* refresh the positions */
3133 while (tmprow->next &&
3134 tmprow->next->par == row->par) {
3135 tmprow = tmprow->next;
3141 /* is there a break one row above */
3142 if (row->previous && row->previous->par == row->par) {
3143 z = NextBreakPoint(row->previous, paperwidth);
3144 if ( z >= row->pos) {
3147 tmprow = row->previous;
3149 /* maybe the current row is now empty */
3150 if (row->pos >= row->par->Last()) {
3156 BreakAgainOneRow(row);
3157 if (row->next && row->next->par == row->par)
3158 need_break_row = row->next;
3163 /* set the dimensions of the row above */
3164 y -= tmprow->height;
3165 tmprow->fill = Fill(tmprow, paperwidth);
3166 SetHeightOfRow(tmprow);
3169 refresh_row = tmprow;
3170 status = LyXText::NEED_MORE_REFRESH;
3171 SetCursor(cursor.par, cursor.pos);
3172 current_font = rawtmpfont;
3173 real_current_font = realtmpfont;
3174 /* check, whether the last character's font has changed. */
3175 rawtmpfont = cursor.par->GetFontSettings(cursor.par->Last() - 1);
3176 if (rawparfont != rawtmpfont)
3177 RedoHeightOfParagraph(cursor);
3182 /* break the cursor row again */
3183 z = NextBreakPoint(row, paperwidth);
3185 if ( z != RowLast(row) ||
3186 (row->next && row->next->par == row->par &&
3187 RowLast(row) == row->par->Last() - 1)){
3189 /* it can happen that a paragraph loses one row
3190 * without a real breakup. This is when a word
3191 * is to long to be broken. Well, I don t care this
3193 if (row->next && row->next->par == row->par &&
3194 RowLast(row) == row->par->Last() - 1)
3195 RemoveRow(row->next);
3199 status = LyXText::NEED_MORE_REFRESH;
3201 BreakAgainOneRow(row);
3203 SetCursor(cursor.par, cursor.pos);
3204 /* cursor MUST be in row now */
3206 if (row->next && row->next->par == row->par)
3207 need_break_row = row->next;
3211 /* set the dimensions of the row */
3212 row->fill = Fill(row, paperwidth);
3213 int tmpheight = row->height;
3214 SetHeightOfRow(row);
3215 if (tmpheight == row->height)
3216 status = LyXText::NEED_VERY_LITTLE_REFRESH;
3218 status = LyXText::NEED_MORE_REFRESH;
3221 SetCursor(cursor.par, cursor.pos);
3225 /* restore the current font
3226 * That is what a user expects! */
3227 current_font = rawtmpfont;
3228 real_current_font = realtmpfont;
3230 /* check, wether the last characters font has changed. */
3231 rawtmpfont = cursor.par->GetFontSettings(cursor.par->Last() - 1);
3232 if (rawparfont != rawtmpfont) {
3233 RedoHeightOfParagraph(cursor);
3235 /* now the special right address boxes */
3236 if (lyxstyle.Style(parameters->textclass,
3237 cursor.par->GetLayout())->margintype == MARGIN_RIGHT_ADDRESS_BOX) {
3238 RedoDrawingOfParagraph(cursor);
3244 void LyXText::GetVisibleRow(LyXScreen &scr, int offset,
3245 Row *row_ptr, long y)
3247 /* returns a printed row */
3251 int y_top, y_bottom;
3252 float fill_separator, fill_hfill, fill_label_hfill;
3253 LyXParagraph *par, *firstpar;
3257 if (row_ptr->height <= 0) {
3258 lyxerr << "LYX_ERROR: row.height: " << row_ptr->height << endl;
3261 left_margin = LabelEnd(row_ptr);
3262 PrepareToPrint(row_ptr, x, fill_separator,
3263 fill_hfill, fill_label_hfill);
3265 int main_body = BeginningOfMainBody(row_ptr->par);
3267 /* initialize the pixmap */
3269 scr.fillRectangle(gc_clear,
3270 0, offset, paperwidth, row_ptr->height);
3271 // check for NOT FAST SELECTION
3272 if (!fast_selection && !mono_video && selection) {
3273 /* selection code */
3274 if (sel_start_cursor.row == row_ptr &&
3275 sel_end_cursor.row == row_ptr) {
3276 scr.fillRectangle(gc_selection, sel_start_cursor.x,
3282 else if (sel_start_cursor.row == row_ptr) {
3283 scr.fillRectangle(gc_selection, sel_start_cursor.x,
3285 paperwidth - sel_start_cursor.x,
3287 } else if (sel_end_cursor.row == row_ptr) {
3288 scr.fillRectangle(gc_selection,0, offset,
3289 sel_end_cursor.x, row_ptr->height);
3290 } else if (y > sel_start_cursor.y && y < sel_end_cursor.y) {
3291 scr.fillRectangle(gc_selection, 0, offset,
3292 paperwidth, row_ptr->height);
3295 } // end of NOT FAST SELECTION code
3297 if (row_ptr->par->appendix){
3298 scr.drawVerticalLine(gc_math, 1, offset, offset+row_ptr->height);
3299 scr.drawVerticalLine(gc_math, paperwidth-2 , offset, offset+row_ptr->height);
3302 if (row_ptr->par->pextra_type == PEXTRA_MINIPAGE) {
3303 /* draw a marker at the left margin! */
3304 LyXFont font = GetFont(row_ptr->par, 0);
3305 int asc = font.maxAscent();
3306 int x = (LYX_PAPER_MARGIN - font.width('|')) / 2;
3307 int y1 = (offset + row_ptr->baseline);
3308 int y2 = (offset + row_ptr->baseline) - asc;
3310 scr.drawVerticalLine(gc_minipage, x, y1, y2);
3312 if (row_ptr->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
3313 LyXFont font(LyXFont::ALL_SANE);
3314 font.setSize(LyXFont::SIZE_FOOTNOTE);
3315 font.setColor(LyXFont::RED);
3317 int box_x = LYX_PAPER_MARGIN;
3318 box_x += font.textWidth(" wide-tab ", 10);
3319 if (row_ptr->previous &&
3320 row_ptr->previous->par->footnoteflag != LyXParagraph::OPEN_FOOTNOTE){
3322 switch (row_ptr->par->footnotekind) {
3323 case LyXParagraph::MARGIN:
3326 case LyXParagraph::FIG:
3329 case LyXParagraph::TAB:
3332 case LyXParagraph::WIDE_FIG:
3335 case LyXParagraph::WIDE_TAB:
3338 case LyXParagraph::ALGORITHM:
3341 case LyXParagraph::FOOTNOTE:
3346 // Determine background color.
3347 gc_type back = gc_lighted;
3351 scr.fillRectangle(back,LYX_PAPER_MARGIN, offset+1,
3352 box_x - LYX_PAPER_MARGIN,
3353 int(font.maxAscent())+
3354 int(font.maxDescent()));
3356 scr.drawLine(gc_foot,
3359 paperwidth - 2*LYX_PAPER_MARGIN);
3361 scr.drawString(font, fs,
3362 offset + int(font.maxAscent())+1,
3364 scr.drawVerticalLine(gc_foot,
3368 + int(font.maxAscent())+
3369 int(font.maxDescent()));
3371 scr.drawLine(gc_foot,
3373 + int(font.maxAscent())
3374 + int(font.maxDescent()) + 1,
3375 LYX_PAPER_MARGIN, box_x - LYX_PAPER_MARGIN);
3378 /* draw the open floats in a red box */
3379 scr.drawVerticalLine(gc_foot,
3381 offset, offset + row_ptr->height);
3383 scr.drawVerticalLine(gc_foot,
3384 paperwidth - LYX_PAPER_MARGIN,
3386 offset + row_ptr->height);
3390 if (row_ptr->previous &&
3391 row_ptr->previous->par->footnoteflag == LyXParagraph::OPEN_FOOTNOTE) {
3392 LyXFont font(LyXFont::ALL_SANE);
3393 font.setSize(LyXFont::SIZE_FOOTNOTE);
3395 int box_x = LYX_PAPER_MARGIN;
3396 box_x += font.textWidth(" wide-tab ", 10);
3398 scr.drawLine(gc_foot,
3401 paperwidth - LYX_PAPER_MARGIN - box_x);
3405 LyXLayout* layout = lyxstyle.Style(parameters->textclass,
3406 row_ptr->par->GetLayout());
3407 firstpar = row_ptr->par->FirstPhysicalPar();
3410 y_bottom = row_ptr->height;
3412 /* is it a first row? */
3413 if (row_ptr->pos == 0
3414 && row_ptr->par == firstpar) {
3416 /* start of appendix? */
3417 if (row_ptr->par->start_of_appendix){
3418 scr.drawLine(gc_math,
3423 /* think about the margins */
3424 if (!row_ptr->previous)
3425 y_top += LYX_PAPER_MARGIN;
3427 if (row_ptr->par->pagebreak_top){ /* draw a top pagebreak */
3428 scr.drawOnOffLine(offset + y_top + 2 * DefaultHeight(),
3430 y_top += 3 * DefaultHeight();
3433 if (row_ptr->par->added_space_top.kind() == VSpace::VFILL) {
3434 /* draw a vfill top */
3435 scr.drawLine(gc_fill,
3437 0, LYX_PAPER_MARGIN);
3438 scr.drawLine(gc_fill,
3439 offset + y_top + 3 * DefaultHeight(),
3440 0, LYX_PAPER_MARGIN);
3441 scr.drawVerticalOnOffLine(LYX_PAPER_MARGIN / 2,
3443 offset + y_top + 3 *
3446 y_top += 3 * DefaultHeight();
3449 /* think about user added space */
3450 y_top += int(row_ptr->par->added_space_top.inPixels());
3452 /* think about the parskip */
3453 /* some parskips VERY EASY IMPLEMENTATION */
3454 if (parameters->paragraph_separation == LYX_PARSEP_SKIP) {
3455 if (layout->latextype == LATEX_PARAGRAPH
3456 && firstpar->GetDepth() == 0
3457 && firstpar->Previous())
3458 y_top += parameters->getDefSkip().inPixels();
3459 else if (firstpar->Previous()
3460 && lyxstyle.Style(parameters->textclass,
3461 firstpar->Previous()->GetLayout())->latextype == LATEX_PARAGRAPH
3462 && firstpar->Previous()->GetDepth() == 0)
3463 // is it right to use defskip here, too? (AS)
3464 y_top += parameters->getDefSkip().inPixels();
3467 if (row_ptr->par->line_top) { /* draw a top line */
3468 y_top += GetFont(row_ptr->par, 0).ascent('x');
3470 scr.drawThickLine(offset + y_top,
3472 y_top += GetFont(row_ptr->par, 0).ascent('x');
3475 /* should we print a label? */
3476 if (layout->labeltype >= LABEL_STATIC
3477 && (layout->labeltype != LABEL_STATIC
3478 || layout->latextype != LATEX_ENVIRONMENT
3479 || row_ptr->par->IsFirstInSequence())) {
3480 font = GetFont(row_ptr->par, -2);
3481 if (!row_ptr->par->GetLabestring().empty()) {
3483 string tmpstring = row_ptr->par->GetLabestring();
3485 if (layout->labeltype == LABEL_COUNTER_CHAPTER) {
3486 if (parameters->secnumdepth >=0){
3487 /* this is special code for the chapter layout. This is printed in
3488 * an extra row and has a pagebreak at the top. */
3489 maxdesc = int(font.maxDescent() * layout->spacing.getValue() * parameters->spacing.getValue())
3490 + int(layout->parsep) * DefaultHeight();
3491 scr.drawString(font, tmpstring,
3492 offset + row_ptr->baseline
3493 - row_ptr->ascent_of_text - maxdesc,
3497 x -= font.stringWidth( layout->labelsep);
3498 x -= font.stringWidth( tmpstring);
3500 scr.drawString(font, tmpstring,
3501 offset + row_ptr->baseline, int(x));
3505 /* the labels at the top of an environment. More or less for bibliography */
3506 } else if (layout->labeltype == LABEL_TOP_ENVIRONMENT ||
3507 layout->labeltype == LABEL_BIBLIO ||
3508 layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) {
3509 if (row_ptr->par->IsFirstInSequence()) {
3510 font = GetFont(row_ptr->par, -2);
3511 if (!row_ptr->par->GetLabestring().empty()) {
3512 string tmpstring = row_ptr->par->GetLabestring();
3514 maxdesc = int(font.maxDescent() * layout->spacing.getValue() * parameters->spacing.getValue()
3515 + (layout->labelbottomsep * DefaultHeight()));
3517 int top_label_x = int(x);
3518 if (layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT){
3519 top_label_x = int(x + (paperwidth - RightMargin(row_ptr) - x) / 2);
3520 top_label_x -= (font.stringWidth( tmpstring)/2);
3523 scr.drawString(font, tmpstring,
3524 offset + row_ptr->baseline
3525 - row_ptr->ascent_of_text - maxdesc,
3530 if (layout->labeltype==LABEL_BIBLIO) { // ale970302
3531 if (row_ptr->par->bibkey) {
3533 x -= font.stringWidth(layout->labelsep);
3534 font = GetFont(row_ptr->par, -1);
3535 x -= row_ptr->par->bibkey->Width(font);
3536 row_ptr->par->bibkey->Draw(font, scr,
3537 offset + row_ptr->baseline,
3544 /* is it a last row? */
3545 par = row_ptr->par->LastPhysicalPar();
3546 if (row_ptr->par->ParFromPos(RowLast(row_ptr) + 1) == par
3547 && (!row_ptr->next || row_ptr->next->par != row_ptr->par)) {
3549 /* think about the margins */
3551 y_bottom -= LYX_PAPER_MARGIN;
3553 /* draw a bottom pagebreak */
3554 if (firstpar->pagebreak_bottom) {
3555 scr.drawOnOffLine(offset + y_bottom - 2 *
3558 y_bottom -= 3 * DefaultHeight();
3561 if (firstpar->added_space_bottom.kind() == VSpace::VFILL) {
3562 /* draw a vfill bottom */
3563 scr.drawLine(gc_fill,
3564 offset + y_bottom - 3 * DefaultHeight(),
3565 0, LYX_PAPER_MARGIN);
3566 scr.drawLine(gc_fill, offset + y_bottom - 2,
3567 0, LYX_PAPER_MARGIN);
3568 scr.drawVerticalOnOffLine(LYX_PAPER_MARGIN / 2,
3569 offset + y_bottom - 3 * DefaultHeight(),
3570 offset + y_bottom - 2
3572 y_bottom -= 3* DefaultHeight();
3575 /* think about user added space */
3576 y_bottom -= int(firstpar->added_space_bottom.inPixels());
3578 if (firstpar->line_bottom) {
3579 /* draw a bottom line */
3580 y_bottom -= GetFont(par, par->Last() - 1).ascent('x');
3582 scr.drawThickLine(offset + y_bottom,
3584 y_bottom -= GetFont(par, par->Last() - 1).ascent('x');
3588 /* draw the text in the pixmap */
3589 pos_end = RowLast(row_ptr);
3592 /* table stuff -- begin*/
3593 if (row_ptr->par->table) {
3595 int cell = NumberOfCell(row_ptr->par, row_ptr->pos);
3597 x += row_ptr->par->table->GetBeginningOfTextInCell(cell);
3599 while (pos <= pos_end) {
3600 if (row_ptr->par->IsNewline(pos)) {
3602 x = x_old + row_ptr->par->table->WidthOfColumn(cell);
3603 /* draw the table lines, still very simple */
3604 on_off = !row_ptr->par->table->TopLine(cell);
3606 !row_ptr->par->table->TopAlreadyDrawed(cell)) &&
3607 !row_ptr->par->table->IsContRow(cell))
3608 scr.drawTableLine(offset + row_ptr->baseline -
3609 row_ptr->ascent_of_text,
3610 int(x_old), int(x - x_old), on_off);
3611 on_off = !row_ptr->par->table->BottomLine(cell);
3612 if ((!on_off && !row_ptr->par->table->RowHasContRow(cell)) ||
3613 row_ptr->par->table->VeryLastRow(cell))
3614 scr.drawTableLine(offset + y_bottom - 1,
3615 int(x_old), int(x - x_old), on_off);
3616 on_off = !row_ptr->par->table->LeftLine(cell);
3618 scr.drawVerticalTableLine(int(x_old),
3619 offset + row_ptr->baseline -
3620 row_ptr->ascent_of_text,
3621 offset + y_bottom - 1,
3623 on_off = !row_ptr->par->table->RightLine(cell);
3625 scr.drawVerticalTableLine(int(x) -
3626 row_ptr->par->table->AdditionalWidth(cell),
3627 offset + row_ptr->baseline -
3628 row_ptr->ascent_of_text,
3629 offset + y_bottom - 1,
3632 /* take care about the alignment and other spaces */
3634 x += row_ptr->par->table->GetBeginningOfTextInCell(cell);
3635 if (row_ptr->par->table->IsFirstCell(cell))
3636 cell--; // little hack, sorry
3638 } else if (row_ptr->par->IsHfill(pos)) {
3641 scr.drawVerticalLine(gc_fill, int(x),
3642 offset + row_ptr->baseline - DefaultHeight()/2,
3643 offset + row_ptr->baseline);
3647 if (row_ptr->par->IsSeparator(pos)) {
3649 x+=SingleWidth(row_ptr->par, pos);
3650 /* -------> Only draw protected spaces when not in
3651 * free-spacing mode. */
3652 if (row_ptr->par->GetChar(pos)==LYX_META_PROTECTED_SEPARATOR && !layout->free_spacing) {
3653 scr.drawVerticalLine(gc_fill, int(tmpx),
3654 offset + row_ptr->baseline - 3,
3655 offset + row_ptr->baseline - 1);
3656 scr.drawLine(gc_fill,
3657 offset + row_ptr->baseline - 1,
3660 scr.drawVerticalLine(gc_fill, int(x-2),
3661 offset + row_ptr->baseline - 3,
3662 offset + row_ptr->baseline - 1);
3663 /* what about underbars? */
3664 font = GetFont(row_ptr->par, pos);
3665 if (font.underbar() == LyXFont::ON
3666 && font.latex() != LyXFont::ON) {
3667 scr.drawLine(gc_copy,
3669 row_ptr->baseline + 2,
3676 Draw(row_ptr, pos, scr, offset, x);
3680 /* do not forget the very last cell. This has no NEWLINE so
3681 * ignored by the code above*/
3682 if (cell == row_ptr->par->table->GetNumberOfCells()-1){
3683 x = x_old + row_ptr->par->table->WidthOfColumn(cell);
3684 on_off = !row_ptr->par->table->TopLine(cell);
3686 !row_ptr->par->table->TopAlreadyDrawed(cell)) &&
3687 !row_ptr->par->table->IsContRow(cell))
3689 scr.drawTableLine(offset + row_ptr->baseline -
3690 row_ptr->ascent_of_text,
3691 int(x_old), int(x - x_old), on_off);
3692 on_off = !row_ptr->par->table->BottomLine(cell);
3693 if ((!on_off && !row_ptr->par->table->RowHasContRow(cell)) ||
3694 row_ptr->par->table->VeryLastRow(cell))
3696 scr.drawTableLine(offset + y_bottom - 1,
3697 int(x_old), int(x - x_old), on_off);
3698 on_off = !row_ptr->par->table->LeftLine(cell);
3700 scr.drawVerticalTableLine(int(x_old),
3701 offset + row_ptr->baseline -
3702 row_ptr->ascent_of_text,
3703 offset + y_bottom - 1,
3705 on_off = !row_ptr->par->table->RightLine(cell);
3707 scr.drawVerticalTableLine(int(x) -
3708 row_ptr->par->table->AdditionalWidth(cell),
3709 offset + row_ptr->baseline -
3710 row_ptr->ascent_of_text,
3711 offset + y_bottom - 1,
3715 /* table stuff -- end*/
3717 while (pos <= pos_end) {
3719 if (row_ptr->par->IsHfill(pos)) {
3721 scr.drawVerticalLine(gc_fill, int(x),
3722 offset + row_ptr->baseline - DefaultHeight()/2,
3723 offset + row_ptr->baseline);
3724 if (HfillExpansion(row_ptr,pos)) {
3725 if (pos >= main_body) {
3726 scr.drawOnOffLine(offset + row_ptr->baseline -
3732 scr.drawOnOffLine(offset + row_ptr->baseline -
3735 int(fill_label_hfill));
3736 x += fill_label_hfill;
3738 scr.drawVerticalLine(gc_fill, int(x),
3739 offset + row_ptr->baseline -
3741 offset + row_ptr->baseline);
3746 if (row_ptr->par->IsSeparator(pos)) {
3748 x+=SingleWidth(row_ptr->par, pos);
3749 if (pos >= main_body)
3751 /* -------> Only draw protected spaces when not in
3752 * free-spacing mode. */
3753 if (row_ptr->par->GetChar(pos)==LYX_META_PROTECTED_SEPARATOR && !layout->free_spacing) {
3755 scr.drawVerticalLine(gc_fill, int(tmpx),
3756 offset + row_ptr->baseline - 3,
3757 offset + row_ptr->baseline - 1);
3758 scr.drawLine(gc_fill,
3759 offset + row_ptr->baseline - 1,
3762 scr.drawVerticalLine(gc_fill, int(x-2),
3763 offset + row_ptr->baseline - 3,
3764 offset + row_ptr->baseline - 1);
3765 /* what about underbars? */
3766 font = GetFont(row_ptr->par, pos);
3767 if (font.underbar() == LyXFont::ON
3768 && font.latex() != LyXFont::ON) {
3769 scr.drawLine(gc_copy,
3770 offset + row_ptr->baseline + 2,
3777 Draw(row_ptr, pos, scr, offset, x);
3779 if (pos == main_body) {
3780 x += GetFont(row_ptr->par, -2).stringWidth(
3782 if (row_ptr->par->IsLineSeparator(pos - 1))
3783 x-= SingleWidth(row_ptr->par, pos - 1);
3784 if (x < left_margin)
3789 // check for FAST SELECTION
3790 if (fast_selection || mono_video){
3793 /* selection code */
3794 if (sel_start_cursor.row == row_ptr && sel_end_cursor.row == row_ptr) {
3795 scr.fillRectangle(gc_select,sel_start_cursor.x, offset,
3796 sel_end_cursor.x - sel_start_cursor.x,
3798 } else if (sel_start_cursor.row == row_ptr) {
3799 scr.fillRectangle(gc_select, sel_start_cursor.x, offset,
3800 paperwidth - sel_start_cursor.x,
3802 } else if (sel_end_cursor.row == row_ptr) {
3803 scr.fillRectangle(gc_select, 0, offset,
3806 } else if (y > sel_start_cursor.y && y < sel_end_cursor.y) {
3807 scr.fillRectangle(gc_select, 0, offset,
3808 paperwidth, row_ptr->height);
3813 // end of FAST SELECTION code
3818 int LyXText::DefaultHeight()
3820 LyXFont font(LyXFont::ALL_SANE);
3821 return int(font.maxAscent() + font.maxDescent() * 1.5);
3825 /* returns the column near the specified x-coordinate of the row
3826 * x is set to the real beginning of this column */
3827 int LyXText::GetColumnNearX(Row *row, int& x)
3831 float fill_separator, fill_hfill, fill_label_hfill;
3834 left_margin = LabelEnd(row);
3835 PrepareToPrint(row, tmpx, fill_separator,
3836 fill_hfill, fill_label_hfill);
3837 int main_body = BeginningOfMainBody(row->par);
3841 int last = RowLast(row);
3842 if (row->par->IsNewline(last))
3845 LyXLayout *layout = lyxstyle.Style(parameters->textclass,
3846 row->par->GetLayout());
3847 /* table stuff -- begin*/
3848 if (row->par->table) {
3849 if (!row->next || row->next->par != row->par)
3850 last = RowLast(row); /* the last row doesn't need a newline at the end*/
3851 int cell = NumberOfCell(row->par, row->pos);
3854 tmpx += row->par->table->GetBeginningOfTextInCell(cell);
3856 && tmpx + (SingleWidth(row->par, c)/2) <= x
3858 if (row->par->IsNewline(c)) {
3859 if (x_old + row->par->table->WidthOfColumn(cell) <= x){
3860 tmpx = x_old + row->par->table->WidthOfColumn(cell);
3863 tmpx += row->par->table->GetBeginningOfTextInCell(cell);
3868 tmpx += SingleWidth(row->par, c);
3873 /* table stuff -- end*/
3876 && tmpx + (SingleWidth(row->par, c)/2) <= x) {
3878 if (c && c == main_body
3879 && !row->par->IsLineSeparator(c - 1)) {
3880 tmpx += GetFont(row->par, -2)
3881 .stringWidth(layout->labelsep);
3882 if (tmpx < left_margin)
3886 tmpx += SingleWidth(row->par, c);
3887 if (HfillExpansion(row, c)) {
3891 tmpx += fill_label_hfill;
3893 else if (c >= main_body
3894 && row->par->IsSeparator(c)) {
3895 tmpx+= fill_separator;
3899 && row->par->IsLineSeparator(c - 1)) {
3900 tmpx += GetFont(row->par, -2)
3901 .stringWidth(layout->labelsep);
3902 tmpx-= SingleWidth(row->par, c - 1);
3903 if (tmpx < left_margin)
3907 /* make sure that a last space in a row doesnt count */
3908 if (c > 0 && c >= last
3909 && row->par->IsLineSeparator(c - 1)
3910 && !(!row->next || row->next->par != row->par)) {
3911 tmpx -= SingleWidth(row->par, c - 1);
3912 tmpx -= fill_separator;
3921 /* turn the selection into a new environment. If there is no selection,
3922 * create an empty environment */
3923 void LyXText::InsertFootnoteEnvironment(LyXParagraph::footnote_kind kind)
3925 /* no footnoteenvironment in a footnoteenvironment */
3926 if (cursor.par->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
3927 WriteAlert(_("Impossible operation"),
3928 _("You can't insert a float in a float!"),
3932 /* no marginpars in minipages */
3933 if (kind == LyXParagraph::MARGIN
3934 && cursor.par->pextra_type == PEXTRA_MINIPAGE) {
3935 WriteAlert(_("Impossible operation"),
3936 _("You can't insert a marginpar in a minipage!"),
3941 /* this doesnt make sense, if there is no selection */
3942 bool dummy_selection = false;
3944 sel_start_cursor = cursor; /* dummy selection */
3945 sel_end_cursor = cursor;
3946 dummy_selection = true;
3949 LyXParagraph *tmppar;
3951 if (sel_start_cursor.par->table || sel_end_cursor.par->table){
3952 WriteAlert(_("Impossible operation"), _("Cannot cut table."), _("Sorry."));
3956 /* a test to make sure there is not already a footnote
3957 * in the selection. */
3959 tmppar = sel_start_cursor.par->ParFromPos(sel_start_cursor.pos);
3961 while (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos) &&
3962 tmppar->footnoteflag == LyXParagraph::NO_FOOTNOTE)
3963 tmppar = tmppar->next;
3965 if (tmppar != sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)
3966 || tmppar->footnoteflag != LyXParagraph::NO_FOOTNOTE) {
3967 WriteAlert(_("Impossible operation"),
3968 _("Float would include float!"),
3973 /* ok we have a selection. This is always between sel_start_cursor
3974 * and sel_end cursor */
3976 SetUndo(Undo::FINISH,
3977 sel_start_cursor.par->ParFromPos(sel_start_cursor.pos)->previous,
3978 sel_end_cursor.par->ParFromPos(sel_end_cursor.pos)->next);
3980 if (sel_end_cursor.pos > 0
3981 && sel_end_cursor.par->IsLineSeparator(sel_end_cursor.pos - 1))
3982 sel_end_cursor.pos--; /* please break before a space at
3984 if (sel_start_cursor.par == sel_end_cursor.par
3985 && sel_start_cursor.pos > sel_end_cursor.pos)
3986 sel_start_cursor.pos--;
3988 sel_end_cursor.par->BreakParagraphConservative(sel_end_cursor.pos);
3990 sel_end_cursor.par = sel_end_cursor.par->Next();
3991 sel_end_cursor.pos = 0;
3993 // don't forget to insert a dummy layout paragraph if necessary
3994 if (sel_start_cursor.par->GetLayout() != sel_end_cursor.par->layout){
3995 sel_end_cursor.par->BreakParagraphConservative(0);
3996 sel_end_cursor.par->layout = LYX_DUMMY_LAYOUT;
3997 sel_end_cursor.par = sel_end_cursor.par->next;
4000 sel_end_cursor.par->layout = LYX_DUMMY_LAYOUT;
4002 cursor = sel_end_cursor;
4004 /* please break behind a space, if there is one. The space should
4006 if (sel_start_cursor.pos > 0
4007 && sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos - 1))
4008 sel_start_cursor.pos--;
4009 if (sel_start_cursor.par->IsLineSeparator(sel_start_cursor.pos)) {
4010 sel_start_cursor.par->Erase(sel_start_cursor.pos);
4013 sel_start_cursor.par->BreakParagraphConservative(sel_start_cursor.pos);
4014 tmppar = sel_start_cursor.par->Next();
4016 if (dummy_selection) {
4018 if (kind == LyXParagraph::TAB
4019 || kind == LyXParagraph::FIG
4020 || kind == LyXParagraph::WIDE_TAB
4021 || kind == LyXParagraph::WIDE_FIG
4022 || kind == LyXParagraph::ALGORITHM) {
4023 int lay = lyxstyle.NumberOfLayout(parameters->textclass,
4025 if (lay == -1) // layout not found
4026 // use default layout "Standard" (0)
4028 tmppar->SetLayout(lay);
4032 if (sel_start_cursor.pos > 0) {
4033 /* the footnote-environment should begin with a standard layout.
4034 * Imagine you insert a footnote within an enumeration, you
4035 * certainly do not want an enumerated footnote! */
4039 /* this is a exception the user would sometimes expect, I hope */
4040 sel_start_cursor.par->Clear();
4044 while (tmppar != sel_end_cursor.par) {
4045 tmppar->footnoteflag = LyXParagraph::OPEN_FOOTNOTE;
4046 tmppar->footnotekind = kind;
4047 tmppar = tmppar->Next();
4050 RedoParagraphs(sel_start_cursor, sel_end_cursor.par->Next());
4052 SetCursor(sel_start_cursor.par->Next(), 0);
4058 /* returns pointer to a specified row */
4059 Row* LyXText::GetRow(LyXParagraph *par, int pos, long &y)
4064 if (par == currentrow->par || par == currentrow->par->Previous()){
4065 // do not dereference par, it may have been deleted
4066 // already! (Matthias)
4067 while (currentrow->previous && currentrow->previous->par != par){
4068 currentrow = currentrow->previous;
4069 currentrow_y -= currentrow->height;
4071 while (currentrow->previous && currentrow->previous->par == par){
4072 currentrow = currentrow->previous;
4073 currentrow_y -= currentrow->height;
4076 tmprow = currentrow;
4078 /* find the first row of the specified paragraph */
4079 while (tmprow->next && (tmprow->par != par)) {
4080 y += tmprow->height;
4081 tmprow = tmprow->next;
4084 if (tmprow->par == par){
4085 /* now find the wanted row */
4086 while (tmprow->pos < pos && tmprow->next && tmprow->next->par == par &&
4087 tmprow->next->pos <= pos) {
4088 y += tmprow->height;
4089 tmprow = tmprow->next;
4091 currentrow = tmprow;
4098 /* find the first row of the specified paragraph */
4099 while (tmprow->next && (tmprow->par != par)) {
4100 y += tmprow->height;
4101 tmprow = tmprow->next;
4104 /* now find the wanted row */
4105 while (tmprow->pos < pos && tmprow->next && tmprow->next->par == par &&
4106 tmprow->next->pos <= pos) {
4107 y += tmprow->height;
4108 tmprow = tmprow->next;
4111 currentrow = tmprow;