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