forked from qt-creator/qt-creator
Merge remote-tracking branch 'origin/master' into 4.4
Change-Id: I5d3d689e7bd8d51094b8416761caed15b7df7bd8
This commit is contained in:
@@ -280,11 +280,10 @@ bool PathsAndLanguages::maybeInsert(const PathAndLanguage &pathAndLanguage) {
|
||||
if (currentElement.path() == pathAndLanguage.path()) {
|
||||
int j = i;
|
||||
do {
|
||||
if (pathAndLanguage.language() < currentElement.language()) {
|
||||
if (currentElement.language() == pathAndLanguage.language())
|
||||
return false;
|
||||
if (pathAndLanguage.language() < currentElement.language())
|
||||
break;
|
||||
}
|
||||
if (currentElement.language() == pathAndLanguage.language())
|
||||
return false;
|
||||
++j;
|
||||
if (j == m_list.length())
|
||||
break;
|
||||
|
@@ -841,6 +841,18 @@ TreeItem *TreeItem::findAnyChild(const std::function<bool(TreeItem *)> &pred) co
|
||||
return 0;
|
||||
}
|
||||
|
||||
TreeItem *TreeItem::reverseFindAnyChild(const std::function<bool (TreeItem *)> &pred) const
|
||||
{
|
||||
auto end = m_children.rend();
|
||||
for (auto it = m_children.rbegin(); it != end; ++it) {
|
||||
if (pred(*it))
|
||||
return *it;
|
||||
if (TreeItem *found = (*it)->reverseFindAnyChild(pred))
|
||||
return found;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void TreeItem::clear()
|
||||
{
|
||||
while (childCount() != 0) {
|
||||
|
@@ -78,6 +78,8 @@ public:
|
||||
void forSelectedChildren(const std::function<bool(TreeItem *)> &pred) const;
|
||||
void forAllChildren(const std::function<void(TreeItem *)> &pred) const;
|
||||
TreeItem *findAnyChild(const std::function<bool(TreeItem *)> &pred) const;
|
||||
// like findAnyChild() but processes children from bottom to top
|
||||
TreeItem *reverseFindAnyChild(const std::function<bool(TreeItem *)> &pred) const;
|
||||
|
||||
// Levels are 1-based: Child at Level 1 is an immediate child.
|
||||
void forChildrenAtLevel(int level, const std::function<void(TreeItem *)> &pred) const;
|
||||
|
200
src/plugins/android/adbcommandswidget.cpp
Normal file
200
src/plugins/android/adbcommandswidget.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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 https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "adbcommandswidget.h"
|
||||
#include "ui_adbcommandswidget.h"
|
||||
|
||||
#include "utils/utilsicons.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QShortcut>
|
||||
#include <QStringListModel>
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
const char defaultCommand[] = "echo \"shell command\"";
|
||||
|
||||
static void swapData(QStringListModel *model, const QModelIndex &srcIndex,
|
||||
const QModelIndex &destIndex)
|
||||
{
|
||||
if (model) {
|
||||
QVariant data = model->data(destIndex, Qt::EditRole); // QTBUG-55078
|
||||
model->setData(destIndex, model->data(srcIndex, Qt::EditRole));
|
||||
model->setData(srcIndex, data);
|
||||
}
|
||||
}
|
||||
|
||||
class AdbCommandsWidgetPrivate
|
||||
{
|
||||
public:
|
||||
AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent, QWidget *parentWidget);
|
||||
~AdbCommandsWidgetPrivate();
|
||||
|
||||
private:
|
||||
void addString(const QString &str);
|
||||
void onAddButton();
|
||||
void onMoveUpButton();
|
||||
void onMoveDownButton();
|
||||
void onRemove();
|
||||
void onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex);
|
||||
|
||||
const AdbCommandsWidget &m_parent;
|
||||
QGroupBox *m_rootWidget = nullptr;
|
||||
Ui::AdbCommandsWidget *m_ui = nullptr;
|
||||
QStringListModel *m_stringModel = nullptr;
|
||||
friend class AdbCommandsWidget;
|
||||
};
|
||||
|
||||
AdbCommandsWidget::AdbCommandsWidget(QWidget *parent) :
|
||||
QObject(parent),
|
||||
d(new AdbCommandsWidgetPrivate(*this, parent))
|
||||
{
|
||||
}
|
||||
|
||||
AdbCommandsWidget::~AdbCommandsWidget()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList AdbCommandsWidget::commandsList() const
|
||||
{
|
||||
return d->m_stringModel->stringList();
|
||||
}
|
||||
|
||||
QWidget *AdbCommandsWidget::widget() const
|
||||
{
|
||||
return d->m_rootWidget;
|
||||
}
|
||||
|
||||
void AdbCommandsWidget::setTitleText(const QString &title)
|
||||
{
|
||||
d->m_rootWidget->setTitle(title);
|
||||
}
|
||||
|
||||
void AdbCommandsWidget::setCommandList(const QStringList &commands)
|
||||
{
|
||||
d->m_stringModel->setStringList(commands);
|
||||
}
|
||||
|
||||
AdbCommandsWidgetPrivate::AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent,
|
||||
QWidget *parentWidget):
|
||||
m_parent(parent),
|
||||
m_rootWidget(new QGroupBox(parentWidget)),
|
||||
m_ui(new Ui::AdbCommandsWidget),
|
||||
m_stringModel(new QStringListModel)
|
||||
{
|
||||
m_ui->setupUi(m_rootWidget);
|
||||
m_ui->addButton->setIcon(Utils::Icons::PLUS.icon());
|
||||
m_ui->removeButton->setIcon(Utils::Icons::MINUS.icon());
|
||||
m_ui->moveUpButton->setIcon(Utils::Icons::ARROW_UP.icon());
|
||||
m_ui->moveDownButton->setIcon(Utils::Icons::ARROW_DOWN.icon());
|
||||
|
||||
auto deleteShortcut = new QShortcut(QKeySequence(QKeySequence::Delete), m_ui->commandsView);
|
||||
deleteShortcut->setContext(Qt::WidgetShortcut);
|
||||
QObject::connect(deleteShortcut, &QShortcut::activated,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onRemove, this));
|
||||
|
||||
QObject::connect(m_ui->addButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onAddButton, this));
|
||||
QObject::connect(m_ui->removeButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onRemove, this));
|
||||
QObject::connect(m_ui->moveUpButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onMoveUpButton, this));
|
||||
QObject::connect(m_ui->moveDownButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onMoveDownButton, this));
|
||||
|
||||
m_ui->commandsView->setModel(m_stringModel);
|
||||
QObject::connect(m_stringModel, &QStringListModel::dataChanged,
|
||||
&m_parent, &AdbCommandsWidget::commandsChanged);
|
||||
QObject::connect(m_stringModel, &QStringListModel::rowsRemoved,
|
||||
&m_parent, &AdbCommandsWidget::commandsChanged);
|
||||
QObject::connect(m_ui->commandsView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onCurrentIndexChanged, this, _1, _2));
|
||||
}
|
||||
|
||||
AdbCommandsWidgetPrivate::~AdbCommandsWidgetPrivate()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_stringModel;
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::addString(const QString &str)
|
||||
{
|
||||
if (!str.isEmpty()) {
|
||||
m_stringModel->insertRows(m_stringModel->rowCount(), 1);
|
||||
const QModelIndex lastItemIndex = m_stringModel->index(m_stringModel->rowCount() - 1);
|
||||
m_stringModel->setData(lastItemIndex, str);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onAddButton()
|
||||
{
|
||||
addString(defaultCommand);
|
||||
const QModelIndex index = m_stringModel->index(m_stringModel->rowCount() - 1);
|
||||
m_ui->commandsView->setCurrentIndex(index);
|
||||
m_ui->commandsView->edit(index);
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onMoveUpButton()
|
||||
{
|
||||
QModelIndex index = m_ui->commandsView->currentIndex();
|
||||
if (index.row() > 0) {
|
||||
const QModelIndex newIndex = m_stringModel->index(index.row() - 1, 0);
|
||||
swapData(m_stringModel, index, newIndex);
|
||||
m_ui->commandsView->setCurrentIndex(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onMoveDownButton()
|
||||
{
|
||||
QModelIndex index = m_ui->commandsView->currentIndex();
|
||||
if (index.row() < m_stringModel->rowCount() - 1) {
|
||||
const QModelIndex newIndex = m_stringModel->index(index.row() + 1, 0);
|
||||
swapData(m_stringModel, index, newIndex);
|
||||
m_ui->commandsView->setCurrentIndex(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onRemove()
|
||||
{
|
||||
const QModelIndex &index = m_ui->commandsView->currentIndex();
|
||||
if (index.isValid())
|
||||
m_stringModel->removeRow(index.row());
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex)
|
||||
{
|
||||
Q_UNUSED(prevIndex)
|
||||
m_ui->moveUpButton->setEnabled(newIndex.row() != 0);
|
||||
m_ui->moveDownButton->setEnabled(newIndex.row() < m_stringModel->rowCount() - 1);
|
||||
m_ui->removeButton->setEnabled(newIndex.isValid());
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Android
|
64
src/plugins/android/adbcommandswidget.h
Normal file
64
src/plugins/android/adbcommandswidget.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** 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 https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
class AdbCommandsWidgetPrivate;
|
||||
|
||||
class AdbCommandsWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AdbCommandsWidget(QWidget *parent);
|
||||
~AdbCommandsWidget();
|
||||
|
||||
QStringList commandsList() const;
|
||||
void setCommandList(const QStringList &commands);
|
||||
|
||||
QWidget *widget() const;
|
||||
|
||||
void setTitleText(const QString &title);
|
||||
|
||||
signals:
|
||||
void commandsChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<AdbCommandsWidgetPrivate> d;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // Android
|
117
src/plugins/android/adbcommandswidget.ui
Normal file
117
src/plugins/android/adbcommandswidget.ui
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AdbCommandsWidget</class>
|
||||
<widget class="QGroupBox" name="AdbCommandsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>682</width>
|
||||
<height>391</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Widget</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="6">
|
||||
<widget class="QListView" name="commandsView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Snap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QToolButton" name="moveUpButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QToolButton" name="addButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QToolButton" name="moveDownButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QToolButton" name="removeButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<tabstops>
|
||||
<tabstop>commandsView</tabstop>
|
||||
<tabstop>addButton</tabstop>
|
||||
<tabstop>removeButton</tabstop>
|
||||
<tabstop>moveUpButton</tabstop>
|
||||
<tabstop>moveDownButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@@ -50,7 +50,8 @@ HEADERS += \
|
||||
androidtoolmanager.h \
|
||||
androidsdkmanager.h \
|
||||
androidavdmanager.h \
|
||||
androidrunconfigurationwidget.h
|
||||
androidrunconfigurationwidget.h \
|
||||
adbcommandswidget.h
|
||||
|
||||
SOURCES += \
|
||||
androidconfigurations.cpp \
|
||||
@@ -94,7 +95,8 @@ SOURCES += \
|
||||
androidtoolmanager.cpp \
|
||||
androidsdkmanager.cpp \
|
||||
androidavdmanager.cpp \
|
||||
androidrunconfigurationwidget.cpp
|
||||
androidrunconfigurationwidget.cpp \
|
||||
adbcommandswidget.cpp
|
||||
|
||||
FORMS += \
|
||||
androidsettingswidget.ui \
|
||||
@@ -103,7 +105,8 @@ FORMS += \
|
||||
androiddevicedialog.ui \
|
||||
androiddeployqtwidget.ui \
|
||||
androidbuildapkwidget.ui \
|
||||
androidrunconfigurationwidget.ui
|
||||
androidrunconfigurationwidget.ui \
|
||||
adbcommandswidget.ui
|
||||
|
||||
RESOURCES = android.qrc
|
||||
|
||||
|
@@ -18,8 +18,11 @@ Project {
|
||||
|
||||
files: [
|
||||
"android_global.h",
|
||||
"addnewavddialog.ui",
|
||||
"android.qrc",
|
||||
"adbcommandswidget.cpp",
|
||||
"adbcommandswidget.h",
|
||||
"adbcommandswidget.ui",
|
||||
"addnewavddialog.ui",
|
||||
"androidanalyzesupport.cpp",
|
||||
"androidanalyzesupport.h",
|
||||
"androidavdmanager.cpp",
|
||||
|
@@ -40,7 +40,10 @@ using namespace ProjectExplorer;
|
||||
|
||||
namespace Android {
|
||||
using namespace Internal;
|
||||
|
||||
const char amStartArgsKey[] = "Android.AmStartArgsKey";
|
||||
const char preStartShellCmdsKey[] = "Android.PreStartShellCmdListKey";
|
||||
const char postFinishShellCmdsKey[] = "Android.PostFinishShellCmdListKey";
|
||||
|
||||
AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id)
|
||||
: RunConfiguration(parent, id)
|
||||
@@ -52,6 +55,16 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, AndroidRunConfi
|
||||
{
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setPreStartShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_preStartShellCommands = cmdList;
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setPostFinishShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_postFinishShellCommands = cmdList;
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setAmStartExtraArgs(const QStringList &args)
|
||||
{
|
||||
m_amStartExtraArgs = args;
|
||||
@@ -61,8 +74,14 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget()
|
||||
{
|
||||
auto configWidget = new AndroidRunConfigurationWidget();
|
||||
configWidget->setAmStartArgs(m_amStartExtraArgs);
|
||||
configWidget->setPreStartShellCommands(m_preStartShellCommands);
|
||||
configWidget->setPostFinishShellCommands(m_postFinishShellCommands);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::amStartArgsChanged,
|
||||
this, &AndroidRunConfiguration::setAmStartExtraArgs);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::preStartCmdsChanged,
|
||||
this, &AndroidRunConfiguration::setPreStartShellCommands);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::postFinishCmdsChanged,
|
||||
this, &AndroidRunConfiguration::setPostFinishShellCommands);
|
||||
return configWidget;
|
||||
}
|
||||
|
||||
@@ -73,6 +92,8 @@ Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const
|
||||
|
||||
bool AndroidRunConfiguration::fromMap(const QVariantMap &map)
|
||||
{
|
||||
m_preStartShellCommands = map.value(preStartShellCmdsKey).toStringList();
|
||||
m_postFinishShellCommands = map.value(postFinishShellCmdsKey).toStringList();
|
||||
m_amStartExtraArgs = map.value(amStartArgsKey).toStringList();
|
||||
return RunConfiguration::fromMap(map);
|
||||
}
|
||||
@@ -80,6 +101,8 @@ bool AndroidRunConfiguration::fromMap(const QVariantMap &map)
|
||||
QVariantMap AndroidRunConfiguration::toMap() const
|
||||
{
|
||||
QVariantMap res = RunConfiguration::toMap();
|
||||
res[preStartShellCmdsKey] = m_preStartShellCommands;
|
||||
res[postFinishShellCmdsKey] = m_postFinishShellCommands;
|
||||
res[amStartArgsKey] = m_amStartExtraArgs;
|
||||
return res;
|
||||
}
|
||||
@@ -88,4 +111,15 @@ const QStringList &AndroidRunConfiguration::amStartExtraArgs() const
|
||||
{
|
||||
return m_amStartExtraArgs;
|
||||
}
|
||||
|
||||
const QStringList &AndroidRunConfiguration::preStartShellCommands() const
|
||||
{
|
||||
return m_preStartShellCommands;
|
||||
}
|
||||
|
||||
const QStringList &AndroidRunConfiguration::postFinishShellCommands() const
|
||||
{
|
||||
return m_postFinishShellCommands;
|
||||
}
|
||||
|
||||
} // namespace Android
|
||||
|
@@ -48,15 +48,21 @@ public:
|
||||
QVariantMap toMap() const override;
|
||||
|
||||
const QStringList &amStartExtraArgs() const;
|
||||
const QStringList &preStartShellCommands() const;
|
||||
const QStringList &postFinishShellCommands() const;
|
||||
|
||||
protected:
|
||||
AndroidRunConfiguration(ProjectExplorer::Target *parent, AndroidRunConfiguration *source);
|
||||
|
||||
private:
|
||||
void setPreStartShellCommands(const QStringList &cmdList);
|
||||
void setPostFinishShellCommands(const QStringList &cmdList);
|
||||
void setAmStartExtraArgs(const QStringList &args);
|
||||
|
||||
private:
|
||||
QStringList m_amStartExtraArgs;
|
||||
QStringList m_preStartShellCommands;
|
||||
QStringList m_postFinishShellCommands;
|
||||
};
|
||||
|
||||
} // namespace Android
|
||||
|
@@ -23,6 +23,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "androidrunconfigurationwidget.h"
|
||||
#include "adbcommandswidget.h"
|
||||
#include "ui_androidrunconfigurationwidget.h"
|
||||
|
||||
#include "utils/utilsicons.h"
|
||||
@@ -39,6 +40,26 @@ AndroidRunConfigurationWidget::AndroidRunConfigurationWidget(QWidget *parent):
|
||||
m_ui->setupUi(detailsWidget);
|
||||
m_ui->m_warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap());
|
||||
|
||||
m_preStartCmdsWidget = new AdbCommandsWidget(detailsWidget);
|
||||
connect(m_preStartCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() {
|
||||
emit preStartCmdsChanged(m_preStartCmdsWidget->commandsList());
|
||||
});
|
||||
m_preStartCmdsWidget->setTitleText(tr("Shell commands to run on Android device before"
|
||||
" application launch."));
|
||||
|
||||
m_postEndCmdsWidget = new AdbCommandsWidget(detailsWidget);
|
||||
connect(m_postEndCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() {
|
||||
emit postFinishCmdsChanged(m_postEndCmdsWidget->commandsList());
|
||||
});
|
||||
m_postEndCmdsWidget->setTitleText(tr("Shell commands to run on Android device after application"
|
||||
" quits."));
|
||||
|
||||
auto mainLayout = static_cast<QGridLayout*>(detailsWidget->layout());
|
||||
mainLayout->addWidget(m_preStartCmdsWidget->widget(), mainLayout->rowCount(),
|
||||
0, mainLayout->columnCount() - 1, 0);
|
||||
mainLayout->addWidget(m_postEndCmdsWidget->widget(), mainLayout->rowCount(),
|
||||
0, mainLayout->columnCount() - 1, 0);
|
||||
|
||||
setWidget(detailsWidget);
|
||||
setSummaryText(tr("Android run settings"));
|
||||
|
||||
@@ -54,8 +75,17 @@ AndroidRunConfigurationWidget::~AndroidRunConfigurationWidget()
|
||||
|
||||
void AndroidRunConfigurationWidget::setAmStartArgs(const QStringList &args)
|
||||
{
|
||||
if (m_ui->m_amStartArgsEdit && !args.isEmpty())
|
||||
m_ui->m_amStartArgsEdit->setText(Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux));
|
||||
m_ui->m_amStartArgsEdit->setText(Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux));
|
||||
}
|
||||
|
||||
void AndroidRunConfigurationWidget::setPreStartShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_preStartCmdsWidget->setCommandList(cmdList);
|
||||
}
|
||||
|
||||
void AndroidRunConfigurationWidget::setPostFinishShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_postEndCmdsWidget->setCommandList(cmdList);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -29,10 +29,14 @@
|
||||
#include "projectexplorer/runconfiguration.h"
|
||||
#include "utils/detailswidget.h"
|
||||
|
||||
#include <QStringListModel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
class AdbCommandsWidget;
|
||||
namespace Ui {
|
||||
class AndroidRunConfigurationWidget;
|
||||
}
|
||||
@@ -45,12 +49,22 @@ public:
|
||||
~AndroidRunConfigurationWidget();
|
||||
|
||||
void setAmStartArgs(const QStringList &args);
|
||||
void setPreStartShellCommands(const QStringList &cmdList);
|
||||
void setPostFinishShellCommands(const QStringList &cmdList);
|
||||
|
||||
signals:
|
||||
void amStartArgsChanged(QStringList args);
|
||||
void preStartCmdsChanged(const QStringList &cmdList);
|
||||
void postFinishCmdsChanged(const QStringList &cmdList);
|
||||
|
||||
private:
|
||||
void addPreStartCommand(const QString &command);
|
||||
void addPostFinishCommand(const QString &command);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::AndroidRunConfigurationWidget> m_ui;
|
||||
AdbCommandsWidget *m_preStartCmdsWidget = nullptr;
|
||||
AdbCommandsWidget *m_postEndCmdsWidget = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -6,25 +6,31 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>625</width>
|
||||
<height>73</height>
|
||||
<width>731</width>
|
||||
<height>119</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="spacing">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Conflicting "am start" options might result in the app startup failure.</string>
|
||||
<string>Activity manager start options:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="m_warningIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
@@ -37,7 +43,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@@ -53,13 +59,32 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<item row="3" column="0" colspan="3">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Activity manager start options:</string>
|
||||
<string>Conflicting "am start" options might result in the app startup failure.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@@ -37,8 +37,8 @@ struct ANDROID_EXPORT AndroidRunnable
|
||||
QString intentName;
|
||||
QStringList amStartExtraArgs;
|
||||
Utils::Environment environment;
|
||||
QVector<QStringList> beforeStartADBCommands;
|
||||
QVector<QStringList> afterFinishADBCommands;
|
||||
QStringList beforeStartAdbCommands;
|
||||
QStringList afterFinishAdbCommands;
|
||||
QString deviceSerialNumber;
|
||||
|
||||
QString displayName() const { return packageName; }
|
||||
@@ -51,8 +51,8 @@ inline bool operator==(const AndroidRunnable &r1, const AndroidRunnable &r2)
|
||||
&& r1.intentName == r2.intentName
|
||||
&& r1.amStartExtraArgs == r2.amStartExtraArgs
|
||||
&& r1.environment == r2.environment
|
||||
&& r1.beforeStartADBCommands == r2.beforeStartADBCommands
|
||||
&& r1.afterFinishADBCommands == r2.afterFinishADBCommands
|
||||
&& r1.beforeStartAdbCommands == r2.beforeStartAdbCommands
|
||||
&& r1.afterFinishAdbCommands == r2.afterFinishAdbCommands
|
||||
&& r1.deviceSerialNumber == r2.deviceSerialNumber;
|
||||
}
|
||||
|
||||
|
@@ -214,14 +214,13 @@ class AndroidRunnerWorker : public QObject
|
||||
};
|
||||
|
||||
public:
|
||||
AndroidRunnerWorker(RunControl *runControl, const QString &packageName,
|
||||
const QStringList &selector);
|
||||
AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable);
|
||||
~AndroidRunnerWorker();
|
||||
|
||||
void asyncStart(const AndroidRunnable &runnable);
|
||||
void asyncStop(const AndroidRunnable &runnable);
|
||||
void asyncStart();
|
||||
void asyncStop();
|
||||
|
||||
void setAdbParameters(const QString &packageName, const QStringList &selector);
|
||||
void setAndroidRunnable(const AndroidRunnable &runnable);
|
||||
void handleRemoteDebuggerRunning();
|
||||
|
||||
Utils::Port localGdbServerPort() const { return m_localGdbServerPort; }
|
||||
@@ -238,7 +237,7 @@ private:
|
||||
void logcatReadStandardError();
|
||||
void logcatReadStandardOutput();
|
||||
void adbKill(qint64 pid);
|
||||
QStringList selector() const { return m_selector; }
|
||||
QStringList selector() const;
|
||||
void forceStop();
|
||||
void findPs();
|
||||
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
||||
@@ -264,22 +263,19 @@ private:
|
||||
QString m_gdbserverPath;
|
||||
QString m_gdbserverSocket;
|
||||
QString m_adb;
|
||||
QStringList m_selector;
|
||||
QRegExp m_logCatRegExp;
|
||||
DebugHandShakeType m_handShakeMethod = SocketHandShake;
|
||||
bool m_customPort = false;
|
||||
|
||||
QString m_packageName;
|
||||
AndroidRunnable m_androidRunnable;
|
||||
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
||||
};
|
||||
|
||||
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &packageName,
|
||||
const QStringList &selector)
|
||||
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable)
|
||||
: m_adbLogcatProcess(nullptr, deleter)
|
||||
, m_psIsAlive(nullptr, deleter)
|
||||
, m_selector(selector)
|
||||
, m_logCatRegExp(regExpLogcat)
|
||||
, m_packageName(packageName)
|
||||
, m_androidRunnable(runnable)
|
||||
{
|
||||
auto runConfig = runControl->runConfiguration();
|
||||
auto aspect = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
|
||||
@@ -307,9 +303,9 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &
|
||||
}
|
||||
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
||||
|
||||
QString packageDir = "/data/data/" + m_packageName;
|
||||
QString packageDir = "/data/data/" + m_androidRunnable.packageName;
|
||||
m_pingFile = packageDir + "/debug-ping";
|
||||
m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_packageName;
|
||||
m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_androidRunnable.packageName;
|
||||
m_gdbserverSocket = packageDir + "/debug-socket";
|
||||
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(
|
||||
runConfig->target()->kit());
|
||||
@@ -347,20 +343,20 @@ AndroidRunnerWorker::~AndroidRunnerWorker()
|
||||
|
||||
void AndroidRunnerWorker::forceStop()
|
||||
{
|
||||
runAdb({"shell", "am", "force-stop", m_packageName}, nullptr, 30);
|
||||
runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, nullptr, 30);
|
||||
|
||||
// try killing it via kill -9
|
||||
const QByteArray out = Utils::SynchronousProcess()
|
||||
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript)
|
||||
.allRawOutput();
|
||||
|
||||
qint64 pid = extractPID(out.simplified(), m_packageName);
|
||||
qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName);
|
||||
if (pid != -1) {
|
||||
adbKill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
void AndroidRunnerWorker::asyncStart()
|
||||
{
|
||||
forceStop();
|
||||
|
||||
@@ -378,12 +374,12 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
if (m_useCppDebugger)
|
||||
runAdb({"shell", "rm", m_pongFile}); // Remove pong file.
|
||||
|
||||
for (const QStringList &entry: runnable.beforeStartADBCommands)
|
||||
runAdb(entry);
|
||||
for (const QString &entry: m_androidRunnable.beforeStartAdbCommands)
|
||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||
|
||||
QStringList args({"shell", "am", "start"});
|
||||
args << runnable.amStartExtraArgs;
|
||||
args << "-n" << runnable.intentName;
|
||||
args << m_androidRunnable.amStartExtraArgs;
|
||||
args << "-n" << m_androidRunnable.intentName;
|
||||
|
||||
if (m_useCppDebugger) {
|
||||
if (!runAdb({"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()})){
|
||||
@@ -395,7 +391,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
return;
|
||||
}
|
||||
|
||||
const QString pingPongSocket(m_packageName + ".ping_pong_socket");
|
||||
const QString pingPongSocket(m_androidRunnable.packageName + ".ping_pong_socket");
|
||||
args << "-e" << "debug_ping" << "true";
|
||||
if (m_handShakeMethod == SocketHandShake) {
|
||||
args << "-e" << "ping_socket" << pingPongSocket;
|
||||
@@ -499,7 +495,8 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
break;
|
||||
|
||||
if (i == 20) {
|
||||
emit remoteProcessFinished(tr("Unable to start \"%1\".").arg(m_packageName));
|
||||
emit remoteProcessFinished(tr("Unable to start \"%1\".")
|
||||
.arg(m_androidRunnable.packageName));
|
||||
return;
|
||||
}
|
||||
qDebug() << "WAITING FOR " << tmp.fileName();
|
||||
@@ -512,7 +509,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
||||
m_adbLogcatProcess = std::move(logcatProcess);
|
||||
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
|
||||
m_packageName),
|
||||
m_androidRunnable.packageName),
|
||||
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
||||
|
||||
}
|
||||
@@ -543,7 +540,7 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage,
|
||||
{
|
||||
Utils::SynchronousProcess adb;
|
||||
adb.setTimeoutS(timeoutS);
|
||||
Utils::SynchronousProcessResponse response = adb.run(m_adb, m_selector + args);
|
||||
Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args);
|
||||
if (exitMessage)
|
||||
*exitMessage = response.exitMessage(m_adb, timeoutS);
|
||||
return response.result == Utils::SynchronousProcessResponse::Finished;
|
||||
@@ -567,7 +564,7 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning()
|
||||
// emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable)
|
||||
void AndroidRunnerWorker::asyncStop()
|
||||
{
|
||||
if (!m_pidFinder.isFinished())
|
||||
m_pidFinder.cancel();
|
||||
@@ -575,14 +572,11 @@ void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable)
|
||||
if (m_processPID != -1) {
|
||||
forceStop();
|
||||
}
|
||||
for (const QStringList &entry: runnable.afterFinishADBCommands)
|
||||
runAdb(entry);
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector)
|
||||
void AndroidRunnerWorker::setAndroidRunnable(const AndroidRunnable &runnable)
|
||||
{
|
||||
m_packageName = packageName;
|
||||
m_selector = selector;
|
||||
m_androidRunnable = runnable;
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
||||
@@ -635,10 +629,14 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
|
||||
m_processPID = pid;
|
||||
if (pid == -1) {
|
||||
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
||||
.arg(m_packageName));
|
||||
.arg(m_androidRunnable.packageName));
|
||||
// App died/killed. Reset log and monitor processes.
|
||||
m_adbLogcatProcess.reset();
|
||||
m_psIsAlive.reset();
|
||||
|
||||
// Run adb commands after application quit.
|
||||
for (const QString &entry: m_androidRunnable.afterFinishAdbCommands)
|
||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||
} else {
|
||||
// In debugging cases this will be funneled to the engine to actually start
|
||||
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
||||
@@ -671,7 +669,12 @@ void AndroidRunnerWorker::logcatReadStandardOutput()
|
||||
void AndroidRunnerWorker::adbKill(qint64 pid)
|
||||
{
|
||||
runAdb({"shell", "kill", "-9", QString::number(pid)});
|
||||
runAdb({"shell", "run-as", m_packageName, "kill", "-9", QString::number(pid)});
|
||||
runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)});
|
||||
}
|
||||
|
||||
QStringList AndroidRunnerWorker::selector() const
|
||||
{
|
||||
return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber);
|
||||
}
|
||||
|
||||
AndroidRunner::AndroidRunner(RunControl *runControl)
|
||||
@@ -694,15 +697,19 @@ AndroidRunner::AndroidRunner(RunControl *runControl)
|
||||
|
||||
auto androidRunConfig = qobject_cast<AndroidRunConfiguration *>(runControl->runConfiguration());
|
||||
m_androidRunnable.amStartExtraArgs = androidRunConfig->amStartExtraArgs();
|
||||
for (QString shellCmd: androidRunConfig->preStartShellCommands())
|
||||
m_androidRunnable.beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
|
||||
|
||||
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber)));
|
||||
for (QString shellCmd: androidRunConfig->postFinishShellCommands())
|
||||
m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
||||
|
||||
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable));
|
||||
m_worker->moveToThread(&m_thread);
|
||||
|
||||
connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorker::asyncStart);
|
||||
connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorker::asyncStop);
|
||||
connect(this, &AndroidRunner::adbParametersChanged,
|
||||
m_worker.data(), &AndroidRunnerWorker::setAdbParameters);
|
||||
connect(this, &AndroidRunner::androidRunnableChanged,
|
||||
m_worker.data(), &AndroidRunnerWorker::setAndroidRunnable);
|
||||
connect(this, &AndroidRunner::remoteDebuggerRunning,
|
||||
m_worker.data(), &AndroidRunnerWorker::handleRemoteDebuggerRunning);
|
||||
|
||||
@@ -738,7 +745,7 @@ void AndroidRunner::start()
|
||||
}
|
||||
}
|
||||
|
||||
emit asyncStart(m_androidRunnable);
|
||||
emit asyncStart();
|
||||
}
|
||||
|
||||
void AndroidRunner::stop()
|
||||
@@ -750,7 +757,7 @@ void AndroidRunner::stop()
|
||||
return;
|
||||
}
|
||||
|
||||
emit asyncStop(m_androidRunnable);
|
||||
emit asyncStop();
|
||||
}
|
||||
|
||||
void AndroidRunner::qmlServerPortReady(Port port)
|
||||
@@ -797,8 +804,7 @@ void AndroidRunner::setRunnable(const AndroidRunnable &runnable)
|
||||
{
|
||||
if (runnable != m_androidRunnable) {
|
||||
m_androidRunnable = runnable;
|
||||
emit adbParametersChanged(runnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(runnable.deviceSerialNumber));
|
||||
emit androidRunnableChanged(m_androidRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,8 +822,7 @@ void AndroidRunner::launchAVD()
|
||||
AndroidConfigurations::None);
|
||||
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
|
||||
m_androidRunnable.deviceSerialNumber = info.serialNumber;
|
||||
emit adbParametersChanged(m_androidRunnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
||||
emit androidRunnableChanged(m_androidRunnable);
|
||||
if (info.isValid()) {
|
||||
AndroidAvdManager avdManager;
|
||||
if (avdManager.findAvd(info.avdname).isEmpty()) {
|
||||
@@ -840,7 +845,7 @@ void AndroidRunner::checkAVD()
|
||||
if (avdManager.isAvdBooted(serialNumber)) {
|
||||
m_checkAVDTimer.stop();
|
||||
AndroidManager::setDeviceSerialNumber(m_target, serialNumber);
|
||||
emit asyncStart(m_androidRunnable);
|
||||
emit asyncStart();
|
||||
} else if (!config.isConnected(serialNumber)) {
|
||||
// device was disconnected
|
||||
m_checkAVDTimer.stop();
|
||||
|
@@ -64,12 +64,11 @@ public:
|
||||
void stop() override;
|
||||
|
||||
signals:
|
||||
void asyncStart(const AndroidRunnable &runnable);
|
||||
void asyncStop(const AndroidRunnable &runnable);
|
||||
void asyncStart();
|
||||
void asyncStop();
|
||||
void remoteDebuggerRunning();
|
||||
void qmlServerReady(const QUrl &serverUrl);
|
||||
|
||||
void adbParametersChanged(const QString &packageName, const QStringList &selector);
|
||||
void androidRunnableChanged(const AndroidRunnable &runnable);
|
||||
void avdDetected();
|
||||
|
||||
private:
|
||||
|
@@ -35,7 +35,17 @@ namespace Internal {
|
||||
TestOutputReader *QtTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const
|
||||
{
|
||||
return new QtTestOutputReader(fi, app, buildDirectory());
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return nullptr;
|
||||
|
||||
if (qtSettings->useXMLOutput)
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::XML);
|
||||
else
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::PlainText);
|
||||
}
|
||||
|
||||
QStringList QtTestConfiguration::argumentsForTestRunner() const
|
||||
@@ -43,14 +53,15 @@ QStringList QtTestConfiguration::argumentsForTestRunner() const
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
|
||||
QStringList arguments("-xml");
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
QStringList arguments;
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return arguments;
|
||||
if (qtSettings->useXMLOutput)
|
||||
arguments << "-xml";
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
const QString &metricsOption = QtTestSettings::metricsTypeToOption(qtSettings->metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
|
@@ -31,6 +31,7 @@
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QRegExp>
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
@@ -128,12 +129,26 @@ static QString constructSourceFilePath(const QString &path, const QString &fileP
|
||||
}
|
||||
|
||||
QtTestOutputReader::QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
|
||||
QProcess *testApplication, const QString &buildDirectory)
|
||||
QProcess *testApplication, const QString &buildDirectory,
|
||||
OutputMode mode)
|
||||
: TestOutputReader(futureInterface, testApplication, buildDirectory)
|
||||
, m_mode(mode)
|
||||
{
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
||||
{
|
||||
switch (m_mode) {
|
||||
case PlainText:
|
||||
processPlainTextOutput(outputLine);
|
||||
break;
|
||||
case XML:
|
||||
processXMLOutput(outputLine);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processXMLOutput(const QByteArray &outputLine)
|
||||
{
|
||||
static QStringList validEndTags = {QStringLiteral("Incident"),
|
||||
QStringLiteral("Message"),
|
||||
@@ -162,24 +177,14 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
||||
if (currentTag == QStringLiteral("TestCase")) {
|
||||
m_className = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||
QTC_ASSERT(!m_className.isEmpty(), continue);
|
||||
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||
testResult->setResult(Result::MessageTestCaseStart);
|
||||
testResult->setDescription(tr("Executing test case %1").arg(m_className));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
sendStartMessage(false);
|
||||
} else if (currentTag == QStringLiteral("TestFunction")) {
|
||||
m_testCase = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||
QTC_ASSERT(!m_testCase.isEmpty(), continue);
|
||||
if (m_testCase == m_formerTestCase) // don't report "Executing..." more than once
|
||||
continue;
|
||||
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||
testResult->setResult(Result::MessageTestCaseStart);
|
||||
testResult->setDescription(tr("Executing test function %1").arg(m_testCase));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
testResult = TestResultPtr(new QtTestResult);
|
||||
testResult->setResult(Result::MessageCurrentTest);
|
||||
testResult->setDescription(tr("Entering test function %1::%2").arg(m_className,
|
||||
m_testCase));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
sendStartMessage(true);
|
||||
sendMessageCurrentTest();
|
||||
} else if (currentTag == QStringLiteral("Duration")) {
|
||||
m_duration = m_xmlReader.attributes().value(QStringLiteral("msecs")).toString();
|
||||
QTC_ASSERT(!m_duration.isEmpty(), continue);
|
||||
@@ -255,23 +260,13 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
||||
m_cdataMode = None;
|
||||
const QStringRef currentTag = m_xmlReader.name();
|
||||
if (currentTag == QStringLiteral("TestFunction")) {
|
||||
QtTestResult *testResult = createDefaultResult();
|
||||
testResult->setResult(Result::MessageTestCaseEnd);
|
||||
testResult->setDescription(
|
||||
m_duration.isEmpty() ? tr("Test function finished.")
|
||||
: tr("Execution took %1 ms.").arg(m_duration));
|
||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||
sendFinishMessage(true);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
m_dataTag.clear();
|
||||
m_formerTestCase = m_testCase;
|
||||
m_testCase.clear();
|
||||
} else if (currentTag == QStringLiteral("TestCase")) {
|
||||
QtTestResult *testResult = createDefaultResult();
|
||||
testResult->setResult(Result::MessageTestCaseEnd);
|
||||
testResult->setDescription(
|
||||
m_duration.isEmpty() ? tr("Test finished.")
|
||||
: tr("Test execution took %1 ms.").arg(m_duration));
|
||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||
sendFinishMessage(false);
|
||||
} else if (validEndTags.contains(currentTag.toString())) {
|
||||
QtTestResult *testResult = createDefaultResult();
|
||||
testResult->setResult(m_result);
|
||||
@@ -290,6 +285,138 @@ void QtTestOutputReader::processOutput(const QByteArray &outputLine)
|
||||
}
|
||||
}
|
||||
|
||||
static QStringList extractFunctionInformation(const QString &testClassName,
|
||||
const QString &lineWithoutResultType,
|
||||
Result::Type resultType)
|
||||
{
|
||||
static QRegularExpression classInformation("^(.+?)\\((.*?)\\)(.*)$");
|
||||
QStringList result;
|
||||
const QRegularExpressionMatch match = classInformation.match(lineWithoutResultType);
|
||||
if (match.hasMatch()) {
|
||||
QString fullQualifiedFunc = match.captured(1);
|
||||
QTC_ASSERT(fullQualifiedFunc.startsWith(testClassName + "::"), return result);
|
||||
fullQualifiedFunc = fullQualifiedFunc.mid(testClassName.length() + 2);
|
||||
result.append(fullQualifiedFunc);
|
||||
if (resultType == Result::Benchmark) { // tag is displayed differently
|
||||
QString possiblyTag = match.captured(3);
|
||||
if (!possiblyTag.isEmpty())
|
||||
possiblyTag = possiblyTag.mid(2, possiblyTag.length() - 4);
|
||||
result.append(possiblyTag);
|
||||
result.append(QString());
|
||||
} else {
|
||||
result.append(match.captured(2));
|
||||
result.append(match.captured(3));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processPlainTextOutput(const QByteArray &outputLine)
|
||||
{
|
||||
static QRegExp start("^[*]{9} Start testing of (.*) [*]{9}$");
|
||||
static QRegExp config("^Config: Using QtTest library (.*), (Qt (\\d+(\\.\\d+){2}) \\(.*\\))$");
|
||||
static QRegExp summary("^Totals: \\d+ passed, \\d+ failed, \\d+ skipped(, \\d+ blacklisted)?$");
|
||||
static QRegExp finish("^[*]{9} Finished testing of (.*) [*]{9}$");
|
||||
|
||||
static QRegExp result("^(PASS |FAIL! |XFAIL |XPASS |SKIP |BPASS |BFAIL |RESULT "
|
||||
"|INFO |QWARN |WARNING|QDEBUG ): (.*)$");
|
||||
|
||||
static QRegExp benchDetails("^\\s+([\\d,.]+ .* per iteration \\(total: [\\d,.]+, iterations: \\d+\\))$");
|
||||
static QRegExp locationUnix("^ Loc: \\[(.*)\\]$");
|
||||
static QRegExp locationWin("^(.*\\(\\d+\\)) : failure location$");
|
||||
|
||||
if (m_futureInterface.isCanceled())
|
||||
return;
|
||||
|
||||
const QString &line = QString::fromLatin1(outputLine);
|
||||
|
||||
if (result.exactMatch(line)) {
|
||||
processResultOutput(result.cap(1).toLower().trimmed(), result.cap(2));
|
||||
} else if (locationUnix.exactMatch(line)) {
|
||||
processLocationOutput(locationUnix.cap(1));
|
||||
} else if (locationWin.exactMatch(line)) {
|
||||
processLocationOutput(locationWin.cap(1));
|
||||
} else if (benchDetails.exactMatch(line)) {
|
||||
m_description = benchDetails.cap(1);
|
||||
} else if (config.exactMatch(line)) {
|
||||
handleAndSendConfigMessage(config);
|
||||
} else if (start.exactMatch(line)) {
|
||||
m_className = start.cap(1);
|
||||
QTC_CHECK(!m_className.isEmpty());
|
||||
sendStartMessage(false);
|
||||
} else if (summary.exactMatch(line) || finish.exactMatch(line)) {
|
||||
processSummaryFinishOutput();
|
||||
} else { // we have some plain output, but we cannot say where for sure it belongs to..
|
||||
if (!m_description.isEmpty())
|
||||
m_description.append('\n');
|
||||
m_description.append(line);
|
||||
}
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processResultOutput(const QString &result, const QString &message)
|
||||
{
|
||||
if (!m_testCase.isEmpty()) { // report the former result if there is any
|
||||
sendCompleteInformation();
|
||||
m_dataTag.clear();
|
||||
m_description.clear();
|
||||
m_file.clear();
|
||||
m_lineNumber = 0;
|
||||
}
|
||||
m_result = TestResult::resultFromString(result);
|
||||
const QStringList funcWithTag = extractFunctionInformation(m_className, message, m_result);
|
||||
QTC_ASSERT(funcWithTag.size() == 3, return);
|
||||
m_testCase = funcWithTag.at(0);
|
||||
if (m_testCase != m_formerTestCase) { // new test function executed
|
||||
if (!m_formerTestCase.isEmpty()) {
|
||||
using namespace std;
|
||||
swap(m_testCase, m_formerTestCase); // we want formerTestCase to be reported
|
||||
sendFinishMessage(true);
|
||||
swap(m_testCase, m_formerTestCase);
|
||||
}
|
||||
sendStartMessage(true);
|
||||
sendMessageCurrentTest();
|
||||
}
|
||||
m_dataTag = funcWithTag.at(1);
|
||||
const QString description = funcWithTag.at(2);
|
||||
if (!description.isEmpty()) {
|
||||
if (!m_description.isEmpty())
|
||||
m_description.append('\n');
|
||||
m_description.append(description.mid(1)); // cut the first whitespace
|
||||
}
|
||||
m_formerTestCase = m_testCase;
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processLocationOutput(const QString &fileWithLine)
|
||||
{
|
||||
QTC_ASSERT(fileWithLine.endsWith(')'), return);
|
||||
int openBrace = fileWithLine.lastIndexOf('(');
|
||||
QTC_ASSERT(openBrace != -1, return);
|
||||
m_file = constructSourceFilePath(m_buildDir, fileWithLine.left(openBrace));
|
||||
QString numberStr = fileWithLine.mid(openBrace + 1);
|
||||
numberStr.chop(1);
|
||||
m_lineNumber = numberStr.toInt();
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processSummaryFinishOutput()
|
||||
{
|
||||
if (m_className.isEmpty()) // we have reported already
|
||||
return;
|
||||
// we still have something to report
|
||||
sendCompleteInformation();
|
||||
m_dataTag.clear();
|
||||
// report finished function
|
||||
sendFinishMessage(true);
|
||||
m_testCase.clear();
|
||||
m_formerTestCase.clear();
|
||||
// create and report the finish message for this test class
|
||||
sendFinishMessage(false);
|
||||
m_className.clear();
|
||||
m_description.clear();
|
||||
m_result = Result::Invalid;
|
||||
m_file.clear();
|
||||
m_lineNumber = 0;
|
||||
}
|
||||
|
||||
QtTestResult *QtTestOutputReader::createDefaultResult() const
|
||||
{
|
||||
QtTestResult *result = new QtTestResult(m_className);
|
||||
@@ -298,5 +425,63 @@ QtTestResult *QtTestOutputReader::createDefaultResult() const
|
||||
return result;
|
||||
}
|
||||
|
||||
void QtTestOutputReader::sendCompleteInformation()
|
||||
{
|
||||
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||
testResult->setResult(m_result);
|
||||
testResult->setFileName(m_file);
|
||||
testResult->setLine(m_lineNumber);
|
||||
testResult->setDescription(m_description);
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
|
||||
void QtTestOutputReader::sendMessageCurrentTest()
|
||||
{
|
||||
TestResultPtr testResult = TestResultPtr(new QtTestResult);
|
||||
testResult->setResult(Result::MessageCurrentTest);
|
||||
testResult->setDescription(tr("Entering test function %1::%2").arg(m_className, m_testCase));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
|
||||
void QtTestOutputReader::sendStartMessage(bool isFunction)
|
||||
{
|
||||
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||
testResult->setResult(Result::MessageTestCaseStart);
|
||||
testResult->setDescription(isFunction ? tr("Executing test function %1").arg(m_testCase)
|
||||
: tr("Executing test case %1").arg(m_className));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
|
||||
void QtTestOutputReader::sendFinishMessage(bool isFunction)
|
||||
{
|
||||
TestResultPtr testResult = TestResultPtr(createDefaultResult());
|
||||
testResult->setResult(Result::MessageTestCaseEnd);
|
||||
if (m_duration.isEmpty()) {
|
||||
testResult->setDescription(isFunction ? tr("Execution took %1 ms.").arg(m_duration)
|
||||
: tr("Test execution took %1 ms.").arg(m_duration));
|
||||
} else {
|
||||
testResult->setDescription(isFunction ? tr("Test function finished.")
|
||||
: tr("Test finished."));
|
||||
}
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
|
||||
// TODO factor out tr() strings to avoid duplication (see XML processing of Characters)
|
||||
void QtTestOutputReader::handleAndSendConfigMessage(const QRegExp &config)
|
||||
{
|
||||
QtTestResult *testResult = createDefaultResult();
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("Qt version: %1").arg(config.cap(3)));
|
||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||
testResult = createDefaultResult();
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("Qt build: %1").arg(config.cap(2)));
|
||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||
testResult = createDefaultResult();
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("QTest version: %1").arg(config.cap(1)));
|
||||
m_futureInterface.reportResult(TestResultPtr(testResult));
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
@@ -40,14 +40,32 @@ class QtTestOutputReader : public TestOutputReader
|
||||
Q_DECLARE_TR_FUNCTIONS(Autotest::Internal::QtTestOutputReader)
|
||||
|
||||
public:
|
||||
enum OutputMode
|
||||
{
|
||||
XML,
|
||||
PlainText
|
||||
};
|
||||
|
||||
QtTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface,
|
||||
QProcess *testApplication, const QString &buildDirectory);
|
||||
QProcess *testApplication, const QString &buildDirectory,
|
||||
OutputMode mode);
|
||||
|
||||
protected:
|
||||
void processOutput(const QByteArray &outputLine) override;
|
||||
|
||||
private:
|
||||
void processXMLOutput(const QByteArray &outputLine);
|
||||
void processPlainTextOutput(const QByteArray &outputLine);
|
||||
void processResultOutput(const QString &result, const QString &message);
|
||||
void processLocationOutput(const QString &fileWithLine);
|
||||
void processSummaryFinishOutput();
|
||||
// helper functions
|
||||
QtTestResult *createDefaultResult() const;
|
||||
void sendCompleteInformation();
|
||||
void sendMessageCurrentTest();
|
||||
void sendStartMessage(bool isFunction);
|
||||
void sendFinishMessage(bool isFunction);
|
||||
void handleAndSendConfigMessage(const QRegExp &config);
|
||||
|
||||
enum CDATAMode
|
||||
{
|
||||
@@ -70,6 +88,8 @@ private:
|
||||
int m_lineNumber = 0;
|
||||
QString m_duration;
|
||||
QXmlStreamReader m_xmlReader;
|
||||
OutputMode m_mode = XML;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -30,6 +30,7 @@ namespace Internal {
|
||||
|
||||
static const char metricsKey[] = "Metrics";
|
||||
static const char noCrashhandlerKey[] = "NoCrashhandlerOnDebug";
|
||||
static const char useXMLOutputKey[] = "UseXMLOutput";
|
||||
|
||||
static MetricsType intToMetrics(int value)
|
||||
{
|
||||
@@ -58,12 +59,14 @@ void QtTestSettings::fromFrameworkSettings(const QSettings *s)
|
||||
{
|
||||
metrics = intToMetrics(s->value(metricsKey, Walltime).toInt());
|
||||
noCrashHandler = s->value(noCrashhandlerKey, true).toBool();
|
||||
useXMLOutput = s->value(useXMLOutputKey, true).toBool();
|
||||
}
|
||||
|
||||
void QtTestSettings::toFrameworkSettings(QSettings *s) const
|
||||
{
|
||||
s->setValue(metricsKey, metrics);
|
||||
s->setValue(noCrashhandlerKey, noCrashHandler);
|
||||
s->setValue(useXMLOutputKey, useXMLOutput);
|
||||
}
|
||||
|
||||
QString QtTestSettings::metricsTypeToOption(const MetricsType type)
|
||||
|
@@ -48,6 +48,7 @@ public:
|
||||
|
||||
MetricsType metrics = Walltime;
|
||||
bool noCrashHandler = true;
|
||||
bool useXMLOutput = true;
|
||||
|
||||
protected:
|
||||
void fromFrameworkSettings(const QSettings *s) override;
|
||||
|
@@ -46,6 +46,7 @@ QtTestSettingsWidget::QtTestSettingsWidget(QWidget *parent)
|
||||
void QtTestSettingsWidget::setSettings(const QtTestSettings &settings)
|
||||
{
|
||||
m_ui.disableCrashhandlerCB->setChecked(settings.noCrashHandler);
|
||||
m_ui.useXMLOutputCB->setChecked(settings.useXMLOutput);
|
||||
switch (settings.metrics) {
|
||||
case MetricsType::Walltime:
|
||||
m_ui.walltimeRB->setChecked(true);
|
||||
@@ -72,6 +73,7 @@ QtTestSettings QtTestSettingsWidget::settings() const
|
||||
QtTestSettings result;
|
||||
|
||||
result.noCrashHandler = m_ui.disableCrashhandlerCB->isChecked();
|
||||
result.useXMLOutput = m_ui.useXMLOutputCB->isChecked();
|
||||
if (m_ui.walltimeRB->isChecked())
|
||||
result.metrics = MetricsType::Walltime;
|
||||
else if (m_ui.tickcounterRB->isChecked())
|
||||
|
@@ -31,6 +31,21 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="useXMLOutputCB">
|
||||
<property name="toolTip">
|
||||
<string>XML output recommended as it avoids parsing issues, while plain text is more human readable.
|
||||
|
||||
Warning: Plain text output is missing some information (e.g. duration)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Use XML output</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
|
@@ -35,7 +35,16 @@ namespace Internal {
|
||||
TestOutputReader *QuickTestConfiguration::outputReader(const QFutureInterface<TestResultPtr> &fi,
|
||||
QProcess *app) const
|
||||
{
|
||||
return new QtTestOutputReader(fi, app, buildDirectory());
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return nullptr;
|
||||
if (qtSettings->useXMLOutput)
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::XML);
|
||||
else
|
||||
return new QtTestOutputReader(fi, app, buildDirectory(), QtTestOutputReader::PlainText);
|
||||
}
|
||||
|
||||
QStringList QuickTestConfiguration::argumentsForTestRunner() const
|
||||
@@ -43,14 +52,15 @@ QStringList QuickTestConfiguration::argumentsForTestRunner() const
|
||||
static const Core::Id id
|
||||
= Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(QtTest::Constants::FRAMEWORK_NAME);
|
||||
|
||||
QStringList arguments("-xml");
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
QStringList arguments;
|
||||
TestFrameworkManager *manager = TestFrameworkManager::instance();
|
||||
auto qtSettings = qSharedPointerCast<QtTestSettings>(manager->settingsForTestFramework(id));
|
||||
if (qtSettings.isNull())
|
||||
return arguments;
|
||||
if (qtSettings->useXMLOutput)
|
||||
arguments << "-xml";
|
||||
if (testCases().count())
|
||||
arguments << testCases();
|
||||
|
||||
const QString &metricsOption = QtTestSettings::metricsTypeToOption(qtSettings->metrics);
|
||||
if (!metricsOption.isEmpty())
|
||||
|
@@ -56,7 +56,7 @@ Result::Type TestResult::resultFromString(const QString &resultString)
|
||||
{
|
||||
if (resultString == "pass")
|
||||
return Result::Pass;
|
||||
if (resultString == "fail")
|
||||
if (resultString == "fail" || resultString == "fail!")
|
||||
return Result::Fail;
|
||||
if (resultString == "xfail")
|
||||
return Result::ExpectedFail;
|
||||
@@ -64,11 +64,13 @@ Result::Type TestResult::resultFromString(const QString &resultString)
|
||||
return Result::UnexpectedPass;
|
||||
if (resultString == "skip")
|
||||
return Result::Skip;
|
||||
if (resultString == "result")
|
||||
return Result::Benchmark;
|
||||
if (resultString == "qdebug")
|
||||
return Result::MessageDebug;
|
||||
if (resultString == "qinfo")
|
||||
if (resultString == "qinfo" || resultString == "info")
|
||||
return Result::MessageInfo;
|
||||
if (resultString == "warn" || resultString == "qwarn")
|
||||
if (resultString == "warn" || resultString == "qwarn" || resultString == "warning")
|
||||
return Result::MessageWarn;
|
||||
if (resultString == "qfatal")
|
||||
return Result::MessageFatal;
|
||||
|
@@ -313,7 +313,7 @@ TestResultItem *TestResultModel::findParentItemFor(const TestResultItem *item,
|
||||
TestResultItem *currentItem = static_cast<TestResultItem *>(it);
|
||||
return currentItem->testResult()->isDirectParentOf(result, &needsIntermediate);
|
||||
};
|
||||
TestResultItem *parent = static_cast<TestResultItem *>(root->findAnyChild(predicate));
|
||||
TestResultItem *parent = static_cast<TestResultItem *>(root->reverseFindAnyChild(predicate));
|
||||
if (parent) {
|
||||
if (needsIntermediate) {
|
||||
// check if the intermediate is present already
|
||||
|
@@ -378,7 +378,8 @@ void TestRunner::debugTests()
|
||||
outputreader, &QObject::deleteLater);
|
||||
}
|
||||
|
||||
connect(this, &TestRunner::requestStopTestRun, runControl, &ProjectExplorer::RunControl::stop);
|
||||
connect(this, &TestRunner::requestStopTestRun, runControl,
|
||||
&ProjectExplorer::RunControl::initiateStop);
|
||||
connect(runControl, &ProjectExplorer::RunControl::finished, this, &TestRunner::onFinished);
|
||||
ProjectExplorer::ProjectExplorerPlugin::startRunControl(runControl);
|
||||
}
|
||||
|
@@ -79,7 +79,7 @@ ClangStaticAnalyzerToolRunner::ClangStaticAnalyzerToolRunner(RunControl *runCont
|
||||
|
||||
RunConfiguration *runConfiguration = runControl->runConfiguration();
|
||||
auto tool = ClangStaticAnalyzerTool::instance();
|
||||
connect(tool->stopAction(), &QAction::triggered, runControl, &RunControl::stop);
|
||||
connect(tool->stopAction(), &QAction::triggered, runControl, &RunControl::initiateStop);
|
||||
|
||||
ProjectInfo projectInfoBeforeBuild = tool->projectInfoBeforeBuild();
|
||||
QTC_ASSERT(projectInfoBeforeBuild.isValid(), return);
|
||||
|
@@ -28,9 +28,14 @@
|
||||
#include "editorarea.h"
|
||||
#include "editormanager_p.h"
|
||||
|
||||
#include <aggregation/aggregate.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/locator/locator.h>
|
||||
#include <coreplugin/locator/locatorwidget.h>
|
||||
#include <coreplugin/statusbarmanager.h>
|
||||
|
||||
#include <QStatusBar>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Core {
|
||||
@@ -46,6 +51,14 @@ EditorWindow::EditorWindow(QWidget *parent) :
|
||||
setLayout(layout);
|
||||
layout->addWidget(m_area);
|
||||
setFocusProxy(m_area);
|
||||
auto statusBar = new QStatusBar;
|
||||
layout->addWidget(statusBar);
|
||||
auto splitter = new NonResizingSplitter(statusBar);
|
||||
splitter->setChildrenCollapsible(false);
|
||||
statusBar->addPermanentWidget(splitter, 10);
|
||||
auto locatorWidget = createStaticLocatorWidget(Locator::instance());
|
||||
splitter->addWidget(locatorWidget);
|
||||
splitter->addWidget(new QWidget);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAttribute(Qt::WA_QuitOnClose, false); // don't prevent Qt Creator from closing
|
||||
resize(QSize(800, 600));
|
||||
@@ -61,6 +74,11 @@ EditorWindow::EditorWindow(QWidget *parent) :
|
||||
deleteLater();
|
||||
});
|
||||
updateWindowTitle();
|
||||
|
||||
// register locator widget for this window
|
||||
auto agg = new Aggregation::Aggregate;
|
||||
agg->add(this);
|
||||
agg->add(locatorWidget);
|
||||
}
|
||||
|
||||
EditorWindow::~EditorWindow()
|
||||
|
@@ -86,6 +86,11 @@ Locator::~Locator()
|
||||
qDeleteAll(m_customFilters);
|
||||
}
|
||||
|
||||
Locator *Locator::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
void Locator::initialize(CorePlugin *corePlugin, const QStringList &, QString *)
|
||||
{
|
||||
m_corePlugin = corePlugin;
|
||||
@@ -103,15 +108,14 @@ void Locator::initialize(CorePlugin *corePlugin, const QStringList &, QString *)
|
||||
ActionContainer *mtools = ActionManager::actionContainer(Constants::M_TOOLS);
|
||||
mtools->addAction(cmd);
|
||||
|
||||
auto locatorWidget = new LocatorWidget(this);
|
||||
new LocatorPopup(locatorWidget, locatorWidget); // child of locatorWidget
|
||||
m_locatorWidget = createStaticLocatorWidget(this);
|
||||
StatusBarWidget *view = new StatusBarWidget;
|
||||
view->setWidget(locatorWidget);
|
||||
view->setWidget(m_locatorWidget);
|
||||
view->setContext(Context("LocatorWidget"));
|
||||
view->setPosition(StatusBarWidget::First);
|
||||
m_corePlugin->addAutoReleasedObject(view);
|
||||
|
||||
new LocatorManager(locatorWidget);
|
||||
new LocatorManager(this);
|
||||
|
||||
m_openDocumentsFilter = new OpenDocumentsFilter;
|
||||
m_corePlugin->addObject(m_openDocumentsFilter);
|
||||
@@ -135,6 +139,11 @@ void Locator::initialize(CorePlugin *corePlugin, const QStringList &, QString *)
|
||||
|
||||
void Locator::extensionsInitialized()
|
||||
{
|
||||
// register locator widget for main window
|
||||
auto agg = new Aggregation::Aggregate;
|
||||
agg->add(ICore::mainWindow());
|
||||
agg->add(m_locatorWidget);
|
||||
|
||||
m_filters = ExtensionSystem::PluginManager::getObjects<ILocatorFilter>();
|
||||
Utils::sort(m_filters, [](const ILocatorFilter *first, const ILocatorFilter *second) -> bool {
|
||||
if (first->priority() != second->priority())
|
||||
@@ -341,6 +350,11 @@ void Locator::setRefreshInterval(int interval)
|
||||
m_refreshTimer.start();
|
||||
}
|
||||
|
||||
LocatorWidget *Locator::mainLocatorWidget()
|
||||
{
|
||||
return m_instance->m_locatorWidget;
|
||||
}
|
||||
|
||||
void Locator::refresh(QList<ILocatorFilter *> filters)
|
||||
{
|
||||
if (filters.isEmpty())
|
||||
|
@@ -43,6 +43,7 @@ class CorePlugin;
|
||||
class OpenDocumentsFilter;
|
||||
class FileSystemFilter;
|
||||
class LocatorSettingsPage;
|
||||
class LocatorWidget;
|
||||
class ExternalToolsFilter;
|
||||
|
||||
class Locator : public QObject
|
||||
@@ -53,6 +54,8 @@ public:
|
||||
Locator();
|
||||
~Locator();
|
||||
|
||||
static Locator *instance();
|
||||
|
||||
void initialize(CorePlugin *corePlugin, const QStringList &arguments, QString *errorMessage);
|
||||
void extensionsInitialized();
|
||||
bool delayedInitialize();
|
||||
@@ -63,6 +66,7 @@ public:
|
||||
void setCustomFilters(QList<ILocatorFilter *> f);
|
||||
int refreshInterval();
|
||||
void setRefreshInterval(int interval);
|
||||
static LocatorWidget *mainLocatorWidget();
|
||||
|
||||
signals:
|
||||
void filtersChanged();
|
||||
@@ -89,6 +93,7 @@ private:
|
||||
ExecuteFilter *m_executeFilter;
|
||||
CorePlugin *m_corePlugin = nullptr;
|
||||
ExternalToolsFilter *m_externalToolsFilter;
|
||||
LocatorWidget *m_locatorWidget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -29,29 +29,45 @@
|
||||
#include "locator.h"
|
||||
#include "locatorwidget.h"
|
||||
|
||||
#include <aggregation/aggregate.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace Core::Internal;
|
||||
|
||||
namespace Core {
|
||||
|
||||
static Internal::LocatorWidget *m_locatorWidget = 0;
|
||||
|
||||
LocatorManager::LocatorManager(Internal::LocatorWidget *locatorWidget)
|
||||
: QObject(locatorWidget)
|
||||
LocatorManager::LocatorManager(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_locatorWidget = locatorWidget;
|
||||
}
|
||||
|
||||
static LocatorWidget *locatorWidget()
|
||||
{
|
||||
static QPointer<LocatorPopup> popup;
|
||||
QWidget *window = ICore::dialogParent()->window();
|
||||
if (auto *widget = Aggregation::query<LocatorWidget>(window)) {
|
||||
if (popup)
|
||||
popup->close();
|
||||
return widget;
|
||||
}
|
||||
if (!popup) {
|
||||
popup = createLocatorPopup(Locator::instance(), window);
|
||||
popup->show();
|
||||
}
|
||||
return popup->inputWidget();
|
||||
}
|
||||
|
||||
void LocatorManager::showFilter(ILocatorFilter *filter)
|
||||
{
|
||||
QTC_ASSERT(filter, return);
|
||||
QTC_ASSERT(m_locatorWidget, return);
|
||||
QString searchText = tr("<type here>");
|
||||
const QString currentText = m_locatorWidget->currentText().trimmed();
|
||||
const QString currentText = locatorWidget()->currentText().trimmed();
|
||||
// add shortcut string at front or replace existing shortcut string
|
||||
if (!currentText.isEmpty()) {
|
||||
searchText = currentText;
|
||||
foreach (ILocatorFilter *otherfilter, Internal::Locator::filters()) {
|
||||
foreach (ILocatorFilter *otherfilter, Locator::filters()) {
|
||||
if (currentText.startsWith(otherfilter->shortcutString() + QLatin1Char(' '))) {
|
||||
searchText = currentText.mid(otherfilter->shortcutString().length() + 1);
|
||||
break;
|
||||
@@ -66,8 +82,7 @@ void LocatorManager::showFilter(ILocatorFilter *filter)
|
||||
void LocatorManager::show(const QString &text,
|
||||
int selectionStart, int selectionLength)
|
||||
{
|
||||
QTC_ASSERT(m_locatorWidget, return);
|
||||
m_locatorWidget->showText(text, selectionStart, selectionLength);
|
||||
locatorWidget()->showText(text, selectionStart, selectionLength);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
|
@@ -40,7 +40,7 @@ class CORE_EXPORT LocatorManager : public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
LocatorManager(Internal::LocatorWidget *locatorWidget);
|
||||
LocatorManager(QObject *parent = 0);
|
||||
|
||||
static void showFilter(ILocatorFilter *filter);
|
||||
static void show(const QString &text, int selectionStart = -1, int selectionLength = 0);
|
||||
|
@@ -49,7 +49,9 @@
|
||||
#include <utils/stylehelper.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QColor>
|
||||
#include <QDesktopWidget>
|
||||
#include <QFileInfo>
|
||||
#include <QTimer>
|
||||
#include <QEvent>
|
||||
@@ -112,6 +114,28 @@ public:
|
||||
void showCurrentItemToolTip();
|
||||
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
bool eventFilter(QObject *watched, QEvent *event);
|
||||
};
|
||||
|
||||
class TopLeftLocatorPopup : public LocatorPopup
|
||||
{
|
||||
public:
|
||||
TopLeftLocatorPopup(LocatorWidget *locatorWidget)
|
||||
: LocatorPopup(locatorWidget, locatorWidget) {}
|
||||
|
||||
protected:
|
||||
void updateGeometry() override;
|
||||
void inputLostFocus() override;
|
||||
};
|
||||
|
||||
class CenteredLocatorPopup : public LocatorPopup
|
||||
{
|
||||
public:
|
||||
CenteredLocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
|
||||
: LocatorPopup(locatorWidget, parent) {}
|
||||
|
||||
protected:
|
||||
void updateGeometry() override;
|
||||
};
|
||||
|
||||
// =========== LocatorModel ===========
|
||||
@@ -226,6 +250,8 @@ CompletionList::CompletionList(QWidget *parent)
|
||||
const QStyleOptionViewItem &option = viewOptions();
|
||||
const QSize shint = itemDelegate()->sizeHint(option, QModelIndex());
|
||||
setFixedHeight(shint.height() * 17 + frameWidth() * 2);
|
||||
|
||||
installEventFilter(this);
|
||||
}
|
||||
|
||||
void CompletionList::setModel(QAbstractItemModel *newModel)
|
||||
@@ -241,20 +267,41 @@ void CompletionList::setModel(QAbstractItemModel *newModel)
|
||||
}
|
||||
}
|
||||
|
||||
void LocatorPopup::resize()
|
||||
void LocatorPopup::updateGeometry()
|
||||
{
|
||||
static const int MIN_WIDTH = 730;
|
||||
const QSize windowSize = m_window ? m_window->size() : QSize(MIN_WIDTH, 0);
|
||||
|
||||
const int width = qMax(MIN_WIDTH, windowSize.width() * 2 / 3);
|
||||
m_preferredSize = QSize(width, sizeHint().height());
|
||||
QWidget::resize(m_preferredSize);
|
||||
m_tree->resizeHeaders();
|
||||
}
|
||||
|
||||
QSize LocatorPopup::preferredSize() const
|
||||
void TopLeftLocatorPopup::updateGeometry()
|
||||
{
|
||||
return m_preferredSize;
|
||||
QTC_ASSERT(parentWidget(), return);
|
||||
const QSize size = preferredSize();
|
||||
const QRect rect(parentWidget()->mapToGlobal(QPoint(0, -size.height())), size);
|
||||
setGeometry(rect);
|
||||
LocatorPopup::updateGeometry();
|
||||
}
|
||||
|
||||
void CenteredLocatorPopup::updateGeometry()
|
||||
{
|
||||
QTC_ASSERT(parentWidget(), return);
|
||||
const QSize size = preferredSize();
|
||||
const QSize parentSize = parentWidget()->size();
|
||||
const QPoint pos = parentWidget()->mapToGlobal({(parentSize.width() - size.width()) / 2,
|
||||
parentSize.height() / 2 - size.height()});
|
||||
QRect rect(pos, size);
|
||||
// invisible widget doesn't have the right screen set yet, so use the parent widget to
|
||||
// check for available geometry
|
||||
const QRect available = QApplication::desktop()->availableGeometry(parentWidget());
|
||||
if (rect.right() > available.right())
|
||||
rect.moveRight(available.right());
|
||||
if (rect.bottom() > available.bottom())
|
||||
rect.moveBottom(available.bottom());
|
||||
if (rect.top() < available.top())
|
||||
rect.moveTop(available.top());
|
||||
if (rect.left() < available.left())
|
||||
rect.moveLeft(available.left());
|
||||
setGeometry(rect);
|
||||
LocatorPopup::updateGeometry();
|
||||
}
|
||||
|
||||
void LocatorPopup::updateWindow()
|
||||
@@ -271,25 +318,36 @@ void LocatorPopup::updateWindow()
|
||||
|
||||
bool LocatorPopup::event(QEvent *event)
|
||||
{
|
||||
if (event->type() == QEvent::ParentChange)
|
||||
if (event->type() == QEvent::ParentChange) {
|
||||
updateWindow();
|
||||
} else if (event->type() == QEvent::Show)
|
||||
updateGeometry();
|
||||
return QWidget::event(event);
|
||||
}
|
||||
|
||||
bool LocatorPopup::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == m_window && event->type() == QEvent::Resize)
|
||||
resize();
|
||||
updateGeometry();
|
||||
return QWidget::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void LocatorPopup::showPopup()
|
||||
QSize LocatorPopup::preferredSize()
|
||||
{
|
||||
static const int MIN_WIDTH = 730;
|
||||
const QSize windowSize = m_window ? m_window->size() : QSize(MIN_WIDTH, 0);
|
||||
|
||||
const int width = qMax(MIN_WIDTH, windowSize.width() * 2 / 3);
|
||||
return QSize(width, sizeHint().height());
|
||||
}
|
||||
|
||||
void TopLeftLocatorPopup::inputLostFocus()
|
||||
{
|
||||
hide();
|
||||
}
|
||||
|
||||
void LocatorPopup::inputLostFocus()
|
||||
{
|
||||
QTC_ASSERT(parentWidget(), return);
|
||||
const QSize size = preferredSize();
|
||||
const QRect rect(parentWidget()->mapToGlobal(QPoint(0, -size.height())), size);
|
||||
setGeometry(rect);
|
||||
show();
|
||||
}
|
||||
|
||||
void CompletionList::resizeHeaders()
|
||||
@@ -300,10 +358,9 @@ void CompletionList::resizeHeaders()
|
||||
|
||||
LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
|
||||
: QWidget(parent),
|
||||
m_tree(new CompletionList(this))
|
||||
m_tree(new CompletionList(this)),
|
||||
m_inputWidget(locatorWidget)
|
||||
{
|
||||
setWindowFlags(Qt::ToolTip);
|
||||
|
||||
m_tree->setFrameStyle(QFrame::NoFrame);
|
||||
m_tree->setModel(locatorWidget->model());
|
||||
|
||||
@@ -315,8 +372,9 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
|
||||
layout->addWidget(m_tree);
|
||||
|
||||
connect(locatorWidget, &LocatorWidget::parentChanged, this, &LocatorPopup::updateWindow);
|
||||
connect(locatorWidget, &LocatorWidget::showPopup, this, &LocatorPopup::showPopup);
|
||||
connect(locatorWidget, &LocatorWidget::hidePopup, this, &LocatorPopup::hide);
|
||||
connect(locatorWidget, &LocatorWidget::showPopup, this, &LocatorPopup::show);
|
||||
connect(locatorWidget, &LocatorWidget::hidePopup, this, &LocatorPopup::close);
|
||||
connect(locatorWidget, &LocatorWidget::lostFocus, this, &LocatorPopup::inputLostFocus);
|
||||
connect(locatorWidget, &LocatorWidget::selectRow, m_tree, [this](int row) {
|
||||
m_tree->setCurrentIndex(m_tree->model()->index(row, 0));
|
||||
});
|
||||
@@ -331,7 +389,7 @@ LocatorPopup::LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent)
|
||||
locatorWidget->scheduleAcceptEntry(index);
|
||||
});
|
||||
|
||||
resize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
CompletionList *LocatorPopup::completionList() const
|
||||
@@ -339,6 +397,11 @@ CompletionList *LocatorPopup::completionList() const
|
||||
return m_tree;
|
||||
}
|
||||
|
||||
LocatorWidget *LocatorPopup::inputWidget() const
|
||||
{
|
||||
return m_inputWidget;
|
||||
}
|
||||
|
||||
void LocatorPopup::focusOutEvent(QFocusEvent *event) {
|
||||
if (event->reason() == Qt::ActiveWindowFocusReason)
|
||||
hide();
|
||||
@@ -402,6 +465,22 @@ void CompletionList::keyPressEvent(QKeyEvent *event)
|
||||
Utils::TreeView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
bool CompletionList::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
if (watched == this && event->type() == QEvent::ShortcutOverride) {
|
||||
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
|
||||
switch (ke->key()) {
|
||||
case Qt::Key_Escape:
|
||||
if (!ke->modifiers()) {
|
||||
event->accept();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Utils::TreeView::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
// =========== LocatorWidget ===========
|
||||
|
||||
LocatorWidget::LocatorWidget(Locator *locator) :
|
||||
@@ -576,7 +655,7 @@ bool LocatorWidget::eventFilter(QObject *obj, QEvent *event)
|
||||
}
|
||||
}
|
||||
} else if (obj == m_fileLineEdit && event->type() == QEvent::FocusOut) {
|
||||
emit hidePopup();
|
||||
emit lostFocus();
|
||||
} else if (obj == m_fileLineEdit && event->type() == QEvent::FocusIn) {
|
||||
QFocusEvent *fev = static_cast<QFocusEvent *>(event);
|
||||
if (fev->reason() != Qt::ActiveWindowFocusReason)
|
||||
@@ -796,5 +875,23 @@ void LocatorWidget::addSearchResults(int firstIndex, int endIndex)
|
||||
}
|
||||
}
|
||||
|
||||
LocatorWidget *createStaticLocatorWidget(Locator *locator)
|
||||
{
|
||||
auto widget = new LocatorWidget(locator);
|
||||
auto popup = new TopLeftLocatorPopup(widget); // owned by widget
|
||||
popup->setWindowFlags(Qt::ToolTip);
|
||||
return widget;
|
||||
}
|
||||
|
||||
LocatorPopup *createLocatorPopup(Locator *locator, QWidget *parent)
|
||||
{
|
||||
auto widget = new LocatorWidget(locator);
|
||||
auto popup = new CenteredLocatorPopup(widget, parent);
|
||||
popup->layout()->addWidget(widget);
|
||||
popup->setWindowFlags(Qt::Popup);
|
||||
popup->setAttribute(Qt::WA_DeleteOnClose);
|
||||
return popup;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
|
@@ -64,6 +64,7 @@ public:
|
||||
|
||||
signals:
|
||||
void showCurrentItemToolTip();
|
||||
void lostFocus();
|
||||
void hidePopup();
|
||||
void selectRow(int row);
|
||||
void handleKey(QKeyEvent *keyEvent); // only use with DirectConnection, event is deleted
|
||||
@@ -109,21 +110,28 @@ public:
|
||||
LocatorPopup(LocatorWidget *locatorWidget, QWidget *parent = 0);
|
||||
|
||||
CompletionList *completionList() const;
|
||||
LocatorWidget *inputWidget() const;
|
||||
|
||||
void focusOutEvent (QFocusEvent *event) override;
|
||||
void resize();
|
||||
QSize preferredSize() const;
|
||||
bool event(QEvent *event) override;
|
||||
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||
|
||||
protected:
|
||||
QSize preferredSize();
|
||||
virtual void updateGeometry();
|
||||
virtual void inputLostFocus();
|
||||
|
||||
QPointer<QWidget> m_window;
|
||||
|
||||
private:
|
||||
void showPopup();
|
||||
void updateWindow();
|
||||
|
||||
CompletionList *m_tree;
|
||||
QSize m_preferredSize;
|
||||
QPointer<QWidget> m_window;
|
||||
void updateWindow();
|
||||
LocatorWidget *m_inputWidget;
|
||||
};
|
||||
|
||||
LocatorWidget *createStaticLocatorWidget(Locator *locator);
|
||||
LocatorPopup *createLocatorPopup(Locator *locator, QWidget *parent);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
|
@@ -475,7 +475,7 @@ void GdbEngine::handleResponse(const QString &buff)
|
||||
|
||||
case '@': {
|
||||
QString data = GdbMi::parseCString(from, to);
|
||||
QString msg = data.mid(2, data.size() - 4);
|
||||
QString msg = data.left(data.size() - 1);
|
||||
showMessage(msg, AppOutput);
|
||||
break;
|
||||
}
|
||||
|
@@ -726,20 +726,10 @@ RunControl::~RunControl()
|
||||
void RunControl::initiateStart()
|
||||
{
|
||||
emit aboutToStart();
|
||||
start();
|
||||
}
|
||||
|
||||
void RunControl::start()
|
||||
{
|
||||
d->initiateStart();
|
||||
}
|
||||
|
||||
void RunControl::initiateStop()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void RunControl::stop()
|
||||
{
|
||||
d->initiateStop();
|
||||
}
|
||||
|
@@ -429,8 +429,8 @@ public:
|
||||
RunControl(RunConfiguration *runConfiguration, Core::Id mode);
|
||||
~RunControl() override;
|
||||
|
||||
void initiateStart(); // Calls start() asynchronously.
|
||||
void initiateStop(); // Calls stop() asynchronously.
|
||||
void initiateStart();
|
||||
void initiateStop();
|
||||
|
||||
bool promptToStop(bool *optionalPrompt = nullptr) const;
|
||||
void setPromptToStop(const std::function<bool(bool *)> &promptToStop);
|
||||
@@ -478,9 +478,6 @@ public:
|
||||
const QString &cancelButtonText = QString(),
|
||||
bool *prompt = nullptr);
|
||||
|
||||
virtual void start();
|
||||
virtual void stop();
|
||||
|
||||
using WorkerCreator = std::function<RunWorker *(RunControl *)>;
|
||||
static void registerWorkerCreator(Core::Id id, const WorkerCreator &workerCreator);
|
||||
RunWorker *workerById(Core::Id id) const;
|
||||
|
@@ -102,7 +102,7 @@ QmlProfilerRunner::QmlProfilerRunner(RunControl *runControl)
|
||||
QmlProfilerRunner::~QmlProfilerRunner()
|
||||
{
|
||||
if (runControl()->isRunning() && d->m_profilerState)
|
||||
runControl()->stop();
|
||||
runControl()->initiateStop();
|
||||
delete d;
|
||||
}
|
||||
|
||||
|
@@ -342,10 +342,10 @@ void QmlProfilerTool::finalizeRunControl(QmlProfilerRunner *runWorker)
|
||||
connect(runControl, &RunControl::finished, this, [this, runControl] {
|
||||
d->m_toolBusy = false;
|
||||
updateRunActions();
|
||||
disconnect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::stop);
|
||||
disconnect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop);
|
||||
});
|
||||
|
||||
connect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::stop);
|
||||
connect(d->m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop);
|
||||
|
||||
updateRunActions();
|
||||
runWorker->registerProfilerStateManager(d->m_profilerState);
|
||||
|
@@ -26,6 +26,7 @@
|
||||
#include "basehoverhandler.h"
|
||||
#include "texteditor.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
|
||||
namespace TextEditor {
|
||||
@@ -33,6 +34,11 @@ namespace TextEditor {
|
||||
BaseHoverHandler::~BaseHoverHandler()
|
||||
{}
|
||||
|
||||
bool BaseHoverHandler::isAsyncHandler() const
|
||||
{
|
||||
return m_isAsyncHandler;
|
||||
}
|
||||
|
||||
void BaseHoverHandler::showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate)
|
||||
{
|
||||
if (decorate)
|
||||
@@ -40,13 +46,18 @@ void BaseHoverHandler::showToolTip(TextEditorWidget *widget, const QPoint &point
|
||||
operateTooltip(widget, point);
|
||||
}
|
||||
|
||||
int BaseHoverHandler::checkToolTip(TextEditorWidget *widget, int pos)
|
||||
void BaseHoverHandler::checkPriority(TextEditorWidget *widget,
|
||||
int pos,
|
||||
ReportPriority report)
|
||||
{
|
||||
widget->setContextHelpId(QString());
|
||||
|
||||
process(widget, pos);
|
||||
process(widget, pos, report);
|
||||
}
|
||||
|
||||
return priority();
|
||||
void BaseHoverHandler::cancelAsyncCheck()
|
||||
{
|
||||
QTC_CHECK(false && "BaseHoverHandler: Implement cancelCheck() in derived class!");
|
||||
}
|
||||
|
||||
int BaseHoverHandler::priority() const
|
||||
@@ -73,7 +84,7 @@ QString BaseHoverHandler::contextHelpId(TextEditorWidget *widget, int pos)
|
||||
// If the tooltip is visible and there is a help match, this match is used to update
|
||||
// the help id. Otherwise, let the identification process happen.
|
||||
if (!Utils::ToolTip::isVisible() || !lastHelpItemIdentified().isValid())
|
||||
process(widget, pos);
|
||||
process(widget, pos, ReportPriority()); // TODO
|
||||
|
||||
if (lastHelpItemIdentified().isValid())
|
||||
return lastHelpItemIdentified().helpId();
|
||||
@@ -100,13 +111,23 @@ const HelpItem &BaseHoverHandler::lastHelpItemIdentified() const
|
||||
return m_lastHelpItemIdentified;
|
||||
}
|
||||
|
||||
void BaseHoverHandler::process(TextEditorWidget *widget, int pos)
|
||||
void BaseHoverHandler::process(TextEditorWidget *widget, int pos, ReportPriority report)
|
||||
{
|
||||
m_toolTip.clear();
|
||||
m_priority = -1;
|
||||
m_lastHelpItemIdentified = HelpItem();
|
||||
|
||||
identifyMatch(widget, pos);
|
||||
if (m_isAsyncHandler) {
|
||||
identifyMatchAsync(widget, pos, report);
|
||||
} else {
|
||||
identifyMatch(widget, pos);
|
||||
report(priority());
|
||||
}
|
||||
}
|
||||
|
||||
void BaseHoverHandler::setIsAsyncHandler(bool isAsyncHandler)
|
||||
{
|
||||
m_isAsyncHandler = isAsyncHandler;
|
||||
}
|
||||
|
||||
void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
|
||||
@@ -116,6 +137,11 @@ void BaseHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
|
||||
setToolTip(tooltip);
|
||||
}
|
||||
|
||||
void BaseHoverHandler::identifyMatchAsync(TextEditorWidget *, int, BaseHoverHandler::ReportPriority)
|
||||
{
|
||||
QTC_CHECK(false && "BaseHoverHandler: Implement identifyMatchAsync() in derived class!");
|
||||
}
|
||||
|
||||
void BaseHoverHandler::decorateToolTip()
|
||||
{
|
||||
if (Qt::mightBeRichText(toolTip()))
|
||||
|
@@ -28,6 +28,8 @@
|
||||
#include "texteditor_global.h"
|
||||
#include "helpitem.h"
|
||||
|
||||
#include <functional>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QPoint;
|
||||
QT_END_NAMESPACE
|
||||
@@ -41,9 +43,15 @@ class TEXTEDITOR_EXPORT BaseHoverHandler
|
||||
public:
|
||||
virtual ~BaseHoverHandler();
|
||||
|
||||
bool isAsyncHandler() const;
|
||||
void setIsAsyncHandler(bool isAsyncHandler);
|
||||
|
||||
QString contextHelpId(TextEditorWidget *widget, int pos);
|
||||
|
||||
int checkToolTip(TextEditorWidget *widget, int pos);
|
||||
using ReportPriority = std::function<void(int priority)>;
|
||||
void checkPriority(TextEditorWidget *widget, int pos, ReportPriority report);
|
||||
virtual void cancelAsyncCheck();
|
||||
|
||||
void showToolTip(TextEditorWidget *widget, const QPoint &point, bool decorate = true);
|
||||
|
||||
protected:
|
||||
@@ -63,11 +71,14 @@ protected:
|
||||
const HelpItem &lastHelpItemIdentified() const;
|
||||
|
||||
virtual void identifyMatch(TextEditorWidget *editorWidget, int pos);
|
||||
virtual void identifyMatchAsync(TextEditorWidget *editorWidget, int pos, ReportPriority report);
|
||||
virtual void decorateToolTip();
|
||||
virtual void operateTooltip(TextEditorWidget *editorWidget, const QPoint &point);
|
||||
|
||||
private:
|
||||
void process(TextEditorWidget *widget, int pos);
|
||||
void process(TextEditorWidget *widget, int pos, ReportPriority report);
|
||||
|
||||
bool m_isAsyncHandler = false;
|
||||
|
||||
QString m_toolTip;
|
||||
HelpItem m_lastHelpItemIdentified;
|
||||
|
@@ -248,6 +248,119 @@ public:
|
||||
TextEditorFactoryPrivate *m_origin;
|
||||
};
|
||||
|
||||
class HoverHandlerRunner
|
||||
{
|
||||
public:
|
||||
HoverHandlerRunner(TextEditorWidget *widget, QList<BaseHoverHandler *> &handlers)
|
||||
: m_widget(widget)
|
||||
, m_handlers(handlers)
|
||||
{
|
||||
}
|
||||
|
||||
void startChecking(const QTextCursor &textCursor, const QPoint &point)
|
||||
{
|
||||
if (m_handlers.empty())
|
||||
return;
|
||||
|
||||
// Does the last handler still applies?
|
||||
const int documentRevision = textCursor.document()->revision();
|
||||
const int position = Convenience::wordStartCursor(textCursor).position();
|
||||
if (m_lastHandlerInfo.applies(documentRevision, position)) {
|
||||
m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancel currently running checks
|
||||
for (BaseHoverHandler *handler : m_handlers) {
|
||||
if (handler->isAsyncHandler())
|
||||
handler->cancelAsyncCheck();
|
||||
}
|
||||
|
||||
// Update invocation data
|
||||
m_documentRevision = documentRevision;
|
||||
m_position = position;
|
||||
m_point = point;
|
||||
|
||||
// Re-initialize process data
|
||||
m_currentHandlerIndex = 0;
|
||||
m_bestHandler = nullptr;
|
||||
m_highestHandlerPriority = -1;
|
||||
|
||||
// Start checking
|
||||
checkNext();
|
||||
}
|
||||
|
||||
void checkNext()
|
||||
{
|
||||
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
||||
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
||||
|
||||
currentHandler->checkPriority(m_widget, m_position, [this](int priority) {
|
||||
onHandlerFinished(m_documentRevision, m_position, priority);
|
||||
});
|
||||
}
|
||||
|
||||
void onHandlerFinished(int documentRevision, int position, int priority)
|
||||
{
|
||||
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
||||
QTC_ASSERT(documentRevision == m_documentRevision, return);
|
||||
QTC_ASSERT(position == m_position, return);
|
||||
|
||||
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
||||
if (priority > m_highestHandlerPriority) {
|
||||
m_highestHandlerPriority = priority;
|
||||
m_bestHandler = currentHandler;
|
||||
}
|
||||
|
||||
// There are more, check next
|
||||
++m_currentHandlerIndex;
|
||||
if (m_currentHandlerIndex < m_handlers.size()) {
|
||||
checkNext();
|
||||
return;
|
||||
}
|
||||
|
||||
// All were queried, run the best
|
||||
if (m_bestHandler) {
|
||||
m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
|
||||
m_bestHandler->showToolTip(m_widget, m_point);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TextEditorWidget *m_widget = nullptr;
|
||||
const QList<BaseHoverHandler *> &m_handlers;
|
||||
|
||||
struct LastHandlerInfo {
|
||||
LastHandlerInfo() = default;
|
||||
LastHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
|
||||
: handler(handler)
|
||||
, documentRevision(documentRevision)
|
||||
, cursorPosition(cursorPosition)
|
||||
{}
|
||||
|
||||
bool applies(int documentRevision, int cursorPosition) const
|
||||
{
|
||||
return handler
|
||||
&& documentRevision == this->documentRevision
|
||||
&& cursorPosition == this->cursorPosition;
|
||||
}
|
||||
|
||||
BaseHoverHandler *handler = nullptr;
|
||||
int documentRevision = -1;
|
||||
int cursorPosition = -1;
|
||||
} m_lastHandlerInfo;
|
||||
|
||||
// invocation data
|
||||
QPoint m_point;
|
||||
int m_position = -1;
|
||||
int m_documentRevision = -1;
|
||||
|
||||
// processing data
|
||||
int m_currentHandlerIndex = -1;
|
||||
int m_highestHandlerPriority = -1;
|
||||
BaseHoverHandler *m_bestHandler = nullptr;
|
||||
};
|
||||
|
||||
class TextEditorWidgetPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
@@ -469,26 +582,8 @@ public:
|
||||
CodeAssistant m_codeAssistant;
|
||||
bool m_assistRelevantContentAdded = false;
|
||||
|
||||
struct LastHoverHandlerInfo {
|
||||
LastHoverHandlerInfo() = default;
|
||||
LastHoverHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
|
||||
: handler(handler)
|
||||
, documentRevision(documentRevision)
|
||||
, cursorPosition(cursorPosition)
|
||||
{}
|
||||
|
||||
bool applies(int documentRevision, int cursorPosition) const
|
||||
{
|
||||
return handler
|
||||
&& documentRevision == this->documentRevision
|
||||
&& cursorPosition == this->cursorPosition;
|
||||
}
|
||||
|
||||
BaseHoverHandler *handler = nullptr;
|
||||
int documentRevision = -1;
|
||||
int cursorPosition = -1;
|
||||
} m_lastHoverHandlerInfo;
|
||||
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
||||
HoverHandlerRunner m_hoverHandlerRunner;
|
||||
|
||||
QPointer<QSequentialAnimationGroup> m_navigationAnimation;
|
||||
|
||||
@@ -535,6 +630,7 @@ TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
|
||||
m_requestMarkEnabled(true),
|
||||
m_lineSeparatorsAllowed(false),
|
||||
m_maybeFakeTooltipEvent(false),
|
||||
m_hoverHandlerRunner(parent, m_hoverHandlers),
|
||||
m_clipboardAssistProvider(new ClipboardAssistProvider),
|
||||
m_autoCompleter(new AutoCompleter)
|
||||
{
|
||||
@@ -978,7 +1074,7 @@ int TextEditorWidgetPrivate::visualIndent(const QTextBlock &block) const
|
||||
void TextEditorWidgetPrivate::updateAutoCompleteHighlight()
|
||||
{
|
||||
const QTextCharFormat &matchFormat
|
||||
= q->textDocument()->fontSettings().toTextCharFormat(C_PARENTHESES);
|
||||
= q->textDocument()->fontSettings().toTextCharFormat(C_AUTOCOMPLETE);
|
||||
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
for (QTextCursor cursor : Utils::asConst(m_autoCompleteHighlightPos)) {
|
||||
@@ -3181,30 +3277,7 @@ void TextEditorWidgetPrivate::processTooltipRequest(const QTextCursor &c)
|
||||
return;
|
||||
}
|
||||
|
||||
// Does the last handler still applies?
|
||||
const int documentRevision = m_document->document()->revision();
|
||||
const int cursorPosition = Convenience::wordStartCursor(c).position();
|
||||
if (m_lastHoverHandlerInfo.applies(documentRevision, cursorPosition)) {
|
||||
m_lastHoverHandlerInfo.handler->showToolTip(q, toolTipPoint, /*decorate=*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine best handler
|
||||
int highestPriority = -1;
|
||||
BaseHoverHandler *highest = 0;
|
||||
foreach (BaseHoverHandler *handler, m_hoverHandlers) {
|
||||
int priority = handler->checkToolTip(q, c.position());
|
||||
if (priority > highestPriority) {
|
||||
highestPriority = priority;
|
||||
highest = handler;
|
||||
}
|
||||
}
|
||||
|
||||
// Let the best handler show the tooltip
|
||||
if (highest) {
|
||||
m_lastHoverHandlerInfo = LastHoverHandlerInfo{highest, documentRevision, cursorPosition};
|
||||
highest->showToolTip(q, toolTipPoint);
|
||||
}
|
||||
m_hoverHandlerRunner.startChecking(c, toolTipPoint);
|
||||
}
|
||||
|
||||
bool TextEditorWidgetPrivate::processAnnotaionTooltipRequest(const QTextBlock &block,
|
||||
|
@@ -752,7 +752,7 @@ ValgrindToolRunner *CallgrindTool::createRunTool(RunControl *runControl)
|
||||
connect(this, &CallgrindTool::resetRequested, toolRunner, &CallgrindToolRunner::reset);
|
||||
connect(this, &CallgrindTool::pauseToggled, toolRunner, &CallgrindToolRunner::setPaused);
|
||||
|
||||
connect(m_stopAction, &QAction::triggered, toolRunner, [runControl] { runControl->stop(); });
|
||||
connect(m_stopAction, &QAction::triggered, toolRunner, [runControl] { runControl->initiateStop(); });
|
||||
|
||||
// initialize run control
|
||||
toolRunner->setPaused(m_pauseAction->isChecked());
|
||||
|
@@ -567,7 +567,7 @@ RunWorker *MemcheckTool::createRunWorker(RunControl *runControl)
|
||||
connect(runTool, &MemcheckToolRunner::internalParserError, this, &MemcheckTool::internalParserError);
|
||||
connect(runTool, &MemcheckToolRunner::stopped, this, &MemcheckTool::engineFinished);
|
||||
|
||||
connect(m_stopAction, &QAction::triggered, runControl, &RunControl::stop);
|
||||
connect(m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop);
|
||||
|
||||
m_toolBusy = true;
|
||||
updateRunActions();
|
||||
|
Reference in New Issue
Block a user