]> git.lyx.org Git - lyx.git/blobdiff - src/TextMetrics.cpp
Fix coverity issues about exceptions
[lyx.git] / src / TextMetrics.cpp
index 66e100b2d90202684ed5317f2cdd3eaaf24be116..3dd052059b44e39d86c07b5eaba743f2631ed59e 100644 (file)
@@ -48,6 +48,7 @@
 #include "support/debug.h"
 #include "support/lassert.h"
 
+#include <stdlib.h>
 #include <cmath>
 
 using namespace std;
@@ -401,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();
@@ -434,7 +436,6 @@ bool TextMetrics::redoParagraph(pit_type const pit)
                }
        }
 
-       par.setBeginOfBody();
        pos_type first = 0;
        size_t row_index = 0;
        bool need_new_row = false;
@@ -481,6 +482,11 @@ bool TextMetrics::redoParagraph(pit_type const pit)
        if (text_->isMainText()) {
                if (pit == 0) {
                        pm.rows().front().dimension().asc += 20;
+                       /* coverity thinks that we should update pm.dim().asc
+                        * below, but all the rows heights are actually counted as
+                        * part of the paragraph metric descent see loop above).
+                        */
+                       // coverity[copy_paste_error]
                        pm.dim().des += 20;
                }
                ParagraphList const & pars = text_->paragraphs();
@@ -508,13 +514,7 @@ bool TextMetrics::redoParagraph(pit_type const pit)
 
 LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const
 {
-       Layout const & layout = par.layout();
-
-       LyXAlignment align;
-       if (par.params().align() == LYX_ALIGN_LAYOUT)
-               align = layout.align;
-       else
-               align = par.params().align();
+       LyXAlignment align = par.getAlign();
 
        // handle alignment inside tabular cells
        Inset const & owner = text_->inset();
@@ -561,10 +561,9 @@ LyXAlignment TextMetrics::getAlign(Paragraph const & par, Row const & row) const
                // 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)
+               if ((row.flushed() && !forced_block)
                    || !bv_->buffer().params().justification)
-                       align = text_->isRTL(par) ? LYX_ALIGN_RIGHT : LYX_ALIGN_LEFT;
+                       align = row.isRTL() ? LYX_ALIGN_RIGHT : LYX_ALIGN_LEFT;
        }
 
        return align;
@@ -619,18 +618,14 @@ void TextMetrics::computeRowMetrics(Row & row, int width) const
                // 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, then stretch the row
-                       if (ns) {
-                               row.setSeparatorExtraWidth(double(w) / ns);
-                               row.dimension().wid += w;
-                       } else if (text_->isRTL(par)) {
+               case LYX_ALIGN_BLOCK:
+                       // Expand expanding characters by a total of w
+                       if (!row.setExtraWidth(w) && row.isRTL()) {
+                               // Justification failed and the text is RTL: align to the right
                                row.left_margin += w;
                                row.dimension().wid += w;
                        }
                        break;
-               }
                case LYX_ALIGN_RIGHT:
                        row.left_margin += w;
                        row.dimension().wid += w;
@@ -649,6 +644,7 @@ void TextMetrics::computeRowMetrics(Row & row, int width) const
                return;
        }
 
+       // Case nh > 0. There are hfill separators.
        hfill = w / nh;
        hfill_rem = w % nh;
        row.dimension().wid += w;
@@ -861,7 +857,23 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
                } else if (c == '\t')
                        row.addSpace(i, theFontMetrics(*fi).width(from_ascii("    ")),
                                     *fi, par.lookupChange(i));
-               else {
+               else if (c == 0x2028 || c == 0x2029) {
+                       /**
+                        * U+2028 LINE SEPARATOR
+                        * U+2029 PARAGRAPH SEPARATOR
+
+                        * These are special unicode characters that break
+                        * lines/pragraphs. Not handling them lead to trouble wrt
+                        * Qt QTextLayout formatting. We add a visible character
+                        * on screen so that the user can see that something is
+                        * happening.
+                       */
+                       row.finalizeLast();
+                       // ⤶ U+2936 ARROW POINTING DOWNWARDS THEN CURVING LEFTWARDS
+                       // ¶ U+00B6 PILCROW SIGN
+                       char_type const screen_char = (c == 0x2028) ? 0x2936 : 0x00B6;
+                       row.add(i, screen_char, *fi, par.lookupChange(i));
+               } else {
                        // FIXME: please someone fix the Hebrew/Arabic parenthesis mess!
                        // see also Paragraph::getUChar.
                        if (fi->language()->lang() == "hebrew") {
@@ -898,7 +910,7 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
                        && inset->display())
                    || (!row.empty() && row.back().inset
                        && row.back().inset->display())) {
-                       row.right_boundary(true);
+                       row.flushed(true);
                        need_new_row = par.isNewline(i);
                        ++i;
                        break;
@@ -922,13 +934,21 @@ bool TextMetrics::breakRow(Row & row, int const right_margin) const
                BufferParams const & bparams
                        = text_->inset().buffer().params();
                f.setLanguage(par.getParLanguage(bparams));
+               // ¶ U+00B6 PILCROW SIGN
                row.addVirtual(end, docstring(1, char_type(0x00B6)), f, Change());
        }
 
        // 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(false);
+       int const next_width = max_width_ - leftMargin(max_width_, row.pit(), row.endpos())
+               - rightMargin(row.pit());
+
+       row.shortenIfNeeded(body_pos, width, next_width);
+       row.right_boundary(!row.empty() && row.endpos() < end
+                          && row.back().endpos == row.endpos());
+       // Last row in paragraph is flushed
+       if (row.endpos() == end)
+               row.flushed(true);
 
        // make sure that the RTL elements are in reverse ordering
        row.reverseRTL(is_rtl);
@@ -1074,7 +1094,7 @@ void TextMetrics::setRowHeight(Row & row) const
 // returns the column near the specified x-coordinate of the row
 // x is set to the real beginning of this column
 pos_type TextMetrics::getPosNearX(Row const & row, int & x,
-                                 bool & boundary) const
+                                  bool & boundary, bool const select) const
 {
        //LYXERR0("getPosNearX(" << x << ") row=" << row);
        /// For the main Text, it is possible that this pit is not
@@ -1104,7 +1124,7 @@ pos_type TextMetrics::getPosNearX(Row const & row, int & x,
                for ( ; cit != cend; ++cit) {
                        if (w <= x &&  w + cit->full_width() > x) {
                                int x_offset = int(x - w);
-                               pos = cit->x2pos(x_offset);
+                               pos = cit->x2pos(x_offset, select);
                                x = int(x_offset + w);
                                break;
                        }
@@ -1341,18 +1361,23 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
        if (edited == inset && cur.pos() == it->pos) {
                // non-editable inset, set cursor after the inset if x is
                // nearer to that position (bug 9628)
+               // TODO: This should be replaced with an improvement of
+               // Cursor::moveToClosestEdge that handles rtl text. (Which could not
+               // be tested because of #10569.)
                CoordCache::Insets const & insetCache = bv_->coordCache().getInsets();
-               Dimension const & dim = insetCache.dim(inset);
-               Point p = insetCache.xy(inset);
-               bool const is_rtl = text_->isRTL(text_->getPar(pit));
-               if (is_rtl) {
-                       // "in front of" == "right of"
-                       if (abs(p.x_ - x) < abs(p.x_ + dim.wid - x))
-                               cur.posForward();
-               } else {
-                       // "in front of" == "left of"
-                       if (abs(p.x_ + dim.wid - x) < abs(p.x_ - x))
-                               cur.posForward();
+               if (insetCache.has(inset)) {
+                       Dimension const & dim = insetCache.dim(inset);
+                       Point p = insetCache.xy(inset);
+                       bool const is_rtl = text_->isRTL(text_->getPar(pit));
+                       if (is_rtl) {
+                               // "in front of" == "right of"
+                               if (abs(p.x_ - x) < abs(p.x_ + dim.wid - x))
+                                       cur.posForward();
+                       } else {
+                               // "in front of" == "left of"
+                               if (abs(p.x_ + dim.wid - x) < abs(p.x_ - x))
+                                       cur.posForward();
+                       }
                }
        }
 
@@ -1362,7 +1387,8 @@ Inset * TextMetrics::editXY(Cursor & cur, int x, int y,
 }
 
 
-void TextMetrics::setCursorFromCoordinates(Cursor & cur, int const x, int const y)
+void TextMetrics::setCursorFromCoordinates(Cursor & cur, int const x,
+                                           int const y, bool const select)
 {
        LASSERT(text_ == cur.text(), return);
        pit_type const pit = getPitNearY(y);
@@ -1389,7 +1415,7 @@ void TextMetrics::setCursorFromCoordinates(Cursor & cur, int const x, int const
 
        bool bound = false;
        int xx = x;
-       pos_type const pos = getPosNearX(row, xx, bound);
+       pos_type const pos = getPosNearX(row, xx, bound, select);
 
        LYXERR(Debug::DEBUG, "setting cursor pit: " << pit << " pos: " << pos);
 
@@ -1407,28 +1433,12 @@ InsetList::InsetTable * TextMetrics::checkInsetHit(pit_type pit, int x, int y)
 
        LYXERR(Debug::DEBUG, "x: " << x << " y: " << y << "  pit: " << pit);
 
-       InsetList::const_iterator iit = par.insetList().begin();
-       InsetList::const_iterator iend = par.insetList().end();
-       for (; iit != iend; ++iit) {
-               Inset * inset = iit->inset;
-
-               LYXERR(Debug::DEBUG, "examining inset " << inset);
+       for (auto const & it : par.insetList()) {
+               LYXERR(Debug::DEBUG, "examining inset " << it.inset);
 
-               if (!insetCache.has(inset)) {
-                       LYXERR(Debug::DEBUG, "inset has no cached position");
-                       return 0;
-               }
-
-               Dimension const & dim = insetCache.dim(inset);
-               Point p = insetCache.xy(inset);
-
-               LYXERR(Debug::DEBUG, "xo: " << p.x_ << "..." << p.x_ + dim.wid
-                       << " yo: " << p.y_ - dim.asc << "..." << p.y_ + dim.des);
-
-               if (x >= p.x_ && x <= p.x_ + dim.wid
-                   && y >= p.y_ - dim.asc && y <= p.y_ + dim.des) {
-                       LYXERR(Debug::DEBUG, "Hit inset: " << inset);
-                       return const_cast<InsetList::InsetTable *>(&(*iit));
+               if (insetCache.covers(it.inset, x, y)) {
+                       LYXERR(Debug::DEBUG, "Hit inset: " << it.inset);
+                       return const_cast<InsetList::InsetTable *>(&it);
                }
        }
 
@@ -1451,56 +1461,6 @@ Inset * TextMetrics::checkInsetHit(int x, int y)
 }
 
 
-Row::const_iterator const
-TextMetrics::findRowElement(Row const & row, pos_type const pos,
-                            bool const boundary, double & x) const
-{
-       /**
-        * When boundary is true, position i is in the row element (pos, endpos)
-        * if
-        *    pos < i <= endpos
-        * whereas, when boundary is false, the test is
-        *    pos <= i < endpos
-        * The correction below allows to handle both cases.
-       */
-       int const boundary_corr = (boundary && pos) ? -1 : 0;
-
-       x = row.left_margin;
-
-       /** Early return in trivial cases
-        * 1) the row is empty
-        * 2) the position is the left-most position of the row; there
-        * is a quirk here however: if the first element is virtual
-        * (end-of-par marker for example), then we have to look
-        * closer
-        */
-       if (row.empty()
-           || (pos == row.begin()->left_pos() && !boundary
-                       && !row.begin()->isVirtual()))
-               return row.begin();
-
-       Row::const_iterator cit = row.begin();
-       for ( ; cit != row.end() ; ++cit) {
-               /** Look whether the cursor is inside the element's
-                * span. Note that it is necessary to take the
-                * boundary into account, and to accept virtual
-                * elements, which have pos == endpos.
-                */
-               if (pos + boundary_corr >= cit->pos
-                   && (pos + boundary_corr < cit->endpos || cit->isVirtual())) {
-                               x += cit->pos2x(pos);
-                               break;
-               }
-               x += cit->full_width();
-       }
-
-       if (cit == row.end())
-               --cit;
-
-       return cit;
-}
-
-
 int TextMetrics::cursorX(CursorSlice const & sl,
                bool boundary) const
 {
@@ -1513,7 +1473,7 @@ int TextMetrics::cursorX(CursorSlice const & sl,
        pos_type const pos = sl.pos();
 
        double x = 0;
-       findRowElement(row, pos, boundary, x);
+       row.findElement(pos, boundary, x);
        return int(x);
 
 }
@@ -1768,12 +1728,7 @@ int TextMetrics::leftMargin(int max_width,
        if (!par.params().leftIndent().zero())
                l_margin += par.params().leftIndent().inPixels(max_width, lfm.em());
 
-       LyXAlignment align;
-
-       if (par.params().align() == LYX_ALIGN_LAYOUT)
-               align = layout.align;
-       else
-               align = par.params().align();
+       LyXAlignment align = par.getAlign();
 
        // set the correct parindent
        if (pos == 0
@@ -1973,15 +1928,14 @@ void TextMetrics::drawParagraph(PainterInfo & pi, pit_type const pit, int const
                rp.paintAppendix();
                rp.paintDepthBar();
                rp.paintChangeBar();
-               bool const is_rtl = text_->isRTL(text_->getPar(pit));
-               if (i == 0 && !is_rtl)
+               if (i == 0 && !row.isRTL())
                        rp.paintFirst();
-               if (i == nrows - 1 && is_rtl)
+               if (i == nrows - 1 && row.isRTL())
                        rp.paintLast();
                rp.paintText();
-               if (i == nrows - 1 && !is_rtl)
+               if (i == nrows - 1 && !row.isRTL())
                        rp.paintLast();
-               if (i == 0 && is_rtl)
+               if (i == 0 && row.isRTL())
                        rp.paintFirst();
                rp.paintTooLargeMarks(row_x + row.left_x() < 0,
                                      row_x + row.right_x() > bv_->workWidth());