diff --git a/README b/README index 98ef7122b..c1a45ec8e 100644 --- a/README +++ b/README @@ -5,8 +5,8 @@ on the latest Git revision of Qt v4.8. There are several things you should be aware before considering Katie: - some components and tools have been removed: - - QMake, D-Bus viewer, Linguist, Assistant, checksdk, macdeployqt, pixeltool, - qconfig, qdoc, qev, qvfb, runonphone and other non-essential + - QMake, Linguist, Assistant, checksdk, macdeployqt, pixeltool, qconfig, + qdoc, qev, qvfb, runonphone and other non-essential - Qt3Support, QtWebKit, ActiveQt and Phonon - some things have changed: diff --git a/cmake/KatieConfig.cmake b/cmake/KatieConfig.cmake index d33fe22a3..a50059704 100644 --- a/cmake/KatieConfig.cmake +++ b/cmake/KatieConfig.cmake @@ -80,6 +80,7 @@ if(NOT KATIE_COMPAT EQUAL FALSE AND NOT KATIE_COMPAT EQUAL OFF) set(QT_VERSION ${KATIE_VERSION}) set(QT_INCLUDES ${KATIE_INCLUDES}) set(QT_INCLUDE_DIR ${KATIE_INCLUDES}) + set(QT4_INCLUDE_DIR ${KATIE_INCLUDES}) set(QT_LIBRARIES ${KATIE_LIBRARIES}) set(QT_USE_FILE ${KATIE_MKSPECS}) set(QT_MKSPECS_DIR ${KATIE_MKSPECS_DIR}) @@ -128,9 +129,13 @@ if(NOT KATIE_COMPAT EQUAL FALSE AND NOT KATIE_COMPAT EQUAL OFF) foreach(component ${_COMPONENTS}) string(TOUPPER ${component} uppercomp) set(QT_QT${uppercomp}_FOUND ${KATIE_${uppercomp}_FOUND}) + set(QT4_QT${uppercomp}_FOUND ${KATIE_${uppercomp}_FOUND}) set(QT_QT${uppercomp}_LIBRARY "${KATIE_${uppercomp}_LIBRARIES}") + set(QT4_QT${uppercomp}_LIBRARY "${KATIE_${uppercomp}_LIBRARIES}") set(QT_QT${uppercomp}_LIBRARIES "${KATIE_${uppercomp}_LIBRARIES}") + set(QT4_QT${uppercomp}_LIBRARIES "${KATIE_${uppercomp}_LIBRARIES}") set(QT_QT${uppercomp}_INCLUDE_DIR "@QT_HEADERS_PATH@" "${KATIE_${uppercomp}_INCLUDES}") + set(QT4_QT${uppercomp}_INCLUDE_DIR "@QT_HEADERS_PATH@" "${KATIE_${uppercomp}_INCLUDES}") # TODO: component definitions endforeach() diff --git a/mkspecs/mkspecs.cmake b/mkspecs/mkspecs.cmake index 04deb2571..f7cae4e50 100644 --- a/mkspecs/mkspecs.cmake +++ b/mkspecs/mkspecs.cmake @@ -10,7 +10,7 @@ set(KATIE_BUGREPORT "xakepa10@gmail.com") set(KATIE_URL "http://github.com/fluxer/katie") set(KATIE_COMPONENTS "Core Gui DBus Declarative Designer DesignerComponents Help Multimedia Network OpenGL Sql Svg Xml XmlPatterns Script ScriptTools Test UiTools") # TODO: make dbus tools optional -set(KATIE_TOOLS "moc uic rcc qdbusxml2cpp qdbuscpp2xml qhelpgenerator qcollectiongenerator lupdate lrelease lconvert designer") +set(KATIE_TOOLS "moc uic rcc qdbusxml2cpp qdbuscpp2xml qdbusviewer qhelpgenerator qcollectiongenerator lupdate lrelease lconvert designer") set(QT_LICENSE "Open Source") set(QT_PRODUCT "AwesomeSauce") # it's not a bug, it's a feature! diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD index 1e28f6378..68abd161a 100644 --- a/package/archlinux/PKGBUILD +++ b/package/archlinux/PKGBUILD @@ -54,9 +54,11 @@ package() { install -vDm644 ../katie/LGPL_EXCEPTION.txt \ "${pkgdir}/usr/share/licenses/katie-git/LGPL_EXCEPTION.txt" - # register Designer as application + # register applications install -v -Dm644 ../katie/src/tools/designer/images/designer.png \ "${pkgdir}/usr/share/pixmaps/designer-katie.png" + install -v -Dm644 ../katie/src/tools/qdbusviewer/images/qdbusviewer.png \ + "${pkgdir}/usr/share/pixmaps/qdbusviewer-katie.png" mkdir -p "${pkgdir}/usr/share/applications" cat > "${pkgdir}/usr/share/applications/designer-katie.desktop" << EOF [Desktop Entry] @@ -69,5 +71,16 @@ Terminal=false Encoding=UTF-8 Type=Application Categories=Qt;Development; +EOF + cat > "${pkgdir}/usr/share/applications/qdbusviewer-katie.desktop" << EOF +[Desktop Entry] +Name=Katie D-Bus Viewer +Comment=Debug D-Bus applications +Exec=qdbusviewer-katie +Icon=qdbusviewer-katie.png +Terminal=false +Encoding=UTF-8 +Type=Application +Categories=Qt;Development;Debugger; EOF } diff --git a/package/entropy/katie/SRCBUILD b/package/entropy/katie/SRCBUILD index 14ebf0de4..2eb8d590e 100644 --- a/package/entropy/katie/SRCBUILD +++ b/package/entropy/katie/SRCBUILD @@ -48,6 +48,8 @@ src_install() { # register Designer as application install -vDm644 ../katie.git/src/tools/designer/images/designer.png \ "$INSTALL_DIR/usr/share/pixmaps/designer-katie.png" + install -vDm644 ../katie.git/src/tools/qdbusviewer/images/qdbusviewer.png \ + "$INSTALL_DIR/usr/share/pixmaps/qdbusviewer-katie.png" mkdir -p "$INSTALL_DIR/usr/share/applications" cat > "$INSTALL_DIR/usr/share/applications/designer-katie.desktop" << EOF [Desktop Entry] @@ -60,6 +62,17 @@ Terminal=false Encoding=UTF-8 Type=Application Categories=Qt;Development; +EOF + cat > "$INSTALL_DIR/usr/share/applications/qdbusviewer-katie.desktop" << EOF +[Desktop Entry] +Name=Katie D-Bus Viewer +Comment=Debug D-Bus applications +Exec=qdbusviewer-katie +Icon=qdbusviewer-katie.png +Terminal=false +Encoding=UTF-8 +Type=Application +Categories=Qt;Development;Debugger; EOF } diff --git a/src/tools/CMakeLists.txt b/src/tools/CMakeLists.txt index ec52d1418..ecbc265a8 100644 --- a/src/tools/CMakeLists.txt +++ b/src/tools/CMakeLists.txt @@ -13,6 +13,7 @@ add_subdirectory(lconvert) if(NOT KATIE_BOOTSTRAP) if(WITH_DBUS AND DBUS_FOUND) add_subdirectory(qdbus) + add_subdirectory(qdbusviewer) endif() add_subdirectory(qhelpgenerator) add_subdirectory(qcollectiongenerator) diff --git a/src/tools/qdbusviewer/CMakeLists.txt b/src/tools/qdbusviewer/CMakeLists.txt new file mode 100644 index 000000000..8943141fc --- /dev/null +++ b/src/tools/qdbusviewer/CMakeLists.txt @@ -0,0 +1,48 @@ +# add_definitions() +set(EXTRA_QDBUSVIEWER_LIBS KtCore KtGui KtXml KtDBus) + +include_directories( + ${CMAKE_BINARY_DIR}/include + ${CMAKE_BINARY_DIR}/privateinclude + ${CMAKE_BINARY_DIR}/include/QtCore + ${CMAKE_BINARY_DIR}/privateinclude/QtCore + ${CMAKE_BINARY_DIR}/include/QtGui + ${CMAKE_BINARY_DIR}/privateinclude/QtGui + ${CMAKE_BINARY_DIR}/include/QtXml + ${CMAKE_BINARY_DIR}/privateinclude/QtXml + ${CMAKE_BINARY_DIR}/include/QtDBus + ${CMAKE_BINARY_DIR}/privateinclude/QtDBus +) + +set(QDBUSVIEWER_HEADERS + ${QDBUSVIEWER_HEADERS} + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusviewer.h + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusmodel.h + ${CMAKE_CURRENT_SOURCE_DIR}/propertydialog.h +) + +set(QDBUSVIEWER_SOURCES + ${QDBUSVIEWER_SOURCES} + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusviewer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusmodel.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/propertydialog.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusviewermain.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/qdbusviewer.qrc +) + +katie_resources(${QDBUSVIEWER_SOURCES} ${QDBUSVIEWER_HEADERS}) +katie_setup_flags() + +add_executable(qdbusviewer ${QDBUSVIEWER_SOURCES} ${QDBUSVIEWER_HEADERS}) +target_link_libraries(qdbusviewer ${EXTRA_QDBUSVIEWER_LIBS}) +set_target_properties(qdbusviewer PROPERTIES + RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} + EXPORT_NAME qdbusviewer +) + +install( + TARGETS qdbusviewer + EXPORT KatieBinaryTargets ${INSTALL_TARGETS_DEFAULT_ARGS} + RUNTIME DESTINATION ${QT_BINARIES_PATH_INST} + COMPONENT Devel +) diff --git a/src/tools/qdbusviewer/Info_mac.plist b/src/tools/qdbusviewer/Info_mac.plist new file mode 100644 index 000000000..b35140997 --- /dev/null +++ b/src/tools/qdbusviewer/Info_mac.plist @@ -0,0 +1,18 @@ + + + + + CFBundleIconFile + @ICON@ + CFBundlePackageType + APPL + CFBundleGetInfoString + Created by Qt/QMake + CFBundleSignature + ???? + CFBundleIdentifier + com.trolltech.dbusviewer + CFBundleExecutable + @EXECUTABLE@ + + diff --git a/src/tools/qdbusviewer/images/qdbusviewer-128.png b/src/tools/qdbusviewer/images/qdbusviewer-128.png new file mode 100644 index 000000000..075491221 Binary files /dev/null and b/src/tools/qdbusviewer/images/qdbusviewer-128.png differ diff --git a/src/tools/qdbusviewer/images/qdbusviewer.icns b/src/tools/qdbusviewer/images/qdbusviewer.icns new file mode 100644 index 000000000..b6f39b9c2 Binary files /dev/null and b/src/tools/qdbusviewer/images/qdbusviewer.icns differ diff --git a/src/tools/qdbusviewer/images/qdbusviewer.ico b/src/tools/qdbusviewer/images/qdbusviewer.ico new file mode 100644 index 000000000..49edb09f0 Binary files /dev/null and b/src/tools/qdbusviewer/images/qdbusviewer.ico differ diff --git a/src/tools/qdbusviewer/images/qdbusviewer.png b/src/tools/qdbusviewer/images/qdbusviewer.png new file mode 100644 index 000000000..5a8c5a341 Binary files /dev/null and b/src/tools/qdbusviewer/images/qdbusviewer.png differ diff --git a/src/tools/qdbusviewer/propertydialog.cpp b/src/tools/qdbusviewer/propertydialog.cpp new file mode 100644 index 000000000..d848216e1 --- /dev/null +++ b/src/tools/qdbusviewer/propertydialog.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertydialog.h" + +#include +#include +#include + +PropertyDialog::PropertyDialog(QWidget *parent, Qt::WindowFlags f) + : QDialog(parent, f) +{ + buttonBox = new QDialogButtonBox; + propertyTable = new QTableWidget; + label = new QLabel; + + buttonBox->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + propertyTable->setColumnCount(2); + const QStringList labels = QStringList() << QLatin1String("Name") << QLatin1String("Value"); + propertyTable->setHorizontalHeaderLabels(labels); + propertyTable->horizontalHeader()->setStretchLastSection(true); + propertyTable->setEditTriggers(QAbstractItemView::AllEditTriggers); + + connect(buttonBox, SIGNAL(accepted()), SLOT(accept()), Qt::QueuedConnection); + connect(buttonBox, SIGNAL(rejected()), SLOT(reject()), Qt::QueuedConnection); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(label); + layout->addWidget(propertyTable); + layout->addWidget(buttonBox); +} + +void PropertyDialog::setInfo(const QString &caption) +{ + label->setText(caption); +} + +void PropertyDialog::addProperty(const QString &aname, QVariant::Type type) +{ + int rowCount = propertyTable->rowCount(); + propertyTable->setRowCount(rowCount + 1); + + QString name = aname; + if (name.isEmpty()) + name = QLatin1String("argument ") + QString::number(rowCount + 1); + name += QLatin1String(" ("); + name += QLatin1String(QVariant::typeToName(type)); + name += QLatin1String(")"); + QTableWidgetItem *nameItem = new QTableWidgetItem(name); + nameItem->setFlags(nameItem->flags() & + ~(Qt::ItemIsEditable | Qt::ItemIsSelectable)); + propertyTable->setItem(rowCount, 0, nameItem); + + QTableWidgetItem *valueItem = new QTableWidgetItem; + valueItem->setData(Qt::DisplayRole, QVariant(type)); + propertyTable->setItem(rowCount, 1, valueItem); +} + +int PropertyDialog::exec() +{ + propertyTable->resizeColumnToContents(0); + propertyTable->setFocus(); + propertyTable->setCurrentCell(0, 1); + return QDialog::exec(); +} + +QList PropertyDialog::values() const +{ + QList result; + + for (int i = 0; i < propertyTable->rowCount(); ++i) + result << propertyTable->item(i, 1)->data(Qt::EditRole); + + return result; +} + +#include diff --git a/src/tools/qdbusviewer/propertydialog.h b/src/tools/qdbusviewer/propertydialog.h new file mode 100644 index 000000000..b337d6b72 --- /dev/null +++ b/src/tools/qdbusviewer/propertydialog.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PROPERTYDIALOG_H +#define PROPERTYDIALOG_H + +#include +#include +#include +#include + +class PropertyDialog: public QDialog +{ + Q_OBJECT +public: + explicit PropertyDialog(QWidget *parent = 0, Qt::WindowFlags f = 0); + + void addProperty(const QString &name, QVariant::Type type); + void setInfo(const QString &caption); + + QList values() const; + + int exec(); + +private: + QLabel *label; + QTableWidget *propertyTable; + QDialogButtonBox *buttonBox; +}; + +#endif + diff --git a/src/tools/qdbusviewer/qdbusmodel.cpp b/src/tools/qdbusviewer/qdbusmodel.cpp new file mode 100644 index 000000000..68c2dcbf4 --- /dev/null +++ b/src/tools/qdbusviewer/qdbusmodel.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusmodel.h" + +#include +#include +#include +#include +#include +#include + +struct QDBusItem +{ + inline QDBusItem(QDBusModel::Type aType, const QString &aName, QDBusItem *aParent = 0) + : type(aType), parent(aParent), isPrefetched(type != QDBusModel::PathItem), name(aName) + {} + inline ~QDBusItem() + { + qDeleteAll(children); + } + + QString path() const + { + Q_ASSERT(type == QDBusModel::PathItem); + + QString s; + const QDBusItem *item = this; + while (item) { + s.prepend(item->name); + item = item->parent; + } + if (s.length() > 1) + s.chop(1); // remove tailing slash + return s; + } + + QDBusModel::Type type; + QDBusItem *parent; + QVector children; + bool isPrefetched; + QString name; + QString caption; + QString typeSignature; +}; + +QDomDocument QDBusModel::introspect(const QString &path) +{ + QDomDocument doc; + + QDBusInterface iface(service, path, QLatin1String("org.freedesktop.DBus.Introspectable"), c); + if (!iface.isValid()) { + QDBusError err(iface.lastError()); + emit busError(QString::fromLatin1("Cannot introspect object %1 at %2:\n %3 (%4)\n").arg(path).arg( + service).arg(err.name()).arg(err.message())); + return doc; + } + + QDBusReply xml = iface.call(QLatin1String("Introspect")); + + if (!xml.isValid()) { + QDBusError err(xml.error()); + if (err.isValid()) { + emit busError(QString::fromLatin1("Call to object %1 at %2:\n %3 (%4) failed\n").arg( + path).arg(service).arg(err.name()).arg(err.message())); + } else { + emit busError(QString::fromLatin1("Invalid XML received from object %1 at %2\n").arg( + path).arg(service)); + } + return doc; + } + + doc.setContent(xml); + return doc; +} + +void QDBusModel::addMethods(QDBusItem *parent, const QDomElement &iface) +{ + Q_ASSERT(parent); + + QDomElement child = iface.firstChildElement(); + while (!child.isNull()) { + QDBusItem *item = 0; + if (child.tagName() == QLatin1String("method")) { + item = new QDBusItem(QDBusModel::MethodItem, + child.attribute(QLatin1String("name")), parent); + item->caption = QLatin1String("Method: ") + item->name; + //get "type" from where "direction" is "in" + QDomElement n = child.firstChildElement(); + while (!n.isNull()) { + if (n.attribute(QLatin1String("direction")) == QLatin1String("in")) + item->typeSignature += n.attribute(QLatin1String("type")); + n = n.nextSiblingElement(); + } + } else if (child.tagName() == QLatin1String("signal")) { + item = new QDBusItem(QDBusModel::SignalItem, + child.attribute(QLatin1String("name")), parent); + item->caption = QLatin1String("Signal: ") + item->name; + } else if (child.tagName() == QLatin1String("property")) { + item = new QDBusItem(QDBusModel::PropertyItem, + child.attribute(QLatin1String("name")), parent); + item->caption = QLatin1String("Property: ") + item->name; + } else { + qDebug() << "addMethods: unknown tag:" << child.tagName(); + } + if (item) + parent->children.append(item); + + child = child.nextSiblingElement(); + } +} + +void QDBusModel::addPath(QDBusItem *parent) +{ + Q_ASSERT(parent); + + QString path = parent->path(); + + QDomDocument doc = introspect(path); + QDomElement node = doc.documentElement(); + QDomElement child = node.firstChildElement(); + while (!child.isNull()) { + if (child.tagName() == QLatin1String("node")) { + QDBusItem *item = new QDBusItem(QDBusModel::PathItem, + child.attribute(QLatin1String("name")) + QLatin1Char('/'), parent); + parent->children.append(item); + + addMethods(item, child); + } else if (child.tagName() == QLatin1String("interface")) { + QDBusItem *item = new QDBusItem(QDBusModel::InterfaceItem, + child.attribute(QLatin1String("name")), parent); + parent->children.append(item); + + addMethods(item, child); + } else { + qDebug() << "addPath: Unknown tag name:" << child.tagName(); + } + child = child.nextSiblingElement(); + } + + parent->isPrefetched = true; +} + +QDBusModel::QDBusModel(const QString &aService, const QDBusConnection &connection) + : service(aService), c(connection), root(0) +{ + root = new QDBusItem(QDBusModel::PathItem, QLatin1String("/")); +} + +QDBusModel::~QDBusModel() +{ + delete root; +} + +QModelIndex QDBusModel::index(int row, int column, const QModelIndex &parent) const +{ + const QDBusItem *item = static_cast(parent.internalPointer()); + if (!item) + item = root; + + if (column != 0 || row < 0 || row >= item->children.count()) + return QModelIndex(); + + return createIndex(row, 0, item->children.at(row)); +} + +QModelIndex QDBusModel::parent(const QModelIndex &child) const +{ + QDBusItem *item = static_cast(child.internalPointer()); + if (!item || !item->parent || !item->parent->parent) + return QModelIndex(); + + return createIndex(item->parent->parent->children.indexOf(item->parent), 0, item->parent); +} + +int QDBusModel::rowCount(const QModelIndex &parent) const +{ + QDBusItem *item = static_cast(parent.internalPointer()); + if (!item) + item = root; + if (!item->isPrefetched) + const_cast(this)->addPath(item); + + return item->children.count(); +} + +int QDBusModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +QVariant QDBusModel::data(const QModelIndex &index, int role) const +{ + const QDBusItem *item = static_cast(index.internalPointer()); + if (!item) + return QVariant(); + + if (role != Qt::DisplayRole) + return QVariant(); + + return item->caption.isEmpty() ? item->name : item->caption; +} + +QVariant QDBusModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole || orientation == Qt::Vertical || section != 0) + return QVariant(); + + return QLatin1String("Methods"); +} + +QDBusModel::Type QDBusModel::itemType(const QModelIndex &index) const +{ + const QDBusItem *item = static_cast(index.internalPointer()); + return item ? item->type : PathItem; +} + +void QDBusModel::refresh(const QModelIndex &aIndex) +{ + QModelIndex index = aIndex; + while (index.isValid() && static_cast(index.internalPointer())->type != PathItem) { + index = index.parent(); + } + + QDBusItem *item = static_cast(index.internalPointer()); + if (!item) + item = root; + + if (!item->children.isEmpty()) { + beginRemoveRows(index, 0, item->children.count() - 1); + qDeleteAll(item->children); + item->children.clear(); + endRemoveRows(); + } + + addPath(item); + if (!item->children.isEmpty()) { + beginInsertRows(index, 0, item->children.count() - 1); + endInsertRows(); + } +} + +QString QDBusModel::dBusPath(const QModelIndex &aIndex) const +{ + QModelIndex index = aIndex; + while (index.isValid() && static_cast(index.internalPointer())->type != PathItem) { + index = index.parent(); + } + + QDBusItem *item = static_cast(index.internalPointer()); + if (!item) + item = root; + + return item->path(); +} + +QString QDBusModel::dBusInterface(const QModelIndex &index) const +{ + QDBusItem *item = static_cast(index.internalPointer()); + if (!item) + return QString(); + if (item->type == InterfaceItem) + return item->name; + if (item->parent && item->parent->type == InterfaceItem) + return item->parent->name; + return QString(); +} + +QString QDBusModel::dBusMethodName(const QModelIndex &index) const +{ + QDBusItem *item = static_cast(index.internalPointer()); + return item ? item->name : QString(); +} + +QString QDBusModel::dBusTypeSignature(const QModelIndex &index) const +{ + QDBusItem *item = static_cast(index.internalPointer()); + return item ? item->typeSignature : QString(); +} + +QModelIndex QDBusModel::findObject(const QDBusObjectPath &objectPath) +{ + QStringList path = objectPath.path().split(QLatin1Char('/'), QString::SkipEmptyParts); + + QDBusItem *item = root; + int childIdx = -1; + while (item && !path.isEmpty()) { + const QString branch = path.takeFirst() + QLatin1Char('/'); + childIdx = -1; + + // do a linear search over all the children + for (int i = 0; i < item->children.count(); ++i) { + QDBusItem *child = item->children.at(i); + if (child->type == PathItem && child->name == branch) { + item = child; + childIdx = i; + + // prefetch the found branch + if (!item->isPrefetched) + addPath(item); + break; + } + } + + // branch not found - bail out + if (childIdx == -1) + return QModelIndex(); + } + + // found the right item + if (childIdx != -1 && item && path.isEmpty()) + return createIndex(childIdx, 0, item); + + return QModelIndex(); +} + +#include diff --git a/src/tools/qdbusviewer/qdbusmodel.h b/src/tools/qdbusviewer/qdbusmodel.h new file mode 100644 index 000000000..fbb503886 --- /dev/null +++ b/src/tools/qdbusviewer/qdbusmodel.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSMODEL_H +#define QDBUSMODEL_H + +#include +#include +#include + +struct QDBusItem; + +QT_FORWARD_DECLARE_CLASS(QDomDocument); +QT_FORWARD_DECLARE_CLASS(QDomElement); + + +class QDBusModel: public QAbstractItemModel +{ + Q_OBJECT + +public: + enum Type { InterfaceItem, PathItem, MethodItem, SignalItem, PropertyItem }; + + QDBusModel(const QString &service, const QDBusConnection &connection); + ~QDBusModel(); + + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + QModelIndex parent(const QModelIndex &child) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + Type itemType(const QModelIndex &index) const; + QString dBusPath(const QModelIndex &index) const; + QString dBusInterface(const QModelIndex &index) const; + QString dBusMethodName(const QModelIndex &index) const; + QString dBusTypeSignature(const QModelIndex &index) const; + + void refresh(const QModelIndex &index = QModelIndex()); + + QModelIndex findObject(const QDBusObjectPath &objectPath); + +Q_SIGNALS: + void busError(const QString &text); + +private: + QDomDocument introspect(const QString &path); + void addMethods(QDBusItem *parent, const QDomElement &iface); + void addPath(QDBusItem *parent); + + QString service; + QDBusConnection c; + QDBusItem *root; +}; + +#endif + diff --git a/src/tools/qdbusviewer/qdbusviewer.cpp b/src/tools/qdbusviewer/qdbusviewer.cpp new file mode 100644 index 000000000..5ee737146 --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewer.cpp @@ -0,0 +1,536 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdbusviewer.h" +#include "qdbusmodel.h" +#include "propertydialog.h" + +#include +#include +#include +#include +#include +#include + +class QDBusViewModel: public QDBusModel +{ +public: + inline QDBusViewModel(const QString &service, const QDBusConnection &connection) + : QDBusModel(service, connection) + {} + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + if (role == Qt::FontRole && itemType(index) == InterfaceItem) { + QFont f; + f.setItalic(true); + return f; + } + return QDBusModel::data(index, role); + } +}; + +QDBusViewer::QDBusViewer(const QDBusConnection &connection, QWidget *parent) : + QWidget(parent), + c(connection), + objectPathRegExp(QLatin1String("\\[ObjectPath: (.*)\\]")) +{ + servicesModel = new QStringListModel(this); + servicesFilterModel = new QSortFilterProxyModel(this); + servicesFilterModel->setSourceModel(servicesModel); + servicesFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + serviceFilterLine = new QLineEdit(this); + serviceFilterLine->setPlaceholderText(tr("Search...")); + servicesView = new QListView(this); + servicesView->setModel(servicesFilterModel); + + connect(serviceFilterLine, SIGNAL(textChanged(QString)), servicesFilterModel, SLOT(setFilterFixedString(QString))); + + tree = new QTreeView; + tree->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(tree, SIGNAL(activated(QModelIndex)), this, SLOT(activate(QModelIndex))); + + refreshAction = new QAction(tr("&Refresh"), tree); + refreshAction->setData(42); // increase the amount of 42 used as magic number by one + refreshAction->setShortcut(QKeySequence::Refresh); + connect(refreshAction, SIGNAL(triggered()), this, SLOT(refreshChildren())); + + QShortcut *refreshShortcut = new QShortcut(QKeySequence::Refresh, tree); + connect(refreshShortcut, SIGNAL(activated()), this, SLOT(refreshChildren())); + + QVBoxLayout *layout = new QVBoxLayout(this); + QSplitter *topSplitter = new QSplitter(Qt::Vertical, this); + layout->addWidget(topSplitter); + + log = new QTextBrowser; + connect(log, SIGNAL(anchorClicked(QUrl)), this, SLOT(anchorClicked(QUrl))); + + QSplitter *splitter = new QSplitter(topSplitter); + splitter->addWidget(servicesView); + + QWidget *servicesWidget = new QWidget; + QVBoxLayout *servicesLayout = new QVBoxLayout(servicesWidget); + servicesLayout->addWidget(serviceFilterLine); + servicesLayout->addWidget(servicesView); + splitter->addWidget(servicesWidget); + splitter->addWidget(tree); + + topSplitter->addWidget(splitter); + topSplitter->addWidget(log); + + connect(servicesView->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(serviceChanged(QModelIndex))); + connect(tree, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(showContextMenu(QPoint))); + + QMetaObject::invokeMethod(this, "refresh", Qt::QueuedConnection); + + if (c.isConnected()) { + logMessage(QLatin1String("Connected to D-Bus.")); + QDBusConnectionInterface *iface = c.interface(); + connect(iface, SIGNAL(serviceRegistered(QString)), + this, SLOT(serviceRegistered(QString))); + connect(iface, SIGNAL(serviceUnregistered(QString)), + this, SLOT(serviceUnregistered(QString))); + connect(iface, SIGNAL(serviceOwnerChanged(QString,QString,QString)), + this, SLOT(serviceOwnerChanged(QString,QString,QString))); + } else { + logError(QLatin1String("Cannot connect to D-Bus: ") + c.lastError().message()); + } + + objectPathRegExp.setMinimal(true); + +} + +void QDBusViewer::logMessage(const QString &msg) +{ + log->append(msg + QLatin1Char('\n')); +} + +void QDBusViewer::logError(const QString &msg) +{ + log->append(QLatin1String("Error: ") + Qt::escape(msg) + QLatin1String("
")); +} + +void QDBusViewer::refresh() +{ + servicesModel->removeRows(0, servicesModel->rowCount()); + + if (c.isConnected()) { + const QStringList serviceNames = c.interface()->registeredServiceNames(); + servicesModel->setStringList(serviceNames); + } +} + +void QDBusViewer::activate(const QModelIndex &item) +{ + if (!item.isValid()) + return; + + const QDBusModel *model = static_cast(item.model()); + + BusSignature sig; + sig.mService = currentService; + sig.mPath = model->dBusPath(item); + sig.mInterface = model->dBusInterface(item); + sig.mName = model->dBusMethodName(item); + sig.mTypeSig = model->dBusTypeSignature(item); + + switch (model->itemType(item)) { + case QDBusModel::SignalItem: + connectionRequested(sig); + break; + case QDBusModel::MethodItem: + callMethod(sig); + break; + case QDBusModel::PropertyItem: + getProperty(sig); + break; + default: + break; + } +} + +void QDBusViewer::getProperty(const BusSignature &sig) +{ + QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Get")); + QList arguments; + arguments << sig.mInterface << sig.mName; + message.setArguments(arguments); + c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage))); +} + +void QDBusViewer::setProperty(const BusSignature &sig) +{ + QDBusInterface iface(sig.mService, sig.mPath, sig.mInterface, c); + QMetaProperty prop = iface.metaObject()->property(iface.metaObject()->indexOfProperty(sig.mName.toLatin1())); + + bool ok; + QString input = QInputDialog::getText(this, tr("Arguments"), + tr("Please enter the value of the property %1 (type %2)").arg( + sig.mName, QString::fromLatin1(prop.typeName())), + QLineEdit::Normal, QString(), &ok); + if (!ok) + return; + + QVariant value = input; + if (!value.convert(prop.type())) { + QMessageBox::warning(this, tr("Unable to marshall"), + tr("Value conversion failed, unable to set property")); + return; + } + + QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, QLatin1String("org.freedesktop.DBus.Properties"), QLatin1String("Set")); + QList arguments; + arguments << sig.mInterface << sig.mName << QVariant::fromValue(QDBusVariant(value)); + message.setArguments(arguments); + c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage))); + +} + +static QString getDbusSignature(const QMetaMethod& method) +{ + // create a D-Bus type signature from QMetaMethod's parameters + QString sig; + for (int i = 0; i < method.parameterTypes().count(); ++i) { + QVariant::Type type = QVariant::nameToType(method.parameterTypes().at(i)); + sig.append(QString::fromLatin1(QDBusMetaType::typeToSignature(type))); + } + return sig; +} + +void QDBusViewer::callMethod(const BusSignature &sig) +{ + QDBusInterface iface(sig.mService, sig.mPath, sig.mInterface, c); + const QMetaObject *mo = iface.metaObject(); + + // find the method + QMetaMethod method; + for (int i = 0; i < mo->methodCount(); ++i) { + const QString signature = QString::fromLatin1(mo->method(i).signature()); + if (signature.startsWith(sig.mName) && signature.at(sig.mName.length()) == QLatin1Char('(')) + if (getDbusSignature(mo->method(i)) == sig.mTypeSig) + method = mo->method(i); + } + if (!method.signature()) { + QMessageBox::warning(this, tr("Unable to find method"), + tr("Unable to find method %1 on path %2 in interface %3").arg( + sig.mName).arg(sig.mPath).arg(sig.mInterface)); + return; + } + + PropertyDialog dialog; + QList args; + + const QList paramTypes = method.parameterTypes(); + const QList paramNames = method.parameterNames(); + QList types; // remember the low-level D-Bus type + for (int i = 0; i < paramTypes.count(); ++i) { + const QByteArray paramType = paramTypes.at(i); + if (paramType.endsWith('&')) + continue; // ignore OUT parameters + + QVariant::Type type = QVariant::nameToType(paramType); + dialog.addProperty(QString::fromLatin1(paramNames.value(i)), type); + types.append(QMetaType::type(paramType)); + } + + if (!types.isEmpty()) { + dialog.setInfo(tr("Please enter parameters for the method \"%1\"").arg(sig.mName)); + + if (dialog.exec() != QDialog::Accepted) + return; + + args = dialog.values(); + } + + // Try to convert the values we got as closely as possible to the + // dbus signature. this is especially important for those input as strings + for (int i = 0; i < args.count(); ++i) { + QVariant a = args.at(i); + int desttype = types.at(i); + if (desttype != int(a.type()) + && desttype < int(QMetaType::LastCoreType) && desttype != int(QVariant::Map) + && a.canConvert(QVariant::Type(desttype))) { + args[i].convert(QVariant::Type(desttype)); + } + // Special case - convert a value to a QDBusVariant if the + // interface wants a variant + if (types.at(i) == qMetaTypeId()) + args[i] = QVariant::fromValue(QDBusVariant(args.at(i))); + } + + QDBusMessage message = QDBusMessage::createMethodCall(sig.mService, sig.mPath, sig.mInterface, + sig.mName); + message.setArguments(args); + c.callWithCallback(message, this, SLOT(dumpMessage(QDBusMessage))); +} + +void QDBusViewer::showContextMenu(const QPoint &point) +{ + QModelIndex item = tree->indexAt(point); + if (!item.isValid()) + return; + + const QDBusModel *model = static_cast(item.model()); + + BusSignature sig; + sig.mService = currentService; + sig.mPath = model->dBusPath(item); + sig.mInterface = model->dBusInterface(item); + sig.mName = model->dBusMethodName(item); + sig.mTypeSig = model->dBusTypeSignature(item); + + QMenu menu; + menu.addAction(refreshAction); + + switch (model->itemType(item)) { + case QDBusModel::SignalItem: { + QAction *action = new QAction(tr("&Connect"), &menu); + action->setData(1); + menu.addAction(action); + break; } + case QDBusModel::MethodItem: { + QAction *action = new QAction(tr("&Call"), &menu); + action->setData(2); + menu.addAction(action); + break; } + case QDBusModel::PropertyItem: { + QAction *actionSet = new QAction(tr("&Set value"), &menu); + actionSet->setData(3); + QAction *actionGet = new QAction(tr("&Get value"), &menu); + actionGet->setData(4); + menu.addAction(actionSet); + menu.addAction(actionGet); + break; } + default: + break; + } + + QAction *selectedAction = menu.exec(tree->viewport()->mapToGlobal(point)); + if (!selectedAction) + return; + + switch (selectedAction->data().toInt()) { + case 1: + connectionRequested(sig); + break; + case 2: + callMethod(sig); + break; + case 3: + setProperty(sig); + break; + case 4: + getProperty(sig); + break; + } +} + +void QDBusViewer::connectionRequested(const BusSignature &sig) +{ + if (!c.connect(sig.mService, QString(), sig.mInterface, sig.mName, this, + SLOT(dumpMessage(QDBusMessage)))) { + logError(tr("Unable to connect to service %1, path %2, interface %3, signal %4").arg( + sig.mService).arg(sig.mPath).arg(sig.mInterface).arg(sig.mName)); + } +} + +void QDBusViewer::dumpMessage(const QDBusMessage &message) +{ + QList args = message.arguments(); + QString out = QLatin1String("Received "); + + switch (message.type()) { + case QDBusMessage::SignalMessage: + out += QLatin1String("signal "); + break; + case QDBusMessage::ErrorMessage: + out += QLatin1String("error message "); + break; + case QDBusMessage::ReplyMessage: + out += QLatin1String("reply "); + break; + default: + out += QLatin1String("message "); + break; + } + + out += QLatin1String("from "); + out += message.service(); + if (!message.path().isEmpty()) + out += QLatin1String(", path ") + message.path(); + if (!message.interface().isEmpty()) + out += QLatin1String(", interface ") + message.interface() + QLatin1String(""); + if (!message.member().isEmpty()) + out += QLatin1String(", member ") + message.member(); + out += QLatin1String("
"); + if (args.isEmpty()) { + out += QLatin1String("  (no arguments)"); + } else { + out += QLatin1String("  Arguments: "); + foreach (QVariant arg, args) { + QString str = Qt::escape(QDBusUtil::argumentToString(arg)); + // turn object paths into clickable links + str.replace(objectPathRegExp, QLatin1String("[ObjectPath: \\1]")); + out += str; + out += QLatin1String(", "); + } + out.chop(2); + } + + log->append(out); +} + +void QDBusViewer::serviceChanged(const QModelIndex &index) +{ + delete tree->model(); + + currentService.clear(); + if (!index.isValid()) + return; + currentService = index.data().toString(); + + tree->setModel(new QDBusViewModel(currentService, c)); + connect(tree->model(), SIGNAL(busError(QString)), this, SLOT(logError(QString))); +} + +void QDBusViewer::serviceRegistered(const QString &service) +{ + if (service == c.baseService()) + return; + + servicesModel->insertRows(0, 1); + servicesModel->setData(servicesModel->index(0, 0), service); +} + +static QModelIndex findItem(QStringListModel *servicesModel, const QString &name) +{ + QModelIndexList hits = servicesModel->match(servicesModel->index(0, 0), Qt::DisplayRole, name); + if (hits.isEmpty()) + return QModelIndex(); + + return hits.first(); +} + +void QDBusViewer::serviceUnregistered(const QString &name) +{ + QModelIndex hit = findItem(servicesModel, name); + if (!hit.isValid()) + return; + servicesModel->removeRows(hit.row(), 1); +} + +void QDBusViewer::serviceOwnerChanged(const QString &name, const QString &oldOwner, + const QString &newOwner) +{ + QModelIndex hit = findItem(servicesModel, name); + + if (!hit.isValid() && oldOwner.isEmpty() && !newOwner.isEmpty()) + serviceRegistered(name); + else if (hit.isValid() && !oldOwner.isEmpty() && newOwner.isEmpty()) + servicesModel->removeRows(hit.row(), 1); + else if (hit.isValid() && !oldOwner.isEmpty() && !newOwner.isEmpty()) { + servicesModel->removeRows(hit.row(), 1); + serviceRegistered(name); + } +} + +void QDBusViewer::refreshChildren() +{ + QDBusModel *model = qobject_cast(tree->model()); + if (!model) + return; + model->refresh(tree->currentIndex()); +} + +void QDBusViewer::about() +{ + QMessageBox box(this); + + box.setText(QString::fromLatin1("
" + "

%1

" + "

Version %2

" + "

Copyright (C) 2015 The Qt Company Ltd.

") + .arg(tr("D-Bus Viewer")).arg(QLatin1String(QT_VERSION_STR))); + box.setWindowTitle(tr("D-Bus Viewer")); + box.exec(); +} + +void QDBusViewer::anchorClicked(const QUrl &url) +{ + if (url.scheme() != QLatin1String("qdbus")) + // not ours + return; + + // swallow the click without setting a new document + log->setSource(QUrl()); + + QDBusModel *model = qobject_cast(tree->model()); + if (!model) + return; + + QModelIndex idx = model->findObject(QDBusObjectPath(url.path())); + if (!idx.isValid()) + return; + + tree->scrollTo(idx); + tree->setCurrentIndex(idx); +} + +/*! + \page qdbusviewer.html + \title D-Bus Viewer + \keyword qdbusviewer + + The Qt D-Bus Viewer is a tool that lets you introspect D-Bus objects and messages. You can + choose between the system bus and the session bus. Click on any service on the list + on the left side to see all the exported objects. + + You can invoke methods by double-clicking on them. If a method takes one or more IN parameters, + a property editor opens. + + Right-click on a signal to connect to it. All emitted signals including their parameters + are output in the message view on the lower side of the window. +*/ + +#include diff --git a/src/tools/qdbusviewer/qdbusviewer.h b/src/tools/qdbusviewer/qdbusviewer.h new file mode 100644 index 000000000..f650dba3d --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewer.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDBUSVIEWER_H +#define QDBUSVIEWER_H + +#include +#include +#include + +QT_FORWARD_DECLARE_CLASS(QTreeView); +QT_FORWARD_DECLARE_CLASS(QDomDocument); +QT_FORWARD_DECLARE_CLASS(QDomElement); + +struct BusSignature +{ + QString mService, mPath, mInterface, mName; + QString mTypeSig; +}; + +class QDBusViewer: public QWidget +{ + Q_OBJECT +public: + QDBusViewer(const QDBusConnection &connection, QWidget *parent = 0); + +public slots: + void refresh(); + void about(); + +private slots: + void serviceChanged(const QModelIndex &index); + void showContextMenu(const QPoint &); + void connectionRequested(const BusSignature &sig); + void callMethod(const BusSignature &sig); + void getProperty(const BusSignature &sig); + void setProperty(const BusSignature &sig); + void dumpMessage(const QDBusMessage &msg); + void refreshChildren(); + + void serviceRegistered(const QString &service); + void serviceUnregistered(const QString &service); + void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + + void activate(const QModelIndex &item); + + void logError(const QString &msg); + void anchorClicked(const QUrl &url); + +private: + void logMessage(const QString &msg); + + QDBusConnection c; + QString currentService; + QTreeView *tree; + QAction *refreshAction; + QTreeWidget *services; + QStringListModel *servicesModel; + QSortFilterProxyModel *servicesFilterModel; + QLineEdit *serviceFilterLine; + QListView *servicesView; + QTextBrowser *log; + QRegExp objectPathRegExp; +}; + +#endif diff --git a/src/tools/qdbusviewer/qdbusviewer.pro b/src/tools/qdbusviewer/qdbusviewer.pro new file mode 100644 index 000000000..99346606e --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewer.pro @@ -0,0 +1,8 @@ +mac { + ICON = images/qdbusviewer.icns + QMAKE_INFO_PLIST = Info_mac.plist +} + +win32 { + RC_FILE = qdbusviewer.rc +} diff --git a/src/tools/qdbusviewer/qdbusviewer.qrc b/src/tools/qdbusviewer/qdbusviewer.qrc new file mode 100644 index 000000000..7d592f32c --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewer.qrc @@ -0,0 +1,6 @@ + + + images/qdbusviewer-128.png + images/qdbusviewer.png + + diff --git a/src/tools/qdbusviewer/qdbusviewer.rc b/src/tools/qdbusviewer/qdbusviewer.rc new file mode 100644 index 000000000..c4b1d60b8 --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewer.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "images/qdbusviewer.ico" diff --git a/src/tools/qdbusviewer/qdbusviewermain.cpp b/src/tools/qdbusviewer/qdbusviewermain.cpp new file mode 100644 index 000000000..48e88968b --- /dev/null +++ b/src/tools/qdbusviewer/qdbusviewermain.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include "qdbusviewer.h" + +#include + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QMainWindow mw; +#ifndef Q_WS_MAC + app.setWindowIcon(QIcon(QLatin1String(":/trolltech/qdbusviewer/images/qdbusviewer.png"))); +#else + mw.setWindowTitle(qApp->translate("QtDBusViewer", "Qt D-Bus Viewer")); +#endif + + + QTabWidget *mainWidget = new QTabWidget; + mw.setCentralWidget(mainWidget); + QDBusViewer *sessionBusViewer = new QDBusViewer(QDBusConnection::sessionBus()); + QDBusViewer *systemBusViewer = new QDBusViewer(QDBusConnection::systemBus()); + mainWidget->addTab(sessionBusViewer, QObject::tr("Session Bus")); + mainWidget->addTab(systemBusViewer, QObject::tr("System Bus")); + + QMenu *fileMenu = mw.menuBar()->addMenu(QObject::tr("&File")); + QAction *quitAction = fileMenu->addAction(QObject::tr("&Quit"), &mw, SLOT(close())); + Q_UNUSED(quitAction); + + QMenu *helpMenu = mw.menuBar()->addMenu(QObject::tr("&Help")); + QAction *aboutAction = helpMenu->addAction(QObject::tr("&About")); + aboutAction->setMenuRole(QAction::AboutRole); + QObject::connect(aboutAction, SIGNAL(triggered()), sessionBusViewer, SLOT(about())); + + QAction *aboutQtAction = helpMenu->addAction(QObject::tr("About &Qt")); + aboutQtAction->setMenuRole(QAction::AboutQtRole); + QObject::connect(aboutQtAction, SIGNAL(triggered()), &app, SLOT(aboutQt())); + + mw.show(); + + return app.exec(); +} +