File indexing completed on 2024-04-28 15:28:40

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"