2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2011-02-10 20:58:17 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2011-02-10 20:58:17 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2011-02-10 20:58:17 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** 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
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2011-02-10 20:58:17 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2011-02-10 20:58:17 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2011-02-10 20:58:17 +01:00
|
|
|
|
|
|
|
|
#include "variablechooser.h"
|
|
|
|
|
|
2020-09-18 12:11:40 +02:00
|
|
|
#include "fancylineedit.h"
|
|
|
|
|
#include "headerviewstretcher.h" // IconButton
|
|
|
|
|
#include "macroexpander.h"
|
|
|
|
|
#include "treemodel.h"
|
|
|
|
|
#include "qtcassert.h"
|
|
|
|
|
#include "utilsicons.h"
|
2011-08-17 12:54:58 +02:00
|
|
|
|
2014-06-20 00:34:41 +02:00
|
|
|
#include <QApplication>
|
2014-10-13 18:49:44 +02:00
|
|
|
#include <QAbstractItemModel>
|
|
|
|
|
#include <QHeaderView>
|
2014-06-20 00:34:41 +02:00
|
|
|
#include <QLabel>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QLineEdit>
|
|
|
|
|
#include <QListWidgetItem>
|
2014-10-20 23:13:13 +02:00
|
|
|
#include <QMenu>
|
2014-06-20 00:34:41 +02:00
|
|
|
#include <QPlainTextEdit>
|
|
|
|
|
#include <QPointer>
|
2020-08-11 20:35:58 +02:00
|
|
|
#include <QScrollBar>
|
2016-10-25 15:37:40 +02:00
|
|
|
#include <QSortFilterProxyModel>
|
2014-06-20 00:34:41 +02:00
|
|
|
#include <QTextEdit>
|
|
|
|
|
#include <QTimer>
|
2014-10-13 18:49:44 +02:00
|
|
|
#include <QTreeView>
|
2014-06-20 00:34:41 +02:00
|
|
|
#include <QVBoxLayout>
|
2014-10-13 18:49:44 +02:00
|
|
|
#include <QVector>
|
|
|
|
|
|
2020-09-18 12:11:40 +02:00
|
|
|
namespace Utils {
|
2014-06-20 00:34:41 +02:00
|
|
|
namespace Internal {
|
|
|
|
|
|
2014-10-20 23:13:13 +02:00
|
|
|
enum {
|
|
|
|
|
UnexpandedTextRole = Qt::UserRole,
|
|
|
|
|
ExpandedTextRole
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class VariableTreeView : public QTreeView
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
VariableTreeView(QWidget *parent, VariableChooserPrivate *target)
|
|
|
|
|
: QTreeView(parent), m_target(target)
|
|
|
|
|
{
|
|
|
|
|
setAttribute(Qt::WA_MacSmallSize);
|
|
|
|
|
setAttribute(Qt::WA_MacShowFocusRect, false);
|
|
|
|
|
setIndentation(indentation() * 7/10);
|
|
|
|
|
header()->hide();
|
2015-02-03 23:48:19 +02:00
|
|
|
new HeaderViewStretcher(header(), 0);
|
2014-10-20 23:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
void contextMenuEvent(QContextMenuEvent *ev) override;
|
2014-10-20 23:13:13 +02:00
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous) override;
|
2015-06-16 10:45:17 +03:00
|
|
|
|
2014-10-20 23:13:13 +02:00
|
|
|
private:
|
|
|
|
|
VariableChooserPrivate *m_target;
|
|
|
|
|
};
|
|
|
|
|
|
2017-02-13 21:46:27 +02:00
|
|
|
class VariableSortFilterProxyModel : public QSortFilterProxyModel
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
explicit VariableSortFilterProxyModel(QObject *parent) : QSortFilterProxyModel(parent) {}
|
|
|
|
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
|
|
|
|
|
{
|
|
|
|
|
const QModelIndex index = sourceModel()->index(sourceRow, filterKeyColumn(), sourceParent);
|
|
|
|
|
if (!index.isValid())
|
|
|
|
|
return false;
|
|
|
|
|
|
2020-03-27 11:56:25 +01:00
|
|
|
const QRegularExpression regexp = filterRegularExpression();
|
|
|
|
|
if (regexp.pattern().isEmpty() || sourceModel()->rowCount(index) > 0)
|
2017-02-13 21:46:27 +02:00
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
const QString displayText = index.data(Qt::DisplayRole).toString();
|
|
|
|
|
return displayText.contains(regexp);
|
|
|
|
|
}
|
|
|
|
|
};
|
2014-10-20 23:13:13 +02:00
|
|
|
|
2014-06-20 00:34:41 +02:00
|
|
|
class VariableChooserPrivate : public QObject
|
|
|
|
|
{
|
|
|
|
|
public:
|
2014-10-13 18:49:44 +02:00
|
|
|
VariableChooserPrivate(VariableChooser *parent);
|
2014-06-20 00:34:41 +02:00
|
|
|
|
|
|
|
|
void createIconButton()
|
|
|
|
|
{
|
2015-02-03 23:48:19 +02:00
|
|
|
m_iconButton = new IconButton;
|
2017-09-05 12:59:31 +02:00
|
|
|
m_iconButton->setIcon(Utils::Icons::REPLACE.icon());
|
2015-07-02 11:41:25 +02:00
|
|
|
m_iconButton->setToolTip(VariableChooser::tr("Insert Variable"));
|
2014-06-20 00:34:41 +02:00
|
|
|
m_iconButton->hide();
|
2014-10-13 18:49:44 +02:00
|
|
|
connect(m_iconButton.data(), static_cast<void(QAbstractButton::*)(bool)>(&QAbstractButton::clicked),
|
|
|
|
|
this, &VariableChooserPrivate::updatePositionAndShow);
|
2014-06-20 00:34:41 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
void updateDescription(const QModelIndex &index);
|
2014-06-20 00:34:41 +02:00
|
|
|
void updateCurrentEditor(QWidget *old, QWidget *widget);
|
2014-10-13 18:49:44 +02:00
|
|
|
void handleItemActivated(const QModelIndex &index);
|
2014-10-20 23:13:13 +02:00
|
|
|
void insertText(const QString &variable);
|
2014-10-13 18:49:44 +02:00
|
|
|
void updatePositionAndShow(bool);
|
2017-02-13 21:46:27 +02:00
|
|
|
void updateFilter(const QString &filterText);
|
2014-06-20 00:34:41 +02:00
|
|
|
|
2020-11-21 01:04:56 +01:00
|
|
|
QWidget *currentWidget() const;
|
2014-06-20 00:34:41 +02:00
|
|
|
|
2014-10-22 13:13:22 +02:00
|
|
|
int buttonMargin() const;
|
|
|
|
|
void updateButtonGeometry();
|
|
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
public:
|
2014-06-20 00:34:41 +02:00
|
|
|
VariableChooser *q;
|
2016-06-24 09:36:42 +02:00
|
|
|
TreeModel<> m_model;
|
2014-10-13 18:49:44 +02:00
|
|
|
|
2014-06-20 00:34:41 +02:00
|
|
|
QPointer<QLineEdit> m_lineEdit;
|
|
|
|
|
QPointer<QTextEdit> m_textEdit;
|
|
|
|
|
QPointer<QPlainTextEdit> m_plainTextEdit;
|
2015-02-03 23:48:19 +02:00
|
|
|
QPointer<IconButton> m_iconButton;
|
2014-06-20 00:34:41 +02:00
|
|
|
|
2017-02-15 12:29:57 +01:00
|
|
|
Utils::FancyLineEdit *m_variableFilter;
|
2014-10-20 23:13:13 +02:00
|
|
|
VariableTreeView *m_variableTree;
|
2014-06-20 00:34:41 +02:00
|
|
|
QLabel *m_variableDescription;
|
2016-11-08 14:34:01 +01:00
|
|
|
QSortFilterProxyModel *m_sortModel;
|
2014-10-13 18:49:44 +02:00
|
|
|
QString m_defaultDescription;
|
|
|
|
|
QByteArray m_currentVariableName; // Prevent recursive insertion of currently expanded item
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
class VariableGroupItem : public TreeItem
|
|
|
|
|
{
|
|
|
|
|
public:
|
2018-07-21 21:11:46 +02:00
|
|
|
VariableGroupItem() = default;
|
2014-10-13 18:49:44 +02:00
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
QVariant data(int column, int role) const override
|
2014-10-13 18:49:44 +02:00
|
|
|
{
|
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
2014-11-27 17:45:49 +01:00
|
|
|
if (column == 0)
|
|
|
|
|
if (MacroExpander *expander = m_provider())
|
|
|
|
|
return expander->displayName();
|
2014-10-13 18:49:44 +02:00
|
|
|
}
|
2014-10-20 23:13:13 +02:00
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
bool canFetchMore() const override
|
2015-04-22 15:56:03 +02:00
|
|
|
{
|
|
|
|
|
return !m_populated;
|
|
|
|
|
}
|
|
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
void fetchMore() override
|
2014-10-13 18:49:44 +02:00
|
|
|
{
|
2014-11-27 17:45:49 +01:00
|
|
|
if (MacroExpander *expander = m_provider())
|
|
|
|
|
populateGroup(expander);
|
2015-04-22 15:56:03 +02:00
|
|
|
m_populated = true;
|
2014-10-13 18:49:44 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-20 23:13:13 +02:00
|
|
|
void populateGroup(MacroExpander *expander);
|
|
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
public:
|
2018-07-21 21:11:46 +02:00
|
|
|
VariableChooserPrivate *m_chooser = nullptr; // Not owned.
|
|
|
|
|
bool m_populated = false;
|
2014-10-13 18:49:44 +02:00
|
|
|
MacroExpanderProvider m_provider;
|
|
|
|
|
};
|
|
|
|
|
|
2016-07-27 17:24:34 +02:00
|
|
|
class VariableItem : public TypedTreeItem<TreeItem, VariableGroupItem>
|
2014-10-20 23:13:13 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2018-07-21 21:11:46 +02:00
|
|
|
VariableItem() = default;
|
2014-10-20 23:13:13 +02:00
|
|
|
|
2016-07-27 17:24:34 +02:00
|
|
|
Qt::ItemFlags flags(int) const override
|
|
|
|
|
{
|
|
|
|
|
if (m_variable == parent()->m_chooser->m_currentVariableName)
|
|
|
|
|
return Qt::ItemIsSelectable;
|
|
|
|
|
return Qt::ItemIsSelectable|Qt::ItemIsEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant data(int column, int role) const override
|
2014-10-20 23:13:13 +02:00
|
|
|
{
|
|
|
|
|
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
|
|
|
|
if (column == 0)
|
|
|
|
|
return m_variable;
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-06 17:04:12 +03:00
|
|
|
if (role == Qt::ToolTipRole) {
|
2016-07-27 17:24:34 +02:00
|
|
|
QString description = m_expander->variableDescription(m_variable);
|
2018-06-04 15:09:38 +02:00
|
|
|
const QString value = m_expander->value(m_variable).toHtmlEscaped();
|
2015-07-06 17:04:12 +03:00
|
|
|
if (!value.isEmpty())
|
|
|
|
|
description += QLatin1String("<p>") + VariableChooser::tr("Current Value: %1").arg(value);
|
|
|
|
|
return description;
|
|
|
|
|
}
|
2014-10-20 23:13:13 +02:00
|
|
|
|
|
|
|
|
if (role == UnexpandedTextRole)
|
2016-07-27 17:24:34 +02:00
|
|
|
return QString::fromUtf8("%{" + m_variable + '}');
|
2014-10-20 23:13:13 +02:00
|
|
|
|
|
|
|
|
if (role == ExpandedTextRole)
|
2016-07-27 17:24:34 +02:00
|
|
|
return m_expander->expand(QString::fromUtf8("%{" + m_variable + '}'));
|
2014-10-20 23:13:13 +02:00
|
|
|
|
|
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
MacroExpander *m_expander;
|
2016-07-27 17:24:34 +02:00
|
|
|
QByteArray m_variable;
|
2014-10-20 23:13:13 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void VariableTreeView::contextMenuEvent(QContextMenuEvent *ev)
|
|
|
|
|
{
|
|
|
|
|
const QModelIndex index = indexAt(ev->pos());
|
|
|
|
|
|
|
|
|
|
QString unexpandedText = index.data(UnexpandedTextRole).toString();
|
|
|
|
|
QString expandedText = index.data(ExpandedTextRole).toString();
|
|
|
|
|
|
|
|
|
|
QMenu menu;
|
2018-07-21 21:11:46 +02:00
|
|
|
QAction *insertUnexpandedAction = nullptr;
|
|
|
|
|
QAction *insertExpandedAction = nullptr;
|
2014-10-20 23:13:13 +02:00
|
|
|
|
|
|
|
|
if (unexpandedText.isEmpty()) {
|
2015-07-02 11:41:25 +02:00
|
|
|
insertUnexpandedAction = menu.addAction(VariableChooser::tr("Insert Unexpanded Value"));
|
2014-10-20 23:13:13 +02:00
|
|
|
insertUnexpandedAction->setEnabled(false);
|
|
|
|
|
} else {
|
2015-03-23 12:41:06 +03:00
|
|
|
insertUnexpandedAction = menu.addAction(VariableChooser::tr("Insert \"%1\"").arg(unexpandedText));
|
2014-10-20 23:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (expandedText.isEmpty()) {
|
2015-07-02 11:41:25 +02:00
|
|
|
insertExpandedAction = menu.addAction(VariableChooser::tr("Insert Expanded Value"));
|
2014-10-20 23:13:13 +02:00
|
|
|
insertExpandedAction->setEnabled(false);
|
|
|
|
|
} else {
|
2015-03-23 12:41:06 +03:00
|
|
|
insertExpandedAction = menu.addAction(VariableChooser::tr("Insert \"%1\"").arg(expandedText));
|
2014-10-20 23:13:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QAction *act = menu.exec(ev->globalPos());
|
|
|
|
|
|
|
|
|
|
if (act == insertUnexpandedAction)
|
|
|
|
|
m_target->insertText(unexpandedText);
|
|
|
|
|
else if (act == insertExpandedAction)
|
|
|
|
|
m_target->insertText(expandedText);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-16 10:45:17 +03:00
|
|
|
void VariableTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
|
|
|
|
{
|
|
|
|
|
m_target->updateDescription(current);
|
2015-07-06 18:03:34 +03:00
|
|
|
QTreeView::currentChanged(current, previous);
|
2015-06-16 10:45:17 +03:00
|
|
|
}
|
|
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
VariableChooserPrivate::VariableChooserPrivate(VariableChooser *parent)
|
|
|
|
|
: q(parent),
|
2018-07-21 21:11:46 +02:00
|
|
|
m_lineEdit(nullptr),
|
|
|
|
|
m_textEdit(nullptr),
|
|
|
|
|
m_plainTextEdit(nullptr),
|
|
|
|
|
m_iconButton(nullptr),
|
|
|
|
|
m_variableFilter(nullptr),
|
|
|
|
|
m_variableTree(nullptr),
|
|
|
|
|
m_variableDescription(nullptr)
|
2014-10-13 18:49:44 +02:00
|
|
|
{
|
|
|
|
|
m_defaultDescription = VariableChooser::tr("Select a variable to insert.");
|
|
|
|
|
|
2017-02-15 12:29:57 +01:00
|
|
|
m_variableFilter = new Utils::FancyLineEdit(q);
|
2014-10-20 23:13:13 +02:00
|
|
|
m_variableTree = new VariableTreeView(q, this);
|
2014-10-13 18:49:44 +02:00
|
|
|
m_variableDescription = new QLabel(q);
|
2016-03-29 18:37:50 +02:00
|
|
|
|
2017-02-15 12:29:57 +01:00
|
|
|
m_variableFilter->setFiltering(true);
|
2017-02-13 21:46:27 +02:00
|
|
|
|
|
|
|
|
m_sortModel = new VariableSortFilterProxyModel(this);
|
2016-11-08 14:34:01 +01:00
|
|
|
m_sortModel->setSourceModel(&m_model);
|
|
|
|
|
m_sortModel->sort(0);
|
2017-02-13 21:46:27 +02:00
|
|
|
m_sortModel->setFilterKeyColumn(0);
|
|
|
|
|
m_sortModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
2016-11-08 14:34:01 +01:00
|
|
|
m_variableTree->setModel(m_sortModel);
|
2017-02-13 21:46:27 +02:00
|
|
|
|
2014-10-13 18:49:44 +02:00
|
|
|
m_variableDescription->setText(m_defaultDescription);
|
|
|
|
|
m_variableDescription->setMinimumSize(QSize(0, 60));
|
|
|
|
|
m_variableDescription->setAlignment(Qt::AlignLeft|Qt::AlignTop);
|
|
|
|
|
m_variableDescription->setWordWrap(true);
|
|
|
|
|
m_variableDescription->setAttribute(Qt::WA_MacSmallSize);
|
2015-07-07 11:35:47 +02:00
|
|
|
m_variableDescription->setTextInteractionFlags(Qt::TextBrowserInteraction);
|
2014-10-13 18:49:44 +02:00
|
|
|
|
2018-07-21 21:11:46 +02:00
|
|
|
auto verticalLayout = new QVBoxLayout(q);
|
2014-10-13 18:49:44 +02:00
|
|
|
verticalLayout->setContentsMargins(3, 3, 3, 12);
|
2017-02-13 21:46:27 +02:00
|
|
|
verticalLayout->addWidget(m_variableFilter);
|
2014-10-13 18:49:44 +02:00
|
|
|
verticalLayout->addWidget(m_variableTree);
|
|
|
|
|
verticalLayout->addWidget(m_variableDescription);
|
|
|
|
|
|
2017-02-13 21:46:27 +02:00
|
|
|
connect(m_variableFilter, &QLineEdit::textChanged,
|
|
|
|
|
this, &VariableChooserPrivate::updateFilter);
|
2014-10-13 18:49:44 +02:00
|
|
|
connect(m_variableTree, &QTreeView::activated,
|
|
|
|
|
this, &VariableChooserPrivate::handleItemActivated);
|
|
|
|
|
connect(qobject_cast<QApplication *>(qApp), &QApplication::focusChanged,
|
|
|
|
|
this, &VariableChooserPrivate::updateCurrentEditor);
|
2018-07-21 21:11:46 +02:00
|
|
|
updateCurrentEditor(nullptr, QApplication::focusWidget());
|
2014-10-13 18:49:44 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-20 23:13:13 +02:00
|
|
|
void VariableGroupItem::populateGroup(MacroExpander *expander)
|
|
|
|
|
{
|
2014-11-27 17:45:49 +01:00
|
|
|
if (!expander)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-11-05 10:30:51 +01:00
|
|
|
foreach (const QByteArray &variable, expander->visibleVariables()) {
|
2014-10-20 23:13:13 +02:00
|
|
|
auto item = new VariableItem;
|
2016-07-27 17:24:34 +02:00
|
|
|
item->m_variable = variable;
|
2014-10-20 23:13:13 +02:00
|
|
|
item->m_expander = expander;
|
|
|
|
|
appendChild(item);
|
|
|
|
|
}
|
|
|
|
|
|
2014-11-27 17:45:49 +01:00
|
|
|
foreach (const MacroExpanderProvider &subProvider, expander->subProviders()) {
|
|
|
|
|
if (!subProvider)
|
|
|
|
|
continue;
|
2014-10-20 23:13:13 +02:00
|
|
|
if (expander->isAccumulating()) {
|
2014-11-27 17:45:49 +01:00
|
|
|
populateGroup(subProvider());
|
2014-10-20 23:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
auto item = new VariableGroupItem;
|
|
|
|
|
item->m_chooser = m_chooser;
|
2014-11-27 17:45:49 +01:00
|
|
|
item->m_provider = subProvider;
|
2014-10-20 23:13:13 +02:00
|
|
|
appendChild(item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-20 00:34:41 +02:00
|
|
|
} // namespace Internal
|
|
|
|
|
|
|
|
|
|
using namespace Internal;
|
2011-02-10 20:58:17 +01:00
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
2020-09-18 12:11:40 +02:00
|
|
|
\class Utils::VariableChooser
|
2020-06-12 16:04:30 +02:00
|
|
|
\inheaderfile coreplugin/variablechooser.h
|
|
|
|
|
\inmodule QtCreator
|
|
|
|
|
|
|
|
|
|
\brief The VariableChooser class is used to add a tool window for selecting \QC variables
|
|
|
|
|
to line edits, text edits or plain text edits.
|
|
|
|
|
|
|
|
|
|
If you allow users to add \QC variables to strings that are specified in your UI, for example
|
|
|
|
|
when users can provide a string through a text control, you should add a variable chooser to it.
|
|
|
|
|
The variable chooser allows users to open a tool window that contains the list of
|
|
|
|
|
all available variables together with a description. Double-clicking a variable inserts the
|
|
|
|
|
corresponding string into the corresponding text control like a line edit.
|
|
|
|
|
|
|
|
|
|
\image variablechooser.png "External Tools Preferences with Variable Chooser"
|
|
|
|
|
|
|
|
|
|
The variable chooser monitors focus changes of all children of its parent widget.
|
|
|
|
|
When a text control gets focus, the variable chooser checks if it has variable support set.
|
|
|
|
|
If the control supports variables,
|
|
|
|
|
a tool button which opens the variable chooser is shown in it while it has focus.
|
|
|
|
|
|
|
|
|
|
Supported text controls are QLineEdit, QTextEdit and QPlainTextEdit.
|
|
|
|
|
|
|
|
|
|
The variable chooser is deleted when its parent widget is deleted.
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
\code
|
|
|
|
|
QWidget *myOptionsContainerWidget = new QWidget;
|
2020-09-18 12:11:40 +02:00
|
|
|
new Utils::VariableChooser(myOptionsContainerWidget)
|
2020-06-12 16:04:30 +02:00
|
|
|
QLineEdit *myLineEditOption = new QLineEdit(myOptionsContainerWidget);
|
|
|
|
|
myOptionsContainerWidget->layout()->addWidget(myLineEditOption);
|
2020-09-18 12:11:40 +02:00
|
|
|
Utils::VariableChooser::addVariableSupport(myLineEditOption);
|
2020-06-12 16:04:30 +02:00
|
|
|
\endcode
|
|
|
|
|
*/
|
2013-03-28 10:27:57 +01:00
|
|
|
|
|
|
|
|
/*!
|
2014-10-13 18:49:44 +02:00
|
|
|
* \internal
|
2013-03-28 10:27:57 +01:00
|
|
|
* \variable VariableChooser::kVariableSupportProperty
|
|
|
|
|
* Property name that is checked for deciding if a widget supports \QC variables.
|
|
|
|
|
* Can be manually set with
|
|
|
|
|
* \c{textcontrol->setProperty(VariableChooser::kVariableSupportProperty, true)}
|
|
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
const char kVariableSupportProperty[] = "QtCreator.VariableSupport";
|
|
|
|
|
const char kVariableNameProperty[] = "QtCreator.VariableName";
|
2013-03-28 11:05:35 +01:00
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* Creates a variable chooser that tracks all children of \a parent for variable support.
|
|
|
|
|
* Ownership is also transferred to \a parent.
|
|
|
|
|
*/
|
2011-02-10 20:58:17 +01:00
|
|
|
VariableChooser::VariableChooser(QWidget *parent) :
|
|
|
|
|
QWidget(parent),
|
2014-06-20 00:34:41 +02:00
|
|
|
d(new VariableChooserPrivate(this))
|
2011-02-10 20:58:17 +01:00
|
|
|
{
|
2014-06-20 00:34:41 +02:00
|
|
|
setWindowTitle(tr("Variables"));
|
2014-11-06 16:59:55 +01:00
|
|
|
setWindowFlags(Qt::Tool);
|
2011-06-28 15:28:29 +02:00
|
|
|
setFocusPolicy(Qt::StrongFocus);
|
2014-10-13 18:49:44 +02:00
|
|
|
setFocusProxy(d->m_variableTree);
|
2017-02-13 21:46:27 +02:00
|
|
|
setGeometry(QRect(0, 0, 400, 500));
|
2014-10-13 18:49:44 +02:00
|
|
|
addMacroExpanderProvider([]() { return globalMacroExpander(); });
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2011-02-10 20:58:17 +01:00
|
|
|
VariableChooser::~VariableChooser()
|
|
|
|
|
{
|
2014-06-20 00:34:41 +02:00
|
|
|
delete d->m_iconButton;
|
|
|
|
|
delete d;
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
|
2020-03-23 11:27:24 +01:00
|
|
|
/*!
|
|
|
|
|
Adds the macro expander provider \a provider.
|
|
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
void VariableChooser::addMacroExpanderProvider(const MacroExpanderProvider &provider)
|
|
|
|
|
{
|
2014-10-20 23:13:13 +02:00
|
|
|
auto item = new VariableGroupItem;
|
|
|
|
|
item->m_chooser = d;
|
2014-10-13 18:49:44 +02:00
|
|
|
item->m_provider = provider;
|
|
|
|
|
d->m_model.rootItem()->prependChild(item);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
2020-03-23 11:27:24 +01:00
|
|
|
* Marks the control \a textcontrol as supporting variables.
|
|
|
|
|
*
|
|
|
|
|
* If the control provides a variable to the macro expander itself, set
|
|
|
|
|
* \a ownName to the variable name to prevent the user from choosing the
|
|
|
|
|
* variable, which would lead to endless recursion.
|
2013-03-28 10:27:57 +01:00
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
void VariableChooser::addSupportedWidget(QWidget *textcontrol, const QByteArray &ownName)
|
2013-03-28 11:05:35 +01:00
|
|
|
{
|
|
|
|
|
QTC_ASSERT(textcontrol, return);
|
2014-10-13 18:49:44 +02:00
|
|
|
textcontrol->setProperty(kVariableSupportProperty, QVariant::fromValue<QWidget *>(this));
|
|
|
|
|
textcontrol->setProperty(kVariableNameProperty, ownName);
|
2013-03-28 11:05:35 +01:00
|
|
|
}
|
|
|
|
|
|
2014-11-05 16:10:09 +01:00
|
|
|
void VariableChooser::addSupportForChildWidgets(QWidget *parent, MacroExpander *expander)
|
|
|
|
|
{
|
|
|
|
|
auto chooser = new VariableChooser(parent);
|
|
|
|
|
chooser->addMacroExpanderProvider([expander] { return expander; });
|
|
|
|
|
foreach (QWidget *child, parent->findChildren<QWidget *>()) {
|
|
|
|
|
if (qobject_cast<QLineEdit *>(child)
|
|
|
|
|
|| qobject_cast<QTextEdit *>(child)
|
|
|
|
|
|| qobject_cast<QPlainTextEdit *>(child))
|
|
|
|
|
chooser->addSupportedWidget(child);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
void VariableChooserPrivate::updateDescription(const QModelIndex &index)
|
2011-02-10 20:58:17 +01:00
|
|
|
{
|
2016-03-24 11:03:25 +01:00
|
|
|
if (m_variableDescription)
|
2016-11-08 14:34:01 +01:00
|
|
|
m_variableDescription->setText(m_model.data(m_sortModel->mapToSource(index),
|
|
|
|
|
Qt::ToolTipRole).toString());
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-22 13:13:22 +02:00
|
|
|
int VariableChooserPrivate::buttonMargin() const
|
|
|
|
|
{
|
2017-09-05 12:59:31 +02:00
|
|
|
return 24;
|
2014-10-22 13:13:22 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void VariableChooserPrivate::updateButtonGeometry()
|
|
|
|
|
{
|
|
|
|
|
QWidget *current = currentWidget();
|
|
|
|
|
int margin = buttonMargin();
|
2020-08-11 20:35:58 +02:00
|
|
|
int rightPadding = 0;
|
|
|
|
|
if (const auto scrollArea = qobject_cast<const QAbstractScrollArea*>(current)) {
|
2020-08-31 22:39:57 +02:00
|
|
|
rightPadding = scrollArea->verticalScrollBar()->isVisible() ?
|
|
|
|
|
scrollArea->verticalScrollBar()->width() : 0;
|
2020-08-11 20:35:58 +02:00
|
|
|
}
|
2014-10-22 13:13:22 +02:00
|
|
|
m_iconButton->setGeometry(current->rect().adjusted(
|
|
|
|
|
current->width() - (margin + 4), 0,
|
2020-08-11 20:35:58 +02:00
|
|
|
0, -qMax(0, current->height() - (margin + 4)))
|
|
|
|
|
.translated(-rightPadding, 0));
|
2014-10-22 13:13:22 +02:00
|
|
|
}
|
|
|
|
|
|
2014-06-20 00:34:41 +02:00
|
|
|
void VariableChooserPrivate::updateCurrentEditor(QWidget *old, QWidget *widget)
|
2011-02-10 20:58:17 +01:00
|
|
|
{
|
2019-07-23 10:58:00 +02:00
|
|
|
Q_UNUSED(old)
|
2011-03-03 20:50:25 +01:00
|
|
|
if (!widget) // we might loose focus, but then keep the previous state
|
|
|
|
|
return;
|
|
|
|
|
// prevent children of the chooser itself, and limit to children of chooser's parent
|
|
|
|
|
bool handle = false;
|
|
|
|
|
QWidget *parent = widget;
|
|
|
|
|
while (parent) {
|
2014-06-20 00:34:41 +02:00
|
|
|
if (parent == q)
|
2011-03-03 20:50:25 +01:00
|
|
|
return;
|
2014-06-20 00:34:41 +02:00
|
|
|
if (parent == q->parentWidget()) {
|
2011-03-03 20:50:25 +01:00
|
|
|
handle = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
parent = parent->parentWidget();
|
|
|
|
|
}
|
|
|
|
|
if (!handle)
|
|
|
|
|
return;
|
2014-10-13 18:49:44 +02:00
|
|
|
|
2011-03-03 20:50:25 +01:00
|
|
|
QLineEdit *previousLineEdit = m_lineEdit;
|
2014-06-05 08:27:17 +02:00
|
|
|
QWidget *previousWidget = currentWidget();
|
2018-07-21 21:11:46 +02:00
|
|
|
m_lineEdit = nullptr;
|
|
|
|
|
m_textEdit = nullptr;
|
|
|
|
|
m_plainTextEdit = nullptr;
|
|
|
|
|
auto chooser = widget->property(kVariableSupportProperty).value<QWidget *>();
|
2017-04-14 15:42:37 +02:00
|
|
|
m_currentVariableName = widget->property(kVariableNameProperty).toByteArray();
|
2014-10-13 18:49:44 +02:00
|
|
|
bool supportsVariables = chooser == q;
|
2018-07-21 21:11:46 +02:00
|
|
|
if (auto lineEdit = qobject_cast<QLineEdit *>(widget))
|
|
|
|
|
m_lineEdit = (supportsVariables ? lineEdit : nullptr);
|
|
|
|
|
else if (auto textEdit = qobject_cast<QTextEdit *>(widget))
|
|
|
|
|
m_textEdit = (supportsVariables ? textEdit : nullptr);
|
|
|
|
|
else if (auto plainTextEdit = qobject_cast<QPlainTextEdit *>(widget))
|
|
|
|
|
m_plainTextEdit = (supportsVariables ? plainTextEdit : nullptr);
|
2014-06-05 08:27:17 +02:00
|
|
|
|
|
|
|
|
QWidget *current = currentWidget();
|
|
|
|
|
if (current != previousWidget) {
|
2014-10-22 13:13:22 +02:00
|
|
|
if (previousWidget)
|
|
|
|
|
previousWidget->removeEventFilter(q);
|
2011-03-03 20:50:25 +01:00
|
|
|
if (previousLineEdit)
|
|
|
|
|
previousLineEdit->setTextMargins(0, 0, 0, 0);
|
|
|
|
|
if (m_iconButton) {
|
|
|
|
|
m_iconButton->hide();
|
2018-07-21 21:11:46 +02:00
|
|
|
m_iconButton->setParent(nullptr);
|
2011-03-03 20:50:25 +01:00
|
|
|
}
|
2014-06-05 08:27:17 +02:00
|
|
|
if (current) {
|
2014-10-22 13:13:22 +02:00
|
|
|
current->installEventFilter(q); // escape key handling and geometry changes
|
2011-03-03 20:50:25 +01:00
|
|
|
if (!m_iconButton)
|
|
|
|
|
createIconButton();
|
2014-10-22 13:13:22 +02:00
|
|
|
int margin = buttonMargin();
|
2014-06-05 08:27:17 +02:00
|
|
|
if (m_lineEdit)
|
|
|
|
|
m_lineEdit->setTextMargins(0, 0, margin, 0);
|
|
|
|
|
m_iconButton->setParent(current);
|
2014-10-22 13:13:22 +02:00
|
|
|
updateButtonGeometry();
|
2011-03-03 20:50:25 +01:00
|
|
|
m_iconButton->show();
|
2014-10-22 13:13:22 +02:00
|
|
|
} else {
|
|
|
|
|
q->hide();
|
2011-03-03 20:50:25 +01:00
|
|
|
}
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-30 14:28:34 +02:00
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
void VariableChooserPrivate::updatePositionAndShow(bool)
|
2011-03-30 14:28:34 +02:00
|
|
|
{
|
2014-06-20 00:34:41 +02:00
|
|
|
if (QWidget *w = q->parentWidget()) {
|
|
|
|
|
QPoint parentCenter = w->mapToGlobal(w->geometry().center());
|
|
|
|
|
q->move(parentCenter.x() - q->width()/2, parentCenter.y() - q->height()/2);
|
2011-03-30 14:28:34 +02:00
|
|
|
}
|
2014-06-20 00:34:41 +02:00
|
|
|
q->show();
|
|
|
|
|
q->raise();
|
|
|
|
|
q->activateWindow();
|
2014-10-20 23:13:13 +02:00
|
|
|
m_variableTree->expandAll();
|
2011-03-03 20:50:25 +01:00
|
|
|
}
|
|
|
|
|
|
2017-02-13 21:46:27 +02:00
|
|
|
void VariableChooserPrivate::updateFilter(const QString &filterText)
|
|
|
|
|
{
|
2020-08-18 15:57:39 +02:00
|
|
|
const QString pattern = QRegularExpression::escape(filterText);
|
|
|
|
|
m_sortModel->setFilterRegularExpression(
|
|
|
|
|
QRegularExpression(pattern, QRegularExpression::CaseInsensitiveOption));
|
2017-02-13 21:46:27 +02:00
|
|
|
m_variableTree->expandAll();
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 08:27:17 +02:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2020-11-21 01:04:56 +01:00
|
|
|
QWidget *VariableChooserPrivate::currentWidget() const
|
2014-06-05 08:27:17 +02:00
|
|
|
{
|
|
|
|
|
if (m_lineEdit)
|
|
|
|
|
return m_lineEdit;
|
|
|
|
|
if (m_textEdit)
|
|
|
|
|
return m_textEdit;
|
|
|
|
|
return m_plainTextEdit;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-13 18:49:44 +02:00
|
|
|
void VariableChooserPrivate::handleItemActivated(const QModelIndex &index)
|
2011-02-10 20:58:17 +01:00
|
|
|
{
|
2016-11-08 14:34:01 +01:00
|
|
|
QString text = m_model.data(m_sortModel->mapToSource(index), UnexpandedTextRole).toString();
|
2014-10-20 23:13:13 +02:00
|
|
|
if (!text.isEmpty())
|
|
|
|
|
insertText(text);
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-20 23:13:13 +02:00
|
|
|
void VariableChooserPrivate::insertText(const QString &text)
|
2011-02-10 20:58:17 +01:00
|
|
|
{
|
|
|
|
|
if (m_lineEdit) {
|
|
|
|
|
m_lineEdit->insert(text);
|
2011-03-03 20:50:25 +01:00
|
|
|
m_lineEdit->activateWindow();
|
2011-02-10 20:58:17 +01:00
|
|
|
} else if (m_textEdit) {
|
|
|
|
|
m_textEdit->insertPlainText(text);
|
2011-03-03 20:50:25 +01:00
|
|
|
m_textEdit->activateWindow();
|
2011-02-10 20:58:17 +01:00
|
|
|
} else if (m_plainTextEdit) {
|
|
|
|
|
m_plainTextEdit->insertPlainText(text);
|
2011-03-03 20:50:25 +01:00
|
|
|
m_plainTextEdit->activateWindow();
|
2011-02-10 20:58:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-06-22 16:21:04 +02:00
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2012-12-20 15:34:42 +01:00
|
|
|
static bool handleEscapePressed(QKeyEvent *ke, QWidget *widget)
|
2011-06-22 16:21:04 +02:00
|
|
|
{
|
|
|
|
|
if (ke->key() == Qt::Key_Escape && !ke->modifiers()) {
|
|
|
|
|
ke->accept();
|
2016-02-02 09:10:54 +02:00
|
|
|
QTimer::singleShot(0, widget, &QWidget::close);
|
2012-12-20 15:34:42 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-11-05 14:32:56 +01:00
|
|
|
bool VariableChooser::event(QEvent *ev)
|
2012-12-20 15:34:42 +01:00
|
|
|
{
|
2014-11-05 14:32:56 +01:00
|
|
|
if (ev->type() == QEvent::KeyPress || ev->type() == QEvent::ShortcutOverride) {
|
2018-07-21 21:11:46 +02:00
|
|
|
auto ke = static_cast<QKeyEvent *>(ev);
|
2014-11-05 14:32:56 +01:00
|
|
|
if (handleEscapePressed(ke, this))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return QWidget::event(ev);
|
2012-12-20 15:34:42 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-28 10:27:57 +01:00
|
|
|
/*!
|
|
|
|
|
* \internal
|
|
|
|
|
*/
|
2014-10-22 13:13:22 +02:00
|
|
|
bool VariableChooser::eventFilter(QObject *obj, QEvent *event)
|
2012-12-20 15:34:42 +01:00
|
|
|
{
|
2014-10-22 13:13:22 +02:00
|
|
|
if (obj != d->currentWidget())
|
|
|
|
|
return false;
|
2014-11-05 14:32:56 +01:00
|
|
|
if ((event->type() == QEvent::KeyPress || event->type() == QEvent::ShortcutOverride) && isVisible()) {
|
2018-07-21 21:11:46 +02:00
|
|
|
auto ke = static_cast<QKeyEvent *>(event);
|
2012-12-20 15:34:42 +01:00
|
|
|
return handleEscapePressed(ke, this);
|
2020-08-11 20:35:58 +02:00
|
|
|
} else if (event->type() == QEvent::Resize || event->type() == QEvent::LayoutRequest) {
|
2014-10-22 13:13:22 +02:00
|
|
|
d->updateButtonGeometry();
|
2014-11-07 10:18:04 +01:00
|
|
|
} else if (event->type() == QEvent::Hide) {
|
|
|
|
|
close();
|
2011-06-22 16:21:04 +02:00
|
|
|
}
|
2012-12-20 15:34:42 +01:00
|
|
|
return false;
|
2011-06-22 16:21:04 +02:00
|
|
|
}
|
2014-06-20 00:34:41 +02:00
|
|
|
|
|
|
|
|
} // namespace Internal
|