Files
qt-creator/src/libs/utils/aspects.cpp
hjk a694487711 Utils/VcsBase: Move ability to specify group settings keys to base
Merge it with the read/writeSettings implementation that was already
there.

Change-Id: I25dfbdf6fd1cf122b17f89eae82cb2598d8470c8
Reviewed-by: David Schulz <david.schulz@qt.io>
2021-03-24 10:14:25 +00:00

2166 lines
55 KiB
C++

/****************************************************************************
**
** Copyright (C) 2018 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 "aspects.h"
#include "algorithm.h"
#include "fancylineedit.h"
#include "layoutbuilder.h"
#include "pathchooser.h"
#include "qtcassert.h"
#include "qtcprocess.h"
#include "qtcsettings.h"
#include "utilsicons.h"
#include "variablechooser.h"
#include <QAction>
#include <QButtonGroup>
#include <QCheckBox>
#include <QComboBox>
#include <QDebug>
#include <QFormLayout>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QPointer>
#include <QRadioButton>
#include <QSettings>
#include <QSpinBox>
#include <QTextEdit>
#include <QToolButton>
namespace Utils {
namespace Internal {
class BaseAspectPrivate
{
public:
Utils::Id m_id;
QVariant m_value;
QVariant m_defaultValue;
QString m_displayName;
QString m_settingsKey; // Name of data in settings.
QString m_tooltip;
QString m_labelText;
QPixmap m_labelPixmap;
QIcon m_icon;
QPointer<QLabel> m_label; // Owned by configuration widget
QPointer<QAction> m_action; // Owned by us.
bool m_visible = true;
bool m_enabled = true;
bool m_readOnly = true;
bool m_autoApply = true;
BaseAspect::ConfigWidgetCreator m_configWidgetCreator;
QList<QPointer<QWidget>> m_subWidgets;
};
} // Internal
/*!
\class Utils::BaseAspect
\inmodule QtCreator
\brief The \c BaseAspect class provides a common base for classes implementing
aspects.
An aspect is a hunk of data like a property or collection of related
properties of some object, together with a description of its behavior
for common operations like visualizing or persisting.
Simple aspects are for example a boolean property represented by a QCheckBox
in the user interface, or a string property represented by a PathChooser,
selecting directories in the filesystem.
While aspects implementations usually have the ability to visualize and to persist
their data, or use an ID, neither of these is mandatory.
*/
/*!
Constructs a BaseAspect.
*/
BaseAspect::BaseAspect()
: d(new Internal::BaseAspectPrivate)
{}
/*!
Destructs a BaseAspect.
*/
BaseAspect::~BaseAspect()
{
delete d->m_action;
}
Id BaseAspect::id() const
{
return d->m_id;
}
void BaseAspect::setId(Id id)
{
d->m_id = id;
}
QVariant BaseAspect::value() const
{
return d->m_value;
}
/*!
Sets value.
Emits changed() if the value changed.
*/
void BaseAspect::setValue(const QVariant &value)
{
if (setValueQuietly(value)) {
emit changed();
emitChangedValue();
}
}
/*!
Sets value without emitting changed()
Returns whether the value changed.
*/
bool BaseAspect::setValueQuietly(const QVariant &value)
{
if (d->m_value == value)
return false;
d->m_value = value;
return true;
}
QVariant BaseAspect::defaultValue() const
{
return d->m_defaultValue;
}
/*!
Sets a default value and the current value for this aspect.
\note The current value will be set silently to the same value.
It is reasonable to only set default values in the setup phase
of the aspect.
Default values will not be stored in settings.
*/
void BaseAspect::setDefaultValue(const QVariant &value)
{
d->m_defaultValue = value;
d->m_value = value;
}
void BaseAspect::setDisplayName(const QString &displayName)
{
d->m_displayName = displayName;
}
bool BaseAspect::isVisible() const
{
return d->m_visible;
}
/*!
Shows or hides the visual representation of this aspect depending
on the value of \a visible.
By default, it is visible.
*/
void BaseAspect::setVisible(bool visible)
{
d->m_visible = visible;
for (QWidget *w : qAsConst(d->m_subWidgets)) {
QTC_ASSERT(w, continue);
// This may happen during layout building. Explicit setting visibility here
// may create a show a toplevel widget for a moment until it is parented
// to some non-shown widget.
if (visible && w->parentWidget())
w->setVisible(visible);
}
}
void BaseAspect::setupLabel()
{
QTC_ASSERT(!d->m_label, delete d->m_label);
if (d->m_labelText.isEmpty() && d->m_labelPixmap.isNull())
return;
d->m_label = new QLabel(d->m_labelText);
d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!d->m_labelPixmap.isNull())
d->m_label->setPixmap(d->m_labelPixmap);
registerSubWidget(d->m_label);
}
void BaseAspect::addLabeledItem(LayoutBuilder &builder, QWidget *widget)
{
setupLabel();
if (QLabel *l = label()) {
l->setBuddy(widget);
builder.addItems({l, widget});
} else {
builder.addItems({LayoutBuilder::LayoutItem(widget)});
}
}
/*!
Sets \a labelText as text for the separate label in the visual
representation of this aspect.
*/
void BaseAspect::setLabelText(const QString &labelText)
{
d->m_labelText = labelText;
if (d->m_label)
d->m_label->setText(labelText);
}
/*!
Sets \a labelPixmap as pixmap for the separate label in the visual
representation of this aspect.
*/
void BaseAspect::setLabelPixmap(const QPixmap &labelPixmap)
{
d->m_labelPixmap = labelPixmap;
if (d->m_label)
d->m_label->setPixmap(labelPixmap);
}
void BaseAspect::setIcon(const QIcon &icon)
{
d->m_icon = icon;
if (d->m_action)
d->m_action->setIcon(icon);
}
/*!
Returns the current text for the separate label in the visual
representation of this aspect.
*/
QString BaseAspect::labelText() const
{
return d->m_labelText;
}
QLabel *BaseAspect::label() const
{
return d->m_label.data();
}
QString BaseAspect::toolTip() const
{
return d->m_tooltip;
}
/*!
Sets \a tooltip as tool tip for the visual representation of this aspect.
*/
void BaseAspect::setToolTip(const QString &tooltip)
{
d->m_tooltip = tooltip;
for (QWidget *w : qAsConst(d->m_subWidgets)) {
QTC_ASSERT(w, continue);
w->setToolTip(tooltip);
}
}
void BaseAspect::setEnabled(bool enabled)
{
d->m_enabled = enabled;
for (QWidget *w : qAsConst(d->m_subWidgets)) {
QTC_ASSERT(w, continue);
w->setEnabled(enabled);
}
}
void BaseAspect::setReadOnly(bool readOnly)
{
d->m_readOnly = readOnly;
for (QWidget *w : qAsConst(d->m_subWidgets)) {
QTC_ASSERT(w, continue);
if (auto lineEdit = qobject_cast<QLineEdit *>(w))
lineEdit->setReadOnly(readOnly);
else if (auto textEdit = qobject_cast<QTextEdit *>(w))
textEdit->setReadOnly(readOnly);
}
}
bool BaseAspect::isAutoApply() const
{
return d->m_autoApply;
}
/*!
Sets auto-apply mode. When auto-apply mode is on, user interaction to this
aspect's widget will not modify the \c value of the aspect until \c apply()
is called programmatically.
\sa setSettingsKey()
*/
void BaseAspect::setAutoApply(bool on)
{
d->m_autoApply = on;
}
/*!
\internal
*/
void BaseAspect::setConfigWidgetCreator(const ConfigWidgetCreator &configWidgetCreator)
{
d->m_configWidgetCreator = configWidgetCreator;
}
/*!
Returns the key to be used when accessing the settings.
\sa setSettingsKey()
*/
QString BaseAspect::settingsKey() const
{
return d->m_settingsKey;
}
/*!
Sets the key to be used when accessing the settings.
\sa settingsKey()
*/
void BaseAspect::setSettingsKey(const QString &key)
{
d->m_settingsKey = key;
}
/*!
Sets the key and group to be used when accessing the settings.
\sa settingsKey()
*/
void BaseAspect::setSettingsKey(const QString &group, const QString &key)
{
d->m_settingsKey = group + "/" + key;
}
/*!
Returns the string that should be used when this action appears in menus
or other places that are typically used with Book style capitalization.
If no display name is set, the label text will be used as fallback.
*/
QString BaseAspect::displayName() const
{
return d->m_displayName.isEmpty() ? d->m_labelText : d->m_displayName;
}
/*!
\internal
*/
QWidget *BaseAspect::createConfigWidget() const
{
return d->m_configWidgetCreator ? d->m_configWidgetCreator() : nullptr;
}
QAction *BaseAspect::action()
{
if (!d->m_action) {
d->m_action = new QAction(labelText());
d->m_action->setIcon(d->m_icon);
}
return d->m_action;
}
/*!
Adds the visual representation of this aspect to a layout using
a layout builder.
*/
void BaseAspect::addToLayout(LayoutBuilder &)
{
}
/*!
Updates this aspect's value from user-initiated changes in the widget.
This has only an effect if \c isAutoApply is false.
*/
void BaseAspect::apply()
{
QTC_CHECK(!d->m_autoApply);
if (isDirty())
setValue(volatileValue());
}
/*!
Discard user changes in the widget and restore widget contents from
aspect's value.
This has only an effect if \c isAutoApply is false.
*/
void BaseAspect::cancel()
{
QTC_CHECK(!d->m_autoApply);
if (!d->m_subWidgets.isEmpty())
setVolatileValue(d->m_value);
}
void BaseAspect::finish()
{
// FIXME: Delete widgets?
}
bool BaseAspect::hasAction() const
{
return d->m_action != nullptr;
}
bool BaseAspect::isDirty() const
{
QTC_CHECK(!isAutoApply());
// Aspects that were never shown cannot contain unsaved user changes.
if (d->m_subWidgets.isEmpty())
return false;
return volatileValue() != d->m_value;
}
QVariant BaseAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
return {};
}
void BaseAspect::setVolatileValue(const QVariant &val)
{
Q_UNUSED(val);
}
void BaseAspect::registerSubWidget(QWidget *widget)
{
d->m_subWidgets.append(widget);
connect(widget, &QObject::destroyed, this, [this, widget] {
d->m_subWidgets.removeAll(widget);
});
widget->setEnabled(d->m_enabled);
widget->setToolTip(d->m_tooltip);
// Visible is on by default. Not setting it explicitly avoid popping
// it up when the parent is not set yet, the normal case.
if (!d->m_visible)
widget->setVisible(d->m_visible);
}
void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value,
const QVariant &defaultValue, const QString &key)
{
if (key.isEmpty())
return;
if (value == defaultValue)
data.remove(key);
else
data.insert(key, value);
}
/*!
Retrieves the internal value of this BaseAspect from a \c QVariantMap.
*/
void BaseAspect::fromMap(const QVariantMap &map)
{
setValue(map.value(settingsKey(), defaultValue()));
}
/*!
Stores the internal value of this BaseAspect into a \c QVariantMap.
*/
void BaseAspect::toMap(QVariantMap &map) const
{
saveToMap(map, d->m_value, d->m_defaultValue, settingsKey());
}
void BaseAspect::readSettings(const QSettings *settings)
{
if (settingsKey().isEmpty())
return;
setValue(settings->value(settingsKey(), defaultValue()));
}
void BaseAspect::writeSettings(QSettings *settings) const
{
if (settingsKey().isEmpty())
return;
QtcSettings::setValueWithDefault(settings, settingsKey(), value(), defaultValue());
}
/*!
\internal
*/
void BaseAspect::acquaintSiblings(const BaseAspects &)
{}
// BaseAspects
/*!
\class BaseAspects
\inmodule QtCreator
\brief This class represent a collection of one or more aspects.
A BaseAspects object assumes ownership on its aspects.
*/
/*!
Constructs a BaseAspects object.
*/
BaseAspects::BaseAspects() = default;
/*!
Destructs a BaseAspects object.
*/
BaseAspects::~BaseAspects()
{
qDeleteAll(m_aspects);
}
/*!
Retrieves a BaseAspect with a given \a id, or nullptr if no such aspect is contained.
\sa BaseAspect.
*/
BaseAspect *BaseAspects::aspect(Utils::Id id) const
{
return Utils::findOrDefault(m_aspects, Utils::equal(&BaseAspect::id, id));
}
/*!
\internal
*/
void BaseAspects::fromMap(const QVariantMap &map) const
{
for (BaseAspect *aspect : m_aspects)
aspect->fromMap(map);
}
/*!
\internal
*/
void BaseAspects::toMap(QVariantMap &map) const
{
for (BaseAspect *aspect : m_aspects)
aspect->toMap(map);
}
namespace Internal {
class BoolAspectPrivate
{
public:
BoolAspect::LabelPlacement m_labelPlacement = BoolAspect::LabelPlacement::AtCheckBox;
QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
};
class SelectionAspectPrivate
{
public:
SelectionAspect::DisplayStyle m_displayStyle
= SelectionAspect::DisplayStyle::RadioButtons;
struct Option { QString displayName; QString tooltip; };
QVector<Option> m_options;
// These are all owned by the configuration widget.
QList<QPointer<QRadioButton>> m_buttons;
QPointer<QComboBox> m_comboBox;
QPointer<QButtonGroup> m_buttonGroup;
};
class MultiSelectionAspectPrivate
{
public:
explicit MultiSelectionAspectPrivate(MultiSelectionAspect *q) : q(q) {}
bool setValueSelectedHelper(const QString &value, bool on);
MultiSelectionAspect *q;
QStringList m_allValues;
MultiSelectionAspect::DisplayStyle m_displayStyle
= MultiSelectionAspect::DisplayStyle::ListView;
// These are all owned by the configuration widget.
QPointer<QListWidget> m_listView;
};
class StringAspectPrivate
{
public:
StringAspect::DisplayStyle m_displayStyle = StringAspect::LabelDisplay;
StringAspect::CheckBoxPlacement m_checkBoxPlacement
= StringAspect::CheckBoxPlacement::Right;
StringAspect::UncheckedSemantics m_uncheckedSemantics
= StringAspect::UncheckedSemantics::Disabled;
std::function<QString(const QString &)> m_displayFilter;
std::unique_ptr<BoolAspect> m_checker;
QString m_placeHolderText;
QString m_historyCompleterKey;
PathChooser::Kind m_expectedKind = PathChooser::File;
Environment m_environment;
QPointer<QLabel> m_labelDisplay;
QPointer<FancyLineEdit> m_lineEditDisplay;
QPointer<PathChooser> m_pathChooserDisplay;
QPointer<QTextEdit> m_textEditDisplay;
MacroExpanderProvider m_expanderProvider;
FilePath m_baseFileName;
StringAspect::ValueAcceptor m_valueAcceptor;
FancyLineEdit::ValidationFunction m_validator;
std::function<void()> m_openTerminal;
bool m_undoRedoEnabled = true;
bool m_acceptRichText = false;
bool m_showToolTipOnLabel = false;
bool m_fileDialogOnly = false;
template<class Widget> void updateWidgetFromCheckStatus(Widget *w)
{
const bool enabled = !m_checker || m_checker->value();
if (m_uncheckedSemantics == StringAspect::UncheckedSemantics::Disabled)
w->setEnabled(enabled);
else
w->setReadOnly(!enabled);
}
};
class IntegerAspectPrivate
{
public:
Utils::optional<qint64> m_minimumValue;
Utils::optional<qint64> m_maximumValue;
int m_displayIntegerBase = 10;
qint64 m_displayScaleFactor = 1;
QString m_prefix;
QString m_suffix;
QString m_specialValueText;
int m_singleStep = 1;
QPointer<QSpinBox> m_spinBox; // Owned by configuration widget
};
class DoubleAspectPrivate
{
public:
Utils::optional<double> m_minimumValue;
Utils::optional<double> m_maximumValue;
QString m_prefix;
QString m_suffix;
QString m_specialValueText;
double m_singleStep = 1;
QPointer<QDoubleSpinBox> m_spinBox; // Owned by configuration widget
};
class StringListAspectPrivate
{
public:
};
class TextDisplayPrivate
{
public:
QString m_message;
Utils::InfoLabel::InfoType m_type;
QPointer<InfoLabel> m_label;
};
} // Internal
/*!
\enum Utils::StringAspect::DisplayStyle
\inmodule QtCreator
The DisplayStyle enum describes the main visual characteristics of a
string aspect.
\value LabelDisplay
Based on QLabel, used for text that cannot be changed by the
user in this place, for example names of executables that are
defined in the build system.
\value LineEditDisplay
Based on QLineEdit, used for user-editable strings that usually
fit on a line.
\value TextEditDisplay
Based on QTextEdit, used for user-editable strings that often
do not fit on a line.
\value PathChooserDisplay
Based on Utils::PathChooser.
\sa Utils::PathChooser
*/
/*!
\class Utils::StringAspect
\inmodule QtCreator
\brief A string aspect is a string-like property of some object, together with
a description of its behavior for common operations like visualizing or
persisting.
String aspects can represent for example a parameter for an external commands,
paths in a file system, or simply strings.
The string can be displayed using a QLabel, QLineEdit, QTextEdit or
Utils::PathChooser.
The visual representation often contains a label in front of the display
of the actual value.
*/
/*!
Constructs a StringAspect.
*/
StringAspect::StringAspect()
: d(new Internal::StringAspectPrivate)
{
setDefaultValue(QString());
}
/*!
\internal
*/
StringAspect::~StringAspect() = default;
/*!
\internal
*/
void StringAspect::setValueAcceptor(StringAspect::ValueAcceptor &&acceptor)
{
d->m_valueAcceptor = std::move(acceptor);
}
/*!
Returns the value of this StringAspect as an ordinary \c QString.
*/
QString StringAspect::value() const
{
return BaseAspect::value().toString();
}
/*!
Sets the \a value of this StringAspect from an ordinary \c QString.
*/
void StringAspect::setValue(const QString &val)
{
const bool isSame = val == value();
if (isSame)
return;
QString processedValue = val;
if (d->m_valueAcceptor) {
const Utils::optional<QString> tmp = d->m_valueAcceptor(value(), val);
if (!tmp) {
update(); // Make sure the original value is retained in the UI
return;
}
processedValue = tmp.value();
}
if (BaseAspect::setValueQuietly(QVariant(processedValue))) {
update();
emit changed();
}
}
void StringAspect::setDefaultValue(const QString &val)
{
BaseAspect::setDefaultValue(val);
}
/*!
\reimp
*/
void StringAspect::fromMap(const QVariantMap &map)
{
if (!settingsKey().isEmpty())
BaseAspect::setValueQuietly(map.value(settingsKey(), defaultValue()));
if (d->m_checker)
d->m_checker->fromMap(map);
}
/*!
\reimp
*/
void StringAspect::toMap(QVariantMap &map) const
{
saveToMap(map, value(), defaultValue(), settingsKey());
if (d->m_checker)
d->m_checker->toMap(map);
}
/*!
Returns the value of this string aspect as \c Utils::FilePath.
\note This simply uses \c FilePath::fromUserInput() for the
conversion. It does not use any check that the value is actually
a valid file path.
*/
FilePath StringAspect::filePath() const
{
return FilePath::fromUserInput(value());
}
/*!
Sets the value of this string aspect to \a value.
\note This simply uses \c FilePath::toUserOutput() for the
conversion. It does not use any check that the value is actually
a file path.
*/
void StringAspect::setFilePath(const FilePath &value)
{
setValue(value.toUserOutput());
}
PathChooser *StringAspect::pathChooser() const
{
return d->m_pathChooserDisplay.data();
}
/*!
\internal
*/
void StringAspect::setShowToolTipOnLabel(bool show)
{
d->m_showToolTipOnLabel = show;
update();
}
/*!
Sets a \a displayFilter for fine-tuning the visual appearance
of the value of this string aspect.
*/
void StringAspect::setDisplayFilter(const std::function<QString(const QString &)> &displayFilter)
{
d->m_displayFilter = displayFilter;
}
/*!
Returns the check box value.
\sa makeCheckable(), setChecked()
*/
bool StringAspect::isChecked() const
{
return !d->m_checker || d->m_checker->value();
}
/*!
Sets the check box of this aspect to \a checked.
\sa makeCheckable(), isChecked()
*/
void StringAspect::setChecked(bool checked)
{
QTC_ASSERT(d->m_checker, return);
d->m_checker->setValue(checked);
}
/*!
Selects the main display characteristics of the aspect according to
\a displayStyle.
\note Not all StringAspect features are available with all display styles.
\sa Utils::StringAspect::DisplayStyle
*/
void StringAspect::setDisplayStyle(DisplayStyle displayStyle)
{
d->m_displayStyle = displayStyle;
}
/*!
Sets \a placeHolderText as place holder for line and text displays.
*/
void StringAspect::setPlaceHolderText(const QString &placeHolderText)
{
d->m_placeHolderText = placeHolderText;
if (d->m_lineEditDisplay)
d->m_lineEditDisplay->setPlaceholderText(placeHolderText);
if (d->m_textEditDisplay)
d->m_textEditDisplay->setPlaceholderText(placeHolderText);
}
/*!
Sets \a historyCompleterKey as key for the history completer settings for
line edits and path chooser displays.
\sa Utils::PathChooser::setExpectedKind()
*/
void StringAspect::setHistoryCompleter(const QString &historyCompleterKey)
{
d->m_historyCompleterKey = historyCompleterKey;
if (d->m_lineEditDisplay)
d->m_lineEditDisplay->setHistoryCompleter(historyCompleterKey);
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey);
}
/*!
Sets \a expectedKind as expected kind for path chooser displays.
\sa Utils::PathChooser::setExpectedKind()
*/
void StringAspect::setExpectedKind(const PathChooser::Kind expectedKind)
{
d->m_expectedKind = expectedKind;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setExpectedKind(expectedKind);
}
void StringAspect::setFileDialogOnly(bool requireFileDialog)
{
d->m_fileDialogOnly = requireFileDialog;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setFileDialogOnly(requireFileDialog);
}
void StringAspect::setEnvironment(const Environment &env)
{
d->m_environment = env;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setEnvironment(env);
}
void StringAspect::setBaseFileName(const FilePath &baseFileName)
{
d->m_baseFileName = baseFileName;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setBaseDirectory(baseFileName);
}
void StringAspect::setUndoRedoEnabled(bool undoRedoEnabled)
{
d->m_undoRedoEnabled = undoRedoEnabled;
if (d->m_textEditDisplay)
d->m_textEditDisplay->setUndoRedoEnabled(undoRedoEnabled);
}
void StringAspect::setAcceptRichText(bool acceptRichText)
{
d->m_acceptRichText = acceptRichText;
if (d->m_textEditDisplay)
d->m_textEditDisplay->setAcceptRichText(acceptRichText);
}
void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
{
d->m_expanderProvider = expanderProvider;
}
void StringAspect::setUseGlobalMacroExpander()
{
d->m_expanderProvider = &globalMacroExpander;
}
void StringAspect::setValidationFunction(const FancyLineEdit::ValidationFunction &validator)
{
d->m_validator = validator;
if (d->m_lineEditDisplay)
d->m_lineEditDisplay->setValidationFunction(d->m_validator);
}
void StringAspect::setOpenTerminalHandler(const std::function<void ()> &openTerminal)
{
d->m_openTerminal = openTerminal;
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setOpenTerminalHandler(openTerminal);
}
void StringAspect::validateInput()
{
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->triggerChanged();
if (d->m_lineEditDisplay)
d->m_lineEditDisplay->validate();
}
void StringAspect::setUncheckedSemantics(StringAspect::UncheckedSemantics semantics)
{
d->m_uncheckedSemantics = semantics;
}
void StringAspect::addToLayout(LayoutBuilder &builder)
{
if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Top) {
d->m_checker->addToLayout(builder);
builder.finishRow();
}
const auto useMacroExpander = [this](QWidget *w) {
if (!d->m_expanderProvider)
return;
const auto chooser = new VariableChooser(w);
chooser->addSupportedWidget(w);
chooser->addMacroExpanderProvider(d->m_expanderProvider);
};
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
switch (d->m_displayStyle) {
case PathChooserDisplay:
d->m_pathChooserDisplay = createSubWidget<PathChooser>();
d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind);
if (!d->m_historyCompleterKey.isEmpty())
d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
d->m_pathChooserDisplay->setEnvironment(d->m_environment);
d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName);
d->m_pathChooserDisplay->setFileDialogOnly(d->m_fileDialogOnly);
d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal);
d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
d->updateWidgetFromCheckStatus(d->m_pathChooserDisplay.data());
addLabeledItem(builder, d->m_pathChooserDisplay);
useMacroExpander(d->m_pathChooserDisplay->lineEdit());
if (isAutoApply()) {
connect(d->m_pathChooserDisplay, &PathChooser::pathChanged, this, [this] {
setValue(d->m_pathChooserDisplay->path()); });
}
break;
case LineEditDisplay:
d->m_lineEditDisplay = createSubWidget<FancyLineEdit>();
d->m_lineEditDisplay->setPlaceholderText(d->m_placeHolderText);
if (!d->m_historyCompleterKey.isEmpty())
d->m_lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey);
if (d->m_validator)
d->m_lineEditDisplay->setValidationFunction(d->m_validator);
d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
d->updateWidgetFromCheckStatus(d->m_lineEditDisplay.data());
addLabeledItem(builder, d->m_lineEditDisplay);
useMacroExpander(d->m_lineEditDisplay);
if (isAutoApply()) {
connect(d->m_lineEditDisplay, &FancyLineEdit::textEdited,
this, &StringAspect::setValue);
}
break;
case TextEditDisplay:
d->m_textEditDisplay = createSubWidget<QTextEdit>();
d->m_textEditDisplay->setPlaceholderText(d->m_placeHolderText);
d->m_textEditDisplay->setUndoRedoEnabled(d->m_undoRedoEnabled);
d->m_textEditDisplay->setAcceptRichText(d->m_acceptRichText);
d->m_textEditDisplay->setTextInteractionFlags(Qt::TextEditorInteraction);
d->m_textEditDisplay->setText(displayedString);
d->updateWidgetFromCheckStatus(d->m_textEditDisplay.data());
addLabeledItem(builder, d->m_textEditDisplay);
useMacroExpander(d->m_textEditDisplay);
if (isAutoApply()) {
connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] {
setValue(d->m_textEditDisplay->document()->toPlainText());
});
}
break;
case LabelDisplay:
d->m_labelDisplay = createSubWidget<QLabel>();
d->m_labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse);
d->m_labelDisplay->setText(displayedString);
d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
addLabeledItem(builder, d->m_labelDisplay);
break;
}
validateInput();
if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Right)
d->m_checker->addToLayout(builder);
}
QVariant StringAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
switch (d->m_displayStyle) {
case PathChooserDisplay:
QTC_ASSERT(d->m_pathChooserDisplay, return {});
return d->m_pathChooserDisplay->path();
case LineEditDisplay:
QTC_ASSERT(d->m_lineEditDisplay, return {});
return d->m_lineEditDisplay->text();
case TextEditDisplay:
QTC_ASSERT(d->m_textEditDisplay, return {});
return d->m_textEditDisplay->document()->toPlainText();
case LabelDisplay:
break;
}
return {};
}
void StringAspect::setVolatileValue(const QVariant &val)
{
switch (d->m_displayStyle) {
case PathChooserDisplay:
if (d->m_pathChooserDisplay)
d->m_pathChooserDisplay->setPath(val.toString());
break;
case LineEditDisplay:
if (d->m_lineEditDisplay)
d->m_lineEditDisplay->setText(val.toString());
break;
case TextEditDisplay:
if (d->m_textEditDisplay)
d->m_textEditDisplay->document()->setPlainText(val.toString());
break;
case LabelDisplay:
break;
}
}
void StringAspect::update()
{
const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
if (d->m_pathChooserDisplay) {
d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
d->updateWidgetFromCheckStatus(d->m_pathChooserDisplay.data());
}
if (d->m_lineEditDisplay) {
d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
d->updateWidgetFromCheckStatus(d->m_lineEditDisplay.data());
}
if (d->m_textEditDisplay) {
const QString old = d->m_textEditDisplay->document()->toPlainText();
if (displayedString != old)
d->m_textEditDisplay->setText(displayedString);
d->updateWidgetFromCheckStatus(d->m_textEditDisplay.data());
}
if (d->m_labelDisplay) {
d->m_labelDisplay->setText(displayedString);
d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
}
validateInput();
}
/*!
Adds a check box with a \a checkerLabel according to \a checkBoxPlacement
to the line edit.
The state of the check box is made persistent when using a non-emtpy
\a checkerKey.
*/
void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement,
const QString &checkerLabel, const QString &checkerKey)
{
QTC_ASSERT(!d->m_checker, return);
d->m_checkBoxPlacement = checkBoxPlacement;
d->m_checker.reset(new BoolAspect);
d->m_checker->setLabel(checkerLabel, checkBoxPlacement == CheckBoxPlacement::Top
? BoolAspect::LabelPlacement::InExtraLabel
: BoolAspect::LabelPlacement::AtCheckBox);
d->m_checker->setSettingsKey(checkerKey);
connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::update);
connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::changed);
connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::checkedChanged);
update();
}
/*!
\class Utils::BoolAspect
\inmodule QtCreator
\brief A boolean aspect is a boolean property of some object, together with
a description of its behavior for common operations like visualizing or
persisting.
The boolean aspect is displayed using a QCheckBox.
The visual representation often contains a label in front or after
the display of the actual checkmark.
*/
BoolAspect::BoolAspect(const QString &settingsKey)
: d(new Internal::BoolAspectPrivate)
{
setDefaultValue(false);
setSettingsKey(settingsKey);
}
/*!
\reimp
*/
BoolAspect::~BoolAspect() = default;
/*!
\reimp
*/
void BoolAspect::addToLayout(LayoutBuilder &builder)
{
QTC_CHECK(!d->m_checkBox);
d->m_checkBox = createSubWidget<QCheckBox>();
switch (d->m_labelPlacement) {
case LabelPlacement::AtCheckBoxWithoutDummyLabel:
d->m_checkBox->setText(labelText());
builder.addItem(d->m_checkBox.data());
break;
case LabelPlacement::AtCheckBox: {
d->m_checkBox->setText(labelText());
LayoutBuilder::LayoutType type = builder.layoutType();
if (type == LayoutBuilder::FormLayout)
builder.addItem(createSubWidget<QLabel>());
builder.addItem(d->m_checkBox.data());
break;
}
case LabelPlacement::InExtraLabel:
addLabeledItem(builder, d->m_checkBox);
break;
}
d->m_checkBox->setChecked(value());
if (isAutoApply()) {
connect(d->m_checkBox.data(), &QAbstractButton::clicked,
this, [this](bool val) { setValue(val); });
}
}
QAction *BoolAspect::action()
{
if (hasAction())
return BaseAspect::action();
auto act = BaseAspect::action(); // Creates it.
act->setCheckable(true);
act->setChecked(value());
connect(act, &QAction::triggered, this, [this](bool newValue) {
// The check would be nice to have in simple conditions, but if we
// have an action that's used both on a settings page and as action
// in a menu like "Use FakeVim", isAutoApply() is false, and yet this
// here can trigger.
//QTC_CHECK(isAutoApply());
setValue(newValue);
});
return act;
}
QVariant BoolAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
QTC_ASSERT(d->m_checkBox, return {});
return d->m_checkBox->isChecked();
}
void BoolAspect::setVolatileValue(const QVariant &val)
{
QTC_CHECK(!isAutoApply());
if (d->m_checkBox)
d->m_checkBox->setChecked(val.toBool());
}
void BoolAspect::emitChangedValue()
{
emit valueChanged(value());
}
/*!
\reimp
*/
bool BoolAspect::value() const
{
return BaseAspect::value().toBool();
}
void BoolAspect::setValue(bool value)
{
if (BaseAspect::setValueQuietly(value)) {
if (d->m_checkBox)
d->m_checkBox->setChecked(value);
//qDebug() << "SetValue: Changing" << labelText() << " to " << value;
emit changed();
//QTC_CHECK(!labelText().isEmpty());
emit valueChanged(value);
//qDebug() << "SetValue: Changed" << labelText() << " to " << value;
if (hasAction()) {
//qDebug() << "SetValue: Triggering " << labelText() << "with" << value;
emit action()->triggered(value);
}
}
}
void BoolAspect::setDefaultValue(bool val)
{
BaseAspect::setDefaultValue(val);
}
void BoolAspect::setLabel(const QString &labelText, LabelPlacement labelPlacement)
{
BaseAspect::setLabelText(labelText);
d->m_labelPlacement = labelPlacement;
}
void BoolAspect::setLabelPlacement(BoolAspect::LabelPlacement labelPlacement)
{
d->m_labelPlacement = labelPlacement;
}
/*!
\class Utils::SelectionAspect
\inmodule QtCreator
\brief A selection aspect represents a specific choice out of
several.
The selection aspect is displayed using a QComboBox or
QRadioButtons in a QButtonGroup.
*/
SelectionAspect::SelectionAspect()
: d(new Internal::SelectionAspectPrivate)
{}
/*!
\reimp
*/
SelectionAspect::~SelectionAspect() = default;
/*!
\reimp
*/
void SelectionAspect::addToLayout(LayoutBuilder &builder)
{
QTC_CHECK(d->m_buttonGroup == nullptr);
QTC_CHECK(!d->m_comboBox);
QTC_ASSERT(d->m_buttons.isEmpty(), d->m_buttons.clear());
switch (d->m_displayStyle) {
case DisplayStyle::RadioButtons:
d->m_buttonGroup = new QButtonGroup();
d->m_buttonGroup->setExclusive(true);
for (int i = 0, n = d->m_options.size(); i < n; ++i) {
const Internal::SelectionAspectPrivate::Option &option = d->m_options.at(i);
auto button = new QRadioButton(option.displayName);
button->setChecked(i == value());
button->setToolTip(option.tooltip);
builder.addItems({{}, button});
d->m_buttons.append(button);
d->m_buttonGroup->addButton(button, i);
connect(button, &QAbstractButton::clicked, this, [this, i] {
setValue(i);
});
}
break;
case DisplayStyle::ComboBox:
setLabelText(displayName());
d->m_comboBox = createSubWidget<QComboBox>();
for (int i = 0, n = d->m_options.size(); i < n; ++i)
d->m_comboBox->addItem(d->m_options.at(i).displayName);
connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::activated),
this, &SelectionAspect::setValue);
d->m_comboBox->setCurrentIndex(value());
addLabeledItem(builder, d->m_comboBox);
break;
}
}
QVariant SelectionAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
switch (d->m_displayStyle) {
case DisplayStyle::RadioButtons:
QTC_ASSERT(d->m_buttonGroup, return {});
return d->m_buttonGroup->checkedId();
case DisplayStyle::ComboBox:
QTC_ASSERT(d->m_comboBox, return {});
return d->m_comboBox->currentIndex();
}
return {};
}
void SelectionAspect::setVolatileValue(const QVariant &val)
{
QTC_CHECK(!isAutoApply());
switch (d->m_displayStyle) {
case DisplayStyle::RadioButtons: {
if (d->m_buttonGroup) {
QAbstractButton *button = d->m_buttonGroup->button(val.toInt());
QTC_ASSERT(button, return);
button->setChecked(true);
}
break;
}
case DisplayStyle::ComboBox:
if (d->m_comboBox)
d->m_comboBox->setCurrentIndex(val.toInt());
break;
}
}
void SelectionAspect::setDisplayStyle(SelectionAspect::DisplayStyle style)
{
d->m_displayStyle = style;
}
int SelectionAspect::value() const
{
return BaseAspect::value().toInt();
}
void SelectionAspect::setValue(int value)
{
if (BaseAspect::setValueQuietly(value)) {
if (d->m_buttonGroup && 0 <= value && value < d->m_buttons.size())
d->m_buttons.at(value)->setChecked(true);
else if (d->m_comboBox)
d->m_comboBox->setCurrentIndex(value);
emit changed();
}
}
void SelectionAspect::setDefaultValue(int val)
{
BaseAspect::setDefaultValue(val);
}
QString SelectionAspect::stringValue() const
{
return d->m_options.at(value()).displayName;
}
void SelectionAspect::addOption(const QString &displayName, const QString &toolTip)
{
d->m_options.append({displayName, toolTip});
}
/*!
\class Utils::MultiSelectionAspect
\inmodule QtCreator
\brief A multi-selection aspect represents one or more choices out of
several.
The multi-selection aspect is displayed using a QListWidget with
checkable items.
*/
MultiSelectionAspect::MultiSelectionAspect()
: d(new Internal::MultiSelectionAspectPrivate(this))
{
setDefaultValue(QStringList());
}
/*!
\reimp
*/
MultiSelectionAspect::~MultiSelectionAspect() = default;
/*!
\reimp
*/
void MultiSelectionAspect::addToLayout(LayoutBuilder &builder)
{
QTC_CHECK(d->m_listView == nullptr);
if (d->m_allValues.isEmpty())
return;
switch (d->m_displayStyle) {
case DisplayStyle::ListView:
d->m_listView = createSubWidget<QListWidget>();
for (const QString &val : qAsConst(d->m_allValues)) {
auto item = new QListWidgetItem(val, d->m_listView);
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(value().contains(item->text()) ? Qt::Checked : Qt::Unchecked);
}
connect(d->m_listView, &QListWidget::itemChanged, this,
[this](QListWidgetItem *item) {
if (d->setValueSelectedHelper(item->text(), item->checkState() & Qt::Checked))
emit changed();
});
addLabeledItem(builder, d->m_listView);
}
}
bool Internal::MultiSelectionAspectPrivate::setValueSelectedHelper(const QString &val, bool on)
{
QStringList list = q->value();
if (on && !list.contains(val)) {
list.append(val);
q->setValue(list);
return true;
}
if (!on && list.contains(val)) {
list.removeOne(val);
q->setValue(list);
return true;
}
return false;
}
QStringList MultiSelectionAspect::allValues() const
{
return d->m_allValues;
}
void MultiSelectionAspect::setAllValues(const QStringList &val)
{
d->m_allValues = val;
}
void MultiSelectionAspect::setDisplayStyle(MultiSelectionAspect::DisplayStyle style)
{
d->m_displayStyle = style;
}
QStringList MultiSelectionAspect::value() const
{
return BaseAspect::value().toStringList();
}
void MultiSelectionAspect::setValue(const QStringList &value)
{
if (BaseAspect::setValueQuietly(value)) {
if (d->m_listView) {
const int n = d->m_listView->count();
QTC_CHECK(n == d->m_allValues.size());
for (int i = 0; i != n; ++i) {
auto item = d->m_listView->item(i);
item->setCheckState(value.contains(item->text()) ? Qt::Checked : Qt::Unchecked);
}
} else {
emit changed();
}
}
}
/*!
\class Utils::IntegerAspect
\inmodule QtCreator
\brief An integer aspect is a integral property of some object, together with
a description of its behavior for common operations like visualizing or
persisting.
The integer aspect is displayed using a \c QSpinBox.
The visual representation often contains a label in front
the display of the spin box.
*/
// IntegerAspect
IntegerAspect::IntegerAspect()
: d(new Internal::IntegerAspectPrivate)
{
setDefaultValue(qint64(0));
}
/*!
\reimp
*/
IntegerAspect::~IntegerAspect() = default;
/*!
\reimp
*/
void IntegerAspect::addToLayout(LayoutBuilder &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QSpinBox>();
d->m_spinBox->setDisplayIntegerBase(d->m_displayIntegerBase);
d->m_spinBox->setPrefix(d->m_prefix);
d->m_spinBox->setSuffix(d->m_suffix);
d->m_spinBox->setSingleStep(d->m_singleStep);
d->m_spinBox->setSpecialValueText(d->m_specialValueText);
if (d->m_maximumValue && d->m_maximumValue)
d->m_spinBox->setRange(int(d->m_minimumValue.value() / d->m_displayScaleFactor),
int(d->m_maximumValue.value() / d->m_displayScaleFactor));
d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange()
addLabeledItem(builder, d->m_spinBox);
if (isAutoApply()) {
connect(d->m_spinBox.data(), QOverload<int>::of(&QSpinBox::valueChanged),
this, [this] { setValue(d->m_spinBox->value()); });
}
}
QVariant IntegerAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
QTC_ASSERT(d->m_spinBox, return {});
return d->m_spinBox->value() * d->m_displayScaleFactor;
}
void IntegerAspect::setVolatileValue(const QVariant &val)
{
QTC_CHECK(!isAutoApply());
if (d->m_spinBox)
d->m_spinBox->setValue(int(val.toLongLong() / d->m_displayScaleFactor));
}
qint64 IntegerAspect::value() const
{
return BaseAspect::value().toLongLong();
}
void IntegerAspect::setValue(qint64 value)
{
BaseAspect::setValue(value);
}
void IntegerAspect::setRange(qint64 min, qint64 max)
{
d->m_minimumValue = min;
d->m_maximumValue = max;
}
void IntegerAspect::setLabel(const QString &label)
{
setLabelText(label);
}
void IntegerAspect::setPrefix(const QString &prefix)
{
d->m_prefix = prefix;
}
void IntegerAspect::setSuffix(const QString &suffix)
{
d->m_suffix = suffix;
}
void IntegerAspect::setDisplayIntegerBase(int base)
{
d->m_displayIntegerBase = base;
}
void IntegerAspect::setDisplayScaleFactor(qint64 factor)
{
d->m_displayScaleFactor = factor;
}
void IntegerAspect::setDefaultValue(qint64 defaultValue)
{
BaseAspect::setDefaultValue(defaultValue);
}
void IntegerAspect::setSpecialValueText(const QString &specialText)
{
d->m_specialValueText = specialText;
}
void IntegerAspect::setSingleStep(qint64 step)
{
d->m_singleStep = step;
}
/*!
\class Utils::DoubleAspect
\inmodule QtCreator
\brief An double aspect is a numerical property of some object, together with
a description of its behavior for common operations like visualizing or
persisting.
The double aspect is displayed using a \c QDoubleSpinBox.
The visual representation often contains a label in front
the display of the spin box.
*/
DoubleAspect::DoubleAspect()
: d(new Internal::DoubleAspectPrivate)
{
setDefaultValue(double(0));
}
/*!
\reimp
*/
DoubleAspect::~DoubleAspect() = default;
/*!
\reimp
*/
void DoubleAspect::addToLayout(LayoutBuilder &builder)
{
QTC_CHECK(!d->m_spinBox);
d->m_spinBox = createSubWidget<QDoubleSpinBox>();
d->m_spinBox->setPrefix(d->m_prefix);
d->m_spinBox->setSuffix(d->m_suffix);
d->m_spinBox->setSingleStep(d->m_singleStep);
d->m_spinBox->setSpecialValueText(d->m_specialValueText);
if (d->m_maximumValue && d->m_maximumValue)
d->m_spinBox->setRange(d->m_minimumValue.value(), d->m_maximumValue.value());
d->m_spinBox->setValue(value()); // Must happen after setRange()!
addLabeledItem(builder, d->m_spinBox);
if (isAutoApply()) {
connect(d->m_spinBox.data(), QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, [this] { setValue(d->m_spinBox->value()); });
}
}
QVariant DoubleAspect::volatileValue() const
{
QTC_CHECK(!isAutoApply());
QTC_ASSERT(d->m_spinBox, return {});
return d->m_spinBox->value();
}
void DoubleAspect::setVolatileValue(const QVariant &val)
{
QTC_CHECK(!isAutoApply());
if (d->m_spinBox)
d->m_spinBox->setValue(val.toDouble());
}
double DoubleAspect::value() const
{
return BaseAspect::value().toDouble();
}
void DoubleAspect::setValue(double value)
{
BaseAspect::setValue(value);
}
void DoubleAspect::setRange(double min, double max)
{
d->m_minimumValue = min;
d->m_maximumValue = max;
}
void DoubleAspect::setPrefix(const QString &prefix)
{
d->m_prefix = prefix;
}
void DoubleAspect::setSuffix(const QString &suffix)
{
d->m_suffix = suffix;
}
void DoubleAspect::setDefaultValue(double defaultValue)
{
BaseAspect::setDefaultValue(defaultValue);
}
void DoubleAspect::setSpecialValueText(const QString &specialText)
{
d->m_specialValueText = specialText;
}
void DoubleAspect::setSingleStep(double step)
{
d->m_singleStep = step;
}
/*!
\class Utils::BaseTristateAspect
\inmodule QtCreator
\brief A tristate aspect is a property of some object that can have
three values: enabled, disabled, and unspecified.
Its visual representation is a QComboBox with three items.
*/
TriStateAspect::TriStateAspect(const QString onString, const QString &offString,
const QString &defaultString)
{
setDisplayStyle(DisplayStyle::ComboBox);
setDefaultValue(TriState::Default);
addOption(onString);
addOption(offString);
addOption(defaultString);
}
TriState TriStateAspect::value() const
{
return TriState::fromVariant(BaseAspect::value());
}
void TriStateAspect::setValue(TriState value)
{
BaseAspect::setValue(value.toVariant());
}
void TriStateAspect::setDefaultValue(TriState value)
{
BaseAspect::setDefaultValue(value.toVariant());
}
const TriState TriState::Enabled{TriState::EnabledValue};
const TriState TriState::Disabled{TriState::DisabledValue};
const TriState TriState::Default{TriState::DefaultValue};
TriState TriState::fromVariant(const QVariant &variant)
{
int v = variant.toInt();
QTC_ASSERT(v == EnabledValue || v == DisabledValue || v == DefaultValue, v = DefaultValue);
return TriState(Value(v));
}
/*!
\class Utils::StringListAspect
\inmodule QtCreator
\brief A string list aspect represents a property of some object
that is a list of strings.
*/
StringListAspect::StringListAspect()
: d(new Internal::StringListAspectPrivate)
{
setDefaultValue(QStringList());
}
/*!
\reimp
*/
StringListAspect::~StringListAspect() = default;
/*!
\reimp
*/
void StringListAspect::addToLayout(LayoutBuilder &builder)
{
Q_UNUSED(builder)
// TODO - when needed.
}
QStringList StringListAspect::value() const
{
return BaseAspect::value().toStringList();
}
void StringListAspect::setValue(const QStringList &value)
{
BaseAspect::setValue(value);
}
void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
{
QStringList val = value();
if (allowDuplicates || !val.contains(s))
val.append(s);
setValue(val);
}
void StringListAspect::removeValue(const QString &s)
{
QStringList val = value();
val.removeAll(s);
setValue(val);
}
void StringListAspect::appendValues(const QStringList &values, bool allowDuplicates)
{
QStringList val = value();
for (const QString &s : values) {
if (allowDuplicates || !val.contains(s))
val.append(s);
}
setValue(val);
}
void StringListAspect::removeValues(const QStringList &values)
{
QStringList val = value();
for (const QString &s : values)
val.removeAll(s);
setValue(val);
}
/*!
\class Utils::IntegerListAspect
\inmodule QtCreator
\brief A string list aspect represents a property of some object
that is a list of strings.
*/
IntegersAspect::IntegersAspect()
{
setDefaultValue({});
}
/*!
\reimp
*/
IntegersAspect::~IntegersAspect() = default;
/*!
\reimp
*/
void IntegersAspect::addToLayout(LayoutBuilder &builder)
{
Q_UNUSED(builder)
// TODO - when needed.
}
void IntegersAspect::emitChangedValue()
{
emit valueChanged(value());
}
QList<int> IntegersAspect::value() const
{
return Utils::transform(BaseAspect::value().toList(),
[](QVariant v) { return v.toInt(); });
}
void IntegersAspect::setValue(const QList<int> &value)
{
BaseAspect::setValue(Utils::transform(value, &QVariant::fromValue<int>));
}
void IntegersAspect::setDefaultValue(const QList<int> &value)
{
BaseAspect::setDefaultValue(Utils::transform(value, &QVariant::fromValue<int>));
}
/*!
\class Utils::TextDisplay
\brief A text display is a phony aspect with the sole purpose of providing
some text display using an Utils::InfoLabel in places where otherwise
more expensive Utils::StringAspect items would be used.
A text display does not have a real value.
*/
/*!
Constructs a text display showing the \a message with an icon representing
type \a type.
*/
TextDisplay::TextDisplay(const QString &message, InfoLabel::InfoType type)
: d(new Internal::TextDisplayPrivate)
{
d->m_message = message;
d->m_type = type;
}
/*!
\reimp
*/
TextDisplay::~TextDisplay() = default;
/*!
\reimp
*/
void TextDisplay::addToLayout(LayoutBuilder &builder)
{
if (!d->m_label) {
d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
d->m_label->setElideMode(Qt::ElideNone);
d->m_label->setWordWrap(true);
// Do not use m_label->setVisible(isVisible()) unconditionally, it does not
// have a QWidget parent yet when used in a LayoutBuilder.
if (!isVisible())
d->m_label->setVisible(false);
}
builder.addItem(d->m_label.data());
}
/*!
Sets \a t as the information label type for the visual representation
of this aspect.
*/
void TextDisplay::setIconType(InfoLabel::InfoType t)
{
d->m_type = t;
if (d->m_label)
d->m_label->setType(t);
}
void TextDisplay::setText(const QString &message)
{
d->m_message = message;
}
/*!
\class Utils::AspectContainer
\inmodule QtCreator
\brief The AspectContainer class wraps one or more aspects while providing
the interface of a single aspect.
*/
namespace Internal {
class AspectContainerPrivate
{
public:
QList<BaseAspect *> m_items; // Not owned
bool m_autoApply = true;
QString m_settingsGroup;
};
} // Internal
AspectContainer::AspectContainer()
: d(new Internal::AspectContainerPrivate)
{}
/*!
\reimp
*/
AspectContainer::~AspectContainer() = default;
/*!
\internal
*/
void AspectContainer::registerAspect(BaseAspect *aspect)
{
aspect->setAutoApply(d->m_autoApply);
d->m_items.append(aspect);
}
void AspectContainer::registerAspects(const AspectContainer &aspects)
{
for (BaseAspect *aspect : qAsConst(aspects.d->m_items))
registerAspect(aspect);
}
void AspectContainer::fromMap(const QVariantMap &map)
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->fromMap(map);
}
void AspectContainer::toMap(QVariantMap &map) const
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->toMap(map);
}
void AspectContainer::readSettings(const QSettings *settings)
{
if (d->m_settingsGroup.isEmpty()) {
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->readSettings(settings);
} else {
const QString keyRoot = d->m_settingsGroup + '/';
forEachAspect([settings, keyRoot](BaseAspect *aspect) {
QString key = aspect->settingsKey();
const QVariant value = settings->value(keyRoot + key, aspect->defaultValue());
aspect->setValue(value);
});
}
}
void AspectContainer::writeSettings(QSettings *settings) const
{
if (d->m_settingsGroup.isEmpty()) {
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->writeSettings(settings);
} else {
settings->remove(d->m_settingsGroup);
settings->beginGroup(d->m_settingsGroup);
forEachAspect([settings](BaseAspect *aspect) {
QtcSettings::setValueWithDefault(settings, aspect->settingsKey(),
aspect->value(), aspect->defaultValue());
});
settings->endGroup();
}
}
void AspectContainer::setSettingsGroup(const QString &key)
{
d->m_settingsGroup = key;
}
void AspectContainer::apply()
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->apply();
}
void AspectContainer::cancel()
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->cancel();
}
void AspectContainer::finish()
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->finish();
}
void AspectContainer::reset()
{
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->setValueQuietly(aspect->defaultValue());
}
void AspectContainer::setAutoApply(bool on)
{
d->m_autoApply = on;
for (BaseAspect *aspect : qAsConst(d->m_items))
aspect->setAutoApply(on);
}
bool AspectContainer::isDirty() const
{
for (BaseAspect *aspect : qAsConst(d->m_items)) {
if (aspect->isDirty())
return true;
}
return false;
}
bool AspectContainer::equals(const AspectContainer &other) const
{
// FIXME: Expensive, but should not really be needed in a fully aspectified world.
QVariantMap thisMap, thatMap;
toMap(thisMap);
other.toMap(thatMap);
return thisMap == thatMap;
}
void AspectContainer::copyFrom(const AspectContainer &other)
{
QVariantMap map;
other.toMap(map);
fromMap(map);
}
void AspectContainer::forEachAspect(const std::function<void(BaseAspect *)> &run) const
{
for (BaseAspect *aspect : qAsConst(d->m_items)) {
if (auto container = dynamic_cast<AspectContainer *>(aspect))
container->forEachAspect(run);
else
run(aspect);
}
}
} // namespace Utils