Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdesigner_promotiondialog.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3// Qt-Security score:significant reason:default
4
7#include "iconloader_p.h"
10
11#include <QtDesigner/abstractformeditor.h>
12#include <QtDesigner/abstractformwindow.h>
13#include <QtDesigner/abstractpromotioninterface.h>
14#include <QtDesigner/abstractwidgetdatabase.h>
15#include <QtDesigner/abstractintegration.h>
16#include <abstractdialoggui_p.h>
17
18#include <QtWidgets/qboxlayout.h>
19#include <QtWidgets/qformlayout.h>
20#include <QtWidgets/qdialogbuttonbox.h>
21#include <QtWidgets/qtreeview.h>
22#include <QtWidgets/qheaderview.h>
23#include <QtWidgets/qpushbutton.h>
24#include <QtWidgets/qcombobox.h>
25#include <QtWidgets/qlineedit.h>
26#include <QtWidgets/qcheckbox.h>
27#include <QtWidgets/qlabel.h>
28#include <QtWidgets/qlayoutitem.h>
29#include <QtWidgets/qmenu.h>
30
31#include <QtGui/qaction.h>
32#include <QtGui/qvalidator.h>
33
34#include <QtCore/qitemselectionmodel.h>
35#include <QtCore/qtimer.h>
36
38
39using namespace Qt::StringLiterals;
40
41namespace qdesigner_internal {
42 // PromotionParameters
48
49 // NewPromotedClassPanel
50 NewPromotedClassPanel::NewPromotedClassPanel(const QStringList &baseClasses,
51 int selectedBaseClass,
52 QWidget *parent) :
53 QGroupBox(parent),
54 m_baseClassCombo(new QComboBox),
55 m_classNameEdit(new QLineEdit),
56 m_includeFileEdit(new QLineEdit),
57 m_globalIncludeCheckBox(new QCheckBox),
58 m_addButton(new QPushButton(tr("Add")))
59 {
60 setTitle(tr("New Promoted Class"));
61 setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum);
62 QHBoxLayout *hboxLayout = new QHBoxLayout(this);
63
64 m_classNameEdit->setValidator(new QRegularExpressionValidator(QRegularExpression(u"^[_a-zA-Z:][:_a-zA-Z0-9]*$"_s), m_classNameEdit));
65 connect(m_classNameEdit, &QLineEdit::textChanged,
66 this, &NewPromotedClassPanel::slotNameChanged);
67 connect(m_includeFileEdit, &QLineEdit::textChanged,
68 this, &NewPromotedClassPanel::slotIncludeFileChanged);
69
70 m_baseClassCombo->setEditable(false);
71 m_baseClassCombo->addItems(baseClasses);
72 if (selectedBaseClass != -1)
73 m_baseClassCombo->setCurrentIndex(selectedBaseClass);
74
75 // Grid
76 QFormLayout *formLayout = new QFormLayout();
77 formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); // Mac
78 formLayout->addRow(tr("Base class name:"), m_baseClassCombo);
79 formLayout->addRow(tr("Promoted class name:"), m_classNameEdit);
80
81 QString toolTip = tr("Header file for C++ classes or module name for Qt for Python.");
82 auto *label = new QLabel(tr("Header file:"));
83 label->setToolTip(toolTip);
84 formLayout->addRow(label, m_includeFileEdit);
85 m_includeFileEdit->setToolTip(toolTip);
86
87 toolTip = tr("Indicates that the header file is a global header file. Does not have any effect on Qt for Python.");
88 label = new QLabel(tr("Global include"));
89 label->setToolTip(toolTip);
90 formLayout->addRow(label, m_globalIncludeCheckBox);
91 m_globalIncludeCheckBox->setToolTip(toolTip);
92
93 hboxLayout->addLayout(formLayout);
94 hboxLayout->addItem(new QSpacerItem(15, 0, QSizePolicy::Fixed, QSizePolicy::Ignored));
95 // Button box
96 QVBoxLayout *buttonLayout = new QVBoxLayout();
97
98 m_addButton->setAutoDefault(false);
99 connect(m_addButton, &QAbstractButton::clicked, this, &NewPromotedClassPanel::slotAdd);
100 m_addButton->setEnabled(false);
101 buttonLayout->addWidget(m_addButton);
102
103 QPushButton *resetButton = new QPushButton(tr("Reset"));
104 resetButton->setAutoDefault(false);
105 connect(resetButton, &QAbstractButton::clicked, this, &NewPromotedClassPanel::slotReset);
106
107 buttonLayout->addWidget(resetButton);
108 buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding));
109 hboxLayout->addLayout(buttonLayout);
110
111 enableButtons();
112 }
113
114 void NewPromotedClassPanel::slotAdd() {
115 bool ok = false;
116 emit newPromotedClass(promotionParameters(), &ok);
117 if (ok)
118 slotReset();
119 }
120
121 void NewPromotedClassPanel::slotReset() {
122 const QString empty;
123 m_classNameEdit->setText(empty);
124 m_includeFileEdit->setText(empty);
125 m_globalIncludeCheckBox->setCheckState(Qt::Unchecked);
126 }
127
128 void NewPromotedClassPanel::grabFocus() {
129 m_classNameEdit->setFocus(Qt::OtherFocusReason);
130 }
131
132 void NewPromotedClassPanel::slotNameChanged(const QString &className) {
133 // Suggest a name
134 if (!className.isEmpty()) {
135 QString suggestedHeader = m_promotedHeaderLowerCase ?
136 className.toLower() : className;
137 suggestedHeader.replace("::"_L1, "_"_L1);
138 if (!m_promotedHeaderSuffix.startsWith(u'.'))
139 suggestedHeader += u'.';
140 suggestedHeader += m_promotedHeaderSuffix;
141
142 const bool blocked = m_includeFileEdit->blockSignals(true);
143 m_includeFileEdit->setText(suggestedHeader);
144 m_includeFileEdit->blockSignals(blocked);
145 }
146 enableButtons();
147 }
148
149 void NewPromotedClassPanel::slotIncludeFileChanged(const QString &){
150 enableButtons();
151 }
152
153 void NewPromotedClassPanel::enableButtons() {
154 const bool enabled = !m_classNameEdit->text().isEmpty() && !m_includeFileEdit->text().isEmpty();
155 m_addButton->setEnabled(enabled);
156 m_addButton->setDefault(enabled);
157 }
158
159 PromotionParameters NewPromotedClassPanel::promotionParameters() const {
161 rc.m_baseClass = m_baseClassCombo->currentText();
162 rc.m_className = m_classNameEdit->text();
163 rc.m_includeFile = buildIncludeFile(m_includeFileEdit->text(),
164 m_globalIncludeCheckBox->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal);
165 return rc;
166 }
167
168 void NewPromotedClassPanel::chooseBaseClass(const QString &baseClass) {
169 const int index = m_baseClassCombo->findText (baseClass);
170 if (index != -1)
171 m_baseClassCombo->setCurrentIndex (index);
172 }
173
174 // --------------- QDesignerPromotionDialog
175 QDesignerPromotionDialog::QDesignerPromotionDialog(QDesignerFormEditorInterface *core,
176 QWidget *parent,
177 const QString &promotableWidgetClassName,
178 QString *promoteTo) :
182 m_core(core),
184 m_promotion(core->promotion()),
185 m_model(new PromotionModel(core)),
187 m_buttonBox(nullptr),
188 m_removeButton(new QPushButton(createIconSet("minus.png"_L1), QString()))
189 {
190 m_buttonBox = createButtonBox();
191 setModal(true);
192 setWindowTitle(tr("Promoted Widgets"));
193
194 QVBoxLayout *vboxLayout = new QVBoxLayout(this);
195
196 // tree view group
197 QGroupBox *treeViewGroup = new QGroupBox();
198 treeViewGroup->setTitle(tr("Promoted Classes"));
199 QVBoxLayout *treeViewVBoxLayout = new QVBoxLayout(treeViewGroup);
200 // tree view
201 m_treeView->setModel (m_model);
202 m_treeView->setMinimumWidth(450);
203 m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
204
205 connect(m_treeView->selectionModel(), &QItemSelectionModel::selectionChanged,
206 this, &QDesignerPromotionDialog::slotSelectionChanged);
207
208 connect(m_treeView, &QWidget::customContextMenuRequested,
209 this, &QDesignerPromotionDialog::slotTreeViewContextMenu);
210
211 QHeaderView *headerView = m_treeView->header();
212 headerView->setSectionResizeMode(QHeaderView::ResizeToContents);
213 treeViewVBoxLayout->addWidget(m_treeView);
214 // remove button
215 QHBoxLayout *hboxLayout = new QHBoxLayout();
216 hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored));
217
218 m_removeButton->setAutoDefault(false);
219 connect(m_removeButton, &QAbstractButton::clicked, this, &QDesignerPromotionDialog::slotRemove);
220 m_removeButton->setEnabled(false);
221 hboxLayout->addWidget(m_removeButton);
222 treeViewVBoxLayout->addLayout(hboxLayout);
223 vboxLayout->addWidget(treeViewGroup);
224 // Create new panel: Try to be smart and preselect a base class. Default to QFrame
225 const QStringList &baseClassNameList = baseClassNames(m_promotion);
226 int preselectedBaseClass = -1;
227 if (m_mode == ModeEditChooseClass) {
228 preselectedBaseClass = baseClassNameList.indexOf(m_promotableWidgetClassName);
229 }
230 if (preselectedBaseClass == -1)
231 preselectedBaseClass = baseClassNameList.indexOf("QFrame"_L1);
232
233 NewPromotedClassPanel *newPromotedClassPanel = new NewPromotedClassPanel(baseClassNameList, preselectedBaseClass);
234 newPromotedClassPanel->setPromotedHeaderSuffix(core->integration()->headerSuffix());
235 newPromotedClassPanel->setPromotedHeaderLowerCase(core->integration()->isHeaderLowercase());
236 connect(newPromotedClassPanel, &NewPromotedClassPanel::newPromotedClass,
237 this, &QDesignerPromotionDialog::slotNewPromotedClass);
238 connect(this, &QDesignerPromotionDialog::selectedBaseClassChanged,
239 newPromotedClassPanel, &NewPromotedClassPanel::chooseBaseClass);
240 vboxLayout->addWidget(newPromotedClassPanel);
241 // button box
242 vboxLayout->addWidget(m_buttonBox);
243 // connect model
244 connect(m_model, &PromotionModel::includeFileChanged,
245 this, &QDesignerPromotionDialog::slotIncludeFileChanged);
246
247 connect(m_model, &PromotionModel::classNameChanged,
248 this, &QDesignerPromotionDialog::slotClassNameChanged);
249
250 // focus
251 if (m_mode == ModeEditChooseClass)
252 newPromotedClassPanel->grabFocus();
253
254 slotUpdateFromWidgetDatabase();
255 }
256
257 QDialogButtonBox *QDesignerPromotionDialog::createButtonBox() {
258 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Close);
259
260 connect(buttonBox, &QDialogButtonBox::accepted,
261 this, &QDesignerPromotionDialog::slotAcceptPromoteTo);
262 buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Promote"));
263 buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
264
265 connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
266 return buttonBox;
267 }
268
269 void QDesignerPromotionDialog::slotUpdateFromWidgetDatabase() {
271 m_treeView->expandAll();
272 m_removeButton->setEnabled(false);
273 }
274
275 void QDesignerPromotionDialog::delayedUpdateFromWidgetDatabase() {
276 QTimer::singleShot(0, this, &QDesignerPromotionDialog::slotUpdateFromWidgetDatabase);
277 }
278
279 const QStringList &QDesignerPromotionDialog::baseClassNames(const QDesignerPromotionInterface *promotion) {
280 using WidgetDataBaseItemList = QList<QDesignerWidgetDataBaseItemInterface *>;
281 static QStringList rc;
282 if (rc.isEmpty()) {
283 // Convert the item list into a string list.
284 const WidgetDataBaseItemList dbItems = promotion->promotionBaseClasses();
285 for (auto *item : dbItems)
286 rc.append(item->name());
287 }
288 return rc;
289 }
290
291 void QDesignerPromotionDialog::slotAcceptPromoteTo() {
292 Q_ASSERT(m_mode == ModeEditChooseClass);
293 unsigned flags;
294 // Ok pressed: Promote to selected class
295 if (QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags)) {
296 if (flags & CanPromote) {
297 *m_promoteTo = dbItem ->name();
298 accept();
299 }
300 }
301 }
302
303 void QDesignerPromotionDialog::slotRemove() {
304 unsigned flags;
305 QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
306 if (!dbItem || (flags & Referenced))
307 return;
308
309 QString errorMessage;
310 if (m_promotion->removePromotedClass(dbItem->name(), &errorMessage)) {
311 slotUpdateFromWidgetDatabase();
312 } else {
313 displayError(errorMessage);
314 }
315 }
316
317 void QDesignerPromotionDialog::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) {
318 // Enable deleting non-referenced items
319 unsigned flags;
320 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(selected, flags);
321 m_removeButton->setEnabled(dbItem && !(flags & Referenced));
322 // In choose mode, can we promote to the class?
323 if (m_mode == ModeEditChooseClass) {
324 const bool enablePromoted = flags & CanPromote;
325 m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enablePromoted);
326 m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(enablePromoted);
327 }
328 // different base?
329 if (dbItem) {
330 const QString baseClass = dbItem->extends();
331 if (baseClass != m_lastSelectedBaseClass) {
332 m_lastSelectedBaseClass = baseClass;
333 emit selectedBaseClassChanged(m_lastSelectedBaseClass);
334 }
335 }
336 }
337
338 QDesignerWidgetDataBaseItemInterface *QDesignerPromotionDialog::databaseItemAt(const QItemSelection &selected, unsigned &flags) const {
339 flags = 0;
340 const QModelIndexList indexes = selected.indexes();
341 if (indexes.isEmpty())
342 return nullptr;
343 const PromotionModel::ModelData data = m_model->modelData(indexes.constFirst());
344 QDesignerWidgetDataBaseItemInterface *dbItem = data.promotedItem;
345
346 if (dbItem) {
347 if (data.referenced)
348 flags |= Referenced;
349 // In choose mode, can we promote to the class?
350 if (m_mode == ModeEditChooseClass && dbItem && dbItem->isPromoted() && dbItem->extends() == m_promotableWidgetClassName)
351 flags |= CanPromote;
352
353 }
354 return dbItem;
355 }
356
357 void QDesignerPromotionDialog::slotNewPromotedClass(const PromotionParameters &p, bool *ok) {
358 QString errorMessage;
359 *ok = m_promotion->addPromotedClass(p.m_baseClass, p.m_className, p.m_includeFile, &errorMessage);
360 if (*ok) {
361 // update and select
362 slotUpdateFromWidgetDatabase();
363 const QModelIndex newClassIndex = m_model->indexOfClass(p.m_className);
364 if (newClassIndex.isValid()) {
365 m_treeView->selectionModel()->select(newClassIndex, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows);
366 }
367 } else {
368 displayError(errorMessage);
369 }
370 }
371
373 if (includeFile.isEmpty()) {
375 return;
376 }
377
379 return;
380
385 }
386 }
387
389 if (newName.isEmpty()) {
391 return;
392 }
393 const QString oldName = dbItem->name();
394 if (newName == oldName)
395 return;
396
401 }
402 }
403
404 void QDesignerPromotionDialog::slotTreeViewContextMenu(const QPoint &pos) {
405 unsigned flags;
406 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
407 if (!dbItem)
408 return;
409
410 QMenu menu;
411 QAction *signalSlotAction = menu.addAction(tr("Change signals/slots..."));
412 connect(signalSlotAction, &QAction::triggered,
413 this, &QDesignerPromotionDialog::slotEditSignalsSlots);
414
415 menu.exec(m_treeView->viewport()->mapToGlobal(pos));
416 }
417
418 void QDesignerPromotionDialog::slotEditSignalsSlots() {
419 unsigned flags;
420 const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags);
421 if (!dbItem)
422 return;
423
424 SignalSlotDialog::editPromotedClass(m_core, dbItem->name(), this);
425 }
426
427 void QDesignerPromotionDialog::displayError(const QString &message) {
428 m_core->dialogGui()->message(this, QDesignerDialogGuiInterface::PromotionErrorMessage, QMessageBox::Warning,
429 tr("%1 - Error").arg(windowTitle()), message, QMessageBox::Close);
430 }
431} // namespace qdesigner_internal
432
433QT_END_NAMESPACE
The QDesignerWidgetDataBaseItemInterface class provides an interface that is used to access individua...
friend class QWidget
Definition qpainter.h:432
PromotionModel(QDesignerFormEditorInterface *core)
QDesignerPromotionDialog(QDesignerFormEditorInterface *core, QWidget *parent=nullptr, const QString &promotableWidgetClassName=QString(), QString *promoteTo=nullptr)
Combined button and popup list for selecting options.
Auxiliary methods to store/retrieve settings.