1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "jsonfieldpage.h"
27 #include "jsonfieldpage_p.h"
28
29 #include "jsonwizard.h"
30 #include "jsonwizardfactory.h"
31
32 #include "../project.h"
33 #include "../projecttree.h"
34
35 #include <coreplugin/icore.h>
36 #include <coreplugin/locator/ilocatorfilter.h>
37 #include <utils/algorithm.h>
38 #include <utils/fancylineedit.h>
39 #include <utils/fileutils.h>
40 #include <utils/qtcassert.h>
41 #include <utils/runextensions.h>
42 #include <utils/stringutils.h>
43 #include <utils/theme/theme.h>
44
45 #include <QApplication>
46 #include <QComboBox>
47 #include <QCheckBox>
48 #include <QCompleter>
49 #include <QDebug>
50 #include <QDir>
51 #include <QFormLayout>
52 #include <QFutureWatcher>
53 #include <QItemSelectionModel>
54 #include <QLabel>
55 #include <QListView>
56 #include <QRegularExpression>
57 #include <QStandardItem>
58 #include <QTextEdit>
59 #include <QVariant>
60 #include <QVariantMap>
61 #include <QVBoxLayout>
62
63 using namespace Utils;
64
65 const char NAME_KEY[] = "name";
66 const char DISPLAY_NAME_KEY[] = "trDisplayName";
67 const char TOOLTIP_KEY[] = "trToolTip";
68 const char MANDATORY_KEY[] = "mandatory";
69 const char PERSISTENCE_KEY_KEY[] = "persistenceKey";
70 const char VISIBLE_KEY[] = "visible";
71 const char ENABLED_KEY[] = "enabled";
72 const char SPAN_KEY[] = "span";
73 const char TYPE_KEY[] = "type";
74 const char DATA_KEY[] = "data";
75 const char IS_COMPLETE_KEY[] = "isComplete";
76 const char IS_COMPLETE_MESSAGE_KEY[] = "trIncompleteMessage";
77
78 namespace {
consumeValue(QVariantMap & map,const QString & key,const QVariant & defaultValue=QVariant ())79 QVariant consumeValue(QVariantMap &map, const QString &key, const QVariant &defaultValue = QVariant())
80 {
81 QVariantMap::iterator i = map.find(key);
82 if (i != map.end()) {
83 QVariant value = i.value();
84 map.erase(i);
85 return value;
86 }
87 return defaultValue;
88 }
89
warnAboutUnsupportedKeys(const QVariantMap & map,const QString & name,const QString & type=QString ())90 void warnAboutUnsupportedKeys(const QVariantMap &map, const QString &name, const QString &type = QString())
91 {
92 if (!map.isEmpty()) {
93
94 QString typeAndName = name;
95 if (!type.isEmpty() && !name.isEmpty())
96 typeAndName = QString("%1 (\"%2\")").arg(type, name);
97
98 qWarning().noquote() << QString("Field %1 has unsupported keys: %2").arg(typeAndName, map.keys().join(", "));
99 }
100 }
101 } // namespace
102
103 namespace ProjectExplorer {
104
105 // --------------------------------------------------------------------
106 // Helper:
107 // --------------------------------------------------------------------
108
109 class LineEdit : public FancyLineEdit
110 {
111 public:
LineEdit(MacroExpander * expander,const QRegularExpression & pattern)112 LineEdit(MacroExpander *expander, const QRegularExpression &pattern)
113 {
114 if (pattern.pattern().isEmpty() || !pattern.isValid())
115 return;
116 m_expander.setDisplayName(JsonFieldPage::tr("Line Edit Validator Expander"));
117 m_expander.setAccumulating(true);
118 m_expander.registerVariable("INPUT", JsonFieldPage::tr("The text edit input to fix up."),
119 [this]() { return m_currentInput; });
120 m_expander.registerSubProvider([expander]() -> MacroExpander * { return expander; });
121 setValidationFunction([this, pattern](FancyLineEdit *, QString *) {
122 return pattern.match(text()).hasMatch();
123 });
124 }
125
setFixupExpando(const QString & expando)126 void setFixupExpando(const QString &expando) { m_fixupExpando = expando; }
127
128 private:
fixInputString(const QString & string)129 QString fixInputString(const QString &string) override
130 {
131 if (m_fixupExpando.isEmpty())
132 return string;
133 m_currentInput = string;
134 return m_expander.expand(m_fixupExpando);
135 }
136
137 private:
138 MacroExpander m_expander;
139 QString m_fixupExpando;
140 mutable QString m_currentInput;
141 };
142
143 // --------------------------------------------------------------------
144 // JsonFieldPage::FieldData:
145 // --------------------------------------------------------------------
146
Field()147 JsonFieldPage::Field::Field() : d(std::make_unique<FieldPrivate>())
148 { }
149
~Field()150 JsonFieldPage::Field::~Field()
151 {
152 delete d->m_widget;
153 delete d->m_label;
154 }
155
type()156 QString JsonFieldPage::Field::type()
157 {
158 return d->m_type;
159 }
160
setHasUserChanges()161 void JsonFieldPage::Field::setHasUserChanges()
162 {
163 d->m_hasUserChanges = true;
164 }
165
fromSettings(const QVariant & value)166 void JsonFieldPage::Field::fromSettings(const QVariant &value)
167 {
168 Q_UNUSED(value);
169 }
170
toSettings() const171 QVariant JsonFieldPage::Field::toSettings() const
172 {
173 return {};
174 }
175
parse(const QVariant & input,QString * errorMessage)176 JsonFieldPage::Field *JsonFieldPage::Field::parse(const QVariant &input, QString *errorMessage)
177 {
178 if (input.type() != QVariant::Map) {
179 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
180 "Field is not an object.");
181 return nullptr;
182 }
183
184 QVariantMap tmp = input.toMap();
185 const QString name = consumeValue(tmp, NAME_KEY).toString();
186 if (name.isEmpty()) {
187 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
188 "Field has no name.");
189 return nullptr;
190 }
191 const QString type = consumeValue(tmp, TYPE_KEY).toString();
192 if (type.isEmpty()) {
193 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
194 "Field \"%1\" has no type.").arg(name);
195 return nullptr;
196 }
197
198 Field *data = createFieldData(type);
199 if (!data) {
200 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
201 "Field \"%1\" has unsupported type \"%2\".")
202 .arg(name).arg(type);
203 return nullptr;
204 }
205 data->setTexts(name,
206 JsonWizardFactory::localizedString(consumeValue(tmp, DISPLAY_NAME_KEY).toString()),
207 consumeValue(tmp, TOOLTIP_KEY).toString());
208
209 data->setVisibleExpression(consumeValue(tmp, VISIBLE_KEY, true));
210 data->setEnabledExpression(consumeValue(tmp, ENABLED_KEY, true));
211 data->setIsMandatory(consumeValue(tmp, MANDATORY_KEY, true).toBool());
212 data->setHasSpan(consumeValue(tmp, SPAN_KEY, false).toBool());
213 data->setIsCompleteExpando(consumeValue(tmp, IS_COMPLETE_KEY, true),
214 consumeValue(tmp, IS_COMPLETE_MESSAGE_KEY).toString());
215 data->setPersistenceKey(consumeValue(tmp, PERSISTENCE_KEY_KEY).toString());
216
217 QVariant dataVal = consumeValue(tmp, DATA_KEY);
218 if (!data->parseData(dataVal, errorMessage)) {
219 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
220 "When parsing Field \"%1\": %2")
221 .arg(name).arg(*errorMessage);
222 delete data;
223 return nullptr;
224 }
225
226 warnAboutUnsupportedKeys(tmp, name);
227 return data;
228 }
229
createWidget(JsonFieldPage * page)230 void JsonFieldPage::Field::createWidget(JsonFieldPage *page)
231 {
232 QWidget *w = widget(displayName(), page);
233 w->setObjectName(name());
234 QFormLayout *layout = page->layout();
235
236 if (suppressName()) {
237 layout->addWidget(w);
238 } else if (hasSpan()) {
239 if (!suppressName()) {
240 d->m_label = new QLabel(displayName());
241 layout->addRow(d->m_label);
242 }
243
244 layout->addRow(w);
245 } else {
246 d->m_label = new QLabel(displayName());
247 layout->addRow(d->m_label, w);
248 }
249
250 setup(page, name());
251 }
252
adjustState(MacroExpander * expander)253 void JsonFieldPage::Field::adjustState(MacroExpander *expander)
254 {
255 setVisible(JsonWizard::boolFromVariant(d->m_visibleExpression, expander));
256 setEnabled(JsonWizard::boolFromVariant(d->m_enabledExpression, expander));
257 QTC_ASSERT(d->m_widget, return);
258 d->m_widget->setToolTip(expander->expand(toolTip()));
259 }
260
setEnabled(bool e)261 void JsonFieldPage::Field::setEnabled(bool e)
262 {
263 QTC_ASSERT(d->m_widget, return);
264 d->m_widget->setEnabled(e);
265 }
266
setVisible(bool v)267 void JsonFieldPage::Field::setVisible(bool v)
268 {
269 QTC_ASSERT(d->m_widget, return);
270 if (d->m_label)
271 d->m_label->setVisible(v);
272 d->m_widget->setVisible(v);
273 }
274
setType(const QString & type)275 void JsonFieldPage::Field::setType(const QString &type)
276 {
277 d->m_type = type;
278 }
279
validate(MacroExpander * expander,QString * message)280 bool JsonFieldPage::Field::validate(MacroExpander *expander, QString *message)
281 {
282 if (!JsonWizard::boolFromVariant(d->m_isCompleteExpando, expander)) {
283 if (message)
284 *message = expander->expand(d->m_isCompleteExpandoMessage);
285 return false;
286 }
287 return true;
288 }
289
initialize(MacroExpander * expander)290 void JsonFieldPage::Field::initialize(MacroExpander *expander)
291 {
292 adjustState(expander);
293 initializeData(expander);
294 }
295
widget(const QString & displayName,JsonFieldPage * page)296 QWidget *JsonFieldPage::Field::widget(const QString &displayName, JsonFieldPage *page)
297 {
298 QTC_ASSERT(!d->m_widget, return d->m_widget);
299
300 d->m_widget = createWidget(displayName, page);
301 return d->m_widget;
302 }
303
name()304 QString JsonFieldPage::Field::name()
305 {
306 return d->m_name;
307 }
308
displayName()309 QString JsonFieldPage::Field::displayName()
310 {
311 return d->m_displayName;
312 }
313
toolTip()314 QString JsonFieldPage::Field::toolTip()
315 {
316 return d->m_toolTip;
317 }
318
persistenceKey() const319 QString JsonFieldPage::Field::persistenceKey() const
320 {
321 return d->m_persistenceKey;
322 }
323
isMandatory()324 bool JsonFieldPage::Field::isMandatory()
325 {
326 return d->m_isMandatory;
327 }
328
hasSpan()329 bool JsonFieldPage::Field::hasSpan()
330 {
331 return d->m_hasSpan;
332 }
333
hasUserChanges() const334 bool JsonFieldPage::Field::hasUserChanges() const
335 {
336 return d->m_hasUserChanges;
337 }
338
value(const QString & key)339 QVariant JsonFieldPage::value(const QString &key)
340 {
341 QVariant v = property(key.toUtf8());
342 if (v.isValid())
343 return v;
344 auto w = qobject_cast<JsonWizard *>(wizard());
345 QTC_ASSERT(w, return QVariant());
346 return w->value(key);
347 }
348
widget() const349 QWidget *JsonFieldPage::Field::widget() const
350 {
351 return d->m_widget;
352 }
353
setTexts(const QString & n,const QString & dn,const QString & tt)354 void JsonFieldPage::Field::setTexts(const QString &n, const QString &dn, const QString &tt)
355 {
356 d->m_name = n;
357 d->m_displayName = dn;
358 d->m_toolTip = tt;
359 }
360
setIsMandatory(bool b)361 void JsonFieldPage::Field::setIsMandatory(bool b)
362 {
363 d->m_isMandatory = b;
364 }
365
setHasSpan(bool b)366 void JsonFieldPage::Field::setHasSpan(bool b)
367 {
368 d->m_hasSpan = b;
369 }
370
setVisibleExpression(const QVariant & v)371 void JsonFieldPage::Field::setVisibleExpression(const QVariant &v)
372 {
373 d->m_visibleExpression = v;
374 }
375
setEnabledExpression(const QVariant & v)376 void JsonFieldPage::Field::setEnabledExpression(const QVariant &v)
377 {
378 d->m_enabledExpression = v;
379 }
380
setIsCompleteExpando(const QVariant & v,const QString & m)381 void JsonFieldPage::Field::setIsCompleteExpando(const QVariant &v, const QString &m)
382 {
383 d->m_isCompleteExpando = v;
384 d->m_isCompleteExpandoMessage = m;
385 }
386
setPersistenceKey(const QString & key)387 void JsonFieldPage::Field::setPersistenceKey(const QString &key)
388 {
389 d->m_persistenceKey = key;
390 }
391
392 // --------------------------------------------------------------------
393 // LabelFieldData:
394 // --------------------------------------------------------------------
395
parseData(const QVariant & data,QString * errorMessage)396 bool LabelField::parseData(const QVariant &data, QString *errorMessage)
397 {
398 if (data.type() != QVariant::Map) {
399 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
400 "Label (\"%1\") data is not an object.")
401 .arg(name());
402 return false;
403 }
404
405 QVariantMap tmp = data.toMap();
406
407 m_wordWrap = consumeValue(tmp, "wordWrap", false).toBool();
408 m_text = JsonWizardFactory::localizedString(consumeValue(tmp, "trText"));
409
410 if (m_text.isEmpty()) {
411 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
412 "Label (\"%1\") has no trText.")
413 .arg(name());
414 return false;
415 }
416 warnAboutUnsupportedKeys(tmp, name(), type());
417 return true;
418 }
419
createWidget(const QString & displayName,JsonFieldPage * page)420 QWidget *LabelField::createWidget(const QString &displayName, JsonFieldPage *page)
421 {
422 Q_UNUSED(displayName)
423 Q_UNUSED(page)
424 auto w = new QLabel;
425 w->setWordWrap(m_wordWrap);
426 w->setText(m_text);
427 w->setSizePolicy(QSizePolicy::Expanding, w->sizePolicy().verticalPolicy());
428 return w;
429 }
430
431 // --------------------------------------------------------------------
432 // SpacerFieldData:
433 // --------------------------------------------------------------------
434
parseData(const QVariant & data,QString * errorMessage)435 bool SpacerField::parseData(const QVariant &data, QString *errorMessage)
436 {
437 if (data.isNull())
438 return true;
439
440 if (data.type() != QVariant::Map) {
441 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
442 "Spacer (\"%1\") data is not an object.")
443 .arg(name());
444 return false;
445 }
446
447 QVariantMap tmp = data.toMap();
448
449 bool ok;
450 m_factor = consumeValue(tmp, "factor", 1).toInt(&ok);
451
452 if (!ok) {
453 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
454 "Spacer (\"%1\") property \"factor\" is no integer value.")
455 .arg(name());
456 return false;
457 }
458 warnAboutUnsupportedKeys(tmp, name(), type());
459
460 return true;
461 }
462
createWidget(const QString & displayName,JsonFieldPage * page)463 QWidget *SpacerField::createWidget(const QString &displayName, JsonFieldPage *page)
464 {
465 Q_UNUSED(displayName)
466 Q_UNUSED(page)
467 int hspace = QApplication::style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing);
468 int vspace = QApplication::style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing);
469 int hsize = hspace * m_factor;
470 int vsize = vspace * m_factor;
471
472 auto w = new QWidget();
473 w->setMinimumSize(hsize, vsize);
474 w->setMaximumSize(hsize, vsize);
475 w->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
476 return w;
477 }
478
479 // --------------------------------------------------------------------
480 // LineEditFieldData:
481 // --------------------------------------------------------------------
482
parseData(const QVariant & data,QString * errorMessage)483 bool LineEditField::parseData(const QVariant &data, QString *errorMessage)
484 {
485 if (data.isNull())
486 return true;
487
488 if (data.type() != QVariant::Map) {
489 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
490 "LineEdit (\"%1\") data is not an object.")
491 .arg(name());
492 return false;
493 }
494
495 QVariantMap tmp = data.toMap();
496
497 m_isPassword = consumeValue(tmp, "isPassword", false).toBool();
498 m_defaultText = JsonWizardFactory::localizedString(consumeValue(tmp, "trText").toString());
499 m_disabledText = JsonWizardFactory::localizedString(consumeValue(tmp, "trDisabledText").toString());
500 m_placeholderText = JsonWizardFactory::localizedString(consumeValue(tmp, "trPlaceholder").toString());
501 m_historyId = consumeValue(tmp, "historyId").toString();
502 m_restoreLastHistoryItem = consumeValue(tmp, "restoreLastHistoryItem", false).toBool();
503 QString pattern = consumeValue(tmp, "validator").toString();
504 if (!pattern.isEmpty()) {
505 m_validatorRegExp = QRegularExpression('^' + pattern + '$');
506 if (!m_validatorRegExp.isValid()) {
507 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
508 "LineEdit (\"%1\") has an invalid regular expression \"%2\" in \"validator\".")
509 .arg(name(), pattern);
510 m_validatorRegExp = QRegularExpression();
511 return false;
512 }
513 }
514 m_fixupExpando = consumeValue(tmp, "fixup").toString();
515
516 const QString completion = consumeValue(tmp, "completion").toString();
517 if (completion == "classes") {
518 m_completion = Completion::Classes;
519 } else if (completion == "namespaces") {
520 m_completion = Completion::Namespaces;
521 } else if (!completion.isEmpty()) {
522 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
523 "LineEdit (\"%1\") has an invalid value \"%2\" in \"completion\".")
524 .arg(name(), completion);
525 return false;
526 }
527
528 warnAboutUnsupportedKeys(tmp, name(), type());
529
530 return true;
531 }
532
createWidget(const QString & displayName,JsonFieldPage * page)533 QWidget *LineEditField::createWidget(const QString &displayName, JsonFieldPage *page)
534 {
535 Q_UNUSED(displayName)
536 const auto w = new LineEdit(page->expander(), m_validatorRegExp);
537 w->setFixupExpando(m_fixupExpando);
538
539 if (!m_historyId.isEmpty())
540 w->setHistoryCompleter(m_historyId, m_restoreLastHistoryItem);
541
542 w->setEchoMode(m_isPassword ? QLineEdit::Password : QLineEdit::Normal);
543 QObject::connect(w, &FancyLineEdit::textEdited, [this] { setHasUserChanges(); });
544 setupCompletion(w);
545
546 return w;
547 }
548
setup(JsonFieldPage * page,const QString & name)549 void LineEditField::setup(JsonFieldPage *page, const QString &name)
550 {
551 auto w = qobject_cast<FancyLineEdit *>(widget());
552 QTC_ASSERT(w, return);
553 page->registerFieldWithName(name, w);
554 QObject::connect(w, &FancyLineEdit::textChanged,
555 page, [this, page]() -> void { m_isModified = true; emit page->completeChanged(); });
556 }
557
validate(MacroExpander * expander,QString * message)558 bool LineEditField::validate(MacroExpander *expander, QString *message)
559 {
560 if (m_isValidating)
561 return true;
562 m_isValidating = true;
563
564 auto w = qobject_cast<FancyLineEdit *>(widget());
565 QTC_ASSERT(w, return false);
566
567 if (w->isEnabled()) {
568 if (m_isModified) {
569 if (!m_currentText.isNull()) {
570 w->setText(m_currentText);
571 m_currentText.clear();
572 }
573 } else {
574 w->setText(expander->expand(m_defaultText));
575 m_isModified = false;
576 }
577 } else {
578 if (!m_disabledText.isNull() && m_currentText.isNull())
579 m_currentText = w->text();
580 }
581
582 const bool baseValid = JsonFieldPage::Field::validate(expander, message);
583 m_isValidating = false;
584 return baseValid && !w->text().isEmpty() && w->isValid();
585 }
586
initializeData(MacroExpander * expander)587 void LineEditField::initializeData(MacroExpander *expander)
588 {
589 auto w = qobject_cast<FancyLineEdit *>(widget());
590 QTC_ASSERT(w, return);
591 m_isValidating = true;
592 w->setText(expander->expand(m_defaultText));
593 w->setPlaceholderText(m_placeholderText);
594 m_isModified = false;
595 m_isValidating = false;
596 }
597
fromSettings(const QVariant & value)598 void LineEditField::fromSettings(const QVariant &value)
599 {
600 m_defaultText = value.toString();
601 }
602
toSettings() const603 QVariant LineEditField::toSettings() const
604 {
605 return qobject_cast<FancyLineEdit *>(widget())->text();
606 }
607
setupCompletion(FancyLineEdit * lineEdit)608 void LineEditField::setupCompletion(FancyLineEdit *lineEdit)
609 {
610 using namespace Core;
611 using namespace Utils;
612 if (m_completion == Completion::None)
613 return;
614 ILocatorFilter * const classesFilter = findOrDefault(
615 ILocatorFilter::allLocatorFilters(),
616 equal(&ILocatorFilter::id, Id("Classes")));
617 if (!classesFilter)
618 return;
619 classesFilter->prepareSearch({});
620 const auto watcher = new QFutureWatcher<LocatorFilterEntry>;
621 const auto handleResults = [this, lineEdit, watcher](int firstIndex, int endIndex) {
622 QSet<QString> namespaces;
623 QStringList classes;
624 Project * const project = ProjectTree::currentProject();
625 for (int i = firstIndex; i < endIndex; ++i) {
626 static const auto isReservedName = [](const QString &name) {
627 static const QRegularExpression rx1("^_[A-Z].*");
628 static const QRegularExpression rx2(".*::_[A-Z].*");
629 return name.contains("__") || rx1.match(name).hasMatch()
630 || rx2.match(name).hasMatch();
631 };
632 const LocatorFilterEntry &entry = watcher->resultAt(i);
633 const bool hasNamespace = !entry.extraInfo.isEmpty()
634 && !entry.extraInfo.startsWith('<') && !entry.extraInfo.contains("::<")
635 && !isReservedName(entry.extraInfo)
636 && !entry.extraInfo.startsWith('~')
637 && !entry.extraInfo.contains("Anonymous:")
638 && !FileUtils::isAbsolutePath(entry.extraInfo);
639 const bool isBaseClassCandidate = !isReservedName(entry.displayName)
640 && !entry.displayName.startsWith("Anonymous:");
641 if (isBaseClassCandidate)
642 classes << entry.displayName;
643 if (hasNamespace) {
644 if (isBaseClassCandidate)
645 classes << (entry.extraInfo + "::" + entry.displayName);
646 if (m_completion == Completion::Namespaces) {
647 if (!project
648 || entry.filePath.startsWith(project->projectDirectory().toString())) {
649 namespaces << entry.extraInfo;
650 }
651 }
652 }
653 }
654 QStringList completionList;
655 if (m_completion == Completion::Namespaces) {
656 completionList = toList(namespaces);
657 completionList = filtered(completionList, [&classes](const QString &ns) {
658 return !classes.contains(ns);
659 });
660 completionList = transform(completionList, [](const QString &ns) {
661 return QString(ns + "::");
662 });
663 } else {
664 completionList = classes;
665 }
666 completionList.sort();
667 lineEdit->setSpecialCompleter(new QCompleter(completionList, lineEdit));
668 };
669 QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::resultsReadyAt, lineEdit,
670 handleResults);
671 QObject::connect(watcher, &QFutureWatcher<LocatorFilterEntry>::finished,
672 watcher, &QFutureWatcher<LocatorFilterEntry>::deleteLater);
673 watcher->setFuture(runAsync([classesFilter](QFutureInterface<LocatorFilterEntry> &f) {
674 const QList<LocatorFilterEntry> matches = classesFilter->matchesFor(f, {});
675 if (!matches.isEmpty())
676 f.reportResults(QVector<LocatorFilterEntry>(matches.cbegin(), matches.cend()));
677 f.reportFinished();
678 }));
679 }
680
681 // --------------------------------------------------------------------
682 // TextEditFieldData:
683 // --------------------------------------------------------------------
684
685
parseData(const QVariant & data,QString * errorMessage)686 bool TextEditField::parseData(const QVariant &data, QString *errorMessage)
687 {
688 if (data.isNull())
689 return true;
690
691 if (data.type() != QVariant::Map) {
692 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
693 "TextEdit (\"%1\") data is not an object.")
694 .arg(name());
695 return false;
696 }
697
698 QVariantMap tmp = data.toMap();
699
700 m_defaultText = JsonWizardFactory::localizedString(consumeValue(tmp, "trText").toString());
701 m_disabledText = JsonWizardFactory::localizedString(consumeValue(tmp, "trDisabledText").toString());
702 m_acceptRichText = consumeValue(tmp, "richText", true).toBool();
703
704 warnAboutUnsupportedKeys(tmp, name(), type());
705 return true;
706 }
707
createWidget(const QString & displayName,JsonFieldPage * page)708 QWidget *TextEditField::createWidget(const QString &displayName, JsonFieldPage *page)
709 {
710 // TODO: Set up modification monitoring...
711 Q_UNUSED(displayName)
712 Q_UNUSED(page)
713 auto w = new QTextEdit;
714 w->setAcceptRichText(m_acceptRichText);
715 QObject::connect(w, &QTextEdit::textChanged, [this, w] {
716 if (w->toPlainText() != m_defaultText)
717 setHasUserChanges();
718 });
719 return w;
720 }
721
setup(JsonFieldPage * page,const QString & name)722 void TextEditField::setup(JsonFieldPage *page, const QString &name)
723 {
724 auto w = qobject_cast<QTextEdit *>(widget());
725 QTC_ASSERT(w, return);
726 page->registerFieldWithName(name, w, "plainText", SIGNAL(textChanged()));
727 QObject::connect(w, &QTextEdit::textChanged, page, &QWizardPage::completeChanged);
728 }
729
validate(MacroExpander * expander,QString * message)730 bool TextEditField::validate(MacroExpander *expander, QString *message)
731 {
732 if (!JsonFieldPage::Field::validate(expander, message))
733 return false;
734
735 auto w = qobject_cast<QTextEdit *>(widget());
736 QTC_ASSERT(w, return false);
737
738 if (!w->isEnabled() && !m_disabledText.isNull() && m_currentText.isNull()) {
739 m_currentText = w->toHtml();
740 w->setPlainText(expander->expand(m_disabledText));
741 } else if (w->isEnabled() && !m_currentText.isNull()) {
742 w->setText(m_currentText);
743 m_currentText.clear();
744 }
745
746 return !w->toPlainText().isEmpty();
747 }
748
initializeData(MacroExpander * expander)749 void TextEditField::initializeData(MacroExpander *expander)
750 {
751 auto w = qobject_cast<QTextEdit *>(widget());
752 QTC_ASSERT(w, return);
753 w->setPlainText(expander->expand(m_defaultText));
754 }
755
fromSettings(const QVariant & value)756 void TextEditField::fromSettings(const QVariant &value)
757 {
758 m_defaultText = value.toString();
759 }
760
toSettings() const761 QVariant TextEditField::toSettings() const
762 {
763 return qobject_cast<QTextEdit *>(widget())->toPlainText();
764 }
765
766 // --------------------------------------------------------------------
767 // PathChooserFieldData:
768 // --------------------------------------------------------------------
769
parseData(const QVariant & data,QString * errorMessage)770 bool PathChooserField::parseData(const QVariant &data, QString *errorMessage)
771 {
772 if (data.isNull())
773 return true;
774
775 if (data.type() != QVariant::Map) {
776 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
777 "PathChooser data is not an object.");
778 return false;
779 }
780
781 QVariantMap tmp = data.toMap();
782
783 m_path = consumeValue(tmp, "path").toString();
784 m_basePath = consumeValue(tmp, "basePath").toString();
785 m_historyId = consumeValue(tmp, "historyId").toString();
786
787 QString kindStr = consumeValue(tmp, "kind", "existingDirectory").toString();
788 if (kindStr == "existingDirectory") {
789 m_kind = PathChooser::ExistingDirectory;
790 } else if (kindStr == "directory") {
791 m_kind = PathChooser::Directory;
792 } else if (kindStr == "file") {
793 m_kind = PathChooser::File;
794 } else if (kindStr == "saveFile") {
795 m_kind = PathChooser::SaveFile;
796 } else if (kindStr == "existingCommand") {
797 m_kind = PathChooser::ExistingCommand;
798 } else if (kindStr == "command") {
799 m_kind = PathChooser::Command;
800 } else if (kindStr == "any") {
801 m_kind = PathChooser::Any;
802 } else {
803 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
804 "kind \"%1\" is not one of the supported \"existingDirectory\", "
805 "\"directory\", \"file\", \"saveFile\", \"existingCommand\", "
806 "\"command\", \"any\".")
807 .arg(kindStr);
808 return false;
809 }
810
811 warnAboutUnsupportedKeys(tmp, name(), type());
812 return true;
813 }
814
createWidget(const QString & displayName,JsonFieldPage * page)815 QWidget *PathChooserField::createWidget(const QString &displayName, JsonFieldPage *page)
816 {
817 Q_UNUSED(displayName)
818 Q_UNUSED(page)
819 auto w = new PathChooser;
820 if (!m_historyId.isEmpty())
821 w->setHistoryCompleter(m_historyId);
822 QObject::connect(w, &PathChooser::pathChanged, [this, w] {
823 if (w->filePath().toString() != m_path)
824 setHasUserChanges();
825 });
826 return w;
827 }
828
setEnabled(bool e)829 void PathChooserField::setEnabled(bool e)
830 {
831 auto w = qobject_cast<PathChooser *>(widget());
832 QTC_ASSERT(w, return);
833 w->setReadOnly(!e);
834 }
835
setup(JsonFieldPage * page,const QString & name)836 void PathChooserField::setup(JsonFieldPage *page, const QString &name)
837 {
838 auto w = qobject_cast<PathChooser *>(widget());
839 QTC_ASSERT(w, return);
840 page->registerFieldWithName(name, w, "path", SIGNAL(rawPathChanged(QString)));
841 QObject::connect(w, &PathChooser::rawPathChanged,
842 page, [page](QString) { emit page->completeChanged(); });
843 }
844
validate(MacroExpander * expander,QString * message)845 bool PathChooserField::validate(MacroExpander *expander, QString *message)
846 {
847 if (!JsonFieldPage::Field::validate(expander, message))
848 return false;
849
850 auto w = qobject_cast<PathChooser *>(widget());
851 QTC_ASSERT(w, return false);
852 return w->isValid();
853 }
854
initializeData(MacroExpander * expander)855 void PathChooserField::initializeData(MacroExpander *expander)
856 {
857 auto w = qobject_cast<PathChooser *>(widget());
858 QTC_ASSERT(w, return);
859 w->setBaseDirectory(expander->expand(FilePath::fromString(m_basePath)));
860 w->setExpectedKind(m_kind);
861
862 if (m_currentPath.isNull())
863 w->setPath(expander->expand(m_path));
864 else
865 w->setPath(m_currentPath);
866 }
867
fromSettings(const QVariant & value)868 void PathChooserField::fromSettings(const QVariant &value)
869 {
870 m_path = value.toString();
871 }
872
toSettings() const873 QVariant PathChooserField::toSettings() const
874 {
875 return qobject_cast<PathChooser *>(widget())->filePath().toString();
876 }
877
878 // --------------------------------------------------------------------
879 // CheckBoxFieldData:
880 // --------------------------------------------------------------------
881
parseData(const QVariant & data,QString * errorMessage)882 bool CheckBoxField::parseData(const QVariant &data, QString *errorMessage)
883 {
884 if (data.isNull())
885 return true;
886
887 if (data.type() != QVariant::Map) {
888 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
889 "CheckBox (\"%1\") data is not an object.")
890 .arg(name());
891 return false;
892 }
893
894 QVariantMap tmp = data.toMap();
895
896 m_checkedValue = consumeValue(tmp, "checkedValue", true).toString();
897 m_uncheckedValue = consumeValue(tmp, "uncheckedValue", false).toString();
898 if (m_checkedValue == m_uncheckedValue) {
899 *errorMessage= QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
900 "CheckBox (\"%1\") values for checked and unchecked state are identical.")
901 .arg(name());
902 return false;
903 }
904 m_checkedExpression = consumeValue(tmp, "checked", false);
905
906 warnAboutUnsupportedKeys(tmp, name(), type());
907 return true;
908 }
909
createWidget(const QString & displayName,JsonFieldPage * page)910 QWidget *CheckBoxField::createWidget(const QString &displayName, JsonFieldPage *page)
911 {
912 Q_UNUSED(page)
913 return new QCheckBox(displayName);
914 }
915
setup(JsonFieldPage * page,const QString & name)916 void CheckBoxField::setup(JsonFieldPage *page, const QString &name)
917 {
918 auto w = qobject_cast<QCheckBox *>(widget());
919 QTC_ASSERT(w, return);
920 page->registerObjectAsFieldWithName<QCheckBox>(name, w, &QCheckBox::stateChanged, [this, page, w] () -> QString {
921 if (w->checkState() == Qt::Checked)
922 return page->expander()->expand(m_checkedValue);
923 return page->expander()->expand(m_uncheckedValue);
924 });
925
926 QObject::connect(w, &QCheckBox::clicked, page, [this, page]() {
927 m_isModified = true;
928 setHasUserChanges();
929 emit page->completeChanged();
930 });
931 }
932
validate(MacroExpander * expander,QString * message)933 bool CheckBoxField::validate(MacroExpander *expander, QString *message)
934 {
935 if (!JsonFieldPage::Field::validate(expander, message))
936 return false;
937
938 if (!m_isModified) {
939 auto w = qobject_cast<QCheckBox *>(widget());
940 QTC_ASSERT(w, return false);
941 w->setChecked(JsonWizard::boolFromVariant(m_checkedExpression, expander));
942 }
943 return true;
944 }
945
initializeData(MacroExpander * expander)946 void CheckBoxField::initializeData(MacroExpander *expander)
947 {
948 auto w = qobject_cast<QCheckBox *>(widget());
949 QTC_ASSERT(widget(), return);
950
951 w->setChecked(JsonWizard::boolFromVariant(m_checkedExpression, expander));
952 }
953
fromSettings(const QVariant & value)954 void CheckBoxField::fromSettings(const QVariant &value)
955 {
956 m_checkedExpression = value;
957 }
958
toSettings() const959 QVariant CheckBoxField::toSettings() const
960 {
961 return qobject_cast<QCheckBox *>(widget())->isChecked();
962 }
963
964 // --------------------------------------------------------------------
965 // ListFieldData:
966 // --------------------------------------------------------------------
967
createStandardItemFromListItem(const QVariant & item,QString * errorMessage)968 std::unique_ptr<QStandardItem> createStandardItemFromListItem(const QVariant &item, QString *errorMessage)
969 {
970 if (item.type() == QVariant::List) {
971 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
972 "No JSON lists allowed inside List items.");
973 return {};
974 }
975 auto standardItem = std::make_unique<QStandardItem>();
976 if (item.type() == QVariant::Map) {
977 QVariantMap tmp = item.toMap();
978 const QString key = JsonWizardFactory::localizedString(consumeValue(tmp, "trKey", QString()).toString());
979 const QVariant value = consumeValue(tmp, "value", key);
980
981 if (key.isNull() || key.isEmpty()) {
982 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
983 "No \"key\" found in List items.");
984 return {};
985 }
986 standardItem->setText(key);
987 standardItem->setData(value, ListField::ValueRole);
988 standardItem->setData(consumeValue(tmp, "condition", true), ListField::ConditionRole);
989 standardItem->setData(consumeValue(tmp, "icon"), ListField::IconStringRole);
990 standardItem->setToolTip(JsonWizardFactory::localizedString(consumeValue(tmp, "trToolTip", QString()).toString()));
991 warnAboutUnsupportedKeys(tmp, QString(), "List");
992 } else {
993 const QString keyvalue = item.toString();
994 standardItem->setText(keyvalue);
995 standardItem->setData(keyvalue, ListField::ValueRole);
996 standardItem->setData(true, ListField::ConditionRole);
997 }
998 return standardItem;
999 }
1000
1001 ListField::ListField() = default;
1002
1003 ListField::~ListField() = default;
1004
parseData(const QVariant & data,QString * errorMessage)1005 bool ListField::parseData(const QVariant &data, QString *errorMessage)
1006 {
1007 if (data.type() != QVariant::Map) {
1008 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
1009 "%1 (\"%2\") data is not an object.")
1010 .arg(type(), name());
1011 return false;
1012 }
1013
1014 QVariantMap tmp = data.toMap();
1015
1016 bool ok;
1017 m_index = consumeValue(tmp, "index", 0).toInt(&ok);
1018 if (!ok) {
1019 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
1020 "%1 (\"%2\") \"index\" is not an integer value.")
1021 .arg(type(), name());
1022 return false;
1023 }
1024 m_disabledIndex = consumeValue(tmp, "disabledIndex", -1).toInt(&ok);
1025 if (!ok) {
1026 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
1027 "%1 (\"%2\") \"disabledIndex\" is not an integer value.")
1028 .arg(type(), name());
1029 return false;
1030 }
1031
1032 const QVariant value = consumeValue(tmp, "items");
1033 if (value.isNull()) {
1034 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
1035 "%1 (\"%2\") \"items\" missing.")
1036 .arg(type(), name());
1037 return false;
1038 }
1039 if (value.type() != QVariant::List) {
1040 *errorMessage = QCoreApplication::translate("ProjectExplorer::JsonFieldPage",
1041 "%1 (\"%2\") \"items\" is not a JSON list.")
1042 .arg(type(), name());
1043 return false;
1044 }
1045
1046 for (const QVariant &i : value.toList()) {
1047 std::unique_ptr<QStandardItem> item = createStandardItemFromListItem(i, errorMessage);
1048 QTC_ASSERT(!item || !item->text().isEmpty(), continue);
1049 m_itemList.emplace_back(std::move(item));
1050 }
1051
1052 warnAboutUnsupportedKeys(tmp, name(), type());
1053 return true;
1054 }
1055
1056
validate(MacroExpander * expander,QString * message)1057 bool ListField::validate(MacroExpander *expander, QString *message)
1058 {
1059 if (!JsonFieldPage::Field::validate(expander, message))
1060 return false;
1061
1062 updateIndex();
1063 return selectionModel()->hasSelection();
1064 }
1065
initializeData(MacroExpander * expander)1066 void ListField::initializeData(MacroExpander *expander)
1067 {
1068 QTC_ASSERT(widget(), return);
1069
1070 if (m_index >= int(m_itemList.size())) {
1071 qWarning().noquote() << QString("%1 (\"%2\") has an index of %3 which does not exist.").arg(type(), name(), QString::number(m_index));
1072 m_index = -1;
1073 }
1074
1075 QStandardItem *currentItem = m_index >= 0 ? m_itemList[uint(m_index)].get() : nullptr;
1076 QList<QStandardItem*> expandedValuesItems;
1077 expandedValuesItems.reserve(int(m_itemList.size()));
1078
1079 for (const std::unique_ptr<QStandardItem> &item : m_itemList) {
1080 bool condition = JsonWizard::boolFromVariant(item->data(ConditionRole), expander);
1081 if (!condition)
1082 continue;
1083 QStandardItem *expandedValuesItem = item->clone();
1084 if (item.get() == currentItem)
1085 currentItem = expandedValuesItem;
1086 expandedValuesItem->setText(expander->expand(item->text()));
1087 expandedValuesItem->setData(expander->expandVariant(item->data(ValueRole)), ValueRole);
1088 expandedValuesItem->setData(expander->expand(item->data(IconStringRole).toString()), IconStringRole);
1089 expandedValuesItem->setData(condition, ConditionRole);
1090
1091 QString iconPath = expandedValuesItem->data(IconStringRole).toString();
1092 if (!iconPath.isEmpty()) {
1093 if (auto *page = qobject_cast<JsonFieldPage*>(widget()->parentWidget())) {
1094 const QString wizardDirectory = page->value("WizardDir").toString();
1095 iconPath = QDir::cleanPath(QDir(wizardDirectory).absoluteFilePath(iconPath));
1096 if (QFileInfo::exists(iconPath)) {
1097 QIcon icon(iconPath);
1098 expandedValuesItem->setIcon(icon);
1099 addPossibleIconSize(icon);
1100 } else {
1101 qWarning().noquote() << QString("Icon file \"%1\" not found.").arg(QDir::toNativeSeparators(iconPath));
1102 }
1103 } else {
1104 qWarning().noquote() << QString("%1 (\"%2\") has no parentWidget JsonFieldPage to get the icon path.").arg(type(), name());
1105 }
1106 }
1107 expandedValuesItems.append(expandedValuesItem);
1108 }
1109
1110 itemModel()->clear();
1111 itemModel()->appendColumn(expandedValuesItems); // inserts the first column
1112
1113 selectionModel()->setCurrentIndex(itemModel()->indexFromItem(currentItem), QItemSelectionModel::ClearAndSelect);
1114
1115 updateIndex();
1116 }
1117
itemModel()1118 QStandardItemModel *ListField::itemModel()
1119 {
1120 if (!m_itemModel)
1121 m_itemModel = new QStandardItemModel(widget());
1122 return m_itemModel;
1123 }
1124
selectionModel() const1125 QItemSelectionModel *ListField::selectionModel() const
1126 {
1127 return m_selectionModel;
1128 }
1129
setSelectionModel(QItemSelectionModel * selectionModel)1130 void ListField::setSelectionModel(QItemSelectionModel *selectionModel)
1131 {
1132 m_selectionModel = selectionModel;
1133 }
1134
maxIconSize()1135 QSize ListField::maxIconSize()
1136 {
1137 return m_maxIconSize;
1138 }
1139
addPossibleIconSize(const QIcon & icon)1140 void ListField::addPossibleIconSize(const QIcon &icon)
1141 {
1142 const QSize iconSize = icon.availableSizes().value(0);
1143 if (iconSize.height() > m_maxIconSize.height())
1144 m_maxIconSize = iconSize;
1145 }
1146
updateIndex()1147 void ListField::updateIndex()
1148 {
1149 if (!widget()->isEnabled() && m_disabledIndex >= 0 && m_savedIndex < 0) {
1150 m_savedIndex = selectionModel()->currentIndex().row();
1151 selectionModel()->setCurrentIndex(itemModel()->index(m_disabledIndex, 0), QItemSelectionModel::ClearAndSelect);
1152 } else if (widget()->isEnabled() && m_savedIndex >= 0) {
1153 selectionModel()->setCurrentIndex(itemModel()->index(m_savedIndex, 0), QItemSelectionModel::ClearAndSelect);
1154 m_savedIndex = -1;
1155 }
1156 }
1157
fromSettings(const QVariant & value)1158 void ListField::fromSettings(const QVariant &value)
1159 {
1160 for (decltype(m_itemList)::size_type i = 0; i < m_itemList.size(); ++i) {
1161 if (m_itemList.at(i)->data(ValueRole) == value) {
1162 m_index = int(i);
1163 break;
1164 }
1165 }
1166 }
1167
toSettings() const1168 QVariant ListField::toSettings() const
1169 {
1170 const int idx = selectionModel()->currentIndex().row();
1171 return idx >= 0 ? m_itemList.at(idx)->data(ValueRole) : QVariant();
1172 }
1173
setup(JsonFieldPage * page,const QString & name)1174 void ComboBoxField::setup(JsonFieldPage *page, const QString &name)
1175 {
1176 auto w = qobject_cast<QComboBox*>(widget());
1177 QTC_ASSERT(w, return);
1178 w->setModel(itemModel());
1179 w->setInsertPolicy(QComboBox::NoInsert);
1180
1181 QSizePolicy s = w->sizePolicy();
1182 s.setHorizontalPolicy(QSizePolicy::Expanding);
1183 w->setSizePolicy(s);
1184
1185 setSelectionModel(w->view()->selectionModel());
1186
1187 // the selectionModel does not behave like expected and wanted - so we block signals here
1188 // (for example there was some losing focus thing when hovering over items, ...)
1189 selectionModel()->blockSignals(true);
1190 QObject::connect(w, QOverload<int>::of(&QComboBox::activated), [w, this](int index) {
1191 w->blockSignals(true);
1192 selectionModel()->clearSelection();
1193
1194 selectionModel()->blockSignals(false);
1195 selectionModel()->setCurrentIndex(w->model()->index(index, 0),
1196 QItemSelectionModel::ClearAndSelect);
1197 selectionModel()->blockSignals(true);
1198 w->blockSignals(false);
1199 });
1200 page->registerObjectAsFieldWithName<QComboBox>(name, w, QOverload<int>::of(&QComboBox::activated), [w]() {
1201 return w->currentData(ValueRole);
1202 });
1203 QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() {
1204 emit page->completeChanged();
1205 });
1206 }
1207
createWidget(const QString &,JsonFieldPage *)1208 QWidget *ComboBoxField::createWidget(const QString & /*displayName*/, JsonFieldPage * /*page*/)
1209 {
1210 const auto comboBox = new QComboBox;
1211 QObject::connect(comboBox, QOverload<int>::of(&QComboBox::activated),
1212 [this] { setHasUserChanges(); });
1213 return comboBox;
1214 }
1215
initializeData(MacroExpander * expander)1216 void ComboBoxField::initializeData(MacroExpander *expander)
1217 {
1218 ListField::initializeData(expander);
1219 // refresh also the current text of the combobox
1220 auto w = qobject_cast<QComboBox*>(widget());
1221 w->setCurrentIndex(selectionModel()->currentIndex().row());
1222 }
1223
toSettings() const1224 QVariant ComboBoxField::toSettings() const
1225 {
1226 if (auto w = qobject_cast<QComboBox*>(widget()))
1227 return w->currentData(ValueRole);
1228 return {};
1229 }
1230
setup(JsonFieldPage * page,const QString & name)1231 void IconListField::setup(JsonFieldPage *page, const QString &name)
1232 {
1233 auto w = qobject_cast<QListView*>(widget());
1234 QTC_ASSERT(w, return);
1235
1236 w->setViewMode(QListView::IconMode);
1237 w->setMovement(QListView::Static);
1238 w->setResizeMode(QListView::Adjust);
1239 w->setSelectionRectVisible(false);
1240 w->setWrapping(true);
1241 w->setWordWrap(true);
1242
1243 w->setModel(itemModel());
1244 setSelectionModel(w->selectionModel());
1245 page->registerObjectAsFieldWithName<QItemSelectionModel>(name, selectionModel(), &QItemSelectionModel::selectionChanged, [this]() {
1246 const QModelIndex i = selectionModel()->currentIndex();
1247 if (i.isValid())
1248 return i.data(ValueRole);
1249 return QVariant();
1250 });
1251 QObject::connect(selectionModel(), &QItemSelectionModel::selectionChanged, page, [page]() {
1252 emit page->completeChanged();
1253 });
1254 }
1255
createWidget(const QString &,JsonFieldPage *)1256 QWidget *IconListField::createWidget(const QString & /*displayName*/, JsonFieldPage * /*page*/)
1257 {
1258 const auto listView = new QListView;
1259 QObject::connect(listView->selectionModel(), &QItemSelectionModel::currentChanged,
1260 [this] { setHasUserChanges(); });
1261 return listView;
1262 }
1263
initializeData(MacroExpander * expander)1264 void IconListField::initializeData(MacroExpander *expander)
1265 {
1266 ListField::initializeData(expander);
1267 auto w = qobject_cast<QListView*>(widget());
1268 const int spacing = 4;
1269 w->setSpacing(spacing);
1270 w->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
1271
1272 // adding a third hight of the icon to see following items if there are some
1273 w->setMinimumHeight(maxIconSize().height() + maxIconSize().height() / 3);
1274 w->setIconSize(maxIconSize());
1275 }
1276
1277 // --------------------------------------------------------------------
1278 // JsonFieldPage:
1279 // --------------------------------------------------------------------
1280
1281 QHash<QString, JsonFieldPage::FieldFactory> JsonFieldPage::m_factories;
1282
JsonFieldPage(MacroExpander * expander,QWidget * parent)1283 JsonFieldPage::JsonFieldPage(MacroExpander *expander, QWidget *parent) :
1284 WizardPage(parent),
1285 m_formLayout(new QFormLayout),
1286 m_errorLabel(new QLabel),
1287 m_expander(expander)
1288 {
1289 QTC_CHECK(m_expander);
1290
1291 auto vLayout = new QVBoxLayout;
1292 m_formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
1293 vLayout->addLayout(m_formLayout);
1294 m_errorLabel->setVisible(false);
1295 QPalette palette = m_errorLabel->palette();
1296 palette.setColor(QPalette::WindowText, creatorTheme()->color(Theme::TextColorError));
1297 m_errorLabel->setPalette(palette);
1298 vLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::MinimumExpanding));
1299 vLayout->addWidget(m_errorLabel);
1300 setLayout(vLayout);
1301 }
1302
~JsonFieldPage()1303 JsonFieldPage::~JsonFieldPage()
1304 {
1305 // Do not delete m_expander, it belongs to the wizard!
1306 qDeleteAll(m_fields);
1307 }
1308
registerFieldFactory(const QString & id,const JsonFieldPage::FieldFactory & ff)1309 void JsonFieldPage::registerFieldFactory(const QString &id, const JsonFieldPage::FieldFactory &ff)
1310 {
1311 QTC_ASSERT(!m_factories.contains(id), return);
1312 m_factories.insert(id, ff);
1313 }
1314
setup(const QVariant & data)1315 bool JsonFieldPage::setup(const QVariant &data)
1316 {
1317 QString errorMessage;
1318 QList<QVariant> fieldList = JsonWizardFactory::objectOrList(data, &errorMessage);
1319 foreach (const QVariant &field, fieldList) {
1320 Field *f = JsonFieldPage::Field::parse(field, &errorMessage);
1321 if (!f)
1322 continue;
1323 f->createWidget(this);
1324 if (!f->persistenceKey().isEmpty()) {
1325 f->setPersistenceKey(m_expander->expand(f->persistenceKey()));
1326 const QVariant value = Core::ICore::settings()
1327 ->value(fullSettingsKey(f->persistenceKey()));
1328 if (value.isValid())
1329 f->fromSettings(value);
1330 }
1331 m_fields.append(f);
1332 }
1333 return true;
1334 }
1335
isComplete() const1336 bool JsonFieldPage::isComplete() const
1337 {
1338 QString message;
1339
1340 bool result = true;
1341 bool hasErrorMessage = false;
1342 foreach (Field *f, m_fields) {
1343 f->adjustState(m_expander);
1344 if (!f->validate(m_expander, &message)) {
1345 if (!message.isEmpty()) {
1346 showError(message);
1347 hasErrorMessage = true;
1348 }
1349 if (f->isMandatory() && !f->widget()->isHidden())
1350 result = false;
1351 }
1352 }
1353
1354 if (!hasErrorMessage)
1355 clearError();
1356
1357 return result;
1358 }
1359
initializePage()1360 void JsonFieldPage::initializePage()
1361 {
1362 foreach (Field *f, m_fields)
1363 f->initialize(m_expander);
1364 }
1365
cleanupPage()1366 void JsonFieldPage::cleanupPage()
1367 {
1368 foreach (Field *f, m_fields)
1369 f->cleanup(m_expander);
1370 }
1371
validatePage()1372 bool JsonFieldPage::validatePage()
1373 {
1374 for (Field * const f : qAsConst(m_fields))
1375 if (!f->persistenceKey().isEmpty() && f->hasUserChanges()) {
1376 const QVariant value = f->toSettings();
1377 if (value.isValid())
1378 Core::ICore::settings()->setValue(fullSettingsKey(f->persistenceKey()), value);
1379 }
1380 return true;
1381 }
1382
showError(const QString & m) const1383 void JsonFieldPage::showError(const QString &m) const
1384 {
1385 m_errorLabel->setText(m);
1386 m_errorLabel->setVisible(true);
1387 }
1388
clearError() const1389 void JsonFieldPage::clearError() const
1390 {
1391 m_errorLabel->setText(QString());
1392 m_errorLabel->setVisible(false);
1393 }
1394
expander()1395 MacroExpander *JsonFieldPage::expander()
1396 {
1397 return m_expander;
1398 }
1399
createFieldData(const QString & type)1400 JsonFieldPage::Field *JsonFieldPage::createFieldData(const QString &type)
1401 {
1402 if (auto factory = m_factories.value(type)) {
1403 JsonFieldPage::Field *field = factory();
1404 field->setType(type);
1405 return field;
1406 }
1407 return nullptr;
1408 }
1409
fullSettingsKey(const QString & fieldKey)1410 QString JsonFieldPage::fullSettingsKey(const QString &fieldKey)
1411 {
1412 return "Wizards/" + fieldKey;
1413 }
1414
1415 } // namespace ProjectExplorer
1416