File indexing completed on 2023-09-24 04:10:18
0001 /* This file is part of the KDE libraries 0002 Copyright (C) 2005, 2006 Ian Reinhart Geiser <geiseri@kde.org> 0003 Copyright (C) 2005, 2006 Matt Broadstone <mbroadst@gmail.com> 0004 Copyright (C) 2005, 2006 Richard J. Moore <rich@kde.org> 0005 Copyright (C) 2005, 2006 Erik L. Bunce <kde@bunce.us> 0006 0007 This library is free software; you can redistribute it and/or 0008 modify it under the terms of the GNU Library General Public 0009 License as published by the Free Software Foundation; either 0010 version 2 of the License, or (at your option) any later version. 0011 0012 This library is distributed in the hope that it will be useful, 0013 but WITHOUT ANY WARRANTY; without even the implied warranty of 0014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0015 Library General Public License for more details. 0016 0017 You should have received a copy of the GNU Library General Public License 0018 along with this library; see the file COPYING.LIB. If not, write to 0019 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0020 Boston, MA 02110-1301, USA. 0021 */ 0022 0023 #include "numberedtextview.h" 0024 0025 #include <QTextDocument> 0026 #include <QTextBlock> 0027 #include <QTextEdit> 0028 #include <QBoxLayout> 0029 #include <QScrollBar> 0030 #include <QPainter> 0031 #include <QTextObjectInterface> 0032 #include <QToolTip> 0033 #include <QDebug> 0034 0035 NumberBar::NumberBar(QWidget *parent) 0036 : QWidget(parent), edit(nullptr), m_stopLine(-1), m_currentLine(-1), m_bugLine(-1) 0037 { 0038 stopMarker = QPixmap(":/images/no.png"); 0039 currentMarker = QPixmap(":/images/next.png"); 0040 bugMarker = QPixmap(":/images/bug.png"); 0041 setFixedWidth(fontMetrics().width(QStringLiteral("0000")) + bugMarker.width() + stopMarker.width() + currentMarker.width()); 0042 } 0043 0044 NumberBar::~NumberBar() 0045 { 0046 } 0047 0048 void NumberBar::setCurrentLine(int lineno) 0049 { 0050 m_currentLine = lineno; 0051 } 0052 0053 void NumberBar::setStopLine(int lineno) 0054 { 0055 m_stopLine = lineno; 0056 } 0057 0058 void NumberBar::setBugLine(int lineno) 0059 { 0060 m_bugLine = lineno; 0061 } 0062 0063 int NumberBar::currentLine() const 0064 { 0065 return m_currentLine; 0066 } 0067 0068 int NumberBar::stopLine() const 0069 { 0070 return m_stopLine; 0071 } 0072 0073 int NumberBar::bugLine() const 0074 { 0075 return m_bugLine; 0076 } 0077 0078 void NumberBar::setTextEdit(QTextEdit *edit) 0079 { 0080 this->edit = edit; 0081 connect(edit->document()->documentLayout(), SIGNAL(update(QRectF)), 0082 this, SLOT(update())); 0083 connect(edit->verticalScrollBar(), SIGNAL(valueChanged(int)), 0084 this, SLOT(update())); 0085 } 0086 0087 void NumberBar::paintEvent(QPaintEvent *) 0088 { 0089 QAbstractTextDocumentLayout *layout = edit->document()->documentLayout(); 0090 int contentsY = edit->verticalScrollBar()->value(); 0091 qreal pageBottom = contentsY + edit->viewport()->height(); 0092 const QFontMetrics fm = fontMetrics(); 0093 const int ascent = fontMetrics().ascent() + 1; // height = ascent + descent + 1 0094 int lineCount = 1; 0095 0096 QPainter p(this); 0097 0098 bugRect = QRect(); 0099 stopRect = QRect(); 0100 currentRect = QRect(); 0101 0102 for (QTextBlock block = edit->document()->begin(); 0103 block.isValid(); block = block.next(), ++lineCount) { 0104 0105 const QRectF boundingRect = layout->blockBoundingRect(block); 0106 0107 QPointF position = boundingRect.topLeft(); 0108 if (position.y() + boundingRect.height() < contentsY) { 0109 continue; 0110 } 0111 if (position.y() > pageBottom) { 0112 break; 0113 } 0114 0115 const QString txt = QString::number(lineCount); 0116 p.drawText(width() - fm.width(txt), qRound(position.y()) - contentsY + ascent, txt); 0117 0118 // Bug marker 0119 if (m_bugLine == lineCount) { 0120 p.drawPixmap(1, qRound(position.y()) - contentsY, bugMarker); 0121 bugRect = QRect(1, qRound(position.y()) - contentsY, bugMarker.width(), bugMarker.height()); 0122 } 0123 0124 // Stop marker 0125 if (m_stopLine == lineCount) { 0126 p.drawPixmap(1, qRound(position.y()) - contentsY, stopMarker); 0127 stopRect = QRect(1, qRound(position.y()) - contentsY, stopMarker.width(), stopMarker.height()); 0128 } 0129 0130 // Current line marker 0131 if (m_currentLine == lineCount) { 0132 p.drawPixmap(1, qRound(position.y()) - contentsY, currentMarker); 0133 currentRect = QRect(1, qRound(position.y()) - contentsY, currentMarker.width(), currentMarker.height()); 0134 } 0135 } 0136 } 0137 0138 bool NumberBar::event(QEvent *event) 0139 { 0140 if (event->type() == QEvent::ToolTip) { 0141 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); 0142 0143 if (stopRect.contains(helpEvent->pos())) { 0144 QToolTip::showText(helpEvent->globalPos(), "Stop Here"); 0145 } else if (currentRect.contains(helpEvent->pos())) { 0146 QToolTip::showText(helpEvent->globalPos(), "Current Line"); 0147 } else if (bugRect.contains(helpEvent->pos())) { 0148 QToolTip::showText(helpEvent->globalPos(), "Error Line"); 0149 } 0150 } 0151 0152 return QWidget::event(event); 0153 } 0154 0155 NumberedTextView::NumberedTextView(QWidget *parent) 0156 : QFrame(parent) 0157 { 0158 setFrameStyle(QFrame::StyledPanel | QFrame::Sunken); 0159 setLineWidth(2); 0160 0161 // Setup the main view 0162 view = new QTextEdit(this); 0163 view->setFontFamily("Monospace"); 0164 view->setLineWrapMode(QTextEdit::NoWrap); 0165 view->setFrameStyle(QFrame::NoFrame); 0166 view->installEventFilter(this); 0167 0168 connect(view->document(), SIGNAL(contentsChange(int,int,int)), this, SLOT(textChanged(int,int,int))); 0169 0170 // Setup the line number pane 0171 numbers = new NumberBar(this); 0172 numbers->setTextEdit(view); 0173 // Testing... 0174 numbers->setStopLine(3); 0175 numbers->setBugLine(1); 0176 setCurrentLine(5); 0177 0178 box = new QHBoxLayout(this); 0179 box->setSpacing(0); 0180 box->setContentsMargins(0, 0, 0, 0); 0181 box->addWidget(numbers); 0182 box->addWidget(view); 0183 } 0184 0185 NumberedTextView::~NumberedTextView() 0186 { 0187 } 0188 0189 void NumberedTextView::setCurrentLine(int lineno) 0190 { 0191 numbers->setCurrentLine(lineno); 0192 textChanged(0, 0, 1); 0193 } 0194 0195 void NumberedTextView::setStopLine(int lineno) 0196 { 0197 numbers->setStopLine(lineno); 0198 } 0199 0200 void NumberedTextView::setBugLine(int lineno) 0201 { 0202 numbers->setBugLine(lineno); 0203 } 0204 0205 int NumberedTextView::currentLine() const 0206 { 0207 return numbers->currentLine(); 0208 } 0209 0210 int NumberedTextView::stopLine() const 0211 { 0212 return numbers->stopLine(); 0213 } 0214 0215 int NumberedTextView::bugLine() const 0216 { 0217 return numbers->bugLine(); 0218 } 0219 0220 QString NumberedTextView::text() const 0221 { 0222 return view->toPlainText(); 0223 } 0224 0225 void NumberedTextView::setText(const QString &text) 0226 { 0227 view->setPlainText(text); 0228 } 0229 0230 void NumberedTextView::textChanged(int pos, int removed, int added) 0231 { 0232 Q_UNUSED(pos); 0233 0234 if (removed == 0 && added == 0) { 0235 return; 0236 } 0237 0238 QTextBlock block = highlight.block(); 0239 QTextBlockFormat fmt = block.blockFormat(); 0240 QColor bg = view->palette().base().color(); 0241 fmt.setBackground(bg); 0242 highlight.setBlockFormat(fmt); 0243 0244 int lineCount = 1; 0245 for (QTextBlock block = view->document()->begin(); 0246 block.isValid(); block = block.next(), ++lineCount) { 0247 0248 if (lineCount == numbers->currentLine()) { 0249 fmt = block.blockFormat(); 0250 QColor bg = view->palette().color(QPalette::Highlight).lighter(175); 0251 fmt.setBackground(bg); 0252 0253 highlight = QTextCursor(block); 0254 highlight.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); 0255 highlight.setBlockFormat(fmt); 0256 0257 break; 0258 } 0259 } 0260 } 0261 0262 bool NumberedTextView::eventFilter(QObject *obj, QEvent *event) 0263 { 0264 if (obj != view) { 0265 return QFrame::eventFilter(obj, event); 0266 } 0267 0268 if (event->type() == QEvent::ToolTip) { 0269 QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event); 0270 0271 QTextCursor cursor = view->cursorForPosition(helpEvent->pos()); 0272 cursor.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor); 0273 cursor.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor); 0274 0275 QString word = cursor.selectedText(); 0276 emit mouseHover(word); 0277 emit mouseHover(helpEvent->pos(), word); 0278 0279 // QToolTip::showText( helpEvent->globalPos(), word ); // For testing 0280 } 0281 0282 return false; 0283 } 0284 0285 #include "moc_numberedtextview.cpp"