/* This file is part of the KDE libraries Copyright (C) 2008, 2009 Matthew Woehlke Copyright (C) 2007 Mirko Stocker Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Anders Lund Copyright (C) 2001 Christoph Cullmann Copyright (C) 2011 Svyatoslav Kuzmich Copyright (C) 2012 Kåre Särs (Minimap) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateviewhelpers.h" #include "katecmd.h" #include #include #include #include #include #include "kateconfig.h" #include "katedocument.h" #include #include "katerenderer.h" #include "kateview.h" #include "kateviewinternal.h" #include "katelayoutcache.h" #include "katetextlayout.h" #include "kateglobal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "ktoolinvocation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //BEGIN KateScrollBar static const int s_lineWidth = 100; static const int s_pixelMargin = 8; static const int s_linePixelIncLimit = 6; static const unsigned char s_characterOpacity[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 15 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, // <- 31 0, 125, 41, 221, 138, 195, 218, 21, 142, 142, 137, 137, 97, 87, 87, 140, // <- 47 223, 164, 183, 190, 191, 193, 214, 158, 227, 216, 103, 113, 146, 140, 146, 149, // <- 63 248, 204, 240, 174, 217, 197, 178, 205, 209, 176, 168, 211, 160, 246, 238, 218, // <- 79 195, 229, 227, 196, 167, 212, 188, 238, 197, 169, 189, 158, 21, 151, 115, 90, // <- 95 15, 192, 209, 153, 208, 187, 162, 221, 183, 149, 161, 191, 146, 203, 167, 182, // <- 111 208, 203, 139, 166, 158, 167, 157, 189, 164, 179, 156, 167, 145, 166, 109, 0, // <- 127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 159 0, 125, 184, 187, 146, 201, 127, 203, 89, 194, 156, 141, 117, 87, 202, 88, // <- 175 115, 165, 118, 121, 85, 190, 236, 87, 88, 111, 151, 140, 194, 191, 203, 148, // <- 191 215, 215, 222, 224, 223, 234, 230, 192, 208, 208, 216, 217, 187, 187, 194, 195, // <- 207 228, 255, 228, 228, 235, 239, 237, 150, 255, 222, 222, 229, 232, 180, 197, 225, // <- 223 208, 208, 216, 217, 212, 230, 218, 170, 202, 202, 211, 204, 156, 156, 165, 159, // <- 239 214, 194, 197, 197, 206, 206, 201, 132, 214, 183, 183, 192, 187, 195, 227, 198 }; KateScrollBar::KateScrollBar (Qt::Orientation orientation, KateViewInternal* parent) : QScrollBar (orientation, parent->m_view) , m_middleMouseDown (false) , m_leftMouseDown (false) , m_view(parent->m_view) , m_doc(parent->doc()) , m_viewInternal(parent) , m_showMarks(false) , m_showMiniMap(false) , m_miniMapAll(true) , m_miniMapWidth(40) , m_grooveHeight(height()) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(sliderMaybeMoved(int))); connect(m_doc, SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(marksChanged())); styleChange(*style()); m_updateTimer.setInterval(300); m_updateTimer.setSingleShot(true); QTimer::singleShot(10, this, SLOT(updatePixmap())); } void KateScrollBar::setShowMiniMap(bool b) { if (b && !m_showMiniMap) { connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(m_doc, SIGNAL(textChanged(KTextEditor::Document*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(m_view, SIGNAL(delayedUpdateOfView()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updatePixmap()), Qt::UniqueConnection); connect(&(m_view->textFolding()), SIGNAL(foldingRangesChanged()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); } else if (!b) { disconnect(&m_updateTimer); } m_showMiniMap = b; updateGeometry(); update(); } QSize KateScrollBar::sizeHint() const { if (m_showMiniMap) { return QSize(m_miniMapWidth, QScrollBar::sizeHint().height()); } return QScrollBar::sizeHint(); } int KateScrollBar::minimapYToStdY(int y) { // Check if the minimap fills the whole scrollbar if (m_stdGroveRect.height() == m_mapGroveRect.height()){ return y; } // check if y is on the step up/down if ((y < m_stdGroveRect.top()) || (y > m_stdGroveRect.bottom())) { return y; } if (y < m_mapGroveRect.top()) { return m_stdGroveRect.top() + 1; } if (y > m_mapGroveRect.bottom()) { return m_stdGroveRect.bottom() - 1; } // check for div/0 if (m_mapGroveRect.height() == 0) { return y; } int newY = (y - m_mapGroveRect.top()) * m_stdGroveRect.height() / m_mapGroveRect.height(); newY += m_stdGroveRect.top(); return newY; } void KateScrollBar::mousePressEvent(QMouseEvent* e) { if (m_showMiniMap) { QMouseEvent eMod(QEvent::MouseButtonPress, QPoint(6, minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mousePressEvent(&eMod); } else { QScrollBar::mousePressEvent(e); } if (e->button() == Qt::MiddleButton) m_middleMouseDown = true; else if (e->button() == Qt::LeftButton) m_leftMouseDown = true; m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); redrawMarks(); } void KateScrollBar::mouseReleaseEvent(QMouseEvent* e) { if (e->button() == Qt::MiddleButton) m_middleMouseDown = false; else if (e->button() == Qt::LeftButton) m_leftMouseDown = false; redrawMarks(); if (m_leftMouseDown || m_middleMouseDown) { QToolTip::hideText(); } if (m_showMiniMap) { QMouseEvent eMod(QEvent::MouseButtonRelease, QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mouseReleaseEvent(&eMod); } else { QScrollBar::mouseReleaseEvent(e); } } void KateScrollBar::mouseMoveEvent(QMouseEvent* e) { if (m_showMiniMap) { QMouseEvent eMod(QEvent::MouseMove, QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mouseMoveEvent(&eMod); } else { QScrollBar::mouseMoveEvent(e); } if (e->buttons() & (Qt::LeftButton | Qt::MiddleButton)) { redrawMarks(); // current line tool tip m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); } } void KateScrollBar::paintEvent(QPaintEvent *e) { if (m_doc->marks().size() != m_lines.size()) { recomputeMarksPositions(); } if (m_showMiniMap) { miniMapPaintEvent(e); } else { normalPaintEvent(e); } } // This function is optimized for bing called in sequence. const QColor KateScrollBar::charColor(const QVector &attributes, int &attributeIndex, const QList &decorations, const QColor &defaultColor, int x, QChar ch) { QColor color = defaultColor; bool styleFound = false; // Query the decorations, that is, things like search highlighting, or the // KDevelop DUChain highlighting, for a color to use foreach (const QTextLayout::FormatRange& range, decorations) { if (range.start <= x && range.start + range.length > x) { // If there's a different background color set (search markers, ...) // use that, otherwise use the foreground color. if ( range.format.hasProperty(QTextFormat::BackgroundBrush) ) { color = range.format.background().color(); } else { color = range.format.foreground().color(); } styleFound = true; break; } } // If there's no decoration set for the current character (this will mostly be the case for // plain Kate), query the styles, that is, the default kate syntax highlighting. if (!styleFound) { // go to the block containing x while ((attributeIndex < attributes.size()) && ((attributes[attributeIndex].offset + attributes[attributeIndex].length) < x)) { ++attributeIndex; } if ((attributeIndex < attributes.size()) && (x < attributes[attributeIndex].offset + attributes[attributeIndex].length)) { color = m_view->renderer()->attribute(attributes[attributeIndex].attributeValue)->foreground().color(); } } // Query how much "blackness" the character has. // This causes for example a dot or a dash to appear less intense // than an A or similar. // This gives the pixels created a bit of structure, which makes it look more // like real text. color.setAlpha((ch.unicode() < 256) ? s_characterOpacity[ch.unicode()] : 1.0); return color; } void KateScrollBar::updatePixmap() { //QTime time; //time.start(); if (!m_showMiniMap) { // make sure no time is wasted if the option is disabled return; } // For performance reason, only every n-th line will be drawn if the widget is // sufficiently small compared to the amount of lines in the document. int docLineCount = m_view->textFolding().visibleLines(); int pixmapLineCount = docLineCount; if (m_view->config()->scrollPastEnd()) { pixmapLineCount += pageStep(); } int pixmapLinesUnscaled = pixmapLineCount; if (m_grooveHeight < 5) m_grooveHeight = 5; int lineDivisor = pixmapLinesUnscaled/m_grooveHeight; if (lineDivisor < 1) lineDivisor = 1; int charIncrement = 1; int lineIncrement = 1; if ( (m_grooveHeight > 10) && (pixmapLineCount >= m_grooveHeight*2) ) { charIncrement = pixmapLineCount / m_grooveHeight; while (charIncrement > s_linePixelIncLimit) { lineIncrement++; pixmapLineCount = pixmapLinesUnscaled/lineIncrement; charIncrement = pixmapLineCount / m_grooveHeight; } pixmapLineCount /= charIncrement; } int pixmapLineWidth = s_pixelMargin + s_lineWidth/charIncrement; //kDebug(13040) << "l" << lineIncrement << "c" << charIncrement << "d" << lineDivisor; //kDebug(13040) << "pixmap" << pixmapLineCount << pixmapLineWidth << "docLines" << m_view->textFolding().visibleLines() << "height" << m_grooveHeight; QColor backgroundColor; QColor defaultTextColor; if (m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)) { backgroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->background().color(); defaultTextColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->foreground().color(); } else { backgroundColor = palette().color(QPalette::Base); defaultTextColor = palette().color(QPalette::Text); } QColor modifiedLineColor = m_view->renderer()->config()->modifiedLineColor(); QColor savedLineColor = m_view->renderer()->config()->savedLineColor(); // move the modified line color away from the background color modifiedLineColor.setHsv(modifiedLineColor.hue(), 255, 255 - backgroundColor.value()/3); savedLineColor.setHsv(savedLineColor.hue(), 100, 255 - backgroundColor.value()/3); m_pixmap = QPixmap(pixmapLineWidth, pixmapLineCount); m_pixmap.fill(QColor("transparent")); QPainter painter; if ( painter.begin(&m_pixmap) ) { // Do not force updates of the highlighting if the document is very large bool simpleMode = m_doc->lines() > 7500; int pixelY = 0; int drawnLines = 0; // Iterate over all visible lines, drawing them. for (int virtualLine=0; virtualLine < docLineCount; virtualLine += lineIncrement) { int realLineNumber = m_view->textFolding().visibleLineToLine(virtualLine); QString lineText = m_doc->line(realLineNumber); // use this to control the offset of the text from the left int pixelX = s_pixelMargin; if (!simpleMode) { m_doc->buffer().ensureHighlighted(realLineNumber); } const Kate::TextLine& kateline = m_doc->plainKateTextLine(realLineNumber); const QVector &attributes = kateline->attributesList(); QList< QTextLayout::FormatRange > decorations = m_view->renderer()->decorationsForLine(kateline, realLineNumber); int attributeIndex = 0; // The color to draw the currently selected text in; change the alpha value to make it // more or less intense QColor selectionColor = palette().color(QPalette::HighlightedText); selectionColor.setAlpha(180); painter.setPen(defaultTextColor); // Iterate over all the characters in the current line for (int x = 0; (x < lineText.size() && x < s_lineWidth); x += charIncrement) { if (pixelX >= s_lineWidth + s_pixelMargin) { break; } // draw the pixels if (lineText[x] == ' ') { pixelX++; } else if (lineText[x] == '\t') { pixelX += qMax(4/charIncrement, 1); // FIXME: tab width... } else { painter.setPen(charColor(attributes, attributeIndex, decorations, defaultTextColor, x, lineText[x])); // Actually draw the pixel with the color queried from the renderer. painter.drawPoint(pixelX, pixelY); pixelX++; } } drawnLines++; if (((drawnLines) % charIncrement) == 0) { pixelY++; } } //kDebug(13040) << drawnLines; // Draw line modification marker map. // Disable this if the document is really huge, // since it requires querying every line. if ( m_doc->lines() < 50000 ) { for ( int lineno = 0; lineno < docLineCount; lineno++ ) { int realLineNo = m_view->textFolding().visibleLineToLine(lineno); const Kate::TextLine& line = m_doc->plainKateTextLine(realLineNo); if ( line->markedAsModified() ) { painter.setPen(modifiedLineColor); } else if ( line->markedAsSavedOnDisk() ) { painter.setPen(savedLineColor); } else { continue; } painter.drawRect(2, lineno/lineDivisor, 3, 1); } } } //kDebug(13040) << time.elapsed(); // Redraw the scrollbar widget with the updated pixmap. update(); } void KateScrollBar::miniMapPaintEvent(QPaintEvent *e) { QScrollBar::paintEvent(e); QPainter painter(this); QStyleOptionSlider opt; opt.init(this); opt.subControls = QStyle::SC_None; opt.activeSubControls = QStyle::SC_None; opt.orientation = orientation(); opt.minimum = minimum(); opt.maximum = maximum(); opt.sliderPosition = sliderPosition(); opt.sliderValue = value(); opt.singleStep = singleStep(); opt.pageStep = pageStep(); int docXMargin = 1; QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); m_stdGroveRect = grooveRect; if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSubLine, this).height() == 0) { int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); grooveRect.moveTop(alignMargin); grooveRect.setHeight(grooveRect.height() - alignMargin); } if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarAddLine, this).height() == 0) { int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); grooveRect.setHeight(grooveRect.height() - alignMargin); } m_grooveHeight = grooveRect.height(); QRect sliderRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); m_stdSliderRect = sliderRect; sliderRect.adjust(docXMargin+1, 1, -(docXMargin+1), -1); //style()->drawControl(QStyle::CE_ScrollBarAddLine, &opt, &painter, this); //style()->drawControl(QStyle::CE_ScrollBarSubLine, &opt, &painter, this); // calculate the document size and position int docHeight = qMin(grooveRect.height(), m_pixmap.height()*2) - 2*docXMargin; int yoffset = 1; // top-aligned in stead of center-aligned (grooveRect.height() - docHeight) / 2; QRect docRect(QPoint(grooveRect.left()+docXMargin, yoffset+grooveRect.top()), QSize(grooveRect.width()-2*docXMargin, docHeight)); m_mapGroveRect = docRect; // calculate the visible area int max = qMax(maximum()+1, 1); int visibleStart = value()*docHeight/(max+pageStep()) + docRect.top(); int visibleEnd = (value()+pageStep())*docHeight/(max+pageStep()) + docRect.top(); QRect visibleRect = docRect; visibleRect.moveTop(visibleStart); visibleRect.setHeight(visibleEnd-visibleStart); m_mapSliderRect = visibleRect; // calculate colors QColor backgroundColor; QColor foregroundColor; // TODO KDE5: If HighlightInterface is a View interface, use HighlightInterface::defaultStyle() again. if (m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)) { backgroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->background().color(); foregroundColor = m_doc->defaultStyle(KTextEditor::HighlightInterface::dsNormal)->foreground().color(); } else { backgroundColor = palette().color(QPalette::Base); foregroundColor = palette().color(QPalette::Text); } qreal backgroundLightness = KColorUtils::luma(backgroundColor); qreal foregroundLightness = KColorUtils::luma(foregroundColor); qreal lighnessDiff = (foregroundLightness - backgroundLightness); // get a color suited for the color theme QColor darkShieldColorBase = palette().color(QPalette::Mid); // apply suitable lightness QColor darkShieldColor = KColorUtils::lighten(darkShieldColorBase, lighnessDiff * 0.35); // gradient for nicer results QLinearGradient gradient(0, 0, width(), 0); gradient.setColorAt(0, darkShieldColor); gradient.setColorAt(0.3, darkShieldColor.lighter(115)); gradient.setColorAt(1, darkShieldColor); QColor lightShieldColor = KColorUtils::lighten(darkShieldColorBase, lighnessDiff * 0.15); QColor outlineColor = KColorUtils::lighten(darkShieldColorBase, lighnessDiff * 0.5); // draw the grove background in case the document is small painter.setPen(QPen(QColor("transparent"),0)); painter.setBrush(palette().brush(QPalette::Dark)); painter.drawRect(grooveRect); // dark "shield" of non-slider parts painter.setBrush(gradient); painter.drawRect(docRect); // light "shield" non-visible parts if ((docHeight+2*docXMargin >= grooveRect.height()) && (sliderRect.height() > visibleRect.height()+2)) { // slider painter.setPen(QPen(QColor("transparent"),0)); painter.setBrush(lightShieldColor); sliderRect.adjust(1,0,-1,0); painter.drawRect(sliderRect); // visible area visibleRect.adjust(2,0,-3,0); painter.setPen(QPen(backgroundColor,1)); painter.setBrush(backgroundColor); painter.drawRect(visibleRect); // slider outline painter.setRenderHint(QPainter::Antialiasing); painter.setPen(QPen(outlineColor, 2)); painter.setBrush(QColor("transparent")); sliderRect.adjust(-1,0,1,0); painter.drawRoundedRect(sliderRect, 4, 4); painter.setRenderHint(QPainter::Antialiasing, false); } else { // visible area with outline painter.setRenderHint(QPainter::Antialiasing); painter.setPen(QPen(outlineColor, 2)); painter.setBrush(backgroundColor); visibleRect.adjust(1,0,-1,2); painter.drawRoundedRect(visibleRect, 4, 4); painter.setRenderHint(QPainter::Antialiasing, false); } // Smooth transform only when squeezing if (grooveRect.height() < m_pixmap.height()) { painter.setRenderHint(QPainter::SmoothPixmapTransform); } // draw the modified lines margin QRect pixmapMarginRect(QPoint(0, 0), QSize(s_pixelMargin, m_pixmap.height())); QRect docPixmapMarginRect(QPoint(0, docRect.top()), QSize(s_pixelMargin, docRect.height())); painter.drawPixmap(docPixmapMarginRect, m_pixmap, pixmapMarginRect); // calculate the stretch and draw the stretched lines QRect pixmapRect(QPoint(s_pixelMargin, 0), QSize(m_pixmap.width() - s_pixelMargin, m_pixmap.height())); QRect docPixmapRect(QPoint(s_pixelMargin, docRect.top()), QSize(docRect.width()-s_pixelMargin, docRect.height())); painter.drawPixmap(docPixmapRect, m_pixmap, pixmapRect); if (!m_showMarks) return; QHashIterator it = m_lines; QPen penBg; penBg.setWidth(4); lightShieldColor.setAlpha(180); penBg.setColor(lightShieldColor); painter.setPen(penBg); while (it.hasNext()) { it.next(); int y = (it.key()-grooveRect.top()) * docHeight/grooveRect.height() + docRect.top();; painter.drawLine(6, y, width()-6, y); } it = m_lines; QPen pen; pen.setWidth(2); while (it.hasNext()) { it.next(); pen.setColor(it.value()); painter.setPen(pen); int y = (it.key()-grooveRect.top()) * docHeight/grooveRect.height() + docRect.top();; painter.drawLine(6, y, width()-6, y); } } void KateScrollBar::normalPaintEvent(QPaintEvent *e) { QScrollBar::paintEvent(e); if (!m_showMarks) return; QPainter painter(this); QStyleOptionSlider opt; opt.init(this); opt.subControls = QStyle::SC_None; opt.activeSubControls = QStyle::SC_None; opt.orientation = orientation(); opt.minimum = minimum(); opt.maximum = maximum(); opt.sliderPosition = sliderPosition(); opt.sliderValue = value(); opt.singleStep = singleStep(); opt.pageStep = pageStep(); QRect rect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); int sideMargin = width() - rect.width(); if (sideMargin < 4) sideMargin = 4; sideMargin /= 2; QHashIterator it = m_lines; while (it.hasNext()) { it.next(); painter.setPen(it.value()); if (it.key() < rect.top() || it.key() > rect.bottom()) { painter.drawLine(0, it.key(), width(), it.key()); } else { painter.drawLine(0, it.key(), sideMargin, it.key()); painter.drawLine(width()-sideMargin, it.key(), width(), it.key()); } } } void KateScrollBar::resizeEvent(QResizeEvent *e) { QScrollBar::resizeEvent(e); m_updateTimer.start(); m_lines.clear(); update(); } void KateScrollBar::styleChange(QStyle &s) { QScrollBar::styleChange(s); m_lines.clear(); update(); } void KateScrollBar::sliderChange ( SliderChange change ) { // call parents implementation QScrollBar::sliderChange (change); if (change == QAbstractSlider::SliderValueChange) { redrawMarks(); } else if (change == QAbstractSlider::SliderRangeChange) { marksChanged(); } if (m_leftMouseDown || m_middleMouseDown) { const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); } } void KateScrollBar::marksChanged() { m_lines.clear(); update(); } void KateScrollBar::redrawMarks() { if (!m_showMarks) return; update(); } void KateScrollBar::recomputeMarksPositions() { // get the style options to compute the scrollbar pixels QStyleOptionSlider opt; initStyleOption(&opt); QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); // cache top margin and groove height const int top = grooveRect.top(); const int h = grooveRect.height() - 1; // make sure we have a sane height if (h <= 0) return; // get total visible (=without folded) lines in the document int visibleLines = m_view->textFolding().visibleLines() - 1; if (m_view->config()->scrollPastEnd()) { visibleLines += m_viewInternal->linesDisplayed() - 1; visibleLines -= m_view->config()->autoCenterLines(); } // now repopulate the scrollbar lines list m_lines.clear(); const QHash &marks = m_doc->marks(); for (QHash::const_iterator i = marks.constBegin(); i != marks.constEnd(); ++i) { KTextEditor::Mark *mark = i.value(); const int line = m_view->textFolding().lineToVisibleLine(mark->line); const double ratio = static_cast(line) / visibleLines; m_lines.insert(top + (int)(h * ratio), KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)); } } void KateScrollBar::sliderMaybeMoved(int value) { if (m_middleMouseDown) { // we only need to emit this signal once, as for the following slider // movements the signal sliderMoved() is already emitted. // Thus, set m_middleMouseDown to false right away. m_middleMouseDown = false; emit sliderMMBMoved(value); } } //END //BEGIN KateCmdLineEditFlagCompletion /** * This class provide completion of flags. It shows a short description of * each flag, and flags are appended. */ class KateCmdLineEditFlagCompletion : public KCompletion { public: KateCmdLineEditFlagCompletion() {;} QString makeCompletion( const QString & /*s*/ ) { return QString(); } }; //END KateCmdLineEditFlagCompletion //BEGIN KateCmdLineEdit KateCommandLineBar::KateCommandLineBar (KateView *view, QWidget *parent) : KateViewBarWidget (true, parent) { QHBoxLayout *topLayout = new QHBoxLayout (); centralWidget()->setLayout(topLayout); topLayout->setMargin(0); m_lineEdit = new KateCmdLineEdit (this, view); connect(m_lineEdit, SIGNAL(hideRequested()), SIGNAL(hideMe())); topLayout->addWidget (m_lineEdit); QToolButton *helpButton = new QToolButton(this); helpButton->setAutoRaise(true); helpButton->setIcon(KIcon(KIcon ("help-contextual"))); topLayout->addWidget(helpButton); connect (helpButton,SIGNAL(clicked()),this,SLOT(showHelpPage())); setFocusProxy (m_lineEdit); } void KateCommandLineBar::showHelpPage() { KToolInvocation::self()->invokeHelp("advanced-editing-tools-commandline","kate"); } KateCommandLineBar::~KateCommandLineBar() { } // inserts the given string in the command line edit and (if selected = true) selects it so the user // can type over it if they want to void KateCommandLineBar::setText(const QString &text, bool selected) { m_lineEdit->setText(text); if (selected) { m_lineEdit->selectAll(); } } void KateCommandLineBar::execute(const QString &text) { m_lineEdit->slotReturnPressed(text); } KateCmdLineEdit::KateCmdLineEdit (KateCommandLineBar *bar, KateView *view) : KLineEdit () , m_view (view) , m_bar (bar) , m_msgMode (false) , m_histpos( 0 ) , m_cmdend( 0 ) , m_command( 0L ) { connect (this, SIGNAL(returnPressed(QString)), this, SLOT(slotReturnPressed(QString))); setCompletionObject(KateCmd::self()->commandCompletionObject()); setAutoDeleteCompletionObject( false ); m_hideTimer = new QTimer(this); m_hideTimer->setSingleShot(true); connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hideLineEdit())); // make sure the timer is stopped when the user switches views. if not, focus will be given to the // wrong view when KateViewBar::hideCurrentBarWidget() is called after 4 seconds. (the timer is // used for showing things like "Success" for four seconds after the user has used the kate // command line) connect(m_view, SIGNAL(focusOut(KTextEditor::View*)), m_hideTimer, SLOT(stop())); } void KateCmdLineEdit::hideEvent(QHideEvent *e) { Q_UNUSED(e); } QString KateCmdLineEdit::helptext( const QPoint & ) const { QString beg = "
Help: "; QString mid = "
"; QString end = "
"; QString t = text(); QRegExp re( "\\s*help\\s+(.*)" ); if ( re.indexIn( t ) > -1 ) { QString s; // get help for command QString name = re.cap( 1 ); if ( name == "list" ) { return beg + i18n("Available Commands") + mid + KateCmd::self()->commandList().join(" ") + i18n("

For help on individual commands, do 'help <command>'

") + end; } else if ( ! name.isEmpty() ) { KTextEditor::Command *cmd = KateCmd::self()->queryCommand( name ); if ( cmd ) { if ( cmd->help( m_view, name, s ) ) return beg + name + mid + s + end; else return beg + name + mid + i18n("No help for '%1'", name ) + end; } else return beg + mid + i18n("No such command %1", name) + end; } } return beg + mid + i18n( "

This is the Katepart command line.
" "Syntax: command [ arguments ]
" "For a list of available commands, enter help list
" "For help for individual commands, enter help <command>

") + end; } bool KateCmdLineEdit::event(QEvent *e) { if (e->type() == QEvent::QueryWhatsThis) { setWhatsThis(helptext(QPoint())); e->accept(); return true; } return KLineEdit::event(e); } /** * Parse the text as a command. * * The following is a simple PEG grammar for the syntax of the command. * (A PEG grammar is like a BNF grammar, except that "/" stands for * ordered choice: only the first matching rule is used. In other words, * the parsing is short-circuited in the manner of the "or" operator in * programming languages, and so the grammar is unambiguous.) * * Text <- Range? Command * / Position * Range <- Position ("," Position)? * / "%" * Position <- Base Offset? * Base <- Line * / LastLine * / ThisLine * / Mark * Offset <- [+-] Base * Line <- [0-9]+ * LastLine <- "$" * ThisLine <- "." * Mark <- "'" [a-z] */ void KateCmdLineEdit::slotReturnPressed ( const QString& text ) { if (text.isEmpty()) return; // silently ignore leading space characters and colon characters (for vi-heads) uint n = 0; const uint textlen=text.length(); while( (n=textlen) return; QString cmd = text.mid( n ); // Built in help: if the command starts with "help", [try to] show some help if ( cmd.startsWith( QLatin1String("help") ) ) { QWhatsThis::showText(mapToGlobal(QPoint(0,0)), helptext( QPoint() ) ); clear(); KateCmd::self()->appendHistory( cmd ); m_histpos = KateCmd::self()->historyLength(); m_oldText.clear(); return; } if (cmd.length () > 0) { KTextEditor::Command *p = KateCmd::self()->queryCommand (cmd); m_oldText = cmd; m_msgMode = true; // the following commands changes the focus themselves, so bar should be hidden before execution. if (QRegExp("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e").exactMatch(cmd.split(" ").at(0))) { emit hideRequested(); } // we got a range and a valid command, but the command does not inherit the RangeCommand // extension. bail out. if (p) { QString msg; const bool cmdresult = p->exec(m_view, cmd, msg); if (msg.length() > 0) { if (msg.contains('\n')) { // multiline error, use widget with more space QWhatsThis::showText(mapToGlobal(QPoint(0,0)), msg); } else { setText(msg); } } else if (!cmdresult) { setText (i18n ("Command \"%1\" failed.", cmd)); } } else { setText (i18n ("No such command: \"%1\"", cmd)); } KNotification::beep(); } // clean up if (completionObject() != KateCmd::self()->commandCompletionObject()) { KCompletion *c = completionObject(); setCompletionObject(KateCmd::self()->commandCompletionObject()); delete c; } m_command = 0; m_cmdend = 0; // the following commands change the focus themselves if (!QRegExp("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e").exactMatch(cmd.split(" ").at(0))) { m_view->setFocus (); } if (isVisible()) { m_hideTimer->start(4000); } } void KateCmdLineEdit::hideLineEdit () // unless i have focus ;) { if ( ! hasFocus() ) { emit hideRequested(); } } void KateCmdLineEdit::focusInEvent ( QFocusEvent *ev ) { if (m_msgMode) { m_msgMode = false; setText (m_oldText); selectAll(); } KLineEdit::focusInEvent (ev); } void KateCmdLineEdit::keyPressEvent( QKeyEvent *ev ) { if (ev->key() == Qt::Key_Escape || (ev->key() == Qt::Key_BracketLeft && ev->modifiers() == Qt::ControlModifier)) { m_view->setFocus (); hideLineEdit(); clear(); } else if ( ev->key() == Qt::Key_Up ) fromHistory( true ); else if ( ev->key() == Qt::Key_Down ) fromHistory( false ); uint cursorpos = cursorPosition(); KLineEdit::keyPressEvent (ev); // during typing, let us see if we have a valid command if ( ! m_cmdend || cursorpos <= m_cmdend ) { QChar c; if ( ! ev->text().isEmpty() ) c = ev->text()[0]; if ( ! m_cmdend && ! c.isNull() ) // we have no command, so lets see if we got one { if ( ! c.isLetterOrNumber() && c != '-' && c != '_' ) { m_command = KateCmd::self()->queryCommand( text().trimmed() ); if ( m_command ) { //kDebug(13025)<<"keypress in commandline: We have a command! "<queryCommand( text().trimmed() ); if ( m_command ) { //kDebug(13025)<<"keypress in commandline: We have a command! "<commandCompletionObject()) { KCompletion *c = completionObject(); setCompletionObject(KateCmd::self()->commandCompletionObject()); delete c; } m_cmdend = 0; } } // if we got a command, check if it wants to do something. if ( m_command ) { //kDebug(13025)<<"Checking for CommandExtension.."; KTextEditor::CommandExtension *ce = dynamic_cast(m_command); if ( ce ) { KCompletion *cmpl = ce->completionObject( m_view, text().left( m_cmdend ).trimmed() ); if ( cmpl ) { // We need to prepend the current command name + flag string // when completion is done //kDebug(13025)<<"keypress in commandline: Setting completion object!"; setCompletionObject( cmpl ); } } } } else if ( m_command )// check if we should call the commands processText() { KTextEditor::CommandExtension *ce = dynamic_cast( m_command ); if ( ce && ce->wantsToProcessText( text().left( m_cmdend ).trimmed() ) && ! ( ev->text().isNull() || ev->text().isEmpty() ) ) ce->processText( m_view, text() ); } } void KateCmdLineEdit::fromHistory( bool up ) { if ( ! KateCmd::self()->historyLength() ) return; QString s; if ( up ) { if ( m_histpos > 0 ) { m_histpos--; s = KateCmd::self()->fromHistory( m_histpos ); } } else { if ( m_histpos < ( KateCmd::self()->historyLength() - 1 ) ) { m_histpos++; s = KateCmd::self()->fromHistory( m_histpos ); } else { m_histpos = KateCmd::self()->historyLength(); setText( m_oldText ); } } if ( ! s.isEmpty() ) { // Select the argument part of the command, so that it is easy to overwrite setText( s ); static QRegExp reCmd = QRegExp(".*[\\w\\-]+(?:[^a-zA-Z0-9_-]|:\\w+)(.*)"); if ( reCmd.indexIn( text() ) == 0 ) setSelection( text().length() - reCmd.cap(1).length(), reCmd.cap(1).length() ); } } //END KateCmdLineEdit //BEGIN KateIconBorder using namespace KTextEditor; KateIconBorder::KateIconBorder ( KateViewInternal* internalView, QWidget *parent ) : QWidget(parent) , m_view( internalView->m_view ) , m_doc( internalView->doc() ) , m_viewInternal( internalView ) , m_iconBorderOn( false ) , m_lineNumbersOn( false ) , m_viRelLineNumbersOn( false ) , m_updateViRelLineNumbers ( false ) , m_foldingMarkersOn( false ) , m_dynWrapIndicatorsOn( false ) , m_annotationBorderOn( false ) , m_dynWrapIndicators( 0 ) , m_cachedLNWidth( 0 ) , m_maxCharWidth( 0.0 ) , iconPaneWidth (16) , m_annotationBorderWidth (6) , m_foldingRange(0) , m_nextHighlightBlock(-2) , m_currentBlockLine(-1) { setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ); setMouseTracking(true); m_doc->setMarkDescription( MarkInterface::markType01, i18n("Bookmark") ); m_doc->setMarkPixmap( MarkInterface::markType01, KIcon("bookmarks").pixmap(16, 16) ); updateFont(); m_delayFoldingHlTimer.setSingleShot(true); m_delayFoldingHlTimer.setInterval(150); connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); } KateIconBorder::~KateIconBorder() { delete m_foldingRange; m_foldingRange = 0; } void KateIconBorder::setIconBorderOn( bool enable ) { if( enable == m_iconBorderOn ) return; m_iconBorderOn = enable; updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::setAnnotationBorderOn( bool enable ) { if( enable == m_annotationBorderOn ) return; m_annotationBorderOn = enable; emit m_view->annotationBorderVisibilityChanged(m_view, enable); updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::removeAnnotationHovering() { // remove hovering if it's still there if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { m_hoveredAnnotationGroupIdentifier.clear(); hideAnnotationTooltip(); QTimer::singleShot( 0, this, SLOT(update()) ); } } void KateIconBorder::setLineNumbersOn( bool enable ) { if( enable == m_lineNumbersOn ) return; m_lineNumbersOn = enable; m_dynWrapIndicatorsOn = (m_dynWrapIndicators == 1) ? enable : m_dynWrapIndicators; updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::setViRelLineNumbersOn(bool enable) { if( enable == m_viRelLineNumbersOn ) return; m_viRelLineNumbersOn = enable; /* * We don't have to touch the m_dynWrapIndicatorsOn because * we already got it right from the m_lineNumbersOn */ updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::updateViRelLineNumbers() { if (m_viRelLineNumbersOn) { m_updateViRelLineNumbers = true; update(); } } void KateIconBorder::setDynWrapIndicators( int state ) { if (state == m_dynWrapIndicators ) return; m_dynWrapIndicators = state; m_dynWrapIndicatorsOn = (state == 1) ? m_lineNumbersOn : state; updateGeometry (); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::setFoldingMarkersOn( bool enable ) { if( enable == m_foldingMarkersOn ) return; m_foldingMarkersOn = enable; updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } QSize KateIconBorder::sizeHint() const { int w = 0; if (m_iconBorderOn) w += iconPaneWidth + 2; if (m_annotationBorderOn) { w += m_annotationBorderWidth + 2; } if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { w += lineNumberWidth() + 2; } if (m_foldingMarkersOn) w += iconPaneWidth; // space for the line modification system border if (m_view->config()->lineModification()) { w += 3; } // two pixel space w += 2; return QSize( w, 0 ); } // This function (re)calculates the maximum width of any of the digit characters (0 -> 9) // for graceful handling of variable-width fonts as the linenumber font. void KateIconBorder::updateFont() { const QFontMetricsF &fm = m_view->renderer()->config()->fontMetrics(); m_maxCharWidth = 0.0; // Loop to determine the widest numeric character in the current font. // 48 is ascii '0' for (int i = 48; i < 58; i++) { const qreal charWidth = ceil(fm.width( QChar(i) )); m_maxCharWidth = qMax(m_maxCharWidth, charWidth); } // the icon pane scales with the font... iconPaneWidth = fm.height(); updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } int KateIconBorder::lineNumberWidth() const { // width = (number of digits + 1) * char width const int digits = (int) ceil(log10((double)(m_view->doc()->lines() + 1))); int width = m_lineNumbersOn ? (int)ceil((digits + 1) * m_maxCharWidth) : 0; if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { // HACK: 16 == style().scrollBarExtent().width() width = qMax(16 + 4, width); if (m_cachedLNWidth != width || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) { int w = 16;// HACK: 16 == style().scrollBarExtent().width() style().scrollBarExtent().width(); int h = m_view->renderer()->lineHeight(); QSize newSize(w, h); if ((m_arrow.size() != newSize || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) && !newSize.isEmpty()) { m_arrow = QPixmap(newSize); QPainter p(&m_arrow); p.fillRect( 0, 0, w, h, m_view->renderer()->config()->iconBarColor() ); h = m_view->renderer()->config()->fontMetrics().ascent(); p.setPen(m_view->renderer()->config()->lineNumberColor()); QPainterPath path; path.moveTo(w/2, h/2); path.lineTo(w/2, 0); path.lineTo(w/4, h/4); path.lineTo(0, 0); path.lineTo(0, h/2); path.lineTo(w/2, h-1); path.lineTo(w*3/4, h-1); path.lineTo(w-1, h*3/4); path.lineTo(w*3/4, h/2); path.lineTo(0, h/2); p.drawPath(path); } } } return width; } void KateIconBorder::paintEvent(QPaintEvent* e) { paintBorder(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); } static void paintTriangle (QPainter &painter, QColor c, int xOffset, int yOffset, int width, int height, bool open) { painter.setRenderHint(QPainter::Antialiasing); qreal size = qMin (width, height); if ( KColorUtils::luma( c ) > 0.25 ) c = KColorUtils::darken( c ); else c = KColorUtils::shade( c, 0.1 ); QPen pen; pen.setJoinStyle (Qt::RoundJoin); pen.setColor (c); pen.setWidthF (1.5); painter.setPen ( pen ); painter.setBrush (c); // let some border, if possible size *= 0.6; qreal halfSize = size / 2; qreal halfSizeP = halfSize * 0.6; QPointF middle (xOffset + (qreal)width / 2, yOffset + (qreal)height / 2); if (open) { QPointF points[3] = { middle+QPointF(-halfSize, -halfSizeP), middle+QPointF(halfSize, -halfSizeP), middle+QPointF(0, halfSizeP) }; painter.drawConvexPolygon(points, 3); } else { QPointF points[3] = { middle+QPointF(-halfSizeP, -halfSize), middle+QPointF(-halfSizeP, halfSize), middle+QPointF(halfSizeP, 0) }; painter.drawConvexPolygon(points, 3); } painter.setRenderHint(QPainter::Antialiasing, false); } void KateIconBorder::paintBorder (int /*x*/, int y, int /*width*/, int height) { uint h = m_view->renderer()->lineHeight(); uint startz = (y / h); uint endz = startz + 1 + (height / h); uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); // center the folding boxes int m_px = (h - 11) / 2; if (m_px < 0) m_px = 0; int lnWidth( 0 ); if ( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) ) // avoid calculating unless needed ;-) { lnWidth = lineNumberWidth(); if ( lnWidth != m_cachedLNWidth || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor() ) { // we went from n0 ->n9 lines or vice verca // this causes an extra updateGeometry() first time the line numbers // are displayed, but sizeHint() is supposed to be const so we can't set // the cached value there. m_cachedLNWidth = lnWidth; m_oldBackgroundColor = m_view->renderer()->config()->iconBarColor(); updateGeometry(); update (); return; } } int w( this->width() ); // sane value/calc only once QPainter p ( this ); p.setRenderHints (QPainter::TextAntialiasing); p.setFont ( m_view->renderer()->config()->font() ); // for line numbers KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); for (uint z=startz; z <= endz; z++) { int y = h * z; int realLine = -1; if (z < lineRangesSize) realLine = m_viewInternal->cache()->viewLine(z).line(); int lnX = 0; p.fillRect( 0, y, w-5, h, m_view->renderer()->config()->iconBarColor() ); p.fillRect( w-5, y, 5, h, m_view->renderer()->config()->backgroundColor() ); // icon pane if( m_iconBorderOn ) { p.setPen ( m_view->renderer()->config()->separatorColor() ); p.setBrush ( m_view->renderer()->config()->separatorColor() ); p.drawLine(lnX+iconPaneWidth+1, y, lnX+iconPaneWidth+1, y+h); if( (realLine > -1) && (m_viewInternal->cache()->viewLine(z).startCol() == 0) ) { uint mrk ( m_doc->mark( realLine ) ); // call only once if ( mrk ) { for( uint bit = 0; bit < 32; bit++ ) { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<markPixmap( markType )); if (!px_mark.isNull() && h > 0 && iconPaneWidth > 0) { if (iconPaneWidth < px_mark.width() || h < (uint)px_mark.height()) px_mark = px_mark.scaled (iconPaneWidth, h, Qt::KeepAspectRatio); // center the mark pixmap int x_px = (iconPaneWidth - px_mark.width()) / 2; if (x_px < 0) x_px = 0; int y_px = (h - px_mark.height()) / 2; if (y_px < 0) y_px = 0; p.drawPixmap( lnX+x_px, y+y_px, px_mark); } } } } } lnX += iconPaneWidth + 2; } // annotation information if( m_annotationBorderOn ) { // Draw a border line between annotations and the line numbers p.setPen ( m_view->renderer()->config()->lineNumberColor() ); p.setBrush ( m_view->renderer()->config()->lineNumberColor() ); int borderWidth = m_annotationBorderWidth; p.drawLine(lnX+borderWidth+1, y, lnX+borderWidth+1, y+h); if( (realLine > -1) && model ) { // Fetch data from the model QVariant text = model->data( realLine, Qt::DisplayRole ); QVariant foreground = model->data( realLine, Qt::ForegroundRole ); QVariant background = model->data( realLine, Qt::BackgroundRole ); // Fill the background if( background.isValid() ) { p.fillRect( lnX, y, borderWidth + 1, h, background.value() ); } // Set the pen for drawing the foreground if( foreground.isValid() ) { p.setPen( foreground.value() ); } // Draw a border around all adjacent entries that have the same text as the currently hovered one const QVariant identifier = model->data( realLine, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ); if( m_hoveredAnnotationGroupIdentifier == identifier.toString() ) { p.drawLine( lnX, y, lnX, y+h ); p.drawLine( lnX+borderWidth, y, lnX+borderWidth, y+h ); QVariant beforeText = model->data( realLine-1, Qt::DisplayRole ); QVariant afterText = model->data( realLine+1, Qt::DisplayRole ); if( ((beforeText.isValid() && beforeText.canConvert() && text.isValid() && text.canConvert() && beforeText.toString() != text.toString()) || realLine == 0) && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { p.drawLine( lnX+1, y, lnX+borderWidth, y ); } if( ((afterText.isValid() && afterText.canConvert() && text.isValid() && text.canConvert() && afterText.toString() != text.toString()) || realLine == m_view->doc()->lines() - 1) && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine)-1) { p.drawLine( lnX+1, y+h-1, lnX+borderWidth, y+h-1 ); } } if( foreground.isValid() ) { QPen pen = p.pen(); pen.setWidth( 1 ); p.setPen( pen ); } // Now draw the normal text if( text.isValid() && text.canConvert() && (m_viewInternal->cache()->viewLine(z).startCol() == 0) ) { p.drawText( lnX+3, y, borderWidth-3, h, Qt::AlignLeft|Qt::AlignVCenter, text.toString() ); } } // adjust current X position and reset the pen and brush lnX += borderWidth + 2; } // line number if( m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) ) { p.setPen ( m_view->renderer()->config()->lineNumberColor() ); p.setBrush ( m_view->renderer()->config()->lineNumberColor() ); if (realLine > -1) { if (m_viewInternal->cache()->viewLine(z).startCol() == 0) { if (m_lineNumbersOn) { p.drawText( lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, Qt::TextDontClip|Qt::AlignRight|Qt::AlignVCenter, QString::number( realLine + 1 ) ); } } else if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { p.drawPixmap(lnX + lnWidth - m_arrow.width() - 2, y, m_arrow); } } lnX += lnWidth + 2; } // folding markers if( m_foldingMarkersOn) { // first icon border background p.fillRect(lnX, y, iconPaneWidth, h, m_view->renderer()->config()->iconBarColor()); // possible additional folding highlighting if ((realLine >= 0) && m_foldingRange && m_foldingRange->overlapsLine (realLine)) { p.save(); // use linear gradient as brush QLinearGradient g(lnX, y, lnX + iconPaneWidth, y); const QColor foldingColor(m_view->renderer()->config()->foldingColor()); g.setColorAt(0, foldingColor); g.setColorAt(0.3, foldingColor.lighter(110)); g.setColorAt(1, foldingColor); p.setBrush(g); p.setPen(foldingColor); p.setClipRect(lnX, y, iconPaneWidth, h); p.setRenderHint(QPainter::Antialiasing); const qreal r = 4.0; if (m_foldingRange->start().line() == realLine && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { p.drawRect(lnX, y, iconPaneWidth, h + r); } else if (m_foldingRange->end().line() == realLine && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine)-1) { p.drawRect(lnX, y-r, iconPaneWidth, h + r); } else { p.drawRect(lnX, y-r, iconPaneWidth, h+2*r); } p.restore(); } if ((realLine >= 0) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine (realLine); bool anyFolded = false; for (int i = 0; i < startingRanges.size(); ++i) if (startingRanges[i].second & Kate::TextFolding::Folded) anyFolded = true; Kate::TextLine tl = m_doc->kateTextLine(realLine); if (!startingRanges.isEmpty() || tl->markedAsFoldingStart()) { if (anyFolded) { paintTriangle (p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, false); } else { paintTriangle (p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, true); } } } lnX += iconPaneWidth; } // modified line system if (m_view->config()->lineModification() && realLine > -1 && !m_doc->url().isEmpty()) { // one pixel space ++lnX; Kate::TextLine tl = m_doc->plainKateTextLine(realLine); if (tl->markedAsModified()) { p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->modifiedLineColor()); } if (tl->markedAsSavedOnDisk()) { p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->savedLineColor()); } } } } KateIconBorder::BorderArea KateIconBorder::positionToArea( const QPoint& p ) const { // see KateIconBorder::sizeHint() for pixel spacings int x = 0; if( m_iconBorderOn ) { x += iconPaneWidth; if( p.x() <= x ) return IconBorder; x += 2; } if( this->m_annotationBorderOn ) { x += m_annotationBorderWidth; if( p.x() <= x ) return AnnotationBorder; x += 2; } if( m_lineNumbersOn || m_dynWrapIndicators ) { x += lineNumberWidth(); if( p.x() <= x ) return LineNumbers; x += 2; } if( m_foldingMarkersOn ) { x += iconPaneWidth; if( p.x() <= x ) return FoldingMarkers; } if (m_view->config()->lineModification()) { x += 3 + 2; if( p.x() <= x ) return ModificationBorder; } return None; } void KateIconBorder::mousePressEvent( QMouseEvent* e ) { const KateTextLayout& t = m_viewInternal->yToKateTextLayout(e->y()); if (t.isValid()) { m_lastClickedLine = t.line(); if ( positionToArea( e->pos() ) != IconBorder && positionToArea( e->pos() ) != AnnotationBorder ) { QMouseEvent forward( QEvent::MouseButtonPress, QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); m_viewInternal->mousePressEvent( &forward ); } return e->accept(); } QWidget::mousePressEvent(e); } void KateIconBorder::showDelayedBlock(int line) { // save the line over which the mouse hovers // either we start the timer for delay, or we show the block immediately // if the moving range already exists m_nextHighlightBlock = line; if (!m_foldingRange) { if (!m_delayFoldingHlTimer.isActive()) { m_delayFoldingHlTimer.start(); } } else { showBlock(); } } void KateIconBorder::showBlock() { if (m_nextHighlightBlock == m_currentBlockLine) return; m_currentBlockLine = m_nextHighlightBlock; /** * compute to which folding range we belong * FIXME: optimize + perhaps have some better threshold or use timers! */ KTextEditor::Range newRange = KTextEditor::Range::invalid(); for (int line = m_currentBlockLine; line >= qMax(0, m_currentBlockLine-1024); --line) { /** * try if we have folding range from that line, should be fast per call */ KTextEditor::Range foldingRange = m_doc->buffer().computeFoldingRangeForStartLine (line); if (!foldingRange.isValid()) continue; /** * does the range reach us? */ if (foldingRange.overlapsLine (m_currentBlockLine)) { newRange = foldingRange; break; } } if (newRange.isValid() && m_foldingRange && m_foldingRange->toRange() == newRange) { // new range equals the old one, nothing to do. return; } else { // the ranges differ, delete the old, if it exists delete m_foldingRange; m_foldingRange = 0; } if (newRange.isValid()) { kDebug(13025) << "new folding hl-range:" << newRange; m_foldingRange = m_doc->newMovingRange(newRange, KTextEditor::MovingRange::ExpandRight); KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); /** * create highlighting color with alpha for the range! */ QColor result = m_view->renderer()->config()->foldingColor(); result.setAlphaF (0.5); attr->setBackground(QBrush( result )); m_foldingRange->setView (m_view); // use z depth defined in moving ranges interface m_foldingRange->setZDepth (-100.0); m_foldingRange->setAttribute(attr); } /** * repaint */ repaint (); } void KateIconBorder::hideBlock() { if (m_delayFoldingHlTimer.isActive()) { m_delayFoldingHlTimer.stop(); } m_nextHighlightBlock = -2; m_currentBlockLine = -1; delete m_foldingRange; m_foldingRange = 0; } void KateIconBorder::leaveEvent(QEvent *event) { hideBlock(); removeAnnotationHovering(); QWidget::leaveEvent(event); } void KateIconBorder::mouseMoveEvent( QMouseEvent* e ) { const KateTextLayout& t = m_viewInternal->yToKateTextLayout(e->y()); if (t.isValid()) { if ( positionToArea( e->pos() ) == FoldingMarkers) showDelayedBlock(t.line()); else hideBlock(); if ( positionToArea( e->pos() ) == AnnotationBorder ) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { m_hoveredAnnotationGroupIdentifier = model->data( t.line(), (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); showAnnotationTooltip( t.line(), e->globalPos() ); QTimer::singleShot( 0, this, SLOT(update()) ); } } else { if( positionToArea( e->pos() ) == IconBorder ) m_doc->requestMarkTooltip( t.line(), e->globalPos() ); m_hoveredAnnotationGroupIdentifier.clear(); hideAnnotationTooltip(); QTimer::singleShot( 0, this, SLOT(update()) ); } if ( positionToArea( e->pos() ) != IconBorder ) { QPoint p = m_viewInternal->mapFromGlobal( e->globalPos() ); QMouseEvent forward( QEvent::MouseMove, p, e->button(), e->buttons(), e->modifiers() ); m_viewInternal->mouseMoveEvent( &forward ); } } else { // remove hovering if it's still there removeAnnotationHovering(); } QWidget::mouseMoveEvent(e); } void KateIconBorder::mouseReleaseEvent( QMouseEvent* e ) { int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); if (cursorOnLine == m_lastClickedLine && cursorOnLine <= m_doc->lastLine() ) { BorderArea area = positionToArea( e->pos() ); if( area == IconBorder) { if (e->button() == Qt::LeftButton) { if( !m_doc->handleMarkClick(cursorOnLine) ) { KateViewConfig *config = m_view->config(); if( m_doc->editableMarks() & config->defaultMarkType() ) { if( m_doc->mark( cursorOnLine ) & config->defaultMarkType() ) m_doc->removeMark( cursorOnLine, config->defaultMarkType() ); else m_doc->addMark( cursorOnLine, config->defaultMarkType() ); } else if (config->allowMarkMenu()) { showMarkMenu( cursorOnLine, QCursor::pos() ); } } } else if (e->button() == Qt::RightButton) { showMarkMenu( cursorOnLine, QCursor::pos() ); } } if ( area == FoldingMarkers) { // ask the folding info for this line, if any folds are around! QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine (cursorOnLine); bool anyFolded = false; for (int i = 0; i < startingRanges.size(); ++i) if (startingRanges[i].second & Kate::TextFolding::Folded) anyFolded = true; // fold or unfold all ranges, remember if any action happened! bool actionDone = false; for (int i = 0; i < startingRanges.size(); ++i) actionDone = (anyFolded ? m_view->textFolding().unfoldRange (startingRanges[i].first) : m_view->textFolding().foldRange (startingRanges[i].first)) || actionDone; // if no action done, try to fold it, create non-persistent folded range, if possible! if (!actionDone) { // either use the fold for this line or the range that is highlighted ATM if any! KTextEditor::Range foldingRange = m_view->doc()->buffer().computeFoldingRangeForStartLine (cursorOnLine); if (!foldingRange.isValid() && m_foldingRange) foldingRange = m_foldingRange->toRange (); m_view->textFolding().newFoldingRange (foldingRange, Kate::TextFolding::Folded); } } if ( area == AnnotationBorder ) { if( e->button() == Qt::LeftButton && KGlobalSettings::singleClick() ) { emit m_view->annotationActivated( m_view, cursorOnLine ); } else if ( e->button() == Qt::RightButton ) { showAnnotationMenu( cursorOnLine, e->globalPos() ); } } } QMouseEvent forward( QEvent::MouseButtonRelease, QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); m_viewInternal->mouseReleaseEvent( &forward ); } void KateIconBorder::mouseDoubleClickEvent( QMouseEvent* e ) { int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); if (cursorOnLine == m_lastClickedLine && cursorOnLine <= m_doc->lastLine() ) { BorderArea area = positionToArea( e->pos() ); if( area == AnnotationBorder && !KGlobalSettings::singleClick() ) { emit m_view->annotationActivated( m_view, cursorOnLine ); } } QMouseEvent forward( QEvent::MouseButtonDblClick, QPoint( 0, e->y() ), e->button(), e->buttons(),e->modifiers() ); m_viewInternal->mouseDoubleClickEvent( &forward ); } void KateIconBorder::wheelEvent(QWheelEvent *e) { QCoreApplication::sendEvent(m_viewInternal, e); } void KateIconBorder::showMarkMenu( uint line, const QPoint& pos ) { if( m_doc->handleMarkContextMenu( line, pos ) ) return; if( !m_view->config()->allowMarkMenu() ) return; KMenu markMenu; KMenu selectDefaultMark; QVector vec( 33 ); int i=1; for( uint bit = 0; bit < 32; bit++ ) { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1<editableMarks() & markType) ) continue; QAction *mA; QAction *dMA; if( !m_doc->markDescription( markType ).isEmpty() ) { mA=markMenu.addAction( m_doc->markDescription( markType )); dMA=selectDefaultMark.addAction( m_doc->markDescription( markType )); } else { mA=markMenu.addAction( i18n("Mark Type %1", bit + 1 )); dMA=selectDefaultMark.addAction( i18n("Mark Type %1", bit + 1 )); } mA->setData(i); mA->setCheckable(true); dMA->setData(i+100); dMA->setCheckable(true); if( m_doc->mark( line ) & markType ) mA->setChecked(true ); if( markType & KateViewConfig::global()->defaultMarkType() ) dMA->setChecked(true ); vec[i++] = markType; } if( markMenu.actions().count() == 0 ) return; if( markMenu.actions().count() > 1 ) markMenu.addAction( i18n("Set Default Mark Type" ))->setMenu(&selectDefaultMark); QAction *rA = markMenu.exec( pos ); if( !rA ) return; int result=rA->data().toInt(); if ( result > 100) { KateViewConfig::global()->setDefaultMarkType (vec[result-100]); // flush config, otherwise it isn't necessarily done KConfigGroup cg(KGlobal::config(), "Kate View Defaults"); KateViewConfig::global()->writeConfig(cg); } else { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes) vec[result]; if( m_doc->mark( line ) & markType ) { m_doc->removeMark( line, markType ); } else { m_doc->addMark( line, markType ); } } } void KateIconBorder::showAnnotationTooltip( int line, const QPoint& pos ) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if( model ) { QVariant data = model->data( line, Qt::ToolTipRole ); QString tip = data.toString(); if (!tip.isEmpty()) QToolTip::showText( pos, data.toString(), this ); } } int KateIconBorder::annotationLineWidth( int line ) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if( model ) { QVariant data = model->data( line, Qt::DisplayRole ); return data.toString().length() * m_maxCharWidth + 8; } return 8; } void KateIconBorder::updateAnnotationLine( int line ) { if( annotationLineWidth(line) > m_annotationBorderWidth ) { m_annotationBorderWidth = annotationLineWidth(line); updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } } void KateIconBorder::showAnnotationMenu( int line, const QPoint& pos) { KMenu menu; QAction a(i18n("Disable Annotation Bar"), &menu); a.setIcon(KIcon("dialog-close")); menu.addAction(&a); emit m_view->annotationContextMenuAboutToShow( m_view, &menu, line ); if (menu.exec(pos) == &a) m_view->setAnnotationBorderVisible(false); } void KateIconBorder::hideAnnotationTooltip() { QToolTip::hideText(); } void KateIconBorder::updateAnnotationBorderWidth( ) { m_annotationBorderWidth = 6; KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if( model ) { for( int i = 0; i < m_view->doc()->lines(); i++ ) { int curwidth = annotationLineWidth( i ); if( curwidth > m_annotationBorderWidth ) m_annotationBorderWidth = curwidth; } } updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::annotationModelChanged( KTextEditor::AnnotationModel * oldmodel, KTextEditor::AnnotationModel * newmodel ) { if( oldmodel ) { oldmodel->disconnect( this ); } if( newmodel ) { connect( newmodel, SIGNAL(reset()), this, SLOT(updateAnnotationBorderWidth()) ); connect( newmodel, SIGNAL(lineChanged(int)), this, SLOT(updateAnnotationLine(int)) ); } updateAnnotationBorderWidth(); } //END KateIconBorder //BEGIN KateViewEncodingAction // Acording to http://www.iana.org/assignments/ianacharset-mib // the default/unknown mib value is 2. #define MIB_DEFAULT 2 class KateViewEncodingAction::Private { public: Private(KateViewEncodingAction *parent) : q(parent), currentSubAction(0) { } void init(); void _k_subActionTriggered(QAction*); KateViewEncodingAction *q; QAction *currentSubAction; }; bool lessThanAction(KSelectAction *a, KSelectAction *b) { return a->text() < b->text(); } void KateViewEncodingAction::Private::init() { QList actions; q->setToolBarMode(MenuMode); int i; foreach(const QStringList &encodingsForScript, KGlobal::charsets()->encodingsByScript()) { KSelectAction* tmp = new KSelectAction(encodingsForScript.at(0),q); for (i=1; iaddAction(encodingsForScript.at(i)); } q->connect(tmp,SIGNAL(triggered(QAction*)),q,SLOT(_k_subActionTriggered(QAction*))); //tmp->setCheckable(true); actions << tmp; } qSort(actions.begin(), actions.end(), lessThanAction); foreach (KSelectAction *action, actions) q->addAction(action); } void KateViewEncodingAction::Private::_k_subActionTriggered(QAction *action) { if (currentSubAction==action) return; currentSubAction=action; bool ok = false; int mib = q->mibForName(action->iconText(), &ok); if (ok) { emit q->KSelectAction::triggered(action->text()); emit q->triggered(q->codecForMib(mib)); } } KateViewEncodingAction::KateViewEncodingAction(KateDocument *_doc, KateView *_view, const QString& text, QObject *parent) : KSelectAction(text, parent), doc(_doc), view (_view), d(new Private(this)) { d->init(); connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); connect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); } KateViewEncodingAction::~KateViewEncodingAction() { delete d; } void KateViewEncodingAction::slotAboutToShow() { setCurrentCodec(doc->config()->encoding()); } void KateViewEncodingAction::setEncoding (const QString &e) { doc->userSetEncodingForNextReload (); doc->setEncoding(e); view->reloadFile(); } int KateViewEncodingAction::mibForName(const QString &codecName, bool *ok) const { // FIXME logic is good but code is ugly bool success = false; int mib = MIB_DEFAULT; KCharsets *charsets = KGlobal::charsets(); QTextCodec *codec = charsets->codecForName(codecName, success); if (!success) { // Maybe we got a description name instead codec = charsets->codecForName(charsets->encodingForName(codecName), success); } if (codec) mib = codec->mibEnum(); if (ok) *ok = success; if (success) return mib; kWarning() << "Invalid codec name: " << codecName; return MIB_DEFAULT; } QTextCodec *KateViewEncodingAction::codecForMib(int mib) const { if (mib == MIB_DEFAULT) { // FIXME offer to change the default codec return QTextCodec::codecForLocale(); } else return QTextCodec::codecForMib(mib); } QTextCodec *KateViewEncodingAction::currentCodec() const { return codecForMib(currentCodecMib()); } bool KateViewEncodingAction::setCurrentCodec( QTextCodec *codec ) { disconnect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); int i,j; for (i=0;imenu()) { for (j=0;jmenu()->actions().size();++j) { if (!j && !actions().at(i)->menu()->actions().at(j)->data().isNull()) continue; if (actions().at(i)->menu()->actions().at(j)->isSeparator()) continue; if (codec==KGlobal::charsets()->codecForName(actions().at(i)->menu()->actions().at(j)->text())) { d->currentSubAction=actions().at(i)->menu()->actions().at(j); d->currentSubAction->setChecked(true); } else actions().at(i)->menu()->actions().at(j)->setChecked (false); } } } connect(this,SIGNAL(triggered(QString)),this,SLOT(setEncoding(QString))); return true; } QString KateViewEncodingAction::currentCodecName() const { return d->currentSubAction->text(); } bool KateViewEncodingAction::setCurrentCodec( const QString &codecName ) { return setCurrentCodec(KGlobal::charsets()->codecForName(codecName)); } int KateViewEncodingAction::currentCodecMib() const { return mibForName(currentCodecName()); } bool KateViewEncodingAction::setCurrentCodec( int mib ) { return setCurrentCodec(codecForMib(mib)); } //END KateViewEncodingAction //BEGIN KateViewBar related classes KateViewBarWidget::KateViewBarWidget (bool addCloseButton, QWidget *parent) : QWidget (parent) , m_viewBar(0) { QHBoxLayout *layout = new QHBoxLayout (this); // NOTE: Here be cosmetics. layout->setMargin(0); // hide button if (addCloseButton) { QToolButton *hideButton = new QToolButton(this); hideButton->setAutoRaise(true); hideButton->setIcon(KIcon("dialog-close")); connect(hideButton, SIGNAL(clicked()), SIGNAL(hideMe())); layout->addWidget(hideButton); layout->setAlignment( hideButton, Qt::AlignLeft|Qt::AlignTop ); } // widget to be used as parent for the real content m_centralWidget = new QWidget (this); layout->addWidget(m_centralWidget); setLayout(layout); setFocusProxy(m_centralWidget); } KateViewBar::KateViewBar (bool external,KTextEditor::ViewBarContainer::Position pos,QWidget *parent, KateView *view) : QWidget (parent), m_external(external), m_pos(pos),m_view (view), m_permanentBarWidget(0) { m_layout = new QVBoxLayout(this); m_stack = new QStackedWidget(this); m_layout->addWidget(m_stack); m_layout->setMargin(0); m_stack->hide(); hide (); } void KateViewBar::addBarWidget (KateViewBarWidget *newBarWidget) { if (hasBarWidget(newBarWidget)) { kDebug(13025) << "this bar widget is already added"; return; } // add new widget, invisible... newBarWidget->hide(); m_stack->addWidget (newBarWidget); newBarWidget->setAssociatedViewBar(this); connect(newBarWidget, SIGNAL(hideMe()), SLOT(hideCurrentBarWidget())); kDebug(13025)<<"add barwidget " << newBarWidget; } void KateViewBar::removeBarWidget (KateViewBarWidget *barWidget) { if (hasBarWidget(barWidget)) { m_stack->removeWidget(barWidget); barWidget->setAssociatedViewBar(0); barWidget->hide(); disconnect(barWidget, 0, this, 0); } } void KateViewBar::addPermanentBarWidget (KateViewBarWidget *barWidget) { // remove old widget from layout (if any) if (m_permanentBarWidget) { m_permanentBarWidget->hide(); m_layout->removeWidget(m_permanentBarWidget); } m_layout->addWidget(barWidget, 0, Qt::AlignBottom); m_permanentBarWidget = barWidget; m_permanentBarWidget->show(); setViewBarVisible(true); } void KateViewBar::removePermanentBarWidget (KateViewBarWidget *barWidget) { if (m_permanentBarWidget != barWidget) { kDebug(13025) << "no such permanent widget exists in bar"; return; } if (!m_permanentBarWidget) return; m_permanentBarWidget->hide(); m_layout->removeWidget(m_permanentBarWidget); m_permanentBarWidget = 0; if (!m_stack->isVisible()) { setViewBarVisible(false); } } bool KateViewBar::hasPermanentWidget (KateViewBarWidget *barWidget ) const { return (m_permanentBarWidget == barWidget); } void KateViewBar::showBarWidget (KateViewBarWidget *barWidget) { Q_ASSERT(barWidget != 0); if (barWidget != qobject_cast(m_stack->currentWidget())) { hideCurrentBarWidget(); } // raise correct widget m_stack->setCurrentWidget (barWidget); barWidget->show(); barWidget->setFocus(Qt::ShortcutFocusReason); m_stack->show(); // if we have any permanent widget, bar is always visible, // no need to show it if (!m_permanentBarWidget) { setViewBarVisible(true); } } bool KateViewBar::hasBarWidget(KateViewBarWidget* barWidget) const { return m_stack->indexOf(barWidget) != -1; } void KateViewBar::hideCurrentBarWidget () { KateViewBarWidget *current=qobject_cast(m_stack->currentWidget()); if (current) { current->closed(); } m_stack->hide(); // if we have any permanent widget, bar is always visible, // no need to hide it if (!m_permanentBarWidget) { setViewBarVisible(false); } m_view->setFocus(); kDebug(13025)<<"hide barwidget"; } void KateViewBar::setViewBarVisible (bool visible) { if (m_external) { KTextEditor::ViewBarContainer *viewBarContainer=qobject_cast( KateGlobal::self()->container() ); if (viewBarContainer) { if (visible) { viewBarContainer->showViewBarForView(m_view,m_pos); } else { viewBarContainer->hideViewBarForView(m_view,m_pos); } } } else { setVisible (visible); } } void KateViewBar::keyPressEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Escape) { hideCurrentBarWidget(); return; } QWidget::keyPressEvent(event); } void KateViewBar::hideEvent(QHideEvent* event) { Q_UNUSED(event); // if (!event->spontaneous()) // m_view->setFocus(); } //END KateViewBar related classes KatePasteMenu::KatePasteMenu (const QString& text, KateView *view) : KActionMenu(text, view) , m_view (view) { connect(menu(),SIGNAL(aboutToShow()),this,SLOT(slotAboutToShow())); } void KatePasteMenu::slotAboutToShow() { menu()->clear (); /** * insert complete paste history */ int i = 0; Q_FOREACH (const QString &text, KateGlobal::self()->clipboardHistory()) { /** * get text for the menu ;) */ QString leftPart = (text.size() > 48) ? (text.left(48) + "...") : text; QAction *a=menu()->addAction (leftPart.replace ("\n", " "), this, SLOT(paste())); a->setData(i++); } } void KatePasteMenu::paste () { if (!sender()) return; QAction *action = qobject_cast(sender()); if (!action) return; // get index int i = action->data().toInt(); if (i >= KateGlobal::self()->clipboardHistory().size()) return; // paste m_view->paste (&KateGlobal::self()->clipboardHistory()[i]); } #include "moc_kateviewhelpers.cpp" // kate: space-indent on; indent-width 2; replace-tabs on;