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"