]> git.lyx.org Git - features.git/blobdiff - src/TextMetrics.cpp
New helper method Row::Element::isRTL()
[features.git] / src / TextMetrics.cpp
index ab857a5fdde1cde62f994450a23ed317ed07b4a8..fc5e1c5ac5ec0e29856bcfcf7738150487749f03 100644 (file)
@@ -58,28 +58,6 @@ using frontend::FontMetrics;
 
 namespace {
 
-int numberOfSeparators(Row const & row)
-{
-       int n = 0;
-       Row::const_iterator cit = row.begin();
-       Row::const_iterator const end = row.end();
-       for ( ; cit != end ; ++cit)
-               if (cit->type == Row::SEPARATOR)
-                       ++n;
-       return n;
-}
-
-
-void setSeparatorWidth(Row & row, double w)
-{
-       row.separator = w;
-       Row::iterator it = row.begin();
-       Row::iterator const end = row.end();
-       for ( ; it != end ; ++it)
-               if (it->type == Row::SEPARATOR)
-                       it->extra = w;
-}
-
 
 int numberOfLabelHfills(Paragraph const & par, Row const & row)
 {
@@ -385,7 +363,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                Cursor & cur = const_cast<Cursor &>(bv_->cursor());
                // In some cases, we do not know how to record undo
                if (&cur.inset() == &text_->inset())
-                       cur.recordUndo(ATOMIC_UNDO, pit, pit);
+                       cur.recordUndo(pit, pit);
 
                int const moveCursor = par.fixBiblio(buffer);
 
@@ -446,7 +424,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                MacroContext mc(&buffer, parPos);
                MetricsInfo mi(bv_, font.fontInfo(), w, mc);
                ii->inset->metrics(mi, dim);
-               Dimension const old_dim = pm.insetDimension(ii->inset);
+               Dimension const old_dim = pm.insetDimension(ii->inset);
                if (old_dim != dim) {
                        pm.setInsetDimension(ii->inset, dim);
                        changed = true;
@@ -506,11 +484,11 @@ bool TextMetrics::redoParagraph(pit_type const pit)
 }
 
 
-int TextMetrics::getAlign(Paragraph const & par, pos_type const pos) const
+LyXAlignment TextMetrics::getAlign(Paragraph const & par, pos_type const pos) const
 {
        Layout const & layout = par.layout();
 
-       int align;
+       LyXAlignment align;
        if (par.params().align() == LYX_ALIGN_LAYOUT)
                align = layout.align;
        else
@@ -565,16 +543,10 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
 
        Paragraph const & par = text_->getPar(pit);
 
-       double const w = width - row.right_margin - row.width();
+       int const w = width - row.right_margin - row.width();
        // FIXME: put back this assertion when the crash on new doc is solved.
        //LASSERT(w >= 0, /**/);
 
-       bool const is_rtl = text_->isRTL(par);
-       if (is_rtl)
-               row.x = rightMargin(pit);
-       else
-               row.x = leftMargin(max_width_, pit, row.pos());
-
        // is there a manual margin with a manual label
        Layout const & layout = par.layout();
 
@@ -600,7 +572,7 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
        // are there any hfills in the row?
        if (int const nh = numberOfHfills(row, par.beginOfBody())) {
                if (w > 0)
-                       hfill = w / double(nh);
+                       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!
@@ -609,45 +581,37 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
                // set x how you need it
                switch (getAlign(par, row.pos())) {
                case LYX_ALIGN_BLOCK: {
-                       int const ns = numberOfSeparators(row);
+                       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()) {
-                               setSeparatorWidth(row, w / ns);
+                               row.setSeparatorExtraWidth(double(w) / ns);
                                row.dimension().wid = width;
-                       } else if (is_rtl) {
+                       } else if (text_->isRTL(par)) {
                                row.dimension().wid = width;
-                               row.x += w;
+                               row.left_margin += w;
                        }
                        break;
                }
                case LYX_ALIGN_RIGHT:
-                       row.x += w;
+                       row.left_margin += w;
+                       row.dimension().wid += w;
                        break;
                case LYX_ALIGN_CENTER:
                        row.dimension().wid = width - w / 2;
-                       row.x += w / 2;
+                       row.left_margin += w / 2;
+                       break;
+               case LYX_ALIGN_LEFT:
+               case LYX_ALIGN_NONE:
+               case LYX_ALIGN_LAYOUT:
+               case LYX_ALIGN_SPECIAL:
+               case LYX_ALIGN_DECIMAL:
                        break;
                }
        }
 
-#if 0
-       if (is_rtl) {
-               pos_type body_pos = par.beginOfBody();
-               pos_type end = row.endpos();
-
-               if (body_pos > 0
-                   && (body_pos > end || !par.isLineSeparator(body_pos - 1))) {
-                       row.x += theFontMetrics(text_->labelFont(par)).
-                               width(layout.labelsep);
-                       if (body_pos <= end)
-                               row.x += row.label_hfill;
-               }
-       }
-#endif
-
        // Finally,  handle hfill insets
        pos_type const endpos = row.endpos();
        pos_type body_pos = par.beginOfBody();
@@ -660,7 +624,7 @@ void TextMetrics::computeRowMetrics(pit_type const pit,
        for ( ; cit != cend; ++cit) {
                if (row.label_hfill && cit->endpos == body_pos
                    && cit->type == Row::SPACE)
-                       cit->dim.wid -= row.label_hfill * (nlh - 1);
+                       cit->dim.wid -= int(row.label_hfill * (nlh - 1));
                if (!cit->inset || !cit->inset->isHfill())
                        continue;
                if (pm.hfillExpansion(row, cit->pos))
@@ -686,7 +650,7 @@ int TextMetrics::labelFill(pit_type const pit, Row const & row) const
        // iterate over elements before main body (except the last one,
        // which is extra space).
        while (cit!= end && cit->endpos < par.beginOfBody()) {
-               w += cit->width();
+               w += cit->dim.wid;
                ++cit;
        }
 
@@ -786,21 +750,28 @@ private:
 
 /** This is the function where the hard work is done. The code here is
  * very sensitive to small changes :) Note that part of the
- * intelligence is also in Row::shorten_if_needed
+ * intelligence is also in Row::shortenIfNeeded.
  */
 void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit) const
 {
        Paragraph const & par = text_->getPar(pit);
        pos_type const end = par.size();
        pos_type const pos = row.pos();
-       int const width = max_width_ - right_margin;
        pos_type const body_pos = par.beginOfBody();
+       bool const is_rtl = text_->isRTL(par);
+
        row.clear();
-       row.dimension().wid = leftMargin(max_width_, pit, pos);
+       row.left_margin = leftMargin(max_width_, pit, pos);
        row.right_margin = right_margin;
+       if (is_rtl)
+               swap(row.left_margin, row.right_margin);
+       // Remember that the row width takes into account the left_margin
+       // but not the right_margin.
+       row.dimension().wid = row.left_margin;
+       // the width available for the row.
+       int const width = max_width_ - row.right_margin;
 
        if (pos >= end || row.width() > width) {
-               row.dimension().wid += right_margin;
                row.endpos(end);
                return;
        }
@@ -817,20 +788,16 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
 #endif
 
        // check for possible inline completion
-       DocIterator const & inlineCompletionPos = bv_->inlineCompletionPos();
-       pos_type inlineCompletionLPos = -1;
-       if (inlineCompletionPos.inTexted()
-           && inlineCompletionPos.text() == text_
-           && inlineCompletionPos.pit() == pit) {
-               // draw logically behind the previous character
-               inlineCompletionLPos = inlineCompletionPos.pos() - 1;
-       }
+       DocIterator const & ic_it = bv_->inlineCompletionPos();
+       pos_type ic_pos = -1;
+       if (ic_it.inTexted() && ic_it.text() == text_ && ic_it.pit() == pit)
+               ic_pos = ic_it.pos();
 
        // Now we iterate through until we reach the right margin
        // 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) {
+       while (i < end && row.width() <= width) {
                char_type c = par.getChar(i);
                // The most special cases are handled first.
                if (par.isInset(i)) {
@@ -843,16 +810,11 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
                        // before body_pos. Instead, insert some spacing to
                        // align text
                        FontMetrics const & fm = theFontMetrics(text_->labelFont(par));
+                       // this is needed to make sure that the row width is correct
+                       row.finalizeLast();
                        int const add = max(fm.width(par.layout().labelsep),
                                            labelEnd(pit) - row.width());
                        row.addSpace(i, add, *fi, par.lookupChange(i));
-               } else if (par.isLineSeparator(i)) {
-                       // In theory, no inset has this property. If
-                       // this is done, a new addSeparator which
-                       // takes an inset as parameter should be
-                       // added.
-                       LATTEST(!par.isInset(i));
-                       row.addSeparator(i, c, *fi, par.lookupChange(i));
                } else if (c == '\t')
                        row.addSpace(i, theFontMetrics(*fi).width(from_ascii("    ")),
                                     *fi, par.lookupChange(i));
@@ -860,12 +822,18 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
                        row.add(i, c, *fi, par.lookupChange(i));
 
                // add inline completion width
-               if (inlineCompletionLPos == i &&
-                   !bv_->inlineCompletion().empty()) {
+               // draw logically behind the previous character
+               if (ic_pos == i + 1 && !bv_->inlineCompletion().empty()) {
+                       docstring const comp = bv_->inlineCompletion();
+                       size_t const uniqueTo =bv_->inlineCompletionUniqueChars();
                        Font f = *fi;
-                       f.fontInfo().setColor(Color_inlinecompletion);
-                       row.addVirtual(i + 1, bv_->inlineCompletion(),
-                                         f, Change());
+
+                       if (uniqueTo > 0) {
+                               f.fontInfo().setColor(Color_inlinecompletion);
+                               row.addVirtual(i + 1, comp.substr(0, uniqueTo), f, Change());
+                       }
+                       f.fontInfo().setColor(Color_nonunique_inlinecompletion);
+                       row.addVirtual(i + 1, comp.substr(uniqueTo), f, Change());
                }
 
                // Handle some situations that abruptly terminate the row
@@ -906,14 +874,9 @@ void TextMetrics::breakRow(Row & row, int const right_margin, pit_type const pit
        // if the row is too large, try to cut at last separator.
        row.shortenIfNeeded(body_pos, width);
 
-       // if the row ends with a separator that is not at end of
-       // paragraph, remove it
-       if (!row.empty() && row.back().type == Row::SEPARATOR
-           && row.endpos() < par.size())
-               row.pop_back();
-
        // make sure that the RTL elements are in reverse ordering
-       row.reverseRTL(text_->isRTL(par));
+       row.reverseRTL(is_rtl);
+       //LYXERR0("breakrow: row is " << row);
 }
 
 
@@ -1099,6 +1062,7 @@ void TextMetrics::setRowHeight(Row & row, pit_type const pit,
 pos_type TextMetrics::getPosNearX(Row const & row, int & x,
                                  bool & boundary) const
 {
+       //LYXERR0("getPosNearX(" << x << ") row=" << row);
        /// For the main Text, it is possible that this pit is not
        /// yet in the CoordCache when moving cursor up.
        /// x Paragraph coordinate is always 0 for main text anyway.
@@ -1108,29 +1072,29 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
        pos_type pos = row.pos();
        boundary = false;
        if (row.empty())
-               x = row.x;
-       else if (x <= row.x) {
+               x = row.left_margin;
+       else if (x <= row.left_margin) {
                pos = row.front().left_pos();
-               x = row.x;
-       } else if (x >= row.width() - row.right_margin) {
+               x = row.left_margin;
+       } else if (x >= row.width()) {
                pos = row.back().right_pos();
-               x = row.width() - row.right_margin;
+               x = row.width();
        } else {
-               double w = row.x;
+               double w = row.left_margin;
                Row::const_iterator cit = row.begin();
                Row::const_iterator cend = row.end();
                for ( ; cit != cend; ++cit) {
-                       if (w <= x &&  w + cit->width() > x) {
-                               double x_offset = x - w;
+                       if (w <= x &&  w + cit->full_width() > x) {
+                               int x_offset = int(x - w);
                                pos = cit->x2pos(x_offset);
-                               x = x_offset + w;
+                               x = int(x_offset + w);
                                break;
                        }
-                       w += cit->width();
+                       w += cit->full_width();
                }
                if (cit == row.end()) {
                        pos = row.back().right_pos();
-                       x = row.width() - row.right_margin;
+                       x = row.width();
                }
                /** This tests for the case where the cursor is placed
                 * just before a font direction change. See comment on
@@ -1139,19 +1103,22 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
                 */
                else if (pos == cit->endpos
                         && cit + 1 != row.end()
-                        && cit->font.isVisibleRightToLeft() != (cit + 1)->font.isVisibleRightToLeft())
+                        && cit->isRTL() != (cit + 1)->isRTL())
                        boundary = true;
        }
 
        /** This tests for the case where the cursor is set at the end
-        * of a row which has been broken due to a display inset on
-        * next row. This is indicated by Row::right_boundary.
+        * of a row which has been broken due something else than a
+        * separator (a display inset or a forced breaking of the
+        * row). We know that there is a separator when the end of the
+        * row is larger than the end of its last element.
         */
        if (!row.empty() && pos == row.back().endpos
            && row.back().endpos == row.endpos())
-               boundary = row.right_boundary();
+               boundary = true;
 
        x += xo;
+       //LYXERR0("getPosNearX ==> pos=" << pos << ", boundary=" << boundary);
        return pos;
 }
 
@@ -1473,10 +1440,10 @@ int TextMetrics::cursorX(CursorSlice const & sl,
        if (row.empty()
            || (pos == row.begin()->left_pos()
                && pos != row.begin()->right_pos()))
-               return int(row.x);
+               return row.left_margin;
 
        Row::const_iterator cit = row.begin();
-       double x = row.x;
+       double x = row.left_margin;
        for ( ; cit != row.end() ; ++cit) {
                /** Look whether the cursor is inside the element's
                 * span. Note that it is necessary to take the
@@ -1489,7 +1456,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
                                x += cit->pos2x(pos);
                                break;
                }
-               x += cit->width();
+               x += cit->full_width();
        }
 
        return int(x);
@@ -1620,12 +1587,16 @@ int TextMetrics::leftMargin(int max_width,
        l_margin += theFontMetrics(buffer.params().getFont()).signedWidth(
                tclass.leftmargin());
 
-       if (par.getDepth() != 0) {
+       int depth = par.getDepth();
+       if (depth != 0) {
                // find the next level paragraph
                pit_type newpar = text_->outerHook(pit);
                if (newpar != pit_type(pars.size())) {
                        if (pars[newpar].layout().isEnvironment()) {
-                               l_margin = leftMargin(max_width, newpar);
+                               int nestmargin = depth * nestMargin();
+                               if (text_->isMainText())
+                                       nestmargin += changebarMargin();
+                               l_margin = max(leftMargin(max_width, newpar), nestmargin);
                                // Remove the parindent that has been added
                                // if the paragraph was empty.
                                if (pars[newpar].empty() &&
@@ -1740,7 +1711,7 @@ int TextMetrics::leftMargin(int max_width,
        }
 
        if (!par.params().leftIndent().zero())
-               l_margin += par.params().leftIndent().inPixels(max_width);
+               l_margin += par.params().leftIndent().inPixels(max_width, labelfont_metrics.em());
 
        LyXAlignment align;
 
@@ -1882,12 +1853,13 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                }
 
                // Row signature; has row changed since last paint?
-               row.setCrc(pm.computeRowSignature(row, bparams));
+               if (pi.pain.isDrawingEnabled())
+                       row.setCrc(pm.computeRowSignature(row, bparams));
                bool row_has_changed = row.changed()
                        || rowSlice == bv_->lastRowSlice();
 
                // Take this opportunity to spellcheck the row contents.
-               if (row_has_changed && lyxrc.spellcheck_continuously) {
+               if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) {
                        text_->getPar(pit).spellCheck();
                }
 
@@ -1905,7 +1877,7 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                // already cleared because of a full repaint.
                if (!pi.full_repaint && row_has_changed) {
                        LYXERR(Debug::PAINTING, "Clear rect@("
-                              << max(row_x, 0) << ", " << y-row.ascent() << ")="
+                              << max(row_x, 0) << ", " << y - row.ascent() << ")="
                               << width() << " x " << row.height());
                        pi.pain.fillRectangle(max(row_x, 0), y - row.ascent(),
                                width(), row.height(), pi.background_color);
@@ -1920,7 +1892,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                        LYXERR(Debug::PAINTING, foreword << "pit=" << pit << " row=" << i
                                << " row_selection="    << row.selection()
                                << " full_repaint="     << pi.full_repaint
-                               << " row_has_changed="  << row_has_changed);
+                               << " row_has_changed="  << row_has_changed
+                               << " drawingEnabled=" << pi.pain.isDrawingEnabled());
                }
 
                // Backup full_repaint status and force full repaint
@@ -1942,6 +1915,8 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                        rp.paintLast();
                if (i == 0 && is_rtl)
                        rp.paintFirst();
+               rp.paintTooLargeMarks(row_x < 0,
+                                     row_x + row.width() > bv_->workWidth());
                y += row.descent();
 
                // Restore full_repaint status.