1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "aspects.h"
27 
28 #include "algorithm.h"
29 #include "fancylineedit.h"
30 #include "layoutbuilder.h"
31 #include "pathchooser.h"
32 #include "qtcassert.h"
33 #include "qtcprocess.h"
34 #include "qtcsettings.h"
35 #include "utilsicons.h"
36 #include "variablechooser.h"
37 
38 #include <QAction>
39 #include <QButtonGroup>
40 #include <QCheckBox>
41 #include <QComboBox>
42 #include <QDebug>
43 #include <QFormLayout>
44 #include <QGroupBox>
45 #include <QLabel>
46 #include <QLineEdit>
47 #include <QListWidget>
48 #include <QPointer>
49 #include <QPushButton>
50 #include <QRadioButton>
51 #include <QSettings>
52 #include <QSpinBox>
53 #include <QTextEdit>
54 #include <QToolButton>
55 
56 namespace Utils {
57 namespace Internal {
58 
59 class BaseAspectPrivate
60 {
61 public:
62     Utils::Id m_id;
63     QVariant m_value;
64     QVariant m_defaultValue;
65     std::function<QVariant(const QVariant &)> m_toSettings;
66     std::function<QVariant(const QVariant &)> m_fromSettings;
67 
68     QString m_displayName;
69     QString m_settingsKey; // Name of data in settings.
70     QString m_tooltip;
71     QString m_labelText;
72     QPixmap m_labelPixmap;
73     QIcon m_icon;
74     QPointer<QLabel> m_label; // Owned by configuration widget
75     QPointer<QAction> m_action; // Owned by us.
76 
77     bool m_visible = true;
78     bool m_enabled = true;
79     bool m_readOnly = true;
80     bool m_autoApply = true;
81     int m_spanX = 1;
82     int m_spanY = 1;
83     BaseAspect::ConfigWidgetCreator m_configWidgetCreator;
84     QList<QPointer<QWidget>> m_subWidgets;
85 };
86 
87 } // Internal
88 
89 /*!
90     \class Utils::BaseAspect
91     \inmodule QtCreator
92 
93     \brief The \c BaseAspect class provides a common base for classes implementing
94     aspects.
95 
96     An aspect is a hunk of data like a property or collection of related
97     properties of some object, together with a description of its behavior
98     for common operations like visualizing or persisting.
99 
100     Simple aspects are for example a boolean property represented by a QCheckBox
101     in the user interface, or a string property represented by a PathChooser,
102     selecting directories in the filesystem.
103 
104     While aspects implementations usually have the ability to visualize and to persist
105     their data, or use an ID, neither of these is mandatory.
106 */
107 
108 /*!
109     Constructs a BaseAspect.
110 */
BaseAspect()111 BaseAspect::BaseAspect()
112     : d(new Internal::BaseAspectPrivate)
113 {}
114 
115 /*!
116     Destructs a BaseAspect.
117 */
~BaseAspect()118 BaseAspect::~BaseAspect()
119 {
120     delete d->m_action;
121 }
122 
id() const123 Id BaseAspect::id() const
124 {
125     return d->m_id;
126 }
127 
setId(Id id)128 void BaseAspect::setId(Id id)
129 {
130     d->m_id = id;
131 }
132 
value() const133 QVariant BaseAspect::value() const
134 {
135     return d->m_value;
136 }
137 
138 /*!
139     Sets value.
140 
141     Emits changed() if the value changed.
142 */
setValue(const QVariant & value)143 void BaseAspect::setValue(const QVariant &value)
144 {
145     if (setValueQuietly(value)) {
146         emit changed();
147         emitChangedValue();
148     }
149 }
150 
151 /*!
152     Sets value without emitting changed()
153 
154     Returns whether the value changed.
155 */
setValueQuietly(const QVariant & value)156 bool BaseAspect::setValueQuietly(const QVariant &value)
157 {
158     if (d->m_value == value)
159         return false;
160     d->m_value = value;
161     return true;
162 }
163 
defaultValue() const164 QVariant BaseAspect::defaultValue() const
165 {
166     return d->m_defaultValue;
167 }
168 
169 /*!
170     Sets a default value and the current value for this aspect.
171 
172     \note The current value will be set silently to the same value.
173     It is reasonable to only set default values in the setup phase
174     of the aspect.
175 
176     Default values will not be stored in settings.
177 */
setDefaultValue(const QVariant & value)178 void BaseAspect::setDefaultValue(const QVariant &value)
179 {
180     d->m_defaultValue = value;
181     d->m_value = value;
182 }
183 
setDisplayName(const QString & displayName)184 void BaseAspect::setDisplayName(const QString &displayName)
185 {
186     d->m_displayName = displayName;
187 }
188 
isVisible() const189 bool BaseAspect::isVisible() const
190 {
191     return d->m_visible;
192 }
193 
194 /*!
195     Shows or hides the visual representation of this aspect depending
196     on the value of \a visible.
197     By default, it is visible.
198  */
setVisible(bool visible)199 void BaseAspect::setVisible(bool visible)
200 {
201     d->m_visible = visible;
202     for (QWidget *w : qAsConst(d->m_subWidgets)) {
203         QTC_ASSERT(w, continue);
204         // This may happen during layout building. Explicit setting visibility here
205         // may create a show a toplevel widget for a moment until it is parented
206         // to some non-shown widget.
207         if (visible && w->parentWidget())
208             w->setVisible(visible);
209     }
210 }
211 
setupLabel()212 void BaseAspect::setupLabel()
213 {
214     QTC_ASSERT(!d->m_label, delete d->m_label);
215     if (d->m_labelText.isEmpty() && d->m_labelPixmap.isNull())
216         return;
217     d->m_label = new QLabel(d->m_labelText);
218     d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
219     if (!d->m_labelPixmap.isNull())
220         d->m_label->setPixmap(d->m_labelPixmap);
221     registerSubWidget(d->m_label);
222 }
223 
addLabeledItem(LayoutBuilder & builder,QWidget * widget)224 void BaseAspect::addLabeledItem(LayoutBuilder &builder, QWidget *widget)
225 {
226     setupLabel();
227     if (QLabel *l = label()) {
228         l->setBuddy(widget);
229         builder.addItem(l);
230         LayoutBuilder::LayoutItem item(widget);
231         item.span = std::max(d->m_spanX - 1, 1);
232         builder.addItem(item);
233     } else {
234         builder.addItem(LayoutBuilder::LayoutItem(widget));
235     }
236 }
237 
238 /*!
239     Sets \a labelText as text for the separate label in the visual
240     representation of this aspect.
241 */
setLabelText(const QString & labelText)242 void BaseAspect::setLabelText(const QString &labelText)
243 {
244     d->m_labelText = labelText;
245     if (d->m_label)
246         d->m_label->setText(labelText);
247 }
248 
249 /*!
250     Sets \a labelPixmap as pixmap for the separate label in the visual
251     representation of this aspect.
252 */
setLabelPixmap(const QPixmap & labelPixmap)253 void BaseAspect::setLabelPixmap(const QPixmap &labelPixmap)
254 {
255     d->m_labelPixmap = labelPixmap;
256     if (d->m_label)
257         d->m_label->setPixmap(labelPixmap);
258 }
259 
setIcon(const QIcon & icon)260 void BaseAspect::setIcon(const QIcon &icon)
261 {
262     d->m_icon = icon;
263     if (d->m_action)
264         d->m_action->setIcon(icon);
265 }
266 
267 /*!
268     Returns the current text for the separate label in the visual
269     representation of this aspect.
270 */
labelText() const271 QString BaseAspect::labelText() const
272 {
273     return d->m_labelText;
274 }
275 
label() const276 QLabel *BaseAspect::label() const
277 {
278     return d->m_label.data();
279 }
280 
toolTip() const281 QString BaseAspect::toolTip() const
282 {
283     return d->m_tooltip;
284 }
285 
286 /*!
287     Sets \a tooltip as tool tip for the visual representation of this aspect.
288  */
setToolTip(const QString & tooltip)289 void BaseAspect::setToolTip(const QString &tooltip)
290 {
291     d->m_tooltip = tooltip;
292     for (QWidget *w : qAsConst(d->m_subWidgets)) {
293         QTC_ASSERT(w, continue);
294         w->setToolTip(tooltip);
295     }
296 }
297 
isEnabled() const298 bool BaseAspect::isEnabled() const
299 {
300     return d->m_enabled;
301 }
302 
setEnabled(bool enabled)303 void BaseAspect::setEnabled(bool enabled)
304 {
305     d->m_enabled = enabled;
306     for (QWidget *w : qAsConst(d->m_subWidgets)) {
307         QTC_ASSERT(w, continue);
308         w->setEnabled(enabled);
309     }
310 }
311 
312 /*!
313     Makes the enabled state of this aspect depend on the checked state of \a checker.
314 */
setEnabler(BoolAspect * checker)315 void BaseAspect::setEnabler(BoolAspect *checker)
316 {
317     QTC_ASSERT(checker, return);
318     setEnabled(checker->value());
319     connect(checker, &BoolAspect::volatileValueChanged, this, &BaseAspect::setEnabled);
320     connect(checker, &BoolAspect::valueChanged, this, &BaseAspect::setEnabled);
321 }
322 
isReadOnly() const323 bool BaseAspect::isReadOnly() const
324 {
325     return d->m_readOnly;
326 }
327 
setReadOnly(bool readOnly)328 void BaseAspect::setReadOnly(bool readOnly)
329 {
330     d->m_readOnly = readOnly;
331     for (QWidget *w : qAsConst(d->m_subWidgets)) {
332         QTC_ASSERT(w, continue);
333         if (auto lineEdit = qobject_cast<QLineEdit *>(w))
334             lineEdit->setReadOnly(readOnly);
335         else if (auto textEdit = qobject_cast<QTextEdit *>(w))
336             textEdit->setReadOnly(readOnly);
337     }
338 }
339 
setSpan(int x,int y)340 void BaseAspect::setSpan(int x, int y)
341 {
342     d->m_spanX = x;
343     d->m_spanY = y;
344 }
345 
isAutoApply() const346 bool BaseAspect::isAutoApply() const
347 {
348     return d->m_autoApply;
349 }
350 
351 /*!
352     Sets auto-apply mode. When auto-apply mode is on, user interaction to this
353     aspect's widget will not modify the \c value of the aspect until \c apply()
354     is called programmatically.
355 
356     \sa setSettingsKey()
357 */
358 
setAutoApply(bool on)359 void BaseAspect::setAutoApply(bool on)
360 {
361     d->m_autoApply = on;
362 }
363 
364 /*!
365     \internal
366 */
setConfigWidgetCreator(const ConfigWidgetCreator & configWidgetCreator)367 void BaseAspect::setConfigWidgetCreator(const ConfigWidgetCreator &configWidgetCreator)
368 {
369     d->m_configWidgetCreator = configWidgetCreator;
370 }
371 
372 /*!
373     Returns the key to be used when accessing the settings.
374 
375     \sa setSettingsKey()
376 */
settingsKey() const377 QString BaseAspect::settingsKey() const
378 {
379     return d->m_settingsKey;
380 }
381 
382 /*!
383     Sets the key to be used when accessing the settings.
384 
385     \sa settingsKey()
386 */
setSettingsKey(const QString & key)387 void BaseAspect::setSettingsKey(const QString &key)
388 {
389     d->m_settingsKey = key;
390 }
391 
392 /*!
393     Sets the key and group to be used when accessing the settings.
394 
395     \sa settingsKey()
396 */
setSettingsKey(const QString & group,const QString & key)397 void BaseAspect::setSettingsKey(const QString &group, const QString &key)
398 {
399     d->m_settingsKey = group + "/" + key;
400 }
401 
402 /*!
403     Returns the string that should be used when this action appears in menus
404     or other places that are typically used with Book style capitalization.
405 
406     If no display name is set, the label text will be used as fallback.
407 */
408 
displayName() const409 QString BaseAspect::displayName() const
410 {
411     return d->m_displayName.isEmpty() ? d->m_labelText : d->m_displayName;
412 }
413 
414 /*!
415     \internal
416 */
createConfigWidget() const417 QWidget *BaseAspect::createConfigWidget() const
418 {
419     return d->m_configWidgetCreator ? d->m_configWidgetCreator() : nullptr;
420 }
421 
action()422 QAction *BaseAspect::action()
423 {
424     if (!d->m_action) {
425         d->m_action = new QAction(labelText());
426         d->m_action->setIcon(d->m_icon);
427     }
428     return d->m_action;
429 }
430 
431 /*!
432     Adds the visual representation of this aspect to a layout using
433     a layout builder.
434 */
addToLayout(LayoutBuilder &)435 void BaseAspect::addToLayout(LayoutBuilder &)
436 {
437 }
438 
439 /*!
440     Updates this aspect's value from user-initiated changes in the widget.
441 
442     This has only an effect if \c isAutoApply is false.
443 */
apply()444 void BaseAspect::apply()
445 {
446     QTC_CHECK(!d->m_autoApply);
447     if (isDirty())
448         setValue(volatileValue());
449 }
450 
451 /*!
452     Discard user changes in the widget and restore widget contents from
453     aspect's value.
454 
455     This has only an effect if \c isAutoApply is false.
456 */
cancel()457 void BaseAspect::cancel()
458 {
459     QTC_CHECK(!d->m_autoApply);
460     if (!d->m_subWidgets.isEmpty())
461         setVolatileValue(d->m_value);
462 }
463 
finish()464 void BaseAspect::finish()
465 {
466     // No qDeleteAll() possible as long as the connect in registerSubWidget() exist.
467     while (d->m_subWidgets.size())
468         delete d->m_subWidgets.takeLast();
469 }
470 
hasAction() const471 bool BaseAspect::hasAction() const
472 {
473     return d->m_action != nullptr;
474 }
475 
isDirty() const476 bool BaseAspect::isDirty() const
477 {
478     QTC_CHECK(!isAutoApply());
479     // Aspects that were never shown cannot contain unsaved user changes.
480     if (d->m_subWidgets.isEmpty())
481         return false;
482     return volatileValue() != d->m_value;
483 }
484 
volatileValue() const485 QVariant BaseAspect::volatileValue() const
486 {
487     QTC_CHECK(!isAutoApply());
488     return {};
489 }
490 
setVolatileValue(const QVariant & val)491 void BaseAspect::setVolatileValue(const QVariant &val)
492 {
493     Q_UNUSED(val);
494 }
495 
registerSubWidget(QWidget * widget)496 void BaseAspect::registerSubWidget(QWidget *widget)
497 {
498     d->m_subWidgets.append(widget);
499 
500     // FIXME: This interferes with qDeleteAll() in finish() and destructor,
501     // it would not be needed when all users actually deleted their subwidgets,
502     // e.g. the SettingsPage::finish() base implementation, but this still
503     // leaves the cases where no such base functionality is available, e.g.
504     // in the run/build config aspects.
505     connect(widget, &QObject::destroyed, this, [this, widget] {
506         d->m_subWidgets.removeAll(widget);
507     });
508 
509     widget->setEnabled(d->m_enabled);
510     widget->setToolTip(d->m_tooltip);
511 
512     // Visible is on by default. Not setting it explicitly avoid popping
513     // it up when the parent is not set yet, the normal case.
514     if (!d->m_visible)
515         widget->setVisible(d->m_visible);
516 }
517 
saveToMap(QVariantMap & data,const QVariant & value,const QVariant & defaultValue,const QString & key)518 void BaseAspect::saveToMap(QVariantMap &data, const QVariant &value,
519                            const QVariant &defaultValue, const QString &key)
520 {
521     if (key.isEmpty())
522         return;
523     if (value == defaultValue)
524         data.remove(key);
525     else
526         data.insert(key, value);
527 }
528 
529 /*!
530     Retrieves the internal value of this BaseAspect from a \c QVariantMap.
531 */
fromMap(const QVariantMap & map)532 void BaseAspect::fromMap(const QVariantMap &map)
533 {
534     const QVariant val = map.value(settingsKey(), toSettingsValue(defaultValue()));
535     setValue(fromSettingsValue(val));
536 }
537 
538 /*!
539     Stores the internal value of this BaseAspect into a \c QVariantMap.
540 */
toMap(QVariantMap & map) const541 void BaseAspect::toMap(QVariantMap &map) const
542 {
543     saveToMap(map, toSettingsValue(d->m_value), toSettingsValue(d->m_defaultValue), settingsKey());
544 }
545 
readSettings(const QSettings * settings)546 void BaseAspect::readSettings(const QSettings *settings)
547 {
548     if (settingsKey().isEmpty())
549         return;
550     const QVariant &val = settings->value(settingsKey());
551     setValue(val.isValid() ? fromSettingsValue(val) : defaultValue());
552 }
553 
writeSettings(QSettings * settings) const554 void BaseAspect::writeSettings(QSettings *settings) const
555 {
556     if (settingsKey().isEmpty())
557         return;
558     QtcSettings::setValueWithDefault(settings,
559                                      settingsKey(),
560                                      toSettingsValue(value()),
561                                      toSettingsValue(defaultValue()));
562 }
563 
setFromSettingsTransformation(const SavedValueTransformation & transform)564 void BaseAspect::setFromSettingsTransformation(const SavedValueTransformation &transform)
565 {
566     d->m_fromSettings = transform;
567 }
568 
setToSettingsTransformation(const SavedValueTransformation & transform)569 void BaseAspect::setToSettingsTransformation(const SavedValueTransformation &transform)
570 {
571     d->m_toSettings = transform;
572 }
573 
toSettingsValue(const QVariant & val) const574 QVariant BaseAspect::toSettingsValue(const QVariant &val) const
575 {
576     return d->m_toSettings ? d->m_toSettings(val) : val;
577 }
578 
fromSettingsValue(const QVariant & val) const579 QVariant BaseAspect::fromSettingsValue(const QVariant &val) const
580 {
581     return d->m_fromSettings ? d->m_fromSettings(val) : val;
582 }
583 
584 /*!
585     \internal
586 */
acquaintSiblings(const AspectContainer &)587 void BaseAspect::acquaintSiblings(const AspectContainer &)
588 {}
589 
590 namespace Internal {
591 
592 class BoolAspectPrivate
593 {
594 public:
595     BoolAspect::LabelPlacement m_labelPlacement = BoolAspect::LabelPlacement::AtCheckBox;
596     QPointer<QCheckBox> m_checkBox; // Owned by configuration widget
597     QPointer<QGroupBox> m_groupBox; // For BoolAspects handling GroupBox check boxes
598 };
599 
600 class SelectionAspectPrivate
601 {
602 public:
~SelectionAspectPrivate()603     ~SelectionAspectPrivate() { delete m_buttonGroup; }
604 
605     SelectionAspect::DisplayStyle m_displayStyle
606             = SelectionAspect::DisplayStyle::RadioButtons;
607     QVector<SelectionAspect::Option> m_options;
608 
609     // These are all owned by the configuration widget.
610     QList<QPointer<QRadioButton>> m_buttons;
611     QPointer<QComboBox> m_comboBox;
612     QPointer<QButtonGroup> m_buttonGroup;
613 };
614 
615 class MultiSelectionAspectPrivate
616 {
617 public:
MultiSelectionAspectPrivate(MultiSelectionAspect * q)618     explicit MultiSelectionAspectPrivate(MultiSelectionAspect *q) : q(q) {}
619 
620     bool setValueSelectedHelper(const QString &value, bool on);
621 
622     MultiSelectionAspect *q;
623     QStringList m_allValues;
624     MultiSelectionAspect::DisplayStyle m_displayStyle
625         = MultiSelectionAspect::DisplayStyle::ListView;
626 
627     // These are all owned by the configuration widget.
628     QPointer<QListWidget> m_listView;
629 };
630 
631 class StringAspectPrivate
632 {
633 public:
634     StringAspect::DisplayStyle m_displayStyle = StringAspect::LabelDisplay;
635     StringAspect::CheckBoxPlacement m_checkBoxPlacement
636         = StringAspect::CheckBoxPlacement::Right;
637     StringAspect::UncheckedSemantics m_uncheckedSemantics
638         = StringAspect::UncheckedSemantics::Disabled;
639     std::function<QString(const QString &)> m_displayFilter;
640     std::unique_ptr<BoolAspect> m_checker;
641 
642     QString m_placeHolderText;
643     QString m_historyCompleterKey;
644     PathChooser::Kind m_expectedKind = PathChooser::File;
645     Environment m_environment;
646     QPointer<QLabel> m_labelDisplay;
647     QPointer<FancyLineEdit> m_lineEditDisplay;
648     QPointer<PathChooser> m_pathChooserDisplay;
649     QPointer<QTextEdit> m_textEditDisplay;
650     MacroExpanderProvider m_expanderProvider;
651     FilePath m_baseFileName;
652     StringAspect::ValueAcceptor m_valueAcceptor;
653     FancyLineEdit::ValidationFunction m_validator;
654     std::function<void()> m_openTerminal;
655 
656     bool m_undoRedoEnabled = true;
657     bool m_acceptRichText = false;
658     bool m_showToolTipOnLabel = false;
659     bool m_fileDialogOnly = false;
660     bool m_useResetButton = false;
661     bool m_autoApplyOnEditingFinished = false;
662     // Used to block recursive editingFinished signals for example when return is pressed, and
663     // the validation changes focus by opening a dialog
664     bool m_blockAutoApply = false;
665 
updateWidgetFromCheckStatus(StringAspect * aspect,Widget * w)666     template<class Widget> void updateWidgetFromCheckStatus(StringAspect *aspect, Widget *w)
667     {
668         const bool enabled = !m_checker || m_checker->value();
669         if (m_uncheckedSemantics == StringAspect::UncheckedSemantics::Disabled)
670             w->setEnabled(enabled && aspect->isEnabled());
671         else
672             w->setReadOnly(!enabled || aspect->isReadOnly());
673     }
674 };
675 
676 class IntegerAspectPrivate
677 {
678 public:
679     Utils::optional<qint64> m_minimumValue;
680     Utils::optional<qint64> m_maximumValue;
681     int m_displayIntegerBase = 10;
682     qint64 m_displayScaleFactor = 1;
683     QString m_prefix;
684     QString m_suffix;
685     QString m_specialValueText;
686     int m_singleStep = 1;
687     QPointer<QSpinBox> m_spinBox; // Owned by configuration widget
688 };
689 
690 class DoubleAspectPrivate
691 {
692 public:
693     Utils::optional<double> m_minimumValue;
694     Utils::optional<double> m_maximumValue;
695     QString m_prefix;
696     QString m_suffix;
697     QString m_specialValueText;
698     double m_singleStep = 1;
699     QPointer<QDoubleSpinBox> m_spinBox; // Owned by configuration widget
700 };
701 
702 class StringListAspectPrivate
703 {
704 public:
705 };
706 
707 class TextDisplayPrivate
708 {
709 public:
710     QString m_message;
711     Utils::InfoLabel::InfoType m_type;
712     QPointer<InfoLabel> m_label;
713 };
714 
715 } // Internal
716 
717 /*!
718     \enum Utils::StringAspect::DisplayStyle
719     \inmodule QtCreator
720 
721     The DisplayStyle enum describes the main visual characteristics of a
722     string aspect.
723 
724       \value LabelDisplay
725              Based on QLabel, used for text that cannot be changed by the
726              user in this place, for example names of executables that are
727              defined in the build system.
728 
729       \value LineEditDisplay
730              Based on QLineEdit, used for user-editable strings that usually
731              fit on a line.
732 
733       \value TextEditDisplay
734              Based on QTextEdit, used for user-editable strings that often
735              do not fit on a line.
736 
737       \value PathChooserDisplay
738              Based on Utils::PathChooser.
739 
740     \sa Utils::PathChooser
741 */
742 
743 /*!
744     \class Utils::StringAspect
745     \inmodule QtCreator
746 
747     \brief A string aspect is a string-like property of some object, together with
748     a description of its behavior for common operations like visualizing or
749     persisting.
750 
751     String aspects can represent for example a parameter for an external commands,
752     paths in a file system, or simply strings.
753 
754     The string can be displayed using a QLabel, QLineEdit, QTextEdit or
755     Utils::PathChooser.
756 
757     The visual representation often contains a label in front of the display
758     of the actual value.
759 */
760 
761 /*!
762     Constructs a StringAspect.
763  */
764 
StringAspect()765 StringAspect::StringAspect()
766     : d(new Internal::StringAspectPrivate)
767 {
768     setDefaultValue(QString());
769     setSpan(2, 1); // Default: Label + something
770 }
771 
772 /*!
773     \internal
774 */
775 StringAspect::~StringAspect() = default;
776 
777 /*!
778     \internal
779 */
setValueAcceptor(StringAspect::ValueAcceptor && acceptor)780 void StringAspect::setValueAcceptor(StringAspect::ValueAcceptor &&acceptor)
781 {
782     d->m_valueAcceptor = std::move(acceptor);
783 }
784 
785 /*!
786     Returns the value of this StringAspect as an ordinary \c QString.
787 */
value() const788 QString StringAspect::value() const
789 {
790     return BaseAspect::value().toString();
791 }
792 
793 /*!
794     Sets the \a value of this StringAspect from an ordinary \c QString.
795 */
setValue(const QString & val)796 void StringAspect::setValue(const QString &val)
797 {
798     const bool isSame = val == value();
799     if (isSame)
800         return;
801 
802     QString processedValue = val;
803     if (d->m_valueAcceptor) {
804         const Utils::optional<QString> tmp = d->m_valueAcceptor(value(), val);
805         if (!tmp) {
806             update(); // Make sure the original value is retained in the UI
807             return;
808         }
809         processedValue = tmp.value();
810     }
811 
812     if (BaseAspect::setValueQuietly(QVariant(processedValue))) {
813         update();
814         emit changed();
815         emit valueChanged(processedValue);
816     }
817 }
818 
setDefaultValue(const QString & val)819 void StringAspect::setDefaultValue(const QString &val)
820 {
821     BaseAspect::setDefaultValue(val);
822 }
823 
824 /*!
825     \reimp
826 */
fromMap(const QVariantMap & map)827 void StringAspect::fromMap(const QVariantMap &map)
828 {
829     if (!settingsKey().isEmpty())
830         BaseAspect::setValueQuietly(map.value(settingsKey(), defaultValue()));
831     if (d->m_checker)
832         d->m_checker->fromMap(map);
833 }
834 
835 /*!
836     \reimp
837 */
toMap(QVariantMap & map) const838 void StringAspect::toMap(QVariantMap &map) const
839 {
840     saveToMap(map, value(), defaultValue(), settingsKey());
841     if (d->m_checker)
842         d->m_checker->toMap(map);
843 }
844 
845 /*!
846     Returns the value of this string aspect as \c Utils::FilePath.
847 
848     \note This simply uses \c FilePath::fromUserInput() for the
849     conversion. It does not use any check that the value is actually
850     a valid file path.
851 */
filePath() const852 FilePath StringAspect::filePath() const
853 {
854     return FilePath::fromUserInput(value());
855 }
856 
857 /*!
858     Sets the value of this string aspect to \a value.
859 
860     \note This simply uses \c FilePath::toUserOutput() for the
861     conversion. It does not use any check that the value is actually
862     a file path.
863 */
setFilePath(const FilePath & value)864 void StringAspect::setFilePath(const FilePath &value)
865 {
866     setValue(value.toUserOutput());
867 }
868 
pathChooser() const869 PathChooser *StringAspect::pathChooser() const
870 {
871     return d->m_pathChooserDisplay.data();
872 }
873 
874 /*!
875     \internal
876 */
setShowToolTipOnLabel(bool show)877 void StringAspect::setShowToolTipOnLabel(bool show)
878 {
879     d->m_showToolTipOnLabel = show;
880     update();
881 }
882 
883 /*!
884     Sets a \a displayFilter for fine-tuning the visual appearance
885     of the value of this string aspect.
886 */
setDisplayFilter(const std::function<QString (const QString &)> & displayFilter)887 void StringAspect::setDisplayFilter(const std::function<QString(const QString &)> &displayFilter)
888 {
889     d->m_displayFilter = displayFilter;
890 }
891 
892 /*!
893     Returns the check box value.
894 
895     \sa makeCheckable(), setChecked()
896 */
isChecked() const897 bool StringAspect::isChecked() const
898 {
899     return !d->m_checker || d->m_checker->value();
900 }
901 
902 /*!
903     Sets the check box of this aspect to \a checked.
904 
905     \sa makeCheckable(), isChecked()
906 */
setChecked(bool checked)907 void StringAspect::setChecked(bool checked)
908 {
909     QTC_ASSERT(d->m_checker, return);
910     d->m_checker->setValue(checked);
911 }
912 
913 /*!
914     Selects the main display characteristics of the aspect according to
915     \a displayStyle.
916 
917     \note Not all StringAspect features are available with all display styles.
918 
919     \sa Utils::StringAspect::DisplayStyle
920 */
setDisplayStyle(DisplayStyle displayStyle)921 void StringAspect::setDisplayStyle(DisplayStyle displayStyle)
922 {
923     d->m_displayStyle = displayStyle;
924 }
925 
926 /*!
927     Sets \a placeHolderText as place holder for line and text displays.
928 */
setPlaceHolderText(const QString & placeHolderText)929 void StringAspect::setPlaceHolderText(const QString &placeHolderText)
930 {
931     d->m_placeHolderText = placeHolderText;
932     if (d->m_lineEditDisplay)
933         d->m_lineEditDisplay->setPlaceholderText(placeHolderText);
934     if (d->m_textEditDisplay)
935         d->m_textEditDisplay->setPlaceholderText(placeHolderText);
936 }
937 
938 /*!
939     Sets \a historyCompleterKey as key for the history completer settings for
940     line edits and path chooser displays.
941 
942     \sa Utils::PathChooser::setExpectedKind()
943 */
setHistoryCompleter(const QString & historyCompleterKey)944 void StringAspect::setHistoryCompleter(const QString &historyCompleterKey)
945 {
946     d->m_historyCompleterKey = historyCompleterKey;
947     if (d->m_lineEditDisplay)
948         d->m_lineEditDisplay->setHistoryCompleter(historyCompleterKey);
949     if (d->m_pathChooserDisplay)
950         d->m_pathChooserDisplay->setHistoryCompleter(historyCompleterKey);
951 }
952 
953 /*!
954   Sets \a expectedKind as expected kind for path chooser displays.
955 
956   \sa Utils::PathChooser::setExpectedKind()
957 */
setExpectedKind(const PathChooser::Kind expectedKind)958 void StringAspect::setExpectedKind(const PathChooser::Kind expectedKind)
959 {
960     d->m_expectedKind = expectedKind;
961     if (d->m_pathChooserDisplay)
962         d->m_pathChooserDisplay->setExpectedKind(expectedKind);
963 }
964 
setEnvironment(const Environment & env)965 void StringAspect::setEnvironment(const Environment &env)
966 {
967     d->m_environment = env;
968     if (d->m_pathChooserDisplay)
969         d->m_pathChooserDisplay->setEnvironment(env);
970 }
971 
setBaseFileName(const FilePath & baseFileName)972 void StringAspect::setBaseFileName(const FilePath &baseFileName)
973 {
974     d->m_baseFileName = baseFileName;
975     if (d->m_pathChooserDisplay)
976         d->m_pathChooserDisplay->setBaseDirectory(baseFileName);
977 }
978 
setUndoRedoEnabled(bool undoRedoEnabled)979 void StringAspect::setUndoRedoEnabled(bool undoRedoEnabled)
980 {
981     d->m_undoRedoEnabled = undoRedoEnabled;
982     if (d->m_textEditDisplay)
983         d->m_textEditDisplay->setUndoRedoEnabled(undoRedoEnabled);
984 }
985 
setAcceptRichText(bool acceptRichText)986 void StringAspect::setAcceptRichText(bool acceptRichText)
987 {
988     d->m_acceptRichText = acceptRichText;
989     if (d->m_textEditDisplay)
990         d->m_textEditDisplay->setAcceptRichText(acceptRichText);
991 }
992 
setMacroExpanderProvider(const MacroExpanderProvider & expanderProvider)993 void StringAspect::setMacroExpanderProvider(const MacroExpanderProvider &expanderProvider)
994 {
995     d->m_expanderProvider = expanderProvider;
996 }
997 
setUseGlobalMacroExpander()998 void StringAspect::setUseGlobalMacroExpander()
999 {
1000     d->m_expanderProvider = &globalMacroExpander;
1001 }
1002 
setUseResetButton()1003 void StringAspect::setUseResetButton()
1004 {
1005     d->m_useResetButton = true;
1006 }
1007 
setValidationFunction(const FancyLineEdit::ValidationFunction & validator)1008 void StringAspect::setValidationFunction(const FancyLineEdit::ValidationFunction &validator)
1009 {
1010     d->m_validator = validator;
1011     if (d->m_lineEditDisplay)
1012         d->m_lineEditDisplay->setValidationFunction(d->m_validator);
1013     else if (d->m_pathChooserDisplay)
1014         d->m_pathChooserDisplay->setValidationFunction(d->m_validator);
1015 }
1016 
setOpenTerminalHandler(const std::function<void ()> & openTerminal)1017 void StringAspect::setOpenTerminalHandler(const std::function<void ()> &openTerminal)
1018 {
1019     d->m_openTerminal = openTerminal;
1020     if (d->m_pathChooserDisplay)
1021         d->m_pathChooserDisplay->setOpenTerminalHandler(openTerminal);
1022 }
1023 
setAutoApplyOnEditingFinished(bool applyOnEditingFinished)1024 void StringAspect::setAutoApplyOnEditingFinished(bool applyOnEditingFinished)
1025 {
1026     d->m_autoApplyOnEditingFinished = applyOnEditingFinished;
1027 }
1028 
validateInput()1029 void StringAspect::validateInput()
1030 {
1031     if (d->m_pathChooserDisplay)
1032         d->m_pathChooserDisplay->triggerChanged();
1033     if (d->m_lineEditDisplay)
1034         d->m_lineEditDisplay->validate();
1035 }
1036 
setUncheckedSemantics(StringAspect::UncheckedSemantics semantics)1037 void StringAspect::setUncheckedSemantics(StringAspect::UncheckedSemantics semantics)
1038 {
1039     d->m_uncheckedSemantics = semantics;
1040 }
1041 
addToLayout(LayoutBuilder & builder)1042 void StringAspect::addToLayout(LayoutBuilder &builder)
1043 {
1044     if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Top) {
1045         d->m_checker->addToLayout(builder);
1046         builder.finishRow();
1047     }
1048 
1049     const auto useMacroExpander = [this](QWidget *w) {
1050         if (!d->m_expanderProvider)
1051             return;
1052         const auto chooser = new VariableChooser(w);
1053         chooser->addSupportedWidget(w);
1054         chooser->addMacroExpanderProvider(d->m_expanderProvider);
1055     };
1056 
1057     const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
1058 
1059     switch (d->m_displayStyle) {
1060     case PathChooserDisplay:
1061         d->m_pathChooserDisplay = createSubWidget<PathChooser>();
1062         d->m_pathChooserDisplay->setExpectedKind(d->m_expectedKind);
1063         if (!d->m_historyCompleterKey.isEmpty())
1064             d->m_pathChooserDisplay->setHistoryCompleter(d->m_historyCompleterKey);
1065         if (d->m_validator)
1066             d->m_pathChooserDisplay->setValidationFunction(d->m_validator);
1067         d->m_pathChooserDisplay->setEnvironment(d->m_environment);
1068         d->m_pathChooserDisplay->setBaseDirectory(d->m_baseFileName);
1069         d->m_pathChooserDisplay->setOpenTerminalHandler(d->m_openTerminal);
1070         d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
1071         d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
1072         addLabeledItem(builder, d->m_pathChooserDisplay);
1073         useMacroExpander(d->m_pathChooserDisplay->lineEdit());
1074         if (isAutoApply()) {
1075             if (d->m_autoApplyOnEditingFinished) {
1076                 connect(d->m_pathChooserDisplay, &PathChooser::editingFinished, this, [this] {
1077                     if (d->m_blockAutoApply)
1078                         return;
1079                     d->m_blockAutoApply = true;
1080                     setValue(d->m_pathChooserDisplay->path());
1081                     d->m_blockAutoApply = false;
1082                 });
1083             } else {
1084                 connect(d->m_pathChooserDisplay, &PathChooser::pathChanged, this, [this] {
1085                     setValue(d->m_pathChooserDisplay->path());
1086                 });
1087             }
1088         }
1089         break;
1090     case LineEditDisplay:
1091         d->m_lineEditDisplay = createSubWidget<FancyLineEdit>();
1092         d->m_lineEditDisplay->setPlaceholderText(d->m_placeHolderText);
1093         if (!d->m_historyCompleterKey.isEmpty())
1094             d->m_lineEditDisplay->setHistoryCompleter(d->m_historyCompleterKey);
1095         if (d->m_validator)
1096             d->m_lineEditDisplay->setValidationFunction(d->m_validator);
1097         d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
1098         d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data());
1099         addLabeledItem(builder, d->m_lineEditDisplay);
1100         useMacroExpander(d->m_lineEditDisplay);
1101         if (isAutoApply()) {
1102             if (d->m_autoApplyOnEditingFinished) {
1103                 connect(d->m_lineEditDisplay, &FancyLineEdit::editingFinished, this, [this] {
1104                     if (d->m_blockAutoApply)
1105                         return;
1106                     d->m_blockAutoApply = true;
1107                     setValue(d->m_lineEditDisplay->text());
1108                     d->m_blockAutoApply = false;
1109                 });
1110             } else {
1111                 connect(d->m_lineEditDisplay,
1112                         &FancyLineEdit::textEdited,
1113                         this,
1114                         &StringAspect::setValue);
1115             }
1116         }
1117         if (d->m_useResetButton) {
1118             auto resetButton = createSubWidget<QPushButton>(tr("Reset"));
1119             resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue());
1120             connect(resetButton, &QPushButton::clicked, this, [this] {
1121                 d->m_lineEditDisplay->setText(defaultValue().toString());
1122             });
1123             connect(d->m_lineEditDisplay, &QLineEdit::textChanged, this, [this, resetButton] {
1124                 resetButton->setEnabled(d->m_lineEditDisplay->text() != defaultValue());
1125             });
1126             builder.addItem(resetButton);
1127         }
1128         break;
1129     case TextEditDisplay:
1130         d->m_textEditDisplay = createSubWidget<QTextEdit>();
1131         d->m_textEditDisplay->setPlaceholderText(d->m_placeHolderText);
1132         d->m_textEditDisplay->setUndoRedoEnabled(d->m_undoRedoEnabled);
1133         d->m_textEditDisplay->setAcceptRichText(d->m_acceptRichText);
1134         d->m_textEditDisplay->setTextInteractionFlags(Qt::TextEditorInteraction);
1135         d->m_textEditDisplay->setText(displayedString);
1136         d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data());
1137         addLabeledItem(builder, d->m_textEditDisplay);
1138         useMacroExpander(d->m_textEditDisplay);
1139         if (isAutoApply()) {
1140             connect(d->m_textEditDisplay, &QTextEdit::textChanged, this, [this] {
1141                 setValue(d->m_textEditDisplay->document()->toPlainText());
1142             });
1143         }
1144         break;
1145     case LabelDisplay:
1146         d->m_labelDisplay = createSubWidget<QLabel>();
1147         d->m_labelDisplay->setTextInteractionFlags(Qt::TextSelectableByMouse);
1148         d->m_labelDisplay->setText(displayedString);
1149         d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
1150         addLabeledItem(builder, d->m_labelDisplay);
1151         break;
1152     }
1153 
1154     validateInput();
1155 
1156     if (d->m_checker && d->m_checkBoxPlacement == CheckBoxPlacement::Right)
1157         d->m_checker->addToLayout(builder);
1158 }
1159 
volatileValue() const1160 QVariant StringAspect::volatileValue() const
1161 {
1162     QTC_CHECK(!isAutoApply());
1163     switch (d->m_displayStyle) {
1164     case PathChooserDisplay:
1165         QTC_ASSERT(d->m_pathChooserDisplay, return {});
1166         return d->m_pathChooserDisplay->path();
1167     case LineEditDisplay:
1168         QTC_ASSERT(d->m_lineEditDisplay, return {});
1169         return d->m_lineEditDisplay->text();
1170     case TextEditDisplay:
1171         QTC_ASSERT(d->m_textEditDisplay, return {});
1172         return d->m_textEditDisplay->document()->toPlainText();
1173     case LabelDisplay:
1174         break;
1175     }
1176     return {};
1177 }
1178 
setVolatileValue(const QVariant & val)1179 void StringAspect::setVolatileValue(const QVariant &val)
1180 {
1181     switch (d->m_displayStyle) {
1182     case PathChooserDisplay:
1183         if (d->m_pathChooserDisplay)
1184             d->m_pathChooserDisplay->setPath(val.toString());
1185         break;
1186     case LineEditDisplay:
1187         if (d->m_lineEditDisplay)
1188             d->m_lineEditDisplay->setText(val.toString());
1189         break;
1190     case TextEditDisplay:
1191         if (d->m_textEditDisplay)
1192             d->m_textEditDisplay->document()->setPlainText(val.toString());
1193         break;
1194     case LabelDisplay:
1195         break;
1196     }
1197 }
1198 
emitChangedValue()1199 void StringAspect::emitChangedValue()
1200 {
1201     emit valueChanged(value());
1202 }
1203 
update()1204 void StringAspect::update()
1205 {
1206     const QString displayedString = d->m_displayFilter ? d->m_displayFilter(value()) : value();
1207 
1208     if (d->m_pathChooserDisplay) {
1209         d->m_pathChooserDisplay->setFilePath(FilePath::fromString(displayedString));
1210         d->updateWidgetFromCheckStatus(this, d->m_pathChooserDisplay.data());
1211     }
1212 
1213     if (d->m_lineEditDisplay) {
1214         d->m_lineEditDisplay->setTextKeepingActiveCursor(displayedString);
1215         d->updateWidgetFromCheckStatus(this, d->m_lineEditDisplay.data());
1216     }
1217 
1218     if (d->m_textEditDisplay) {
1219         const QString old = d->m_textEditDisplay->document()->toPlainText();
1220         if (displayedString != old)
1221             d->m_textEditDisplay->setText(displayedString);
1222         d->updateWidgetFromCheckStatus(this, d->m_textEditDisplay.data());
1223     }
1224 
1225     if (d->m_labelDisplay) {
1226         d->m_labelDisplay->setText(displayedString);
1227         d->m_labelDisplay->setToolTip(d->m_showToolTipOnLabel ? displayedString : toolTip());
1228     }
1229 
1230     validateInput();
1231 }
1232 
1233 /*!
1234     Adds a check box with a \a checkerLabel according to \a checkBoxPlacement
1235     to the line edit.
1236 
1237     The state of the check box is made persistent when using a non-emtpy
1238     \a checkerKey.
1239 */
makeCheckable(CheckBoxPlacement checkBoxPlacement,const QString & checkerLabel,const QString & checkerKey)1240 void StringAspect::makeCheckable(CheckBoxPlacement checkBoxPlacement,
1241                                      const QString &checkerLabel, const QString &checkerKey)
1242 {
1243     QTC_ASSERT(!d->m_checker, return);
1244     d->m_checkBoxPlacement = checkBoxPlacement;
1245     d->m_checker.reset(new BoolAspect);
1246     d->m_checker->setLabel(checkerLabel, checkBoxPlacement == CheckBoxPlacement::Top
1247                            ? BoolAspect::LabelPlacement::InExtraLabel
1248                            : BoolAspect::LabelPlacement::AtCheckBox);
1249     d->m_checker->setSettingsKey(checkerKey);
1250 
1251     connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::update);
1252     connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::changed);
1253     connect(d->m_checker.get(), &BoolAspect::changed, this, &StringAspect::checkedChanged);
1254 
1255     update();
1256 }
1257 
1258 /*!
1259     \class Utils::BoolAspect
1260     \inmodule QtCreator
1261 
1262     \brief A boolean aspect is a boolean property of some object, together with
1263     a description of its behavior for common operations like visualizing or
1264     persisting.
1265 
1266     The boolean aspect is displayed using a QCheckBox.
1267 
1268     The visual representation often contains a label in front or after
1269     the display of the actual checkmark.
1270 */
1271 
1272 
BoolAspect(const QString & settingsKey)1273 BoolAspect::BoolAspect(const QString &settingsKey)
1274     : d(new Internal::BoolAspectPrivate)
1275 {
1276     setDefaultValue(false);
1277     setSettingsKey(settingsKey);
1278     setSpan(2, 1);
1279 }
1280 
1281 /*!
1282     \reimp
1283 */
1284 BoolAspect::~BoolAspect() = default;
1285 
1286 /*!
1287     \reimp
1288 */
addToLayout(LayoutBuilder & builder)1289 void BoolAspect::addToLayout(LayoutBuilder &builder)
1290 {
1291     QTC_CHECK(!d->m_checkBox);
1292     d->m_checkBox = createSubWidget<QCheckBox>();
1293     switch (d->m_labelPlacement) {
1294     case LabelPlacement::AtCheckBoxWithoutDummyLabel:
1295         d->m_checkBox->setText(labelText());
1296         builder.addItem(d->m_checkBox.data());
1297         break;
1298     case LabelPlacement::AtCheckBox: {
1299         d->m_checkBox->setText(labelText());
1300         LayoutBuilder::LayoutType type = builder.layoutType();
1301         if (type == LayoutBuilder::FormLayout)
1302             builder.addItem(createSubWidget<QLabel>());
1303         builder.addItem(d->m_checkBox.data());
1304         break;
1305     }
1306     case LabelPlacement::InExtraLabel:
1307         addLabeledItem(builder, d->m_checkBox);
1308         break;
1309     }
1310     d->m_checkBox->setChecked(value());
1311     if (isAutoApply()) {
1312         connect(d->m_checkBox.data(), &QAbstractButton::clicked,
1313                 this, [this](bool val) { setValue(val); });
1314     }
1315     connect(d->m_checkBox.data(), &QAbstractButton::clicked,
1316             this, &BoolAspect::volatileValueChanged);
1317 }
1318 
action()1319 QAction *BoolAspect::action()
1320 {
1321     if (hasAction())
1322         return BaseAspect::action();
1323     auto act = BaseAspect::action(); // Creates it.
1324     act->setCheckable(true);
1325     act->setChecked(value());
1326     connect(act, &QAction::triggered, this, [this](bool newValue) {
1327         // The check would be nice to have in simple conditions, but if we
1328         // have an action that's used both on a settings page and as action
1329         // in a menu like "Use FakeVim", isAutoApply() is false, and yet this
1330         // here can trigger.
1331         //QTC_CHECK(isAutoApply());
1332         setValue(newValue);
1333     });
1334     return act;
1335 }
1336 
volatileValue() const1337 QVariant BoolAspect::volatileValue() const
1338 {
1339     QTC_CHECK(!isAutoApply());
1340     if (d->m_checkBox)
1341         return d->m_checkBox->isChecked();
1342     if (d->m_groupBox)
1343         return d->m_groupBox->isChecked();
1344     QTC_CHECK(false);
1345     return {};
1346 }
1347 
setVolatileValue(const QVariant & val)1348 void BoolAspect::setVolatileValue(const QVariant &val)
1349 {
1350     QTC_CHECK(!isAutoApply());
1351     if (d->m_checkBox)
1352         d->m_checkBox->setChecked(val.toBool());
1353     else if (d->m_groupBox)
1354         d->m_groupBox->setChecked(val.toBool());
1355 }
1356 
emitChangedValue()1357 void BoolAspect::emitChangedValue()
1358 {
1359     emit valueChanged(value());
1360 }
1361 
1362 
1363 /*!
1364     \reimp
1365 */
1366 
value() const1367 bool BoolAspect::value() const
1368 {
1369     return BaseAspect::value().toBool();
1370 }
1371 
setValue(bool value)1372 void BoolAspect::setValue(bool value)
1373 {
1374     if (BaseAspect::setValueQuietly(value)) {
1375         if (d->m_checkBox)
1376             d->m_checkBox->setChecked(value);
1377         //qDebug() << "SetValue: Changing" << labelText() << " to " << value;
1378         emit changed();
1379         //QTC_CHECK(!labelText().isEmpty());
1380         emit valueChanged(value);
1381         //qDebug() << "SetValue: Changed" << labelText() << " to " << value;
1382         if (hasAction()) {
1383             //qDebug() << "SetValue: Triggering " << labelText() << "with" << value;
1384             emit action()->triggered(value);
1385         }
1386     }
1387 }
1388 
setDefaultValue(bool val)1389 void BoolAspect::setDefaultValue(bool val)
1390 {
1391     BaseAspect::setDefaultValue(val);
1392 }
1393 
setLabel(const QString & labelText,LabelPlacement labelPlacement)1394 void BoolAspect::setLabel(const QString &labelText, LabelPlacement labelPlacement)
1395 {
1396     BaseAspect::setLabelText(labelText);
1397     d->m_labelPlacement = labelPlacement;
1398 }
1399 
setLabelPlacement(BoolAspect::LabelPlacement labelPlacement)1400 void BoolAspect::setLabelPlacement(BoolAspect::LabelPlacement labelPlacement)
1401 {
1402     d->m_labelPlacement = labelPlacement;
1403 }
1404 
setHandlesGroup(QGroupBox * box)1405 void BoolAspect::setHandlesGroup(QGroupBox *box)
1406 {
1407     registerSubWidget(box);
1408     d->m_groupBox = box;
1409 }
1410 
1411 /*!
1412     \class Utils::SelectionAspect
1413     \inmodule QtCreator
1414 
1415     \brief A selection aspect represents a specific choice out of
1416     several.
1417 
1418     The selection aspect is displayed using a QComboBox or
1419     QRadioButtons in a QButtonGroup.
1420 */
1421 
SelectionAspect()1422 SelectionAspect::SelectionAspect()
1423     : d(new Internal::SelectionAspectPrivate)
1424 {
1425     setSpan(2, 1);
1426 }
1427 
1428 /*!
1429     \reimp
1430 */
1431 SelectionAspect::~SelectionAspect() = default;
1432 
1433 /*!
1434     \reimp
1435 */
addToLayout(LayoutBuilder & builder)1436 void SelectionAspect::addToLayout(LayoutBuilder &builder)
1437 {
1438     QTC_CHECK(d->m_buttonGroup == nullptr);
1439     QTC_CHECK(!d->m_comboBox);
1440     QTC_ASSERT(d->m_buttons.isEmpty(), d->m_buttons.clear());
1441 
1442     switch (d->m_displayStyle) {
1443     case DisplayStyle::RadioButtons:
1444         d->m_buttonGroup = new QButtonGroup();
1445         d->m_buttonGroup->setExclusive(true);
1446         for (int i = 0, n = d->m_options.size(); i < n; ++i) {
1447             const Option &option = d->m_options.at(i);
1448             auto button = createSubWidget<QRadioButton>(option.displayName);
1449             button->setChecked(i == value());
1450             button->setEnabled(option.enabled);
1451             button->setToolTip(option.tooltip);
1452             builder.addItems({{}, button});
1453             d->m_buttons.append(button);
1454             d->m_buttonGroup->addButton(button, i);
1455             if (isAutoApply()) {
1456                 connect(button, &QAbstractButton::clicked, this, [this, i] {
1457                     setValue(i);
1458                 });
1459             }
1460         }
1461         break;
1462     case DisplayStyle::ComboBox:
1463         setLabelText(displayName());
1464         d->m_comboBox = createSubWidget<QComboBox>();
1465         for (int i = 0, n = d->m_options.size(); i < n; ++i)
1466             d->m_comboBox->addItem(d->m_options.at(i).displayName);
1467         if (isAutoApply()) {
1468             connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::activated),
1469                     this, &SelectionAspect::setValue);
1470         }
1471         connect(d->m_comboBox.data(), QOverload<int>::of(&QComboBox::currentIndexChanged),
1472                 this, &SelectionAspect::volatileValueChanged);
1473         d->m_comboBox->setCurrentIndex(value());
1474         addLabeledItem(builder, d->m_comboBox);
1475         break;
1476     }
1477 }
1478 
volatileValue() const1479 QVariant SelectionAspect::volatileValue() const
1480 {
1481     QTC_CHECK(!isAutoApply());
1482     switch (d->m_displayStyle) {
1483     case DisplayStyle::RadioButtons:
1484         QTC_ASSERT(d->m_buttonGroup, return {});
1485         return d->m_buttonGroup->checkedId();
1486     case DisplayStyle::ComboBox:
1487         QTC_ASSERT(d->m_comboBox, return {});
1488         return d->m_comboBox->currentIndex();
1489     }
1490     return {};
1491 }
1492 
setVolatileValue(const QVariant & val)1493 void SelectionAspect::setVolatileValue(const QVariant &val)
1494 {
1495     QTC_CHECK(!isAutoApply());
1496     switch (d->m_displayStyle) {
1497     case DisplayStyle::RadioButtons: {
1498         if (d->m_buttonGroup) {
1499             QAbstractButton *button = d->m_buttonGroup->button(val.toInt());
1500             QTC_ASSERT(button, return);
1501             button->setChecked(true);
1502         }
1503         break;
1504     }
1505     case DisplayStyle::ComboBox:
1506         if (d->m_comboBox)
1507             d->m_comboBox->setCurrentIndex(val.toInt());
1508         break;
1509     }
1510 }
1511 
finish()1512 void SelectionAspect::finish()
1513 {
1514     delete d->m_buttonGroup;
1515     d->m_buttonGroup = nullptr;
1516     BaseAspect::finish();
1517     d->m_buttons.clear();
1518 }
1519 
setDisplayStyle(SelectionAspect::DisplayStyle style)1520 void SelectionAspect::setDisplayStyle(SelectionAspect::DisplayStyle style)
1521 {
1522     d->m_displayStyle = style;
1523 }
1524 
value() const1525 int SelectionAspect::value() const
1526 {
1527     return BaseAspect::value().toInt();
1528 }
1529 
setValue(int value)1530 void SelectionAspect::setValue(int value)
1531 {
1532     if (BaseAspect::setValueQuietly(value)) {
1533         if (d->m_buttonGroup && 0 <= value && value < d->m_buttons.size())
1534             d->m_buttons.at(value)->setChecked(true);
1535         else if (d->m_comboBox)
1536             d->m_comboBox->setCurrentIndex(value);
1537         emit changed();
1538     }
1539 }
1540 
setStringValue(const QString & val)1541 void SelectionAspect::setStringValue(const QString &val)
1542 {
1543     const int index = indexForDisplay(val);
1544     QTC_ASSERT(index >= 0, return);
1545     setValue(index);
1546 }
1547 
setDefaultValue(int val)1548 void SelectionAspect::setDefaultValue(int val)
1549 {
1550     BaseAspect::setDefaultValue(val);
1551 }
1552 
1553 // Note: This needs to be set after all options are added.
setDefaultValue(const QString & val)1554 void SelectionAspect::setDefaultValue(const QString &val)
1555 {
1556     BaseAspect::setDefaultValue(indexForDisplay(val));
1557 }
1558 
stringValue() const1559 QString SelectionAspect::stringValue() const
1560 {
1561     return d->m_options.at(value()).displayName;
1562 }
1563 
itemValue() const1564 QVariant SelectionAspect::itemValue() const
1565 {
1566     return d->m_options.at(value()).itemData;
1567 }
1568 
addOption(const QString & displayName,const QString & toolTip)1569 void SelectionAspect::addOption(const QString &displayName, const QString &toolTip)
1570 {
1571     d->m_options.append(Option(displayName, toolTip, {}));
1572 }
1573 
addOption(const Option & option)1574 void SelectionAspect::addOption(const Option &option)
1575 {
1576     d->m_options.append(option);
1577 }
1578 
indexForDisplay(const QString & displayName) const1579 int SelectionAspect::indexForDisplay(const QString &displayName) const
1580 {
1581     for (int i = 0, n = d->m_options.size(); i < n; ++i) {
1582         if (d->m_options.at(i).displayName == displayName)
1583             return i;
1584     }
1585     return -1;
1586 }
1587 
displayForIndex(int index) const1588 QString SelectionAspect::displayForIndex(int index) const
1589 {
1590     QTC_ASSERT(index >= 0 && index < d->m_options.size(), return {});
1591     return d->m_options.at(index).displayName;
1592 }
1593 
indexForItemValue(const QVariant & value) const1594 int SelectionAspect::indexForItemValue(const QVariant &value) const
1595 {
1596     for (int i = 0, n = d->m_options.size(); i < n; ++i) {
1597         if (d->m_options.at(i).itemData == value)
1598             return i;
1599     }
1600     return -1;
1601 }
1602 
itemValueForIndex(int index) const1603 QVariant SelectionAspect::itemValueForIndex(int index) const
1604 {
1605     QTC_ASSERT(index >= 0 && index < d->m_options.size(), return {});
1606     return d->m_options.at(index).itemData;
1607 }
1608 
1609 /*!
1610     \class Utils::MultiSelectionAspect
1611     \inmodule QtCreator
1612 
1613     \brief A multi-selection aspect represents one or more choices out of
1614     several.
1615 
1616     The multi-selection aspect is displayed using a QListWidget with
1617     checkable items.
1618 */
1619 
MultiSelectionAspect()1620 MultiSelectionAspect::MultiSelectionAspect()
1621     : d(new Internal::MultiSelectionAspectPrivate(this))
1622 {
1623     setDefaultValue(QStringList());
1624     setSpan(2, 1);
1625 }
1626 
1627 /*!
1628     \reimp
1629 */
1630 MultiSelectionAspect::~MultiSelectionAspect() = default;
1631 
1632 /*!
1633     \reimp
1634 */
addToLayout(LayoutBuilder & builder)1635 void MultiSelectionAspect::addToLayout(LayoutBuilder &builder)
1636 {
1637     QTC_CHECK(d->m_listView == nullptr);
1638     if (d->m_allValues.isEmpty())
1639         return;
1640 
1641     switch (d->m_displayStyle) {
1642     case DisplayStyle::ListView:
1643         d->m_listView = createSubWidget<QListWidget>();
1644         for (const QString &val : qAsConst(d->m_allValues)) {
1645             auto item = new QListWidgetItem(val, d->m_listView);
1646             item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
1647             item->setCheckState(value().contains(item->text()) ? Qt::Checked : Qt::Unchecked);
1648         }
1649         connect(d->m_listView, &QListWidget::itemChanged, this,
1650             [this](QListWidgetItem *item) {
1651             if (d->setValueSelectedHelper(item->text(), item->checkState() & Qt::Checked))
1652                 emit changed();
1653         });
1654         addLabeledItem(builder, d->m_listView);
1655     }
1656 }
1657 
setValueSelectedHelper(const QString & val,bool on)1658 bool Internal::MultiSelectionAspectPrivate::setValueSelectedHelper(const QString &val, bool on)
1659 {
1660     QStringList list = q->value();
1661     if (on && !list.contains(val)) {
1662         list.append(val);
1663         q->setValue(list);
1664         return true;
1665     }
1666     if (!on && list.contains(val)) {
1667         list.removeOne(val);
1668         q->setValue(list);
1669         return true;
1670     }
1671     return false;
1672 }
1673 
allValues() const1674 QStringList MultiSelectionAspect::allValues() const
1675 {
1676     return d->m_allValues;
1677 }
1678 
setAllValues(const QStringList & val)1679 void MultiSelectionAspect::setAllValues(const QStringList &val)
1680 {
1681     d->m_allValues = val;
1682 }
1683 
setDisplayStyle(MultiSelectionAspect::DisplayStyle style)1684 void MultiSelectionAspect::setDisplayStyle(MultiSelectionAspect::DisplayStyle style)
1685 {
1686     d->m_displayStyle = style;
1687 }
1688 
value() const1689 QStringList MultiSelectionAspect::value() const
1690 {
1691     return BaseAspect::value().toStringList();
1692 }
1693 
setValue(const QStringList & value)1694 void MultiSelectionAspect::setValue(const QStringList &value)
1695 {
1696     if (BaseAspect::setValueQuietly(value)) {
1697         if (d->m_listView) {
1698             const int n = d->m_listView->count();
1699             QTC_CHECK(n == d->m_allValues.size());
1700             for (int i = 0; i != n; ++i) {
1701                 auto item = d->m_listView->item(i);
1702                 item->setCheckState(value.contains(item->text()) ? Qt::Checked : Qt::Unchecked);
1703             }
1704         } else {
1705             emit changed();
1706         }
1707     }
1708 }
1709 
1710 
1711 /*!
1712     \class Utils::IntegerAspect
1713     \inmodule QtCreator
1714 
1715     \brief An integer aspect is a integral property of some object, together with
1716     a description of its behavior for common operations like visualizing or
1717     persisting.
1718 
1719     The integer aspect is displayed using a \c QSpinBox.
1720 
1721     The visual representation often contains a label in front
1722     the display of the spin box.
1723 */
1724 
1725 // IntegerAspect
1726 
IntegerAspect()1727 IntegerAspect::IntegerAspect()
1728     : d(new Internal::IntegerAspectPrivate)
1729 {
1730     setDefaultValue(qint64(0));
1731     setSpan(2, 1);
1732 }
1733 
1734 /*!
1735     \reimp
1736 */
1737 IntegerAspect::~IntegerAspect() = default;
1738 
1739 /*!
1740     \reimp
1741 */
addToLayout(LayoutBuilder & builder)1742 void IntegerAspect::addToLayout(LayoutBuilder &builder)
1743 {
1744     QTC_CHECK(!d->m_spinBox);
1745     d->m_spinBox = createSubWidget<QSpinBox>();
1746     d->m_spinBox->setDisplayIntegerBase(d->m_displayIntegerBase);
1747     d->m_spinBox->setPrefix(d->m_prefix);
1748     d->m_spinBox->setSuffix(d->m_suffix);
1749     d->m_spinBox->setSingleStep(d->m_singleStep);
1750     d->m_spinBox->setSpecialValueText(d->m_specialValueText);
1751     if (d->m_maximumValue && d->m_maximumValue)
1752         d->m_spinBox->setRange(int(d->m_minimumValue.value() / d->m_displayScaleFactor),
1753                                int(d->m_maximumValue.value() / d->m_displayScaleFactor));
1754     d->m_spinBox->setValue(int(value() / d->m_displayScaleFactor)); // Must happen after setRange()
1755     addLabeledItem(builder, d->m_spinBox);
1756 
1757     if (isAutoApply()) {
1758         connect(d->m_spinBox.data(), QOverload<int>::of(&QSpinBox::valueChanged),
1759                 this, [this] { setValue(d->m_spinBox->value()); });
1760     }
1761 }
1762 
volatileValue() const1763 QVariant IntegerAspect::volatileValue() const
1764 {
1765     QTC_CHECK(!isAutoApply());
1766     QTC_ASSERT(d->m_spinBox, return {});
1767     return d->m_spinBox->value() * d->m_displayScaleFactor;
1768 }
1769 
setVolatileValue(const QVariant & val)1770 void IntegerAspect::setVolatileValue(const QVariant &val)
1771 {
1772     QTC_CHECK(!isAutoApply());
1773     if (d->m_spinBox)
1774         d->m_spinBox->setValue(int(val.toLongLong() / d->m_displayScaleFactor));
1775 }
1776 
value() const1777 qint64 IntegerAspect::value() const
1778 {
1779     return BaseAspect::value().toLongLong();
1780 }
1781 
setValue(qint64 value)1782 void IntegerAspect::setValue(qint64 value)
1783 {
1784     BaseAspect::setValue(value);
1785 }
1786 
setRange(qint64 min,qint64 max)1787 void IntegerAspect::setRange(qint64 min, qint64 max)
1788 {
1789     d->m_minimumValue = min;
1790     d->m_maximumValue = max;
1791 }
1792 
setLabel(const QString & label)1793 void IntegerAspect::setLabel(const QString &label)
1794 {
1795     setLabelText(label);
1796 }
1797 
setPrefix(const QString & prefix)1798 void IntegerAspect::setPrefix(const QString &prefix)
1799 {
1800     d->m_prefix = prefix;
1801 }
1802 
setSuffix(const QString & suffix)1803 void IntegerAspect::setSuffix(const QString &suffix)
1804 {
1805     d->m_suffix = suffix;
1806 }
1807 
setDisplayIntegerBase(int base)1808 void IntegerAspect::setDisplayIntegerBase(int base)
1809 {
1810     d->m_displayIntegerBase = base;
1811 }
1812 
setDisplayScaleFactor(qint64 factor)1813 void IntegerAspect::setDisplayScaleFactor(qint64 factor)
1814 {
1815     d->m_displayScaleFactor = factor;
1816 }
1817 
setDefaultValue(qint64 defaultValue)1818 void IntegerAspect::setDefaultValue(qint64 defaultValue)
1819 {
1820     BaseAspect::setDefaultValue(defaultValue);
1821 }
1822 
setSpecialValueText(const QString & specialText)1823 void IntegerAspect::setSpecialValueText(const QString &specialText)
1824 {
1825     d->m_specialValueText = specialText;
1826 }
1827 
setSingleStep(qint64 step)1828 void IntegerAspect::setSingleStep(qint64 step)
1829 {
1830     d->m_singleStep = step;
1831 }
1832 
1833 
1834 /*!
1835     \class Utils::DoubleAspect
1836     \inmodule QtCreator
1837 
1838     \brief An double aspect is a numerical property of some object, together with
1839     a description of its behavior for common operations like visualizing or
1840     persisting.
1841 
1842     The double aspect is displayed using a \c QDoubleSpinBox.
1843 
1844     The visual representation often contains a label in front
1845     the display of the spin box.
1846 */
1847 
DoubleAspect()1848 DoubleAspect::DoubleAspect()
1849     : d(new Internal::DoubleAspectPrivate)
1850 {
1851     setDefaultValue(double(0));
1852     setSpan(2, 1);
1853 }
1854 
1855 /*!
1856     \reimp
1857 */
1858 DoubleAspect::~DoubleAspect() = default;
1859 
1860 /*!
1861     \reimp
1862 */
addToLayout(LayoutBuilder & builder)1863 void DoubleAspect::addToLayout(LayoutBuilder &builder)
1864 {
1865     QTC_CHECK(!d->m_spinBox);
1866     d->m_spinBox = createSubWidget<QDoubleSpinBox>();
1867     d->m_spinBox->setPrefix(d->m_prefix);
1868     d->m_spinBox->setSuffix(d->m_suffix);
1869     d->m_spinBox->setSingleStep(d->m_singleStep);
1870     d->m_spinBox->setSpecialValueText(d->m_specialValueText);
1871     if (d->m_maximumValue && d->m_maximumValue)
1872         d->m_spinBox->setRange(d->m_minimumValue.value(), d->m_maximumValue.value());
1873     d->m_spinBox->setValue(value()); // Must happen after setRange()!
1874     addLabeledItem(builder, d->m_spinBox);
1875 
1876     if (isAutoApply()) {
1877         connect(d->m_spinBox.data(), QOverload<double>::of(&QDoubleSpinBox::valueChanged),
1878                 this, [this] { setValue(d->m_spinBox->value()); });
1879     }
1880 }
1881 
volatileValue() const1882 QVariant DoubleAspect::volatileValue() const
1883 {
1884     QTC_CHECK(!isAutoApply());
1885     QTC_ASSERT(d->m_spinBox, return {});
1886     return d->m_spinBox->value();
1887 }
1888 
setVolatileValue(const QVariant & val)1889 void DoubleAspect::setVolatileValue(const QVariant &val)
1890 {
1891     QTC_CHECK(!isAutoApply());
1892     if (d->m_spinBox)
1893         d->m_spinBox->setValue(val.toDouble());
1894 }
1895 
value() const1896 double DoubleAspect::value() const
1897 {
1898     return BaseAspect::value().toDouble();
1899 }
1900 
setValue(double value)1901 void DoubleAspect::setValue(double value)
1902 {
1903     BaseAspect::setValue(value);
1904 }
1905 
setRange(double min,double max)1906 void DoubleAspect::setRange(double min, double max)
1907 {
1908     d->m_minimumValue = min;
1909     d->m_maximumValue = max;
1910 }
1911 
setPrefix(const QString & prefix)1912 void DoubleAspect::setPrefix(const QString &prefix)
1913 {
1914     d->m_prefix = prefix;
1915 }
1916 
setSuffix(const QString & suffix)1917 void DoubleAspect::setSuffix(const QString &suffix)
1918 {
1919     d->m_suffix = suffix;
1920 }
1921 
setDefaultValue(double defaultValue)1922 void DoubleAspect::setDefaultValue(double defaultValue)
1923 {
1924     BaseAspect::setDefaultValue(defaultValue);
1925 }
1926 
setSpecialValueText(const QString & specialText)1927 void DoubleAspect::setSpecialValueText(const QString &specialText)
1928 {
1929     d->m_specialValueText = specialText;
1930 }
1931 
setSingleStep(double step)1932 void DoubleAspect::setSingleStep(double step)
1933 {
1934     d->m_singleStep = step;
1935 }
1936 
1937 
1938 /*!
1939     \class Utils::BaseTristateAspect
1940     \inmodule QtCreator
1941 
1942     \brief A tristate aspect is a property of some object that can have
1943     three values: enabled, disabled, and unspecified.
1944 
1945     Its visual representation is a QComboBox with three items.
1946 */
1947 
TriStateAspect(const QString onString,const QString & offString,const QString & defaultString)1948 TriStateAspect::TriStateAspect(const QString onString, const QString &offString,
1949                                const QString &defaultString)
1950 {
1951     setDisplayStyle(DisplayStyle::ComboBox);
1952     setDefaultValue(TriState::Default);
1953     addOption(onString);
1954     addOption(offString);
1955     addOption(defaultString);
1956 }
1957 
value() const1958 TriState TriStateAspect::value() const
1959 {
1960     return TriState::fromVariant(BaseAspect::value());
1961 }
1962 
setValue(TriState value)1963 void TriStateAspect::setValue(TriState value)
1964 {
1965     BaseAspect::setValue(value.toVariant());
1966 }
1967 
setDefaultValue(TriState value)1968 void TriStateAspect::setDefaultValue(TriState value)
1969 {
1970     BaseAspect::setDefaultValue(value.toVariant());
1971 }
1972 
1973 const TriState TriState::Enabled{TriState::EnabledValue};
1974 const TriState TriState::Disabled{TriState::DisabledValue};
1975 const TriState TriState::Default{TriState::DefaultValue};
1976 
fromVariant(const QVariant & variant)1977 TriState TriState::fromVariant(const QVariant &variant)
1978 {
1979     int v = variant.toInt();
1980     QTC_ASSERT(v == EnabledValue || v == DisabledValue || v == DefaultValue, v = DefaultValue);
1981     return TriState(Value(v));
1982 }
1983 
1984 
1985 /*!
1986     \class Utils::StringListAspect
1987     \inmodule QtCreator
1988 
1989     \brief A string list aspect represents a property of some object
1990     that is a list of strings.
1991 */
1992 
StringListAspect()1993 StringListAspect::StringListAspect()
1994     : d(new Internal::StringListAspectPrivate)
1995 {
1996     setDefaultValue(QStringList());
1997 }
1998 
1999 /*!
2000     \reimp
2001 */
2002 StringListAspect::~StringListAspect() = default;
2003 
2004 /*!
2005     \reimp
2006 */
addToLayout(LayoutBuilder & builder)2007 void StringListAspect::addToLayout(LayoutBuilder &builder)
2008 {
2009     Q_UNUSED(builder)
2010     // TODO - when needed.
2011 }
2012 
value() const2013 QStringList StringListAspect::value() const
2014 {
2015     return BaseAspect::value().toStringList();
2016 }
2017 
setValue(const QStringList & value)2018 void StringListAspect::setValue(const QStringList &value)
2019 {
2020     BaseAspect::setValue(value);
2021 }
2022 
appendValue(const QString & s,bool allowDuplicates)2023 void StringListAspect::appendValue(const QString &s, bool allowDuplicates)
2024 {
2025     QStringList val = value();
2026     if (allowDuplicates || !val.contains(s))
2027         val.append(s);
2028     setValue(val);
2029 }
2030 
removeValue(const QString & s)2031 void StringListAspect::removeValue(const QString &s)
2032 {
2033     QStringList val = value();
2034     val.removeAll(s);
2035     setValue(val);
2036 }
2037 
appendValues(const QStringList & values,bool allowDuplicates)2038 void StringListAspect::appendValues(const QStringList &values, bool allowDuplicates)
2039 {
2040     QStringList val = value();
2041     for (const QString &s : values) {
2042         if (allowDuplicates || !val.contains(s))
2043             val.append(s);
2044     }
2045     setValue(val);
2046 }
2047 
removeValues(const QStringList & values)2048 void StringListAspect::removeValues(const QStringList &values)
2049 {
2050     QStringList val = value();
2051     for (const QString &s : values)
2052         val.removeAll(s);
2053     setValue(val);
2054 }
2055 
2056 /*!
2057     \class Utils::IntegerListAspect
2058     \inmodule QtCreator
2059 
2060     \brief A string list aspect represents a property of some object
2061     that is a list of strings.
2062 */
2063 
IntegersAspect()2064 IntegersAspect::IntegersAspect()
2065 {
2066     setDefaultValue({});
2067 }
2068 
2069 /*!
2070     \reimp
2071 */
2072 IntegersAspect::~IntegersAspect() = default;
2073 
2074 /*!
2075     \reimp
2076 */
addToLayout(LayoutBuilder & builder)2077 void IntegersAspect::addToLayout(LayoutBuilder &builder)
2078 {
2079     Q_UNUSED(builder)
2080     // TODO - when needed.
2081 }
2082 
emitChangedValue()2083 void IntegersAspect::emitChangedValue()
2084 {
2085     emit valueChanged(value());
2086 }
2087 
value() const2088 QList<int> IntegersAspect::value() const
2089 {
2090     return Utils::transform(BaseAspect::value().toList(),
2091                             [](QVariant v) { return v.toInt(); });
2092 }
2093 
setValue(const QList<int> & value)2094 void IntegersAspect::setValue(const QList<int> &value)
2095 {
2096     BaseAspect::setValue(Utils::transform(value, &QVariant::fromValue<int>));
2097 }
2098 
setDefaultValue(const QList<int> & value)2099 void IntegersAspect::setDefaultValue(const QList<int> &value)
2100 {
2101     BaseAspect::setDefaultValue(Utils::transform(value, &QVariant::fromValue<int>));
2102 }
2103 
2104 
2105 /*!
2106     \class Utils::TextDisplay
2107 
2108     \brief A text display is a phony aspect with the sole purpose of providing
2109     some text display using an Utils::InfoLabel in places where otherwise
2110     more expensive Utils::StringAspect items would be used.
2111 
2112     A text display does not have a real value.
2113 */
2114 
2115 /*!
2116     Constructs a text display showing the \a message with an icon representing
2117     type \a type.
2118  */
TextDisplay(const QString & message,InfoLabel::InfoType type)2119 TextDisplay::TextDisplay(const QString &message, InfoLabel::InfoType type)
2120     : d(new Internal::TextDisplayPrivate)
2121 {
2122     d->m_message = message;
2123     d->m_type = type;
2124 }
2125 
2126 /*!
2127     \reimp
2128 */
2129 TextDisplay::~TextDisplay() = default;
2130 
2131 /*!
2132     \reimp
2133 */
addToLayout(LayoutBuilder & builder)2134 void TextDisplay::addToLayout(LayoutBuilder &builder)
2135 {
2136     if (!d->m_label) {
2137         d->m_label = createSubWidget<InfoLabel>(d->m_message, d->m_type);
2138         d->m_label->setTextInteractionFlags(Qt::TextSelectableByMouse);
2139         d->m_label->setElideMode(Qt::ElideNone);
2140         d->m_label->setWordWrap(true);
2141         // Do not use m_label->setVisible(isVisible()) unconditionally, it does not
2142         // have a QWidget parent yet when used in a LayoutBuilder.
2143         if (!isVisible())
2144             d->m_label->setVisible(false);
2145     }
2146     builder.addItem(d->m_label.data());
2147 }
2148 
2149 /*!
2150     Sets \a t as the information label type for the visual representation
2151     of this aspect.
2152  */
setIconType(InfoLabel::InfoType t)2153 void TextDisplay::setIconType(InfoLabel::InfoType t)
2154 {
2155     d->m_type = t;
2156     if (d->m_label)
2157         d->m_label->setType(t);
2158 }
2159 
setText(const QString & message)2160 void TextDisplay::setText(const QString &message)
2161 {
2162     d->m_message = message;
2163 }
2164 
2165 /*!
2166     \class Utils::AspectContainer
2167     \inmodule QtCreator
2168 
2169     \brief The AspectContainer class wraps one or more aspects while providing
2170     the interface of a single aspect.
2171 
2172     Sub-aspects ownership can be declared using \a setOwnsSubAspects.
2173 */
2174 
2175 namespace Internal {
2176 
2177 class AspectContainerPrivate
2178 {
2179 public:
2180     QList<BaseAspect *> m_items; // Not owned
2181     bool m_autoApply = true;
2182     bool m_ownsSubAspects = false;
2183     QStringList m_settingsGroup;
2184 };
2185 
2186 } // Internal
2187 
AspectContainer(QObject * parent)2188 AspectContainer::AspectContainer(QObject *parent)
2189     : QObject(parent), d(new Internal::AspectContainerPrivate)
2190 {}
2191 
2192 /*!
2193     \reimp
2194 */
~AspectContainer()2195 AspectContainer::~AspectContainer()
2196 {
2197     if (d->m_ownsSubAspects)
2198         qDeleteAll(d->m_items);
2199 }
2200 
2201 /*!
2202     \internal
2203 */
registerAspect(BaseAspect * aspect)2204 void AspectContainer::registerAspect(BaseAspect *aspect)
2205 {
2206     aspect->setAutoApply(d->m_autoApply);
2207     d->m_items.append(aspect);
2208 }
2209 
registerAspects(const AspectContainer & aspects)2210 void AspectContainer::registerAspects(const AspectContainer &aspects)
2211 {
2212     for (BaseAspect *aspect : qAsConst(aspects.d->m_items))
2213         registerAspect(aspect);
2214 }
2215 
2216 /*!
2217     Retrieves a BaseAspect with a given \a id, or nullptr if no such aspect is contained.
2218 
2219     \sa BaseAspect.
2220 */
aspect(Id id) const2221 BaseAspect *AspectContainer::aspect(Id id) const
2222 {
2223     return Utils::findOrDefault(d->m_items, Utils::equal(&BaseAspect::id, id));
2224 }
2225 
begin() const2226 AspectContainer::const_iterator AspectContainer::begin() const
2227 {
2228     return d->m_items.begin();
2229 }
2230 
end() const2231 AspectContainer::const_iterator AspectContainer::end() const
2232 {
2233     return d->m_items.end();
2234 }
2235 
aspects() const2236 const QList<BaseAspect *> &AspectContainer::aspects() const
2237 {
2238     return d->m_items;
2239 }
2240 
fromMap(const QVariantMap & map)2241 void AspectContainer::fromMap(const QVariantMap &map)
2242 {
2243     for (BaseAspect *aspect : qAsConst(d->m_items))
2244         aspect->fromMap(map);
2245 
2246     emit fromMapFinished();
2247 
2248 }
2249 
toMap(QVariantMap & map) const2250 void AspectContainer::toMap(QVariantMap &map) const
2251 {
2252     for (BaseAspect *aspect : qAsConst(d->m_items))
2253         aspect->toMap(map);
2254 }
2255 
readSettings(QSettings * settings)2256 void AspectContainer::readSettings(QSettings *settings)
2257 {
2258     for (const QString &group : d->m_settingsGroup)
2259         settings->beginGroup(group);
2260 
2261     for (BaseAspect *aspect : qAsConst(d->m_items))
2262         aspect->readSettings(settings);
2263 
2264     for (int i = 0; i != d->m_settingsGroup.size(); ++i)
2265         settings->endGroup();
2266 }
2267 
writeSettings(QSettings * settings) const2268 void AspectContainer::writeSettings(QSettings *settings) const
2269 {
2270     for (const QString &group : d->m_settingsGroup)
2271         settings->beginGroup(group);
2272 
2273     for (BaseAspect *aspect : qAsConst(d->m_items))
2274         aspect->writeSettings(settings);
2275 
2276     for (int i = 0; i != d->m_settingsGroup.size(); ++i)
2277         settings->endGroup();
2278 }
2279 
setSettingsGroup(const QString & groupKey)2280 void AspectContainer::setSettingsGroup(const QString &groupKey)
2281 {
2282     d->m_settingsGroup = QStringList{groupKey};
2283 }
2284 
setSettingsGroups(const QString & groupKey,const QString & subGroupKey)2285 void AspectContainer::setSettingsGroups(const QString &groupKey, const QString &subGroupKey)
2286 {
2287     d->m_settingsGroup = QStringList{groupKey, subGroupKey};
2288 }
2289 
apply()2290 void AspectContainer::apply()
2291 {
2292     for (BaseAspect *aspect : qAsConst(d->m_items))
2293         aspect->apply();
2294 
2295     emit applied();
2296 }
2297 
cancel()2298 void AspectContainer::cancel()
2299 {
2300     for (BaseAspect *aspect : qAsConst(d->m_items))
2301         aspect->cancel();
2302 }
2303 
finish()2304 void AspectContainer::finish()
2305 {
2306     for (BaseAspect *aspect : qAsConst(d->m_items))
2307         aspect->finish();
2308 }
2309 
reset()2310 void AspectContainer::reset()
2311 {
2312     for (BaseAspect *aspect : qAsConst(d->m_items))
2313         aspect->setValueQuietly(aspect->defaultValue());
2314 }
2315 
setAutoApply(bool on)2316 void AspectContainer::setAutoApply(bool on)
2317 {
2318     d->m_autoApply = on;
2319     for (BaseAspect *aspect : qAsConst(d->m_items))
2320         aspect->setAutoApply(on);
2321 }
2322 
setOwnsSubAspects(bool on)2323 void AspectContainer::setOwnsSubAspects(bool on)
2324 {
2325     d->m_ownsSubAspects = on;
2326 }
2327 
isDirty() const2328 bool AspectContainer::isDirty() const
2329 {
2330     for (BaseAspect *aspect : qAsConst(d->m_items)) {
2331         if (aspect->isDirty())
2332             return true;
2333     }
2334     return false;
2335 }
2336 
equals(const AspectContainer & other) const2337 bool AspectContainer::equals(const AspectContainer &other) const
2338 {
2339     // FIXME: Expensive, but should not really be needed in a fully aspectified world.
2340     QVariantMap thisMap, thatMap;
2341     toMap(thisMap);
2342     other.toMap(thatMap);
2343     return thisMap == thatMap;
2344 }
2345 
copyFrom(const AspectContainer & other)2346 void AspectContainer::copyFrom(const AspectContainer &other)
2347 {
2348     QVariantMap map;
2349     other.toMap(map);
2350     fromMap(map);
2351 }
2352 
forEachAspect(const std::function<void (BaseAspect *)> & run) const2353 void AspectContainer::forEachAspect(const std::function<void(BaseAspect *)> &run) const
2354 {
2355     for (BaseAspect *aspect : qAsConst(d->m_items)) {
2356         if (auto container = dynamic_cast<AspectContainer *>(aspect))
2357             container->forEachAspect(run);
2358         else
2359             run(aspect);
2360     }
2361 }
2362 
2363 } // namespace Utils
2364