]> git.lyx.org Git - lyx.git/blobdiff - src/TextMetrics.cpp
info-insert without arg is valid
[lyx.git] / src / TextMetrics.cpp
index d710e5f80afea656696a108d23c77ff473a294ad..2b0903c750d23f1398d462db0ddb8e8956300eb5 100644 (file)
@@ -45,6 +45,7 @@
 #include "frontends/Painter.h"
 #include "frontends/NullPainter.h"
 
+#include "support/convert.h"
 #include "support/debug.h"
 #include "support/lassert.h"
 
@@ -159,7 +160,8 @@ ParagraphMetrics & TextMetrics::parMetrics(pit_type pit, bool redo)
 }
 
 
-bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
+bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width,
+                         bool const expand_on_multipars)
 {
        LBUFERR(mi.base.textwidth > 0);
        max_width_ = mi.base.textwidth;
@@ -169,7 +171,7 @@ bool TextMetrics::metrics(MetricsInfo & mi, Dimension & dim, int min_width)
        dim_ = Dimension();
        dim_.wid = min_width;
        pit_type const npar = text_->paragraphs().size();
-       if (npar > 1)
+       if (npar > 1 && expand_on_multipars)
                // If there is more than one row, expand the text to
                // the full allowable width.
                dim_.wid = max_width_;
@@ -413,19 +415,17 @@ bool TextMetrics::redoParagraph(pit_type const pit)
        par.setBeginOfBody();
        Font const bufferfont = buffer.params().getFont();
        CoordCache::Insets & insetCache = bv_->coordCache().insets();
-       InsetList::const_iterator ii = par.insetList().begin();
-       InsetList::const_iterator iend = par.insetList().end();
-       for (; ii != iend; ++ii) {
+       for (auto const & e : par.insetList()) {
                // FIXME Doesn't this HAVE to be non-empty?
                // position already initialized?
                if (!parPos.empty()) {
-                       parPos.pos() = ii->pos;
+                       parPos.pos() = e.pos;
 
                        // A macro template would normally not be visible
                        // by itself. But the tex macro semantics allow
                        // recursion, so we artifically take the context
                        // after the macro template to simulate this.
-                       if (ii->inset->lyxCode() == MATHMACRO_CODE)
+                       if (e.inset->lyxCode() == MATHMACRO_CODE)
                                parPos.pos()++;
                }
 
@@ -433,7 +433,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                // substracted to the available width. The logic here is
                // almost the same as in breakRow, remember keep them in sync.
                int eop = 0;
-               if (lyxrc.paragraph_markers && ii->pos + 1 == par.size()
+               if (lyxrc.paragraph_markers && e.pos + 1 == par.size()
                    && size_type(pit + 1) < text_->paragraphs().size()) {
                        Font f(text_->layoutFont(pit));
                        // ΒΆ U+00B6 PILCROW SIGN
@@ -442,15 +442,15 @@ bool TextMetrics::redoParagraph(pit_type const pit)
 
                // do the metric calculation
                Dimension dim;
-               int const w = max_width_ - leftMargin(pit, ii->pos)
+               int const w = max_width_ - leftMargin(pit, e.pos)
                        - right_margin - eop;
-               Font const & font = ii->inset->inheritFont() ?
-                       displayFont(pit, ii->pos) : bufferfont;
+               Font const & font = e.inset->inheritFont() ?
+                       displayFont(pit, e.pos) : bufferfont;
                MacroContext mc(&buffer, parPos);
                MetricsInfo mi(bv_, font.fontInfo(), w, mc);
-               ii->inset->metrics(mi, dim);
-               if (!insetCache.has(ii->inset) || insetCache.dim(ii->inset) != dim) {
-                       insetCache.add(ii->inset, dim);
+               e.inset->metrics(mi, dim);
+               if (!insetCache.has(e.inset) || insetCache.dim(e.inset) != dim) {
+                       insetCache.add(e.inset, dim);
                        changed = true;
                }
        }
@@ -469,16 +469,13 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                need_new_row = breakRow(row, right_margin);
                setRowHeight(row);
                row.changed(true);
-               if (row_index || row.endpos() < par.size()
-                   || (row.right_boundary() && par.inInset().lyxCode() != CELL_CODE)) {
+               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).
+                        * needed for the computeRowMetrics() below.
+                        * We do nothing when inside a table cell.
                         */
                        if (dim_.wid < max_width_)
                                dim_.wid = max_width_;
@@ -500,7 +497,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
        // Top and bottom margin of the document (only at top-level)
        if (text_->isMainText()) {
                // original value was 20px, which is 0.2in at 100dpi
-               int const margin = Length(0.2, Length::IN).inPixels(0);
+               int const margin = bv_->zoomedPixels(20);
                if (pit == 0) {
                        pm.rows().front().dimension().asc += margin;
                        /* coverity thinks that we should update pm.dim().asc
@@ -689,22 +686,20 @@ void TextMetrics::computeRowMetrics(Row & row, int width) const
                body_pos = 0;
 
        CoordCache::Insets & insetCache = bv_->coordCache().insets();
-       Row::iterator cit = row.begin();
-       Row::iterator const cend = row.end();
-       for ( ; cit != cend; ++cit) {
-               if (row.label_hfill && cit->endpos == body_pos
-                   && cit->type == Row::SPACE)
-                       cit->dim.wid -= int(row.label_hfill * (nlh - 1));
-               if (cit->inset && pm.hfillExpansion(row, cit->pos)) {
-                       if (cit->pos >= body_pos) {
-                               cit->dim.wid += hfill;
+       for (Row::Element & e : row) {
+               if (row.label_hfill && e.endpos == body_pos
+                   && e.type == Row::SPACE)
+                       e.dim.wid -= int(row.label_hfill * (nlh - 1));
+               if (e.inset && pm.hfillExpansion(row, e.pos)) {
+                       if (e.pos >= body_pos) {
+                               e.dim.wid += hfill;
                                --nh;
                                if (nh == 0)
-                                       cit->dim.wid += hfill_rem;
+                                       e.dim.wid += hfill_rem;
                        } else
-                               cit->dim.wid += int(row.label_hfill);
+                               e.dim.wid += int(row.label_hfill);
                        // Cache the inset dimension.
-                       insetCache.add(cit->inset, cit->dim);
+                       insetCache.add(e.inset, e.dim);
                }
        }
 }
@@ -716,13 +711,12 @@ int TextMetrics::labelFill(Row const & row) const
        LBUFERR(par.beginOfBody() > 0 || par.isEnvSeparator(0));
 
        int w = 0;
-       Row::const_iterator cit = row.begin();
-       Row::const_iterator const end = row.end();
        // iterate over elements before main body (except the last one,
        // which is extra space).
-       while (cit!= end && cit->endpos < par.beginOfBody()) {
-               w += cit->dim.wid;
-               ++cit;
+       for (Row::Element const & e : row) {
+               if (e.endpos >= par.beginOfBody())
+                       break;
+               w += e.dim.wid;
        }
 
        docstring const & label = par.params().labelWidthString();
@@ -843,11 +837,6 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
        // the width available for the row.
        int const width = max_width_ - row.right_margin;
 
-       if (pos >= end || row.width() > width) {
-               row.endpos(end);
-               return need_new_row;
-       }
-
 #if 0
        //FIXME: As long as leftMargin() is not correctly implemented for
        // MARGIN_RIGHT_ADDRESS_BOX, we should also not do this here.
@@ -866,10 +855,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
        // or the end of the par, then build a representation of the row.
        pos_type i = pos;
        FontIterator fi = FontIterator(*this, par, row.pit(), pos);
-       do {
-               // this can happen for an empty row after a newline
-               if (i >= end)
-                       break;
+       while (i < end && (i == pos || row.width() <= width)) {
                char_type c = par.getChar(i);
                // The most special cases are handled first.
                if (par.isInset(i)) {
@@ -951,7 +937,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
 
                ++i;
                ++fi;
-       } while (i < end && row.width() <= width);
+       }
        row.finalizeLast();
        row.endpos(i);
 
@@ -1053,8 +1039,8 @@ int TextMetrics::parTopSpacing(pit_type const pit) const
        if (prev != pit_type(pars.size())) {
                asc += int(pars[prev].layout().parsep * dh);
        } else if (pit != 0) {
-               Paragraph const & prevpar = pars[pit - 1];
-               if (prevpar.getDepth() != 0 || prevpar.layout() == layout)
+               Paragraph const & prevpar2 = pars[pit - 1];
+               if (prevpar2.getDepth() != 0 || prevpar2.layout() == layout)
                        asc += int(layout.parsep * dh);
        }
 
@@ -1107,16 +1093,14 @@ void TextMetrics::setRowHeight(Row & row) const
        int maxdes = int(fm.maxDescent() * spacing_val);
 
        // Find the ascent/descent of the row contents
-       Row::const_iterator cit = row.begin();
-       Row::const_iterator cend = row.end();
-       for ( ; cit != cend; ++cit) {
-               if (cit->inset) {
-                       maxasc = max(maxasc, cit->dim.ascent());
-                       maxdes = max(maxdes, cit->dim.descent());
+       for (Row::Element const & e : row) {
+               if (e.inset) {
+                       maxasc = max(maxasc, e.dim.ascent());
+                       maxdes = max(maxdes, e.dim.descent());
                } else {
-                       FontMetrics const & fm = theFontMetrics(cit->font);
-                       maxasc = max(maxasc, int(fm.maxAscent() * spacing_val));
-                       maxdes = max(maxdes, int(fm.maxDescent() * spacing_val));
+                       FontMetrics const & fm2 = theFontMetrics(e.font);
+                       maxasc = max(maxasc, int(fm2.maxAscent() * spacing_val));
+                       maxdes = max(maxdes, int(fm2.maxDescent() * spacing_val));
                }
        }
 
@@ -1302,9 +1286,9 @@ pit_type TextMetrics::getPitNearY(int y)
                LYXERR(Debug::DEBUG, "examining: pit: " << it->first
                        << " y: " << it->second.position());
 
-               ParagraphMetrics const & pm = par_metrics_[it->first];
+               ParagraphMetrics const & pm2 = par_metrics_[it->first];
 
-               if (it->first >= pit && int(it->second.position()) - int(pm.ascent()) <= y) {
+               if (it->first >= pit && int(it->second.position()) - int(pm2.ascent()) <= y) {
                        pit = it->first;
                        yy = it->second.position();
                }
@@ -1375,9 +1359,9 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        cur.pit() = pit;
 
        // Do we cover an inset?
-       InsetList::InsetTable * it = checkInsetHit(pit, x, y);
+       InsetList::Element * e = checkInsetHit(pit, x, y);
 
-       if (!it) {
+       if (!e) {
                // No inset, set position in the text
                bool bound = false; // is modified by getPosNearX
                cur.pos() = getPosNearX(row, x, bound);
@@ -1387,11 +1371,11 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
                return 0;
        }
 
-       Inset * inset = it->inset;
+       Inset * inset = e->inset;
        //lyxerr << "inset " << inset << " hit at x: " << x << " y: " << y << endl;
 
        // Set position in front of inset
-       cur.pos() = it->pos;
+       cur.pos() = e->pos;
        cur.boundary(false);
        cur.setTargetX(x);
 
@@ -1399,7 +1383,7 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        Inset * edited = inset->editXY(cur, x, y);
        // FIXME: it is not clear that the test on position is needed
        // Remove it if/when semantics of editXY is clarified
-       if (cur.text() == text_ && cur.pos() == it->pos) {
+       if (cur.text() == text_ && cur.pos() == e->pos) {
                // non-editable inset, set cursor after the inset if x is
                // nearer to that position (bug 9628)
                bool bound = false; // is modified by getPosNearX
@@ -1453,19 +1437,19 @@ void TextMetrics::setCursorFromCoordinates(Cursor & cur, int const x, int const
 
 
 //takes screen x,y coordinates
-InsetList::InsetTable * TextMetrics::checkInsetHit(pit_type pit, int x, int y)
+InsetList::Element * TextMetrics::checkInsetHit(pit_type pit, int x, int y)
 {
        Paragraph const & par = text_->paragraphs()[pit];
        CoordCache::Insets const & insetCache = bv_->coordCache().getInsets();
 
        LYXERR(Debug::DEBUG, "x: " << x << " y: " << y << "  pit: " << pit);
 
-       for (auto const & it : par.insetList()) {
-               LYXERR(Debug::DEBUG, "examining inset " << it.inset);
+       for (InsetList::Element const & e : par.insetList()) {
+               LYXERR(Debug::DEBUG, "examining inset " << e.inset);
 
-               if (insetCache.covers(it.inset, x, y)) {
-                       LYXERR(Debug::DEBUG, "Hit inset: " << it.inset);
-                       return const_cast<InsetList::InsetTable *>(&it);
+               if (insetCache.covers(e.inset, x, y)) {
+                       LYXERR(Debug::DEBUG, "Hit inset: " << e.inset);
+                       return const_cast<InsetList::Element *>(&e);
                }
        }
 
@@ -1479,12 +1463,12 @@ Inset * TextMetrics::checkInsetHit(int x, int y)
 {
        pit_type const pit = getPitNearY(y);
        LASSERT(pit != -1, return 0);
-       InsetList::InsetTable * it = checkInsetHit(pit, x, y);
+       InsetList::Element * e = checkInsetHit(pit, x, y);
 
-       if (!it)
+       if (!e)
                return 0;
 
-       return it->inset;
+       return e->inset;
 }
 
 
@@ -1509,14 +1493,14 @@ int TextMetrics::cursorX(CursorSlice const & sl,
 int TextMetrics::cursorY(CursorSlice const & sl, bool boundary) const
 {
        //lyxerr << "TextMetrics::cursorY: boundary: " << boundary << endl;
-       ParagraphMetrics const & pm = par_metrics_[sl.pit()];
+       ParagraphMetrics const & pm = parMetrics(sl.pit());
        if (pm.rows().empty())
                return 0;
 
        int h = 0;
-       h -= par_metrics_[0].rows()[0].ascent();
+       h -= parMetrics(0).rows()[0].ascent();
        for (pit_type pit = 0; pit < sl.pit(); ++pit) {
-               h += par_metrics_[pit].height();
+               h += parMetrics(pit).height();
        }
        int pos = sl.pos();
        if (pos && boundary)
@@ -1580,7 +1564,7 @@ void TextMetrics::deleteLineForward(Cursor & cur)
                if (!cur.selection())
                        text_->deleteWordForward(cur);
                else
-                       cap::cutSelection(cur, true, false);
+                       cap::cutSelection(cur, false);
                cur.checkBufferStructure();
        }
 }
@@ -1625,10 +1609,10 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const
 
        int l_margin = 0;
 
-       if (text_->isMainText())
+       if (text_->isMainText()) {
                l_margin += bv_->leftMargin();
-
-       l_margin += bfm.signedWidth(tclass.leftmargin());
+               l_margin += bfm.signedWidth(tclass.leftmargin());
+       }
 
        int depth = par.getDepth();
        if (depth != 0) {
@@ -1736,12 +1720,10 @@ int TextMetrics::leftMargin(pit_type const pit, pos_type const pos) const
                // same time this function is used in redoParagraph to construct
                // the rows.
                ParagraphMetrics const & pm = par_metrics_[pit];
-               RowList::const_iterator rit = pm.rows().begin();
-               RowList::const_iterator end = pm.rows().end();
                int minfill = max_width_;
-               for ( ; rit != end; ++rit)
-                       if (rit->fill() < minfill)
-                               minfill = rit->fill();
+               for (row : pm.rows())
+                       if (row.fill() < minfill)
+                               minfill = row.fill();
                l_margin += bfm.signedWidth(layout.leftmargin);
                l_margin += minfill;
 #endif
@@ -1798,17 +1780,15 @@ void TextMetrics::draw(PainterInfo & pi, int x, int y) const
        origin_.x_ = x;
        origin_.y_ = y;
 
-       ParMetricsCache::iterator it = par_metrics_.begin();
-       ParMetricsCache::iterator const pm_end = par_metrics_.end();
-       y -= it->second.ascent();
-       for (; it != pm_end; ++it) {
-               ParagraphMetrics const & pmi = it->second;
-               y += pmi.ascent();
-               pit_type const pit = it->first;
+       y -= par_metrics_.begin()->second.ascent();
+       for (auto & pm_pair : par_metrics_) {
+               pit_type const pit = pm_pair.first;
+               ParagraphMetrics & pm = pm_pair.second;
+               y += pm.ascent();
                // Save the paragraph position in the cache.
-               it->second.setPosition(y);
+               pm.setPosition(y);
                drawParagraph(pi, pit, x, y);
-               y += pmi.descent();
+               y += pm.descent();
        }
 }
 
@@ -1885,21 +1865,20 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                if (selection)
                        row.setSelectionAndMargins(sel_beg_par, sel_end_par);
                else
-                       row.setSelection(-1, -1);
+                       row.clearSelectionAndMargins();
 
                // The row knows nothing about the paragraph, so we have to check
                // whether this row is the first or last and update the margins.
                if (row.selection()) {
                        if (row.sel_beg == 0)
-                               row.begin_margin_sel = sel_beg.pit() < pit;
+                               row.change(row.begin_margin_sel, sel_beg.pit() < pit);
                        if (row.sel_end == sel_end_par.lastpos())
-                               row.end_margin_sel = sel_end.pit() > pit;
+                               row.change(row.end_margin_sel, sel_end.pit() > pit);
                }
 
                // has row changed since last paint?
                bool row_has_changed = row.changed()
-                       || bv_->hadHorizScrollOffset(text_, pit, row.pos())
-                       || bv_->needRepaint(text_, row);
+                       || bv_->hadHorizScrollOffset(text_, pit, row.pos());
 
                // Take this opportunity to spellcheck the row contents.
                if (row_has_changed && pi.do_spellcheck && lyxrc.spellcheck_continuously) {
@@ -1925,8 +1904,16 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                        LYXERR(Debug::PAINTING, "Clear rect@("
                               << 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);
+                       // FIXME: this is a hack. We know that at least this
+                       // amount of pixels can be cleared on right and left.
+                       // Doing so gets rid of caret ghosts when the cursor is at
+                       // the begining/end of row. However, it will not work if
+                       // the caret has a ridiculous width like 6. (see ticket
+                       // #10797)
+                       pi.pain.fillRectangle(max(row_x, 0) - Inset::TEXT_TO_INSET_OFFSET,
+                                             y - row.ascent(),
+                                             width() + 2 * Inset::TEXT_TO_INSET_OFFSET,
+                                             row.height(), pi.background_color);
                }
 
                // Instrumentation for testing row cache (see also
@@ -1964,6 +1951,20 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                                      row_x + row.right_x() > bv_->workWidth());
                y += row.descent();
 
+#if 0
+               // This debug code shows on screen which rows are repainted.
+               // FIXME: since the updates related to caret blinking restrict
+               // the painter to a small rectangle, the numbers are not
+               // updated when this happens. Change the code in
+               // GuiWorkArea::Private::show/hideCaret if this is important.
+               static int count = 0;
+               ++count;
+               FontInfo fi(sane_font);
+               fi.setSize(FONT_SIZE_TINY);
+               fi.setColor(Color_red);
+               pi.pain.text(row_x, y, convert<docstring>(count), fi);
+#endif
+
                // Restore full_repaint status.
                pi.full_repaint = tmp;