1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Designer of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include "embeddedoptionspage.h"
30#include "deviceprofiledialog.h"
31#include "widgetfactory_p.h"
32#include "formwindowmanager.h"
33
34#include <deviceprofile_p.h>
35#include <iconloader_p.h>
36#include <shared_settings_p.h>
37#include <abstractdialoggui_p.h>
38#include <formwindowbase_p.h>
39
40
41// SDK
42#include <QtDesigner/abstractformeditor.h>
43#include <QtDesigner/abstractformwindowmanager.h>
44
45#include <QtWidgets/qlabel.h>
46#include <QtWidgets/qboxlayout.h>
47#include <QtWidgets/qapplication.h>
48#include <QtWidgets/qcombobox.h>
49#include <QtWidgets/qtoolbutton.h>
50#include <QtWidgets/qmessagebox.h>
51#include <QtWidgets/qlabel.h>
52#include <QtWidgets/qgroupbox.h>
53
54#include <QtCore/qset.h>
55#include <QtCore/qvector.h>
56
57#include <algorithm>
58
59QT_BEGIN_NAMESPACE
60
61namespace qdesigner_internal {
62
63using DeviceProfileList = QVector<DeviceProfile>;
64
65enum { profileComboIndexOffset = 1 };
66
67// Sort by name. Used by template, do not make it static!
68bool deviceProfileLessThan(const DeviceProfile &d1, const DeviceProfile &d2)
69{
70 return d1.name().toLower() < d2.name().toLower();
71}
72
73static bool ask(QWidget *parent,
74 QDesignerDialogGuiInterface *dlgui,
75 const QString &title,
76 const QString &what)
77{
78 return dlgui->message(parent, context: QDesignerDialogGuiInterface::OtherMessage,
79 icon: QMessageBox::Question, title, text: what,
80 buttons: QMessageBox::Yes|QMessageBox::No, defaultButton: QMessageBox::No) == QMessageBox::Yes;
81}
82
83// ------------ EmbeddedOptionsControlPrivate
84class EmbeddedOptionsControlPrivate {
85 Q_DISABLE_COPY_MOVE(EmbeddedOptionsControlPrivate)
86public:
87 EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core);
88 void init(EmbeddedOptionsControl *q);
89
90 bool isDirty() const { return m_dirty; }
91
92 void loadSettings();
93 void saveSettings();
94 void slotAdd();
95 void slotEdit();
96 void slotDelete();
97 void slotProfileIndexChanged(int);
98
99private:
100 QStringList existingProfileNames() const;
101 void sortAndPopulateProfileCombo();
102 void updateState();
103 void updateDescriptionLabel();
104
105 QDesignerFormEditorInterface *m_core;
106 QComboBox *m_profileCombo;
107 QToolButton *m_addButton;
108 QToolButton *m_editButton;
109 QToolButton *m_deleteButton;
110 QLabel *m_descriptionLabel;
111
112 DeviceProfileList m_sortedProfiles;
113 EmbeddedOptionsControl *m_q = nullptr;
114 QSet<QString> m_usedProfiles;
115 bool m_dirty = false;
116};
117
118EmbeddedOptionsControlPrivate::EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core) :
119 m_core(core),
120 m_profileCombo(new QComboBox),
121 m_addButton(new QToolButton),
122 m_editButton(new QToolButton),
123 m_deleteButton(new QToolButton),
124 m_descriptionLabel(new QLabel)
125{
126 m_descriptionLabel->setMinimumHeight(80);
127 // Determine used profiles to lock them
128 const QDesignerFormWindowManagerInterface *fwm = core->formWindowManager();
129 if (const int fwCount = fwm->formWindowCount()) {
130 for (int i = 0; i < fwCount; i++)
131 if (const FormWindowBase *fwb = qobject_cast<const FormWindowBase *>(object: fwm->formWindow(index: i))) {
132 const QString deviceProfileName = fwb->deviceProfileName();
133 if (!deviceProfileName.isEmpty())
134 m_usedProfiles.insert(value: deviceProfileName);
135 }
136 }
137}
138
139void EmbeddedOptionsControlPrivate::init(EmbeddedOptionsControl *q)
140{
141 m_q = q;
142 QVBoxLayout *vLayout = new QVBoxLayout;
143 QHBoxLayout *hLayout = new QHBoxLayout;
144 m_profileCombo->setMinimumWidth(200);
145 m_profileCombo->setEditable(false);
146 hLayout->addWidget(m_profileCombo);
147 m_profileCombo->addItem(atext: EmbeddedOptionsControl::tr(s: "None"));
148 EmbeddedOptionsControl::connect(sender: m_profileCombo, signal: QOverload<int>::of(ptr: &QComboBox::currentIndexChanged),
149 receiver: m_q, slot: &EmbeddedOptionsControl::slotProfileIndexChanged);
150
151 m_addButton->setIcon(createIconSet(name: QString::fromUtf8(str: "plus.png")));
152 m_addButton->setToolTip(EmbeddedOptionsControl::tr(s: "Add a profile"));
153 EmbeddedOptionsControl::connect(sender: m_addButton, signal: &QAbstractButton::clicked,
154 receiver: m_q, slot: &EmbeddedOptionsControl::slotAdd);
155 hLayout->addWidget(m_addButton);
156
157 EmbeddedOptionsControl::connect(sender: m_editButton, signal: &QAbstractButton::clicked,
158 receiver: m_q, slot: &EmbeddedOptionsControl::slotEdit);
159 m_editButton->setIcon(createIconSet(name: QString::fromUtf8(str: "edit.png")));
160 m_editButton->setToolTip(EmbeddedOptionsControl::tr(s: "Edit the selected profile"));
161 hLayout->addWidget(m_editButton);
162
163 m_deleteButton->setIcon(createIconSet(name: QString::fromUtf8(str: "minus.png")));
164 m_deleteButton->setToolTip(EmbeddedOptionsControl::tr(s: "Delete the selected profile"));
165 EmbeddedOptionsControl::connect(sender: m_deleteButton, signal: &QAbstractButton::clicked,
166 receiver: m_q, slot: &EmbeddedOptionsControl::slotDelete);
167 hLayout->addWidget(m_deleteButton);
168
169 hLayout->addStretch();
170 vLayout->addLayout(layout: hLayout);
171 vLayout->addWidget(m_descriptionLabel);
172 m_q->setLayout(vLayout);
173}
174
175QStringList EmbeddedOptionsControlPrivate::existingProfileNames() const
176{
177 QStringList rc;
178 const DeviceProfileList::const_iterator dcend = m_sortedProfiles.constEnd();
179 for (DeviceProfileList::const_iterator it = m_sortedProfiles.constBegin(); it != dcend; ++it)
180 rc.push_back(t: it->name());
181 return rc;
182}
183
184void EmbeddedOptionsControlPrivate::slotAdd()
185{
186 DeviceProfileDialog dlg(m_core->dialogGui(), m_q);
187 dlg.setWindowTitle(EmbeddedOptionsControl::tr(s: "Add Profile"));
188 // Create a new profile with a new, unique name
189 DeviceProfile settings;
190 settings.fromSystem();
191 dlg.setDeviceProfile(settings);
192
193 const QStringList names = existingProfileNames();
194 const QString newNamePrefix = EmbeddedOptionsControl::tr(s: "New profile");
195 QString newName = newNamePrefix;
196 for (int i = 2; names.contains(str: newName); i++) {
197 newName = newNamePrefix;
198 newName += QString::number(i);
199 }
200
201 settings.setName(newName);
202 dlg.setDeviceProfile(settings);
203 if (dlg.showDialog(existingNames: names)) {
204 const DeviceProfile newProfile = dlg.deviceProfile();
205 m_sortedProfiles.push_back(t: newProfile);
206 // Maintain sorted order
207 sortAndPopulateProfileCombo();
208 const int index = m_profileCombo->findText(text: newProfile.name());
209 m_profileCombo->setCurrentIndex(index);
210 m_dirty = true;
211 }
212}
213
214void EmbeddedOptionsControlPrivate::slotEdit()
215{
216 const int index = m_profileCombo->currentIndex() - profileComboIndexOffset;
217 if (index < 0)
218 return;
219
220 // Edit the profile, compile a list of existing names
221 // excluding current one. re-insert if changed,
222 // re-sort if name changed.
223 const DeviceProfile oldProfile = m_sortedProfiles.at(i: index);
224 const QString oldName = oldProfile.name();
225 QStringList names = existingProfileNames();
226 names.removeAll(t: oldName);
227
228 DeviceProfileDialog dlg(m_core->dialogGui(), m_q);
229 dlg.setWindowTitle(EmbeddedOptionsControl::tr(s: "Edit Profile"));
230 dlg.setDeviceProfile(oldProfile);
231 if (dlg.showDialog(existingNames: names)) {
232 const DeviceProfile newProfile = dlg.deviceProfile();
233 if (newProfile != oldProfile) {
234 m_dirty = true;
235 m_sortedProfiles[index] = newProfile;
236 if (newProfile.name() != oldName) {
237 sortAndPopulateProfileCombo();
238 const int index = m_profileCombo->findText(text: newProfile.name());
239 m_profileCombo->setCurrentIndex(index);
240 } else {
241 updateDescriptionLabel();
242 }
243
244 }
245 }
246}
247
248void EmbeddedOptionsControlPrivate::slotDelete()
249{
250 const int index = m_profileCombo->currentIndex() - profileComboIndexOffset;
251 if (index < 0)
252 return;
253 const QString name = m_sortedProfiles.at(i: index).name();
254 if (ask(parent: m_q, dlgui: m_core->dialogGui(),
255 title: EmbeddedOptionsControl::tr(s: "Delete Profile"),
256 what: EmbeddedOptionsControl::tr(s: "Would you like to delete the profile '%1'?").arg(a: name))) {
257 m_profileCombo->setCurrentIndex(0);
258 m_sortedProfiles.removeAt(i: index);
259 m_profileCombo->removeItem(index: index + profileComboIndexOffset);
260 m_dirty = true;
261 }
262}
263
264void EmbeddedOptionsControlPrivate::sortAndPopulateProfileCombo()
265{
266 // Clear items until only "None" is left
267 for (int i = m_profileCombo->count() - 1; i > 0; i--)
268 m_profileCombo->removeItem(index: i);
269 if (!m_sortedProfiles.isEmpty()) {
270 std::sort(first: m_sortedProfiles.begin(), last: m_sortedProfiles.end(), comp: deviceProfileLessThan);
271 m_profileCombo->addItems(texts: existingProfileNames());
272 }
273}
274
275void EmbeddedOptionsControlPrivate::loadSettings()
276{
277 const QDesignerSharedSettings settings(m_core);
278 m_sortedProfiles = settings.deviceProfiles();
279 sortAndPopulateProfileCombo();
280 // Index: 0 is "None"
281 const int settingsIndex = settings.currentDeviceProfileIndex();
282 const int profileIndex = settingsIndex >= 0 && settingsIndex < m_sortedProfiles.size() ? settingsIndex + profileComboIndexOffset : 0;
283 m_profileCombo->setCurrentIndex(profileIndex);
284 updateState();
285 m_dirty = false;
286}
287
288void EmbeddedOptionsControlPrivate::saveSettings()
289{
290 QDesignerSharedSettings settings(m_core);
291 settings.setDeviceProfiles(m_sortedProfiles);
292 // Index: 0 is "None"
293 settings.setCurrentDeviceProfileIndex(m_profileCombo->currentIndex() - profileComboIndexOffset);
294 m_dirty = false;
295}
296
297//: Format embedded device profile description
298static const char *descriptionFormat = QT_TRANSLATE_NOOP("EmbeddedOptionsControl",
299"<html>"
300"<table>"
301"<tr><td><b>Font</b></td><td>%1, %2</td></tr>"
302"<tr><td><b>Style</b></td><td>%3</td></tr>"
303"<tr><td><b>Resolution</b></td><td>%4 x %5</td></tr>"
304"</table>"
305"</html>");
306
307static inline QString description(const DeviceProfile& p)
308{
309 QString styleName = p.style();
310 if (styleName.isEmpty())
311 styleName = EmbeddedOptionsControl::tr(s: "Default");
312 return EmbeddedOptionsControl::tr(s: descriptionFormat).
313 arg(a: p.fontFamily()).arg(a: p.fontPointSize()).arg(a: styleName).arg(a: p.dpiX()).arg(a: p.dpiY());
314}
315
316void EmbeddedOptionsControlPrivate::updateDescriptionLabel()
317{
318 const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset;
319 if (profileIndex >= 0) {
320 m_descriptionLabel->setText(description(p: m_sortedProfiles.at(i: profileIndex)));
321 } else {
322 m_descriptionLabel->clear();
323 }
324}
325
326void EmbeddedOptionsControlPrivate::updateState()
327{
328 const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset;
329 // Allow for changing/deleting only if it is not in use
330 bool modifyEnabled = false;
331 if (profileIndex >= 0)
332 modifyEnabled = !m_usedProfiles.contains(value: m_sortedProfiles.at(i: profileIndex).name());
333 m_editButton->setEnabled(modifyEnabled);
334 m_deleteButton->setEnabled(modifyEnabled);
335 updateDescriptionLabel();
336}
337
338void EmbeddedOptionsControlPrivate::slotProfileIndexChanged(int)
339{
340 updateState();
341 m_dirty = true;
342}
343
344// ------------- EmbeddedOptionsControl
345EmbeddedOptionsControl::EmbeddedOptionsControl(QDesignerFormEditorInterface *core, QWidget *parent) :
346 QWidget(parent),
347 m_d(new EmbeddedOptionsControlPrivate(core))
348{
349 m_d->init(q: this);
350}
351
352EmbeddedOptionsControl::~EmbeddedOptionsControl()
353{
354 delete m_d;
355}
356
357void EmbeddedOptionsControl::slotAdd()
358{
359 m_d->slotAdd();
360}
361
362void EmbeddedOptionsControl::slotEdit()
363{
364 m_d->slotEdit();
365}
366
367void EmbeddedOptionsControl::slotDelete()
368{
369 m_d->slotDelete();
370}
371
372void EmbeddedOptionsControl::loadSettings()
373{
374 m_d->loadSettings();
375}
376
377void EmbeddedOptionsControl::saveSettings()
378{
379 m_d->saveSettings();
380}
381
382void EmbeddedOptionsControl::slotProfileIndexChanged(int i)
383{
384 m_d->slotProfileIndexChanged(i);
385}
386
387bool EmbeddedOptionsControl::isDirty() const
388{
389 return m_d->isDirty();
390}
391
392// EmbeddedOptionsPage:
393EmbeddedOptionsPage::EmbeddedOptionsPage(QDesignerFormEditorInterface *core) :
394 m_core(core)
395{
396}
397
398QString EmbeddedOptionsPage::name() const
399{
400 //: Tab in preferences dialog
401 return QCoreApplication::translate(context: "EmbeddedOptionsPage", key: "Embedded Design");
402}
403
404QWidget *EmbeddedOptionsPage::createPage(QWidget *parent)
405{
406 QWidget *optionsWidget = new QWidget(parent);
407
408 QVBoxLayout *optionsVLayout = new QVBoxLayout();
409
410 //: EmbeddedOptionsControl group box"
411 QGroupBox *gb = new QGroupBox(QCoreApplication::translate(context: "EmbeddedOptionsPage", key: "Device Profiles"));
412 QVBoxLayout *gbVLayout = new QVBoxLayout();
413 m_embeddedOptionsControl = new EmbeddedOptionsControl(m_core);
414 m_embeddedOptionsControl->loadSettings();
415 gbVLayout->addWidget(m_embeddedOptionsControl);
416 gb->setLayout(gbVLayout);
417 optionsVLayout->addWidget(gb);
418
419 optionsVLayout->addStretch(stretch: 1);
420
421 // Outer layout to give it horizontal stretch
422 QHBoxLayout *optionsHLayout = new QHBoxLayout();
423 optionsHLayout->addLayout(layout: optionsVLayout);
424 optionsHLayout->addStretch(stretch: 1);
425 optionsWidget->setLayout(optionsHLayout);
426 return optionsWidget;
427}
428
429void EmbeddedOptionsPage::apply()
430{
431 if (!m_embeddedOptionsControl || !m_embeddedOptionsControl->isDirty())
432 return;
433
434 m_embeddedOptionsControl->saveSettings();
435 if (FormWindowManager *fw = qobject_cast<qdesigner_internal::FormWindowManager *>(object: m_core->formWindowManager()))
436 fw->deviceProfilesChanged();
437}
438
439void EmbeddedOptionsPage::finish()
440{
441}
442}
443
444QT_END_NAMESPACE
445

source code of qttools/src/designer/src/components/formeditor/embeddedoptionspage.cpp