File indexing completed on 2024-12-01 09:50:25

0001 /*
0002  *  This file is part of the KDE libraries
0003  *  Copyright (C) 2006 Matt Broadstone (mbroadst@gmail.com)
0004  *  Copyright (C) 2007 Maks Orlovich   (maksim@kde.org)
0005  *  Copyright ©   2007 Fredrik Höglund <fredrik@kde.org>
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
0018  *  License along with this library; if not, write to the Free Software
0019  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0020  */
0021 
0022 #include "consoledock.h"
0023 
0024 #include <QVBoxLayout>
0025 #include <QPushButton>
0026 #include <QModelIndex>
0027 #include <QTextLayout>
0028 #include <QPainter>
0029 #include <QLineEdit>
0030 #include <QListWidget>
0031 #include "khtml_debug.h"
0032 #include <QAbstractItemDelegate>
0033 #include <QTextLayout>
0034 
0035 #include <khistorycombobox.h>
0036 #include <klocalizedstring.h>
0037 
0038 namespace KJSDebugger
0039 {
0040 
0041 class ConsoleItemDelegate : public QAbstractItemDelegate
0042 {
0043 public:
0044     enum ItemRole { ResultRole = 0x106EE9CA };
0045 
0046     ConsoleItemDelegate(QObject *parent = 0);
0047     ~ConsoleItemDelegate();
0048     QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
0049     void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
0050 
0051 private:
0052     void setLayoutOption(QTextLayout &layout, const QStyleOptionViewItemV3 &option, Qt::Alignment alignment) const;
0053     QSize layoutText(QTextLayout &layout, const QString &text, const QSize &constraints) const;
0054     void drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect) const;
0055 };
0056 
0057 ConsoleItemDelegate::ConsoleItemDelegate(QObject *parent)
0058     : QAbstractItemDelegate(parent)
0059 {
0060 }
0061 
0062 ConsoleItemDelegate::~ConsoleItemDelegate()
0063 {
0064 }
0065 
0066 void ConsoleItemDelegate::setLayoutOption(QTextLayout &layout, const QStyleOptionViewItemV3 &option, Qt::Alignment alignment) const
0067 {
0068     QTextOption textoption;
0069     textoption.setTextDirection(option.direction);
0070     textoption.setAlignment(QStyle::visualAlignment(option.direction, alignment));
0071     textoption.setWrapMode(option.features & QStyleOptionViewItemV2::WrapText ? QTextOption::WordWrap
0072                            : QTextOption::NoWrap);
0073 
0074     layout.setTextOption(textoption);
0075 }
0076 
0077 QSize ConsoleItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0078 {
0079     if (!index.isValid()) {
0080         return QSize();
0081     }
0082 
0083     QFont normalFont = option.font;
0084     QFont boldFont = normalFont;
0085     boldFont.setBold(true);
0086 
0087     // Extract the items we want to measure
0088     QString input      = index.data(Qt::DisplayRole).toString();
0089     QString result     = index.data(ResultRole).toString();
0090 
0091     QStyleOptionViewItemV3 opt(option);
0092     int maxWidth = static_cast<const QListView *>(opt.widget)->viewport()->width();
0093 
0094     QTextLayout inputLayout, resultLayout;
0095     setLayoutOption(inputLayout, opt, Qt::AlignLeft);
0096     setLayoutOption(resultLayout, opt, Qt::AlignRight);
0097 
0098     inputLayout.setFont(normalFont);
0099     resultLayout.setFont(boldFont);
0100 
0101     QSize constraints(maxWidth - 4, 65536);
0102 
0103     QSize inputSize  = layoutText(inputLayout, input, constraints);
0104     QSize resultSize = layoutText(resultLayout, result, constraints);
0105 
0106     int width = qBound(0, qMax(inputSize.width(), resultSize.width()) + 4, maxWidth);
0107     int height = inputSize.height() + option.fontMetrics.leading() + resultSize.height();
0108 
0109     return QSize(width, height + 8);
0110 }
0111 
0112 QSize ConsoleItemDelegate::layoutText(QTextLayout &layout, const QString &text, const QSize &constraints) const
0113 {
0114     QFontMetrics metrics(layout.font());
0115     int leading     = metrics.leading();
0116     int height      = 0;
0117     int maxWidth    = constraints.width();
0118     int widthUsed   = 0;
0119     QTextLine line;
0120 
0121     layout.setText(text);
0122 
0123     layout.beginLayout();
0124     while ((line = layout.createLine()).isValid()) {
0125         height += leading;
0126 
0127         line.setLineWidth(maxWidth);
0128 
0129         // Make the last line that will fit infinitely long.
0130         // drawTextLayout() will handle this by fading the line out
0131         // if it won't fit in the contraints.
0132         if (height + metrics.height() * 2 + leading >= constraints.height()) {
0133             line.setLineWidth(INT_MAX);
0134             line.setLineWidth(line.naturalTextWidth());
0135         } else {
0136             line.setLineWidth(maxWidth);
0137         }
0138 
0139         line.setPosition(QPoint(0, height));
0140 
0141         height += int(line.height());
0142         widthUsed = int(qMax(qreal(widthUsed), line.naturalTextWidth()));
0143     }
0144     layout.endLayout();
0145 
0146     return QSize(widthUsed, height);
0147 }
0148 
0149 void ConsoleItemDelegate::drawTextLayout(QPainter *painter, const QTextLayout &layout, const QRect &rect) const
0150 {
0151     if (rect.width() < 1 || rect.height() < 1) {
0152         return;
0153     }
0154 
0155     QFontMetrics fm(layout.font());
0156     bool leftAligned = layout.textOption().alignment() & Qt::AlignLeft;
0157 
0158     // Draw each line in the layout
0159     for (int i = 0; i < layout.lineCount(); i++) {
0160         QTextLine line = layout.lineAt(i);
0161 
0162         int width = line.naturalTextWidth();
0163         int x = width > rect.width() && !leftAligned ? rect.width() - width : 0;
0164         line.draw(painter, QPoint(x + rect.x(), rect.y()));
0165     }
0166 }
0167 
0168 void ConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0169 {
0170     if (!index.isValid()) {
0171         return;
0172     }
0173 
0174     painter->save();
0175 
0176     QFont normalFont = option.font;
0177     QFont boldFont = normalFont;
0178     boldFont.setBold(true);
0179 
0180     QString input  = index.data(Qt::DisplayRole).toString();
0181     QString result = index.data(ResultRole).toString();
0182 
0183     // Draw the background
0184     bool selected = option.state & QStyle::State_Selected;
0185 
0186     QStyleOptionViewItemV4 opt(option);
0187 
0188     QStyle *style = opt.widget->style();
0189     style->drawPrimitive(QStyle::PE_PanelItemViewRow, &opt, painter, opt.widget);
0190     if (selected) {
0191         painter->setPen(option.palette.color(QPalette::HighlightedText));
0192     }
0193 
0194     // Draw the text
0195     QTextLayout inputLayout, resultLayout;
0196     setLayoutOption(inputLayout, opt, Qt::AlignLeft);
0197     setLayoutOption(resultLayout, opt, Qt::AlignRight);
0198 
0199     inputLayout.setFont(normalFont);
0200     resultLayout.setFont(boldFont);
0201 
0202     QRect contentRect = option.rect.adjusted(2, 4, -2, -4);
0203 
0204     QSize inputSize = layoutText(inputLayout, input, contentRect.size());
0205     QSize resultSize = layoutText(resultLayout, result, QSize(contentRect.width(),
0206                                   contentRect.height() - inputSize.height() - option.fontMetrics.leading()));
0207 
0208     QRect inputRect  = QStyle::alignedRect(option.direction, Qt::AlignLeft | Qt::AlignTop, inputSize, contentRect);
0209     QRect resultRect = QStyle::alignedRect(option.direction, Qt::AlignRight | Qt::AlignBottom, resultSize, contentRect);
0210 
0211     drawTextLayout(painter, inputLayout, inputRect & option.rect);
0212     drawTextLayout(painter, resultLayout, resultRect & option.rect);
0213 
0214     painter->restore();
0215 }
0216 
0217 //------------------------------------------------------------------------
0218 
0219 ConsoleDock::ConsoleDock(QWidget *parent)
0220     : QDockWidget(i18n("Console"), parent)
0221 {
0222     setFeatures(DockWidgetMovable | DockWidgetFloatable);
0223 
0224     QWidget *mainFrame = new QWidget(this);
0225 
0226     consoleView = new QListWidget(mainFrame);
0227     consoleView->setWordWrap(true);
0228     consoleView->setItemDelegate(new ConsoleItemDelegate(consoleView));
0229     consoleView->setAlternatingRowColors(true);
0230 
0231     connect(consoleView, SIGNAL(itemClicked(QListWidgetItem*)),
0232             this, SLOT(slotPasteItem(QListWidgetItem*)));
0233     connect(consoleView, SIGNAL(itemDoubleClicked(QListWidgetItem*)),
0234             this, SLOT(slotPasteItem(QListWidgetItem*)));
0235 
0236     consoleInput = new KHistoryComboBox(mainFrame);
0237     consoleInput->setSizePolicy(QSizePolicy::MinimumExpanding,
0238                                 QSizePolicy::Fixed);
0239 
0240     connect(consoleInput, SIGNAL(returnPressed()),
0241             this, SLOT(slotUserRequestedEval()));
0242 
0243     consoleInputButton = new QPushButton(i18n("Enter"), mainFrame);
0244     connect(consoleInputButton, SIGNAL(clicked(bool)),
0245             this, SLOT(slotUserRequestedEval()));
0246 
0247     QHBoxLayout *bottomLayout = new QHBoxLayout;
0248     bottomLayout->setSpacing(0);
0249     bottomLayout->setContentsMargins(0, 0, 0, 0);
0250     bottomLayout->addWidget(consoleInput);
0251     bottomLayout->addWidget(consoleInputButton);
0252 
0253     QVBoxLayout *mainLayout = new QVBoxLayout;
0254     mainLayout->setSpacing(0);
0255     mainLayout->setContentsMargins(0, 0, 0, 0);
0256     mainLayout->addWidget(consoleView);
0257     mainLayout->addLayout(bottomLayout);
0258     mainFrame->setLayout(mainLayout);
0259 
0260     setWidget(mainFrame);
0261 }
0262 
0263 ConsoleDock::~ConsoleDock()
0264 {
0265 }
0266 
0267 void ConsoleDock::reportResult(const QString &src, const QString &msg)
0268 {
0269     QListWidgetItem *item = new QListWidgetItem(src, consoleView);
0270     item->setData(ConsoleItemDelegate::ResultRole, msg);
0271     consoleView->scrollToItem(item);
0272 }
0273 
0274 void ConsoleDock::slotPasteItem(QListWidgetItem *item)
0275 {
0276     consoleInput->lineEdit()->setText(item->data(Qt::DisplayRole).toString());
0277 }
0278 
0279 void ConsoleDock::slotUserRequestedEval()
0280 {
0281     QString src = consoleInput->lineEdit()->text();
0282     consoleInput->addToHistory(src);
0283     consoleInput->lineEdit()->clear();
0284 
0285     emit requestEval(src);
0286 }
0287 
0288 }
0289 
0290 #include "moc_consoledock.cpp"