]> git.lyx.org Git - lyx.git/blobdiff - src/TextMetrics.cpp
Fix typo.
[lyx.git] / src / TextMetrics.cpp
index cd7beb2350878c688d60c1db9bd85ca5f7a6930f..a26b88dc726b0d6a32f9cb5f90fdec712e4783b5 100644 (file)
@@ -80,15 +80,18 @@ int numberOfLabelHfills(Paragraph const & par, Row const & row)
        return n;
 }
 
-
-int numberOfHfills(Row const & row, pos_type const body_pos)
+// FIXME: this needs to be rewritten, probably by merging it into some
+// code that, besides counting, sets the active status of the space
+// inset in the row element.
+int numberOfHfills(Row const & row, ParagraphMetrics const & pm,
+                   pos_type const body_pos)
 {
        int n = 0;
        Row::const_iterator cit = row.begin();
        Row::const_iterator const end = row.end();
        for ( ; cit != end ; ++cit)
                if (cit->pos >= body_pos
-                   && cit->inset && cit->inset->isHfill())
+                   && cit->inset && pm.hfillExpansion(row, cit->pos))
                        ++n;
        return n;
 }
@@ -399,6 +402,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
        }
 
        // redo insets
+       par.setBeginOfBody();
        Font const bufferfont = buffer.params().getFont();
        CoordCache::Insets & insetCache = bv_->coordCache().insets();
        InsetList::const_iterator ii = par.insetList().begin();
@@ -432,7 +436,6 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                }
        }
 
-       par.setBeginOfBody();
        pos_type first = 0;
        size_t row_index = 0;
        // maximum pixel width of a row
@@ -440,16 +443,25 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                if (row_index == pm.rows().size())
                        pm.rows().push_back(Row());
                Row & row = pm.rows()[row_index];
+               row.pit(pit);
                row.pos(first);
                breakRow(row, right_margin, pit);
                setRowHeight(row, pit);
                row.setChanged(false);
-               if (row_index || row.right_boundary() || row.endpos() < par.size())
-                       // If there is more than one row or the row has been
-                       // broken by a display inset or a newline, expand the text
-                       // to the full allowable width. This setting here is
-                       // needed for the computeRowMetrics() below.
-                       dim_.wid = max_width_;
+               if (row_index || row.endpos() < par.size()
+                   || (row.right_boundary() && par.inInset().lyxCode() != CELL_CODE)) {
+                       /* If there is more than one row or the row has been
+                        * broken by a display inset or a newline, expand the text
+                        * to the full allowable width. This setting here is
+                        * needed for the computeRowMetrics() below. In the case
+                        * of a display inset, we do nothing when inside a table
+                        * cell, because the tabular code is not prepared for
+                        * that, and it triggers when using a caption in a
+                        * longtable (see bugs #9945 and #9757).
+                        */
+                       if (dim_.wid < max_width_)
+                               dim_.wid = max_width_;
+               }
                int const max_row_width = max(dim_.wid, row.width());
                computeRowMetrics(pit, row, max_row_width);
                first = row.endpos();
@@ -469,7 +481,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                        pm.rows().push_back(Row());
                Row & row = pm.rows()[row_index];
                row.pos(first);
-               row.endpos(first);
+               breakRow(row, right_margin, pit);
                setRowHeight(row, pit);
                row.setChanged(false);
                int const max_row_width = max(dim_.wid, row.width());
@@ -486,7 +498,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
 }
 
 
-LyXAlignment TextMetrics::getAlign(Paragraph const & par, pos_type const pos) const
+LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const
 {
        Layout const & layout = par.layout();
 
@@ -498,7 +510,14 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, pos_type const pos) co
 
        // handle alignment inside tabular cells
        Inset const & owner = text_->inset();
+       bool forced_block = false;
        switch (owner.contentAlignment()) {
+       case LYX_ALIGN_BLOCK:
+               // In general block align is the default state, but here it is
+               // an explicit choice. Therefore it should not be overridden
+               // later.
+               forced_block = true;
+               // fall through
        case LYX_ALIGN_CENTER:
        case LYX_ALIGN_LEFT:
        case LYX_ALIGN_RIGHT:
@@ -511,7 +530,7 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, pos_type const pos) co
        }
 
        // Display-style insets should always be on a centered row
-       if (Inset const * inset = par.getInset(pos)) {
+       if (Inset const * inset = par.getInset(row.pos())) {
                switch (inset->display()) {
                case Inset::AlignLeft:
                        align = LYX_ALIGN_BLOCK;
@@ -528,10 +547,17 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, pos_type const pos) co
                }
        }
 
-       // Has the user requested we not justify stuff?
-       if (!bv_->buffer().params().justification
-           && align == LYX_ALIGN_BLOCK)
-               align = LYX_ALIGN_LEFT;
+       if (align == LYX_ALIGN_BLOCK) {
+               // If this row has been broken abruptly by a display inset, or
+               // it is the end of the paragraph, or the user requested we
+               // not justify stuff, then don't stretch.
+               // A forced block alignment can only be overridden the 'no
+               // justification on screen' setting.
+               if (((row.right_boundary() || row.endpos() == par.size())
+                    && !forced_block)
+                   || !bv_->buffer().params().justification)
+                       align = text_->isRTL(par) ? LYX_ALIGN_RIGHT : LYX_ALIGN_LEFT;
+       }
 
        return align;
 }
@@ -570,25 +596,26 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                        row.label_hfill = labelFill(pit, row) / double(nlh);
        }
 
-       double hfill = 0;
        // are there any hfills in the row?
-       if (int const nh = numberOfHfills(row, par.beginOfBody())) {
-               if (w > 0)
-                       hfill = double(w) / nh;
-       // 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 (int(row.width()) < max_width_) {
-               // is it block, flushleft or flushright?
-               // set x how you need it
-               switch (getAlign(par, row.pos())) {
+       ParagraphMetrics & pm = par_metrics_[pit];
+       int nh = numberOfHfills(row, pm, par.beginOfBody());
+       int hfill = 0;
+       int hfill_rem = 0;
+
+       // We don't have to look at the alignment if the row is already
+       // larger then the permitted width as then we force the
+       // LEFT_ALIGN'edness!
+       if (int(row.width()) >= max_width_)
+               return;
+
+       if (nh == 0) {
+               // Common case : there is no hfill, and the alignment will be
+               // meaningful
+               switch (getAlign(par, row)) {
                case LYX_ALIGN_BLOCK: {
                        int const ns = row.countSeparators();
-                       /** If we have separators, and this row has
-                        * not be broken abruptly by a display inset
-                        * or newline, then stretch it */
-                       if (ns && !row.right_boundary()
-                           && row.endpos() != par.size()) {
+                       // If we have separators, then stretch the row
+                       if (ns) {
                                row.setSeparatorExtraWidth(double(w) / ns);
                                row.dimension().wid += w;
                        } else if (text_->isRTL(par)) {
@@ -612,15 +639,19 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                case LYX_ALIGN_DECIMAL:
                        break;
                }
+               return;
        }
 
-       // Finally,  handle hfill insets
+       hfill = w / nh;
+       hfill_rem = w % nh;
+       row.dimension().wid += w;
+       // Set size of hfill insets
        pos_type const endpos = row.endpos();
        pos_type body_pos = par.beginOfBody();
        if (body_pos > 0
            && (body_pos > endpos || !par.isLineSeparator(body_pos - 1)))
                body_pos = 0;
-       ParagraphMetrics & pm = par_metrics_[pit];
+
        CoordCache::Insets & insetCache = bv_->coordCache().insets();
        Row::iterator cit = row.begin();
        Row::iterator const cend = row.end();
@@ -628,15 +659,17 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                if (row.label_hfill && cit->endpos == body_pos
                    && cit->type == Row::SPACE)
                        cit->dim.wid -= int(row.label_hfill * (nlh - 1));
-               if (!cit->inset || !cit->inset->isHfill())
-                       continue;
-               if (pm.hfillExpansion(row, cit->pos))
-                       cit->dim.wid = int(cit->pos >= body_pos ?
-                                          max(hfill, 5.0) : row.label_hfill);
-               else
-                       cit->dim.wid = 5;
-               // Cache the inset dimension.
-               insetCache.add(cit->inset, cit->dim);
+               if (cit->inset && pm.hfillExpansion(row, cit->pos)) {
+                       if (cit->pos >= body_pos) {
+                               cit->dim.wid += hfill;
+                               --nh;
+                               if (nh == 0)
+                                       cit->dim.wid += hfill_rem;
+                       } else
+                               cit->dim.wid += int(row.label_hfill);
+                       // Cache the inset dimension.
+                       insetCache.add(cit->inset, cit->dim);
+               }
        }
 }
 
@@ -761,6 +794,7 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
        pos_type const pos = row.pos();
        pos_type const body_pos = par.beginOfBody();
        bool const is_rtl = text_->isRTL(par);
+       bool need_new_row = false;
 
        row.clear();
        row.left_margin = leftMargin(max_width_, pit, pos);
@@ -773,11 +807,6 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
        // the width available for the row.
        int const width = max_width_ - row.right_margin;
 
-       if (pos >= end || row.width() > width) {
-               row.endpos(end);
-               return;
-       }
-
        ParagraphList const & pars = text_->paragraphs();
 
 #if 0
@@ -798,7 +827,10 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
        // or the end of the par, then build a representation of the row.
        pos_type i = pos;
        FontIterator fi = FontIterator(*this, par, pit, pos);
-       while (i < end && row.width() <= width) {
+       do {
+               // this can happen for an empty row after a newline
+               if (i >= end)
+                       break;
                char_type c = par.getChar(i);
                // The most special cases are handled first.
                if (par.isInset(i)) {
@@ -857,18 +889,19 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
                    || (!row.empty() && row.back().inset
                        && row.back().inset->display())) {
                        row.right_boundary(true);
+                       need_new_row = par.isNewline(i);
                        ++i;
                        break;
                }
 
                ++i;
                ++fi;
-       }
+       } while (i < end && row.width() <= width);
        row.finalizeLast();
        row.endpos(i);
 
        // End of paragraph marker
-       if (lyxrc.paragraph_markers
+       if (lyxrc.paragraph_markers && !need_new_row
            && i == end && size_type(pit + 1) < pars.size()) {
                // add a virtual element for the end-of-paragraph
                // marker; it is shown on screen, but does not exist
@@ -881,8 +914,10 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
                row.addVirtual(end, docstring(1, char_type(0x00B6)), f, Change());
        }
 
-       // if the row is too large, try to cut at last separator.
-       row.shortenIfNeeded(body_pos, width);
+       // if the row is too large, try to cut at last separator. In case
+       // of success, reset indication that the row was broken abruptly.
+       if (row.shortenIfNeeded(body_pos, width))
+               row.right_boundary(!row.empty() && row.back().endpos == row.endpos());
 
        // make sure that the RTL elements are in reverse ordering
        row.reverseRTL(is_rtl);
@@ -1079,6 +1114,16 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
        int const xo = origin_.x_;
        x -= xo;
 
+       int offset = 0;
+       CursorSlice rowSlice(const_cast<InsetText &>(text_->inset()));
+       rowSlice.pit() = row.pit();
+       rowSlice.pos() = row.pos();
+
+       // Adapt to cursor row scroll offset if applicable.
+       if (bv_->currentRowSlice() == rowSlice)
+               offset = bv_->horizScrollOffset();
+       x += offset;
+
        pos_type pos = row.pos();
        boundary = false;
        if (row.empty())
@@ -1124,11 +1169,18 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
         * row is larger than the end of its last element.
         */
        if (!row.empty() && pos == row.back().endpos
-           && row.back().endpos == row.endpos())
-               boundary = true;
+           && row.back().endpos == row.endpos()) {
+               Inset const * inset = row.back().inset;
+               if (inset && (inset->lyxCode() == NEWLINE_CODE
+                             || inset->lyxCode() == SEPARATOR_CODE))
+                       pos = row.back().pos;
+               else
+                       boundary = row.right_boundary();
+       }
 
-       x += xo;
+       x += xo - offset;
        //LYXERR0("getPosNearX ==> pos=" << pos << ", boundary=" << boundary);
+
        return pos;
 }
 
@@ -1557,7 +1609,8 @@ bool TextMetrics::cursorEnd(Cursor & cur)
                        boundary = true;
                else
                        --end;
-       }
+       } else if (cur.paragraph().isEnvSeparator(end-1))
+               --end;
        return text_->setCursor(cur, cur.pit(), end, true, boundary);
 }
 
@@ -1821,7 +1874,6 @@ void TextMetrics::draw(PainterInfo & pi, int x, int y) const
 
 void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const x, int y) const
 {
-       BufferParams const & bparams = bv_->buffer().params();
        ParagraphMetrics const & pm = par_metrics_[pit];
        if (pm.rows().empty())
                return;
@@ -1895,7 +1947,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
 
                // Row signature; has row changed since last paint?
                if (pi.pain.isDrawingEnabled())
-                       row.setCrc(pm.computeRowSignature(row, bparams));
+                       row.setCrc(pm.computeRowSignature(row, *bv_));
                bool row_has_changed = row.changed()
                        || rowSlice == bv_->lastRowSlice();