diff --git a/src/libs/qmljs/consoleitem.cpp b/src/libs/qmljs/consoleitem.cpp index 6b972693388..2f22696a44a 100644 --- a/src/libs/qmljs/consoleitem.cpp +++ b/src/libs/qmljs/consoleitem.cpp @@ -38,111 +38,34 @@ namespace QmlJS { // /////////////////////////////////////////////////////////////////////// -ConsoleItem::ConsoleItem(ConsoleItem *parent, ConsoleItem::ItemType itemType, - const QString &text) - : m_parentItem(parent), - itemType(itemType), - line(-1) - +QString addZeroWidthSpace(QString text) { - setText(text); -} - -ConsoleItem::~ConsoleItem() -{ - qDeleteAll(m_childItems); -} - -ConsoleItem *ConsoleItem::child(int number) -{ - return m_childItems.value(number); -} - -int ConsoleItem::childCount() const -{ - return m_childItems.size(); -} - -int ConsoleItem::childNumber() const -{ - if (m_parentItem) - return m_parentItem->m_childItems.indexOf(const_cast(this)); - - return 0; -} - -bool ConsoleItem::insertChildren(int position, int count) -{ - if (position < 0 || position > m_childItems.size()) - return false; - - for (int row = 0; row < count; ++row) { - ConsoleItem *item = new ConsoleItem(this, ConsoleItem::UndefinedType, - QString()); - m_childItems.insert(position, item); + for (int i = 0; i < text.length(); ++i) { + if (text.at(i).isPunct()) + text.insert(++i, QChar(0x200b)); // ZERO WIDTH SPACE } - - return true; + return text; } -void ConsoleItem::insertChild(ConsoleItem *item, bool sorted) +ConsoleItem::ConsoleItem(ItemType itemType, const QString &expression, const QString &file, + int line) : + m_itemType(itemType), m_text(addZeroWidthSpace(expression)), m_file(file), m_line(line) { - if (!sorted) { - m_childItems.insert(m_childItems.count(), item); - return; - } - - int i = 0; - for (; i < m_childItems.count(); i++) { - if (item->m_text < m_childItems[i]->m_text) - break; - } - m_childItems.insert(i, item); + setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | + (itemType == InputType ? Qt::ItemIsEditable : Qt::NoItemFlags))); } -bool ConsoleItem::insertChild(int position, ConsoleItem *item) +ConsoleItem::ConsoleItem(ConsoleItem::ItemType itemType, const QString &expression, + std::function doFetch) : + m_itemType(itemType), m_text(addZeroWidthSpace(expression)), m_line(-1), m_doFetch(doFetch) { - if (position < 0 || position > m_childItems.size()) - return false; - - m_childItems.insert(position, item); - - return true; + setFlags(Qt::ItemFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | + (itemType == InputType ? Qt::ItemIsEditable : Qt::NoItemFlags))); } -ConsoleItem *ConsoleItem::parent() +ConsoleItem::ItemType ConsoleItem::itemType() const { - return m_parentItem; -} - -bool ConsoleItem::removeChildren(int position, int count) -{ - if (position < 0 || position + count > m_childItems.size()) - return false; - - for (int row = 0; row < count; ++row) - delete m_childItems.takeAt(position); - - return true; -} - -bool ConsoleItem::detachChild(int position) -{ - if (position < 0 || position > m_childItems.size()) - return false; - - m_childItems.removeAt(position); - - return true; -} - -void ConsoleItem::setText(const QString &text) -{ - m_text = text; - for (int i = 0; i < m_text.length(); ++i) { - if (m_text.at(i).isPunct()) - m_text.insert(++i, QChar(0x200b)); // ZERO WIDTH SPACE - } + return m_itemType; } QString ConsoleItem::text() const @@ -150,10 +73,95 @@ QString ConsoleItem::text() const return m_text; } +QString ConsoleItem::file() const +{ + return m_file; +} + +int ConsoleItem::line() const +{ + return m_line; +} + +QVariant ConsoleItem::data(int column, int role) const +{ + if (column != 0) + return QVariant(); + + switch (role) + { + case TypeRole: + return m_itemType; + case FileRole: + return m_file; + case LineRole: + return m_line; + case ExpressionRole: + return expression(); + case Qt::DisplayRole: + return m_text; + default: + return TreeItem::data(column, role); + } +} + +bool ConsoleItem::setData(int column, const QVariant &data, int role) +{ + if (column != 0) + return false; + + switch (role) + { + case TypeRole: + m_itemType = ItemType(data.toInt()); + return true; + case FileRole: + m_file = data.toString(); + return true; + case LineRole: + m_line = data.toInt(); + return true; + case ExpressionRole: + m_text = addZeroWidthSpace(data.toString()); + return true; + case Qt::DisplayRole: + m_text = data.toString(); + return true; + default: + return TreeItem::setData(column, data, role); + } +} + +bool ConsoleItem::canFetchMore() const +{ + // Always fetch all children, too, as the labels depend on them. + foreach (TreeItem *child, children()) { + if (static_cast(child)->m_doFetch) + return true; + } + + return bool(m_doFetch); +} + +void ConsoleItem::fetchMore() +{ + if (m_doFetch) { + m_doFetch(this); + m_doFetch = std::function(); + } + + foreach (TreeItem *child, children()) { + ConsoleItem *item = static_cast(child); + if (item->m_doFetch) { + item->m_doFetch(item); + item->m_doFetch = m_doFetch; + } + } +} + QString ConsoleItem::expression() const { - QString text = m_text; - return text.remove(QChar(0x200b)); // ZERO WIDTH SPACE + return text().remove(QChar(0x200b)); // ZERO WIDTH SPACE } } // QmlJS diff --git a/src/libs/qmljs/consoleitem.h b/src/libs/qmljs/consoleitem.h index 2d822073e71..b6def3db74f 100644 --- a/src/libs/qmljs/consoleitem.h +++ b/src/libs/qmljs/consoleitem.h @@ -32,53 +32,56 @@ #define CONSOLEITEM_H #include "qmljs_global.h" +#include -#include #include +#include namespace QmlJS { -class QMLJS_EXPORT ConsoleItem +class QMLJS_EXPORT ConsoleItem : public Utils::TreeItem { public: + enum Roles { + TypeRole = Qt::UserRole, + FileRole, + LineRole, + ExpressionRole + }; + enum ItemType { - UndefinedType = 0x01, // Can be used for unknown and for Return values - DebugType = 0x02, - WarningType = 0x04, - ErrorType = 0x08, - InputType = 0x10, - DefaultTypes = InputType | UndefinedType + DefaultType = 0x01, // Can be used for unknown and for Return values + DebugType = 0x02, + WarningType = 0x04, + ErrorType = 0x08, + InputType = 0x10, }; Q_DECLARE_FLAGS(ItemTypes, ItemType) - ConsoleItem(ConsoleItem *parent, - ConsoleItem::ItemType type = ConsoleItem::UndefinedType, - const QString &data = QString()); - ~ConsoleItem(); + ConsoleItem(ItemType itemType = ConsoleItem::DefaultType, const QString &expression = QString(), + const QString &file = QString(), int line = -1); + ConsoleItem(ItemType itemType, const QString &expression, + std::function doFetch); - ConsoleItem *child(int number); - int childCount() const; - bool insertChildren(int position, int count); - void insertChild(ConsoleItem *item, bool sorted); - bool insertChild(int position, ConsoleItem *item); - ConsoleItem *parent(); - bool removeChildren(int position, int count); - bool detachChild(int position); - int childNumber() const; - void setText(const QString &text); - QString text() const; + ItemType itemType() const; QString expression() const; + QString text() const; + QString file() const; + int line() const; + QVariant data(int column, int role) const; + bool setData(int column, const QVariant &data, int role); + + bool canFetchMore() const; + void fetchMore(); private: - ConsoleItem *m_parentItem; - QList m_childItems; + ItemType m_itemType; QString m_text; + QString m_file; + int m_line; -public: - ConsoleItem::ItemType itemType; - QString file; - int line; + std::function m_doFetch; }; } // QmlJS diff --git a/src/libs/qmljs/consolemanagerinterface.h b/src/libs/qmljs/consolemanagerinterface.h index 25a0492083a..09f86964710 100644 --- a/src/libs/qmljs/consolemanagerinterface.h +++ b/src/libs/qmljs/consolemanagerinterface.h @@ -50,8 +50,6 @@ public: virtual void showConsolePane() = 0; - virtual ConsoleItem *rootItem() const = 0; - virtual void setScriptEvaluator(IScriptEvaluator *scriptEvaluator) = 0; virtual void setContext(const QString &context) = 0; diff --git a/src/plugins/debugger/debuggerengine.cpp b/src/plugins/debugger/debuggerengine.cpp index 950ffb1e86d..e19c526c7a0 100644 --- a/src/plugins/debugger/debuggerengine.cpp +++ b/src/plugins/debugger/debuggerengine.cpp @@ -530,7 +530,7 @@ void DebuggerEngine::showMessage(const QString &msg, int channel, int timeout) c // qDebug() << qPrintable(msg) << "IN STATE" << state(); QmlJS::ConsoleManagerInterface *consoleManager = QmlJS::ConsoleManagerInterface::instance(); if (channel == ConsoleOutput && consoleManager) - consoleManager->printToConsolePane(QmlJS::ConsoleItem::UndefinedType, msg); + consoleManager->printToConsolePane(QmlJS::ConsoleItem::DefaultType, msg); Internal::showMessage(msg, channel, timeout); if (d->m_runControl) { diff --git a/src/plugins/debugger/qml/qmlengine.cpp b/src/plugins/debugger/qml/qmlengine.cpp index 194f8a159c3..3e7c9c25794 100644 --- a/src/plugins/debugger/qml/qmlengine.cpp +++ b/src/plugins/debugger/qml/qmlengine.cpp @@ -182,7 +182,7 @@ public: QmlV8ObjectData extractData(const QVariant &data) const; void insertSubItems(WatchItem *parent, const QVariantList &properties); void checkForFinishedUpdate(); - ConsoleItem *constructLogItemTree(ConsoleItem *parent, const QmlV8ObjectData &objectData); + ConsoleItem *constructLogItemTree(const QmlV8ObjectData &objectData); public: QHash refVals; // The mapping of target object handles to retrieved values. @@ -220,6 +220,10 @@ public: QmlDebug::QDebugMessageClient *msgClient = 0; QHash callbackForToken; +private: + ConsoleItem *constructLogItemTree(const QmlV8ObjectData &objectData, QList &seenHandles); + void constructChildLogItems(ConsoleItem *item, const QmlV8ObjectData &objectData, + QList &seenHandles); }; static void updateDocument(IDocument *document, const QTextDocument *textDocument) @@ -1012,44 +1016,73 @@ void QmlEngine::selectWatchData(const QByteArray &iname) d->inspectorAdapter.agent()->watchDataSelected(item->id); } -static ConsoleItem *constructLogItemTree(ConsoleItem *parent, - const QVariant &result, +bool compareConsoleItems(const ConsoleItem *a, const ConsoleItem *b) +{ + if (a == 0) + return true; + if (b == 0) + return false; + return a->text() < b->text(); +} + +static ConsoleItem *constructLogItemTree(const QVariant &result, const QString &key = QString()) { bool sorted = boolSetting(SortStructMembers); if (!result.isValid()) return 0; - ConsoleItem *item = new ConsoleItem(parent); + QString text; + ConsoleItem *item = 0; if (result.type() == QVariant::Map) { if (key.isEmpty()) - item->setText(_("Object")); + text = _("Object"); else - item->setText(key + _(" : Object")); + text = key + _(" : Object"); + QMap resultMap = result.toMap(); + QVarLengthArray children(resultMap.size()); QMapIterator i(result.toMap()); + auto it = children.begin(); while (i.hasNext()) { i.next(); - ConsoleItem *child = constructLogItemTree(item, i.value(), i.key()); - if (child) - item->insertChild(child, sorted); + *(it++) = constructLogItemTree(i.value(), i.key()); } + + // Sort before inserting as ConsoleItem::sortChildren causes a whole cascade of changes we + // may not want to handle here. + if (sorted) + std::sort(children.begin(), children.end(), compareConsoleItems); + + item = new ConsoleItem(ConsoleItem::DefaultType, text); + foreach (ConsoleItem *child, children) { + if (child) + item->appendChild(child); + } + } else if (result.type() == QVariant::List) { if (key.isEmpty()) - item->setText(_("List")); + text = _("List"); else - item->setText(QString(_("[%1] : List")).arg(key)); + text = QString(_("[%1] : List")).arg(key); + QVariantList resultList = result.toList(); - for (int i = 0; i < resultList.count(); i++) { - ConsoleItem *child = constructLogItemTree(item, resultList.at(i), - QString::number(i)); + QVarLengthArray children(resultList.size()); + for (int i = 0; i < resultList.count(); i++) + children[i] = constructLogItemTree(resultList.at(i), QString::number(i)); + + if (sorted) + std::sort(children.begin(), children.end(), compareConsoleItems); + + item = new ConsoleItem(ConsoleItem::DefaultType, text); + foreach (ConsoleItem *child, children) { if (child) - item->insertChild(child, sorted); + item->appendChild(child); } } else if (result.canConvert(QVariant::String)) { - item->setText(result.toString()); + item = new ConsoleItem(ConsoleItem::DefaultType, result.toString()); } else { - item->setText(_("Unknown Value")); + item = new ConsoleItem(ConsoleItem::DefaultType, _("Unknown Value")); } return item; @@ -1060,7 +1093,7 @@ void QmlEngine::expressionEvaluated(quint32 queryId, const QVariant &result) if (d->queryIds.contains(queryId)) { d->queryIds.removeOne(queryId); if (auto consoleManager = ConsoleManagerInterface::instance()) { - if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), result)) + if (ConsoleItem *item = constructLogItemTree(result)) consoleManager->printToConsolePane(item); } } @@ -2034,13 +2067,10 @@ void QmlEnginePrivate::messageReceived(const QByteArray &data) //This is most probably due to a wrong eval expression. //Redirect output to console. if (eventType.isEmpty()) { - QmlV8ObjectData entry; - entry.type = "string"; - entry.value = resp.value(_("message")); - if (auto consoleManager = ConsoleManagerInterface::instance()) { - if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), entry)) - consoleManager->printToConsolePane(item); - } + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->printToConsolePane(new ConsoleItem( + ConsoleItem::ErrorType, + resp.value(_(MESSAGE)).toString())); } } //EVENT @@ -2319,37 +2349,99 @@ void QmlEnginePrivate::checkForFinishedUpdate() engine->watchHandler()->notifyUpdateFinished(); } -ConsoleItem *QmlEnginePrivate::constructLogItemTree(ConsoleItem *parent, - const QmlV8ObjectData &objectData) +ConsoleItem *QmlEnginePrivate::constructLogItemTree(const QmlV8ObjectData &objectData) { - bool sorted = boolSetting(SortStructMembers); - if (!objectData.value.isValid()) - return 0; + QList handles; + return constructLogItemTree(objectData, handles); +} +void QmlEnginePrivate::constructChildLogItems(ConsoleItem *item, const QmlV8ObjectData &objectData, + QList &seenHandles) +{ + // We cannot sort the children after attaching them to the parent as that would cause layout + // changes, invalidating cached indices. So we presort them before inserting. + QVarLengthArray children(objectData.properties.size()); + auto it = children.begin(); + foreach (const QVariant &property, objectData.properties) + *(it++) = constructLogItemTree(extractData(property), seenHandles); + + if (boolSetting(SortStructMembers)) + std::sort(children.begin(), children.end(), compareConsoleItems); + + foreach (ConsoleItem *child, children) + item->appendChild(child); +} + +ConsoleItem *QmlEnginePrivate::constructLogItemTree(const QmlV8ObjectData &objectData, + QList &seenHandles) +{ QString text; - if (objectData.name.isEmpty()) + if (objectData.value.isValid()) { text = objectData.value.toString(); - else - text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name)) - .arg(objectData.value.toString()); + } else if (!objectData.type.isEmpty()) { + text = QString::fromLatin1(objectData.type); + } else { + int handle = objectData.handle; + ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType, + QString::fromLatin1(objectData.name), + [this, handle](ConsoleItem *item) + { + DebuggerCommand cmd(LOOKUP); + cmd.arg(HANDLES, QList() << handle); + runCommand(cmd, [this, item, handle](const QVariantMap &response) { + const QVariantMap body = response.value(_(BODY)).toMap(); + QStringList handlesList = body.keys(); + foreach (const QString &handleString, handlesList) { + if (handle != handleString.toInt()) + continue; - ConsoleItem *item = new ConsoleItem(parent, ConsoleItem::UndefinedType, text); + QmlV8ObjectData objectData = extractData(body.value(handleString)); - QSet childrenFetched; - foreach (const QVariant &property, objectData.properties) { - const QmlV8ObjectData childObjectData = extractData(property); - if (childObjectData.handle == objectData.handle) - continue; - ConsoleItem *child = constructLogItemTree(item, childObjectData); - if (child) { - const QString text = child->text(); - if (childrenFetched.contains(text)) - continue; - childrenFetched.insert(text); - item->insertChild(child, sorted); - } + // keep original name, if possible + QString name = item->expression(); + if (name.isEmpty()) + name = QString::fromLatin1(objectData.name); + + QString value = objectData.value.isValid() ? + objectData.value.toString() : QString::fromLatin1(objectData.type); + + // We can do setData() and cause dataChanged() here, but only because this + // callback is executed after fetchMore() has returned. + item->model()->setData(item->index(), + QString::fromLatin1("%1: %2").arg(name).arg(value), + ConsoleItem::ExpressionRole); + + QList newHandles; + constructChildLogItems(item, objectData, newHandles); + + break; + } + }); + }); + return item; } + if (!objectData.name.isEmpty()) + text = QString(_("%1: %2")).arg(QString::fromLatin1(objectData.name)).arg(text); + + if (objectData.properties.isEmpty()) + return new ConsoleItem(ConsoleItem::DefaultType, text); + + if (seenHandles.contains(objectData.handle)) { + ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType, text, + [this, objectData](ConsoleItem *item) + { + QList newHandles; + constructChildLogItems(item, objectData, newHandles); + }); + return item; + } + + seenHandles.append(objectData.handle); + ConsoleItem *item = new ConsoleItem(ConsoleItem::DefaultType, text); + constructChildLogItems(item, objectData, seenHandles); + seenHandles.removeLast(); + return item; } @@ -2397,14 +2489,22 @@ void QmlEnginePrivate::insertSubItems(WatchItem *parent, const QVariantList &pro void QmlEnginePrivate::handleExecuteDebuggerCommand(const QVariantMap &response) { - QmlV8ObjectData body = extractData(response.value(_(BODY))); - if (auto consoleManager = ConsoleManagerInterface::instance()) { - if (ConsoleItem *item = constructLogItemTree(consoleManager->rootItem(), body)) - consoleManager->printToConsolePane(item); + auto consoleManager = ConsoleManagerInterface::instance(); + if (!consoleManager) + return; + + auto it = response.constFind(_(SUCCESS)); + if (it != response.constEnd() && it.value().toBool()) { + consoleManager->printToConsolePane(constructLogItemTree( + extractData(response.value(_(BODY))))); + + // Update the locals + foreach (int index, currentFrameScopes) + scope(index); + } else { + consoleManager->printToConsolePane(new ConsoleItem(ConsoleItem::ErrorType, + response.value(_(MESSAGE)).toString())); } - // Update the locals - foreach (int index, currentFrameScopes) - scope(index); } void QmlEnginePrivate::handleLookup(const QVariantMap &response) diff --git a/src/plugins/debugger/qml/qmlengineutils.cpp b/src/plugins/debugger/qml/qmlengineutils.cpp index 08670f46e97..bdd36e24e4a 100644 --- a/src/plugins/debugger/qml/qmlengineutils.cpp +++ b/src/plugins/debugger/qml/qmlengineutils.cpp @@ -250,12 +250,9 @@ void appendDebugOutput(QtMsgType type, const QString &message, const QDebugConte return; } - if (auto consoleManager = ConsoleManagerInterface::instance()) { - ConsoleItem *item = new ConsoleItem(consoleManager->rootItem(), itemType, message); - item->file = info.file; - item->line = info.line; - consoleManager->printToConsolePane(item); - } + if (auto consoleManager = ConsoleManagerInterface::instance()) + consoleManager->printToConsolePane(new ConsoleItem(itemType, message, info.file, + info.line)); } void clearExceptionSelection() diff --git a/src/plugins/debugger/qml/qmlv8debuggerclientconstants.h b/src/plugins/debugger/qml/qmlv8debuggerclientconstants.h index 5af1d5f4b57..e1e4683cc66 100644 --- a/src/plugins/debugger/qml/qmlv8debuggerclientconstants.h +++ b/src/plugins/debugger/qml/qmlv8debuggerclientconstants.h @@ -105,6 +105,8 @@ const char REFS[] = "refs"; const char BODY[] = "body"; const char NAME[] = "name"; const char VALUE[] = "value"; +const char SUCCESS[] = "success"; +const char MESSAGE[] = "message"; const char OBJECT[] = "{}"; const char ARRAY[] = "[]"; diff --git a/src/plugins/qmljstools/qmlconsoleedit.cpp b/src/plugins/qmljstools/qmlconsoleedit.cpp index d411fed2255..948957878af 100644 --- a/src/plugins/qmljstools/qmlconsoleedit.cpp +++ b/src/plugins/qmljstools/qmlconsoleedit.cpp @@ -215,10 +215,10 @@ void QmlConsoleEdit::handleUpKey() if (model->hasIndex(currentRow, 0)) { QModelIndex index = model->index(currentRow, 0); if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data( - index, QmlConsoleItemModel::TypeRole).toInt()) { + index, ConsoleItem::TypeRole).toInt()) { m_historyIndex = index; replaceCurrentScript( - model->data(index, QmlConsoleItemModel::ExpressionRole).toString()); + model->data(index, ConsoleItem::ExpressionRole).toString()); break; } } @@ -235,13 +235,13 @@ void QmlConsoleEdit::handleDownKey() if (model->hasIndex(currentRow, 0)) { QModelIndex index = model->index(currentRow, 0); if (ConsoleItem::InputType == (ConsoleItem::ItemType)model->data( - index, QmlConsoleItemModel::TypeRole).toInt()) { + index, ConsoleItem::TypeRole).toInt()) { m_historyIndex = index; if (currentRow == model->rowCount() - 1) { replaceCurrentScript(m_cachedScript); } else { replaceCurrentScript( - model->data(index, QmlConsoleItemModel::ExpressionRole).toString()); + model->data(index, ConsoleItem::ExpressionRole).toString()); } break; } diff --git a/src/plugins/qmljstools/qmlconsoleitemdelegate.cpp b/src/plugins/qmljstools/qmlconsoleitemdelegate.cpp index d42af3cd212..1bc78325da2 100644 --- a/src/plugins/qmljstools/qmlconsoleitemdelegate.cpp +++ b/src/plugins/qmljstools/qmlconsoleitemdelegate.cpp @@ -92,7 +92,7 @@ QColor QmlConsoleItemDelegate::drawBackground(QPainter *painter, const QRect &re { painter->save(); ConsoleItem::ItemType itemType = (ConsoleItem::ItemType)index.data( - QmlConsoleItemModel::TypeRole).toInt(); + ConsoleItem::TypeRole).toInt(); QColor backgroundColor; switch (itemType) { case ConsoleItem::DebugType: @@ -138,7 +138,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem QColor textColor; QIcon taskIcon; ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data( - QmlConsoleItemModel::TypeRole).toInt(); + ConsoleItem::TypeRole).toInt(); switch (type) { case ConsoleItem::DebugType: textColor = QColor(CONSOLE_LOG_TEXT_COLOR); @@ -175,7 +175,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem } int width = view->width() - level * view->indentation() - view->verticalScrollBar()->width(); bool showTypeIcon = index.parent() == QModelIndex(); - bool showExpandableIcon = type == ConsoleItem::UndefinedType; + bool showExpandableIcon = type == ConsoleItem::DefaultType; QRect rect(opt.rect.x(), opt.rect.top(), width, opt.rect.height()); ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon); @@ -206,7 +206,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem if (showExpandableIcon) { // Paint ExpandableIconArea: QIcon expandCollapseIcon; - if (index.model()->rowCount(index)) { + if (index.model()->rowCount(index) || index.model()->canFetchMore(index)) { if (view->isExpanded(index)) expandCollapseIcon = m_collapseIcon; else @@ -219,7 +219,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem if (showFileLineInfo) { // Check for file info - QString file = index.data(QmlConsoleItemModel::FileRole).toString(); + QString file = index.data(ConsoleItem::FileRole).toString(); const QUrl fileUrl = QUrl(file); if (fileUrl.isLocalFile()) file = fileUrl.toLocalFile(); @@ -244,7 +244,7 @@ void QmlConsoleItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem } // Paint LineArea - QString lineText = index.data(QmlConsoleItemModel::LineRole).toString(); + QString lineText = index.data(ConsoleItem::LineRole).toString(); painter->setClipRect(positions.lineArea()); const int realLineWidth = fm.width(lineText); painter->drawText(positions.lineAreaRight() - realLineWidth, @@ -277,9 +277,9 @@ QSize QmlConsoleItemDelegate::sizeHint(const QStyleOptionViewItem &option, return QSize(width, m_cachedHeight); ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data( - QmlConsoleItemModel::TypeRole).toInt(); + ConsoleItem::TypeRole).toInt(); bool showTypeIcon = index.parent() == QModelIndex(); - bool showExpandableIcon = type == ConsoleItem::UndefinedType; + bool showExpandableIcon = type == ConsoleItem::DefaultType; QRect rect(level * view->indentation(), 0, width, 0); ConsoleItemPositions positions(rect, opt.font, showTypeIcon, showExpandableIcon); @@ -322,7 +322,7 @@ void QmlConsoleItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QmlConsoleEdit *edtr = qobject_cast(editor); - edtr->insertPlainText(index.data(QmlConsoleItemModel::ExpressionRole).toString()); + edtr->insertPlainText(index.data(ConsoleItem::ExpressionRole).toString()); } void QmlConsoleItemDelegate::setModelData(QWidget *editor, @@ -330,8 +330,8 @@ void QmlConsoleItemDelegate::setModelData(QWidget *editor, const QModelIndex &index) const { QmlConsoleEdit *edtr = qobject_cast(editor); - model->setData(index, edtr->getCurrentScript(), Qt::DisplayRole); - model->setData(index, ConsoleItem::InputType, QmlConsoleItemModel::TypeRole); + model->setData(index, edtr->getCurrentScript(), ConsoleItem::ExpressionRole); + model->setData(index, ConsoleItem::InputType, ConsoleItem::TypeRole); } void QmlConsoleItemDelegate::updateEditorGeometry(QWidget *editor, diff --git a/src/plugins/qmljstools/qmlconsoleitemmodel.cpp b/src/plugins/qmljstools/qmlconsoleitemmodel.cpp index 1458c812233..53c28b2f939 100644 --- a/src/plugins/qmljstools/qmlconsoleitemmodel.cpp +++ b/src/plugins/qmljstools/qmlconsoleitemmodel.cpp @@ -45,89 +45,55 @@ namespace Internal { /////////////////////////////////////////////////////////////////////// QmlConsoleItemModel::QmlConsoleItemModel(QObject *parent) : - QAbstractItemModel(parent), - m_hasEditableRow(false), - m_rootItem(new ConsoleItem(0)), + Utils::TreeModel(new ConsoleItem, parent), m_maxSizeOfFileName(0) { -} - -QmlConsoleItemModel::~QmlConsoleItemModel() -{ - delete m_rootItem; + clear(); } void QmlConsoleItemModel::clear() { - beginResetModel(); - delete m_rootItem; - m_rootItem = new ConsoleItem(0); - endResetModel(); - - if (m_hasEditableRow) - appendEditableRow(); + Utils::TreeModel::clear(); + appendItem(new ConsoleItem(ConsoleItem::InputType)); + emit selectEditableRow(index(0, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect); } -bool QmlConsoleItemModel::appendItem(ConsoleItem *item, int position) +void QmlConsoleItemModel::appendItem(ConsoleItem *item, int position) { if (position < 0) - position = m_rootItem->childCount() - 1; + position = rootItem()->childCount() - 1; // append before editable row if (position < 0) position = 0; - beginInsertRows(QModelIndex(), position, position); - bool success = m_rootItem->insertChild(position, item); - endInsertRows(); - - return success; + rootItem()->insertChild(position, item); } -bool QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType, +void QmlConsoleItemModel::appendMessage(ConsoleItem::ItemType itemType, const QString &message, int position) { - return appendItem(new ConsoleItem(m_rootItem, itemType, message), position); + appendItem(new ConsoleItem(itemType, message), position); } -void QmlConsoleItemModel::setHasEditableRow(bool hasEditableRow) +void QmlConsoleItemModel::shiftEditableRow() { - if (m_hasEditableRow && !hasEditableRow) - removeEditableRow(); + int position = rootItem()->childCount(); + Q_ASSERT(position > 0); - if (!m_hasEditableRow && hasEditableRow) - appendEditableRow(); + // Disable editing for old editable row + rootItem()->lastChild()->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable); - m_hasEditableRow = hasEditableRow; -} - -bool QmlConsoleItemModel::hasEditableRow() const -{ - return m_hasEditableRow; -} - -void QmlConsoleItemModel::appendEditableRow() -{ - int position = m_rootItem->childCount(); - if (appendItem(new ConsoleItem(m_rootItem, ConsoleItem::InputType), position)) - emit selectEditableRow(index(position, 0), QItemSelectionModel::ClearAndSelect); -} - -void QmlConsoleItemModel::removeEditableRow() -{ - if (m_rootItem->child(m_rootItem->childCount() - 1)->itemType == ConsoleItem::InputType) - removeRow(m_rootItem->childCount() - 1); + appendItem(new ConsoleItem(ConsoleItem::InputType), position); + emit selectEditableRow(index(position, 0, QModelIndex()), QItemSelectionModel::ClearAndSelect); } int QmlConsoleItemModel::sizeOfFile(const QFont &font) { - int lastReadOnlyRow = m_rootItem->childCount(); - if (m_hasEditableRow) - lastReadOnlyRow -= 2; - else - lastReadOnlyRow -= 1; + int lastReadOnlyRow = rootItem()->childCount(); + lastReadOnlyRow -= 2; // skip editable row if (lastReadOnlyRow < 0) return 0; - QString filename = m_rootItem->child(lastReadOnlyRow)->file; + QString filename = static_cast(rootItem()->child(lastReadOnlyRow))->file(); const int pos = filename.lastIndexOf(QLatin1Char('/')); if (pos != -1) filename = filename.mid(pos + 1); @@ -144,141 +110,5 @@ int QmlConsoleItemModel::sizeOfLineNumber(const QFont &font) return fm.width(QLatin1String("88888")); } -QVariant QmlConsoleItemModel::data(const QModelIndex &index, int role) const -{ - if (!index.isValid()) - return QVariant(); - - ConsoleItem *item = getItem(index); - - if (role == Qt::DisplayRole ) - return item->text(); - else if (role == QmlConsoleItemModel::TypeRole) - return int(item->itemType); - else if (role == QmlConsoleItemModel::FileRole) - return item->file; - else if (role == QmlConsoleItemModel::LineRole) - return item->line; - else if (role == QmlConsoleItemModel::ExpressionRole) - return item->expression(); - else - return QVariant(); -} - -QModelIndex QmlConsoleItemModel::index(int row, int column, const QModelIndex &parent) const -{ - if (parent.isValid() && parent.column() != 0) - return QModelIndex(); - - if (column > 0) - return QModelIndex(); - - ConsoleItem *parentItem = getItem(parent); - - ConsoleItem *childItem = parentItem->child(row); - if (childItem) - return createIndex(row, column, childItem); - else - return QModelIndex(); -} - -QModelIndex QmlConsoleItemModel::parent(const QModelIndex &index) const -{ - if (!index.isValid()) - return QModelIndex(); - - ConsoleItem *childItem = getItem(index); - ConsoleItem *parentItem = childItem->parent(); - - if (parentItem == m_rootItem) - return QModelIndex(); - - if (!parentItem) - return QModelIndex(); - return createIndex(parentItem->childNumber(), 0, parentItem); -} - -int QmlConsoleItemModel::rowCount(const QModelIndex &parent) const -{ - ConsoleItem *parentItem = getItem(parent); - - return parentItem->childCount(); -} - -int QmlConsoleItemModel::columnCount(const QModelIndex & /* parent */) const -{ - return 1; -} - -Qt::ItemFlags QmlConsoleItemModel::flags(const QModelIndex &index) const -{ - if (!index.isValid()) - return 0; - - ConsoleItem *item = getItem(index); - if (m_hasEditableRow && item->parent() == m_rootItem - && index.row() == m_rootItem->childCount() - 1) - return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -bool QmlConsoleItemModel::setData(const QModelIndex &index, const QVariant &value, int role) -{ - ConsoleItem *item = getItem(index); - bool result = false; - if (role == Qt::DisplayRole) { - item->setText(value.toString()); - result = true; - } else if (role == QmlConsoleItemModel::TypeRole) { - item->itemType = (ConsoleItem::ItemType)value.toInt(); - result = true; - } else if (role == QmlConsoleItemModel::FileRole) { - item->file = value.toString(); - result = true; - } else if (role == QmlConsoleItemModel::LineRole) { - item->line = value.toInt(); - result = true; - } - - if (result) - emit dataChanged(index, index); - - return result; -} - -bool QmlConsoleItemModel::insertRows(int position, int rows, const QModelIndex &parent) -{ - ConsoleItem *parentItem = getItem(parent); - bool success; - - beginInsertRows(parent, position, position + rows - 1); - success = parentItem->insertChildren(position, rows); - endInsertRows(); - - return success; -} - -bool QmlConsoleItemModel::removeRows(int position, int rows, const QModelIndex &parent) -{ - ConsoleItem *parentItem = getItem(parent); - bool success = true; - - beginRemoveRows(parent, position, position + rows - 1); - success = parentItem->removeChildren(position, rows); - endRemoveRows(); - - return success; -} - -ConsoleItem *QmlConsoleItemModel::getItem(const QModelIndex &index) const -{ - if (index.isValid()) { - ConsoleItem *item = static_cast(index.internalPointer()); - if (item) - return item; - } - return m_rootItem; -} - } // Internal } // QmlJSTools diff --git a/src/plugins/qmljstools/qmlconsoleitemmodel.h b/src/plugins/qmljstools/qmlconsoleitemmodel.h index 4e25ad3e617..03900985fe4 100644 --- a/src/plugins/qmljstools/qmlconsoleitemmodel.h +++ b/src/plugins/qmljstools/qmlconsoleitemmodel.h @@ -32,8 +32,8 @@ #define QMLCONSOLEITEMMODEL_H #include +#include -#include #include QT_FORWARD_DECLARE_CLASS(QFont) @@ -41,58 +41,29 @@ QT_FORWARD_DECLARE_CLASS(QFont) namespace QmlJSTools { namespace Internal { -class QmlConsoleItemModel : public QAbstractItemModel +class QmlConsoleItemModel : public Utils::TreeModel { Q_OBJECT public: - enum Roles { TypeRole = Qt::UserRole, FileRole, LineRole, ExpressionRole }; explicit QmlConsoleItemModel(QObject *parent = 0); - ~QmlConsoleItemModel(); - void setHasEditableRow(bool hasEditableRow); - bool hasEditableRow() const; - void appendEditableRow(); - void removeEditableRow(); + void shiftEditableRow(); - bool appendItem(QmlJS::ConsoleItem *item, int position = -1); - bool appendMessage(QmlJS::ConsoleItem::ItemType itemType, const QString &message, + void appendItem(QmlJS::ConsoleItem *item, int position = -1); + void appendMessage(QmlJS::ConsoleItem::ItemType itemType, const QString &message, int position = -1); - int rowCount(const QModelIndex &parent = QModelIndex()) const; - int sizeOfFile(const QFont &font); int sizeOfLineNumber(const QFont &font); - QmlJS::ConsoleItem *root() const { return m_rootItem; } - public slots: void clear(); signals: void selectEditableRow(const QModelIndex &index, QItemSelectionModel::SelectionFlags flags); - void rowInserted(const QModelIndex &index); - -protected: - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - QModelIndex parent(const QModelIndex &index) const; - - - int columnCount(const QModelIndex &parent = QModelIndex()) const; - - Qt::ItemFlags flags(const QModelIndex &index) const; - bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); - - bool insertRows(int position, int rows, const QModelIndex &parent = QModelIndex()); - bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()); - - QmlJS::ConsoleItem *getItem(const QModelIndex &index) const; private: - bool m_hasEditableRow; - QmlJS::ConsoleItem *m_rootItem; int m_maxSizeOfFileName; }; diff --git a/src/plugins/qmljstools/qmlconsolemanager.cpp b/src/plugins/qmljstools/qmlconsolemanager.cpp index 616a1702ac0..fc09fe3a37f 100644 --- a/src/plugins/qmljstools/qmlconsolemanager.cpp +++ b/src/plugins/qmljstools/qmlconsolemanager.cpp @@ -57,7 +57,6 @@ QmlConsoleManager::QmlConsoleManager(QObject *parent) d(new QmlConsoleManagerPrivate) { d->qmlConsoleItemModel = new Internal::QmlConsoleItemModel(this); - d->qmlConsoleItemModel->setHasEditableRow(true); d->qmlConsolePane = new Internal::QmlConsolePane(this); d->scriptEvaluator = 0; ExtensionSystem::PluginManager::addObject(d->qmlConsolePane); @@ -76,11 +75,6 @@ void QmlConsoleManager::showConsolePane() d->qmlConsolePane->popup(Core::IOutputPane::ModeSwitch); } -ConsoleItem *QmlConsoleManager::rootItem() const -{ - return d->qmlConsoleItemModel->root(); -} - void QmlConsoleManager::setScriptEvaluator(IScriptEvaluator *scriptEvaluator) { d->scriptEvaluator = scriptEvaluator; @@ -109,7 +103,7 @@ void QmlConsoleManager::printToConsolePane(ConsoleItem *item, bool bringToForegr { if (!d->qmlConsolePane) return; - if (item->itemType == ConsoleItem::ErrorType) + if (item->itemType() == ConsoleItem::ErrorType) bringToForeground = true; if (bringToForeground) d->qmlConsolePane->popup(Core::IOutputPane::ModeSwitch); @@ -118,47 +112,6 @@ void QmlConsoleManager::printToConsolePane(ConsoleItem *item, bool bringToForegr namespace Internal { -ConsoleItem *constructLogItemTree(ConsoleItem *parent, const QVariant &result, - const QString &key = QString()) -{ - if (!result.isValid()) - return 0; - - ConsoleItem *item = new ConsoleItem(parent); - if (result.type() == QVariant::Map) { - if (key.isEmpty()) - item->setText(QLatin1String("Object")); - else - item->setText(key + QLatin1String(" : Object")); - - QMapIterator i(result.toMap()); - while (i.hasNext()) { - i.next(); - ConsoleItem *child = constructLogItemTree(item, i.value(), i.key()); - if (child) - item->insertChild(child, true); - } - } else if (result.type() == QVariant::List) { - if (key.isEmpty()) - item->setText(QLatin1String("List")); - else - item->setText(QString::fromLatin1("[%1] : List").arg(key)); - QVariantList resultList = result.toList(); - for (int i = 0; i < resultList.count(); i++) { - ConsoleItem *child = constructLogItemTree(item, resultList.at(i), - QString::number(i)); - if (child) - item->insertChild(child, true); - } - } else if (result.canConvert(QVariant::String)) { - item->setText(result.toString()); - } else { - item->setText(QLatin1String("Unknown Value")); - } - - return item; -} - QmlConsoleItemModel *QmlConsoleModel::qmlConsoleItemModel() { QmlConsoleManager *manager = qobject_cast(QmlConsoleManager::instance()); @@ -172,15 +125,15 @@ void QmlConsoleModel::evaluate(const QString &expression) QmlConsoleManager *manager = qobject_cast(QmlConsoleManager::instance()); if (manager) { if (manager->d->scriptEvaluator) { - QmlConsoleModel::qmlConsoleItemModel()->appendEditableRow(); + QmlConsoleModel::qmlConsoleItemModel()->shiftEditableRow(); manager->d->scriptEvaluator->evaluateScript(expression); } else { - ConsoleItem *root = manager->rootItem(); - ConsoleItem *item = constructLogItemTree( - root, QCoreApplication::translate("QmlJSTools::Internal::QmlConsoleModel", - "Can only evaluate during a QML debug session.")); + ConsoleItem *item = new ConsoleItem( + ConsoleItem::ErrorType, QCoreApplication::translate( + "QmlJSTools::Internal::QmlConsoleModel", + "Can only evaluate during a QML debug session.")); if (item) { - QmlConsoleModel::qmlConsoleItemModel()->appendEditableRow(); + QmlConsoleModel::qmlConsoleItemModel()->shiftEditableRow(); manager->printToConsolePane(item); } } diff --git a/src/plugins/qmljstools/qmlconsolemanager.h b/src/plugins/qmljstools/qmlconsolemanager.h index 190220a01ec..5ca811ff692 100644 --- a/src/plugins/qmljstools/qmlconsolemanager.h +++ b/src/plugins/qmljstools/qmlconsolemanager.h @@ -52,8 +52,6 @@ public: void showConsolePane(); - QmlJS::ConsoleItem *rootItem() const; - void setScriptEvaluator(QmlJS::IScriptEvaluator *scriptEvaluator); void setContext(const QString &context); diff --git a/src/plugins/qmljstools/qmlconsoleproxymodel.cpp b/src/plugins/qmljstools/qmlconsoleproxymodel.cpp index 64077498de4..5ab1fdb9550 100644 --- a/src/plugins/qmljstools/qmlconsoleproxymodel.cpp +++ b/src/plugins/qmljstools/qmlconsoleproxymodel.cpp @@ -38,7 +38,7 @@ namespace Internal { QmlConsoleProxyModel::QmlConsoleProxyModel(QObject *parent) : QSortFilterProxyModel(parent), - m_filter(ConsoleItem::DefaultTypes) + m_filter(ConsoleItem::DefaultType | ConsoleItem::InputType) { } @@ -72,7 +72,7 @@ bool QmlConsoleProxyModel::filterAcceptsRow(int sourceRow, { QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); return m_filter.testFlag((ConsoleItem::ItemType)sourceModel()->data( - index, QmlConsoleItemModel::TypeRole).toInt()); + index, ConsoleItem::TypeRole).toInt()); } void QmlConsoleProxyModel::onRowsInserted(const QModelIndex &index, int start, int end) diff --git a/src/plugins/qmljstools/qmlconsoleview.cpp b/src/plugins/qmljstools/qmlconsoleview.cpp index ee374f0f008..5c0f054a422 100644 --- a/src/plugins/qmljstools/qmlconsoleview.cpp +++ b/src/plugins/qmljstools/qmlconsoleview.cpp @@ -147,9 +147,9 @@ void QmlConsoleView::mousePressEvent(QMouseEvent *event) QModelIndex index = indexAt(pos); if (index.isValid()) { ConsoleItem::ItemType type = (ConsoleItem::ItemType)index.data( - QmlConsoleItemModel::TypeRole).toInt(); + ConsoleItem::TypeRole).toInt(); bool handled = false; - if (type == ConsoleItem::UndefinedType) { + if (type == ConsoleItem::DefaultType) { bool showTypeIcon = index.parent() == QModelIndex(); ConsoleItemPositions positions(visualRect(index), viewOptions().font, showTypeIcon, true); @@ -222,14 +222,14 @@ void QmlConsoleView::onRowActivated(const QModelIndex &index) return; // See if we have file and line Info - QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString(); + QString filePath = model()->data(index, ConsoleItem::FileRole).toString(); const QUrl fileUrl = QUrl(filePath); if (fileUrl.isLocalFile()) filePath = fileUrl.toLocalFile(); if (!filePath.isEmpty()) { QFileInfo fi(filePath); if (fi.exists() && fi.isFile() && fi.isReadable()) { - int line = model()->data(index, QmlConsoleItemModel::LineRole).toInt(); + int line = model()->data(index, ConsoleItem::LineRole).toInt(); Core::EditorManager::openEditorAt(fi.canonicalFilePath(), line); } } @@ -240,15 +240,15 @@ void QmlConsoleView::copyToClipboard(const QModelIndex &index) if (!index.isValid()) return; - QString contents = model()->data(index, QmlConsoleItemModel::ExpressionRole).toString(); + QString contents = model()->data(index, ConsoleItem::ExpressionRole).toString(); // See if we have file and line Info - QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString(); + QString filePath = model()->data(index, ConsoleItem::FileRole).toString(); const QUrl fileUrl = QUrl(filePath); if (fileUrl.isLocalFile()) filePath = fileUrl.toLocalFile(); if (!filePath.isEmpty()) { contents = QString::fromLatin1("%1 %2: %3").arg(contents).arg(filePath).arg( - model()->data(index, QmlConsoleItemModel::LineRole).toString()); + model()->data(index, ConsoleItem::LineRole).toString()); } QClipboard *cb = QApplication::clipboard(); cb->setText(contents); @@ -260,7 +260,7 @@ bool QmlConsoleView::canShowItemInTextEditor(const QModelIndex &index) return false; // See if we have file and line Info - QString filePath = model()->data(index, QmlConsoleItemModel::FileRole).toString(); + QString filePath = model()->data(index, ConsoleItem::FileRole).toString(); const QUrl fileUrl = QUrl(filePath); if (fileUrl.isLocalFile()) filePath = fileUrl.toLocalFile();