X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Ftext.C;h=8e68a70a32f797702cf157a8266ab2fa36d220dd;hb=fe390e9da1538e20eabbc98977d845295f8e563d;hp=4b653f5d14fe09a0579cd44d87647c775a895b35;hpb=0855311e9838bc9babf29c0fc28ee9bd3c6cd6a4;p=lyx.git diff --git a/src/text.C b/src/text.C index 4b653f5d14..8e68a70a32 100644 --- a/src/text.C +++ b/src/text.C @@ -30,10 +30,12 @@ #include "language.h" #include "ParagraphParameters.h" #include "undo_funcs.h" +#include "text_funcs.h" #include "WordLangTuple.h" #include "paragraph_funcs.h" +#include "rowpainter.h" +#include "lyxrow_funcs.h" -#include "insets/insetbib.h" #include "insets/insettext.h" #include "support/textutils.h" @@ -42,56 +44,122 @@ #include +using namespace lyx::support; + using std::max; using std::min; using std::endl; using std::pair; -using lyx::pos_type; -namespace { +using lyx::pos_type; +using lyx::word_location; -int const PAPER_MARGIN = 20; +using namespace bv_funcs; -} // namespace anon +/// top, right, bottom pixel margin +extern int const PAPER_MARGIN = 20; +/// margin for changebar +extern int const CHANGEBAR_MARGIN = 10; +/// left margin +extern int const LEFT_MARGIN = PAPER_MARGIN + CHANGEBAR_MARGIN; extern int bibitemMaxWidth(BufferView *, LyXFont const &); -int LyXText::workWidth(BufferView * bview) const +BufferView * LyXText::bv() { - if (inset_owner) { - return inset_owner->textWidth(bview); + Assert(bv_owner != 0); + return bv_owner; +} + + +BufferView * LyXText::bv() const +{ + Assert(bv_owner != 0); + return bv_owner; +} + + +void LyXText::updateRowPositions() +{ + RowList::iterator rit = rows().begin(); + RowList::iterator rend = rows().end(); + for (int y = 0; rit != rend ; ++rit) { + rit->y(y); + y += rit->height(); } - return bview->workWidth(); } -int LyXText::workWidth(BufferView * bview, Inset * inset) const +int LyXText::top_y() const { - Paragraph * par = 0; - pos_type pos = -1; - - par = inset->parOwner(); - if (par) - pos = par->getPositionOfInset(inset); - - if (!par || pos == -1) { - lyxerr << "LyXText::workWidth: something is wrong," - " fall back to the brute force method" << endl; - Buffer::inset_iterator it = bview->buffer()->inset_iterator_begin(); - Buffer::inset_iterator end = bview->buffer()->inset_iterator_end(); - for (; it != end; ++it) { - if (&(*it) == inset) { - par = it.getPar(); - pos = it.getPos(); - break; - } - } + if (anchor_row_ == rowlist_.end()) + return 0; + + return anchor_row_->y() + anchor_row_offset_; +} + + +void LyXText::top_y(int newy) +{ + if (rows().empty()) + return; + + if (isInInset()) { + anchor_row_ = rows().begin(); + anchor_row_offset_ = newy; + return; + } + + lyxerr[Debug::GUI] << "setting top y = " << newy << endl; + + int y = newy; + RowList::iterator rit = getRowNearY(y); + + if (rit == anchor_row_ && anchor_row_offset_ == newy - y) { + lyxerr[Debug::GUI] << "top_y to same value, skipping update" << endl; + return; } - if (!par) { - return workWidth(bview); + anchor_row_ = rit; + anchor_row_offset_ = newy - y; + lyxerr[Debug::GUI] << "changing reference to row: " << &*anchor_row_ + << " offset: " << anchor_row_offset_ << endl; + postPaint(0); +} + + +void LyXText::anchor_row(RowList::iterator rit) +{ + int old_y = top_y(); + anchor_row_offset_ = 0; + anchor_row_ = rit; + anchor_row_offset_ = old_y - top_y(); + lyxerr[Debug::GUI] << "anchor_row(): changing reference to row: " + << &*anchor_row_ << " offset: " + << anchor_row_offset_ << endl; +} + + +int LyXText::workWidth() const +{ + if (inset_owner) { + // FIXME: pass (const ?) ref + return inset_owner->textWidth(bv()); } + return bv()->workWidth(); +} + + +int LyXText::workWidth(Inset const * inset) const +{ + ParagraphList::iterator par = std::find(ownerParagraphs().begin(), + ownerParagraphs().end(), + *inset->parOwner()); + //Assert(par); + + pos_type pos = par->getPositionOfInset(inset); + Assert(pos != -1); LyXLayout_ptr const & layout = par->layout(); @@ -102,37 +170,44 @@ int LyXText::workWidth(BufferView * bview, Inset * inset) const Row dummyrow; dummyrow.par(par); dummyrow.pos(pos); - return workWidth(bview) - leftMargin(bview, &dummyrow); + return workWidth() - leftMargin(dummyrow); } else { int dummy_y; - Row * row = getRow(par, pos, dummy_y); - Row * frow = row; - while (frow->previous() && frow->par() == frow->previous()->par()) - frow = frow->previous(); + RowList::iterator row = getRow(par, pos, dummy_y); + RowList::iterator frow = row; + RowList::iterator beg = rowlist_.begin(); + + while (frow != beg && frow->par() == boost::prior(frow)->par()) + --frow; + + // FIXME: I don't understand this code - jbl + unsigned int maxw = 0; - while (frow->next() && frow->par() == frow->next()->par()) { + while (!isParEnd(*this, frow)) { if ((frow != row) && (maxw < frow->width())) maxw = frow->width(); - frow = frow->next(); + ++frow; } if (maxw) return maxw; + } - return workWidth(bview); + return workWidth(); } -int LyXText::getRealCursorX(BufferView * bview) const +int LyXText::getRealCursorX() const { int x = cursor.x(); - if (the_locking_inset && (the_locking_inset->getLyXText(bview)!=this)) - x = the_locking_inset->getLyXText(bview)->getRealCursorX(bview); + if (the_locking_inset && (the_locking_inset->getLyXText(bv())!= this)) + x = the_locking_inset->getLyXText(bv())->getRealCursorX(); return x; } -unsigned char LyXText::transformChar(unsigned char c, Paragraph * par, - pos_type pos) const +#warning FIXME This function seems to belong outside of LyxText. +unsigned char LyXText::transformChar(unsigned char c, Paragraph const & par, + pos_type pos) const { if (!Encodings::is_arabic(c)) if (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 && IsDigit(c)) @@ -140,14 +215,18 @@ unsigned char LyXText::transformChar(unsigned char c, Paragraph * par, else return c; - unsigned char const prev_char = pos > 0 ? par->getChar(pos-1) : ' '; + unsigned char const prev_char = pos > 0 ? par.getChar(pos - 1) : ' '; unsigned char next_char = ' '; - for (pos_type i = pos+1; i < par->size(); ++i) - if (!Encodings::IsComposeChar_arabic(par->getChar(i))) { - next_char = par->getChar(i); + pos_type const par_size = par.size(); + + for (pos_type i = pos + 1; i < par_size; ++i) { + unsigned char const par_char = par.getChar(i); + if (!Encodings::IsComposeChar_arabic(par_char)) { + next_char = par_char; break; } + } if (Encodings::is_arabic(next_char)) { if (Encodings::is_arabic(prev_char) && @@ -189,95 +268,103 @@ unsigned char LyXText::transformChar(unsigned char c, Paragraph * par, // // Lgb -int LyXText::singleWidth(BufferView * bview, Paragraph * par, - pos_type pos) const +int LyXText::singleWidth(ParagraphList::iterator pit, pos_type pos) const { - char const c = par->getChar(pos); - return singleWidth(bview, par, pos, c); + if (pos >= pit->size()) + return 0; + + char const c = pit->getChar(pos); + return singleWidth(pit, pos, c); } -int LyXText::singleWidth(BufferView * bview, Paragraph * par, +int LyXText::singleWidth(ParagraphList::iterator pit, pos_type pos, char c) const { - LyXFont const font = getFont(bview->buffer(), par, pos); + if (pos >= pit->size()) + return 0; + + LyXFont const font = getFont(bv()->buffer(), pit, pos); // The most common case is handled first (Asger) if (IsPrintable(c)) { if (font.language()->RightToLeft()) { if (font.language()->lang() == "arabic" && (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 || - lyxrc.font_norm_type == LyXRC::ISO_10646_1)) + lyxrc.font_norm_type == LyXRC::ISO_10646_1)) { if (Encodings::IsComposeChar_arabic(c)) return 0; else - c = transformChar(c, par, pos); - else if (font.language()->lang() == "hebrew" && + c = transformChar(c, *pit, pos); + } else if (font.language()->lang() == "hebrew" && Encodings::IsComposeChar_hebrew(c)) return 0; } return font_metrics::width(c, font); - } else if (IsHfillChar(c)) { - // Because of the representation as vertical lines - return 3; - } else if (c == Paragraph::META_INSET) { - Inset * tmpinset = par->getInset(pos); + } + + if (c == Paragraph::META_INSET) { + Inset * tmpinset = pit->getInset(pos); if (tmpinset) { + if (tmpinset->lyxCode() == Inset::HFILL_CODE) { + // Because of the representation as vertical lines + return 3; + } #if 1 +#warning inset->update FIXME // this IS needed otherwise on initialitation we don't get the fill // of the row right (ONLY on initialization if we read a file!) // should be changed! (Jug 20011204) - tmpinset->update(bview, font); + tmpinset->update(bv()); #endif - return tmpinset->width(bview, font); - } else - return 0; + return tmpinset->width(bv(), font); + } + return 0; + } - } else if (IsSeparatorChar(c)) + if (IsSeparatorChar(c)) c = ' '; - else if (IsNewlineChar(c)) - c = 'n'; return font_metrics::width(c, font); } -// Returns the paragraph position of the last character in the specified row -pos_type LyXText::rowLast(Row const * row) const +lyx::pos_type LyXText::log2vis(lyx::pos_type pos) const { - if (!row->next() || row->next()->par() != row->par()) { - return row->par()->size() - 1; - } else { - return row->next()->pos() - 1; - } + if (bidi_start == -1) + return pos; + else + return log2vis_list[pos - bidi_start]; } -pos_type LyXText::rowLastPrintable(Row const * row) const +lyx::pos_type LyXText::vis2log(lyx::pos_type pos) const { - pos_type const last = rowLast(row); - bool ignore_the_space_on_the_last_position = true; - Inset * ins; - // we have to consider a space on the last position in this case! - if (row->next() && row->par() == row->next()->par() && - row->next()->par()->getChar(last+1) == Paragraph::META_INSET && - (ins=row->next()->par()->getInset(last+1)) && - (ins->needFullRow() || ins->display())) - { - ignore_the_space_on_the_last_position = false; - } - if (last >= row->pos() - && row->next() - && row->next()->par() == row->par() - && row->par()->isSeparator(last) - && ignore_the_space_on_the_last_position) - return last - 1; + if (bidi_start == -1) + return pos; else - return last; + return vis2log_list[pos - bidi_start]; +} + + +lyx::pos_type LyXText::bidi_level(lyx::pos_type pos) const +{ + if (bidi_start == -1) + return 0; + else + return bidi_levels[pos - bidi_start]; +} + + +bool LyXText::bidi_InRange(lyx::pos_type pos) const +{ + return bidi_start == -1 || + (bidi_start <= pos && pos <= bidi_end); } -void LyXText::computeBidiTables(Buffer const * buf, Row * row) const +void LyXText::computeBidiTables(Buffer const * buf, + RowList::iterator row) const { bidi_same_direction = true; if (!lyxrc.rtl_support) { @@ -285,7 +372,9 @@ void LyXText::computeBidiTables(Buffer const * buf, Row * row) const return; } - Inset * inset = row->par()->inInset(); + ParagraphList::iterator row_par = row->par(); + + Inset * inset = row_par->inInset(); if (inset && inset->owner() && inset->owner()->lyxCode() == Inset::ERT_CODE) { bidi_start = -1; @@ -293,7 +382,7 @@ void LyXText::computeBidiTables(Buffer const * buf, Row * row) const } bidi_start = row->pos(); - bidi_end = rowLastPrintable(row); + bidi_end = lastPrintablePos(*this, row); if (bidi_start > bidi_end) { bidi_start = -1; @@ -315,25 +404,25 @@ void LyXText::computeBidiTables(Buffer const * buf, Row * row) const pos_type stack[2]; bool const rtl_par = - row->par()->isRightToLeftPar(buf->params); + row_par->isRightToLeftPar(buf->params); int level = 0; bool rtl = false; bool rtl0 = false; - pos_type const main_body = beginningOfMainBody(buf, row->par()); + pos_type const body_pos = row_par->beginningOfBody(); for (pos_type lpos = bidi_start; lpos <= bidi_end; ++lpos) { - bool is_space = row->par()->isLineSeparator(lpos); + bool is_space = row_par->isLineSeparator(lpos); pos_type const pos = (is_space && lpos + 1 <= bidi_end && - !row->par()->isLineSeparator(lpos + 1) && - !row->par()->isNewline(lpos + 1)) + !row_par->isLineSeparator(lpos + 1) && + !row_par->isNewline(lpos + 1)) ? lpos + 1 : lpos; - LyXFont font = row->par()->getFontSettings(buf->params, pos); + LyXFont font = row_par->getFontSettings(buf->params, pos); if (pos != lpos && 0 < lpos && rtl0 && font.isRightToLeft() && font.number() == LyXFont::ON && - row->par()->getFontSettings(buf->params, lpos - 1).number() + row_par->getFontSettings(buf->params, lpos - 1).number() == LyXFont::ON) { - font = row->par()->getFontSettings(buf->params, lpos); + font = row_par->getFontSettings(buf->params, lpos); is_space = false; } @@ -342,8 +431,8 @@ void LyXText::computeBidiTables(Buffer const * buf, Row * row) const bool new_rtl0 = font.isRightToLeft(); int new_level; - if (lpos == main_body - 1 - && row->pos() < main_body - 1 + if (lpos == body_pos - 1 + && row->pos() < body_pos - 1 && is_space) { new_level = (rtl_par) ? 1 : 0; new_rtl = new_rtl0 = rtl_par; @@ -404,7 +493,7 @@ void LyXText::computeBidiTables(Buffer const * buf, Row * row) const // This method requires a previous call to ComputeBidiTables() -bool LyXText::isBoundary(Buffer const * buf, Paragraph * par, +bool LyXText::isBoundary(Buffer const * buf, Paragraph const & par, pos_type pos) const { if (!lyxrc.rtl_support || pos == 0) @@ -419,12 +508,12 @@ bool LyXText::isBoundary(Buffer const * buf, Paragraph * par, bool const rtl = bidi_level(pos - 1) % 2; bool const rtl2 = bidi_InRange(pos) ? bidi_level(pos) % 2 - : par->isRightToLeftPar(buf->params); + : par.isRightToLeftPar(buf->params); return rtl != rtl2; } -bool LyXText::isBoundary(Buffer const * buf, Paragraph * par, +bool LyXText::isBoundary(Buffer const * buf, Paragraph const & par, pos_type pos, LyXFont const & font) const { if (!lyxrc.rtl_support) @@ -433,323 +522,65 @@ bool LyXText::isBoundary(Buffer const * buf, Paragraph * par, bool const rtl = font.isVisibleRightToLeft(); bool const rtl2 = bidi_InRange(pos) ? bidi_level(pos) % 2 - : par->isRightToLeftPar(buf->params); + : par.isRightToLeftPar(buf->params); return rtl != rtl2; } -void LyXText::drawNewline(DrawRowParams & p, pos_type const pos) -{ - // Draw end-of-line marker - LyXFont const font = getFont(p.bv->buffer(), p.row->par(), pos); - int const wid = font_metrics::width('n', font); - int const asc = font_metrics::maxAscent(font); - int const y = p.yo + p.row->baseline(); - int xp[3]; - int yp[3]; - - yp[0] = int(y - 0.875 * asc * 0.75); - yp[1] = int(y - 0.500 * asc * 0.75); - yp[2] = int(y - 0.125 * asc * 0.75); - - if (bidi_level(pos) % 2 == 0) { - xp[0] = int(p.x + wid * 0.375); - xp[1] = int(p.x); - xp[2] = int(p.x + wid * 0.375); - } else { - xp[0] = int(p.x + wid * 0.625); - xp[1] = int(p.x + wid); - xp[2] = int(p.x + wid * 0.625); - } - - p.pain->lines(xp, yp, 3, LColor::eolmarker); - - yp[0] = int(y - 0.500 * asc * 0.75); - yp[1] = int(y - 0.500 * asc * 0.75); - yp[2] = int(y - asc * 0.75); - - if (bidi_level(pos) % 2 == 0) { - xp[0] = int(p.x); - xp[1] = int(p.x + wid); - xp[2] = int(p.x + wid); - } else { - xp[0] = int(p.x + wid); - xp[1] = int(p.x); - xp[2] = int(p.x); - } - - p.pain->lines(xp, yp, 3, LColor::eolmarker); - - p.x += wid; -} - - -bool LyXText::drawInset(DrawRowParams & p, pos_type const pos) -{ - Inset * inset = p.row->par()->getInset(pos); - - // FIXME: shouldn't happen - if (!inset) { - return true; - } - - LyXFont const & font = getFont(p.bv->buffer(), p.row->par(), pos); - // we need this here as the row pointer may be illegal - // at a later time (Jug20020502) - Row * prev = p.row->previous(); - - inset->update(p.bv, font, false); - inset->draw(p.bv, font, p.yo + p.row->baseline(), p.x, p.cleared); - - if (!need_break_row && !inset_owner - && p.bv->text->status() == CHANGED_IN_DRAW) { - if (prev && prev->par() == p.row->par()) { - breakAgainOneRow(p.bv, prev); - if (prev->next() != p.row) { - // breakAgainOneRow() has removed p.row - p.row = 0; // see what this breaks - need_break_row = prev; - } else { - need_break_row = p.row; - } - } else if (!prev) { - need_break_row = firstrow; - } else { - need_break_row = prev->next(); - } - setCursor(p.bv, cursor.par(), cursor.pos()); - return false; - } - return true; -} - - -void LyXText::drawForeignMark(DrawRowParams & p, float const orig_x, LyXFont const & orig_font) -{ - if (!lyxrc.mark_foreign_language) - return; - if (orig_font.language() == latex_language) - return; - if (orig_font.language() == p.bv->buffer()->params.language) - return; - - int const y = p.yo + p.row->height() - 1; - p.pain->line(int(orig_x), y, int(p.x), y, LColor::language); -} - - -void LyXText::drawHebrewComposeChar(DrawRowParams & p, pos_type & vpos) -{ - pos_type pos = vis2log(vpos); - - string str; - - // first char - char c = p.row->par()->getChar(pos); - str += c; - ++vpos; - - LyXFont const & font = getFont(p.bv->buffer(), p.row->par(), pos); - int const width = font_metrics::width(c, font); - int dx = 0; - - for (pos_type i = pos-1; i >= 0; --i) { - c = p.row->par()->getChar(i); - if (!Encodings::IsComposeChar_hebrew(c)) { - if (IsPrintableNonspace(c)) { - int const width2 = - singleWidth(p.bv, p.row->par(), i, c); - // dalet / resh - dx = (c == 'ø' || c == 'ã') - ? width2 - width - : (width2 - width) / 2; - } - break; - } - } - - // Draw nikud - p.pain->text(int(p.x) + dx, p.yo + p.row->baseline(), str, font); -} - - -void LyXText::drawArabicComposeChar(DrawRowParams & p, pos_type & vpos) -{ - pos_type pos = vis2log(vpos); - string str; - - // first char - char c = p.row->par()->getChar(pos); - c = transformChar(c, p.row->par(), pos); - str +=c; - ++vpos; - - LyXFont const & font = getFont(p.bv->buffer(), p.row->par(), pos); - int const width = font_metrics::width(c, font); - int dx = 0; - - for (pos_type i = pos-1; i >= 0; --i) { - c = p.row->par()->getChar(i); - if (!Encodings::IsComposeChar_arabic(c)) { - if (IsPrintableNonspace(c)) { - int const width2 = - singleWidth(p.bv, p.row->par(), i, c); - dx = (width2 - width) / 2; - } - break; - } - } - // Draw nikud - p.pain->text(int(p.x) + dx, p.yo + p.row->baseline(), str, font); -} - - -void LyXText::drawChars(DrawRowParams & p, pos_type & vpos, - bool hebrew, bool arabic) -{ - pos_type pos = vis2log(vpos); - pos_type const last = rowLastPrintable(p.row); - LyXFont const & orig_font = getFont(p.bv->buffer(), p.row->par(), pos); - - // first character - string str; - str += p.row->par()->getChar(pos); - if (arabic) { - unsigned char c = str[0]; - str[0] = transformChar(c, p.row->par(), pos); - } - ++vpos; - - // collect as much similar chars as we can - while (vpos <= last && (pos = vis2log(vpos)) >= 0) { - char c = p.row->par()->getChar(pos); - - if (!IsPrintableNonspace(c)) - break; - - if (arabic && Encodings::IsComposeChar_arabic(c)) - break; - if (hebrew && Encodings::IsComposeChar_hebrew(c)) - break; - - if (orig_font != getFont(p.bv->buffer(), p.row->par(), pos)) - break; - - if (arabic) - c = transformChar(c, p.row->par(), pos); - str += c; - ++vpos; - } - - // Draw text and set the new x position - p.pain->text(int(p.x), p.yo + p.row->baseline(), str, orig_font); - p.x += font_metrics::width(str, orig_font); -} - - -bool LyXText::draw(DrawRowParams & p, pos_type & vpos) -{ - pos_type const pos = vis2log(vpos); - Paragraph * par = p.row->par(); - - LyXFont const & orig_font = getFont(p.bv->buffer(), par, pos); - - float const orig_x = p.x; - - char const c = par->getChar(pos); - - if (IsNewlineChar(c)) { - ++vpos; - drawNewline(p, pos); - return true; - } else if (IsInsetChar(c)) { - if (!drawInset(p, pos)) - return false; - ++vpos; - drawForeignMark(p, orig_x, orig_font); - return true; - } - - // usual characters, no insets - - // special case languages - bool const hebrew = (orig_font.language()->lang() == "hebrew"); - bool const arabic = - orig_font.language()->lang() == "arabic" && - (lyxrc.font_norm_type == LyXRC::ISO_8859_6_8 || - lyxrc.font_norm_type == LyXRC::ISO_10646_1); - - // draw as many chars as we can - if ((!hebrew && !arabic) - || (hebrew && !Encodings::IsComposeChar_hebrew(c)) - || (arabic && !Encodings::IsComposeChar_arabic(c))) { - drawChars(p, vpos, hebrew, arabic); - } else if (hebrew) { - drawHebrewComposeChar(p, vpos); - } else if (arabic) { - drawArabicComposeChar(p, vpos); - } - - drawForeignMark(p, orig_x, orig_font); - - return true; -} - - -int LyXText::leftMargin(BufferView * bview, Row const * row) const +int LyXText::leftMargin(Row const & row) const { Inset * ins; - if ((row->par()->getChar(row->pos()) == Paragraph::META_INSET) && - (ins=row->par()->getInset(row->pos())) && - (ins->needFullRow() || ins->display())) - return PAPER_MARGIN; + + if (row.pos() < row.par()->size()) + if ((row.par()->getChar(row.pos()) == Paragraph::META_INSET) && + (ins = row.par()->getInset(row.pos())) && + (ins->needFullRow() || ins->display())) + return LEFT_MARGIN; LyXTextClass const & tclass = - bview->buffer()->params.getLyXTextClass(); - LyXLayout_ptr const & layout = row->par()->layout(); + bv()->buffer()->params.getLyXTextClass(); + LyXLayout_ptr const & layout = row.par()->layout(); string parindent = layout->parindent; - int x = PAPER_MARGIN; + int x = LEFT_MARGIN; x += font_metrics::signedWidth(tclass.leftmargin(), tclass.defaultfont()); // this is the way, LyX handles the LaTeX-Environments. // I have had this idea very late, so it seems to be a // later added hack and this is true - if (!row->par()->getDepth()) { - if (row->par()->layout() == tclass.defaultLayout()) { + if (!row.par()->getDepth()) { + if (row.par()->layout() == tclass.defaultLayout()) { // find the previous same level paragraph - if (row->par()->previous()) { - Paragraph * newpar = row->par() - ->depthHook(row->par()->getDepth()); - if (newpar && - newpar->layout()->nextnoindent) + if (row.par() != ownerParagraphs().begin()) { + ParagraphList::iterator newpit = + depthHook(row.par(), ownerParagraphs(), + row.par()->getDepth()); + if (newpit == row.par() && + newpit->layout()->nextnoindent) parindent.erase(); } } } else { // find the next level paragraph - Paragraph * newpar = row->par()->outerHook(); + ParagraphList::iterator newpar = outerHook(row.par(), + ownerParagraphs()); - // make a corresponding row. Needed to call LeftMargin() + // make a corresponding row. Needed to call leftMargin() // check wether it is a sufficent paragraph - if (newpar && newpar->layout()->isEnvironment()) { + if (newpar != ownerParagraphs().end() && + newpar->layout()->isEnvironment()) { Row dummyrow; dummyrow.par(newpar); dummyrow.pos(newpar->size()); - x = leftMargin(bview, &dummyrow); - } else { - // this is no longer an error, because this function - // is used to clear impossible depths after changing - // a layout. Since there is always a redo, - // LeftMargin() is always called - row->par()->params().depth(0); + x = leftMargin(dummyrow); } - if (newpar && row->par()->layout() == tclass.defaultLayout()) { + if (newpar != ownerParagraphs().end() && + row.par()->layout() == tclass.defaultLayout()) { if (newpar->params().noindent()) parindent.erase(); else { @@ -759,26 +590,27 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const } } - LyXFont const labelfont = getLabelFont(bview->buffer(), row->par()); + LyXFont const labelfont = getLabelFont(bv()->buffer(), row.par()); switch (layout->margintype) { case MARGIN_DYNAMIC: if (!layout->leftmargin.empty()) { x += font_metrics::signedWidth(layout->leftmargin, tclass.defaultfont()); } - if (!row->par()->getLabelstring().empty()) { + if (!row.par()->getLabelstring().empty()) { x += font_metrics::signedWidth(layout->labelindent, labelfont); - x += font_metrics::width(row->par()->getLabelstring(), + x += font_metrics::width(row.par()->getLabelstring(), labelfont); x += font_metrics::width(layout->labelsep, labelfont); } break; case MARGIN_MANUAL: x += font_metrics::signedWidth(layout->labelindent, labelfont); - if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) { - if (!row->par()->getLabelWidthString().empty()) { - x += font_metrics::width(row->par()->getLabelWidthString(), + // The width of an empty par, even with manual label, should be 0 + if (!row.par()->empty() && row.pos() >= row.par()->beginningOfBody()) { + if (!row.par()->getLabelWidthString().empty()) { + x += font_metrics::width(row.par()->getLabelWidthString(), labelfont); x += font_metrics::width(layout->labelsep, labelfont); } @@ -786,23 +618,23 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const break; case MARGIN_STATIC: x += font_metrics::signedWidth(layout->leftmargin, tclass.defaultfont()) * 4 - / (row->par()->getDepth() + 4); + / (row.par()->getDepth() + 4); break; case MARGIN_FIRST_DYNAMIC: if (layout->labeltype == LABEL_MANUAL) { - if (row->pos() >= beginningOfMainBody(bview->buffer(), row->par())) { + if (row.pos() >= row.par()->beginningOfBody()) { x += font_metrics::signedWidth(layout->leftmargin, labelfont); } else { x += font_metrics::signedWidth(layout->labelindent, labelfont); } - } else if (row->pos() + } else if (row.pos() // Special case to fix problems with // theorems (JMarc) || (layout->labeltype == LABEL_STATIC && layout->latextype == LATEX_ENVIRONMENT - && ! row->par()->isFirstInSequence())) { + && !isFirstInSequence(row.par(), ownerParagraphs()))) { x += font_metrics::signedWidth(layout->leftmargin, labelfont); } else if (layout->labeltype != LABEL_TOP_ENVIRONMENT @@ -812,7 +644,7 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const x += font_metrics::signedWidth(layout->labelindent, labelfont); x += font_metrics::width(layout->labelsep, labelfont); - x += font_metrics::width(row->par()->getLabelstring(), + x += font_metrics::width(row.par()->getLabelstring(), labelfont); } break; @@ -824,16 +656,17 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const // are *NOT* allowed in the LaTeX realisation of this layout. // find the first row of this paragraph - Row const * tmprow = row; - while (tmprow->previous() - && tmprow->previous()->par() == row->par()) - tmprow = tmprow->previous(); + RowList::iterator tmprit = rowlist_.begin(); + while (tmprit != rowlist_.end() + && tmprit->par() != row.par()) + ++tmprit; - int minfill = tmprow->fill(); - while (tmprow->next() && tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); - if (tmprow->fill() < minfill) - minfill = tmprow->fill(); + int minfill = tmprit->fill(); + while (boost::next(tmprit) != rowlist_.end() && + boost::next(tmprit)->par() == row.par()) { + ++tmprit; + if (tmprit->fill() < minfill) + minfill = tmprit->fill(); } x += font_metrics::signedWidth(layout->leftmargin, @@ -843,44 +676,44 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const break; } - if ((workWidth(bview) > 0) && - !row->par()->params().leftIndent().zero()) + if ((workWidth() > 0) && + !row.par()->params().leftIndent().zero()) { - LyXLength const len = row->par()->params().leftIndent(); + LyXLength const len = row.par()->params().leftIndent(); int const tw = inset_owner ? - inset_owner->latexTextWidth(bview) : workWidth(bview); + inset_owner->latexTextWidth(bv()) : workWidth(); x += len.inPixels(tw); } - LyXAlignment align; // wrong type + LyXAlignment align; - if (row->par()->params().align() == LYX_ALIGN_LAYOUT) + if (row.par()->params().align() == LYX_ALIGN_LAYOUT) align = layout->align; else - align = row->par()->params().align(); + align = row.par()->params().align(); // set the correct parindent - if (row->pos() == 0) { + if (row.pos() == 0) { if ((layout->labeltype == LABEL_NO_LABEL || layout->labeltype == LABEL_TOP_ENVIRONMENT || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT || (layout->labeltype == LABEL_STATIC && layout->latextype == LATEX_ENVIRONMENT - && ! row->par()->isFirstInSequence())) + && !isFirstInSequence(row.par(), ownerParagraphs()))) && align == LYX_ALIGN_BLOCK - && !row->par()->params().noindent() + && !row.par()->params().noindent() // in tabulars and ert paragraphs are never indented! - && (!row->par()->inInset() || !row->par()->inInset()->owner() || - (row->par()->inInset()->owner()->lyxCode() != Inset::TABULAR_CODE && - row->par()->inInset()->owner()->lyxCode() != Inset::ERT_CODE)) - && (row->par()->layout() != tclass.defaultLayout() || - bview->buffer()->params.paragraph_separation == + && (!row.par()->inInset() || !row.par()->inInset()->owner() || + (row.par()->inInset()->owner()->lyxCode() != Inset::TABULAR_CODE && + row.par()->inInset()->owner()->lyxCode() != Inset::ERT_CODE)) + && (row.par()->layout() != tclass.defaultLayout() || + bv()->buffer()->params.paragraph_separation == BufferParams::PARSEP_INDENT)) { x += font_metrics::signedWidth(parindent, tclass.defaultfont()); } else if (layout->labeltype == LABEL_BIBLIO) { // ale970405 Right width for bibitems - x += bibitemMaxWidth(bview, tclass.defaultfont()); + x += bibitemMaxWidth(bv(), tclass.defaultfont()); } } @@ -888,412 +721,305 @@ int LyXText::leftMargin(BufferView * bview, Row const * row) const } -int LyXText::rightMargin(Buffer const * buf, Row const * row) const +int LyXText::rightMargin(Buffer const & buf, Row const & row) const { Inset * ins; - if ((row->par()->getChar(row->pos()) == Paragraph::META_INSET) && - (ins=row->par()->getInset(row->pos())) && - (ins->needFullRow() || ins->display())) - return PAPER_MARGIN; - LyXTextClass const & tclass = buf->params.getLyXTextClass(); - LyXLayout_ptr const & layout = row->par()->layout(); + if (row.pos() < row.par()->size()) + if ((row.par()->getChar(row.pos()) == Paragraph::META_INSET) && + (ins = row.par()->getInset(row.pos())) && + (ins->needFullRow() || ins->display())) + return PAPER_MARGIN; + + LyXTextClass const & tclass = buf.params.getLyXTextClass(); + LyXLayout_ptr const & layout = row.par()->layout(); int x = PAPER_MARGIN + font_metrics::signedWidth(tclass.rightmargin(), tclass.defaultfont()); - // this is the way, LyX handles the LaTeX-Environments. - // I have had this idea very late, so it seems to be a - // later added hack and this is true - if (row->par()->getDepth()) { - // find the next level paragraph - - Paragraph * newpar = row->par(); - - do { - newpar = newpar->previous(); - } while (newpar - && newpar->getDepth() >= row->par()->getDepth()); + x += font_metrics::signedWidth(layout->rightmargin, + tclass.defaultfont()) + * 4 / (row.par()->getDepth() + 4); + return x; +} - // make a corresponding row. Needed to call LeftMargin() - // check wether it is a sufficent paragraph - if (newpar && newpar->layout()->isEnvironment()) { - Row dummyrow; - dummyrow.par(newpar); - dummyrow.pos(0); - x = rightMargin(buf, &dummyrow); - } else { - // this is no longer an error, because this function - // is used to clear impossible depths after changing - // a layout. Since there is always a redo, - // LeftMargin() is always called - row->par()->params().depth(0); - } +int LyXText::labelEnd(Row const & row) const +{ + if (row.par()->layout()->margintype == MARGIN_MANUAL) { + Row tmprow = row; + tmprow.pos(row.par()->size()); + // return the beginning of the body + return leftMargin(tmprow); } - //lyxerr << "rightmargin: " << layout->rightmargin << endl; - x += font_metrics::signedWidth(layout->rightmargin, - tclass.defaultfont()) - * 4 / (row->par()->getDepth() + 4); - return x; + // LabelEnd is only needed if the layout + // fills a flushleft label. + return 0; } -int LyXText::labelEnd(BufferView * bview, Row const * row) const +namespace { + +// this needs special handling - only newlines count as a break point +pos_type addressBreakPoint(pos_type i, Paragraph const & par) { - if (row->par()->layout()->margintype == MARGIN_MANUAL) { - Row tmprow; - tmprow = *row; - tmprow.pos(row->par()->size()); - // just the beginning of the main body - return leftMargin(bview, &tmprow); - } else { - // LabelEnd is only needed, - // if the layout fills a flushleft label. - return 0; + for (; i < par.size(); ++i) { + if (par.isNewline(i)) + return i; } + + return par.size(); } +}; -// get the next breakpoint in a given paragraph -pos_type -LyXText::nextBreakPoint(BufferView * bview, Row const * row, int width) const + +pos_type LyXText::rowBreakPoint(Row const & row) const { - Paragraph * par = row->par(); + ParagraphList::iterator pit = row.par(); + + // maximum pixel width of a row. + int width = workWidth() - rightMargin(*bv()->buffer(), row); + // inset->textWidth() returns -1 via workWidth(), + // but why ? if (width < 0) - return par->size(); + return pit->size(); - pos_type const pos = row->pos(); + LyXLayout_ptr const & layout = pit->layout(); - // position of the last possible breakpoint - // -1 isn't a suitable value, but a flag - pos_type last_separator = -1; - width -= rightMargin(bview->buffer(), row); + if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) + return addressBreakPoint(row.pos(), *pit); - pos_type const main_body = - beginningOfMainBody(bview->buffer(), par); - LyXLayout_ptr const & layout = par->layout(); + pos_type const pos = row.pos(); + pos_type const body_pos = pit->beginningOfBody(); + pos_type const last = pit->size(); + pos_type point = last; + + if (pos == last) + return last; + + // Now we iterate through until we reach the right margin + // or the end of the par, then choose the possible break + // nearest that. + + int const left = leftMargin(row); + int x = left; + + // pixel width since last breakpoint + int chunkwidth = 0; + bool fullrow = false; pos_type i = pos; + for (; i < last; ++i) { + if (pit->isNewline(i)) { + point = i; + break; + } - if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) { - // special code for right address boxes, only newlines count - while (i < par->size()) { - if (par->isNewline(i)) { - last_separator = i; - i = par->size() - 1; // this means break - //x = width; - } else if (par->isInset(i) && par->getInset(i) - && par->getInset(i)->display()) { - par->getInset(i)->display(false); + char const c = pit->getChar(i); + + int thiswidth; + + // add the auto-hfill from label end to the body + if (body_pos && i == body_pos) { + thiswidth = font_metrics::width(layout->labelsep, + getLabelFont(bv()->buffer(), pit)); + if (pit->isLineSeparator(i - 1)) + thiswidth -= singleWidth(pit, i - 1); + int left_margin = labelEnd(row); + if (thiswidth + x < left_margin) + thiswidth = left_margin - x; + thiswidth += singleWidth(pit, i, c); + } else { + thiswidth = singleWidth(pit, i, c); + } + + x += thiswidth; + chunkwidth += thiswidth; + + Inset * in = pit->isInset(i) ? pit->getInset(i) : 0; + fullrow = (in && (in->display() || in->needFullRow())); + + // break before a character that will fall off + // the right of the row + if (x >= width) { + // if no break before or we are at an inset + // that will take up a row, break here + if (point == last || fullrow || chunkwidth >= (width - left)) { + if (pos < i) + point = i - 1; + else + point = i; } - ++i; + break; } - } else { - // Last position is an invariant - pos_type const last = par->size(); - // this is the usual handling - int x = leftMargin(bview, row); - bool doitonetime = true; - while (doitonetime || ((x < width) && (i < last))) { - doitonetime = false; - char const c = par->getChar(i); - Inset * in = 0; - if (c == Paragraph::META_INSET) - in = par->getInset(i); - if (IsNewlineChar(c)) { - last_separator = i; - x = width; // this means break - } else if (in && !in->isChar()) { - // check wether a Display() inset is - // valid here. if not, change it to - // non-display - if (in->display() && - (layout->isCommand() || - (layout->labeltype == LABEL_MANUAL - && i < beginningOfMainBody(bview->buffer(), par)))) - { - // display istn't allowd - in->display(false); - x += singleWidth(bview, par, i, c); - } else if (in->display() || in->needFullRow()) { - // So break the line here - if (i == pos) { - if (pos < last-1) { - last_separator = i; - if (par->isLineSeparator(i+1)) - ++last_separator; - } else - last_separator = last; // to avoid extra rows - } else - last_separator = i - 1; - x = width; // this means break - } else { - x += singleWidth(bview, par, i, c); - // we have to check this separately as we could have a - // lineseparator and then the algorithm below would prefer - // that which IS wrong! We should always break on an inset - // if it's too long and not on the last separator. - // Maybe the only exeption is insets used as chars but - // then we would have to have a special function inside - // the inset to tell us this. Till then we leave it as - // it is now. (Jug 20020106) - if (pos < i && x >= width && last_separator >= 0) - last_separator = i - 1; - } - } else { - if (par->isLineSeparator(i)) - last_separator = i; - x += singleWidth(bview, par, i, c); + + if (!in || in->isChar()) { + // some insets are line separators too + if (pit->isLineSeparator(i)) { + point = i; + chunkwidth = 0; } - ++i; - if (i == main_body) { - x += font_metrics::width(layout->labelsep, - getLabelFont(bview->buffer(), par)); - if (par->isLineSeparator(i - 1)) - x-= singleWidth(bview, par, i - 1); - int left_margin = labelEnd(bview, row); - if (x < left_margin) - x = left_margin; + continue; + } + + if (!fullrow) + continue; + + // full row insets start at a new row + if (i == pos) { + if (pos < last - 1) { + point = i; + if (pit->isLineSeparator(i + 1)) + ++point; + } else { + // to avoid extra rows + point = last; } + } else { + point = i - 1; } - if ((pos+1 < i) && (last_separator < 0) && (x >= width)) - last_separator = i - 2; - else if ((pos < i) && (last_separator < 0) && (x >= width)) - last_separator = i - 1; - // end of paragraph is always a suitable separator - else if (i == last && x < width) - last_separator = i; + + return point; } - // well, if last_separator is still 0, the line isn't breakable. - // don't care and cut simply at the end - if (last_separator < 0) { - last_separator = i; + if (point == last && x >= width) { + // didn't find one, break at the point we reached the edge + point = i; + } else if (i == last && x < width) { + // found one, but we fell off the end of the par, so prefer + // that. + point = last; } - // manual labels cannot be broken in LaTeX, do not care - if (main_body && last_separator < main_body) - last_separator = main_body - 1; + // manual labels cannot be broken in LaTeX. But we + // want to make our on-screen rendering of footnotes + // etc. still break + if (!fullrow && body_pos && point < body_pos) + point = body_pos - 1; - return last_separator; + return point; } // returns the minimum space a row needs on the screen in pixel -int LyXText::fill(BufferView * bview, Row * row, int paper_width) const +int LyXText::fill(RowList::iterator row, int paper_width) const { if (paper_width < 0) return 0; int w; // get the pure distance - pos_type const last = rowLastPrintable(row); + pos_type const last = lastPrintablePos(*this, row); + + ParagraphList::iterator pit = row->par(); + LyXLayout_ptr const & layout = pit->layout(); // special handling of the right address boxes - if (row->par()->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) { + if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) { int const tmpfill = row->fill(); row->fill(0); // the minfill in MarginLeft() - w = leftMargin(bview, row); + w = leftMargin(*row); row->fill(tmpfill); } else - w = leftMargin(bview, row); - - LyXLayout_ptr const & layout = row->par()->layout(); + w = leftMargin(*row); - pos_type const main_body = - beginningOfMainBody(bview->buffer(), row->par()); + pos_type const body_pos = pit->beginningOfBody(); pos_type i = row->pos(); while (i <= last) { - if (main_body > 0 && i == main_body) { - w += font_metrics::width(layout->labelsep, getLabelFont(bview->buffer(), row->par())); - if (row->par()->isLineSeparator(i - 1)) - w -= singleWidth(bview, row->par(), i - 1); - int left_margin = labelEnd(bview, row); + if (body_pos > 0 && i == body_pos) { + w += font_metrics::width(layout->labelsep, getLabelFont(bv()->buffer(), pit)); + if (pit->isLineSeparator(i - 1)) + w -= singleWidth(pit, i - 1); + int left_margin = labelEnd(*row); if (w < left_margin) w = left_margin; } - w += singleWidth(bview, row->par(), i); + w += singleWidth(pit, i); ++i; } - if (main_body > 0 && main_body > last) { - w += font_metrics::width(layout->labelsep, getLabelFont(bview->buffer(), row->par())); - if (last >= 0 && row->par()->isLineSeparator(last)) - w -= singleWidth(bview, row->par(), last); - int const left_margin = labelEnd(bview, row); + if (body_pos > 0 && body_pos > last) { + w += font_metrics::width(layout->labelsep, getLabelFont(bv()->buffer(), pit)); + if (last >= 0 && pit->isLineSeparator(last)) + w -= singleWidth(pit, last); + int const left_margin = labelEnd(*row); if (w < left_margin) w = left_margin; } - int const fill = paper_width - w - rightMargin(bview->buffer(), row); + int const fill = paper_width - w - rightMargin(*bv()->buffer(), *row); + + // If this case happens, it means that our calculation + // of the widths of the chars when we do rowBreakPoint() + // went wrong for some reason. Typically in list bodies. + // Things just about hobble on anyway, though you'll end + // up with a "fill_separator" less than zero, which corresponds + // to inter-word spacing being too small. Hopefully this problem + // will die when the label hacks die. + if (lyxerr.debugging() && fill < 0) { + lyxerr[Debug::GUI] << "Eek, fill() was < 0: " << fill + << " w " << w << " paper_width " << paper_width + << " right margin " << rightMargin(*bv()->buffer(), *row) << endl; + } + return fill; } // returns the minimum space a manual label needs on the screen in pixel -int LyXText::labelFill(BufferView * bview, Row const * row) const +int LyXText::labelFill(Row const & row) const { - pos_type last = beginningOfMainBody(bview->buffer(), row->par()) - 1; + ParagraphList::iterator pit = row.par(); + + pos_type last = pit->beginningOfBody(); + + Assert(last > 0); + // -1 because a label ends either with a space that is in the label, // or with the beginning of a footnote that is outside the label. - - // I don't understand this code in depth, but sometimes "last" is - // less than 0 and this causes a crash. This fix seems to work - // correctly, but I bet the real error is elsewhere. The bug is - // triggered when you have an open footnote in a paragraph - // environment with a manual label. (Asger) - if (last < 0) last = 0; + --last; // a separator at this end does not count - if (row->par()->isLineSeparator(last)) + if (pit->isLineSeparator(last)) --last; int w = 0; - pos_type i = row->pos(); + pos_type i = row.pos(); while (i <= last) { - w += singleWidth(bview, row->par(), i); + w += singleWidth(pit, i); ++i; } int fill = 0; - if (!row->par()->params().labelWidthString().empty()) { - fill = max(font_metrics::width(row->par()->params().labelWidthString(), - getLabelFont(bview->buffer(), row->par())) - w, - 0); + string const & labwidstr = pit->params().labelWidthString(); + if (!labwidstr.empty()) { + LyXFont const labfont = getLabelFont(bv()->buffer(), pit); + int const labwidth = font_metrics::width(labwidstr, labfont); + fill = max(labwidth - w, 0); } return fill; } -// returns the number of separators in the specified row. The separator -// on the very last column doesnt count -int LyXText::numberOfSeparators(Buffer const * buf, Row const * row) const +LColor::color LyXText::backgroundColor() const { - pos_type last = rowLastPrintable(row); - pos_type p = max(row->pos(), beginningOfMainBody(buf, row->par())); - - int n = 0; - for (; p <= last; ++p) { - if (row->par()->isSeparator(p)) { - ++n; - } - } - return n; + if (inset_owner) + return inset_owner->backgroundColor(); + else + return LColor::background; } -// returns the number of hfills in the specified row. The LyX-Hfill is -// a LaTeX \hfill so that the hfills at the beginning and at the end were -// ignored. This is *MUCH* more usefull than not to ignore! -int LyXText::numberOfHfills(Buffer const * buf, Row const * row) const +void LyXText::setHeightOfRow(RowList::iterator rit) { - pos_type const last = rowLast(row); - pos_type first = row->pos(); - - if (first) { - // hfill *DO* count at the beginning of paragraphs! - while (first <= last && row->par()->isHfill(first)) { - ++first; - } - } + Assert(rit != rows().end()); - first = max(first, beginningOfMainBody(buf, row->par())); - int n = 0; - for (pos_type p = first; p <= last; ++p) { - // last, because the end is ignored! - - if (row->par()->isHfill(p)) { - ++n; - } - } - return n; -} - - -// like NumberOfHfills, but only those in the manual label! -int LyXText::numberOfLabelHfills(Buffer const * buf, Row const * row) const -{ - pos_type last = rowLast(row); - pos_type first = row->pos(); - if (first) { - // hfill *DO* count at the beginning of paragraphs! - while (first < last && row->par()->isHfill(first)) - ++first; - } - - last = min(last, beginningOfMainBody(buf, row->par())); - int n = 0; - for (pos_type p = first; p < last; ++p) { - // last, because the end is ignored! - if (row->par()->isHfill(p)) { - ++n; - } - } - return n; -} - - -// returns true, if a expansion is needed. -// Rules are given by LaTeX -bool LyXText::hfillExpansion(Buffer const * buf, Row const * row_ptr, - pos_type pos) const -{ - // by the way, is it a hfill? - if (!row_ptr->par()->isHfill(pos)) - return false; - - // at the end of a row it does not count - // unless another hfill exists on the line - if (pos >= rowLast(row_ptr)) { - pos_type i = row_ptr->pos(); - while (i < pos && !row_ptr->par()->isHfill(i)) { - ++i; - } - if (i == pos) { - return false; - } - } - - // at the beginning of a row it does not count, if it is not - // the first row of a paragaph - if (!row_ptr->pos()) - return true; - - // in some labels it does not count - if (row_ptr->par()->layout()->margintype != MARGIN_MANUAL - && pos < beginningOfMainBody(buf, row_ptr->par())) - return false; - - // if there is anything between the first char of the row and - // the sepcified position that is not a newline and not a hfill, - // the hfill will count, otherwise not - pos_type i = row_ptr->pos(); - while (i < pos && (row_ptr->par()->isNewline(i) - || row_ptr->par()->isHfill(i))) - ++i; - - return i != pos; -} - - -LColor::color LyXText::backgroundColor() -{ - if (inset_owner) - return inset_owner->backgroundColor(); - else - return LColor::background; -} - -void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const -{ // get the maximum ascent and the maximum descent - int asc = 0; - int desc = 0; float layoutasc = 0; float layoutdesc = 0; float tmptop = 0; @@ -1307,27 +1033,26 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const // Correction: only the fontsize count. The other properties // are taken from the layoutfont. Nicer on the screen :) - Paragraph * par = row_ptr->par(); - Paragraph * firstpar = row_ptr->par(); + ParagraphList::iterator pit = rit->par(); - LyXLayout_ptr const & layout = firstpar->layout(); + LyXLayout_ptr const & layout = pit->layout(); - // as max get the first character of this row then it can increes but not - // decrees the height. Just some point to start with so we don't have to + // as max get the first character of this row then it can increase but not + // decrease the height. Just some point to start with so we don't have to // do the assignment below too often. - LyXFont font = getFont(bview->buffer(), par, row_ptr->pos()); + LyXFont font = getFont(bv()->buffer(), pit, rit->pos()); LyXFont::FONT_SIZE const tmpsize = font.size(); - font = getLayoutFont(bview->buffer(), par); + font = getLayoutFont(bv()->buffer(), pit); LyXFont::FONT_SIZE const size = font.size(); font.setSize(tmpsize); - LyXFont labelfont = getLabelFont(bview->buffer(), par); + LyXFont labelfont = getLabelFont(bv()->buffer(), pit); float spacing_val = 1.0; - if (!row_ptr->par()->params().spacing().isDefault()) { - spacing_val = row_ptr->par()->params().spacing().getValue(); + if (!pit->params().spacing().isDefault()) { + spacing_val = pit->params().spacing().getValue(); } else { - spacing_val = bview->buffer()->params.spacing.getValue(); + spacing_val = bv()->buffer()->params.spacing.getValue(); } //lyxerr << "spacing_val = " << spacing_val << endl; @@ -1338,27 +1063,30 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const layout->spacing.getValue() * spacing_val); - pos_type const pos_end = rowLast(row_ptr); + pos_type const pos_end = lastPos(*this, rit); int labeladdon = 0; int maxwidth = 0; - // Check if any insets are larger - for (pos_type pos = row_ptr->pos(); pos <= pos_end; ++pos) { - if (row_ptr->par()->isInset(pos)) { - tmpfont = getFont(bview->buffer(), row_ptr->par(), pos); - tmpinset = row_ptr->par()->getInset(pos); - if (tmpinset) { + if (!pit->empty()) { + // Check if any insets are larger + for (pos_type pos = rit->pos(); pos <= pos_end; ++pos) { + if (pit->isInset(pos)) { + tmpfont = getFont(bv()->buffer(), pit, pos); + tmpinset = pit->getInset(pos); + if (tmpinset) { #if 1 // this is needed for deep update on initialitation - tmpinset->update(bview, tmpfont); +#warning inset->update FIXME + tmpinset->update(bv()); #endif - asc = tmpinset->ascent(bview, tmpfont); - desc = tmpinset->descent(bview, tmpfont); - maxwidth += tmpinset->width(bview, tmpfont); - maxasc = max(maxasc, asc); - maxdesc = max(maxdesc, desc); + maxwidth += tmpinset->width(bv(), tmpfont); + maxasc = max(maxasc, + tmpinset->ascent(bv(), tmpfont)); + maxdesc = max(maxdesc, + tmpinset->descent(bv(), tmpfont)); + } + } else { + maxwidth += singleWidth(pit, pos); } - } else { - maxwidth += singleWidth(bview, row_ptr->par(), pos); } } @@ -1366,72 +1094,70 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const // This is not completely correct, but we can live with the small, // cosmetic error for now. LyXFont::FONT_SIZE maxsize = - row_ptr->par()->highestFontInRange(row_ptr->pos(), pos_end, size); + pit->highestFontInRange(rit->pos(), pos_end, size); if (maxsize > font.size()) { font.setSize(maxsize); - - asc = font_metrics::maxAscent(font); - desc = font_metrics::maxDescent(font); - if (asc > maxasc) - maxasc = asc; - if (desc > maxdesc) - maxdesc = desc; + maxasc = max(maxasc, font_metrics::maxAscent(font)); + maxdesc = max(maxdesc, font_metrics::maxDescent(font)); } // This is nicer with box insets: ++maxasc; ++maxdesc; - row_ptr->ascent_of_text(maxasc); + rit->ascent_of_text(maxasc); // is it a top line? - if (!row_ptr->pos() && (row_ptr->par() == firstpar)) { + if (!rit->pos()) { // some parksips VERY EASY IMPLEMENTATION - if (bview->buffer()->params.paragraph_separation == + if (bv()->buffer()->params.paragraph_separation == BufferParams::PARSEP_SKIP) { if (layout->isParagraph() - && firstpar->getDepth() == 0 - && firstpar->previous()) + && pit->getDepth() == 0 + && pit != ownerParagraphs().begin()) { - maxasc += bview->buffer()->params.getDefSkip().inPixels(bview); - } else if (firstpar->previous() && - firstpar->previous()->layout()->isParagraph() && - firstpar->previous()->getDepth() == 0) + maxasc += bv()->buffer()->params.getDefSkip().inPixels(*bv()); + } else if (pit != ownerParagraphs().begin() && + boost::prior(pit)->layout()->isParagraph() && + boost::prior(pit)->getDepth() == 0) { // is it right to use defskip here too? (AS) - maxasc += bview->buffer()->params.getDefSkip().inPixels(bview); + maxasc += bv()->buffer()->params.getDefSkip().inPixels(*bv()); } } - // the paper margins - if (!row_ptr->par()->previous() && bv_owner) + // the top margin + if (pit == ownerParagraphs().begin() && !isInInset()) maxasc += PAPER_MARGIN; // add the vertical spaces, that the user added - maxasc += getLengthMarkerHeight(bview, firstpar->params().spaceTop()); + maxasc += getLengthMarkerHeight(*bv(), pit->params().spaceTop()); // do not forget the DTP-lines! // there height depends on the font of the nearest character - if (firstpar->params().lineTop()) + if (pit->params().lineTop()) - maxasc += 2 * font_metrics::ascent('x', getFont(bview->buffer(), - firstpar, 0)); + maxasc += 2 * font_metrics::ascent('x', getFont(bv()->buffer(), + pit, 0)); // and now the pagebreaks - if (firstpar->params().pagebreakTop()) - maxasc += 3 * defaultHeight(); + if (pit->params().pagebreakTop()) + maxasc += 3 * defaultRowHeight(); + + if (pit->params().startOfAppendix()) + maxasc += 3 * defaultRowHeight(); // This is special code for the chapter, since the label of this // layout is printed in an extra row if (layout->labeltype == LABEL_COUNTER_CHAPTER - && bview->buffer()->params.secnumdepth >= 0) + && bv()->buffer()->params.secnumdepth >= 0) { float spacing_val = 1.0; - if (!row_ptr->par()->params().spacing().isDefault()) { - spacing_val = row_ptr->par()->params().spacing().getValue(); + if (!pit->params().spacing().isDefault()) { + spacing_val = pit->params().spacing().getValue(); } else { - spacing_val = bview->buffer()->params.spacing.getValue(); + spacing_val = bv()->buffer()->params.spacing.getValue(); } labeladdon = int(font_metrics::maxDescent(labelfont) * @@ -1446,14 +1172,14 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const if ((layout->labeltype == LABEL_TOP_ENVIRONMENT || layout->labeltype == LABEL_BIBLIO || layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) - && row_ptr->par()->isFirstInSequence() - && !row_ptr->par()->getLabelstring().empty()) + && isFirstInSequence(pit, ownerParagraphs()) + && !pit->getLabelstring().empty()) { float spacing_val = 1.0; - if (!row_ptr->par()->params().spacing().isDefault()) { - spacing_val = row_ptr->par()->params().spacing().getValue(); + if (!pit->params().spacing().isDefault()) { + spacing_val = pit->params().spacing().getValue(); } else { - spacing_val = bview->buffer()->params.spacing.getValue(); + spacing_val = bv()->buffer()->params.spacing.getValue(); } labeladdon = int( @@ -1463,277 +1189,289 @@ void LyXText::setHeightOfRow(BufferView * bview, Row * row_ptr) const +(font_metrics::maxDescent(labelfont) * layout->spacing.getValue() * spacing_val) - + layout->topsep * defaultHeight() - + layout->labelbottomsep * defaultHeight()); + + layout->topsep * defaultRowHeight() + + layout->labelbottomsep * defaultRowHeight()); } - // and now the layout spaces, for example before and after a section, - // or between the items of a itemize or enumerate environment - - if (!firstpar->params().pagebreakTop()) { - Paragraph * prev = row_ptr->par()->previous(); - if (prev) - prev = row_ptr->par()->depthHook(row_ptr->par()->getDepth()); - if (prev && prev->layout() == firstpar->layout() && - prev->getDepth() == firstpar->getDepth() && - prev->getLabelWidthString() == firstpar->getLabelWidthString()) + // And now the layout spaces, for example before and after + // a section, or between the items of a itemize or enumerate + // environment. + + if (!pit->params().pagebreakTop()) { + ParagraphList::iterator prev = + depthHook(pit, ownerParagraphs(), + pit->getDepth()); + if (prev != pit && prev->layout() == layout && + prev->getDepth() == pit->getDepth() && + prev->getLabelWidthString() == pit->getLabelWidthString()) { - layoutasc = (layout->itemsep * defaultHeight()); - } else if (row_ptr->previous()) { + layoutasc = (layout->itemsep * defaultRowHeight()); + } else if (rit != rows().begin()) { tmptop = layout->topsep; - if (row_ptr->previous()->par()->getDepth() >= row_ptr->par()->getDepth()) - tmptop -= row_ptr->previous()->par()->layout()->bottomsep; + if (boost::prior(pit)->getDepth() >= pit->getDepth()) + tmptop -= boost::prior(rit)->par()->layout()->bottomsep; if (tmptop > 0) - layoutasc = (tmptop * defaultHeight()); - } else if (row_ptr->par()->params().lineTop()) { + layoutasc = (tmptop * defaultRowHeight()); + } else if (pit->params().lineTop()) { tmptop = layout->topsep; if (tmptop > 0) - layoutasc = (tmptop * defaultHeight()); + layoutasc = (tmptop * defaultRowHeight()); } - prev = row_ptr->par()->outerHook(); - if (prev) { - maxasc += int(prev->layout()->parsep * defaultHeight()); - } else { - if (firstpar->previous() && - firstpar->previous()->getDepth() == 0 && - firstpar->previous()->layout() != - firstpar->layout()) - { - // avoid parsep - } else if (firstpar->previous()) { - maxasc += int(layout->parsep * defaultHeight()); + prev = outerHook(pit, ownerParagraphs()); + if (prev != ownerParagraphs().end()) { + maxasc += int(prev->layout()->parsep * defaultRowHeight()); + } else if (pit != ownerParagraphs().begin()) { + ParagraphList::iterator prior_pit = boost::prior(pit); + if (prior_pit->getDepth() != 0 || + prior_pit->layout() == layout) { + maxasc += int(layout->parsep * defaultRowHeight()); } } } } // is it a bottom line? - if (row_ptr->par() == par - && (!row_ptr->next() || row_ptr->next()->par() != row_ptr->par())) - { - // the paper margins - if (!par->next() && bv_owner) + RowList::iterator next_rit = boost::next(rit); + if (next_rit == rows().end() || + next_rit->par() != pit) { + // the bottom margin + ParagraphList::iterator nextpit = boost::next(pit); + if (nextpit == ownerParagraphs().end() && + !isInInset()) maxdesc += PAPER_MARGIN; // add the vertical spaces, that the user added - maxdesc += getLengthMarkerHeight(bview, firstpar->params().spaceBottom()); + maxdesc += getLengthMarkerHeight(*bv(), pit->params().spaceBottom()); // do not forget the DTP-lines! // there height depends on the font of the nearest character - if (firstpar->params().lineBottom()) + if (pit->params().lineBottom()) maxdesc += 2 * font_metrics::ascent('x', - getFont(bview->buffer(), - par, - max(pos_type(0), par->size() - 1))); + getFont(bv()->buffer(), + pit, + max(pos_type(0), pit->size() - 1))); // and now the pagebreaks - if (firstpar->params().pagebreakBottom()) - maxdesc += 3 * defaultHeight(); + if (pit->params().pagebreakBottom()) + maxdesc += 3 * defaultRowHeight(); // and now the layout spaces, for example before and after // a section, or between the items of a itemize or enumerate // environment - if (!firstpar->params().pagebreakBottom() - && row_ptr->par()->next()) { - Paragraph * nextpar = row_ptr->par()->next(); - Paragraph * comparepar = row_ptr->par(); + if (!pit->params().pagebreakBottom() + && nextpit != ownerParagraphs().end()) { + ParagraphList::iterator comparepit = pit; float usual = 0; float unusual = 0; - if (comparepar->getDepth() > nextpar->getDepth()) { - usual = (comparepar->layout()->bottomsep * defaultHeight()); - comparepar = comparepar->depthHook(nextpar->getDepth()); - if (comparepar->layout()!= nextpar->layout() - || nextpar->getLabelWidthString() != - comparepar->getLabelWidthString()) + if (comparepit->getDepth() > nextpit->getDepth()) { + usual = (comparepit->layout()->bottomsep * defaultRowHeight()); + comparepit = depthHook(comparepit, ownerParagraphs(), nextpit->getDepth()); + if (comparepit->layout()!= nextpit->layout() + || nextpit->getLabelWidthString() != + comparepit->getLabelWidthString()) { - unusual = (comparepar->layout()->bottomsep * defaultHeight()); + unusual = (comparepit->layout()->bottomsep * defaultRowHeight()); } if (unusual > usual) layoutdesc = unusual; else layoutdesc = usual; - } else if (comparepar->getDepth() == nextpar->getDepth()) { + } else if (comparepit->getDepth() == nextpit->getDepth()) { - if (comparepar->layout() != nextpar->layout() - || nextpar->getLabelWidthString() != - comparepar->getLabelWidthString()) - layoutdesc = int(comparepar->layout()->bottomsep * defaultHeight()); + if (comparepit->layout() != nextpit->layout() + || nextpit->getLabelWidthString() != + comparepit->getLabelWidthString()) + layoutdesc = int(comparepit->layout()->bottomsep * defaultRowHeight()); } } } // incalculate the layout spaces - maxasc += int(layoutasc * 2 / (2 + firstpar->getDepth())); - maxdesc += int(layoutdesc * 2 / (2 + firstpar->getDepth())); + maxasc += int(layoutasc * 2 / (2 + pit->getDepth())); + maxdesc += int(layoutdesc * 2 / (2 + pit->getDepth())); // calculate the new height of the text - height -= row_ptr->height(); + height -= rit->height(); - row_ptr->height(maxasc + maxdesc + labeladdon); - row_ptr->baseline(maxasc + labeladdon); + rit->height(maxasc + maxdesc + labeladdon); + rit->baseline(maxasc + labeladdon); + + height += rit->height(); + + rit->top_of_text(rit->baseline() - font_metrics::maxAscent(font)); - height += row_ptr->height(); float x = 0; if (layout->margintype != MARGIN_RIGHT_ADDRESS_BOX) { float dummy; // this IS needed - row_ptr->width(maxwidth); - prepareToPrint(bview, row_ptr, x, dummy, dummy, dummy, false); + rit->width(maxwidth); + prepareToPrint(rit, x, dummy, dummy, dummy, false); } - row_ptr->width(int(maxwidth + x)); + rit->width(int(maxwidth + x)); if (inset_owner) { - Row * r = firstrow; - width = max(0,workWidth(bview)); - while (r) { - if (r->width() > width) - width = r->width(); - r = r->next(); + width = max(0, workWidth()); + RowList::iterator it = rows().begin(); + RowList::iterator end = rows().end(); + for (; it != end; ++it) { + if (it->width() > width) + width = it->width(); } } } -// Appends the implicit specified paragraph behind the specified row, +// Appends the implicit specified paragraph before the specified row, // start at the implicit given position -void LyXText::appendParagraph(BufferView * bview, Row * row) const +void LyXText::appendParagraph(RowList::iterator rowit) { - bool not_ready = true; + Assert(rowit != rowlist_.end()); + + pos_type const last = rowit->par()->size(); + bool done = false; - // The last character position of a paragraph is an invariant so we can - // safely get it here. (Asger) - pos_type const lastposition = row->par()->size(); do { - // Get the next breakpoint - pos_type z = nextBreakPoint(bview, row, workWidth(bview)); + pos_type z = rowBreakPoint(*rowit); - Row * tmprow = row; + RowList::iterator tmprow = rowit; - // Insert the new row - if (z < lastposition) { + if (z < last) { ++z; - insertRow(row, row->par(), z); - row = row->next(); - - row->height(0); - } else - not_ready = false; + Row newrow(rowit->par(), z); + rowit = rowlist_.insert(boost::next(rowit), newrow); + } else { + done = true; + } // Set the dimensions of the row // fixed fill setting now by calling inset->update() in // SingleWidth when needed! - tmprow->fill(fill(bview, tmprow, workWidth(bview))); - setHeightOfRow(bview, tmprow); + tmprow->fill(fill(tmprow, workWidth())); + setHeightOfRow(tmprow); - } while (not_ready); + } while (!done); } -void LyXText::breakAgain(BufferView * bview, Row * row) const +void LyXText::breakAgain(RowList::iterator rit) { + Assert(rit != rows().end()); + bool not_ready = true; do { - // get the next breakpoint - pos_type z = nextBreakPoint(bview, row, workWidth(bview)); - Row * tmprow = row; + pos_type z = rowBreakPoint(*rit); + RowList::iterator tmprit = rit; + RowList::iterator end = rows().end(); - if (z < row->par()->size()) { - if (!row->next() || (row->next() && row->next()->par() != row->par())) { + if (z < rit->par()->size()) { + RowList::iterator next_rit = boost::next(rit); + + if (next_rit == end || + (next_rit != end && + next_rit->par() != rit->par())) { // insert a new row ++z; - insertRow(row, row->par(), z); - row = row->next(); - row->height(0); + Row newrow(rit->par(), z); + rit = rowlist_.insert(next_rit, newrow); } else { - row = row->next(); + ++rit; ++z; - if (row->pos() == z) - not_ready = false; // the rest will not change + if (rit->pos() == z) + not_ready = false; // the rest will not change else { - row->pos(z); + rit->pos(z); } } } else { // if there are some rows too much, delete them // only if you broke the whole paragraph! - Row * tmprow2 = row; - while (tmprow2->next() && tmprow2->next()->par() == row->par()) { - tmprow2 = tmprow2->next(); + RowList::iterator tmprit2 = rit; + while (boost::next(tmprit2) != end + && boost::next(tmprit2)->par() == rit->par()) { + ++tmprit2; } - while (tmprow2 != row) { - tmprow2 = tmprow2->previous(); - removeRow(tmprow2->next()); + while (tmprit2 != rit) { + --tmprit2; + removeRow(boost::next(tmprit2)); } not_ready = false; } // set the dimensions of the row - tmprow->fill(fill(bview, tmprow, workWidth(bview))); - setHeightOfRow(bview, tmprow); + tmprit->fill(fill(tmprit, workWidth())); + setHeightOfRow(tmprit); } while (not_ready); } // this is just a little changed version of break again -void LyXText::breakAgainOneRow(BufferView * bview, Row * row) +void LyXText::breakAgainOneRow(RowList::iterator rit) { - // get the next breakpoint - pos_type z = nextBreakPoint(bview, row, workWidth(bview)); - Row * tmprow = row; + Assert(rit != rows().end()); + + pos_type z = rowBreakPoint(*rit); + RowList::iterator tmprit = rit; + RowList::iterator end = rows().end(); - if (z < row->par()->size()) { - if (!row->next() - || (row->next() && row->next()->par() != row->par())) { + if (z < rit->par()->size()) { + RowList::iterator next_rit = boost::next(rit); + + if (next_rit == end || + (next_rit != end && + next_rit->par() != rit->par())) { // insert a new row ++z; - insertRow(row, row->par(), z); - row = row->next(); - row->height(0); + Row newrow(rit->par(), z); + rit = rowlist_.insert(next_rit, newrow); } else { - row = row->next(); + ++rit; ++z; - if (row->pos() != z) - row->pos(z); + if (rit->pos() != z) + rit->pos(z); } } else { // if there are some rows too much, delete them // only if you broke the whole paragraph! - Row * tmprow2 = row; - while (tmprow2->next() - && tmprow2->next()->par() == row->par()) { - tmprow2 = tmprow2->next(); + RowList::iterator tmprit2 = rit; + while (boost::next(tmprit2) != end + && boost::next(tmprit2)->par() == rit->par()) { + ++tmprit2; } - while (tmprow2 != row) { - tmprow2 = tmprow2->previous(); - removeRow(tmprow2->next()); + while (tmprit2 != rit) { + --tmprit2; + removeRow(boost::next(tmprit2)); } } // set the dimensions of the row - tmprow->fill(fill(bview, tmprow, workWidth(bview))); - setHeightOfRow(bview, tmprow); + tmprit->fill(fill(tmprit, workWidth())); + setHeightOfRow(tmprit); } -void LyXText::breakParagraph(BufferView * bview, char keep_layout) +void LyXText::breakParagraph(ParagraphList & paragraphs, char keep_layout) { + // allow only if at start or end, or all previous is new text + if (cursor.pos() && cursor.pos() != cursor.par()->size() + && cursor.par()->isChangeEdited(0, cursor.pos())) + return; + LyXTextClass const & tclass = - bview->buffer()->params.getLyXTextClass(); + bv()->buffer()->params.getLyXTextClass(); LyXLayout_ptr const & layout = cursor.par()->layout(); // this is only allowed, if the current paragraph is not empty or caption - // and if it has not the keepempty flag aktive - if (cursor.par()->empty() - && layout->labeltype != LABEL_SENSITIVE - && !layout->keepempty) + // and if it has not the keepempty flag active + if (cursor.par()->empty() && !cursor.par()->allowEmpty() + && layout->labeltype != LABEL_SENSITIVE) return; - setUndo(bview, Undo::FINISH, cursor.par(), cursor.par()->next()); + setUndo(bv(), Undo::FINISH, cursor.par()); // Always break behind a space // @@ -1753,8 +1491,8 @@ void LyXText::breakParagraph(BufferView * bview, char keep_layout) // breakParagraph call should return a bool if it inserts the // paragraph before or behind and we should react on that one // but we can fix this in 1.3.0 (Jug 20020509) - bool const isempty = (layout->keepempty && cursor.par()->empty()); - ::breakParagraph(bview->buffer()->params, cursor.par(), cursor.pos(), + bool const isempty = (cursor.par()->allowEmpty() && cursor.par()->empty()); + ::breakParagraph(bv()->buffer()->params, paragraphs, cursor.par(), cursor.pos(), keep_layout); // well this is the caption hack since one caption is really enough @@ -1764,81 +1502,87 @@ void LyXText::breakParagraph(BufferView * bview, char keep_layout) cursor.par()->applyLayout(tclass.defaultLayout()); else // set to standard-layout - cursor.par()->next()->applyLayout(tclass.defaultLayout()); + boost::next(cursor.par())->applyLayout(tclass.defaultLayout()); } // if the cursor is at the beginning of a row without prior newline, // move one row up! // This touches only the screen-update. Otherwise we would may have // an empty row on the screen - if (cursor.pos() && !cursor.row()->par()->isNewline(cursor.row()->pos() - 1) - && cursor.row()->pos() == cursor.pos()) + if (cursor.pos() && cursorRow()->pos() == cursor.pos() + && !cursorRow()->par()->isNewline(cursor.pos() - 1)) { - cursorLeft(bview); + cursorLeft(bv()); } - status(bview, LyXText::NEED_MORE_REFRESH); - refresh_row = cursor.row(); - refresh_y = cursor.y() - cursor.row()->baseline(); + int y = cursor.y() - cursorRow()->baseline(); // Do not forget the special right address boxes if (layout->margintype == MARGIN_RIGHT_ADDRESS_BOX) { - while (refresh_row->previous() && - refresh_row->previous()->par() == refresh_row->par()) - { - refresh_row = refresh_row->previous(); - refresh_y -= refresh_row->height(); + RowList::iterator r = cursorRow(); + RowList::iterator beg = rows().begin(); + + while (r != beg && boost::prior(r)->par() == r->par()) { + --r; + y -= r->height(); } } - removeParagraph(cursor.row()); + + postPaint(y); + + removeParagraph(cursorRow()); // set the dimensions of the cursor row - cursor.row()->fill(fill(bview, cursor.row(), workWidth(bview))); + cursorRow()->fill(fill(cursorRow(), workWidth())); - setHeightOfRow(bview, cursor.row()); + setHeightOfRow(cursorRow()); - while (!cursor.par()->next()->empty() - && cursor.par()->next()->isNewline(0)) - cursor.par()->next()->erase(0); +#warning Trouble Point! (Lgb) + // When ::breakParagraph is called from within an inset we must + // ensure that the correct ParagraphList is used. Today that is not + // the case and the Buffer::paragraphs is used. Not good. (Lgb) + ParagraphList::iterator next_par = boost::next(cursor.par()); - insertParagraph(bview, cursor.par()->next(), cursor.row()); + while (!next_par->empty() && next_par->isNewline(0)) + next_par->erase(0); - updateCounters(bview); + insertParagraph(next_par, boost::next(cursorRow())); + updateCounters(); // This check is necessary. Otherwise the new empty paragraph will // be deleted automatically. And it is more friendly for the user! if (cursor.pos() || isempty) - setCursor(bview, cursor.par()->next(), 0); + setCursor(next_par, 0); else - setCursor(bview, cursor.par(), 0); + setCursor(cursor.par(), 0); - if (cursor.row()->next()) - breakAgain(bview, cursor.row()->next()); + if (boost::next(cursorRow()) != rows().end()) + breakAgain(boost::next(cursorRow())); - need_break_row = 0; + need_break_row = rows().end(); } // Just a macro to make some thing easier. -void LyXText::redoParagraph(BufferView * bview) const +void LyXText::redoParagraph() { clearSelection(); - redoParagraphs(bview, cursor, cursor.par()->next()); - setCursorIntern(bview, cursor.par(), cursor.pos()); + redoParagraphs(cursor, boost::next(cursor.par())); + setCursorIntern(cursor.par(), cursor.pos()); } // insert a character, moves all the following breaks in the // same Paragraph one to the right and make a rebreak -void LyXText::insertChar(BufferView * bview, char c) +void LyXText::insertChar(char c) { - setUndo(bview, Undo::INSERT, cursor.par(), cursor.par()->next()); + setUndo(bv(), Undo::INSERT, cursor.par()); // When the free-spacing option is set for the current layout, // disable the double-space checking - bool const freeSpacing = cursor.row()->par()->layout()->free_spacing || - cursor.row()->par()->isFreeSpacing(); + bool const freeSpacing = cursorRow()->par()->layout()->free_spacing || + cursorRow()->par()->isFreeSpacing(); if (lyxrc.auto_number) { static string const number_operators = "+-/*"; @@ -1850,17 +1594,17 @@ void LyXText::insertChar(BufferView * bview, char c) !(contains(number_seperators, c) && cursor.pos() >= 1 && cursor.pos() < cursor.par()->size() && - getFont(bview->buffer(), + getFont(bv()->buffer(), cursor.par(), cursor.pos()).number() == LyXFont::ON && - getFont(bview->buffer(), + getFont(bv()->buffer(), cursor.par(), cursor.pos() - 1).number() == LyXFont::ON) ) - number(bview); // Set current_font.number to OFF + number(bv()); // Set current_font.number to OFF } else if (IsDigit(c) && real_current_font.isVisibleRightToLeft()) { - number(bview); // Set current_font.number to ON + number(bv()); // Set current_font.number to ON if (cursor.pos() > 0) { char const c = cursor.par()->getChar(cursor.pos() - 1); @@ -1869,16 +1613,16 @@ void LyXText::insertChar(BufferView * bview, char c) cursor.par()->isSeparator(cursor.pos() - 2) || cursor.par()->isNewline(cursor.pos() - 2)) ) { - setCharFont(bview->buffer(), + setCharFont(bv()->buffer(), cursor.par(), cursor.pos() - 1, current_font); } else if (contains(number_seperators, c) && cursor.pos() >= 2 && - getFont(bview->buffer(), + getFont(bv()->buffer(), cursor.par(), cursor.pos() - 2).number() == LyXFont::ON) { - setCharFont(bview->buffer(), + setCharFont(bv()->buffer(), cursor.par(), cursor.pos() - 1, current_font); @@ -1907,7 +1651,7 @@ void LyXText::insertChar(BufferView * bview, char c) // Get the font that is used to calculate the baselineskip pos_type const lastpos = cursor.par()->size(); LyXFont rawparfont = - cursor.par()->getFontSettings(bview->buffer()->params, + cursor.par()->getFontSettings(bv()->buffer()->params, lastpos - 1); bool jumped_over_space = false; @@ -1921,101 +1665,89 @@ void LyXText::insertChar(BufferView * bview, char c) static bool sent_space_message = false; if (!sent_space_message) { if (cursor.pos() == 0) - bview->owner()->message(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial.")); + bv()->owner()->message(_("You cannot insert a space at the beginning of a paragraph. Please read the Tutorial.")); else - bview->owner()->message(_("You cannot type two spaces this way. Please read the Tutorial.")); + bv()->owner()->message(_("You cannot type two spaces this way. Please read the Tutorial.")); sent_space_message = true; } charInserted(); return; } - } else if (IsNewlineChar(c)) { - if (cursor.pos() <= beginningOfMainBody(bview->buffer(), - cursor.par())) - { - charInserted(); - return; - } - // No newline at first position of a paragraph or behind labels. - // TeX does not allow that - - if (cursor.pos() < cursor.par()->size() && - cursor.par()->isLineSeparator(cursor.pos())) - // newline always after a blank! - cursorRight(bview); - cursor.row()->fill(-1); // to force a new break } // the display inset stuff - if (cursor.row()->par()->isInset(cursor.row()->pos())) { - Inset * inset = cursor.row()->par()->getInset(cursor.row()->pos()); + if (cursorRow()->pos() < cursorRow()->par()->size() + && cursorRow()->par()->isInset(cursorRow()->pos())) { + Inset * inset = cursorRow()->par()->getInset(cursorRow()->pos()); if (inset && (inset->display() || inset->needFullRow())) { // force a new break - cursor.row()->fill(-1); // to force a new break + cursorRow()->fill(-1); // to force a new break } } // get the cursor row fist - Row * row = cursor.row(); + RowList::iterator row = cursorRow(); int y = cursor.y() - row->baseline(); if (c != Paragraph::META_INSET) { // Here case LyXText::InsertInset already insertet the character cursor.par()->insertChar(cursor.pos(), c); } - setCharFont(bview->buffer(), cursor.par(), cursor.pos(), rawtmpfont); + setCharFont(bv()->buffer(), cursor.par(), cursor.pos(), rawtmpfont); if (!jumped_over_space) { // refresh the positions - Row * tmprow = row; - while (tmprow->next() && tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); + RowList::iterator tmprow = row; + while (boost::next(tmprow) != rows().end() && + boost::next(tmprow)->par() == row->par()) { + ++tmprow; tmprow->pos(tmprow->pos() + 1); } } // Is there a break one row above - if (row->previous() && row->previous()->par() == row->par() + if (row != rows().begin() && + boost::prior(row)->par() == row->par() && (cursor.par()->isLineSeparator(cursor.pos()) || cursor.par()->isNewline(cursor.pos()) - || ((cursor.pos() < cursor.par()->size()) && - cursor.par()->isInset(cursor.pos()+1)) - || cursor.row()->fill() == -1)) + || ((cursor.pos() + 1 < cursor.par()->size()) && + cursor.par()->isInset(cursor.pos() + 1)) + || cursorRow()->fill() == -1)) { - pos_type z = nextBreakPoint(bview, - row->previous(), - workWidth(bview)); + pos_type z = rowBreakPoint(*boost::prior(row)); + if (z >= row->pos()) { row->pos(z + 1); // set the dimensions of the row above - row->previous()->fill(fill(bview, - row->previous(), - workWidth(bview))); + boost::prior(row)->fill(fill( + boost::prior(row), + workWidth())); + + setHeightOfRow(boost::prior(row)); - setHeightOfRow(bview, row->previous()); + y -= boost::prior(row)->height(); - y -= row->previous()->height(); - refresh_y = y; - refresh_row = row->previous(); - status(bview, LyXText::NEED_MORE_REFRESH); + postPaint(y); - breakAgainOneRow(bview, row); + breakAgainOneRow(row); current_font = rawtmpfont; real_current_font = realtmpfont; - setCursor(bview, cursor.par(), cursor.pos() + 1, + setCursor(cursor.par(), cursor.pos() + 1, false, cursor.boundary()); // cursor MUST be in row now. - if (row->next() && row->next()->par() == row->par()) - need_break_row = row->next(); + RowList::iterator next_row = boost::next(row); + if (next_row != rows().end() && + next_row->par() == row->par()) + need_break_row = next_row; else - need_break_row = 0; + need_break_row = rows().end(); // check, wether the last characters font has changed. if (cursor.pos() && cursor.pos() == cursor.par()->size() && rawparfont != rawtmpfont) - redoHeightOfParagraph(bview, cursor); + redoHeightOfParagraph(); charInserted(); return; @@ -2026,60 +1758,70 @@ void LyXText::insertChar(BufferView * bview, char c) if (row->fill() >= 0) { // needed because a newline will set fill to -1. Otherwise // we would not get a rebreak! - row->fill(fill(bview, row, workWidth(bview))); + row->fill(fill(row, workWidth())); } if (c == Paragraph::META_INSET || row->fill() < 0) { - refresh_y = y; - refresh_row = row; - status(bview, LyXText::NEED_MORE_REFRESH); - breakAgainOneRow(bview, row); + postPaint(y); + breakAgainOneRow(row); + + RowList::iterator next_row = boost::next(row); + // will the cursor be in another row now? - if (rowLast(row) <= cursor.pos() + 1 && row->next()) { - if (row->next() && row->next()->par() == row->par()) + if (lastPos(*this, row) <= cursor.pos() + 1 && + next_row != rows().end()) { + if (next_row != rows().end() && + next_row->par() == row->par()) { // this should always be true - row = row->next(); - breakAgainOneRow(bview, row); + ++row; + } + + breakAgainOneRow(row); } current_font = rawtmpfont; real_current_font = realtmpfont; - setCursor(bview, cursor.par(), cursor.pos() + 1, false, + setCursor(cursor.par(), cursor.pos() + 1, false, cursor.boundary()); - if (isBoundary(bview->buffer(), cursor.par(), cursor.pos()) + if (isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()) != cursor.boundary()) - setCursor(bview, cursor.par(), cursor.pos(), false, + setCursor(cursor.par(), cursor.pos(), false, !cursor.boundary()); - if (row->next() && row->next()->par() == row->par()) - need_break_row = row->next(); + + next_row = boost::next(row); + + if (next_row != rows().end() && + next_row->par() == row->par()) + need_break_row = next_row; else - need_break_row = 0; + need_break_row = rows().end(); } else { - refresh_y = y; - refresh_row = row; - + // FIXME: similar code is duplicated all over - make resetHeightOfRow int const tmpheight = row->height(); - setHeightOfRow(bview, row); - if (tmpheight == row->height()) - status(bview, LyXText::NEED_VERY_LITTLE_REFRESH); - else - status(bview, LyXText::NEED_MORE_REFRESH); + + setHeightOfRow(row); + + if (tmpheight == row->height()) { + postRowPaint(row, y); + } else { + postPaint(y); + } current_font = rawtmpfont; real_current_font = realtmpfont; - setCursor(bview, cursor.par(), cursor.pos() + 1, false, + setCursor(cursor.par(), cursor.pos() + 1, false, cursor.boundary()); } // check, wether the last characters font has changed. if (cursor.pos() && cursor.pos() == cursor.par()->size() && rawparfont != rawtmpfont) { - redoHeightOfParagraph(bview, cursor); + redoHeightOfParagraph(); } else { // now the special right address boxes if (cursor.par()->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) { - redoDrawingOfParagraph(bview, cursor); + redoDrawingOfParagraph(cursor); } } @@ -2090,7 +1832,7 @@ void LyXText::insertChar(BufferView * bview, char c) void LyXText::charInserted() { // Here we could call FinishUndo for every 20 characters inserted. - // This is from my experience how emacs does it. + // This is from my experience how emacs does it. (Lgb) static unsigned int counter; if (counter < 20) { ++counter; @@ -2101,45 +1843,52 @@ void LyXText::charInserted() } -void LyXText::prepareToPrint(BufferView * bview, - Row * row, float & x, +void LyXText::prepareToPrint(RowList::iterator rit, float & x, float & fill_separator, float & fill_hfill, float & fill_label_hfill, bool bidi) const { - float nlh; - float ns; - - float w = row->fill(); + float w = rit->fill(); fill_hfill = 0; fill_label_hfill = 0; fill_separator = 0; fill_label_hfill = 0; + ParagraphList::iterator pit = rit->par(); + bool const is_rtl = - row->par()->isRightToLeftPar(bview->buffer()->params); + pit->isRightToLeftPar(bv()->buffer()->params); if (is_rtl) { - x = (workWidth(bview) > 0) - ? rightMargin(bview->buffer(), row) : 0; + x = (workWidth() > 0) + ? rightMargin(*bv()->buffer(), *rit) : 0; } else - x = (workWidth(bview) > 0) - ? leftMargin(bview, row) : 0; + x = (workWidth() > 0) + ? leftMargin(*rit) : 0; // is there a manual margin with a manual label - LyXLayout_ptr const & layout = row->par()->layout(); + LyXLayout_ptr const & layout = pit->layout(); if (layout->margintype == MARGIN_MANUAL && layout->labeltype == LABEL_MANUAL) { - // one more since labels are left aligned - nlh = numberOfLabelHfills(bview->buffer(), row) + 1; - if (nlh && !row->par()->getLabelWidthString().empty()) { - fill_label_hfill = labelFill(bview, row) / nlh; + /// We might have real hfills in the label part + float nlh = numberOfLabelHfills(*this, rit); + + // A manual label par (e.g. List) has an auto-hfill + // between the label text and the body of the + // paragraph too. + // But we don't want to do this auto hfill if the par + // is empty. + if (!pit->empty()) + ++nlh; + + if (nlh && !pit->getLabelWidthString().empty()) { + fill_label_hfill = labelFill(*rit) / nlh; } } // are there any hfills in the row? - float const nh = numberOfHfills(bview->buffer(), row); + float const nh = numberOfHfills(*this, rit); if (nh) { if (w > 0) @@ -2147,25 +1896,26 @@ void LyXText::prepareToPrint(BufferView * bview, // we don't have to look at the alignment if it is ALIGN_LEFT and // if the row is already larger then the permitted width as then // we force the LEFT_ALIGN'edness! - } else if (static_cast(row->width()) < workWidth(bview)) { + } else if (static_cast(rit->width()) < workWidth()) { // is it block, flushleft or flushright? // set x how you need it int align; - if (row->par()->params().align() == LYX_ALIGN_LAYOUT) { + if (pit->params().align() == LYX_ALIGN_LAYOUT) { align = layout->align; } else { - align = row->par()->params().align(); + align = pit->params().align(); } // center displayed insets - Inset * inset; - if (row->par()->isInset(row->pos()) - && (inset=row->par()->getInset(row->pos())) + Inset * inset = 0; + if (rit->pos() < pit->size() + && pit->isInset(rit->pos()) + && (inset = pit->getInset(rit->pos())) && (inset->display())) // || (inset->scroll() < 0))) align = (inset->lyxCode() == Inset::MATHMACRO_CODE) ? LYX_ALIGN_BLOCK : LYX_ALIGN_CENTER; // ERT insets should always be LEFT ALIGNED on screen - inset = row->par()->inInset(); + inset = pit->inInset(); if (inset && inset->owner() && inset->owner()->lyxCode() == Inset::ERT_CODE) { @@ -2174,19 +1924,24 @@ void LyXText::prepareToPrint(BufferView * bview, switch (align) { case LYX_ALIGN_BLOCK: - ns = numberOfSeparators(bview->buffer(), row); - if (ns && row->next() && row->next()->par() == row->par() && - !(row->next()->par()->isNewline(row->next()->pos() - 1)) - && !(row->next()->par()->isInset(row->next()->pos()) - && row->next()->par()->getInset(row->next()->pos()) - && row->next()->par()->getInset(row->next()->pos())->display()) - ) - { + { + float const ns = numberOfSeparators(*this, rit); + RowList::iterator next_row = boost::next(rit); + ParagraphList::iterator next_pit = next_row->par(); + + if (ns && next_row != rowlist_.end() && + next_pit == pit && + !(next_pit->isNewline(next_row->pos() - 1)) + && !(next_pit->isInset(next_row->pos()) && + next_pit->getInset(next_row->pos()) && + next_pit->getInset(next_row->pos())->display()) + ) { fill_separator = w / ns; } else if (is_rtl) { x += w; } break; + } case LYX_ALIGN_RIGHT: x += w; break; @@ -2198,18 +1953,18 @@ void LyXText::prepareToPrint(BufferView * bview, if (!bidi) return; - computeBidiTables(bview->buffer(), row); + computeBidiTables(bv()->buffer(), rit); if (is_rtl) { - pos_type main_body = - beginningOfMainBody(bview->buffer(), row->par()); - pos_type last = rowLast(row); + pos_type body_pos = pit->beginningOfBody(); + pos_type last = lastPos(*this, rit); - if (main_body > 0 && - (main_body - 1 > last || - !row->par()->isLineSeparator(main_body - 1))) { + if (body_pos > 0 && + (body_pos - 1 > last || + !pit->isLineSeparator(body_pos - 1))) { x += font_metrics::width(layout->labelsep, - getLabelFont(bview->buffer(), row->par())); - if (main_body - 1 <= last) + getLabelFont(bv()->buffer(), + pit)); + if (body_pos - 1 <= last) x += fill_label_hfill; } } @@ -2223,176 +1978,95 @@ void LyXText::prepareToPrint(BufferView * bview, // realize, that you left an empty paragraph, they will delete it. // They also delete the corresponding row -void LyXText::cursorRightOneWord(BufferView * bview) const +void LyXText::cursorRightOneWord() { - // treat floats, HFills and Insets as words - LyXCursor tmpcursor = cursor; - // CHECK See comment on top of text.C - - if (tmpcursor.pos() == tmpcursor.par()->size() - && tmpcursor.par()->next()) { - tmpcursor.par(tmpcursor.par()->next()); - tmpcursor.pos(0); - } else { - int steps = 0; - - // Skip through initial nonword stuff. - while (tmpcursor.pos() < tmpcursor.par()->size() && - ! tmpcursor.par()->isWord(tmpcursor.pos())) { - // printf("Current pos1 %d", tmpcursor.pos()) ; - tmpcursor.pos(tmpcursor.pos() + 1); - ++steps; - } - // Advance through word. - while (tmpcursor.pos() < tmpcursor.par()->size() && - tmpcursor.par()->isWord(tmpcursor.pos())) { - // printf("Current pos2 %d", tmpcursor.pos()) ; - tmpcursor.pos(tmpcursor.pos() + 1); - ++steps; - } - } - setCursor(bview, tmpcursor.par(), tmpcursor.pos()); + ::cursorRightOneWord(cursor, ownerParagraphs()); + setCursor(cursor.par(), cursor.pos()); } -void LyXText::cursorTab(BufferView * bview) const +// Skip initial whitespace at end of word and move cursor to *start* +// of prior word, not to end of next prior word. +void LyXText::cursorLeftOneWord() { LyXCursor tmpcursor = cursor; - while (tmpcursor.pos() < tmpcursor.par()->size() - && !tmpcursor.par()->isNewline(tmpcursor.pos())) - tmpcursor.pos(tmpcursor.pos() + 1); - - if (tmpcursor.pos() == tmpcursor.par()->size()) { - if (tmpcursor.par()->next()) { - tmpcursor.par(tmpcursor.par()->next()); - tmpcursor.pos(0); - } - } else - tmpcursor.pos(tmpcursor.pos() + 1); - setCursor(bview, tmpcursor.par(), tmpcursor.pos()); + ::cursorLeftOneWord(tmpcursor, ownerParagraphs()); + setCursor(tmpcursor.par(), tmpcursor.pos()); } -// Skip initial whitespace at end of word and move cursor to *start* -// of prior word, not to end of next prior word. -void LyXText::cursorLeftOneWord(BufferView * bview) const +void LyXText::selectWord(word_location loc) { - LyXCursor tmpcursor = cursor; - cursorLeftOneWord(tmpcursor); - setCursor(bview, tmpcursor.par(), tmpcursor.pos()); + LyXCursor from = cursor; + LyXCursor to; + ::getWord(from, to, loc, ownerParagraphs()); + if (cursor != from) + setCursor(from.par(), from.pos()); + if (to == from) + return; + selection.cursor = cursor; + setCursor(to.par(), to.pos()); + setSelection(); } -void LyXText::cursorLeftOneWord(LyXCursor & cur) const +// Select the word currently under the cursor when no +// selection is currently set +bool LyXText::selectWordWhenUnderCursor(word_location loc) { - // treat HFills, floats and Insets as words - cur = cursor; - while (cur.pos() - && (cur.par()->isSeparator(cur.pos() - 1) - || cur.par()->isKomma(cur.pos() - 1)) - && !(cur.par()->isHfill(cur.pos() - 1) - || cur.par()->isInset(cur.pos() - 1))) - cur.pos(cur.pos() - 1); - - if (cur.pos() - && (cur.par()->isInset(cur.pos() - 1) - || cur.par()->isHfill(cur.pos() - 1))) { - cur.pos(cur.pos() - 1); - } else if (!cur.pos()) { - if (cur.par()->previous()) { - cur.par(cur.par()->previous()); - cur.pos(cur.par()->size()); - } - } else { // Here, cur != 0 - while (cur.pos() > 0 && - cur.par()->isWord(cur.pos() - 1)) - cur.pos(cur.pos() - 1); + if (!selection.set()) { + selectWord(loc); + return selection.set(); } + return false; } -// Select current word. This depends on behaviour of -// CursorLeftOneWord(), so it is patched as well. -void LyXText::getWord(LyXCursor & from, LyXCursor & to, - word_location const loc) const +void LyXText::acceptChange() { - // first put the cursor where we wana start to select the word - from = cursor; - switch (loc) { - case WHOLE_WORD_STRICT: - if (cursor.pos() == 0 || cursor.pos() == cursor.par()->size() - || cursor.par()->isSeparator(cursor.pos()) - || cursor.par()->isKomma(cursor.pos()) - || cursor.par()->isSeparator(cursor.pos() - 1) - || cursor.par()->isKomma(cursor.pos() - 1)) { - to = from; - return; - } - // no break here, we go to the next + if (!selection.set() && cursor.par()->size()) + return; - case WHOLE_WORD: - // Move cursor to the beginning, when not already there. - if (from.pos() && !from.par()->isSeparator(from.pos() - 1) - && !from.par()->isKomma(from.pos() - 1)) - cursorLeftOneWord(from); - break; - case PREVIOUS_WORD: - // always move the cursor to the beginning of previous word - cursorLeftOneWord(from); - break; - case NEXT_WORD: - lyxerr << "LyXText::getWord: NEXT_WORD not implemented yet\n"; - break; - case PARTIAL_WORD: - break; - } - to = from; - while (to.pos() < to.par()->size() - && !to.par()->isSeparator(to.pos()) - && !to.par()->isKomma(to.pos()) - && !to.par()->isHfill(to.pos()) - && !to.par()->isInset(to.pos())) - { - to.pos(to.pos() + 1); + if (selection.start.par() == selection.end.par()) { + LyXCursor & startc = selection.start; + LyXCursor & endc = selection.end; + setUndo(bv(), Undo::INSERT, startc.par()); + startc.par()->acceptChange(startc.pos(), endc.pos()); + finishUndo(); + clearSelection(); + redoParagraphs(startc, boost::next(startc.par())); + setCursorIntern(startc.par(), 0); } +#warning handle multi par selection } -void LyXText::selectWord(BufferView * bview, word_location const loc) +void LyXText::rejectChange() { - LyXCursor from; - LyXCursor to; - getWord(from, to, loc); - if (cursor != from) - setCursor(bview, from.par(), from.pos()); - if (to == from) + if (!selection.set() && cursor.par()->size()) return; - selection.cursor = cursor; - setCursor(bview, to.par(), to.pos()); - setSelection(bview); -} - -// Select the word currently under the cursor when no -// selection is currently set -bool LyXText::selectWordWhenUnderCursor(BufferView * bview, - word_location const loc) -{ - if (!selection.set()) { - selectWord(bview, loc); - return selection.set(); + if (selection.start.par() == selection.end.par()) { + LyXCursor & startc = selection.start; + LyXCursor & endc = selection.end; + setUndo(bv(), Undo::INSERT, startc.par()); + startc.par()->rejectChange(startc.pos(), endc.pos()); + finishUndo(); + clearSelection(); + redoParagraphs(startc, boost::next(startc.par())); + setCursorIntern(startc.par(), 0); } - return false; +#warning handle multi par selection } // This function is only used by the spellchecker for NextWord(). // It doesn't handle LYX_ACCENTs and probably never will. WordLangTuple const -LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const +LyXText::selectNextWordToSpellcheck(float & value) { if (the_locking_inset) { - WordLangTuple word = the_locking_inset->selectNextWordToSpellcheck(bview, value); + WordLangTuple word = the_locking_inset->selectNextWordToSpellcheck(bv(), value); if (!word.word().empty()) { value += float(cursor.y()); value /= float(height); @@ -2400,53 +2074,62 @@ LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const } // we have to go on checking so move cursor to the next char if (cursor.pos() == cursor.par()->size()) { - if (!cursor.par()->next()) + if (boost::next(cursor.par()) == ownerParagraphs().end()) return word; - cursor.par(cursor.par()->next()); + cursor.par(boost::next(cursor.par())); cursor.pos(0); } else cursor.pos(cursor.pos() + 1); } - Paragraph * tmppar = cursor.par(); + ParagraphList::iterator tmppit = cursor.par(); // If this is not the very first word, skip rest of // current word because we are probably in the middle // of a word if there is text here. - if (cursor.pos() || cursor.par()->previous()) { + if (cursor.pos() || cursor.par() != ownerParagraphs().begin()) { while (cursor.pos() < cursor.par()->size() && cursor.par()->isLetter(cursor.pos())) cursor.pos(cursor.pos() + 1); } // Now, skip until we have real text (will jump paragraphs) - while ((cursor.par()->size() > cursor.pos() - && (!cursor.par()->isLetter(cursor.pos())) - && (!cursor.par()->isInset(cursor.pos()) || - !cursor.par()->getInset(cursor.pos())->allowSpellcheck())) - || (cursor.par()->size() == cursor.pos() - && cursor.par()->next())) - { - if (cursor.pos() == cursor.par()->size()) { - cursor.par(cursor.par()->next()); - cursor.pos(0); - } else - cursor.pos(cursor.pos() + 1); + while (true) { + ParagraphList::iterator cpit = cursor.par(); + pos_type const cpos(cursor.pos()); + + if (cpos == cpit->size()) { + if (boost::next(cpit) != ownerParagraphs().end()) { + cursor.par(boost::next(cpit)); + cursor.pos(0); + continue; + } + break; + } + + bool const is_good_inset = cpit->isInset(cpos) + && cpit->getInset(cpos)->allowSpellcheck(); + + if (!isDeletedText(*cpit, cpos) + && (is_good_inset || cpit->isLetter(cpos))) + break; + + cursor.pos(cpos + 1); } // now check if we hit an inset so it has to be a inset containing text! if (cursor.pos() < cursor.par()->size() && - cursor.par()->isInset(cursor.pos())) - { + cursor.par()->isInset(cursor.pos())) { // lock the inset! - cursor.par()->getInset(cursor.pos())->edit(bview); + FuncRequest cmd(bv(), LFUN_INSET_EDIT, "left"); + cursor.par()->getInset(cursor.pos())->localDispatch(cmd); // now call us again to do the above trick // but obviously we have to start from down below ;) - return bview->text->selectNextWordToSpellcheck(bview, value); + return bv()->text->selectNextWordToSpellcheck(value); } // Update the value if we changed paragraphs - if (cursor.par() != tmppar) { - setCursor(bview, cursor.par(), cursor.pos()); + if (cursor.par() != tmppit) { + setCursor(cursor.par(), cursor.pos()); value = float(cursor.y())/float(height); } @@ -2454,12 +2137,13 @@ LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const selection.cursor = cursor; string lang_code( - getFont(bview->buffer(), cursor.par(), cursor.pos()) + getFont(bv()->buffer(), cursor.par(), cursor.pos()) .language()->code()); // and find the end of the word (insets like optional hyphens // and ligature break are part of a word) while (cursor.pos() < cursor.par()->size() - && (cursor.par()->isLetter(cursor.pos()))) + && cursor.par()->isLetter(cursor.pos()) + && !isDeletedText(*cursor.par(), cursor.pos())) cursor.pos(cursor.pos() + 1); // Finally, we copy the word to a string and return it @@ -2476,14 +2160,14 @@ LyXText::selectNextWordToSpellcheck(BufferView * bview, float & value) const // This one is also only for the spellchecker -void LyXText::selectSelectedWord(BufferView * bview) +void LyXText::selectSelectedWord() { if (the_locking_inset) { - the_locking_inset->selectSelectedWord(bview); + the_locking_inset->selectSelectedWord(bv()); return; } // move cursor to the beginning - setCursor(bview, selection.cursor.par(), selection.cursor.pos()); + setCursor(selection.cursor.par(), selection.cursor.pos()); // set the sel cursor selection.cursor = cursor; @@ -2493,88 +2177,78 @@ void LyXText::selectSelectedWord(BufferView * bview) && (cursor.par()->isLetter(cursor.pos()))) cursor.pos(cursor.pos() + 1); - setCursor(bview, cursor.par(), cursor.pos()); + setCursor(cursor.par(), cursor.pos()); // finally set the selection - setSelection(bview); + setSelection(); } // Delete from cursor up to the end of the current or next word. -void LyXText::deleteWordForward(BufferView * bview) +void LyXText::deleteWordForward() { if (cursor.par()->empty()) - cursorRight(bview); + cursorRight(bv()); else { LyXCursor tmpcursor = cursor; - tmpcursor.row(0); // ?? selection.set(true); // to avoid deletion - cursorRightOneWord(bview); - setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos()); + cursorRightOneWord(); + setCursor(tmpcursor, tmpcursor.par(), tmpcursor.pos()); selection.cursor = cursor; cursor = tmpcursor; - setSelection(bview); + setSelection(); // Great, CutSelection() gets rid of multiple spaces. - cutSelection(bview, true, false); + cutSelection(true, false); } } // Delete from cursor to start of current or prior word. -void LyXText::deleteWordBackward(BufferView * bview) +void LyXText::deleteWordBackward() { if (cursor.par()->empty()) - cursorLeft(bview); + cursorLeft(bv()); else { LyXCursor tmpcursor = cursor; - tmpcursor.row(0); // ?? selection.set(true); // to avoid deletion - cursorLeftOneWord(bview); - setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos()); + cursorLeftOneWord(); + setCursor(tmpcursor, tmpcursor.par(), tmpcursor.pos()); selection.cursor = cursor; cursor = tmpcursor; - setSelection(bview); - cutSelection(bview, true, false); + setSelection(); + cutSelection(true, false); } } // Kill to end of line. -void LyXText::deleteLineForward(BufferView * bview) +void LyXText::deleteLineForward() { if (cursor.par()->empty()) // Paragraph is empty, so we just go to the right - cursorRight(bview); + cursorRight(bv()); else { LyXCursor tmpcursor = cursor; // We can't store the row over a regular setCursor // so we set it to 0 and reset it afterwards. - tmpcursor.row(0); // ?? selection.set(true); // to avoid deletion - cursorEnd(bview); - setCursor(bview, tmpcursor, tmpcursor.par(), tmpcursor.pos()); + cursorEnd(); + setCursor(tmpcursor, tmpcursor.par(), tmpcursor.pos()); selection.cursor = cursor; cursor = tmpcursor; - setSelection(bview); + setSelection(); // What is this test for ??? (JMarc) if (!selection.set()) { - deleteWordForward(bview); + deleteWordForward(); } else { - cutSelection(bview, true, false); + cutSelection(true, false); } } } -// Change the case of a word at cursor position. -// This function directly manipulates Paragraph::text because there -// is no Paragraph::SetChar currently. I did what I could to ensure -// that it is correct. I guess part of it should be moved to -// Paragraph, but it will have to change for 1.1 anyway. At least -// it does not access outside of the allocated array as the older -// version did. (JMarc) -void LyXText::changeCase(BufferView * bview, LyXText::TextCase action) +void LyXText::changeCase(LyXText::TextCase action) { LyXCursor from; LyXCursor to; @@ -2583,34 +2257,25 @@ void LyXText::changeCase(BufferView * bview, LyXText::TextCase action) from = selection.start; to = selection.end; } else { - getWord(from, to, PARTIAL_WORD); - setCursor(bview, to.par(), to.pos() + 1); + from = cursor; + ::getWord(from, to, lyx::PARTIAL_WORD, ownerParagraphs()); + setCursor(to.par(), to.pos() + 1); } - changeRegionCase(bview, from, to, action); -} - - -void LyXText::changeRegionCase(BufferView * bview, - LyXCursor const & from, - LyXCursor const & to, - LyXText::TextCase action) -{ - lyx::Assert(from <= to); - - setUndo(bview, Undo::FINISH, from.par(), to.par()->next()); + setUndo(bv(), Undo::FINISH, from.par(), to.par()); pos_type pos = from.pos(); - Paragraph * par = from.par(); + ParagraphList::iterator pit = from.par(); - while (par && (pos != to.pos() || par != to.par())) { - if (pos == par->size()) { - par = par->next(); + while (pit != ownerParagraphs().end() && + (pos != to.pos() || pit != to.par())) { + if (pos == pit->size()) { + ++pit; pos = 0; continue; } - unsigned char c = par->getChar(pos); - if (!IsInsetChar(c) && !IsHfillChar(c)) { + unsigned char c = pit->getChar(pos); + if (!IsInsetChar(c)) { switch (action) { case text_lowercase: c = lowercase(c); @@ -2624,60 +2289,30 @@ void LyXText::changeRegionCase(BufferView * bview, break; } } - par->setChar(pos, c); - checkParagraph(bview, par, pos); +#warning changes + pit->setChar(pos, c); + checkParagraph(pit, pos); ++pos; } - if (to.row() != from.row()) { - refresh_y = from.y() - from.row()->baseline(); - refresh_row = from.row(); - status(bview, LyXText::NEED_MORE_REFRESH); - } -} - - -void LyXText::transposeChars(BufferView & bview) -{ - Paragraph * tmppar = cursor.par(); - - setUndo(&bview, Undo::FINISH, tmppar, tmppar->next()); - - pos_type tmppos = cursor.pos(); - - // First decide if it is possible to transpose at all - - // We are at the beginning of a paragraph. - if (tmppos == 0) return; - // We are at the end of a paragraph. - if (tmppos == tmppar->size() - 1) return; - - unsigned char c1 = tmppar->getChar(tmppos); - unsigned char c2 = tmppar->getChar(tmppos - 1); - - if (c1 != Paragraph::META_INSET - && c2 != Paragraph::META_INSET) { - tmppar->setChar(tmppos, c2); - tmppar->setChar(tmppos - 1, c1); - } - // We should have an implementation that handles insets - // as well, but that will have to come later. (Lgb) - checkParagraph(const_cast(&bview), tmppar, tmppos); + if (getRow(to) != getRow(from)) + postPaint(from.y() - getRow(from)->baseline()); } -void LyXText::Delete(BufferView * bview) +void LyXText::Delete() { // this is a very easy implementation LyXCursor old_cursor = cursor; int const old_cur_par_id = old_cursor.par()->id(); - int const old_cur_par_prev_id = old_cursor.par()->previous() ? - old_cursor.par()->previous()->id() : 0; + int const old_cur_par_prev_id = + (old_cursor.par() != ownerParagraphs().begin() ? + boost::prior(old_cursor.par())->id() : -1); // just move to the right - cursorRight(bview); + cursorRight(bv()); // CHECK Look at the comment here. // This check is not very good... @@ -2685,7 +2320,7 @@ void LyXText::Delete(BufferView * bview) // and that can very well delete the par or par->previous in // old_cursor. Will a solution where we compare paragraph id's //work better? - if ((cursor.par()->previous() ? cursor.par()->previous()->id() : 0) + if ((cursor.par() != ownerParagraphs().begin() ? boost::prior(cursor.par())->id() : -1) == old_cur_par_prev_id && cursor.par()->id() != old_cur_par_id) { // delete-empty-paragraph-mechanism has done it @@ -2697,26 +2332,29 @@ void LyXText::Delete(BufferView * bview) LyXCursor tmpcursor = cursor; // to make sure undo gets the right cursor position cursor = old_cursor; - setUndo(bview, Undo::DELETE, - cursor.par(), cursor.par()->next()); + setUndo(bv(), Undo::DELETE, cursor.par()); cursor = tmpcursor; - backspace(bview); + backspace(); } } -void LyXText::backspace(BufferView * bview) +void LyXText::backspace() { // Get the font that is used to calculate the baselineskip pos_type lastpos = cursor.par()->size(); LyXFont rawparfont = - cursor.par()->getFontSettings(bview->buffer()->params, + cursor.par()->getFontSettings(bv()->buffer()->params, lastpos - 1); if (cursor.pos() == 0) { // The cursor is at the beginning of a paragraph, // so the the backspace will collapse two paragraphs into one. + // but it's not allowed unless it's new + if (cursor.par()->isChangeEdited(0, cursor.par()->size())) + return; + // we may paste some paragraphs // is it an empty paragraph? @@ -2727,47 +2365,46 @@ void LyXText::backspace(BufferView * bview) // left and let the DeleteEmptyParagraphMechanism handle the actual deletion // of the paragraph. - if (cursor.par()->previous()) { - Paragraph * tmppar = cursor.par()->previous(); - if (cursor.par()->layout() == tmppar->layout() - && cursor.par()->getAlign() == tmppar->getAlign()) { + if (cursor.par() != ownerParagraphs().begin()) { + ParagraphList::iterator tmppit = boost::prior(cursor.par()); + if (cursor.par()->layout() == tmppit->layout() + && cursor.par()->getAlign() == tmppit->getAlign()) { // Inherit bottom DTD from the paragraph below. // (the one we are deleting) - tmppar->params().lineBottom(cursor.par()->params().lineBottom()); - tmppar->params().spaceBottom(cursor.par()->params().spaceBottom()); - tmppar->params().pagebreakBottom(cursor.par()->params().pagebreakBottom()); + tmppit->params().lineBottom(cursor.par()->params().lineBottom()); + tmppit->params().spaceBottom(cursor.par()->params().spaceBottom()); + tmppit->params().pagebreakBottom(cursor.par()->params().pagebreakBottom()); } - cursorLeft(bview); + cursorLeft(bv()); // the layout things can change the height of a row ! - int const tmpheight = cursor.row()->height(); - setHeightOfRow(bview, cursor.row()); - if (cursor.row()->height() != tmpheight) { - refresh_y = cursor.y() - cursor.row()->baseline(); - refresh_row = cursor.row(); - status(bview, LyXText::NEED_MORE_REFRESH); + int const tmpheight = cursorRow()->height(); + setHeightOfRow(cursorRow()); + if (cursorRow()->height() != tmpheight) { + postPaint(cursor.y() - cursorRow()->baseline()); } return; } } - if (cursor.par()->previous()) { - setUndo(bview, Undo::DELETE, - cursor.par()->previous(), cursor.par()->next()); + if (cursor.par() != ownerParagraphs().begin()) { + setUndo(bv(), Undo::DELETE, + boost::prior(cursor.par()), + cursor.par()); } - Paragraph * tmppar = cursor.par(); - Row * tmprow = cursor.row(); + ParagraphList::iterator tmppit = cursor.par(); + RowList::iterator tmprow = cursorRow(); // We used to do cursorLeftIntern() here, but it is // not a good idea since it triggers the auto-delete // mechanism. So we do a cursorLeftIntern()-lite, // without the dreaded mechanism. (JMarc) - if (cursor.par()->previous()) { + if (cursor.par() != ownerParagraphs().begin()) { // steps into the above paragraph. - setCursorIntern(bview, cursor.par()->previous(), - cursor.par()->previous()->size(), + setCursorIntern(boost::prior(cursor.par()), + boost::prior(cursor.par())->size(), false); } @@ -2781,15 +2418,15 @@ void LyXText::backspace(BufferView * bview) // Correction: Pasting is always allowed with standard-layout LyXTextClass const & tclass = - bview->buffer()->params.getLyXTextClass(); + bv()->buffer()->params.getLyXTextClass(); - if (cursor.par() != tmppar - && (cursor.par()->layout() == tmppar->layout() - || tmppar->layout() == tclass.defaultLayout()) - && cursor.par()->getAlign() == tmppar->getAlign()) { + if (cursor.par() != tmppit + && (cursor.par()->layout() == tmppit->layout() + || tmppit->layout() == tclass.defaultLayout()) + && cursor.par()->getAlign() == tmppit->getAlign()) { removeParagraph(tmprow); removeRow(tmprow); - mergeParagraph(bview->buffer()->params, cursor.par()); + mergeParagraph(bv()->buffer()->params, bv()->buffer()->paragraphs, cursor.par()); if (!cursor.pos() || !cursor.par()->isSeparator(cursor.pos() - 1)) ; //cursor.par()->insertChar(cursor.pos(), ' '); @@ -2800,9 +2437,7 @@ void LyXText::backspace(BufferView * bview) if (cursor.pos()) cursor.pos(cursor.pos() - 1); - status(bview, LyXText::NEED_MORE_REFRESH); - refresh_row = cursor.row(); - refresh_y = cursor.y() - cursor.row()->baseline(); + postPaint(cursor.y() - cursorRow()->baseline()); // remove the lost paragraph // This one is not safe, since the paragraph that the tmprow and the @@ -2813,46 +2448,42 @@ void LyXText::backspace(BufferView * bview) //RemoveRow(tmprow); // This rebuilds the rows. - appendParagraph(bview, cursor.row()); - updateCounters(bview); + appendParagraph(cursorRow()); + updateCounters(); // the row may have changed, block, hfills etc. - setCursor(bview, cursor.par(), cursor.pos(), false); + setCursor(cursor.par(), cursor.pos(), false); } } else { // this is the code for a normal backspace, not pasting // any paragraphs - setUndo(bview, Undo::DELETE, - cursor.par(), cursor.par()->next()); + setUndo(bv(), Undo::DELETE, cursor.par()); // We used to do cursorLeftIntern() here, but it is // not a good idea since it triggers the auto-delete // mechanism. So we do a cursorLeftIntern()-lite, // without the dreaded mechanism. (JMarc) - setCursorIntern(bview, cursor.par(), cursor.pos()- 1, + setCursorIntern(cursor.par(), cursor.pos()- 1, false, cursor.boundary()); - // some insets are undeletable here if (cursor.par()->isInset(cursor.pos())) { - if (!cursor.par()->getInset(cursor.pos())->deletable()) - return; // force complete redo when erasing display insets // this is a cruel method but safe..... Matthias if (cursor.par()->getInset(cursor.pos())->display() || cursor.par()->getInset(cursor.pos())->needFullRow()) { cursor.par()->erase(cursor.pos()); - redoParagraph(bview); + redoParagraph(); return; } } - Row * row = cursor.row(); + RowList::iterator row = cursorRow(); int y = cursor.y() - row->baseline(); pos_type z; // remember that a space at the end of a row doesnt count // when calculating the fill - if (cursor.pos() < rowLast(row) || + if (cursor.pos() < lastPos(*this, row) || !cursor.par()->isLineSeparator(cursor.pos())) { - row->fill(row->fill() + singleWidth(bview, + row->fill(row->fill() + singleWidth( cursor.par(), cursor.pos())); } @@ -2862,9 +2493,10 @@ void LyXText::backspace(BufferView * bview) if (cursor.pos() && cursor.par()->isNewline(cursor.pos())) { cursor.par()->erase(cursor.pos()); // refresh the positions - Row * tmprow = row; - while (tmprow->next() && tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); + RowList::iterator tmprow = row; + while (boost::next(tmprow) != rows().end() && + boost::next(tmprow)->par() == row->par()) { + ++tmprow; tmprow->pos(tmprow->pos() - 1); } if (cursor.par()->isLineSeparator(cursor.pos() - 1)) @@ -2873,12 +2505,13 @@ void LyXText::backspace(BufferView * bview) if (cursor.pos() < cursor.par()->size() && !cursor.par()->isSeparator(cursor.pos())) { cursor.par()->insertChar(cursor.pos(), ' '); - setCharFont(bview->buffer(), cursor.par(), + setCharFont(bv()->buffer(), cursor.par(), cursor.pos(), current_font); // refresh the positions tmprow = row; - while (tmprow->next() && tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); + while (boost::next(tmprow) != rows().end() && + boost::next(tmprow)->par() == row->par()) { + ++tmprow; tmprow->pos(tmprow->pos() + 1); } } @@ -2886,1057 +2519,248 @@ void LyXText::backspace(BufferView * bview) cursor.par()->erase(cursor.pos()); // refresh the positions - Row * tmprow = row; - while (tmprow->next() - && tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); + RowList::iterator tmprow = row; + while (boost::next(tmprow) != rows().end() && + boost::next(tmprow)->par() == row->par()) { + ++tmprow; tmprow->pos(tmprow->pos() - 1); } // delete newlines at the beginning of paragraphs while (!cursor.par()->empty() && + cursor.pos() < cursor.par()->size() && cursor.par()->isNewline(cursor.pos()) && - cursor.pos() == beginningOfMainBody(bview->buffer(), - cursor.par())) { + cursor.pos() == cursor.par()->beginningOfBody()) { cursor.par()->erase(cursor.pos()); // refresh the positions tmprow = row; - while (tmprow->next() && - tmprow->next()->par() == row->par()) { - tmprow = tmprow->next(); + while (boost::next(tmprow) != rows().end() && + boost::next(tmprow)->par() == row->par()) { + ++tmprow; tmprow->pos(tmprow->pos() - 1); } } } // is there a break one row above - if (row->previous() && row->previous()->par() == row->par()) { - z = nextBreakPoint(bview, row->previous(), - workWidth(bview)); + if (row != rows().begin() && boost::prior(row)->par() == row->par()) { + z = rowBreakPoint(*boost::prior(row)); if (z >= row->pos()) { row->pos(z + 1); - Row * tmprow = row->previous(); + RowList::iterator tmprow = boost::prior(row); // maybe the current row is now empty if (row->pos() >= row->par()->size()) { // remove it removeRow(row); - need_break_row = 0; + need_break_row = rows().end(); } else { - breakAgainOneRow(bview, row); - if (row->next() && row->next()->par() == row->par()) - need_break_row = row->next(); + breakAgainOneRow(row); + if (boost::next(row) != rows().end() && + boost::next(row)->par() == row->par()) + need_break_row = boost::next(row); else - need_break_row = 0; + need_break_row = rows().end(); } // set the dimensions of the row above y -= tmprow->height(); - tmprow->fill(fill(bview, tmprow, - workWidth(bview))); - setHeightOfRow(bview, tmprow); - - refresh_y = y; - refresh_row = tmprow; - status(bview, LyXText::NEED_MORE_REFRESH); - setCursor(bview, cursor.par(), cursor.pos(), + tmprow->fill(fill(tmprow, workWidth())); + setHeightOfRow(tmprow); + + postPaint(y); + + setCursor(cursor.par(), cursor.pos(), false, cursor.boundary()); //current_font = rawtmpfont; //real_current_font = realtmpfont; // check, whether the last character's font has changed. if (rawparfont != - cursor.par()->getFontSettings(bview->buffer()->params, + cursor.par()->getFontSettings(bv()->buffer()->params, cursor.par()->size() - 1)) - redoHeightOfParagraph(bview, cursor); + redoHeightOfParagraph(); return; } } // break the cursor row again - if (row->next() && row->next()->par() == row->par() && - (rowLast(row) == row->par()->size() - 1 || - nextBreakPoint(bview, row, workWidth(bview)) != rowLast(row))) { + if (boost::next(row) != rows().end() && + boost::next(row)->par() == row->par() && + (lastPos(*this, row) == row->par()->size() - 1 || + rowBreakPoint(*row) != lastPos(*this, row))) { // it can happen that a paragraph loses one row // without a real breakup. This is when a word // is to long to be broken. Well, I don t care this // hack ;-) - if (rowLast(row) == row->par()->size() - 1) - removeRow(row->next()); + if (lastPos(*this, row) == row->par()->size() - 1) + removeRow(boost::next(row)); - refresh_y = y; - refresh_row = row; - status(bview, LyXText::NEED_MORE_REFRESH); + postPaint(y); - breakAgainOneRow(bview, row); + breakAgainOneRow(row); // will the cursor be in another row now? - if (row->next() && row->next()->par() == row->par() && - rowLast(row) <= cursor.pos()) { - row = row->next(); - breakAgainOneRow(bview, row); + if (boost::next(row) != rows().end() && + boost::next(row)->par() == row->par() && + lastPos(*this, row) <= cursor.pos()) { + ++row; + breakAgainOneRow(row); } - setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary()); + setCursor(cursor.par(), cursor.pos(), false, cursor.boundary()); - if (row->next() && row->next()->par() == row->par()) - need_break_row = row->next(); + if (boost::next(row) != rows().end() && + boost::next(row)->par() == row->par()) + need_break_row = boost::next(row); else - need_break_row = 0; + need_break_row = rows().end(); } else { // set the dimensions of the row - row->fill(fill(bview, row, workWidth(bview))); + row->fill(fill(row, workWidth())); int const tmpheight = row->height(); - setHeightOfRow(bview, row); - if (tmpheight == row->height()) - status(bview, LyXText::NEED_VERY_LITTLE_REFRESH); - else - status(bview, LyXText::NEED_MORE_REFRESH); - refresh_y = y; - refresh_row = row; - setCursor(bview, cursor.par(), cursor.pos(), false, cursor.boundary()); + setHeightOfRow(row); + if (tmpheight == row->height()) { + postRowPaint(row, y); + } else { + postPaint(y); + } + setCursor(cursor.par(), cursor.pos(), false, cursor.boundary()); } } // current_font = rawtmpfont; // real_current_font = realtmpfont; - if (isBoundary(bview->buffer(), cursor.par(), cursor.pos()) + if (isBoundary(bv()->buffer(), *cursor.par(), cursor.pos()) != cursor.boundary()) - setCursor(bview, cursor.par(), cursor.pos(), false, + setCursor(cursor.par(), cursor.pos(), false, !cursor.boundary()); lastpos = cursor.par()->size(); if (cursor.pos() == lastpos) - setCurrentFont(bview); + setCurrentFont(); // check, whether the last characters font has changed. if (rawparfont != - cursor.par()->getFontSettings(bview->buffer()->params, lastpos - 1)) { - redoHeightOfParagraph(bview, cursor); + cursor.par()->getFontSettings(bv()->buffer()->params, lastpos - 1)) { + redoHeightOfParagraph(); } else { // now the special right address boxes if (cursor.par()->layout()->margintype == MARGIN_RIGHT_ADDRESS_BOX) { - redoDrawingOfParagraph(bview, cursor); - } - } -} - - -bool LyXText::paintRowBackground(DrawRowParams & p) -{ - bool clear_area = true; - Inset * inset = 0; - LyXFont font(LyXFont::ALL_SANE); - - pos_type const last = rowLastPrintable(p.row); - - if (!p.bv->screen().forceClear() && last == p.row->pos() - && p.row->par()->isInset(p.row->pos())) { - inset = p.row->par()->getInset(p.row->pos()); - if (inset) { - clear_area = inset->doClearArea(); - } - } - - if (p.cleared) { - return true; - } - - if (clear_area) { - int const x = p.xo; - int const y = p.yo < 0 ? 0 : p.yo; - int const h = p.yo < 0 ? p.row->height() + p.yo : p.row->height(); - p.pain->fillRectangle(x, y, p.width, h, backgroundColor()); - return true; - } - - if (inset == 0) - return false; - - int h = p.row->baseline() - inset->ascent(p.bv, font); - - // first clear the whole row above the inset! - if (h > 0) { - p.pain->fillRectangle(p.xo, p.yo, p.width, h, backgroundColor()); - } - - // clear the space below the inset! - h += inset->ascent(p.bv, font) + inset->descent(p.bv, font); - if ((p.row->height() - h) > 0) { - p.pain->fillRectangle(p.xo, p.yo + h, - p.width, p.row->height() - h, backgroundColor()); - } - - // clear the space behind the inset, if needed - if (!inset->display() && !inset->needFullRow()) { - int const xp = int(p.x) + inset->width(p.bv, font); - if (p.width - xp > 0) { - p.pain->fillRectangle(xp, p.yo, p.width - xp, - p.row->height(), backgroundColor()); - } - } - - return false; -} - - -void LyXText::paintRowSelection(DrawRowParams & p) -{ - bool const is_rtl = p.row->par()->isRightToLeftPar(p.bv->buffer()->params); - - // the current selection - int const startx = selection.start.x(); - int const endx = selection.end.x(); - int const starty = selection.start.y(); - int const endy = selection.end.y(); - Row const * startrow = selection.start.row(); - Row const * endrow = selection.end.row(); - - Row * row = p.row; - - if (bidi_same_direction) { - int x; - int y = p.yo; - int w; - int h = row->height(); - - if (startrow == row && endrow == row) { - if (startx < endx) { - x = p.xo + startx; - w = endx - startx; - p.pain->fillRectangle(x, y, w, h, LColor::selection); - } else { - x = p.xo + endx; - w = startx - endx; - p.pain->fillRectangle(x, y, w, h, LColor::selection); - } - } else if (startrow == row) { - int const x = (is_rtl) ? p.xo : (p.xo + startx); - int const w = (is_rtl) ? startx : (p.width - startx); - p.pain->fillRectangle(x, y, w, h, LColor::selection); - } else if (endrow == row) { - int const x = (is_rtl) ? (p.xo + endx) : p.xo; - int const w = (is_rtl) ? (p.width - endx) : endx; - p.pain->fillRectangle(x, y, w, h, LColor::selection); - } else if (p.y > starty && p.y < endy) { - p.pain->fillRectangle(p.xo, y, p.width, h, LColor::selection); - } - return; - } else if (startrow != row && endrow != row) { - if (p.y > starty && p.y < endy) { - int w = p.width; - int h = row->height(); - p.pain->fillRectangle(p.xo, p.yo, w, h, LColor::selection); - } - return; - } - - if ((startrow != row && !is_rtl) || (endrow != row && is_rtl)) - p.pain->fillRectangle(p.xo, p.yo, int(p.x), row->height(), LColor::selection); - - Buffer const * buffer = p.bv->buffer(); - Paragraph * par = row->par(); - pos_type main_body = beginningOfMainBody(buffer, par); - pos_type const last = rowLastPrintable(row); - float tmpx = p.x; - - for (pos_type vpos = row->pos(); vpos <= last; ++vpos) { - pos_type pos = vis2log(vpos); - float const old_tmpx = tmpx; - if (main_body > 0 && pos == main_body - 1) { - LyXLayout_ptr const & layout = par->layout(); - LyXFont const lfont = getLabelFont(buffer, par); - - tmpx += p.label_hfill + font_metrics::width(layout->labelsep, lfont); - - if (par->isLineSeparator(main_body - 1)) - tmpx -= singleWidth(p.bv, par, main_body - 1); - } - - if (hfillExpansion(buffer, row, pos)) { - tmpx += singleWidth(p.bv, par, pos); - if (pos >= main_body) - tmpx += p.hfill; - else - tmpx += p.label_hfill; - } - - else if (par->isSeparator(pos)) { - tmpx += singleWidth(p.bv, par, pos); - if (pos >= main_body) - tmpx += p.separator; - } else { - tmpx += singleWidth(p.bv, par, pos); - } - - if ((startrow != row || selection.start.pos() <= pos) && - (endrow != row || pos < selection.end.pos())) { - // Here we do not use p.x as p.xo was added to p.x. - p.pain->fillRectangle(int(old_tmpx), p.yo, - int(tmpx - old_tmpx + 1), - row->height(), LColor::selection); - } - } - - if ((startrow != row && is_rtl) || (endrow != row && !is_rtl)) { - p.pain->fillRectangle(p.xo + int(tmpx), - p.yo, int(p.bv->workWidth() - tmpx), - row->height(), LColor::selection); - } -} - - -void LyXText::paintRowAppendix(DrawRowParams & p) -{ - // FIXME: can be just p.width ? - int const ww = p.bv->workWidth(); - Paragraph * firstpar = p.row->par(); - - if (firstpar->params().appendix()) { - p.pain->line(1, p.yo, 1, p.yo + p.row->height(), LColor::appendixline); - p.pain->line(ww - 2, p.yo, ww - 2, p.yo + p.row->height(), LColor::appendixline); - } -} - - -void LyXText::paintRowDepthBar(DrawRowParams & p) -{ - Paragraph::depth_type const depth = p.row->par()->getDepth(); - - if (depth <= 0) - return; - - Paragraph::depth_type prev_depth = 0; - if (p.row->previous()) - prev_depth = p.row->previous()->par()->getDepth(); - Paragraph::depth_type next_depth = 0; - if (p.row->next()) - next_depth = p.row->next()->par()->getDepth(); - - for (Paragraph::depth_type i = 1; i <= depth; ++i) { - int const x = (PAPER_MARGIN / 5) * i + p.xo; - int const h = p.yo + p.row->height() - 1 - (i - next_depth - 1) * 3; - - p.pain->line(x, p.yo, x, h, LColor::depthbar); - - int const w = PAPER_MARGIN / 5; - - if (i > prev_depth) { - p.pain->fillRectangle(x, p.yo, w, 2, LColor::depthbar); - } - if (i > next_depth) { - p.pain->fillRectangle(x, h, w, 2, LColor::depthbar); + redoDrawingOfParagraph(cursor); } } } -int LyXText::getLengthMarkerHeight(BufferView * bv, VSpace const & vsp) const -{ - if (vsp.kind() == VSpace::NONE) - return 0; - - int const arrow_size = 4; - int const space_size = int(vsp.inPixels(bv)); - - LyXFont font; - font.decSize(); - int const min_size = max(3 * arrow_size, - font_metrics::maxAscent(font) - + font_metrics::maxDescent(font)); - - if (vsp.length().len().value() < 0.0) - return min_size; - else - return max(min_size, space_size); -} - - -int LyXText::drawLengthMarker(DrawRowParams & p, string const & prefix, - VSpace const & vsp, int start) +RowList::iterator LyXText::cursorRow() const { - if (vsp.kind() == VSpace::NONE) - return 0; - - int const arrow_size = 4; - int const size = getLengthMarkerHeight(p.bv, vsp); - int const end = start + size; - - // the label to display (if any) - string str; - // y-values for top arrow - int ty1, ty2; - // y-values for bottom arrow - int by1, by2; - - str = prefix + " (" + vsp.asLyXCommand() + ")"; - - switch (vsp.kind()) { - case VSpace::LENGTH: { - // adding or removing space - bool const added = !(vsp.length().len().value() < 0.0); - ty1 = added ? (start + arrow_size) : start; - ty2 = added ? start : (start + arrow_size); - by1 = added ? (end - arrow_size) : end; - by2 = added ? end : (end - arrow_size); - break; - } - default: - ty1 = ty2 = start; - by1 = by2 = end; - break; - } - - int const leftx = p.xo + leftMargin(p.bv, p.row); - int const midx = leftx + arrow_size; - int const rightx = midx + arrow_size; - - // first the string - int w = 0; - int a = 0; - int d = 0; - - LyXFont font; - font.setColor(LColor::added_space).decSize(); - font_metrics::rectText(str, font, w, a, d); - - p.pain->rectText(leftx + 2 * arrow_size + 5, - start + ((end - start) / 2) + d, - str, font); - - if (vsp.kind() != VSpace::LENGTH && vsp.kind() != VSpace::VFILL ) - return size; - - // top arrow - p.pain->line(leftx, ty1, midx, ty2, LColor::added_space); - p.pain->line(midx, ty2, rightx, ty1, LColor::added_space); - - // bottom arrow - p.pain->line(leftx, by1, midx, by2, LColor::added_space); - p.pain->line(midx, by2, rightx, by1, LColor::added_space); - - // joining line - p.pain->line(midx, ty2, midx, by2, LColor::added_space); - - return size; + return getRow(cursor.par(), cursor.pos()); } -int LyXText::paintPageBreak(string const & label, int y, DrawRowParams & p) +RowList::iterator LyXText::getRow(LyXCursor const & cur) const { - LyXFont pb_font; - pb_font.setColor(LColor::pagebreak).decSize(); - - int w = 0; - int a = 0; - int d = 0; - font_metrics::rectText(label, pb_font, w, a, d); - - int const text_start = p.xo + ((p.width - w) / 2); - int const text_end = text_start + w; - - p.pain->rectText(text_start, y + d, label, pb_font); - - p.pain->line(p.xo, y, text_start, y, - LColor::pagebreak, Painter::line_onoffdash); - p.pain->line(text_end, y, p.xo + p.width, y, - LColor::pagebreak, Painter::line_onoffdash); - - return 3 * defaultHeight(); + return getRow(cur.par(), cur.pos()); } -void LyXText::paintFirstRow(DrawRowParams & p) +RowList::iterator +LyXText::getRow(ParagraphList::iterator pit, pos_type pos) const { - Paragraph * par = p.row->par(); - ParagraphParameters const & parparams = par->params(); - - // start of appendix? - if (parparams.startOfAppendix()) { - p.pain->line(1, p.yo, p.width - 2, p.yo, LColor::appendixline); - } - - int y_top = 0; - - // think about the margins - if (!p.row->previous() && bv_owner) - y_top += PAPER_MARGIN; - - // draw a top pagebreak - if (parparams.pagebreakTop()) { - y_top += paintPageBreak(_("Page Break (top)"), - p.yo + y_top + 2 * defaultHeight(), p); - } - - // draw the additional space if needed: - y_top += drawLengthMarker(p, _("Space above"), - parparams.spaceTop(), p.yo + y_top); - - Buffer const * buffer = p.bv->buffer(); - - LyXLayout_ptr const & layout = par->layout(); + if (rows().empty()) + return rowlist_.end(); - // think about the parskip - // some parskips VERY EASY IMPLEMENTATION - if (buffer->params.paragraph_separation == BufferParams::PARSEP_SKIP) { - if (par->previous()) { - if (layout->latextype == LATEX_PARAGRAPH - && !par->getDepth()) { - y_top += buffer->params.getDefSkip().inPixels(p.bv); - } else { - LyXLayout_ptr const & playout = - par->previous()->layout(); - if (playout->latextype == LATEX_PARAGRAPH - && !par->previous()->getDepth()) { - // is it right to use defskip here, too? (AS) - y_top += buffer->params.getDefSkip().inPixels(p.bv); - } - } - } - } - - int const ww = p.bv->workWidth(); - - // draw a top line - if (parparams.lineTop()) { - LyXFont font(LyXFont::ALL_SANE); - int const asc = font_metrics::ascent('x', getFont(buffer, par, 0)); - - y_top += asc; - - int const w = (inset_owner ? inset_owner->width(p.bv, font) : ww); - int const xp = static_cast(inset_owner ? p.xo : 0); - p.pain->line(xp, p.yo + y_top, xp + w, p.yo + y_top, - LColor::topline, Painter::line_solid, - Painter::line_thick); - - y_top += asc; + // find the first row of the specified paragraph + RowList::iterator rit = rowlist_.begin(); + RowList::iterator end = rowlist_.end(); + while (boost::next(rit) != end && rit->par() != pit) { + ++rit; } - bool const is_rtl = p.row->par()->isRightToLeftPar(p.bv->buffer()->params); - - // should we print a label? - if (layout->labeltype >= LABEL_STATIC - && (layout->labeltype != LABEL_STATIC - || layout->latextype != LATEX_ENVIRONMENT - || par->isFirstInSequence())) { - - LyXFont font = getLabelFont(buffer, par); - if (!par->getLabelstring().empty()) { - float x = p.x; - string const str = par->getLabelstring(); - - // this is special code for the chapter layout. This is - // printed in an extra row and has a pagebreak at - // the top. - if (layout->labeltype == LABEL_COUNTER_CHAPTER) { - if (buffer->params.secnumdepth >= 0) { - float spacing_val = 1.0; - if (!parparams.spacing().isDefault()) { - spacing_val = parparams.spacing().getValue(); - } else { - spacing_val = buffer->params.spacing.getValue(); - } - - int const maxdesc = - int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val) - + int(layout->parsep) * defaultHeight(); - - if (is_rtl) { - x = ww - leftMargin(p.bv, p.row) - - font_metrics::width(str, font); - } - - p.pain->text(int(x), - p.yo + p.row->baseline() - - p.row->ascent_of_text() - maxdesc, - str, font); - } - } else { - if (is_rtl) { - x = ww - leftMargin(p.bv, p.row) - + font_metrics::width(layout->labelsep, font); - } else { - x = p.x - font_metrics::width(layout->labelsep, font) - - font_metrics::width(str, font); - } - - p.pain->text(int(x), p.yo + p.row->baseline(), str, font); - } - } - // the labels at the top of an environment. - // More or less for bibliography - } else if (par->isFirstInSequence() && - (layout->labeltype == LABEL_TOP_ENVIRONMENT || - layout->labeltype == LABEL_BIBLIO || - layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT)) { - LyXFont font = getLabelFont(buffer, par); - if (!par->getLabelstring().empty()) { - string const str = par->getLabelstring(); - float spacing_val = 1.0; - if (!parparams.spacing().isDefault()) { - spacing_val = parparams.spacing().getValue(); - } else { - spacing_val = buffer->params.spacing.getValue(); - } - - int maxdesc = - int(font_metrics::maxDescent(font) * layout->spacing.getValue() * spacing_val - + (layout->labelbottomsep * defaultHeight())); - - float x = p.x; - if (layout->labeltype == LABEL_CENTERED_TOP_ENVIRONMENT) { - x = ((is_rtl ? leftMargin(p.bv, p.row) : p.x) - + ww - rightMargin(buffer, p.row)) / 2; - x -= font_metrics::width(str, font) / 2; - } else if (is_rtl) { - x = ww - leftMargin(p.bv, p.row) - - font_metrics::width(str, font); - } - p.pain->text(int(x), p.yo + p.row->baseline() - - p.row->ascent_of_text() - maxdesc, - str, font); - } + // now find the wanted row + while (rit->pos() < pos + && boost::next(rit) != end + && boost::next(rit)->par() == pit + && boost::next(rit)->pos() <= pos) { + ++rit; } - if (layout->labeltype == LABEL_BIBLIO && par->bibkey) { - LyXFont font = getLayoutFont(buffer, par); - float x; - if (is_rtl) { - x = ww - leftMargin(p.bv, p.row) - + font_metrics::width(layout->labelsep, font); - } else { - x = p.x - font_metrics::width(layout->labelsep, font) - - par->bibkey->width(p.bv, font); - } - par->bibkey->draw(p.bv, font, p.yo + p.row->baseline(), x, p.cleared); - } + return rit; } - -void LyXText::paintLastRow(DrawRowParams & p) +// returns pointer to a specified row +RowList::iterator +LyXText::getRow(ParagraphList::iterator pit, pos_type pos, int & y) const { - Paragraph * par = p.row->par(); - ParagraphParameters const & parparams = par->params(); - int y_bottom = p.row->height() - 1; - - // think about the margins - if (!p.row->next() && bv_owner) - y_bottom -= PAPER_MARGIN; - - int const ww = p.bv->workWidth(); - - // draw a bottom pagebreak - if (parparams.pagebreakBottom()) { - y_bottom -= paintPageBreak(_("Page Break (bottom)"), - p.yo + y_bottom - 2 * defaultHeight(), p); - } - - // draw the additional space if needed: - int const height = getLengthMarkerHeight(p.bv, - parparams.spaceBottom()); - y_bottom -= drawLengthMarker(p, _("Space below"), - parparams.spaceBottom(), - p.yo + y_bottom - height); - - Buffer const * buffer = p.bv->buffer(); - - // draw a bottom line - if (parparams.lineBottom()) { - LyXFont font(LyXFont::ALL_SANE); - int const asc = font_metrics::ascent('x', - getFont(buffer, par, - max(pos_type(0), par->size() - 1))); - - y_bottom -= asc; - - int const w = (inset_owner ? inset_owner->width(p.bv, font) : ww); - int const xp = static_cast(inset_owner ? p.xo : 0); - int const y = p.yo + y_bottom; - p.pain->line(xp, y, xp + w, y, LColor::topline, Painter::line_solid, - Painter::line_thick); - - y_bottom -= asc; - } - - bool const is_rtl = p.row->par()->isRightToLeftPar(p.bv->buffer()->params); - int const endlabel = par->getEndLabel(); - - // draw an endlabel - switch (endlabel) { - case END_LABEL_BOX: - case END_LABEL_FILLED_BOX: - { - LyXFont const font = getLabelFont(buffer, par); - int const size = int(0.75 * font_metrics::maxAscent(font)); - int const y = (p.yo + p.row->baseline()) - size; - int x = is_rtl ? PAPER_MARGIN : ww - PAPER_MARGIN - size; + y = 0; - if (p.row->fill() <= size) - x += (size - p.row->fill() + 1) * (is_rtl ? -1 : 1); + if (rows().empty()) + return rowlist_.end(); - if (endlabel == END_LABEL_BOX) { - p.pain->rectangle(x, y, size, size, LColor::eolmarker); - } else { - p.pain->fillRectangle(x, y, size, size, - LColor::eolmarker); - } - break; - } - case END_LABEL_STATIC: - { -#if 0 - LyXFont font(LyXFont::ALL_SANE); - font = getLabelFont(buffer, par); -#else - LyXFont font = getLabelFont(buffer, par); -#endif - string const & str = par->layout()->endlabelstring(); - int const x = is_rtl ? - int(p.x) - font_metrics::width(str, font) - : ww - rightMargin(buffer, p.row) - p.row->fill(); - p.pain->text(x, p.yo + p.row->baseline(), str, font); - break; - } - case END_LABEL_NO_LABEL: - break; + // find the first row of the specified paragraph + RowList::iterator rit = rowlist_.begin(); + RowList::iterator end = rowlist_.end(); + while (boost::next(rit) != end && rit->par() != pit) { + y += rit->height(); + ++rit; } -} - - -void LyXText::paintRowText(DrawRowParams & p) -{ - Paragraph * par = p.row->par(); - Buffer const * buffer = p.bv->buffer(); - pos_type const last = rowLastPrintable(p.row); - pos_type main_body = - beginningOfMainBody(buffer, par); - if (main_body > 0 && - (main_body - 1 > last || - !par->isLineSeparator(main_body - 1))) { - main_body = 0; + // now find the wanted row + while (rit->pos() < pos + && boost::next(rit) != end + && boost::next(rit)->par() == pit + && boost::next(rit)->pos() <= pos) { + y += rit->height(); + ++rit; } - LyXLayout_ptr const & layout = par->layout(); - - pos_type vpos = p.row->pos(); - while (vpos <= last) { - if (p.x > p.bv->workWidth()) - break; - pos_type pos = vis2log(vpos); - if (p.x + singleWidth(p.bv, par, pos) < 0) { - p.x += singleWidth(p.bv, par, pos); - ++vpos; - continue; - } - if (main_body > 0 && pos == main_body - 1) { - int const lwidth = font_metrics::width(layout->labelsep, - getLabelFont(buffer, par)); - - p.x += p.label_hfill + lwidth - - singleWidth(p.bv, par, main_body - 1); - } - - if (par->isHfill(pos)) { - p.x += 1; - - int const y0 = p.yo + p.row->baseline(); - int const y1 = y0 - defaultHeight() / 2; - - p.pain->line(int(p.x), y1, int(p.x), y0, - LColor::added_space); - - if (hfillExpansion(buffer, p.row, pos)) { - int const y2 = (y0 + y1) / 2; - - if (pos >= main_body) { - p.pain->line(int(p.x), y2, - int(p.x + p.hfill), y2, - LColor::added_space, - Painter::line_onoffdash); - p.x += p.hfill; - } else { - p.pain->line(int(p.x), y2, - int(p.x + p.label_hfill), y2, - LColor::added_space, - Painter::line_onoffdash); - p.x += p.label_hfill; - } - p.pain->line(int(p.x), y1, - int(p.x), y0, - LColor::added_space); - } - p.x += 2; - ++vpos; - } else if (par->isSeparator(pos)) { - p.x += singleWidth(p.bv, par, pos); - if (pos >= main_body) - p.x += p.separator; - ++vpos; - } else { - if (!draw(p, vpos)) - break; - } - } + return rit; } -void LyXText::getVisibleRow(BufferView * bv, int y_offset, int x_offset, - Row * row, int y, bool cleared) +RowList::iterator LyXText::getRowNearY(int & y) const { - if (row->height() <= 0) { - lyxerr << "LYX_ERROR: row.height: " - << row->height() << endl; - return; - } - - DrawRowParams p; - - // set up drawing parameters - p.bv = bv; - p.pain = &bv->painter(); - p.row = row; - p.xo = x_offset; - p.yo = y_offset; - prepareToPrint(bv, row, p.x, p.separator, p.hfill, p.label_hfill); - if (inset_owner && (p.x < 0)) - p.x = 0; - p.x += p.xo; - p.y = y; - p.width = inset_owner ? inset_owner->textWidth(bv, true) : bv->workWidth(); - p.cleared = cleared; - - // start painting - - // clear to background if necessary - p.cleared = paintRowBackground(p); - - // paint the selection background - if (selection.set()) { - paintRowSelection(p); - } - - // vertical lines for appendix - paintRowAppendix(p); - - // environment depth brackets - paintRowDepthBar(p); - // draw any stuff wanted for a first row of a paragraph - if (!row->pos()) { - paintFirstRow(p); - } + RowList::iterator rit = anchor_row_; + RowList::iterator const beg = rows().begin(); + RowList::iterator const end = rows().end(); - // draw any stuff wanted for the last row of a paragraph - if (!row->next() || (row->next()->par() != row->par())) { - paintLastRow(p); + if (rows().empty()) { + y = 0; + return end; } + if (rit == end) + rit = beg; - // paint text - paintRowText(p); -} - - -int LyXText::defaultHeight() const -{ - LyXFont font(LyXFont::ALL_SANE); - return int(font_metrics::maxAscent(font) - + font_metrics::maxDescent(font) * 1.5); -} - - -// returns the column near the specified x-coordinate of the row -// x is set to the real beginning of this column -pos_type -LyXText::getColumnNearX(BufferView * bview, Row * row, int & x, - bool & boundary) const -{ - float tmpx = 0.0; - float fill_separator; - float fill_hfill; - float fill_label_hfill; - - prepareToPrint(bview, row, tmpx, fill_separator, - fill_hfill, fill_label_hfill); - - pos_type vc = row->pos(); - pos_type last = rowLastPrintable(row); - pos_type c = 0; - - LyXLayout_ptr const & layout = row->par()->layout(); - - bool left_side = false; - - pos_type main_body = beginningOfMainBody(bview->buffer(), row->par()); - float last_tmpx = tmpx; + int tmpy = rit->y(); - if (main_body > 0 && - (main_body - 1 > last || - !row->par()->isLineSeparator(main_body - 1))) - main_body = 0; - - while (vc <= last && tmpx <= x) { - c = vis2log(vc); - last_tmpx = tmpx; - if (main_body > 0 && c == main_body-1) { - tmpx += fill_label_hfill + - font_metrics::width(layout->labelsep, - getLabelFont(bview->buffer(), row->par())); - if (row->par()->isLineSeparator(main_body - 1)) - tmpx -= singleWidth(bview, row->par(), main_body-1); + if (tmpy <= y) { + while (rit != end && tmpy <= y) { + tmpy += rit->height(); + ++rit; } - - if (hfillExpansion(bview->buffer(), row, c)) { - tmpx += singleWidth(bview, row->par(), c); - if (c >= main_body) - tmpx += fill_hfill; - else - tmpx += fill_label_hfill; + if (rit != beg) { + --rit; + tmpy -= rit->height(); } - else if (row->par()->isSeparator(c)) { - tmpx += singleWidth(bview, row->par(), c); - if (c >= main_body) - tmpx+= fill_separator; - } else - tmpx += singleWidth(bview, row->par(), c); - ++vc; - } - - if ((tmpx + last_tmpx) / 2 > x) { - tmpx = last_tmpx; - left_side = true; - } - - if (vc > last + 1) // This shouldn't happen. - vc = last + 1; - - boundary = false; - bool const lastrow = lyxrc.rtl_support // This is not needed, but gives - // some speedup if rtl_support=false - && (!row->next() || row->next()->par() != row->par()); - bool const rtl = (lastrow) - ? row->par()->isRightToLeftPar(bview->buffer()->params) - : false; // If lastrow is false, we don't need to compute - // the value of rtl. - - if (row->pos() > last) // Row is empty? - c = row->pos(); - else if (lastrow && - ((rtl && left_side && vc == row->pos() && x < tmpx - 5) || - (!rtl && !left_side && vc == last + 1 && x > tmpx + 5))) - c = last + 1; - else if (vc == row->pos()) { - c = vis2log(vc); - if (bidi_level(c) % 2 == 1) - ++c; } else { - c = vis2log(vc - 1); - bool const rtl = (bidi_level(c) % 2 == 1); - if (left_side == rtl) { - ++c; - boundary = isBoundary(bview->buffer(), row->par(), c); + while (rit != beg && tmpy > y) { + --rit; + tmpy -= rit->height(); } } - - if (row->pos() <= last && c > last - && row->par()->isNewline(last)) { - if (bidi_level(last) % 2 == 0) - tmpx -= singleWidth(bview, row->par(), last); - else - tmpx += singleWidth(bview, row->par(), last); - c = last; - } - - c -= row->pos(); - x = int(tmpx); - return c; -} - - -// returns pointer to a specified row -Row * LyXText::getRow(Paragraph * par, pos_type pos, int & y) const -{ - if (!firstrow) - return 0; - - Row * tmprow = firstrow; - y = 0; - - // find the first row of the specified paragraph - while (tmprow->next() && tmprow->par() != par) { - y += tmprow->height(); - tmprow = tmprow->next(); - } - - // now find the wanted row - while (tmprow->pos() < pos - && tmprow->next() - && tmprow->next()->par() == par - && tmprow->next()->pos() <= pos) { - y += tmprow->height(); - tmprow = tmprow->next(); - } - - return tmprow; -} - - -Row * LyXText::getRowNearY(int & y) const -{ -#if 1 - // If possible we should optimize this method. (Lgb) - Row * tmprow = firstrow; - int tmpy = 0; - - while (tmprow->next() && tmpy + tmprow->height() <= y) { - tmpy += tmprow->height(); - tmprow = tmprow->next(); - } - - y = tmpy; // return the real y - - //lyxerr << "returned y = " << y << endl; - - return tmprow; -#else - // Search from the current cursor position. - - Row * tmprow = cursor.row(); - int tmpy = cursor.y() - tmprow->baseline(); - - lyxerr << "cursor.y() = " << tmpy << endl; - lyxerr << "tmprow->height() = " << tmprow->height() << endl; - lyxerr << "tmprow->baseline() = " << tmprow->baseline() << endl; - lyxerr << "first = " << first << endl; - lyxerr << "y = " << y << endl; - - if (y < tmpy) { - lyxerr << "up" << endl; - do { - tmpy -= tmprow->height(); - tmprow = tmprow->previous(); - } while (tmprow && tmpy - tmprow->height() >= y); - } else if (y > tmpy) { - lyxerr << "down" << endl; - - while (tmprow->next() && tmpy + tmprow->height() <= y) { - tmpy += tmprow->height(); - tmprow = tmprow->next(); - } - } else { - lyxerr << "equal" << endl; + if (tmpy < 0 || rit == end) { + tmpy = 0; + rit = beg; } - y = tmpy; // return the real y + // return the rel y + y = tmpy; - lyxerr << "returned y = " << y << endl; - - return tmprow; - -#endif + return rit; }