Esempio n. 1
0
int KateLayoutCache::lastViewLine(int realLine)
{
  if (!m_renderer->view()->dynWordWrap()) return 0;

  KateLineLayoutPtr l = line(realLine);
  Q_ASSERT(l);
  return l->viewLineCount() - 1;
}
Esempio n. 2
0
/**
 * This returns the view line upon which realCursor is situated.
 * The view line is the number of lines in the view from the first line
 * The supplied cursor should be in real lines.
 */
int KateLayoutCache::viewLine(const KTextEditor::Cursor& realCursor)
{
  if (realCursor.column() <= 0 || realCursor.line() < 0) return 0;

  KateLineLayoutPtr thisLine = line(realCursor.line());

  for (int i = 0; i < thisLine->viewLineCount(); ++i) {
    const KateTextLayout& l = thisLine->viewLine(i);
    if (realCursor.column() >= l.startCol() && realCursor.column() < l.endCol())
      return i;
  }

  return thisLine->viewLineCount() - 1;
}
Esempio n. 3
0
KateLineLayoutPtr KateLayoutCache::line( int realLine, int virtualLine )
{
  if (m_lineLayouts.contains(realLine)) {
    KateLineLayoutPtr l = m_lineLayouts[realLine];
    
    // ensure line is OK
    Q_ASSERT (l->line() == realLine);
    Q_ASSERT (realLine < m_renderer->doc()->buffer().lines());

    if (virtualLine != -1)
      l->setVirtualLine(virtualLine);

    if (!l->isValid())
    {
      l->setUsePlainTextLine (acceptDirtyLayouts());
      l->textLine (!acceptDirtyLayouts());
      m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
    }
    else if (l->isLayoutDirty() && !acceptDirtyLayouts())
    {
      // reset textline
      l->setUsePlainTextLine (false);
      l->textLine (true);
      m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
    }

    Q_ASSERT(l->isValid() && (!l->isLayoutDirty() || acceptDirtyLayouts()));

    return l;
  }

  if (realLine < 0 || realLine >= m_renderer->doc()->lines())
    return KateLineLayoutPtr();

  KateLineLayoutPtr l(new KateLineLayout(*m_renderer));
  l->setLine(realLine, virtualLine);

  // Mark it dirty, because it may not have the syntax highlighting applied
  // mark this here, to allow layoutLine to use plainLines...
  if (acceptDirtyLayouts())
    l->setUsePlainTextLine (true);

  m_renderer->layoutLine(l, wrap() ? m_viewWidth : -1, enableLayoutCache);
  Q_ASSERT(l->isValid());

  if (acceptDirtyLayouts())
    l->setLayoutDirty (true);

  m_lineLayouts.insert(realLine, l);
  return l;
}
Esempio n. 4
0
void KateLayoutCache::updateViewCache(const KTextEditor::Cursor& startPos, int newViewLineCount, int viewLinesScrolled)
{
  //kDebug( 13033 ) << startPos << " nvlc " << newViewLineCount << " vls " << viewLinesScrolled;

  int oldViewLineCount = m_textLayouts.count();
  if (newViewLineCount == -1)
    newViewLineCount = oldViewLineCount;

  enableLayoutCache = true;

  int realLine;
  if (newViewLineCount == -1)
    realLine = m_renderer->folding().visibleLineToLine(m_renderer->folding().lineToVisibleLine(startPos.line()));
  else
    realLine = m_renderer->folding().visibleLineToLine(startPos.line());
  int _viewLine = 0;

  if (wrap()) {
    // TODO check these assumptions are ok... probably they don't give much speedup anyway?
    if (startPos == m_startPos && m_textLayouts.count()) {
      _viewLine = m_textLayouts.first().viewLine();

    } else if (viewLinesScrolled > 0 && viewLinesScrolled < m_textLayouts.count()) {
      _viewLine = m_textLayouts[viewLinesScrolled].viewLine();

    } else {
      KateLineLayoutPtr l = line(realLine);
      if (l) {
        Q_ASSERT(l->isValid());
        Q_ASSERT(l->length() >= startPos.column() || m_renderer->view()->wrapCursor());

        for (; _viewLine < l->viewLineCount(); ++_viewLine) {
          const KateTextLayout& t = l->viewLine(_viewLine);
          if (t.startCol() >= startPos.column() || _viewLine == l->viewLineCount() - 1)
            goto foundViewLine;
        }

        // FIXME FIXME need to calculate past-end-of-line position here...
        Q_ASSERT(false);

        foundViewLine:
        Q_ASSERT(true);
      }
    }
  }

  m_startPos = startPos;

  // Move the text layouts if we've just scrolled...
  if (viewLinesScrolled != 0) {
    // loop backwards if we've just scrolled up...
    bool forwards = viewLinesScrolled >= 0 ? true : false;
    for (int z = forwards ? 0 : m_textLayouts.count() - 1; forwards ? (z < m_textLayouts.count()) : (z >= 0); forwards ? z++ : z--) {
      int oldZ = z + viewLinesScrolled;
      if (oldZ >= 0 && oldZ < m_textLayouts.count())
        m_textLayouts[z] = m_textLayouts[oldZ];
    }
  }

  // Resize functionality
  if (newViewLineCount > oldViewLineCount) {
    m_textLayouts.reserve(newViewLineCount);

  } else if (newViewLineCount < oldViewLineCount) {
    /* FIXME reintroduce... check we're not missing any
    int lastLine = m_textLayouts[newSize - 1].line();
    for (int i = oldSize; i < newSize; i++) {
      const KateTextLayout& layout = m_textLayouts[i];
      if (layout.line() > lastLine && !layout.viewLine())
        layout.kateLineLayout()->layout()->setCacheEnabled(false);
    }*/
    m_textLayouts.resize(newViewLineCount);
  }

  KateLineLayoutPtr l = line(realLine);
  for (int i = 0; i < newViewLineCount; ++i) {
    if (!l) {
      if (i < m_textLayouts.count()) {
        if (m_textLayouts[i].isValid())
          m_textLayouts[i] = KateTextLayout::invalid();
      } else {
        m_textLayouts.append(KateTextLayout::invalid());
      }
      continue;
    }

    Q_ASSERT(l->isValid());
    Q_ASSERT(_viewLine < l->viewLineCount());

    if (i < m_textLayouts.count()) {
      bool dirty = false;
      if (m_textLayouts[i].line() != realLine || m_textLayouts[i].viewLine() != _viewLine || (!m_textLayouts[i].isValid() && l->viewLine(_viewLine).isValid()))
        dirty = true;
      m_textLayouts[i] = l->viewLine(_viewLine);
      if (dirty)
        m_textLayouts[i].setDirty(true);

    } else {
      m_textLayouts.append(l->viewLine(_viewLine));
    }

    //kDebug( 13033 ) << "Laid out line " << realLine << " (" << l << "), viewLine " << _viewLine << " (" << m_textLayouts[i].kateLineLayout().data() << ")";
    //m_textLayouts[i].debugOutput();

    _viewLine++;

    if (_viewLine > l->viewLineCount() - 1) {
      int virtualLine = l->virtualLine() + 1;
      realLine = m_renderer->folding().visibleLineToLine(virtualLine);
      _viewLine = 0;
      if (realLine < m_renderer->doc()->lines()) {
        l = line(realLine, virtualLine);
      } else {
        l = 0;
      }
    }
  }

  enableLayoutCache = false;
}
Esempio n. 5
0
/*
The ultimate line painting function.
Currently missing features:
- draw indent lines
*/
void KateRenderer::paintTextLine(QPainter& paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor* cursor)
{
  Q_ASSERT(range->isValid());

//   kDebug( 13033 )<<"KateRenderer::paintTextLine";

  // font data
  const QFontMetricsF &fm = config()->fontMetrics();

  int currentViewLine = -1;
  if (cursor && cursor->line() == range->line())
    currentViewLine = range->viewLineForColumn(cursor->column());

  paintTextLineBackground(paint, range, currentViewLine, xStart, xEnd);

  if (range->layout()) {
    bool drawSelection = m_view->selection() && showSelections() && m_view->selectionRange().overlapsLine(range->line());
    // Draw selection in block selecton mode. We need 2 kinds of selections that QTextLayout::draw can't render:
    //   - past-end-of-line selection and
    //   - 0-column-wide selection (used to indicate where text will be typed)
    if (drawSelection && m_view->blockSelection()) {
      int selectionStartColumn = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().start()));
      int selectionEndColumn   = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().end()));
      QBrush selectionBrush = config()->selectionColor();
      if (selectionStartColumn != selectionEndColumn) {
        KateTextLayout lastLine = range->viewLine(range->viewLineCount() - 1);
        if (selectionEndColumn > lastLine.startCol()) {
          int selectionStartX = (selectionStartColumn > lastLine.startCol()) ? cursorToX(lastLine, selectionStartColumn, true) : 0;
          int selectionEndX = cursorToX(lastLine, selectionEndColumn, true);
          paint.fillRect(QRect(selectionStartX - xStart, (int)lastLine.lineLayout().y(), selectionEndX - selectionStartX, lineHeight()), selectionBrush);
        }
      } else {
        const int selectStickWidth = 2;
        KateTextLayout selectionLine = range->viewLine(range->viewLineForColumn(selectionStartColumn));
        int selectionX = cursorToX(selectionLine, selectionStartColumn, true);
        paint.fillRect(QRect(selectionX - xStart, (int)selectionLine.lineLayout().y(), selectStickWidth, lineHeight()), selectionBrush);
      }
    }

    QVector<QTextLayout::FormatRange> additionalFormats;
    if (range->length() > 0) {
      // We may have changed the pen, be absolutely sure it gets set back to
      // normal foreground color before drawing text for text that does not
      // set the pen color
      paint.setPen(attribute(KTextEditor::HighlightInterface::dsNormal)->foreground().color());
      // Draw the text :)
      if (drawSelection) {
        // FIXME toVector() may be a performance issue
        additionalFormats = decorationsForLine(range->textLine(), range->line(), true).toVector();
        range->layout()->draw(&paint, QPoint(-xStart,0), additionalFormats);

      } else {
        range->layout()->draw(&paint, QPoint(-xStart,0));
      }
    }

    QBrush backgroundBrush;
    bool backgroundBrushSet = false;

    // Loop each individual line for additional text decoration etc.
    QListIterator<QTextLayout::FormatRange> it = range->layout()->additionalFormats();
    QVectorIterator<QTextLayout::FormatRange> it2 = additionalFormats;
    for (int i = 0; i < range->viewLineCount(); ++i) {
      KateTextLayout line = range->viewLine(i);

      // Determine the background to use, if any, for the end of this view line
      backgroundBrushSet = false;
      while (it2.hasNext()) {
        const QTextLayout::FormatRange& fr = it2.peekNext();
        if (fr.start > line.endCol())
          break;

        if (fr.start + fr.length > line.endCol()) {
          if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
            backgroundBrushSet = true;
            backgroundBrush = fr.format.background();
          }

          goto backgroundDetermined;
        }

        it2.next();
      }

      while (it.hasNext()) {
        const QTextLayout::FormatRange& fr = it.peekNext();
        if (fr.start > line.endCol())
          break;

        if (fr.start + fr.length > line.endCol()) {
          if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) {
            backgroundBrushSet = true;
            backgroundBrush = fr.format.background();
          }

          break;
        }

        it.next();
      }

      backgroundDetermined:

      // Draw selection or background color outside of areas where text is rendered
      if (!m_printerFriendly ) {
        bool draw = false;
        QBrush drawBrush;
        if (m_view->selection() && !m_view->blockSelection() && m_view->lineEndSelected(line.end(true))) {
          draw = true;
          drawBrush = config()->selectionColor();
        } else if (backgroundBrushSet && !m_view->blockSelection()) {
          draw = true;
          drawBrush = backgroundBrush;
        }

        if (draw) {
          int fillStartX = line.endX() - line.startX() + line.xOffset() - xStart;
          int fillStartY = lineHeight() * i;
          int width= xEnd - xStart - fillStartX;
          int height= lineHeight();

          // reverse X for right-aligned lines
          if (range->layout()->textOption().alignment() == Qt::AlignRight)
            fillStartX = 0;

          if (width > 0) {
            QRect area(fillStartX, fillStartY, width, height);
            paint.fillRect(area, drawBrush);
          }
        }
      }
      // Draw indent lines
      if (showIndentLines() && i == 0)
      {
        const qreal w = spaceWidth();
        const int lastIndentColumn = range->textLine()->indentDepth(m_tabWidth);

        for (int x = m_indentWidth; x < lastIndentColumn; x += m_indentWidth)
        {
          paintIndentMarker(paint, x * w + 1 - xStart, range->line());
        }
      }

      // draw an open box to mark non-breaking spaces
      const QString& text = range->textLine()->string();
      int y = lineHeight() * i + fm.ascent() - fm.strikeOutPos();
      int nbSpaceIndex = text.indexOf(nbSpaceChar, line.lineLayout().xToCursor(xStart));

      while (nbSpaceIndex != -1 && nbSpaceIndex < line.endCol()) {
        int x = line.lineLayout().cursorToX(nbSpaceIndex);
        if (x > xEnd)
          break;
        paintNonBreakSpace(paint, x - xStart, y);
        nbSpaceIndex = text.indexOf(nbSpaceChar, nbSpaceIndex + 1);
      }

      // draw tab stop indicators
      if (showTabs()) {
        int tabIndex = text.indexOf(tabChar, line.lineLayout().xToCursor(xStart));
        while (tabIndex != -1 && tabIndex < line.endCol()) {
          int x = line.lineLayout().cursorToX(tabIndex);
          if (x > xEnd)
            break;
          paintTabstop(paint, x - xStart + spaceWidth()/2.0, y);
          tabIndex = text.indexOf(tabChar, tabIndex + 1);
        }
      }

      // draw trailing spaces
      if (showTrailingSpaces()) {
        int spaceIndex = line.endCol() - 1;
        int trailingPos = range->textLine()->lastChar();
        if (trailingPos < 0)
          trailingPos = 0;
        if (spaceIndex >= trailingPos) {
          while (spaceIndex >= line.startCol() && text.at(spaceIndex).isSpace()) {
            if (text.at(spaceIndex) != '\t' || !showTabs())
              paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart + spaceWidth()/2.0, y);
            --spaceIndex;
          }
        }
      }
    }

    // draw word-wrap-honor-indent filling
    if ( (range->viewLineCount() > 1)  && range->shiftX() && (range->shiftX() > xStart) )
    {
      if (backgroundBrushSet)
        paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1),
          backgroundBrush);
      paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1),
        QBrush(config()->wordWrapMarkerColor(), Qt::Dense4Pattern));
    }

    // Draw caret
    if (drawCaret() && cursor && range->includesCursor(*cursor)) {
      int caretWidth, lineWidth = 2;
      QColor color;
      QTextLine line = range->layout()->lineForTextPosition(qMin(cursor->column(), range->length()));

      // Determine the caret's style
      caretStyles style = caretStyle();

      // Make the caret the desired width
      if (style == Line) {
        caretWidth = lineWidth;
      } else if (line.isValid() && cursor->column() < range->length()) {
        caretWidth = int(line.cursorToX(cursor->column() + 1) - line.cursorToX(cursor->column()));
        if (caretWidth < 0) {
          caretWidth = -caretWidth;
        }
      } else {
        caretWidth = spaceWidth();
      }

      // Determine the color
      if (m_caretOverrideColor.isValid()) {
        // Could actually use the real highlighting system for this...
        // would be slower, but more accurate for corner cases
        color = m_caretOverrideColor;
      } else {
        // search for the FormatRange that includes the cursor
        foreach (const QTextLayout::FormatRange &r, range->layout()->additionalFormats()) {
          if ((r.start <= cursor->column() ) && ( (r.start + r.length)  > cursor->column())) {
            // check for Qt::NoBrush, as the returned color is black() and no invalid QColor
            QBrush foregroundBrush = r.format.foreground();
            if (foregroundBrush != Qt::NoBrush) {
              color = r.format.foreground().color();
            }
            break;
          }
        }
        // still no color found, fall back to default style
        if (!color.isValid())
          color = attribute(KTextEditor::HighlightInterface::dsNormal)->foreground().color();
      }

      // Clip the caret - Qt's caret has a habit of intruding onto other lines.
      paint.save();
      paint.setClipRect(0, line.lineNumber() * lineHeight(), xEnd - xStart, lineHeight());
      switch(style) {
      case Line :
        paint.setPen(QPen(color, caretWidth));
        break;
      case Block :
        // use a gray caret so it's possible to see the character
        color.setAlpha(128);
        paint.setPen(QPen(color, caretWidth));
        break;
      case Underline :
        paint.setClipRect(0, lineHeight() - lineWidth, xEnd - xStart, lineWidth);
        break;
      case Half :
        color.setAlpha(128);
        paint.setPen(QPen(color, caretWidth));
        paint.setClipRect(0, lineHeight() / 2, xEnd - xStart, lineHeight() / 2);
        break;
      }

      if (cursor->column() <= range->length()) {
        range->layout()->drawCursor(&paint, QPoint(-xStart,0), cursor->column(), caretWidth);
      } else {
        // Off the end of the line... must be block mode. Draw the caret ourselves.
        const KateTextLayout& lastLine = range->viewLine(range->viewLineCount() - 1);
        int x = cursorToX(lastLine, KTextEditor::Cursor(range->line(), cursor->column()), true);
        if ((x >= xStart) && (x <= xEnd)) {
          paint.fillRect(x - xStart, (int)lastLine.lineLayout().y(), caretWidth, lineHeight(), color);
        }
      }

      paint.restore();
    }
Esempio n. 6
0
void KateRenderer::paintTextLineBackground(QPainter& paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd)
{
  if (isPrinterFriendly())
    return;

  // Normal background color
  QColor backgroundColor( config()->backgroundColor() );

  // paint the current line background if we're on the current line
  QColor currentLineColor = config()->highlightedLineColor();

  // Check for mark background
  int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0;

  // Retrieve marks for this line
  uint mrk = m_doc->mark( layout->line() );
  if (mrk)
  {
    for (uint bit = 0; bit < 32; bit++)
    {
      KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1<<bit);
      if (mrk & markType)
      {
        QColor markColor = config()->lineMarkerColor(markType);

        if (markColor.isValid()) {
          markCount++;
          markRed += markColor.red();
          markGreen += markColor.green();
          markBlue += markColor.blue();
        }
      }
    } // for
  } // Marks

  if (markCount) {
    markRed /= markCount;
    markGreen /= markCount;
    markBlue /= markCount;
    backgroundColor.setRgb(
      int((backgroundColor.red() * 0.9) + (markRed * 0.1)),
      int((backgroundColor.green() * 0.9) + (markGreen * 0.1)),
      int((backgroundColor.blue() * 0.9) + (markBlue * 0.1))
    );
  }

  // Draw line background
  paint.fillRect(0, 0, xEnd - xStart, lineHeight() * layout->viewLineCount(), backgroundColor);

  // paint the current line background if we're on the current line
  if (currentViewLine != -1) {
    if (markCount) {
      markRed /= markCount;
      markGreen /= markCount;
      markBlue /= markCount;
      currentLineColor.setRgb(
        int((currentLineColor.red() * 0.9) + (markRed * 0.1)),
        int((currentLineColor.green() * 0.9) + (markGreen * 0.1)),
        int((currentLineColor.blue() * 0.9) + (markBlue * 0.1))
      );
    }

    paint.fillRect(0, lineHeight() * currentViewLine, xEnd - xStart, lineHeight(), currentLineColor);
  }
}