Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++ // Qt-Security score:significant reason:default
++
++ #include "embeddedoptionspage.h"
++ #include "deviceprofiledialog.h"
++ #include "widgetfactory_p.h"
++ #include "formwindowmanager.h"
++
++ #include <deviceprofile_p.h>
++ #include <iconloader_p.h>
++ #include <shared_settings_p.h>
++ #include <abstractdialoggui_p.h>
++ #include <formwindowbase_p.h>
++
++
++ // SDK
++ #include <QtDesigner/abstractformeditor.h>
++ #include <QtDesigner/abstractformwindowmanager.h>
++
++ #include <QtWidgets/qlabel.h>
++ #include <QtWidgets/qboxlayout.h>
++ #include <QtWidgets/qapplication.h>
++ #include <QtWidgets/qcombobox.h>
++ #include <QtWidgets/qtoolbutton.h>
++ #include <QtWidgets/qmessagebox.h>
++ #include <QtWidgets/qlabel.h>
++ #include <QtWidgets/qgroupbox.h>
++
++ #include <QtCore/qset.h>
++ #include <QtCore/qlist.h>
++
++ #include <algorithm>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::StringLiterals;
++
++ namespace qdesigner_internal {
++
++ using DeviceProfileList = QList<DeviceProfile>;
++
++ enum { profileComboIndexOffset = 1 };
++
++ // Sort by name. Used by template, do not make it static!
++ bool deviceProfileLessThan(const DeviceProfile &d1, const DeviceProfile &d2)
++ {
++ return d1.name().toLower() < d2.name().toLower();
++ }
++
++ static bool ask(QWidget *parent,
++ QDesignerDialogGuiInterface *dlgui,
++ const QString &title,
++ const QString &what)
++ {
++ return dlgui->message(parent, QDesignerDialogGuiInterface::OtherMessage,
++ QMessageBox::Question, title, what,
++ QMessageBox::Yes|QMessageBox::No, QMessageBox::No) == QMessageBox::Yes;
++ }
++
++ // ------------ EmbeddedOptionsControlPrivate
++ class EmbeddedOptionsControlPrivate {
++ Q_DISABLE_COPY_MOVE(EmbeddedOptionsControlPrivate)
++ public:
++ EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core);
++ void init(EmbeddedOptionsControl *q);
++
++ bool isDirty() const { return m_dirty; }
++
++ void loadSettings();
++ void saveSettings();
++ void slotAdd();
++ void slotEdit();
++ void slotDelete();
++ void slotProfileIndexChanged(int);
++
++ private:
++ QStringList existingProfileNames() const;
++ void sortAndPopulateProfileCombo();
++ void updateState();
++ void updateDescriptionLabel();
++
++ QDesignerFormEditorInterface *m_core;
++ QComboBox *m_profileCombo;
++ QToolButton *m_addButton;
++ QToolButton *m_editButton;
++ QToolButton *m_deleteButton;
++ QLabel *m_descriptionLabel;
++
++ DeviceProfileList m_sortedProfiles;
++ EmbeddedOptionsControl *m_q = nullptr;
++ QSet<QString> m_usedProfiles;
++ bool m_dirty = false;
++ };
++
++ EmbeddedOptionsControlPrivate::EmbeddedOptionsControlPrivate(QDesignerFormEditorInterface *core) :
++ m_core(core),
++ m_profileCombo(new QComboBox),
++ m_addButton(new QToolButton),
++ m_editButton(new QToolButton),
++ m_deleteButton(new QToolButton),
++ m_descriptionLabel(new QLabel)
++ {
++ m_descriptionLabel->setMinimumHeight(80);
++ // Determine used profiles to lock them
++ const QDesignerFormWindowManagerInterface *fwm = core->formWindowManager();
++ if (const int fwCount = fwm->formWindowCount()) {
++ for (int i = 0; i < fwCount; i++)
++ if (const FormWindowBase *fwb = qobject_cast<const FormWindowBase *>(fwm->formWindow(i))) {
++ const QString deviceProfileName = fwb->deviceProfileName();
++ if (!deviceProfileName.isEmpty())
++ m_usedProfiles.insert(deviceProfileName);
++ }
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::init(EmbeddedOptionsControl *q)
++ {
++ m_q = q;
++ QVBoxLayout *vLayout = new QVBoxLayout;
++ QHBoxLayout *hLayout = new QHBoxLayout;
++ m_profileCombo->setMinimumWidth(200);
++ m_profileCombo->setEditable(false);
++ hLayout->addWidget(m_profileCombo);
++ m_profileCombo->addItem(EmbeddedOptionsControl::tr("None"));
++ EmbeddedOptionsControl::connect(m_profileCombo, &QComboBox::currentIndexChanged,
++ m_q, &EmbeddedOptionsControl::slotProfileIndexChanged);
++
++ m_addButton->setIcon(createIconSet("plus.png"_L1));
++ m_addButton->setToolTip(EmbeddedOptionsControl::tr("Add a profile"));
++ EmbeddedOptionsControl::connect(m_addButton, &QAbstractButton::clicked,
++ m_q, &EmbeddedOptionsControl::slotAdd);
++ hLayout->addWidget(m_addButton);
++
++ EmbeddedOptionsControl::connect(m_editButton, &QAbstractButton::clicked,
++ m_q, &EmbeddedOptionsControl::slotEdit);
++ m_editButton->setIcon(createIconSet("edit.png"_L1));
++ m_editButton->setToolTip(EmbeddedOptionsControl::tr("Edit the selected profile"));
++ hLayout->addWidget(m_editButton);
++
++ m_deleteButton->setIcon(createIconSet("minus.png"_L1));
++ m_deleteButton->setToolTip(EmbeddedOptionsControl::tr("Delete the selected profile"));
++ EmbeddedOptionsControl::connect(m_deleteButton, &QAbstractButton::clicked,
++ m_q, &EmbeddedOptionsControl::slotDelete);
++ hLayout->addWidget(m_deleteButton);
++
++ hLayout->addStretch();
++ vLayout->addLayout(hLayout);
++ vLayout->addWidget(m_descriptionLabel);
++ m_q->setLayout(vLayout);
++ }
++
++ QStringList EmbeddedOptionsControlPrivate::existingProfileNames() const
++ {
++ QStringList rc;
++ for (const auto &dp : m_sortedProfiles)
++ rc.append(dp.name());
++ return rc;
++ }
++
++ void EmbeddedOptionsControlPrivate::slotAdd()
++ {
++ DeviceProfileDialog dlg(m_core->dialogGui(), m_q);
++ dlg.setWindowTitle(EmbeddedOptionsControl::tr("Add Profile"));
++ // Create a new profile with a new, unique name
++ DeviceProfile settings;
++ settings.fromSystem();
++ dlg.setDeviceProfile(settings);
++
++ const QStringList names = existingProfileNames();
++ const QString newNamePrefix = EmbeddedOptionsControl::tr("New profile");
++ QString newName = newNamePrefix;
++ for (int i = 2; names.contains(newName); i++) {
++ newName = newNamePrefix;
++ newName += QString::number(i);
++ }
++
++ settings.setName(newName);
++ dlg.setDeviceProfile(settings);
++ if (dlg.showDialog(names)) {
++ const DeviceProfile newProfile = dlg.deviceProfile();
++ m_sortedProfiles.push_back(newProfile);
++ // Maintain sorted order
++ sortAndPopulateProfileCombo();
++ const int index = m_profileCombo->findText(newProfile.name());
++ m_profileCombo->setCurrentIndex(index);
++ m_dirty = true;
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::slotEdit()
++ {
++ const int index = m_profileCombo->currentIndex() - profileComboIndexOffset;
++ if (index < 0)
++ return;
++
++ // Edit the profile, compile a list of existing names
++ // excluding current one. re-insert if changed,
++ // re-sort if name changed.
++ const DeviceProfile oldProfile = m_sortedProfiles.at(index);
++ const QString oldName = oldProfile.name();
++ QStringList names = existingProfileNames();
++ names.removeAll(oldName);
++
++ DeviceProfileDialog dlg(m_core->dialogGui(), m_q);
++ dlg.setWindowTitle(EmbeddedOptionsControl::tr("Edit Profile"));
++ dlg.setDeviceProfile(oldProfile);
++ if (dlg.showDialog(names)) {
++ const DeviceProfile newProfile = dlg.deviceProfile();
++ if (newProfile != oldProfile) {
++ m_dirty = true;
++ m_sortedProfiles[index] = newProfile;
++ if (newProfile.name() != oldName) {
++ sortAndPopulateProfileCombo();
++ const int index = m_profileCombo->findText(newProfile.name());
++ m_profileCombo->setCurrentIndex(index);
++ } else {
++ updateDescriptionLabel();
++ }
++
++ }
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::slotDelete()
++ {
++ const int index = m_profileCombo->currentIndex() - profileComboIndexOffset;
++ if (index < 0)
++ return;
++ const QString name = m_sortedProfiles.at(index).name();
++ if (ask(m_q, m_core->dialogGui(),
++ EmbeddedOptionsControl::tr("Delete Profile"),
++ EmbeddedOptionsControl::tr("Would you like to delete the profile '%1'?").arg(name))) {
++ m_profileCombo->setCurrentIndex(0);
++ m_sortedProfiles.removeAt(index);
++ m_profileCombo->removeItem(index + profileComboIndexOffset);
++ m_dirty = true;
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::sortAndPopulateProfileCombo()
++ {
++ // Clear items until only "None" is left
++ for (int i = m_profileCombo->count() - 1; i > 0; i--)
++ m_profileCombo->removeItem(i);
++ if (!m_sortedProfiles.isEmpty()) {
++ std::sort(m_sortedProfiles.begin(), m_sortedProfiles.end(), deviceProfileLessThan);
++ m_profileCombo->addItems(existingProfileNames());
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::loadSettings()
++ {
++ const QDesignerSharedSettings settings(m_core);
++ m_sortedProfiles = settings.deviceProfiles();
++ sortAndPopulateProfileCombo();
++ // Index: 0 is "None"
++ const int settingsIndex = settings.currentDeviceProfileIndex();
++ const int profileIndex = settingsIndex >= 0 && settingsIndex < m_sortedProfiles.size() ? settingsIndex + profileComboIndexOffset : 0;
++ m_profileCombo->setCurrentIndex(profileIndex);
++ updateState();
++ m_dirty = false;
++ }
++
++ void EmbeddedOptionsControlPrivate::saveSettings()
++ {
++ QDesignerSharedSettings settings(m_core);
++ settings.setDeviceProfiles(m_sortedProfiles);
++ // Index: 0 is "None"
++ settings.setCurrentDeviceProfileIndex(m_profileCombo->currentIndex() - profileComboIndexOffset);
++ m_dirty = false;
++ }
++
++ //: Format embedded device profile description
++ static const char *descriptionFormat = QT_TRANSLATE_NOOP("EmbeddedOptionsControl",
++ "<html>"
++ "<table>"
++ "<tr><td><b>Font</b></td><td>%1, %2</td></tr>"
++ "<tr><td><b>Style</b></td><td>%3</td></tr>"
++ "<tr><td><b>Resolution</b></td><td>%4 x %5</td></tr>"
++ "</table>"
++ "</html>");
++
++ static inline QString description(const DeviceProfile& p)
++ {
++ QString styleName = p.style();
++ if (styleName.isEmpty())
++ styleName = EmbeddedOptionsControl::tr("Default");
++ return EmbeddedOptionsControl::tr(descriptionFormat).
++ arg(p.fontFamily()).arg(p.fontPointSize()).arg(styleName).arg(p.dpiX()).arg(p.dpiY());
++ }
++
++ void EmbeddedOptionsControlPrivate::updateDescriptionLabel()
++ {
++ const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset;
++ if (profileIndex >= 0) {
++ m_descriptionLabel->setText(description(m_sortedProfiles.at(profileIndex)));
++ } else {
++ m_descriptionLabel->clear();
++ }
++ }
++
++ void EmbeddedOptionsControlPrivate::updateState()
++ {
++ const int profileIndex = m_profileCombo->currentIndex() - profileComboIndexOffset;
++ // Allow for changing/deleting only if it is not in use
++ bool modifyEnabled = false;
++ if (profileIndex >= 0)
++ modifyEnabled = !m_usedProfiles.contains(m_sortedProfiles.at(profileIndex).name());
++ m_editButton->setEnabled(modifyEnabled);
++ m_deleteButton->setEnabled(modifyEnabled);
++ updateDescriptionLabel();
++ }
++
++ void EmbeddedOptionsControlPrivate::slotProfileIndexChanged(int)
++ {
++ updateState();
++ m_dirty = true;
++ }
++
++ // ------------- EmbeddedOptionsControl
++ EmbeddedOptionsControl::EmbeddedOptionsControl(QDesignerFormEditorInterface *core, QWidget *parent) :
++ QWidget(parent),
++ m_d(new EmbeddedOptionsControlPrivate(core))
++ {
++ m_d->init(this);
++ }
++
++ EmbeddedOptionsControl::~EmbeddedOptionsControl()
++ {
++ delete m_d;
++ }
++
++ void EmbeddedOptionsControl::slotAdd()
++ {
++ m_d->slotAdd();
++ }
++
++ void EmbeddedOptionsControl::slotEdit()
++ {
++ m_d->slotEdit();
++ }
++
++ void EmbeddedOptionsControl::slotDelete()
++ {
++ m_d->slotDelete();
++ }
++
++ void EmbeddedOptionsControl::loadSettings()
++ {
++ m_d->loadSettings();
++ }
++
++ void EmbeddedOptionsControl::saveSettings()
++ {
++ m_d->saveSettings();
++ }
++
++ void EmbeddedOptionsControl::slotProfileIndexChanged(int i)
++ {
++ m_d->slotProfileIndexChanged(i);
++ }
++
++ bool EmbeddedOptionsControl::isDirty() const
++ {
++ return m_d->isDirty();
++ }
++
++ // EmbeddedOptionsPage:
++ EmbeddedOptionsPage::EmbeddedOptionsPage(QDesignerFormEditorInterface *core) :
++ m_core(core)
++ {
++ }
++
++ QString EmbeddedOptionsPage::name() const
++ {
++ //: Tab in preferences dialog
++ return QCoreApplication::translate("EmbeddedOptionsPage", "Embedded Design");
++ }
++
++ QWidget *EmbeddedOptionsPage::createPage(QWidget *parent)
++ {
++ QWidget *optionsWidget = new QWidget(parent);
++
++ QVBoxLayout *optionsVLayout = new QVBoxLayout();
++
++ //: EmbeddedOptionsControl group box"
++ QGroupBox *gb = new QGroupBox(QCoreApplication::translate("EmbeddedOptionsPage", "Device Profiles"));
++ QVBoxLayout *gbVLayout = new QVBoxLayout();
++ m_embeddedOptionsControl = new EmbeddedOptionsControl(m_core);
++ m_embeddedOptionsControl->loadSettings();
++ gbVLayout->addWidget(m_embeddedOptionsControl);
++ gb->setLayout(gbVLayout);
++ optionsVLayout->addWidget(gb);
++
++ optionsVLayout->addStretch(1);
++
++ // Outer layout to give it horizontal stretch
++ QHBoxLayout *optionsHLayout = new QHBoxLayout();
++ optionsHLayout->addLayout(optionsVLayout);
++ optionsHLayout->addStretch(1);
++ optionsWidget->setLayout(optionsHLayout);
++ return optionsWidget;
++ }
++
++ void EmbeddedOptionsPage::apply()
++ {
++ if (!m_embeddedOptionsControl || !m_embeddedOptionsControl->isDirty())
++ return;
++
++ m_embeddedOptionsControl->saveSettings();
++ if (FormWindowManager *fw = qobject_cast<qdesigner_internal::FormWindowManager *>(m_core->formWindowManager()))
++ fw->deviceProfilesChanged();
++ }
++
++ void EmbeddedOptionsPage::finish()
++ {
++ }
++ }
++
++ QT_END_NAMESPACE