diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:18:55 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:18:55 (GMT) |
commit | e5fcad302d86d316390c6b0f62759a067313e8a9 (patch) | |
tree | c2afbf6f1066b6ce261f14341cf6d310e5595bc1 /tools/designer/src/lib/shared | |
download | Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.zip Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.gz Qt-e5fcad302d86d316390c6b0f62759a067313e8a9.tar.bz2 |
Long live Qt 4.5!
Diffstat (limited to 'tools/designer/src/lib/shared')
189 files changed, 57145 insertions, 0 deletions
diff --git a/tools/designer/src/lib/shared/actioneditor.cpp b/tools/designer/src/lib/shared/actioneditor.cpp new file mode 100644 index 0000000..6a66442 --- /dev/null +++ b/tools/designer/src/lib/shared/actioneditor.cpp @@ -0,0 +1,822 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::ActionEditor +*/ + +#include "actioneditor_p.h" +#include "filterwidget_p.h" +#include "actionrepository_p.h" +#include "iconloader_p.h" +#include "newactiondialog_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_objectinspector_p.h" +#include "qdesigner_utils_p.h" +#include "qsimpleresource_p.h" +#include "formwindowbase_p.h" +#include "qdesigner_taskmenu_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QDesignerIconCacheInterface> +#include <QtDesigner/private/abstractsettings_p.h> + +#include <QtGui/QMenu> +#include <QtGui/QToolBar> +#include <QtGui/QSplitter> +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtGui/QItemDelegate> +#include <QtGui/QPainter> +#include <QtGui/QVBoxLayout> +#include <QtGui/QLineEdit> +#include <QtGui/QLabel> +#include <QtGui/QPushButton> +#include <QtGui/QToolButton> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QItemSelection> + +#include <QtCore/QRegExp> +#include <QtCore/QDebug> +#include <QtCore/QSignalMapper> +#include <QtCore/QBuffer> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +static const char *actionEditorViewModeKey = "ActionEditorViewMode"; + +static const char *iconPropertyC = "icon"; +static const char *shortcutPropertyC = "shortcut"; +static const char *toolTipPropertyC = "toolTip"; +static const char *checkablePropertyC = "checkable"; +static const char *objectNamePropertyC = "objectName"; +static const char *textPropertyC = "text"; + +namespace qdesigner_internal { +//-------- ActionGroupDelegate +class ActionGroupDelegate: public QItemDelegate +{ +public: + ActionGroupDelegate(QObject *parent) + : QItemDelegate(parent) {} + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const + { + if (option.state & QStyle::State_Selected) + painter->fillRect(option.rect, option.palette.highlight()); + + QItemDelegate::paint(painter, option, index); + } + + virtual void drawFocus(QPainter * /*painter*/, const QStyleOptionViewItem &/*option*/, const QRect &/*rect*/) const {} +}; + +//-------- ActionEditor +ActionEditor::ActionEditor(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerActionEditorInterface(parent, flags), + m_core(core), + m_actionGroups(0), + m_actionView(new ActionView), + m_actionNew(new QAction(tr("New..."), this)), + m_actionEdit(new QAction(tr("Edit..."), this)), + m_actionNavigateToSlot(new QAction(tr("Go to slot..."), this)), + m_actionCopy(new QAction(tr("Copy"), this)), + m_actionCut(new QAction(tr("Cut"), this)), + m_actionPaste(new QAction(tr("Paste"), this)), + m_actionSelectAll(new QAction(tr("Select all"), this)), + m_actionDelete(new QAction(tr("Delete"), this)), + m_viewModeGroup(new QActionGroup(this)), + m_iconViewAction(0), + m_listViewAction(0), + m_filterWidget(0), + m_selectAssociatedWidgetsMapper(0) +{ + m_actionView->initialize(m_core); + m_actionView->setSelectionMode(QAbstractItemView::ExtendedSelection); + setWindowTitle(tr("Actions")); + + QVBoxLayout *l = new QVBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + + QToolBar *toolbar = new QToolBar; + toolbar->setIconSize(QSize(22, 22)); + toolbar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + l->addWidget(toolbar); + // edit actions + m_actionNew->setIcon(createIconSet(QLatin1String("filenew.png"))); + m_actionNew->setEnabled(false); + connect(m_actionNew, SIGNAL(triggered()), this, SLOT(slotNewAction())); + toolbar->addAction(m_actionNew); + + connect(m_actionSelectAll, SIGNAL(triggered()), m_actionView, SLOT(selectAll())); + + m_actionCut->setEnabled(false); + connect(m_actionCut, SIGNAL(triggered()), this, SLOT(slotCut())); + m_actionCut->setIcon(createIconSet(QLatin1String("editcut.png"))); + + m_actionCopy->setEnabled(false); + connect(m_actionCopy, SIGNAL(triggered()), this, SLOT(slotCopy())); + m_actionCopy->setIcon(createIconSet(QLatin1String("editcopy.png"))); + toolbar->addAction(m_actionCopy); + + connect(m_actionPaste, SIGNAL(triggered()), this, SLOT(slotPaste())); + m_actionPaste->setIcon(createIconSet(QLatin1String("editpaste.png"))); + toolbar->addAction(m_actionPaste); + + m_actionEdit->setEnabled(false); + connect(m_actionEdit, SIGNAL(triggered()), this, SLOT(editCurrentAction())); + + connect(m_actionNavigateToSlot, SIGNAL(triggered()), this, SLOT(navigateToSlotCurrentAction())); + + m_actionDelete->setIcon(createIconSet(QLatin1String("editdelete.png"))); + m_actionDelete->setEnabled(false); + connect(m_actionDelete, SIGNAL(triggered()), this, SLOT(slotDelete())); + toolbar->addAction(m_actionDelete); + + // Toolbutton with menu containing action group for detailed/icon view. Steal the icons from the file dialog. + // + QMenu *configureMenu; + toolbar->addWidget(createConfigureMenuButton(tr("Configure Action Editor"), &configureMenu)); + + connect(m_viewModeGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotViewMode(QAction*))); + m_iconViewAction = m_viewModeGroup->addAction(tr("Icon View")); + m_iconViewAction->setData(QVariant(ActionView::IconView)); + m_iconViewAction->setCheckable(true); + m_iconViewAction->setIcon(style()->standardIcon (QStyle::SP_FileDialogListView)); + configureMenu->addAction(m_iconViewAction); + + m_listViewAction = m_viewModeGroup->addAction(tr("Detailed View")); + m_listViewAction->setData(QVariant(ActionView::DetailedView)); + m_listViewAction->setCheckable(true); + m_listViewAction->setIcon(style()->standardIcon (QStyle::SP_FileDialogDetailedView)); + configureMenu->addAction(m_listViewAction); + // filter + m_filterWidget = new FilterWidget(toolbar); + connect(m_filterWidget, SIGNAL(filterChanged(QString)), this, SLOT(setFilter(QString))); + m_filterWidget->setEnabled(false); + toolbar->addWidget(m_filterWidget); + + // main layout + QSplitter *splitter = new QSplitter(Qt::Horizontal); + splitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + splitter->addWidget(m_actionView); + l->addWidget(splitter); + +#if 0 // ### implement me + m_actionGroups = new QListWidget(splitter); + splitter->addWidget(m_actionGroups); + m_actionGroups->setItemDelegate(new ActionGroupDelegate(m_actionGroups)); + m_actionGroups->setMovement(QListWidget::Static); + m_actionGroups->setResizeMode(QListWidget::Fixed); + m_actionGroups->setIconSize(QSize(48, 48)); + m_actionGroups->setFlow(QListWidget::TopToBottom); + m_actionGroups->setViewMode(QListWidget::IconMode); + m_actionGroups->setWrapping(false); +#endif + + connect(m_actionView, SIGNAL(resourceImageDropped(QString,QAction*)), + this, SLOT(resourceImageDropped(QString,QAction*))); + + connect(m_actionView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentItemChanged(QAction*))); + // make it possible for vs integration to reimplement edit action dialog + connect(m_actionView, SIGNAL(activated(QAction*)), this, SIGNAL(itemActivated(QAction*))); + + connect(m_actionView,SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection,QItemSelection))); + + connect(m_actionView, SIGNAL(contextMenuRequested(QContextMenuEvent*, QAction*)), + this, SLOT(slotContextMenuRequested(QContextMenuEvent*, QAction*))); + + connect(this, SIGNAL(itemActivated(QAction*)), this, SLOT(editAction(QAction*))); + + restoreSettings(); + updateViewModeActions(); +} + +// Utility to create a configure button with menu for usage on toolbars +QToolButton *ActionEditor::createConfigureMenuButton(const QString &t, QMenu **ptrToMenu) +{ + QToolButton *configureButton = new QToolButton; + QAction *configureAction = new QAction(t, configureButton); + configureAction->setIcon(createIconSet(QLatin1String("configure.png"))); + QMenu *configureMenu = new QMenu; + configureAction->setMenu(configureMenu); + configureButton->setDefaultAction(configureAction); + configureButton->setPopupMode(QToolButton::InstantPopup); + *ptrToMenu = configureMenu; + return configureButton; +} + +ActionEditor::~ActionEditor() +{ + saveSettings(); +} + +QAction *ActionEditor::actionNew() const +{ + return m_actionNew; +} + +QAction *ActionEditor::actionDelete() const +{ + return m_actionDelete; +} + +QDesignerFormWindowInterface *ActionEditor::formWindow() const +{ + return m_formWindow; +} + +void ActionEditor::setFormWindow(QDesignerFormWindowInterface *formWindow) +{ + if (formWindow != 0 && formWindow->mainContainer() == 0) + formWindow = 0; + + // we do NOT rely on this function to update the action editor + if (m_formWindow == formWindow) + return; + + if (m_formWindow != 0) { + const ActionList actionList = qFindChildren<QAction*>(m_formWindow->mainContainer()); + foreach (QAction *action, actionList) + disconnect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + } + + m_formWindow = formWindow; + + m_actionView->model()->clearActions(); + + m_actionEdit->setEnabled(false); + m_actionCopy->setEnabled(false); + m_actionCut->setEnabled(false); + m_actionDelete->setEnabled(false); + + if (!formWindow || !formWindow->mainContainer()) { + m_actionNew->setEnabled(false); + m_filterWidget->setEnabled(false); + return; + } + + m_actionNew->setEnabled(true); + m_filterWidget->setEnabled(true); + + const ActionList actionList = qFindChildren<QAction*>(formWindow->mainContainer()); + foreach (QAction *action, actionList) + if (!action->isSeparator() && core()->metaDataBase()->item(action) != 0) { + // Show unless it has a menu. However, listen for change on menu actions also as it might be removed + if (!action->menu()) + m_actionView->model()->addAction(action); + connect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + } + + setFilter(m_filter); +} + +void ActionEditor::slotSelectionChanged(const QItemSelection& selected, const QItemSelection& /*deselected*/) +{ + const bool hasSelection = !selected.indexes().empty(); + m_actionCopy->setEnabled(hasSelection); + m_actionCut->setEnabled(hasSelection); + m_actionDelete->setEnabled(hasSelection); +} + +void ActionEditor::slotCurrentItemChanged(QAction *action) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const bool hasCurrentAction = action != 0; + m_actionEdit->setEnabled(hasCurrentAction); + + if (!action) { + fw->clearSelection(); + return; + } + + QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core()->objectInspector()); + + if (action->associatedWidgets().empty()) { + // Special case: action not in object tree. Deselect all and set in property editor + fw->clearSelection(false); + if (oi) + oi->clearSelection(); + core()->propertyEditor()->setObject(action); + } else { + if (oi) + oi->selectObject(action); + } +} + +void ActionEditor::slotActionChanged() +{ + QAction *action = qobject_cast<QAction*>(sender()); + Q_ASSERT(action != 0); + + ActionModel *model = m_actionView->model(); + const int row = model->findAction(action); + if (row == -1) { + if (action->menu() == 0) // action got its menu deleted, create item + model->addAction(action); + } else if (action->menu() != 0) { // action got its menu created, remove item + model->removeRow(row); + } else { + // action text or icon changed, update item + model->update(row); + } +} + +QDesignerFormEditorInterface *ActionEditor::core() const +{ + return m_core; +} + +QString ActionEditor::filter() const +{ + return m_filter; +} + +void ActionEditor::setFilter(const QString &f) +{ + m_filter = f; + m_actionView->filter(m_filter); +} + +// Set changed state of icon property, reset when icon is cleared +static void refreshIconPropertyChanged(const QAction *action, QDesignerPropertySheetExtension *sheet) +{ + sheet->setChanged(sheet->indexOf(QLatin1String(iconPropertyC)), !action->icon().isNull()); +} + +void ActionEditor::manageAction(QAction *action) +{ + action->setParent(formWindow()->mainContainer()); + core()->metaDataBase()->add(action); + + if (action->isSeparator() || action->menu() != 0) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), action); + sheet->setChanged(sheet->indexOf(QLatin1String(objectNamePropertyC)), true); + sheet->setChanged(sheet->indexOf(QLatin1String(textPropertyC)), true); + refreshIconPropertyChanged(action, sheet); + + m_actionView->setCurrentIndex(m_actionView->model()->addAction(action)); + connect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); +} + +void ActionEditor::unmanageAction(QAction *action) +{ + core()->metaDataBase()->remove(action); + action->setParent(0); + + disconnect(action, SIGNAL(changed()), this, SLOT(slotActionChanged())); + + const int row = m_actionView->model()->findAction(action); + if (row != -1) + m_actionView->model()->remove(row); +} + +// Set an intial property and mark it as changed in the sheet +static void setInitialProperty(QDesignerPropertySheetExtension *sheet, const QString &name, const QVariant &value) +{ + const int index = sheet->indexOf(name); + Q_ASSERT(index != -1); + sheet->setProperty(index, value); + sheet->setChanged(index, true); +} + +void ActionEditor::slotNewAction() +{ + NewActionDialog dlg(this); + dlg.setWindowTitle(tr("New action")); + + if (dlg.exec() == QDialog::Accepted) { + const ActionData actionData = dlg.actionData(); + m_actionView->clearSelection(); + + QAction *action = new QAction(formWindow()); + action->setObjectName(actionData.name); + formWindow()->ensureUniqueObjectName(action); + action->setText(actionData.text); + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), action); + if (!actionData.toolTip.isEmpty()) + setInitialProperty(sheet, QLatin1String(toolTipPropertyC), actionData.toolTip); + + if (actionData.checkable) + setInitialProperty(sheet, QLatin1String(checkablePropertyC), QVariant(true)); + + if (!actionData.keysequence.isEmpty()) + setInitialProperty(sheet, QLatin1String(shortcutPropertyC), qVariantFromValue(actionData.keysequence)); + + sheet->setProperty(sheet->indexOf(QLatin1String(iconPropertyC)), qVariantFromValue(actionData.icon)); + + AddActionCommand *cmd = new AddActionCommand(formWindow()); + cmd->init(action); + formWindow()->commandHistory()->push(cmd); + } +} + +static inline bool isSameIcon(const QIcon &i1, const QIcon &i2) +{ + return i1.serialNumber() == i2.serialNumber(); +} + +// return a FormWindow command to apply an icon or a reset command in case it +// is empty. + +static QDesignerFormWindowCommand *setIconPropertyCommand(const PropertySheetIconValue &newIcon, QAction *action, QDesignerFormWindowInterface *fw) +{ + const QString iconProperty = QLatin1String(iconPropertyC); + if (newIcon.paths().isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(action, iconProperty); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, iconProperty, qVariantFromValue(newIcon)); + return cmd; +} + +// return a FormWindow command to apply a QKeySequence or a reset command +// in case it is empty. + +static QDesignerFormWindowCommand *setKeySequencePropertyCommand(const QKeySequence &ks, QAction *action, QDesignerFormWindowInterface *fw) +{ + const QString shortcutProperty = QLatin1String(shortcutPropertyC); + if (ks.isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(action, shortcutProperty); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, shortcutProperty, qVariantFromValue(ks)); + return cmd; +} + +// return a FormWindow command to apply a POD value or reset command in case +// it is equal to the default value. + +template <class T> +QDesignerFormWindowCommand *setPropertyCommand(const QString &name, T value, T defaultValue, + QObject *o, QDesignerFormWindowInterface *fw) +{ + if (value == defaultValue) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(o, name); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(o, name, QVariant(value)); + return cmd; +} + +// Return the text value of a string property via PropertySheetStringValue +static inline QString textPropertyValue(const QDesignerPropertySheetExtension *sheet, const QString &name) +{ + const int index = sheet->indexOf(name); + Q_ASSERT(index != -1); + const PropertySheetStringValue ps = qVariantValue<PropertySheetStringValue>(sheet->property(index)); + return ps.value(); +} + +void ActionEditor::editAction(QAction *action) +{ + if (!action) + return; + + NewActionDialog dlg(this); + dlg.setWindowTitle(tr("Edit action")); + + ActionData oldActionData; + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), action); + oldActionData.name = action->objectName(); + oldActionData.text = action->text(); + oldActionData.toolTip = textPropertyValue(sheet, QLatin1String(toolTipPropertyC)); + oldActionData.icon = qVariantValue<PropertySheetIconValue>(sheet->property(sheet->indexOf(QLatin1String(iconPropertyC)))); + oldActionData.keysequence = qVariantValue<QKeySequence>(sheet->property(sheet->indexOf(QLatin1String(shortcutPropertyC)))); + oldActionData.checkable = action->isCheckable(); + dlg.setActionData(oldActionData); + + if (!dlg.exec()) + return; + + // figure out changes and whether to start a macro + const ActionData newActionData = dlg.actionData(); + const unsigned changeMask = newActionData.compare(oldActionData); + if (changeMask == 0u) + return; + + const bool severalChanges = (changeMask != ActionData::TextChanged) && (changeMask != ActionData::NameChanged) + && (changeMask != ActionData::ToolTipChanged) && (changeMask != ActionData::IconChanged) + && (changeMask != ActionData::CheckableChanged) && (changeMask != ActionData::KeysequenceChanged); + + QDesignerFormWindowInterface *fw = formWindow(); + QUndoStack *undoStack = fw->commandHistory(); + if (severalChanges) + fw->beginCommand(QLatin1String("Edit action")); + + if (changeMask & ActionData::NameChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(objectNamePropertyC), newActionData.name, action, fw)); + + if (changeMask & ActionData::TextChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(textPropertyC), newActionData.text, action, fw)); + + if (changeMask & ActionData::ToolTipChanged) + undoStack->push(createTextPropertyCommand(QLatin1String(toolTipPropertyC), newActionData.toolTip, action, fw)); + + if (changeMask & ActionData::IconChanged) + undoStack->push(setIconPropertyCommand(newActionData.icon, action, fw)); + + if (changeMask & ActionData::CheckableChanged) + undoStack->push(setPropertyCommand(QLatin1String(checkablePropertyC), newActionData.checkable, false, action, fw)); + + if (changeMask & ActionData::KeysequenceChanged) + undoStack->push(setKeySequencePropertyCommand(newActionData.keysequence, action, fw)); + + if (severalChanges) + fw->endCommand(); +} + +void ActionEditor::editCurrentAction() +{ + if (QAction *a = m_actionView->currentAction()) + editAction(a); +} + +void ActionEditor::navigateToSlotCurrentAction() +{ + if (QAction *a = m_actionView->currentAction()) + QDesignerTaskMenu::navigateToSlot(m_core, a, QLatin1String("triggered()")); +} + +void ActionEditor::deleteActions(QDesignerFormWindowInterface *fw, const ActionList &actions) +{ + // We need a macro even in the case of single action because the commands might cause the + // scheduling of other commands (signal slots connections) + const QString description = actions.size() == 1 ? + tr("Remove action '%1'").arg(actions.front()->objectName()) : tr("Remove actions"); + fw->beginCommand(description); + foreach(QAction *action, actions) { + RemoveActionCommand *cmd = new RemoveActionCommand(fw); + cmd->init(action); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void ActionEditor::copyActions(QDesignerFormWindowInterface *fwi, const ActionList &actions) +{ + FormWindowBase *fw = qobject_cast<FormWindowBase *>(fwi); + if (!fw ) + return; + + FormBuilderClipboard clipboard; + clipboard.m_actions = actions; + + if (clipboard.empty()) + return; + + QEditorFormBuilder *formBuilder = fw->createFormBuilder(); + Q_ASSERT(formBuilder); + + QBuffer buffer; + if (buffer.open(QIODevice::WriteOnly)) + if (formBuilder->copy(&buffer, clipboard)) + qApp->clipboard()->setText(QString::fromUtf8(buffer.buffer()), QClipboard::Clipboard); + delete formBuilder; +} + +void ActionEditor::slotDelete() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + deleteActions(fw, selection); +} + +QString ActionEditor::actionTextToName(const QString &text, const QString &prefix) +{ + QString name = text; + if (name.isEmpty()) + return QString(); + + name[0] = name.at(0).toUpper(); + name.prepend(prefix); + const QString underscore = QString(QLatin1Char('_')); + name.replace(QRegExp(QString(QLatin1String("[^a-zA-Z_0-9]"))), underscore); + name.replace(QRegExp(QLatin1String("__*")), underscore); + if (name.endsWith(underscore.at(0))) + name.truncate(name.size() - 1); + + return name; +} + +void ActionEditor::resourceImageDropped(const QString &path, QAction *action) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), action); + const PropertySheetIconValue oldIcon = + qVariantValue<PropertySheetIconValue>(sheet->property(sheet->indexOf(QLatin1String(iconPropertyC)))); + PropertySheetIconValue newIcon; + newIcon.setPixmap(QIcon::Normal, QIcon::Off, PropertySheetPixmapValue(path)); + if (newIcon.paths().isEmpty() || newIcon.paths() == oldIcon.paths()) + return; + + fw->commandHistory()->push(setIconPropertyCommand(newIcon , action, fw)); +} + +void ActionEditor::mainContainerChanged() +{ + // Invalidate references to objects kept in model + if (sender() == formWindow()) + setFormWindow(0); +} + +void ActionEditor::slotViewMode(QAction *a) +{ + m_actionView->setViewMode(a->data().toInt()); + updateViewModeActions(); +} + +void ActionEditor::slotSelectAssociatedWidget(QWidget *w) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core()->objectInspector()); + if (!oi) + return; + + fw->clearSelection(); // Actually, there are no widgets selected due to focus in event handling. Just to be sure. + oi->selectObject(w); +} + +void ActionEditor::restoreSettings() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + m_actionView->setViewMode(settings->value(QLatin1String(actionEditorViewModeKey), 0).toInt()); + updateViewModeActions(); +} + +void ActionEditor::saveSettings() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->setValue(QLatin1String(actionEditorViewModeKey), m_actionView->viewMode()); +} + +void ActionEditor::updateViewModeActions() +{ + switch (m_actionView->viewMode()) { + case ActionView::IconView: + m_iconViewAction->setChecked(true); + break; + case ActionView::DetailedView: + m_listViewAction->setChecked(true); + break; + } +} + +void ActionEditor::slotCopy() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + copyActions(fw, selection); +} + +void ActionEditor::slotCut() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw ) + return; + + const ActionView::ActionList selection = m_actionView->selectedActions(); + if (selection.empty()) + return; + + copyActions(fw, selection); + deleteActions(fw, selection); +} + +void ActionEditor::slotPaste() +{ + FormWindowBase *fw = qobject_cast<FormWindowBase *>(formWindow()); + if (!fw) + return; + m_actionView->clearSelection(); + fw->paste(FormWindowBase::PasteActionsOnly); +} + +void ActionEditor::slotContextMenuRequested(QContextMenuEvent *e, QAction *item) +{ + // set up signal mapper + if (!m_selectAssociatedWidgetsMapper) { + m_selectAssociatedWidgetsMapper = new QSignalMapper(this); + connect(m_selectAssociatedWidgetsMapper, SIGNAL(mapped(QWidget*)), this, SLOT(slotSelectAssociatedWidget(QWidget*))); + } + + QMenu menu(this); + menu.addAction(m_actionNew); + menu.addSeparator(); + menu.addAction(m_actionEdit); + if (QDesignerTaskMenu::isSlotNavigationEnabled(m_core)) + menu.addAction(m_actionNavigateToSlot); + + // Associated Widgets + if (QAction *action = m_actionView->currentAction()) { + const QWidgetList associatedWidgets = ActionModel::associatedWidgets(action); + if (!associatedWidgets.empty()) { + QMenu *associatedWidgetsSubMenu = menu.addMenu(tr("Used In")); + foreach (QWidget *w, associatedWidgets) { + QAction *action = associatedWidgetsSubMenu->addAction(w->objectName()); + m_selectAssociatedWidgetsMapper->setMapping(action, w); + connect(action, SIGNAL(triggered()), m_selectAssociatedWidgetsMapper, SLOT(map())); + } + } + } + + menu.addSeparator(); + menu.addAction(m_actionCut); + menu.addAction(m_actionCopy); + menu.addAction(m_actionPaste); + menu.addAction(m_actionSelectAll); + menu.addAction(m_actionDelete); + menu.addSeparator(); + menu.addAction(m_iconViewAction); + menu.addAction(m_listViewAction); + + emit contextMenuRequested(&menu, item); + + menu.exec(e->globalPos()); + e->accept(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + diff --git a/tools/designer/src/lib/shared/actioneditor_p.h b/tools/designer/src/lib/shared/actioneditor_p.h new file mode 100644 index 0000000..36baf50 --- /dev/null +++ b/tools/designer/src/lib/shared/actioneditor_p.h @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ACTIONEDITOR_H +#define ACTIONEDITOR_H + +#include "shared_global_p.h" +#include <QtDesigner/QDesignerActionEditorInterface> + +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerPropertyEditorInterface; +class QDesignerSettingsInterface; +class QMenu; +class QActionGroup; +class QSignalMapper; +class QItemSelection; +class QListWidget; +class QPushButton; +class QLineEdit; +class QToolButton; + +namespace qdesigner_internal { + +class ActionView; +class ResourceMimeData; + +class QDESIGNER_SHARED_EXPORT ActionEditor: public QDesignerActionEditorInterface +{ + Q_OBJECT +public: + explicit ActionEditor(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~ActionEditor(); + + QDesignerFormWindowInterface *formWindow() const; + virtual void setFormWindow(QDesignerFormWindowInterface *formWindow); + + virtual QDesignerFormEditorInterface *core() const; + + QAction *actionNew() const; + QAction *actionDelete() const; + + QString filter() const; + + virtual void manageAction(QAction *action); + virtual void unmanageAction(QAction *action); + + static QString actionTextToName(const QString &text, const QString &prefix = QLatin1String("action")); + + // Utility to create a configure button with menu for usage on toolbars + static QToolButton *createConfigureMenuButton(const QString &t, QMenu **ptrToMenu); + +public slots: + void setFilter(const QString &filter); + void mainContainerChanged(); + +private slots: + void slotCurrentItemChanged(QAction *item); + void slotSelectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void editAction(QAction *item); + void editCurrentAction(); + void navigateToSlotCurrentAction(); + void slotActionChanged(); + void slotNewAction(); + void slotDelete(); + void resourceImageDropped(const QString &path, QAction *action); + void slotContextMenuRequested(QContextMenuEvent *, QAction *); + void slotViewMode(QAction *a); + void slotSelectAssociatedWidget(QWidget *w); + void slotCopy(); + void slotCut(); + void slotPaste(); + +signals: + void itemActivated(QAction *item); + // Context menu for item or global menu if item == 0. + void contextMenuRequested(QMenu *menu, QAction *item); + +private: + typedef QList<QAction *> ActionList; + void deleteActions(QDesignerFormWindowInterface *formWindow, const ActionList &); + void copyActions(QDesignerFormWindowInterface *formWindow, const ActionList &); + + void restoreSettings(); + void saveSettings(); + + void updateViewModeActions(); + + QDesignerFormEditorInterface *m_core; + QPointer<QDesignerFormWindowInterface> m_formWindow; + QListWidget *m_actionGroups; + + ActionView *m_actionView; + + QAction *m_actionNew; + QAction *m_actionEdit; + QAction *m_actionNavigateToSlot; + QAction *m_actionCopy; + QAction *m_actionCut; + QAction *m_actionPaste; + QAction *m_actionSelectAll; + QAction *m_actionDelete; + + QActionGroup *m_viewModeGroup; + QAction *m_iconViewAction; + QAction *m_listViewAction; + + QString m_filter; + QWidget *m_filterWidget; + QSignalMapper *m_selectAssociatedWidgetsMapper; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ACTIONEDITOR_H diff --git a/tools/designer/src/lib/shared/actionprovider_p.h b/tools/designer/src/lib/shared/actionprovider_p.h new file mode 100644 index 0000000..e358f61 --- /dev/null +++ b/tools/designer/src/lib/shared/actionprovider_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef ACTIONPROVIDER_H +#define ACTIONPROVIDER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtDesigner/extension.h> +#include <QtCore/QPoint> +#include <QtCore/QRect> +#include <QtGui/QApplication> + +QT_BEGIN_NAMESPACE + +class QAction; + +class QDesignerActionProviderExtension +{ +public: + virtual ~QDesignerActionProviderExtension() {} + + virtual QRect actionGeometry(QAction *action) const = 0; + virtual QAction *actionAt(const QPoint &pos) const = 0; + + virtual void adjustIndicator(const QPoint &pos) = 0; +}; + +// Find action at the given position for a widget that has actionGeometry() (QToolBar, +// QMenuBar, QMenu). They usually have actionAt(), but that fails since Designer usually sets +// WA_TransparentForMouseEvents on the widgets. +template <class Widget> + int actionIndexAt(const Widget *w, const QPoint &pos, Qt::Orientation orientation) +{ + const QList<QAction*> actions = w->actions(); + const int actionCount = actions.count(); + if (actionCount == 0) + return -1; + // actionGeometry() can be wrong sometimes; it returns a geometry that + // stretches to the end of the toolbar/menu bar. So, check from the beginning + // in the case of a horizontal right-to-left orientation. + const bool checkTopRight = orientation == Qt::Horizontal && QApplication::layoutDirection() == Qt::RightToLeft; + const QPoint topRight = QPoint(w->rect().width(), 0); + for (int index = 0; index < actionCount; ++index) { + QRect g = w->actionGeometry(actions.at(index)); + if (checkTopRight) + g.setTopRight(topRight); + else + g.setTopLeft(QPoint(0, 0)); + + if (g.contains(pos)) + return index; + } + return -1; +} + +Q_DECLARE_EXTENSION_INTERFACE(QDesignerActionProviderExtension, "com.trolltech.Qt.Designer.ActionProvider") + +QT_END_NAMESPACE + +#endif // ACTIONPROVIDER_H diff --git a/tools/designer/src/lib/shared/actionrepository.cpp b/tools/designer/src/lib/shared/actionrepository.cpp new file mode 100644 index 0000000..941a9ba --- /dev/null +++ b/tools/designer/src/lib/shared/actionrepository.cpp @@ -0,0 +1,659 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "actionrepository_p.h" +#include "qtresourceview_p.h" +#include "iconloader_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QDrag> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QStandardItemModel> +#include <QtGui/QToolButton> +#include <QtGui/QPixmap> +#include <QtGui/QAction> +#include <QtGui/QHeaderView> +#include <QtGui/QToolBar> +#include <QtGui/QMenu> +#include <QtGui/qevent.h> +#include <QtCore/QSet> +#include <QtCore/QDebug> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +namespace { + enum { listModeIconSize = 16, iconModeIconSize = 24 }; +} + +static const char *actionMimeType = "action-repository/actions"; +static const char *plainTextMimeType = "text/plain"; + +static inline QAction *actionOfItem(const QStandardItem* item) +{ + return qvariant_cast<QAction*>(item->data(qdesigner_internal::ActionModel::ActionRole)); +} + +static QIcon fixActionIcon(const QIcon &icon) +{ + if (icon.isNull()) + return qdesigner_internal::emptyIcon(); + return icon; +} + +namespace qdesigner_internal { + +// ----------- ActionModel +ActionModel::ActionModel(QWidget *parent ) : + QStandardItemModel(parent), + m_core(0) +{ + QStringList headers; + headers += tr("Name"); + headers += tr("Used"); + headers += tr("Text"); + headers += tr("Shortcut"); + headers += tr("Checkable"); + headers += tr("ToolTip"); + Q_ASSERT(NumColumns == headers.size()); + setHorizontalHeaderLabels(headers); +} + +void ActionModel::clearActions() +{ + removeRows(0, rowCount()); +} + +int ActionModel::findAction(QAction *action) const +{ + const int rows = rowCount(); + for (int i = 0; i < rows; i++) + if (action == actionOfItem(item(i))) + return i; + return -1; +} + +void ActionModel::update(int row) +{ + Q_ASSERT(m_core); + // need to create the row list ... grrr.. + if (row >= rowCount()) + return; + + QStandardItemList list; + for (int i = 0; i < NumColumns; i++) + list += item(row, i); + + setItems(m_core, actionOfItem(list.front()), list); +} + +void ActionModel::remove(int row) +{ + qDeleteAll(takeRow(row)); +} + +QModelIndex ActionModel::addAction(QAction *action) +{ + Q_ASSERT(m_core); + QStandardItemList items; + const Qt::ItemFlags flags = Qt::ItemIsSelectable|Qt::ItemIsDropEnabled|Qt::ItemIsDragEnabled|Qt::ItemIsEnabled; + + QVariant itemData; + qVariantSetValue(itemData, action); + + for (int i = 0; i < NumColumns; i++) { + QStandardItem *item = new QStandardItem; + item->setData(itemData, ActionRole); + item->setFlags(flags); + items.push_back(item); + } + setItems(m_core, action, items); + appendRow(items); + return indexFromItem(items.front()); +} + +// Find the associated menus and toolbars, ignore toolbuttons +QWidgetList ActionModel::associatedWidgets(const QAction *action) +{ + QWidgetList rc = action->associatedWidgets(); + for (QWidgetList::iterator it = rc.begin(); it != rc.end(); ) + if (qobject_cast<const QMenu *>(*it) || qobject_cast<const QToolBar *>(*it)) { + ++it; + } else { + it = rc.erase(it); + } + return rc; +} + +// shortcut is a fake property, need to retrieve it via property sheet. +static QString actionShortCut(QDesignerFormEditorInterface *core, QAction *action) +{ + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), action); + if (!sheet) + return QString(); + const int index = sheet->indexOf(QLatin1String("shortcut")); + if (index == -1) + return QString(); + const QKeySequence keysequence = qvariant_cast<QKeySequence>(sheet->property(index)); + return keysequence.toString(); +} + +void ActionModel::setItems(QDesignerFormEditorInterface *core, QAction *action, QStandardItemList &sl) +{ + + // Tooltip, mostly for icon view mode + QString firstTooltip = action->objectName(); + const QString text = action->text(); + if (!text.isEmpty()) { + firstTooltip += QLatin1Char('\n'); + firstTooltip += text; + } + + Q_ASSERT(sl.size() == NumColumns); + + QStandardItem *item = sl[NameColumn]; + item->setText(action->objectName()); + item->setIcon(fixActionIcon(action->icon())); + item->setToolTip(firstTooltip); + item->setWhatsThis(firstTooltip); + // Used + const QWidgetList associatedDesignerWidgets = associatedWidgets(action); + const bool used = !associatedDesignerWidgets.empty(); + item = sl[UsedColumn]; + item->setCheckState(used ? Qt::Checked : Qt::Unchecked); + if (used) { + QString usedToolTip; + const QString separator = QLatin1String(", "); + const int count = associatedDesignerWidgets.size(); + for (int i = 0; i < count; i++) { + if (i) + usedToolTip += separator; + usedToolTip += associatedDesignerWidgets.at(i)->objectName(); + } + item->setToolTip(usedToolTip); + } else { + item->setToolTip(QString()); + } + // text + item = sl[TextColumn]; + item->setText(action->text()); + item->setToolTip(action->text()); + // shortcut + const QString shortcut = actionShortCut(core, action); + item = sl[ShortCutColumn]; + item->setText(shortcut); + item->setToolTip(shortcut); + // checkable + sl[CheckedColumn]->setCheckState(action->isCheckable() ? Qt::Checked : Qt::Unchecked); + // ToolTip. This might be multi-line, rich text + QString toolTip = action->toolTip(); + item = sl[ToolTipColumn]; + item->setToolTip(toolTip); + item->setText(toolTip.replace(QLatin1Char('\n'), QLatin1Char(' '))); +} + +QMimeData *ActionModel::mimeData(const QModelIndexList &indexes ) const +{ + ActionRepositoryMimeData::ActionList actionList; + + QSet<QAction*> actions; + foreach (const QModelIndex &index, indexes) + if (QStandardItem *item = itemFromIndex(index)) + if (QAction *action = actionOfItem(item)) + actions.insert(action); + return new ActionRepositoryMimeData(actions.toList(), Qt::CopyAction); +} + +// Resource images are plain text. The drag needs to be restricted, however. +QStringList ActionModel::mimeTypes() const +{ + return QStringList(QLatin1String(plainTextMimeType)); +} + +QString ActionModel::actionName(int row) const +{ + return item(row, NameColumn)->text(); +} + +bool ActionModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &) +{ + if (action != Qt::CopyAction) + return false; + + QStandardItem *droppedItem = item(row, column); + if (!droppedItem) + return false; + + + QtResourceView::ResourceType type; + QString path; + if (!QtResourceView::decodeMimeData(data, &type, &path) || type != QtResourceView::ResourceImage) + return false; + + emit resourceImageDropped(path, actionOfItem(droppedItem)); + return true; +} + +QAction *ActionModel::actionAt(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + QStandardItem *i = itemFromIndex(index); + if (!i) + return 0; + return actionOfItem(i); +} + +// helpers + +static bool handleImageDragEnterMoveEvent(QDropEvent *event) +{ + QtResourceView::ResourceType type; + const bool rc = QtResourceView::decodeMimeData(event->mimeData(), &type) && type == QtResourceView::ResourceImage; + if (rc) + event->acceptProposedAction(); + else + event->ignore(); + return rc; +} + +static void handleImageDropEvent(const QAbstractItemView *iv, QDropEvent *event, ActionModel *am) +{ + const QModelIndex index = iv->indexAt(event->pos()); + if (!index.isValid()) { + event->ignore(); + return; + } + + if (!handleImageDragEnterMoveEvent(event)) + return; + + am->dropMimeData(event->mimeData(), event->proposedAction(), index.row(), 0, iv->rootIndex()); +} + +// Basically mimic QAbstractItemView's startDrag routine, except that +// another pixmap is used, we don't want the whole row. + +void startActionDrag(QWidget *dragParent, ActionModel *model, const QModelIndexList &indexes, Qt::DropActions supportedActions) +{ + if (indexes.empty()) + return; + + QDrag *drag = new QDrag(dragParent); + QMimeData *data = model->mimeData(indexes); + drag->setMimeData(data); + if (ActionRepositoryMimeData *actionMimeData = qobject_cast<ActionRepositoryMimeData *>(data)) + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(actionMimeData->actionList().front())); + + drag->start(supportedActions); +} + +// ---------------- ActionTreeView: +ActionTreeView::ActionTreeView(ActionModel *model, QWidget *parent) : + QTreeView(parent), + m_model(model) +{ + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(DragDrop); + setModel(model); + setRootIsDecorated(false); + setTextElideMode(Qt::ElideMiddle); + + setModel(model); + connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(slotActivated(QModelIndex))); + connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int))); + + setIconSize(QSize(listModeIconSize, listModeIconSize)); + +} + +QAction *ActionTreeView::currentAction() const +{ + return m_model->actionAt(currentIndex()); +} + +void ActionTreeView::filter(const QString &text) +{ + const int rowCount = m_model->rowCount(); + const bool empty = text.isEmpty(); + const QModelIndex parent = rootIndex(); + for (int i = 0; i < rowCount; i++) + setRowHidden(i, parent, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive)); +} + +void ActionTreeView::dragEnterEvent(QDragEnterEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionTreeView::dragMoveEvent(QDragMoveEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionTreeView::dropEvent(QDropEvent *event) +{ + handleImageDropEvent(this, event, m_model); +} + +void ActionTreeView::focusInEvent(QFocusEvent *event) +{ + QTreeView::focusInEvent(event); + // Make property editor display current action + if (QAction *a = currentAction()) + emit currentChanged(a); +} + +void ActionTreeView::contextMenuEvent(QContextMenuEvent *event) +{ + emit contextMenuRequested(event, m_model->actionAt(indexAt(event->pos()))); +} + +void ActionTreeView::currentChanged(const QModelIndex ¤t, const QModelIndex &/*previous*/) +{ + emit currentChanged(m_model->actionAt(current)); +} + +void ActionTreeView::slotActivated(const QModelIndex &index) +{ + emit activated(m_model->actionAt(index)); +} + +void ActionTreeView::startDrag(Qt::DropActions supportedActions) +{ + startActionDrag(this, m_model, selectedIndexes(), supportedActions); +} + +// ---------------- ActionListView: +ActionListView::ActionListView(ActionModel *model, QWidget *parent) : + QListView(parent), + m_model(model) +{ + setDragEnabled(true); + setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(DragDrop); + setModel(model); + setTextElideMode(Qt::ElideMiddle); + connect(this, SIGNAL(activated(QModelIndex)), this, SLOT(slotActivated(QModelIndex))); + + // We actually want 'Static' as the user should be able to + // drag away actions only (not to rearrange icons). + // We emulate that by not accepting our own + // drag data. 'Static' causes the list view to disable drag and drop + // on the viewport. + setMovement(Snap); + setViewMode(IconMode); + setIconSize(QSize(iconModeIconSize, iconModeIconSize)); + setGridSize(QSize(4 * iconModeIconSize, 2 * iconModeIconSize)); + setSpacing(iconModeIconSize / 3); +} + +QAction *ActionListView::currentAction() const +{ + return m_model->actionAt(currentIndex()); +} + +void ActionListView::filter(const QString &text) +{ + const int rowCount = m_model->rowCount(); + const bool empty = text.isEmpty(); + for (int i = 0; i < rowCount; i++) + setRowHidden(i, !empty && !m_model->actionName(i).contains(text, Qt::CaseInsensitive)); +} + +void ActionListView::dragEnterEvent(QDragEnterEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionListView::dragMoveEvent(QDragMoveEvent *event) +{ + handleImageDragEnterMoveEvent(event); +} + +void ActionListView::dropEvent(QDropEvent *event) +{ + handleImageDropEvent(this, event, m_model); +} + +void ActionListView::focusInEvent(QFocusEvent *event) +{ + QListView::focusInEvent(event); + // Make property editor display current action + if (QAction *a = currentAction()) + emit currentChanged(a); +} + +void ActionListView::contextMenuEvent(QContextMenuEvent *event) +{ + emit contextMenuRequested(event, m_model->actionAt(indexAt(event->pos()))); +} + +void ActionListView::currentChanged(const QModelIndex ¤t, const QModelIndex & /*previous*/) +{ + emit currentChanged(m_model->actionAt(current)); +} + +void ActionListView::slotActivated(const QModelIndex &index) +{ + emit activated(m_model->actionAt(index)); +} + +void ActionListView::startDrag(Qt::DropActions supportedActions) +{ + startActionDrag(this, m_model, selectedIndexes(), supportedActions); +} + +// ActionView +ActionView::ActionView(QWidget *parent) : + QStackedWidget(parent), + m_model(new ActionModel(this)), + m_actionTreeView(new ActionTreeView(m_model)), + m_actionListView(new ActionListView(m_model)) +{ + addWidget(m_actionListView); + addWidget(m_actionTreeView); + // Wire signals + connect(m_actionTreeView, SIGNAL(contextMenuRequested(QContextMenuEvent*, QAction*)), + this, SIGNAL(contextMenuRequested(QContextMenuEvent*, QAction*))); + connect(m_actionListView, SIGNAL(contextMenuRequested(QContextMenuEvent*, QAction*)), + this, SIGNAL(contextMenuRequested(QContextMenuEvent*, QAction*))); + + // make it possible for vs integration to reimplement edit action dialog + // [which it shouldn't do actually] + connect(m_actionListView, SIGNAL(activated(QAction*)), this, SIGNAL(activated(QAction*))); + connect(m_actionTreeView, SIGNAL(activated(QAction*)), this, SIGNAL(activated(QAction*))); + + connect(m_actionListView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentChanged(QAction*))); + connect(m_actionTreeView, SIGNAL(currentChanged(QAction*)),this, SLOT(slotCurrentChanged(QAction*))); + + connect(m_model, SIGNAL(resourceImageDropped(QString,QAction*)), + this, SIGNAL(resourceImageDropped(QString,QAction*))); + + // sync selection models + QItemSelectionModel *selectionModel = m_actionTreeView->selectionModel(); + m_actionListView->setSelectionModel(selectionModel); + connect(selectionModel, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SIGNAL(selectionChanged(QItemSelection,QItemSelection))); +} + +int ActionView::viewMode() const +{ + return currentWidget() == m_actionListView ? IconView : DetailedView; +} + +void ActionView::setViewMode(int lm) +{ + if (viewMode() == lm) + return; + + switch (lm) { + case IconView: + setCurrentWidget(m_actionListView); + break; + case DetailedView: + setCurrentWidget(m_actionTreeView); + break; + default: + break; + } +} + +void ActionView::slotCurrentChanged(QAction *action) +{ + // emit only for currently visible + if (sender() == currentWidget()) + emit currentChanged(action); +} + +void ActionView::filter(const QString &text) +{ + m_actionTreeView->filter(text); + m_actionListView->filter(text); +} + +void ActionView::selectAll() +{ + m_actionTreeView->selectAll(); +} + +void ActionView::clearSelection() +{ + m_actionTreeView->selectionModel()->clearSelection(); +} + +void ActionView::setCurrentIndex(const QModelIndex &index) +{ + m_actionTreeView->setCurrentIndex(index); +} + +QAction *ActionView::currentAction() const +{ + return m_actionListView->currentAction(); +} + +void ActionView::setSelectionMode(QAbstractItemView::SelectionMode sm) +{ + m_actionTreeView->setSelectionMode(sm); + m_actionListView->setSelectionMode(sm); +} + +QAbstractItemView::SelectionMode ActionView::selectionMode() const +{ + return m_actionListView->selectionMode(); +} + +QItemSelection ActionView::selection() const +{ + return m_actionListView->selectionModel()->selection(); +} + +ActionView::ActionList ActionView::selectedActions() const +{ + ActionList rc; + foreach (const QModelIndex &index, selection().indexes()) + if (index.column() == 0) + rc += actionOfItem(m_model->itemFromIndex(index)); + return rc; +} +// ---------- ActionRepositoryMimeData +ActionRepositoryMimeData::ActionRepositoryMimeData(QAction *a, Qt::DropAction dropAction) : + m_dropAction(dropAction) +{ + m_actionList += a; +} + +ActionRepositoryMimeData::ActionRepositoryMimeData(const ActionList &al, Qt::DropAction dropAction) : + m_dropAction(dropAction), + m_actionList(al) +{ +} + +QStringList ActionRepositoryMimeData::formats() const +{ + return QStringList(QLatin1String(actionMimeType)); +} + +QPixmap ActionRepositoryMimeData::actionDragPixmap(const QAction *action) +{ + + // Try to find a suitable pixmap. Grab either widget or icon. + const QIcon icon = action->icon(); + if (!icon.isNull()) + return icon.pixmap(QSize(22, 22)); + + foreach (QWidget *w, action->associatedWidgets()) + if (QToolButton *tb = qobject_cast<QToolButton *>(w)) + return QPixmap::grabWidget(tb); + + // Create a QToolButton + QToolButton *tb = new QToolButton; + tb->setText(action->text()); + tb->setToolButtonStyle(Qt::ToolButtonTextOnly); +#ifdef Q_WS_WIN // Force alien off to make adjustSize() take the system minimumsize into account. + tb->createWinId(); +#endif + tb->adjustSize(); + const QPixmap rc = QPixmap::grabWidget(tb); + tb->deleteLater(); + return rc; +} + +void ActionRepositoryMimeData::accept(QDragMoveEvent *event) const +{ + if (event->proposedAction() == m_dropAction) { + event->acceptProposedAction(); + } else { + event->setDropAction(m_dropAction); + event->accept(); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/actionrepository_p.h b/tools/designer/src/lib/shared/actionrepository_p.h new file mode 100644 index 0000000..9d1af5a --- /dev/null +++ b/tools/designer/src/lib/shared/actionrepository_p.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ACTIONREPOSITORY_H +#define ACTIONREPOSITORY_H + +#include "shared_global_p.h" +#include <QtCore/QMimeData> +#include <QtGui/QStandardItemModel> +#include <QtGui/QTreeView> +#include <QtGui/QListView> +#include <QtGui/QStackedWidget> + +QT_BEGIN_NAMESPACE + +class QPixmap; + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +// Shared model of actions, to be used for several views (detailed/icon view). +class QDESIGNER_SHARED_EXPORT ActionModel: public QStandardItemModel +{ + Q_OBJECT +public: + enum Columns { NameColumn, UsedColumn, TextColumn, ShortCutColumn, CheckedColumn, ToolTipColumn, NumColumns }; + enum { ActionRole = Qt::UserRole + 1000 }; + + explicit ActionModel(QWidget *parent = 0); + void initialize(QDesignerFormEditorInterface *core) { m_core = core; } + + void clearActions(); + QModelIndex addAction(QAction *a); + // remove row + void remove(int row); + // update the row from the underlying action + void update(int row); + + // return row of action or -1. + int findAction(QAction *) const; + + QString actionName(int row) const; + QAction *actionAt(const QModelIndex &index) const; + + virtual QMimeData *mimeData(const QModelIndexList &indexes) const; + virtual QStringList mimeTypes() const; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent); + + // Find the associated menus and toolbars, ignore toolbuttons + static QWidgetList associatedWidgets(const QAction *action); + +signals: + void resourceImageDropped(const QString &path, QAction *action); + +private: + void initializeHeaders(); + + typedef QList<QStandardItem *> QStandardItemList; + static void setItems(QDesignerFormEditorInterface *core, QAction *a, QStandardItemList &sl); + + QDesignerFormEditorInterface *m_core; +}; + +// Internal class that provides the detailed view of actions. +class ActionTreeView: public QTreeView +{ + Q_OBJECT +public: + explicit ActionTreeView(ActionModel *model, QWidget *parent = 0); + QAction *currentAction() const; + +public slots: + void filter(const QString &text); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + +protected slots: + virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +protected: + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void startDrag(Qt::DropActions supportedActions); + +private slots: + void slotActivated(const QModelIndex &); + +private: + ActionModel *m_model; +}; + +// Internal class that provides the icon view of actions. +class ActionListView: public QListView +{ + Q_OBJECT +public: + explicit ActionListView(ActionModel *model, QWidget *parent = 0); + QAction *currentAction() const; + +public slots: + void filter(const QString &text); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + +protected slots: + virtual void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +protected: + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void focusInEvent(QFocusEvent *event); + virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void startDrag(Qt::DropActions supportedActions); + +private slots: + void slotActivated(const QModelIndex &); + +private: + ActionModel *m_model; +}; + +// Action View that can be switched between detailed and icon view +// using a QStackedWidget of ActionListView / ActionTreeView +// that share the item model and the selection model. + +class ActionView : public QStackedWidget { + Q_OBJECT +public: + // Separate initialize() function takes core argument to make this + // thing usable as promoted widget. + explicit ActionView(QWidget *parent = 0); + void initialize(QDesignerFormEditorInterface *core) { m_model->initialize(core); } + + // View mode + enum { DetailedView, IconView }; + int viewMode() const; + void setViewMode(int lm); + + void setSelectionMode(QAbstractItemView::SelectionMode sm); + QAbstractItemView::SelectionMode selectionMode() const; + + ActionModel *model() const { return m_model; } + + QAction *currentAction() const; + void setCurrentIndex(const QModelIndex &index); + + typedef QList<QAction*> ActionList; + ActionList selectedActions() const; + QItemSelection selection() const; + +public slots: + void filter(const QString &text); + void selectAll(); + void clearSelection(); + +signals: + void contextMenuRequested(QContextMenuEvent *event, QAction *); + void currentChanged(QAction *action); + void activated(QAction *action); + void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected); + void resourceImageDropped(const QString &data, QAction *action); + +private slots: + void slotCurrentChanged(QAction *action); + +private: + ActionModel *m_model; + ActionTreeView *m_actionTreeView; + ActionListView *m_actionListView; +}; + +class QDESIGNER_SHARED_EXPORT ActionRepositoryMimeData: public QMimeData +{ + Q_OBJECT +public: + typedef QList<QAction*> ActionList; + + ActionRepositoryMimeData(const ActionList &, Qt::DropAction dropAction); + ActionRepositoryMimeData(QAction *, Qt::DropAction dropAction); + + const ActionList &actionList() const { return m_actionList; } + virtual QStringList formats() const; + + static QPixmap actionDragPixmap(const QAction *action); + + // Utility to accept with right action + void accept(QDragMoveEvent *event) const; +private: + const Qt::DropAction m_dropAction; + ActionList m_actionList; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ACTIONREPOSITORY_H diff --git a/tools/designer/src/lib/shared/addlinkdialog.ui b/tools/designer/src/lib/shared/addlinkdialog.ui new file mode 100644 index 0000000..3171159 --- /dev/null +++ b/tools/designer/src/lib/shared/addlinkdialog.ui @@ -0,0 +1,112 @@ +<ui version="4.0" > + <class>AddLinkDialog</class> + <widget class="QDialog" name="AddLinkDialog" > + <property name="windowTitle" > + <string>Insert Link</string> + </property> + <property name="sizeGripEnabled" > + <bool>false</bool> + </property> + <property name="modal" > + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <layout class="QFormLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Title:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="titleInput" > + <property name="minimumSize" > + <size> + <width>337</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>URL:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="urlInput" /> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer" > + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Line" name="line" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>AddLinkDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>AddLinkDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/codedialog.cpp b/tools/designer/src/lib/shared/codedialog.cpp new file mode 100644 index 0000000..5a2db33 --- /dev/null +++ b/tools/designer/src/lib/shared/codedialog.cpp @@ -0,0 +1,266 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::CodeDialog +*/ + +#include "codedialog_p.h" +#include "qdesigner_utils_p.h" +#include "iconloader_p.h" + +#include <texteditfindwidget.h> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QFileDialog> +#include <QtGui/QIcon> +#include <QtGui/QKeyEvent> +#include <QtGui/QMessageBox> +#include <QtGui/QPushButton> +#include <QtGui/QTextEdit> +#include <QtGui/QToolBar> +#include <QtGui/QVBoxLayout> + +#include <QtCore/QDebug> +#include <QtCore/QDir> +#include <QtCore/QTemporaryFile> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// ----------------- CodeDialogPrivate +struct CodeDialog::CodeDialogPrivate { + CodeDialogPrivate(); + + QTextEdit *m_textEdit; + TextEditFindWidget *m_findWidget; + QString m_formFileName; +}; + +CodeDialog::CodeDialogPrivate::CodeDialogPrivate() + : m_textEdit(new QTextEdit) + , m_findWidget(new TextEditFindWidget) +{ +} + +// ----------------- CodeDialog +CodeDialog::CodeDialog(QWidget *parent) : + QDialog(parent), + m_impl(new CodeDialogPrivate) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + QVBoxLayout *vBoxLayout = new QVBoxLayout; + + // Edit tool bar + QToolBar *toolBar = new QToolBar; + + const QIcon saveIcon = createIconSet(QLatin1String("filesave.png")); + QAction *saveAction = toolBar->addAction(saveIcon, tr("Save...")); + connect(saveAction, SIGNAL(triggered()), this, SLOT(slotSaveAs())); + + const QIcon copyIcon = createIconSet(QLatin1String("editcopy.png")); + QAction *copyAction = toolBar->addAction(copyIcon, tr("Copy All")); + connect(copyAction, SIGNAL(triggered()), this, SLOT(copyAll())); + + QAction *findAction = toolBar->addAction( + TextEditFindWidget::findIconSet(), + tr("&Find in Text..."), + m_impl->m_findWidget, SLOT(activate())); + findAction->setShortcut(QKeySequence::Find); + + vBoxLayout->addWidget(toolBar); + + // Edit + m_impl->m_textEdit->setReadOnly(true); + m_impl->m_textEdit->setMinimumSize(QSize( + m_impl->m_findWidget->minimumSize().width(), + 500)); + vBoxLayout->addWidget(m_impl->m_textEdit); + + // Find + m_impl->m_findWidget->setTextEdit(m_impl->m_textEdit); + vBoxLayout->addWidget(m_impl->m_findWidget); + + // Button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + // Disable auto default + QPushButton *closeButton = buttonBox->button(QDialogButtonBox::Close); + closeButton->setAutoDefault(false); + vBoxLayout->addWidget(buttonBox); + + setLayout(vBoxLayout); +} + +CodeDialog::~CodeDialog() +{ + delete m_impl; +} + +void CodeDialog::setCode(const QString &code) +{ + m_impl->m_textEdit->setPlainText(code); +} + +QString CodeDialog::code() const +{ + return m_impl->m_textEdit->toPlainText(); +} + +void CodeDialog::setFormFileName(const QString &f) +{ + m_impl->m_formFileName = f; +} + +QString CodeDialog::formFileName() const +{ + return m_impl->m_formFileName; +} + +bool CodeDialog::generateCode(const QDesignerFormWindowInterface *fw, + QString *code, + QString *errorMessage) +{ + // Generate temporary file name similar to form file name + // (for header guards) + QString tempPattern = QDir::tempPath(); + if (!tempPattern.endsWith(QDir::separator())) // platform-dependant + tempPattern += QDir::separator(); + const QString fileName = fw->fileName(); + if (fileName.isEmpty()) { + tempPattern += QLatin1String("designer"); + } else { + tempPattern += QFileInfo(fileName).baseName(); + } + tempPattern += QLatin1String("XXXXXX.ui"); + // Write to temp file + QTemporaryFile tempFormFile(tempPattern); + + tempFormFile.setAutoRemove(true); + if (!tempFormFile.open()) { + *errorMessage = tr("A temporary form file could not be created in %1.").arg(QDir::tempPath()); + return false; + } + const QString tempFormFileName = tempFormFile.fileName(); + tempFormFile.write(fw->contents().toUtf8()); + if (!tempFormFile.flush()) { + *errorMessage = tr("The temporary form file %1 could not be written.").arg(tempFormFileName); + return false; + } + tempFormFile.close(); + // Run uic + QByteArray rc; + if (!runUIC(tempFormFileName, UIC_GenerateCode, rc, *errorMessage)) + return false; + *code = QString::fromUtf8(rc); + return true; +} + +bool CodeDialog::showCodeDialog(const QDesignerFormWindowInterface *fw, + QWidget *parent, + QString *errorMessage) +{ + QString code; + if (!generateCode(fw, &code, errorMessage)) + return false; + + CodeDialog dialog(parent); + dialog.setWindowTitle(tr("%1 - [Code]").arg(fw->mainContainer()->windowTitle())); + dialog.setCode(code); + dialog.setFormFileName(fw->fileName()); + dialog.exec(); + return true; +} + +void CodeDialog::slotSaveAs() +{ + // build the default relative name 'ui_sth.h' + const QString headerSuffix = QString(QLatin1Char('h')); + QString filter; + const QString uiFile = formFileName(); + + if (!uiFile.isEmpty()) { + filter = QLatin1String("ui_"); + filter += QFileInfo(uiFile).baseName(); + filter += QLatin1Char('.'); + filter += headerSuffix; + } + // file dialog + while (true) { + const QString fileName = + QFileDialog::getSaveFileName (this, tr("Save Code"), filter, tr("Header Files (*.%1)").arg(headerSuffix)); + if (fileName.isEmpty()) + break; + + QFile file(fileName); + if (!file.open(QIODevice::WriteOnly|QIODevice::Text)) { + warning(tr("The file %1 could not be opened: %2").arg(fileName).arg(file.errorString())); + continue; + } + file.write(code().toUtf8()); + if (!file.flush()) { + warning(tr("The file %1 could not be written: %2").arg(fileName).arg(file.errorString())); + continue; + } + file.close(); + break; + } +} + +void CodeDialog::warning(const QString &msg) +{ + QMessageBox::warning( + this, tr("%1 - Error").arg(windowTitle()), + msg, QMessageBox::Close); +} + +void CodeDialog::copyAll() +{ + QApplication::clipboard()->setText(code()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/codedialog_p.h b/tools/designer/src/lib/shared/codedialog_p.h new file mode 100644 index 0000000..89f5fa9 --- /dev/null +++ b/tools/designer/src/lib/shared/codedialog_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef CODEPREVIEWDIALOG_H +#define CODEPREVIEWDIALOG_H + +#include "shared_global_p.h" +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +// Dialog for viewing code. +class QDESIGNER_SHARED_EXPORT CodeDialog : public QDialog +{ + Q_OBJECT + explicit CodeDialog(QWidget *parent = 0); +public: + virtual ~CodeDialog(); + + static bool generateCode(const QDesignerFormWindowInterface *fw, + QString *code, + QString *errorMessage); + + static bool showCodeDialog(const QDesignerFormWindowInterface *fw, + QWidget *parent, + QString *errorMessage); + +private slots: + void slotSaveAs(); + void copyAll(); + +private: + void setCode(const QString &code); + QString code() const; + void setFormFileName(const QString &f); + QString formFileName() const; + + void warning(const QString &msg); + + struct CodeDialogPrivate; + CodeDialogPrivate *m_impl; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CODEPREVIEWDIALOG_H diff --git a/tools/designer/src/lib/shared/connectionedit.cpp b/tools/designer/src/lib/shared/connectionedit.cpp new file mode 100644 index 0000000..579a59e --- /dev/null +++ b/tools/designer/src/lib/shared/connectionedit.cpp @@ -0,0 +1,1612 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "connectionedit_p.h" + +#include <QtDesigner/abstractformwindow.h> + +#include <QtGui/QPainter> +#include <QtGui/QPaintEvent> +#include <QtGui/QFontMetrics> +#include <QtGui/QPixmap> +#include <QtGui/QMatrix> +#include <QtGui/QApplication> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +#include <QtCore/QMultiMap> + +QT_BEGIN_NAMESPACE + +static const int BG_ALPHA = 32; +static const int LINE_PROXIMITY_RADIUS = 3; +static const int LOOP_MARGIN = 20; +static const int VLABEL_MARGIN = 1; +static const int HLABEL_MARGIN = 3; +static const int GROUND_W = 20; +static const int GROUND_H = 25; + +/******************************************************************************* +** Tools +*/ + +static QRect fixRect(const QRect &r) +{ + return QRect(r.x(), r.y(), r.width() - 1, r.height() - 1); +} + +static QRect expand(const QRect &r, int i) +{ + return QRect(r.x() - i, r.y() - i, r.width() + 2*i, r.height() + 2*i); +} + +static QRect endPointRectHelper(const QPoint &pos) +{ + const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), + QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); + return r; +} + +static void paintGround(QPainter *p, QRect r) +{ + const QPoint mid = r.center(); + p->drawLine(mid.x(), r.top(), mid.x(), mid.y()); + p->drawLine(r.left(), mid.y(), r.right(), mid.y()); + int y = r.top() + 4*r.height()/6; + int x = GROUND_W/6; + p->drawLine(r.left() + x, y, r.right() - x, y); + y = r.top() + 5*r.height()/6; + x = 2*GROUND_W/6; + p->drawLine(r.left() + x, y, r.right() - x, y); + p->drawLine(mid.x(), r.bottom(), mid.x() + 1, r.bottom()); +} + +static void paintEndPoint(QPainter *p, const QPoint &pos) +{ + const QRect r(pos + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS), + QSize(2*LINE_PROXIMITY_RADIUS, 2*LINE_PROXIMITY_RADIUS)); + p->fillRect(fixRect(r), p->pen().color()); +} + +static qdesigner_internal::CETypes::LineDir classifyLine(const QPoint &p1, const QPoint &p2) +{ + if (p1.x() == p2.x()) + return p1.y() < p2.y() ? qdesigner_internal::CETypes::DownDir : qdesigner_internal::CETypes::UpDir; + Q_ASSERT(p1.y() == p2.y()); + return p1.x() < p2.x() ? qdesigner_internal::CETypes::RightDir : qdesigner_internal::CETypes::LeftDir; +} + +static QPoint pointInsideRect(const QRect &r, QPoint p) +{ + if (p.x() < r.left()) + p.setX(r.left()); + else if (p.x() > r.right()) + p.setX(r.right()); + + if (p.y() < r.top()) + p.setY(r.top()); + else if (p.y() > r.bottom()) + p.setY(r.bottom()); + + return p; +} + +namespace qdesigner_internal { + +/******************************************************************************* +** Commands +*/ + +AddConnectionCommand::AddConnectionCommand(ConnectionEdit *edit, Connection *con) + : CECommand(edit), m_con(con) +{ + setText(QApplication::translate("Command", "Add connection")); +} + +void AddConnectionCommand::redo() +{ + edit()->selectNone(); + emit edit()->aboutToAddConnection(edit()->m_con_list.size()); + edit()->m_con_list.append(m_con); + m_con->inserted(); + edit()->setSelected(m_con, true); + emit edit()->connectionAdded(m_con); +} + +void AddConnectionCommand::undo() +{ + const int idx = edit()->indexOfConnection(m_con); + emit edit()->aboutToRemoveConnection(m_con); + edit()->setSelected(m_con, false); + m_con->update(); + m_con->removed(); + edit()->m_con_list.removeAll(m_con); + emit edit()->connectionRemoved(idx); +} + +class AdjustConnectionCommand : public CECommand +{ +public: + AdjustConnectionCommand(ConnectionEdit *edit, Connection *con, + const QPoint &old_source_pos, + const QPoint &old_target_pos, + const QPoint &new_source_pos, + const QPoint &new_target_pos); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; + const QPoint m_old_source_pos; + const QPoint m_old_target_pos; + const QPoint m_new_source_pos; + const QPoint m_new_target_pos; +}; + +AdjustConnectionCommand::AdjustConnectionCommand(ConnectionEdit *edit, Connection *con, + const QPoint &old_source_pos, + const QPoint &old_target_pos, + const QPoint &new_source_pos, + const QPoint &new_target_pos) : + CECommand(edit), + m_con(con), + m_old_source_pos(old_source_pos), + m_old_target_pos(old_target_pos), + m_new_source_pos(new_source_pos), + m_new_target_pos(new_target_pos) +{ + setText(QApplication::translate("Command", "Adjust connection")); +} + +void AdjustConnectionCommand::undo() +{ + m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_old_source_pos); + m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_old_target_pos); +} + +void AdjustConnectionCommand::redo() +{ + m_con->setEndPoint(EndPoint::Source, m_con->widget(EndPoint::Source), m_new_source_pos); + m_con->setEndPoint(EndPoint::Target, m_con->widget(EndPoint::Target), m_new_target_pos); +} + +DeleteConnectionsCommand::DeleteConnectionsCommand(ConnectionEdit *edit, + const ConnectionList &con_list) + : CECommand(edit), m_con_list(con_list) +{ + setText(QApplication::translate("Command", "Delete connections")); +} + +void DeleteConnectionsCommand::redo() +{ + foreach (Connection *con, m_con_list) { + const int idx = edit()->indexOfConnection(con); + emit edit()->aboutToRemoveConnection(con); + Q_ASSERT(edit()->m_con_list.contains(con)); + edit()->setSelected(con, false); + con->update(); + con->removed(); + edit()->m_con_list.removeAll(con); + emit edit()->connectionRemoved(idx); + } +} + +void DeleteConnectionsCommand::undo() +{ + foreach (Connection *con, m_con_list) { + Q_ASSERT(!edit()->m_con_list.contains(con)); + emit edit()->aboutToAddConnection(edit()->m_con_list.size()); + edit()->m_con_list.append(con); + edit()->setSelected(con, true); + con->update(); + con->inserted(); + emit edit()->connectionAdded(con); + } +} + +class SetEndPointCommand : public CECommand +{ +public: + SetEndPointCommand(ConnectionEdit *edit, Connection *con, EndPoint::Type type, QObject *object); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; + const EndPoint::Type m_type; + QObject *m_old_widget, *m_new_widget; + const QPoint m_old_pos; + QPoint m_new_pos; +}; + +SetEndPointCommand::SetEndPointCommand(ConnectionEdit *edit, Connection *con, + EndPoint::Type type, QObject *object) : + CECommand(edit), + m_con(con), + m_type(type), + m_old_widget(con->object(type)), + m_new_widget(object), + m_old_pos(con->endPointPos(type)) +{ + if (QWidget *widget = qobject_cast<QWidget*>(object)) { + m_new_pos = edit->widgetRect(widget).center(); + } + + if (m_type == EndPoint::Source) + setText(QApplication::translate("Command", "Change source")); + else + setText(QApplication::translate("Command", "Change target")); +} + +void SetEndPointCommand::redo() +{ + m_con->setEndPoint(m_type, m_new_widget, m_new_pos); + emit edit()->connectionChanged(m_con); +} + +void SetEndPointCommand::undo() +{ + m_con->setEndPoint(m_type, m_old_widget, m_old_pos); + emit edit()->connectionChanged(m_con); +} + +/******************************************************************************* +** Connection +*/ + +Connection::Connection(ConnectionEdit *edit) : + m_source_pos(QPoint(-1, -1)), + m_target_pos(QPoint(-1, -1)), + m_source(0), + m_target(0), + m_edit(edit), + m_visible(true) +{ + +} + +Connection::Connection(ConnectionEdit *edit, QObject *source, QObject *target) : + m_source_pos(QPoint(-1, -1)), + m_target_pos(QPoint(-1, -1)), + m_source(source), + m_target(target), + m_edit(edit), + m_visible(true) +{ +} + +void Connection::setVisible(bool b) +{ + m_visible = b; +} + +void Connection::updateVisibility() +{ + QWidget *source = widget(EndPoint::Source); + QWidget *target = widget(EndPoint::Target); + + if (source == 0 || target == 0) { + setVisible(false); + return; + } + + QWidget *w = source; + while (w && w->parentWidget()) { + if (!w->isVisibleTo(w->parentWidget())) { + setVisible(false); + return; + } + w = w->parentWidget(); + } + + w = target; + while (w && w->parentWidget()) { + if (!w->isVisibleTo(w->parentWidget())) { + setVisible(false); + return; + } + w = w->parentWidget(); + } + + setVisible(true); +} + +bool Connection::isVisible() const +{ + return m_visible; +} + +bool Connection::ground() const +{ + return m_target != 0 && m_target == m_edit->m_bg_widget; +} + +QPoint Connection::endPointPos(EndPoint::Type type) const +{ + if (type == EndPoint::Source) + return m_source_pos; + else + return m_target_pos; +} + +static QPoint lineEntryPos(const QPoint &p1, const QPoint &p2, const QRect &rect) +{ + QPoint result; + + switch (classifyLine(p1, p2)) { + case CETypes::UpDir: + result = QPoint(p1.x(), rect.bottom()); + break; + case CETypes::DownDir: + result = QPoint(p1.x(), rect.top()); + break; + case CETypes::LeftDir: + result = QPoint(rect.right(), p1.y()); + break; + case CETypes::RightDir: + result = QPoint(rect.left(), p1.y()); + break; + } + + return result; +} + +static QPolygonF arrowHead(const QPoint &p1, const QPoint &p2) +{ + QPolygonF result; + + switch (classifyLine(p1, p2)) { + case CETypes::UpDir: + result.append(p2 + QPoint(0, 1)); + result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); + result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS*2 + 1)); + break; + case CETypes::DownDir: + result.append(p2); + result.append(p2 + QPoint(LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); + result.append(p2 + QPoint(-LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS*2)); + break; + case CETypes::LeftDir: + result.append(p2 + QPoint(1, 0)); + result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, -LINE_PROXIMITY_RADIUS)); + result.append(p2 + QPoint(2*LINE_PROXIMITY_RADIUS + 1, LINE_PROXIMITY_RADIUS)); + break; + case CETypes::RightDir: + result.append(p2); + result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, -LINE_PROXIMITY_RADIUS)); + result.append(p2 + QPoint(-2*LINE_PROXIMITY_RADIUS, LINE_PROXIMITY_RADIUS)); + break; + } + + return result; +} + +static CETypes::LineDir closestEdge(const QPoint &p, const QRect &r) +{ + CETypes::LineDir result = CETypes::UpDir; + int min = p.y() - r.top(); + + int d = p.x() - r.left(); + if (d < min) { + min = d; + result = CETypes::LeftDir; + } + + d = r.bottom() - p.y(); + if (d < min) { + min = d; + result = CETypes::DownDir; + } + + d = r.right() - p.x(); + if (d < min) { + min = d; + result = CETypes::RightDir; + } + + return result; +} + +static bool pointAboveLine(const QPoint &l1, const QPoint &l2, const QPoint &p) +{ + if (l1.x() == l2.x()) + return p.x() >= l1.x(); + return p.y() <= l1.y() + (p.x() - l1.x())*(l2.y() - l1.y())/(l2.x() - l1.x()); +} + +void Connection::updateKneeList() +{ + const LineDir old_source_label_dir = labelDir(EndPoint::Source); + const LineDir old_target_label_dir = labelDir(EndPoint::Target); + + QPoint s = endPointPos(EndPoint::Source); + QPoint t = endPointPos(EndPoint::Target); + const QRect sr = m_source_rect; + const QRect tr = m_target_rect; + + m_knee_list.clear(); + m_arrow_head.clear(); + + if (m_source == 0 || s == QPoint(-1, -1) || t == QPoint(-1, -1)) + return; + + const QRect r = sr | tr; + + m_knee_list.append(s); + if (m_target == 0) { + m_knee_list.append(QPoint(t.x(), s.y())); + } else if (m_target == m_edit->m_bg_widget) { + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (tr.contains(sr) || sr.contains(tr)) { +/* + +------------------+ + | +----------+ | + | | | | + | | o | | + | +---|------+ | + | | x | + +-----|-----|------+ + +-----+ + + We find out which edge of the outer rectangle is closest to the target + point, and make a loop which exits and re-enters through that edge. +*/ + const LineDir dir = closestEdge(t, tr); + switch (dir) { + case UpDir: + m_knee_list.append(QPoint(s.x(), r.top() - LOOP_MARGIN)); + m_knee_list.append(QPoint(t.x(), r.top() - LOOP_MARGIN)); + break; + case DownDir: + m_knee_list.append(QPoint(s.x(), r.bottom() + LOOP_MARGIN)); + m_knee_list.append(QPoint(t.x(), r.bottom() + LOOP_MARGIN)); + break; + case LeftDir: + m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, s.y())); + m_knee_list.append(QPoint(r.left() - LOOP_MARGIN, t.y())); + break; + case RightDir: + m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, s.y())); + m_knee_list.append(QPoint(r.right() + LOOP_MARGIN, t.y())); + break; + } + } else { + if (r.height() < sr.height() + tr.height()) { + if ((s.y() >= tr.top() && s.y() <= tr.bottom()) || (t.y() >= sr.bottom() || t.y() <= sr.top())) { +/* + +--------+ + | | +--------+ + | o--+---+--x | + | o | | | + +-----|--+ | | + +------+--x | + +--------+ + + When dragging one end point, move the other end point to the same y position, + if that does not cause it to exit it's rectangle. +*/ + if (m_edit->state() == ConnectionEdit::Dragging) { + if (m_edit->m_drag_end_point.type == EndPoint::Source) { + const QPoint p(t.x(), s.y()); + m_knee_list.append(p); + if (tr.contains(p)) + t = m_target_pos = p; + } else { + const QPoint p(s.x(), t.y()); + m_knee_list.append(p); + if (sr.contains(p)) + s = m_source_pos = p; + } + } else { + m_knee_list.append(QPoint(s.x(), t.y())); + } + } else { +/* + +--------+ + | o----+-------+ + | | +---|----+ + +--------+ | | | + | x | + +--------+ +*/ + m_knee_list.append(QPoint(t.x(), s.y())); + } + } else if (r.width() < sr.width() + tr.width()) { + if ((s.x() >= tr.left() && s.x() <= tr.right()) || t.x() >= sr.right() || t.x() <= sr.left()) { +/* + +--------+ + | | + | o o+--+ + +----|---+ | + +-|------|-+ + | x x | + | | + +----------+ + + When dragging one end point, move the other end point to the same x position, + if that does not cause it to exit it's rectangle. +*/ + if (m_edit->state() == ConnectionEdit::Dragging) { + if (m_edit->m_drag_end_point.type == EndPoint::Source) { + const QPoint p(s.x(), t.y()); + m_knee_list.append(p); + if (tr.contains(p)) + t = m_target_pos = p; + } else { + const QPoint p(t.x(), s.y()); + m_knee_list.append(p); + if (sr.contains(p)) + s = m_source_pos = p; + } + } else { + m_knee_list.append(QPoint(t.x(), s.y())); + } + } else { +/* + +--------+ + | | + | o | + +--|-----+ + | +--------+ + +---+-x | + | | + +--------+ + +*/ + m_knee_list.append(QPoint(s.x(), t.y())); + } + } else { +/* + +--------+ + | | + | o o-+--------+ + +--|-----+ | + | +-----|--+ + | | x | + +--------+-x | + +--------+ + + The line enters the target rectangle through the closest edge. +*/ + if (sr.topLeft() == r.topLeft()) { + if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t)) + m_knee_list.append(QPoint(t.x(), s.y())); + else + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (sr.topRight() == r.topRight()) { + if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t)) + m_knee_list.append(QPoint(t.x(), s.y())); + else + m_knee_list.append(QPoint(s.x(), t.y())); + } else if (sr.bottomRight() == r.bottomRight()) { + if (pointAboveLine(tr.topLeft(), tr.bottomRight(), t)) + m_knee_list.append(QPoint(s.x(), t.y())); + else + m_knee_list.append(QPoint(t.x(), s.y())); + } else { + if (pointAboveLine(tr.bottomLeft(), tr.topRight(), t)) + m_knee_list.append(QPoint(s.x(), t.y())); + else + m_knee_list.append(QPoint(t.x(), s.y())); + } + } + } + m_knee_list.append(t); + + if (m_knee_list.size() == 2) + m_knee_list.clear(); + + trimLine(); + + const LineDir new_source_label_dir = labelDir(EndPoint::Source); + const LineDir new_target_label_dir = labelDir(EndPoint::Target); + if (new_source_label_dir != old_source_label_dir) + updatePixmap(EndPoint::Source); + if (new_target_label_dir != old_target_label_dir) + updatePixmap(EndPoint::Target); +} + +void Connection::trimLine() +{ + if (m_source == 0 || m_source_pos == QPoint(-1, -1) || m_target_pos == QPoint(-1, -1)) + return; + int cnt = m_knee_list.size(); + if (cnt < 2) + return; + + const QRect sr = m_source_rect; + const QRect tr = m_target_rect; + + if (sr.contains(m_knee_list.at(1))) + m_knee_list.removeFirst(); + + cnt = m_knee_list.size(); + if (cnt < 2) + return; + + if (!tr.contains(sr) && tr.contains(m_knee_list.at(cnt - 2))) + m_knee_list.removeLast(); + + cnt = m_knee_list.size(); + if (cnt < 2) + return; + + if (sr.contains(m_knee_list.at(0)) && !sr.contains(m_knee_list.at(1))) + m_knee_list[0] = lineEntryPos(m_knee_list.at(1), m_knee_list.at(0), sr); + + if (tr.contains(m_knee_list.at(cnt - 1)) && !tr.contains(m_knee_list.at(cnt - 2))) { + m_knee_list[cnt - 1] + = lineEntryPos(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1), tr); + m_arrow_head = arrowHead(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1)); + } +} + +void Connection::setSource(QObject *source, const QPoint &pos) +{ + if (source == m_source && m_source_pos == pos) + return; + + update(false); + + m_source = source; + if (QWidget *widget = qobject_cast<QWidget*>(source)) { + m_source_pos = pos; + m_source_rect = m_edit->widgetRect(widget); + updateKneeList(); + } + + update(false); +} + +void Connection::setTarget(QObject *target, const QPoint &pos) +{ + if (target == m_target && m_target_pos == pos) + return; + + update(false); + + m_target = target; + if (QWidget *widget = qobject_cast<QWidget*>(target)) { + m_target_pos = pos; + m_target_rect = m_edit->widgetRect(widget); + updateKneeList(); + } + + update(false); +} + +static QRect lineRect(const QPoint &a, const QPoint &b) +{ + const QPoint c(qMin(a.x(), b.x()), qMin(a.y(), b.y())); + const QPoint d(qMax(a.x(), b.x()), qMax(a.y(), b.y())); + + QRect result(c, d); + return expand(result, LINE_PROXIMITY_RADIUS); +} + +QRect Connection::groundRect() const +{ + if (!ground()) + return QRect(); + if (m_knee_list.isEmpty()) + return QRect(); + + const QPoint p = m_knee_list.last(); + return QRect(p.x() - GROUND_W/2, p.y(), GROUND_W, GROUND_H); +} + +QRegion Connection::region() const +{ + QRegion result; + + for (int i = 0; i < m_knee_list.size() - 1; ++i) + result = result.unite(lineRect(m_knee_list.at(i), m_knee_list.at(i + 1))); + + if (!m_arrow_head.isEmpty()) { + QRect r = m_arrow_head.boundingRect().toRect(); + r = expand(r, 1); + result = result.unite(r); + } else if (ground()) { + result = result.unite(groundRect()); + } + + result = result.unite(labelRect(EndPoint::Source)); + result = result.unite(labelRect(EndPoint::Target)); + + return result; +} + +void Connection::update(bool update_widgets) const +{ + m_edit->update(region()); + if (update_widgets) { + if (m_source != 0) + m_edit->update(m_source_rect); + if (m_target != 0) + m_edit->update(m_target_rect); + } + + m_edit->update(endPointRect(EndPoint::Source)); + m_edit->update(endPointRect(EndPoint::Target)); +} + +void Connection::paint(QPainter *p) const +{ + for (int i = 0; i < m_knee_list.size() - 1; ++i) + p->drawLine(m_knee_list.at(i), m_knee_list.at(i + 1)); + + if (!m_arrow_head.isEmpty()) { + p->save(); + p->setBrush(p->pen().color()); + p->drawPolygon(m_arrow_head); + p->restore(); + } else if (ground()) { + paintGround(p, groundRect()); + } +} + +bool Connection::contains(const QPoint &pos) const +{ + return region().contains(pos); +} + +QRect Connection::endPointRect(EndPoint::Type type) const +{ + if (type == EndPoint::Source) { + if (m_source_pos != QPoint(-1, -1)) + return endPointRectHelper(m_source_pos); + } else { + if (m_target_pos != QPoint(-1, -1)) + return endPointRectHelper(m_target_pos); + } + return QRect(); +} + +CETypes::LineDir Connection::labelDir(EndPoint::Type type) const +{ + const int cnt = m_knee_list.size(); + if (cnt < 2) + return RightDir; + + LineDir dir; + if (type == EndPoint::Source) + dir = classifyLine(m_knee_list.at(0), m_knee_list.at(1)); + else + dir = classifyLine(m_knee_list.at(cnt - 2), m_knee_list.at(cnt - 1)); + + if (dir == LeftDir) + dir = RightDir; + if (dir == UpDir) + dir = DownDir; + + return dir; +} + +QRect Connection::labelRect(EndPoint::Type type) const +{ + const int cnt = m_knee_list.size(); + if (cnt < 2) + return QRect(); + const QString text = label(type); + if (text.isEmpty()) + return QRect(); + + const QSize size = labelPixmap(type).size(); + QPoint p1, p2; + if (type == EndPoint::Source) { + p1 = m_knee_list.at(0); + p2 = m_knee_list.at(1); + } else { + p1 = m_knee_list.at(cnt - 1); + p2 = m_knee_list.at(cnt - 2); + } + const LineDir dir = classifyLine(p1, p2); + + QRect result; + switch (dir) { + case UpDir: + result = QRect(p1 + QPoint(-size.width()/2, 0), size); + break; + case DownDir: + result = QRect(p1 + QPoint(-size.width()/2, -size.height()), size); + break; + case LeftDir: + result = QRect(p1 + QPoint(0, -size.height()/2), size); + break; + case RightDir: + result = QRect(p1 + QPoint(-size.width(), -size.height()/2), size); + break; + } + + return result; +} + +void Connection::setLabel(EndPoint::Type type, const QString &text) +{ + if (text == label(type)) + return; + + if (type == EndPoint::Source) + m_source_label = text; + else + m_target_label = text; + + updatePixmap(type); +} + +void Connection::updatePixmap(EndPoint::Type type) +{ + QPixmap *pm = type == EndPoint::Source ? &m_source_label_pm : &m_target_label_pm; + + const QString text = label(type); + if (text.isEmpty()) { + *pm = QPixmap(); + return; + } + + const QFontMetrics fm = m_edit->fontMetrics(); + const QSize size = fm.size(Qt::TextSingleLine, text) + QSize(HLABEL_MARGIN*2, VLABEL_MARGIN*2); + *pm = QPixmap(size); + QColor color = m_edit->palette().color(QPalette::Normal, QPalette::Base); + color.setAlpha(190); + pm->fill(color); + + QPainter p(pm); + p.setPen(m_edit->palette().color(QPalette::Normal, QPalette::Text)); + p.drawText(-fm.leftBearing(text.at(0)) + HLABEL_MARGIN, fm.ascent() + VLABEL_MARGIN, text); + p.end(); + + const LineDir dir = labelDir(type); + + if (dir == DownDir) + *pm = pm->transformed(QMatrix(0.0, -1.0, 1.0, 0.0, 0.0, 0.0)); +} + +void Connection::checkWidgets() +{ + bool changed = false; + + if (QWidget *sourceWidget = qobject_cast<QWidget*>(m_source)) { + const QRect r = m_edit->widgetRect(sourceWidget); + if (r != m_source_rect) { + if (m_source_pos != QPoint(-1, -1) && !r.contains(m_source_pos)) { + QPoint offset = m_source_pos - m_source_rect.topLeft(); + QPoint old_pos = m_source_pos; + m_source_pos = pointInsideRect(r, r.topLeft() + offset); + } + m_edit->update(m_source_rect); + m_source_rect = r; + changed = true; + } + } + + if (QWidget *targetWidget = qobject_cast<QWidget*>(m_target)) { + const QRect r = m_edit->widgetRect(targetWidget); + if (r != m_target_rect) { + if (m_target_pos != QPoint(-1, -1) && !r.contains(m_target_pos)) { + const QPoint offset = m_target_pos - m_target_rect.topLeft(); + const QPoint old_pos = m_target_pos; + m_target_pos = pointInsideRect(r, r.topLeft() + offset); + } + m_edit->update(m_target_rect); + m_target_rect = r; + changed = true; + } + } + + if (changed) { + update(); + updateKneeList(); + update(); + } +} + +/******************************************************************************* +** ConnectionEdit +*/ + +ConnectionEdit::ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form) : + QWidget(parent), + m_bg_widget(0), + m_undo_stack(form->commandHistory()), + m_enable_update_background(false), + m_tmp_con(0), + m_start_connection_on_drag(true), + m_widget_under_mouse(0), + m_inactive_color(Qt::blue), + m_active_color(Qt::red) +{ + setAttribute(Qt::WA_MouseTracking, true); + setFocusPolicy(Qt::ClickFocus); + + connect(form, SIGNAL(widgetRemoved(QWidget*)), this, SLOT(widgetRemoved(QWidget*))); + connect(form, SIGNAL(objectRemoved(QObject*)), this, SLOT(objectRemoved(QObject*))); +} + +ConnectionEdit::~ConnectionEdit() +{ + qDeleteAll(m_con_list); +} + +void ConnectionEdit::clear() +{ + m_con_list.clear(); + m_sel_con_set.clear(); + m_bg_widget = 0; + m_widget_under_mouse = 0; + m_tmp_con = 0; +} + +void ConnectionEdit::setBackground(QWidget *background) +{ + if (background == m_bg_widget) { + // nothing to do + return; + } + + m_bg_widget = background; + updateBackground(); +} + +void ConnectionEdit::enableUpdateBackground(bool enable) +{ + m_enable_update_background = enable; + if (enable) + updateBackground(); +} + +void ConnectionEdit::updateBackground() +{ + // Might happen while reloading a form. + if (m_bg_widget == 0) + return; + + if (!m_enable_update_background) + return; + + foreach(Connection *c, m_con_list) + c->updateVisibility(); + + updateLines(); + update(); +} + +QWidget *ConnectionEdit::widgetAt(const QPoint &pos) const +{ + if (m_bg_widget == 0) + return 0; + QWidget *widget = m_bg_widget->childAt(pos); + if (widget == 0) + widget = m_bg_widget; + + return widget; +} + + +QRect ConnectionEdit::widgetRect(QWidget *w) const +{ + if (w == 0) + return QRect(); + QRect r = w->geometry(); + QPoint pos = w->mapToGlobal(QPoint(0, 0)); + pos = mapFromGlobal(pos); + r.moveTopLeft(pos); + return r; +} + +ConnectionEdit::State ConnectionEdit::state() const +{ + if (m_tmp_con != 0) + return Connecting; + if (!m_drag_end_point.isNull()) + return Dragging; + return Editing; +} + +void ConnectionEdit::paintLabel(QPainter *p, EndPoint::Type type, Connection *con) +{ + if (con->label(type).isEmpty()) + return; + + const bool heavy = selected(con) || con == m_tmp_con; + p->setPen(heavy ? m_active_color : m_inactive_color); + p->setBrush(Qt::NoBrush); + const QRect r = con->labelRect(type); + p->drawPixmap(r.topLeft(), con->labelPixmap(type)); + p->drawRect(fixRect(r)); +} + +void ConnectionEdit::paintConnection(QPainter *p, Connection *con, + WidgetSet *heavy_highlight_set, + WidgetSet *light_highlight_set) const +{ + QWidget *source = con->widget(EndPoint::Source); + QWidget *target = con->widget(EndPoint::Target); + + const bool heavy = selected(con) || con == m_tmp_con; + WidgetSet *set = heavy ? heavy_highlight_set : light_highlight_set; + p->setPen(heavy ? m_active_color : m_inactive_color); + con->paint(p); + + if (source != 0 && source != m_bg_widget) + set->insert(source, source); + + if (target != 0 && target != m_bg_widget) + set->insert(target, target); +} + +void ConnectionEdit::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + p.setClipRegion(e->region()); + + WidgetSet heavy_highlight_set, light_highlight_set; + + foreach (Connection *con, m_con_list) { + if (!con->isVisible()) + continue; + + paintConnection(&p, con, &heavy_highlight_set, &light_highlight_set); + } + + if (m_tmp_con != 0) + paintConnection(&p, m_tmp_con, &heavy_highlight_set, &light_highlight_set); + + if (!m_widget_under_mouse.isNull() && m_widget_under_mouse != m_bg_widget) + heavy_highlight_set.insert(m_widget_under_mouse, m_widget_under_mouse); + + QColor c = m_active_color; + p.setPen(c); + c.setAlpha(BG_ALPHA); + p.setBrush(c); + + foreach (QWidget *w, heavy_highlight_set) { + p.drawRect(fixRect(widgetRect(w))); + light_highlight_set.remove(w); + } + + c = m_inactive_color; + p.setPen(c); + c.setAlpha(BG_ALPHA); + p.setBrush(c); + + foreach (QWidget *w, light_highlight_set) + p.drawRect(fixRect(widgetRect(w))); + + p.setBrush(palette().color(QPalette::Base)); + p.setPen(palette().color(QPalette::Text)); + foreach (Connection *con, m_con_list) { + if (!con->isVisible()) + continue; + + paintLabel(&p, EndPoint::Source, con); + paintLabel(&p, EndPoint::Target, con); + } + + p.setPen(m_active_color); + p.setBrush(m_active_color); + + foreach (Connection *con, m_con_list) { + if (!selected(con) || !con->isVisible()) + continue; + + paintEndPoint(&p, con->endPointPos(EndPoint::Source)); + + if (con->widget(EndPoint::Target) != 0) + paintEndPoint(&p, con->endPointPos(EndPoint::Target)); + } +} + +void ConnectionEdit::abortConnection() +{ + m_tmp_con->update(); + delete m_tmp_con; + m_tmp_con = 0; +#ifndef QT_NO_CURSOR + setCursor(QCursor()); +#endif + if (m_widget_under_mouse == m_bg_widget) + m_widget_under_mouse = 0; +} + +void ConnectionEdit::mousePressEvent(QMouseEvent *e) +{ + // Right click only to cancel + const Qt::MouseButton button = e->button(); + const State cstate = state(); + if (button != Qt::LeftButton && !(button == Qt::RightButton && cstate == Connecting)) { + QWidget::mousePressEvent(e); + return; + } + + e->accept(); + // Prefer a non-background widget over the connection, + // otherwise, widgets covered by the connection labels cannot be accessed + Connection *con_under_mouse = 0; + if (!m_widget_under_mouse || m_widget_under_mouse == m_bg_widget) + con_under_mouse = connectionAt(e->pos()); + + m_start_connection_on_drag = false; + switch (cstate) { + case Connecting: + if (button == Qt::RightButton) + abortConnection(); + break; + case Dragging: + break; + case Editing: + if (!m_end_point_under_mouse.isNull()) { + if (!(e->modifiers() & Qt::ShiftModifier)) { + startDrag(m_end_point_under_mouse, e->pos()); + } + } else if (con_under_mouse != 0) { + if (!(e->modifiers() & Qt::ShiftModifier)) { + selectNone(); + setSelected(con_under_mouse, true); + } else { + setSelected(con_under_mouse, !selected(con_under_mouse)); + } + } else { + if (!(e->modifiers() & Qt::ShiftModifier)) { + selectNone(); + if (!m_widget_under_mouse.isNull()) + m_start_connection_on_drag = true; + } + } + break; + } +} + +void ConnectionEdit::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QWidget::mouseDoubleClickEvent(e); + return; + } + + e->accept(); + switch (state()) { + case Connecting: + abortConnection(); + break; + case Dragging: + break; + case Editing: + if (!m_widget_under_mouse.isNull()) { + emit widgetActivated(m_widget_under_mouse); + } else if (m_sel_con_set.size() == 1) { + Connection *con = m_sel_con_set.keys().first(); + modifyConnection(con); + } + break; + } + +} + +void ConnectionEdit::mouseReleaseEvent(QMouseEvent *e) +{ + if (e->button() != Qt::LeftButton) { + QWidget::mouseReleaseEvent(e); + return; + } + e->accept(); + + switch (state()) { + case Connecting: + if (m_widget_under_mouse.isNull()) + abortConnection(); + else + endConnection(m_widget_under_mouse, e->pos()); +#ifndef QT_NO_CURSOR + setCursor(QCursor()); +#endif + break; + case Editing: + break; + case Dragging: + endDrag(e->pos()); + break; + } +} + + +void ConnectionEdit::findObjectsUnderMouse(const QPoint &pos) +{ + Connection *con_under_mouse = connectionAt(pos); + + QWidget *w = widgetAt(pos); + // Prefer a non-background widget over the connection, + // otherwise, widgets covered by the connection labels cannot be accessed + if (w == m_bg_widget && con_under_mouse) + w = 0; + else + con_under_mouse = 0; + + if (w != m_widget_under_mouse) { + if (!m_widget_under_mouse.isNull()) + update(widgetRect(m_widget_under_mouse)); + m_widget_under_mouse = w; + if (!m_widget_under_mouse.isNull()) + update(widgetRect(m_widget_under_mouse)); + } + + const EndPoint hs = endPointAt(pos); + if (hs != m_end_point_under_mouse) { +#ifndef QT_NO_CURSOR + if (m_end_point_under_mouse.isNull()) + setCursor(Qt::PointingHandCursor); + else + setCursor(QCursor()); +#endif + m_end_point_under_mouse = hs; + } +} + +void ConnectionEdit::mouseMoveEvent(QMouseEvent *e) +{ + findObjectsUnderMouse(e->pos()); + switch (state()) { + case Connecting: + continueConnection(m_widget_under_mouse, e->pos()); + break; + case Editing: + if ((e->buttons() & Qt::LeftButton) + && m_start_connection_on_drag + && !m_widget_under_mouse.isNull()) { + m_start_connection_on_drag = false; + startConnection(m_widget_under_mouse, e->pos()); +#ifndef QT_NO_CURSOR + setCursor(Qt::CrossCursor); +#endif + } + break; + case Dragging: + continueDrag(e->pos()); + break; + } + + e->accept(); +} + +void ConnectionEdit::keyPressEvent(QKeyEvent *e) +{ + switch (e->key()) { + case Qt::Key_Delete: + if (state() == Editing) + deleteSelected(); + break; + case Qt::Key_Escape: + if (state() == Connecting) + abortConnection(); + break; + } + + e->accept(); +} + +void ConnectionEdit::startConnection(QWidget *source, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con == 0); + + m_tmp_con = new Connection(this); + m_tmp_con->setEndPoint(EndPoint::Source, source, pos); +} + +void ConnectionEdit::endConnection(QWidget *target, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con != 0); + + m_tmp_con->setEndPoint(EndPoint::Target, target, pos); + + QWidget *source = m_tmp_con->widget(EndPoint::Source); + Q_ASSERT(source != 0); + Q_ASSERT(target != 0); + setEnabled(false); + Connection *new_con = createConnection(source, target); + setEnabled(true); + if (new_con != 0) { + new_con->setEndPoint(EndPoint::Source, source, m_tmp_con->endPointPos(EndPoint::Source)); + new_con->setEndPoint(EndPoint::Target, target, m_tmp_con->endPointPos(EndPoint::Target)); + m_undo_stack->push(new AddConnectionCommand(this, new_con)); + emit connectionChanged(new_con); + } + + delete m_tmp_con; + m_tmp_con = 0; + + findObjectsUnderMouse(mapFromGlobal(QCursor::pos())); +} + +void ConnectionEdit::continueConnection(QWidget *target, const QPoint &pos) +{ + Q_ASSERT(m_tmp_con != 0); + + m_tmp_con->setEndPoint(EndPoint::Target, target, pos); +} + +void ConnectionEdit::modifyConnection(Connection *) +{ +} + +Connection *ConnectionEdit::createConnection(QWidget *source, QWidget *target) +{ + Connection *con = new Connection(this, source, target); + return con; +} + +// Find all connections which in which a sequence of objects is involved +template <class ObjectIterator> +static ConnectionEdit::ConnectionSet findConnectionsOf(const ConnectionEdit::ConnectionList &cl, ObjectIterator oi1, const ObjectIterator &oi2) +{ + ConnectionEdit::ConnectionSet rc; + + const ConnectionEdit::ConnectionList::const_iterator ccend = cl.constEnd(); + for ( ; oi1 != oi2; ++oi1) { + for (ConnectionEdit::ConnectionList::const_iterator cit = cl.constBegin(); cit != ccend; ++cit) { + Connection *con = *cit; + if (con->object(ConnectionEdit::EndPoint::Source) == *oi1 || con->object(ConnectionEdit::EndPoint::Target) == *oi1) + rc.insert(con, con); + } + } + return rc; +} + +void ConnectionEdit::widgetRemoved(QWidget *widget) +{ + // Remove all connections of that widget and its children. + if (m_con_list.empty()) + return; + + QWidgetList child_list = qFindChildren<QWidget*>(widget); + child_list.prepend(widget); + + const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd()); + + if (!remove_set.isEmpty()) + m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys())); + + updateBackground(); +} + +void ConnectionEdit::objectRemoved(QObject *o) +{ + // Remove all connections of that object and its children (in case of action groups). + if (m_con_list.empty()) + return; + + QObjectList child_list = o->children(); + child_list.prepend(o); + const ConnectionSet remove_set = findConnectionsOf(m_con_list, child_list.constBegin(), child_list.constEnd()); + if (!remove_set.isEmpty()) + m_undo_stack->push(new DeleteConnectionsCommand(this, remove_set.keys())); + + updateBackground(); +} + +void ConnectionEdit::setSelected(Connection *con, bool sel) +{ + if (!con || sel == m_sel_con_set.contains(con)) + return; + + if (sel) { + m_sel_con_set.insert(con, con); + emit connectionSelected(con); + } else { + m_sel_con_set.remove(con); + } + + con->update(); +} + +bool ConnectionEdit::selected(const Connection *con) const +{ + return m_sel_con_set.contains(const_cast<Connection*>(con)); +} + +void ConnectionEdit::selectNone() +{ + foreach (Connection *con, m_sel_con_set) + con->update(); + + m_sel_con_set.clear(); +} + +void ConnectionEdit::selectAll() +{ + if (m_sel_con_set.size() == m_con_list.size()) + return; + foreach (Connection *con, m_con_list) + setSelected(con, true); +} + +Connection *ConnectionEdit::connectionAt(const QPoint &pos) const +{ + foreach (Connection *con, m_con_list) { + if (con->contains(pos)) + return con; + } + return 0; +} + +CETypes::EndPoint ConnectionEdit::endPointAt(const QPoint &pos) const +{ + foreach (Connection *con, m_con_list) { + if (!selected(con)) + continue; + const QRect sr = con->endPointRect(EndPoint::Source); + const QRect tr = con->endPointRect(EndPoint::Target); + + if (sr.contains(pos)) + return EndPoint(con, EndPoint::Source); + if (tr.contains(pos)) + return EndPoint(con, EndPoint::Target); + } + return EndPoint(); +} + +void ConnectionEdit::startDrag(const EndPoint &end_point, const QPoint &pos) +{ + Q_ASSERT(m_drag_end_point.isNull()); + m_drag_end_point = end_point; + m_old_source_pos = m_drag_end_point.con->endPointPos(EndPoint::Source); + m_old_target_pos = m_drag_end_point.con->endPointPos(EndPoint::Target); + adjustHotSopt(m_drag_end_point, pos); +} + +void ConnectionEdit::continueDrag(const QPoint &pos) +{ + Q_ASSERT(!m_drag_end_point.isNull()); + adjustHotSopt(m_drag_end_point, pos); +} + +void ConnectionEdit::endDrag(const QPoint &pos) +{ + Q_ASSERT(!m_drag_end_point.isNull()); + adjustHotSopt(m_drag_end_point, pos); + + Connection *con = m_drag_end_point.con; + const QPoint new_source_pos = con->endPointPos(EndPoint::Source); + const QPoint new_target_pos = con->endPointPos(EndPoint::Target); + m_undo_stack->push(new AdjustConnectionCommand(this, con, m_old_source_pos, m_old_target_pos, + new_source_pos, new_target_pos)); + + m_drag_end_point = EndPoint(); +} + +void ConnectionEdit::adjustHotSopt(const EndPoint &end_point, const QPoint &pos) +{ + QWidget *w = end_point.con->widget(end_point.type); + end_point.con->setEndPoint(end_point.type, w, pointInsideRect(widgetRect(w), pos)); +} + +void ConnectionEdit::deleteSelected() +{ + if (m_sel_con_set.isEmpty()) + return; + m_undo_stack->push(new DeleteConnectionsCommand(this, m_sel_con_set.keys())); +} + +void ConnectionEdit::addConnection(Connection *con) +{ + m_con_list.append(con); +} + +void ConnectionEdit::updateLines() +{ + foreach (Connection *con, m_con_list) + con->checkWidgets(); +} + +void ConnectionEdit::resizeEvent(QResizeEvent *e) +{ + updateBackground(); + QWidget::resizeEvent(e); +} + +void ConnectionEdit::setSource(Connection *con, const QString &obj_name) +{ + QObject *object = 0; + if (!obj_name.isEmpty()) { + object = qFindChild<QObject*>(m_bg_widget, obj_name); + if (object == 0 && m_bg_widget->objectName() == obj_name) + object = m_bg_widget; + + if (object == con->object(EndPoint::Source)) + return; + } + m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Source, object)); +} + +void ConnectionEdit::setTarget(Connection *con, const QString &obj_name) +{ + QObject *object = 0; + if (!obj_name.isEmpty()) { + object = qFindChild<QObject*>(m_bg_widget, obj_name); + if (object == 0 && m_bg_widget->objectName() == obj_name) + object = m_bg_widget; + + if (object == con->object(EndPoint::Target)) + return; + } + m_undo_stack->push(new SetEndPointCommand(this, con, EndPoint::Target, object)); +} + +Connection *ConnectionEdit::takeConnection(Connection *con) +{ + if (!m_con_list.contains(con)) + return 0; + m_con_list.removeAll(con); + return con; +} + +void ConnectionEdit::clearNewlyAddedConnection() +{ + delete m_tmp_con; + m_tmp_con = 0; +} + +void ConnectionEdit::createContextMenu(QMenu &menu) +{ + // Select + QAction *selectAllAction = menu.addAction(tr("Select All")); + selectAllAction->setEnabled(connectionList().size()); + connect(selectAllAction, SIGNAL(triggered()), this, SLOT(selectAll())); + QAction *deselectAllAction = menu.addAction(tr("Deselect All")); + deselectAllAction->setEnabled(selection().size()); + connect(deselectAllAction, SIGNAL(triggered()), this, SLOT(selectNone())); + menu.addSeparator(); + // Delete + QAction *deleteAction = menu.addAction(tr("Delete")); + deleteAction->setShortcut(QKeySequence::Delete); + deleteAction->setEnabled(!selection().isEmpty()); + connect(deleteAction, SIGNAL(triggered()), this, SLOT(deleteSelected())); +} + +void ConnectionEdit::contextMenuEvent(QContextMenuEvent * event) +{ + QMenu menu; + createContextMenu(menu); + menu.exec(event->globalPos()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/connectionedit_p.h b/tools/designer/src/lib/shared/connectionedit_p.h new file mode 100644 index 0000000..c4f735a --- /dev/null +++ b/tools/designer/src/lib/shared/connectionedit_p.h @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef CONNECTIONEDIT_H +#define CONNECTIONEDIT_H + +#include "shared_global_p.h" + +#include <QtCore/QMultiMap> +#include <QtCore/QList> +#include <QtCore/QPointer> + +#include <QtGui/QWidget> +#include <QtGui/QPixmap> +#include <QtGui/QPolygonF> + +#include <QtGui/QUndoCommand> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QUndoStack; +class QMenu; + +namespace qdesigner_internal { + +class Connection; +class ConnectionEdit; + +class QDESIGNER_SHARED_EXPORT CETypes +{ +public: + typedef QList<Connection*> ConnectionList; + typedef QMap<Connection*, Connection*> ConnectionSet; + typedef QMap<QWidget*, QWidget*> WidgetSet; + + class EndPoint { + public: + enum Type { Source, Target }; + EndPoint(Connection *_con = 0, Type _type = Source) : con(_con), type(_type) {} + bool isNull() const { return con == 0; } + bool operator == (const EndPoint &other) const { return con == other.con && type == other.type; } + bool operator != (const EndPoint &other) const { return !operator == (other); } + Connection *con; + Type type; + }; + enum LineDir { UpDir = 0, DownDir, RightDir, LeftDir }; +}; + +class QDESIGNER_SHARED_EXPORT Connection : public CETypes +{ +public: + explicit Connection(ConnectionEdit *edit); + explicit Connection(ConnectionEdit *edit, QObject *source, QObject *target); + virtual ~Connection() {} + + QObject *object(EndPoint::Type type) const + { + return (type == EndPoint::Source ? m_source : m_target); + } + + QWidget *widget(EndPoint::Type type) const + { + return qobject_cast<QWidget*>(object(type)); + } + + QPoint endPointPos(EndPoint::Type type) const; + QRect endPointRect(EndPoint::Type) const; + void setEndPoint(EndPoint::Type type, QObject *w, const QPoint &pos) + { type == EndPoint::Source ? setSource(w, pos) : setTarget(w, pos); } + + bool isVisible() const; + virtual void updateVisibility(); + void setVisible(bool b); + + virtual QRegion region() const; + bool contains(const QPoint &pos) const; + virtual void paint(QPainter *p) const; + + void update(bool update_widgets = true) const; + void checkWidgets(); + + QString label(EndPoint::Type type) const + { return type == EndPoint::Source ? m_source_label : m_target_label; } + void setLabel(EndPoint::Type type, const QString &text); + QRect labelRect(EndPoint::Type type) const; + QPixmap labelPixmap(EndPoint::Type type) const + { return type == EndPoint::Source ? m_source_label_pm : m_target_label_pm; } + + ConnectionEdit *edit() const { return m_edit; } + + virtual void inserted() {} + virtual void removed() {} + +private: + QPoint m_source_pos, m_target_pos; + QObject *m_source, *m_target; + QList<QPoint> m_knee_list; + QPolygonF m_arrow_head; + ConnectionEdit *m_edit; + QString m_source_label, m_target_label; + QPixmap m_source_label_pm, m_target_label_pm; + QRect m_source_rect, m_target_rect; + bool m_visible; + + void setSource(QObject *source, const QPoint &pos); + void setTarget(QObject *target, const QPoint &pos); + void updateKneeList(); + void trimLine(); + void updatePixmap(EndPoint::Type type); + LineDir labelDir(EndPoint::Type type) const; + bool ground() const; + QRect groundRect() const; +}; + +class QDESIGNER_SHARED_EXPORT ConnectionEdit : public QWidget, public CETypes +{ + Q_OBJECT +public: + ConnectionEdit(QWidget *parent, QDesignerFormWindowInterface *form); + virtual ~ConnectionEdit(); + + inline const QPointer<QWidget> &background() const { return m_bg_widget; } + + void setSelected(Connection *con, bool sel); + bool selected(const Connection *con) const; + + int connectionCount() const { return m_con_list.size(); } + Connection *connection(int i) const { return m_con_list.at(i); } + int indexOfConnection(Connection *con) const { return m_con_list.indexOf(con); } + + virtual void setSource(Connection *con, const QString &obj_name); + virtual void setTarget(Connection *con, const QString &obj_name); + + QUndoStack *undoStack() const { return m_undo_stack; } + + void clear(); + + void showEvent(QShowEvent * /*e*/) + { + updateBackground(); + } + +signals: + void aboutToAddConnection(int idx); + void connectionAdded(Connection *con); + void aboutToRemoveConnection(Connection *con); + void connectionRemoved(int idx); + void connectionSelected(Connection *con); + void widgetActivated(QWidget *wgt); + void connectionChanged(Connection *con); + +public slots: + void selectNone(); + void selectAll(); + virtual void deleteSelected(); + virtual void setBackground(QWidget *background); + virtual void updateBackground(); + virtual void widgetRemoved(QWidget *w); + virtual void objectRemoved(QObject *o); + + void updateLines(); + void enableUpdateBackground(bool enable); + +protected: + virtual void paintEvent(QPaintEvent *e); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + virtual void contextMenuEvent(QContextMenuEvent * event); + + virtual Connection *createConnection(QWidget *source, QWidget *target); + virtual void modifyConnection(Connection *con); + + virtual QWidget *widgetAt(const QPoint &pos) const; + virtual void createContextMenu(QMenu &menu); + void addConnection(Connection *con); + QRect widgetRect(QWidget *w) const; + + enum State { Editing, Connecting, Dragging }; + State state() const; + + virtual void endConnection(QWidget *target, const QPoint &pos); + + const ConnectionList &connectionList() const { return m_con_list; } + const ConnectionSet &selection() const { return m_sel_con_set; } + Connection *takeConnection(Connection *con); + Connection *newlyAddedConnection() { return m_tmp_con; } + void clearNewlyAddedConnection(); + + void findObjectsUnderMouse(const QPoint &pos); + +private: + void startConnection(QWidget *source, const QPoint &pos); + void continueConnection(QWidget *target, const QPoint &pos); + void abortConnection(); + + void startDrag(const EndPoint &end_point, const QPoint &pos); + void continueDrag(const QPoint &pos); + void endDrag(const QPoint &pos); + void adjustHotSopt(const EndPoint &end_point, const QPoint &pos); + Connection *connectionAt(const QPoint &pos) const; + EndPoint endPointAt(const QPoint &pos) const; + void paintConnection(QPainter *p, Connection *con, + WidgetSet *heavy_highlight_set, + WidgetSet *light_highlight_set) const; + void paintLabel(QPainter *p, EndPoint::Type type, Connection *con); + + + QPointer<QWidget> m_bg_widget; + QUndoStack *m_undo_stack; + bool m_enable_update_background; + + Connection *m_tmp_con; // the connection we are currently editing + ConnectionList m_con_list; + bool m_start_connection_on_drag; + EndPoint m_end_point_under_mouse; + QPointer<QWidget> m_widget_under_mouse; + + EndPoint m_drag_end_point; + QPoint m_old_source_pos, m_old_target_pos; + ConnectionSet m_sel_con_set; + const QColor m_inactive_color; + const QColor m_active_color; + +private: + friend class Connection; + friend class AddConnectionCommand; + friend class DeleteConnectionsCommand; + friend class SetEndPointCommand; +}; + +class QDESIGNER_SHARED_EXPORT CECommand : public QUndoCommand, public CETypes +{ +public: + explicit CECommand(ConnectionEdit *edit) + : m_edit(edit) {} + + virtual bool mergeWith(const QUndoCommand *) { return false; } + + ConnectionEdit *edit() const { return m_edit; } + +private: + ConnectionEdit *m_edit; +}; + +class QDESIGNER_SHARED_EXPORT AddConnectionCommand : public CECommand +{ +public: + AddConnectionCommand(ConnectionEdit *edit, Connection *con); + virtual void redo(); + virtual void undo(); +private: + Connection *m_con; +}; + +class QDESIGNER_SHARED_EXPORT DeleteConnectionsCommand : public CECommand +{ +public: + DeleteConnectionsCommand(ConnectionEdit *edit, const ConnectionList &con_list); + virtual void redo(); + virtual void undo(); +private: + ConnectionList m_con_list; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CONNECTIONEDIT_H diff --git a/tools/designer/src/lib/shared/csshighlighter.cpp b/tools/designer/src/lib/shared/csshighlighter.cpp new file mode 100644 index 0000000..d5e045b --- /dev/null +++ b/tools/designer/src/lib/shared/csshighlighter.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::StyleSheetEditorDialog +*/ + +#include "csshighlighter_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +CssHighlighter::CssHighlighter(QTextDocument *document) +: QSyntaxHighlighter(document) +{ +} + +void CssHighlighter::highlightBlock(const QString& text) +{ + enum Token { ALNUM, LBRACE, RBRACE, COLON, SEMICOLON, COMMA, QUOTE, SLASH, STAR }; + static const int transitions[10][9] = { + { Selector, Property, Selector, Pseudo, Property, Selector, Quote, MaybeComment, Selector }, // Selector + { Property, Property, Selector, Value, Property, Property, Quote, MaybeComment, Property }, // Property + { Value, Property, Selector, Value, Property, Value, Quote, MaybeComment, Value }, // Value + { Pseudo1, Property, Selector, Pseudo2, Selector, Selector, Quote, MaybeComment, Pseudo }, // Pseudo + { Pseudo1, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo1 }, // Pseudo1 + { Pseudo2, Property, Selector, Pseudo, Selector, Selector, Quote, MaybeComment, Pseudo2 }, // Pseudo2 + { Quote, Quote, Quote, Quote, Quote, Quote, -1, Quote, Quote }, // Quote + { -1, -1, -1, -1, -1, -1, -1, -1, Comment }, // MaybeComment + { Comment, Comment, Comment, Comment, Comment, Comment, Comment, Comment, MaybeCommentEnd }, // Comment + { Comment, Comment, Comment, Comment, Comment, Comment, Comment, -1, MaybeCommentEnd } // MaybeCommentEnd + }; + + int lastIndex = 0; + bool lastWasSlash = false; + int state = previousBlockState(), save_state; + if (state == -1) { + // As long as the text is empty, leave the state undetermined + if (text.isEmpty()) { + setCurrentBlockState(-1); + return; + } + // The initial state is based on the precense of a : and the absense of a {. + // This is because Qt style sheets support both a full stylesheet as well as + // an inline form with just properties. + state = save_state = (text.indexOf(QLatin1Char(':')) > -1 && + text.indexOf(QLatin1Char('{')) == -1) ? Property : Selector; + } else { + save_state = state>>16; + state &= 0x00ff; + } + + if (state == MaybeCommentEnd) { + state = Comment; + } else if (state == MaybeComment) { + state = save_state; + } + + for (int i = 0; i < text.length(); i++) { + int token = ALNUM; + const QChar c = text.at(i); + const char a = c.toAscii(); + + if (state == Quote) { + if (a == '\\') { + lastWasSlash = true; + } else { + if (a == '\"' && !lastWasSlash) { + token = QUOTE; + } + lastWasSlash = false; + } + } else { + switch (a) { + case '{': token = LBRACE; break; + case '}': token = RBRACE; break; + case ':': token = COLON; break; + case ';': token = SEMICOLON; break; + case ',': token = COMMA; break; + case '\"': token = QUOTE; break; + case '/': token = SLASH; break; + case '*': token = STAR; break; + default: break; + } + } + + int new_state = transitions[state][token]; + + if (new_state != state) { + bool include_token = new_state == MaybeCommentEnd || (state == MaybeCommentEnd && new_state!= Comment) + || state == Quote; + highlight(text, lastIndex, i-lastIndex+include_token, state); + + if (new_state == Comment) { + lastIndex = i-1; // include the slash and star + } else { + lastIndex = i + ((token == ALNUM || new_state == Quote) ? 0 : 1); + } + } + + if (new_state == -1) { + state = save_state; + } else if (state <= Pseudo2) { + save_state = state; + state = new_state; + } else { + state = new_state; + } + } + + highlight(text, lastIndex, text.length() - lastIndex, state); + setCurrentBlockState(state + (save_state<<16)); +} + +void CssHighlighter::highlight(const QString &text, int start, int length, int state) +{ + if (start >= text.length() || length <= 0) + return; + + QTextCharFormat format; + + switch (state) { + case Selector: + setFormat(start, length, Qt::darkRed); + break; + case Property: + setFormat(start, length, Qt::blue); + break; + case Value: + setFormat(start, length, Qt::black); + break; + case Pseudo1: + setFormat(start, length, Qt::darkRed); + break; + case Pseudo2: + setFormat(start, length, Qt::darkRed); + break; + case Quote: + setFormat(start, length, Qt::darkMagenta); + break; + case Comment: + case MaybeCommentEnd: + format.setForeground(Qt::darkGreen); + setFormat(start, length, format); + break; + default: + break; + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/csshighlighter_p.h b/tools/designer/src/lib/shared/csshighlighter_p.h new file mode 100644 index 0000000..fa671ea --- /dev/null +++ b/tools/designer/src/lib/shared/csshighlighter_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef CSSHIGHLIGHTER_H +#define CSSHIGHLIGHTER_H + +#include <QtGui/QSyntaxHighlighter> +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT CssHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT +public: + explicit CssHighlighter(QTextDocument *document); + +protected: + void highlightBlock(const QString&); + void highlight(const QString&, int, int, int/*State*/); + +private: + enum State { Selector, Property, Value, Pseudo, Pseudo1, Pseudo2, Quote, + MaybeComment, Comment, MaybeCommentEnd }; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // CSSHIGHLIGHTER_H diff --git a/tools/designer/src/lib/shared/defaultgradients.xml b/tools/designer/src/lib/shared/defaultgradients.xml new file mode 100644 index 0000000..70559ad --- /dev/null +++ b/tools/designer/src/lib/shared/defaultgradients.xml @@ -0,0 +1,498 @@ +<gradients> + <gradient name="BlackWhite" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Czech" > + <gradientData centerX="0.5" centerY="0.5" spread="RepeatSpread" coordinateMode="StretchToDeviceMode" type="ConicalGradient" angle="0" > + <stopData position="0" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.373978669201521" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.3739913434727503" > + <colorData g="30" r="33" a="255" b="255" /> + </stopData> + <stopData position="0.6240176679340937" > + <colorData g="30" r="33" a="255" b="255" /> + </stopData> + <stopData position="0.6240430164765525" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Dutch" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="0" endY="1" > + <stopData position="0" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.3397947548460661" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.339798898163606" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.6624439732888147" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.6624690150250417" > + <colorData g="0" r="0" a="255" b="255" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="0" a="255" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Eye" > + <gradientData centerX="0.5" centerY="0.5" radius="0.5" spread="PadSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.1939699465240642" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.202312192513369" > + <colorData g="97" r="122" a="255" b="0" /> + </stopData> + <stopData position="0.4955143315508022" > + <colorData g="58" r="76" a="255" b="0" /> + </stopData> + <stopData position="0.5048191443850267" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.79" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="1" > + <colorData g="158" r="255" a="255" b="158" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Flare1" > + <gradientData centerX="0.5" centerY="0.5" radius="0.5" spread="PadSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.1" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.2" > + <colorData g="176" r="255" a="167" b="176" /> + </stopData> + <stopData position="0.3" > + <colorData g="151" r="255" a="92" b="151" /> + </stopData> + <stopData position="0.4" > + <colorData g="125" r="255" a="51" b="125" /> + </stopData> + <stopData position="0.5" > + <colorData g="76" r="255" a="205" b="76" /> + </stopData> + <stopData position="0.52" > + <colorData g="76" r="255" a="205" b="76" /> + </stopData> + <stopData position="0.6" > + <colorData g="180" r="255" a="84" b="180" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="0" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Flare2" > + <gradientData centerX="0.5" centerY="0.5" radius="0.5" spread="PadSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="0" r="0" a="0" b="0" /> + </stopData> + <stopData position="0.52" > + <colorData g="0" r="0" a="0" b="0" /> + </stopData> + <stopData position="0.5649999999999999" > + <colorData g="121" r="82" a="33" b="76" /> + </stopData> + <stopData position="0.65" > + <colorData g="235" r="159" a="64" b="148" /> + </stopData> + <stopData position="0.7219251336898396" > + <colorData g="238" r="255" a="129" b="150" /> + </stopData> + <stopData position="0.77" > + <colorData g="128" r="255" a="204" b="128" /> + </stopData> + <stopData position="0.89" > + <colorData g="128" r="191" a="64" b="255" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="0" a="0" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Flare3" > + <gradientData centerX="0.5" centerY="0.5" radius="0.5" spread="PadSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="235" r="255" a="206" b="235" /> + </stopData> + <stopData position="0.35" > + <colorData g="188" r="255" a="80" b="188" /> + </stopData> + <stopData position="0.4" > + <colorData g="162" r="255" a="80" b="162" /> + </stopData> + <stopData position="0.425" > + <colorData g="132" r="255" a="156" b="132" /> + </stopData> + <stopData position="0.44" > + <colorData g="128" r="252" a="80" b="128" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="0" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="German" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="0" endY="1" > + <stopData position="0" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.33" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.34" > + <colorData g="30" r="255" a="255" b="30" /> + </stopData> + <stopData position="0.66" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.67" > + <colorData g="255" r="255" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Golden" > + <gradientData centerX="0.5" centerY="0.5" spread="PadSpread" coordinateMode="StretchToDeviceMode" type="ConicalGradient" angle="0" > + <stopData position="0" > + <colorData g="40" r="35" a="255" b="3" /> + </stopData> + <stopData position="0.16" > + <colorData g="106" r="136" a="255" b="22" /> + </stopData> + <stopData position="0.225" > + <colorData g="140" r="166" a="255" b="41" /> + </stopData> + <stopData position="0.285" > + <colorData g="181" r="204" a="255" b="74" /> + </stopData> + <stopData position="0.345" > + <colorData g="219" r="235" a="255" b="102" /> + </stopData> + <stopData position="0.415" > + <colorData g="236" r="245" a="255" b="112" /> + </stopData> + <stopData position="0.52" > + <colorData g="190" r="209" a="255" b="76" /> + </stopData> + <stopData position="0.57" > + <colorData g="156" r="187" a="255" b="51" /> + </stopData> + <stopData position="0.635" > + <colorData g="142" r="168" a="255" b="42" /> + </stopData> + <stopData position="0.695" > + <colorData g="174" r="202" a="255" b="68" /> + </stopData> + <stopData position="0.75" > + <colorData g="202" r="218" a="255" b="86" /> + </stopData> + <stopData position="0.8149999999999999" > + <colorData g="187" r="208" a="255" b="73" /> + </stopData> + <stopData position="0.88" > + <colorData g="156" r="187" a="255" b="51" /> + </stopData> + <stopData position="0.9350000000000001" > + <colorData g="108" r="137" a="255" b="26" /> + </stopData> + <stopData position="1" > + <colorData g="40" r="35" a="255" b="3" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Japanese" > + <gradientData centerX="0.5" centerY="0.5" radius="0.5" spread="PadSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.4799044117647059" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.5226851604278074" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Norwegian" > + <gradientData spread="RepeatSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.17" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.18" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.2102117403738299" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.2202117403738299" > + <colorData g="16" r="0" a="255" b="255" /> + </stopData> + <stopData position="0.2798973635190806" > + <colorData g="16" r="0" a="255" b="255" /> + </stopData> + <stopData position="0.2898973635190806" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.32" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.33" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Pastels" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="224" r="245" a="255" b="176" /> + </stopData> + <stopData position="0.09" > + <colorData g="189" r="246" a="255" b="237" /> + </stopData> + <stopData position="0.14" > + <colorData g="207" r="194" a="255" b="246" /> + </stopData> + <stopData position="0.19" > + <colorData g="160" r="184" a="255" b="168" /> + </stopData> + <stopData position="0.25" > + <colorData g="186" r="171" a="255" b="248" /> + </stopData> + <stopData position="0.32" > + <colorData g="248" r="243" a="255" b="224" /> + </stopData> + <stopData position="0.385" > + <colorData g="162" r="249" a="255" b="183" /> + </stopData> + <stopData position="0.47" > + <colorData g="115" r="100" a="255" b="124" /> + </stopData> + <stopData position="0.58" > + <colorData g="205" r="251" a="255" b="202" /> + </stopData> + <stopData position="0.65" > + <colorData g="128" r="170" a="255" b="185" /> + </stopData> + <stopData position="0.75" > + <colorData g="222" r="252" a="255" b="204" /> + </stopData> + <stopData position="0.805" > + <colorData g="122" r="206" a="255" b="218" /> + </stopData> + <stopData position="0.86" > + <colorData g="223" r="254" a="255" b="175" /> + </stopData> + <stopData position="0.91" > + <colorData g="236" r="254" a="255" b="244" /> + </stopData> + <stopData position="1" > + <colorData g="191" r="255" a="255" b="221" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Polish" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="0" endY="1" > + <stopData position="0" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.495" > + <colorData g="255" r="255" a="255" b="255" /> + </stopData> + <stopData position="0.505" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Rainbow" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.166" > + <colorData g="255" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.333" > + <colorData g="255" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.5" > + <colorData g="255" r="0" a="255" b="255" /> + </stopData> + <stopData position="0.666" > + <colorData g="0" r="0" a="255" b="255" /> + </stopData> + <stopData position="0.833" > + <colorData g="0" r="255" a="255" b="255" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="255" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Sky" > + <gradientData spread="PadSpread" startX="0" startY="1" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="0" endY="0" > + <stopData position="0" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="0.05" > + <colorData g="8" r="14" a="255" b="73" /> + </stopData> + <stopData position="0.36" > + <colorData g="17" r="28" a="255" b="145" /> + </stopData> + <stopData position="0.6" > + <colorData g="14" r="126" a="255" b="81" /> + </stopData> + <stopData position="0.75" > + <colorData g="11" r="234" a="255" b="11" /> + </stopData> + <stopData position="0.79" > + <colorData g="70" r="244" a="255" b="5" /> + </stopData> + <stopData position="0.86" > + <colorData g="136" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.9350000000000001" > + <colorData g="236" r="239" a="255" b="55" /> + </stopData> + </gradientData> + </gradient> + <gradient name="SunRay" > + <gradientData centerX="0" centerY="0" spread="RepeatSpread" coordinateMode="StretchToDeviceMode" type="ConicalGradient" angle="135" > + <stopData position="0" > + <colorData g="255" r="255" a="69" b="0" /> + </stopData> + <stopData position="0.375" > + <colorData g="255" r="255" a="69" b="0" /> + </stopData> + <stopData position="0.4235329090018885" > + <colorData g="255" r="251" a="145" b="0" /> + </stopData> + <stopData position="0.45" > + <colorData g="255" r="247" a="208" b="0" /> + </stopData> + <stopData position="0.4775811200061043" > + <colorData g="244" r="255" a="130" b="71" /> + </stopData> + <stopData position="0.5187165775401069" > + <colorData g="218" r="255" a="130" b="71" /> + </stopData> + <stopData position="0.55" > + <colorData g="255" r="255" a="255" b="0" /> + </stopData> + <stopData position="0.5775401069518716" > + <colorData g="203" r="255" a="130" b="0" /> + </stopData> + <stopData position="0.625" > + <colorData g="255" r="255" a="69" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="255" r="255" a="69" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Tropical" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="41" r="9" a="255" b="4" /> + </stopData> + <stopData position="0.08500000000000001" > + <colorData g="79" r="2" a="255" b="0" /> + </stopData> + <stopData position="0.19" > + <colorData g="147" r="50" a="255" b="22" /> + </stopData> + <stopData position="0.275" > + <colorData g="191" r="236" a="255" b="49" /> + </stopData> + <stopData position="0.39" > + <colorData g="61" r="243" a="255" b="34" /> + </stopData> + <stopData position="0.555" > + <colorData g="81" r="135" a="255" b="60" /> + </stopData> + <stopData position="0.667" > + <colorData g="75" r="121" a="255" b="255" /> + </stopData> + <stopData position="0.825" > + <colorData g="255" r="164" a="255" b="244" /> + </stopData> + <stopData position="0.885" > + <colorData g="222" r="104" a="255" b="71" /> + </stopData> + <stopData position="1" > + <colorData g="128" r="93" a="255" b="0" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Wave" > + <gradientData centerX="0.5" centerY="0.5" radius="0.077" spread="RepeatSpread" focalX="0.5" focalY="0.5" coordinateMode="StretchToDeviceMode" type="RadialGradient" > + <stopData position="0" > + <colorData g="169" r="0" a="147" b="255" /> + </stopData> + <stopData position="0.4973262032085561" > + <colorData g="0" r="0" a="147" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="169" r="0" a="147" b="255" /> + </stopData> + </gradientData> + </gradient> + <gradient name="Wood" > + <gradientData spread="PadSpread" startX="0" startY="0" coordinateMode="StretchToDeviceMode" type="LinearGradient" endX="1" endY="0" > + <stopData position="0" > + <colorData g="178" r="255" a="255" b="102" /> + </stopData> + <stopData position="0.55" > + <colorData g="148" r="235" a="255" b="61" /> + </stopData> + <stopData position="0.98" > + <colorData g="0" r="0" a="255" b="0" /> + </stopData> + <stopData position="1" > + <colorData g="0" r="0" a="0" b="0" /> + </stopData> + </gradientData> + </gradient> +</gradients> diff --git a/tools/designer/src/lib/shared/deviceprofile.cpp b/tools/designer/src/lib/shared/deviceprofile.cpp new file mode 100644 index 0000000..c512ff5 --- /dev/null +++ b/tools/designer/src/lib/shared/deviceprofile.cpp @@ -0,0 +1,467 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "deviceprofile_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <widgetfactory_p.h> +#include <qdesigner_utils_p.h> + +#include <QtGui/QApplication> +#include <QtGui/QFont> +#include <QtGui/QDesktopWidget> +#include <QtGui/QStyle> +#include <QtGui/QStyleFactory> +#include <QtGui/QApplication> + +#include <QtCore/QSharedData> +#include <QtCore/QTextStream> + +#include <QtCore/QXmlStreamWriter> +#include <QtCore/QXmlStreamReader> + + +static const char *dpiXPropertyC = "_q_customDpiX"; +static const char *dpiYPropertyC = "_q_customDpiY"; + +// XML serialization +static const char *xmlVersionC="1.0"; +static const char *rootElementC="deviceprofile"; +static const char *nameElementC = "name"; +static const char *fontFamilyElementC = "fontfamily"; +static const char *fontPointSizeElementC = "fontpointsize"; +static const char *dPIXElementC = "dpix"; +static const char *dPIYElementC = "dpiy"; +static const char *styleElementC = "style"; + +/* DeviceProfile: + * For preview purposes (preview, widget box, new form dialog), the + * QDesignerFormBuilder applies the settings to the form main container + * (Point being here that the DPI must be set directly for size calculations + * to be correct). + * For editing purposes, FormWindow applies the settings to the form container + * as not to interfere with the font settings of the form main container. + * In addition, the widgetfactory maintains the system settings style + * and applies it when creating widgets. */ + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---------------- DeviceProfileData +class DeviceProfileData : public QSharedData { +public: + DeviceProfileData(); + void fromSystem(); + void clear(); + + QString m_fontFamily; + int m_fontPointSize; + QString m_style; + int m_dpiX; + int m_dpiY; + QString m_name; +}; + +DeviceProfileData::DeviceProfileData() : + m_fontPointSize(-1), + m_dpiX(-1), + m_dpiY(-1) +{ +} + +void DeviceProfileData::clear() +{ + m_fontPointSize = -1; + m_dpiX = 0; + m_dpiY = 0; + m_name.clear(); + m_style.clear(); +} + +void DeviceProfileData::fromSystem() +{ + const QFont appFont = QApplication::font(); + m_fontFamily = appFont.family(); + m_fontPointSize = appFont.pointSize(); + DeviceProfile::systemResolution(&m_dpiX, &m_dpiY); + m_style.clear(); +} + +// ---------------- DeviceProfile +DeviceProfile::DeviceProfile() : + m_d(new DeviceProfileData) +{ +} + +DeviceProfile::DeviceProfile(const DeviceProfile &o) : + m_d(o.m_d) + +{ +} + +DeviceProfile& DeviceProfile::operator=(const DeviceProfile &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +DeviceProfile::~DeviceProfile() +{ +} + +void DeviceProfile::clear() +{ + m_d->clear(); +} + +bool DeviceProfile::isEmpty() const +{ + return m_d->m_name.isEmpty(); +} + +QString DeviceProfile::fontFamily() const +{ + return m_d->m_fontFamily; +} + +void DeviceProfile::setFontFamily(const QString &f) +{ + m_d->m_fontFamily = f; +} + +int DeviceProfile::fontPointSize() const +{ + return m_d->m_fontPointSize; +} + +void DeviceProfile::setFontPointSize(int p) +{ + m_d->m_fontPointSize = p; +} + +QString DeviceProfile::style() const +{ + return m_d->m_style; +} + +void DeviceProfile::setStyle(const QString &s) +{ + m_d->m_style = s; +} + +int DeviceProfile::dpiX() const +{ + return m_d->m_dpiX; +} + +void DeviceProfile::setDpiX(int d) +{ + m_d->m_dpiX = d; +} + +int DeviceProfile::dpiY() const +{ + return m_d->m_dpiY; +} + +void DeviceProfile::setDpiY(int d) +{ + m_d->m_dpiY = d; +} + +void DeviceProfile::fromSystem() +{ + m_d->fromSystem(); +} + +QString DeviceProfile::name() const +{ + return m_d->m_name; +} + +void DeviceProfile::setName(const QString &n) +{ + m_d->m_name = n; +} + +void DeviceProfile::systemResolution(int *dpiX, int *dpiY) +{ + const QDesktopWidget *dw = qApp->desktop(); + *dpiX = dw->logicalDpiX(); + *dpiY = dw->logicalDpiY(); +} + +class FriendlyWidget : public QWidget { + friend class DeviceProfile; +}; + +void DeviceProfile::widgetResolution(const QWidget *w, int *dpiX, int *dpiY) +{ + const FriendlyWidget *fw = static_cast<const FriendlyWidget*>(w); + *dpiX = fw->metric(QPaintDevice::PdmDpiX); + *dpiY = fw->metric(QPaintDevice::PdmDpiY); +} + +QString DeviceProfile::toString() const +{ + const DeviceProfileData &d = *m_d; + QString rc; + QTextStream(&rc) << "DeviceProfile:name=" << d.m_name << " Font=" << d.m_fontFamily << ' ' + << d.m_fontPointSize << " Style=" << d.m_style << " DPI=" << d.m_dpiX << ',' << d.m_dpiY; + return rc; +} + +// Apply font to widget +static void applyFont(const QString &family, int size, DeviceProfile::ApplyMode am, QWidget *widget) +{ + QFont currentFont = widget->font(); + if (currentFont.pointSize() == size && currentFont.family() == family) + return; + switch (am) { + case DeviceProfile::ApplyFormParent: + // Invisible form parent: Apply all + widget->setFont(QFont(family, size)); + break; + case DeviceProfile::ApplyPreview: { + // Preview: Apply only subproperties that have not been changed by designer properties + bool apply = false; + const uint resolve = currentFont.resolve(); + if (!(resolve & QFont::FamilyResolved)) { + currentFont.setFamily(family); + apply = true; + } + if (!(resolve & QFont::SizeResolved)) { + currentFont.setPointSize(size); + apply = true; + } + if (apply) + widget->setFont(currentFont); + } + break; + } +} + +void DeviceProfile::applyDPI(int dpiX, int dpiY, QWidget *widget) +{ + int sysDPIX, sysDPIY; // Set dynamic variables in case values are different from system DPI + systemResolution(&sysDPIX, &sysDPIY); + if (dpiX != sysDPIX && dpiY != sysDPIY) { + widget->setProperty(dpiXPropertyC, QVariant(dpiX)); + widget->setProperty(dpiYPropertyC, QVariant(dpiY)); + } +} + +void DeviceProfile::apply(const QDesignerFormEditorInterface *core, QWidget *widget, ApplyMode am) const +{ + if (isEmpty()) + return; + + const DeviceProfileData &d = *m_d; + + if (!d.m_fontFamily.isEmpty()) + applyFont(d.m_fontFamily, d.m_fontPointSize, am, widget); + + applyDPI(d.m_dpiX, d.m_dpiY, widget); + + if (!d.m_style.isEmpty()) { + if (WidgetFactory *wf = qobject_cast<qdesigner_internal::WidgetFactory *>(core->widgetFactory())) + wf->applyStyleTopLevel(d.m_style, widget); + } +} + +bool DeviceProfile::equals(const DeviceProfile& rhs) const +{ + const DeviceProfileData &d = *m_d; + const DeviceProfileData &rhs_d = *rhs.m_d; + return d.m_fontPointSize == rhs_d.m_fontPointSize && + d.m_dpiX == rhs_d.m_dpiX && d.m_dpiY == rhs_d.m_dpiY && d.m_fontFamily == rhs_d.m_fontFamily && + d.m_style == rhs_d.m_style && d.m_name == rhs_d.m_name; +} + +static inline void writeElement(QXmlStreamWriter &writer, const QString &element, const QString &cdata) +{ + writer.writeStartElement(element); + writer.writeCharacters(cdata); + writer.writeEndElement(); +} + +QString DeviceProfile::toXml() const +{ + const DeviceProfileData &d = *m_d; + QString rc; + QXmlStreamWriter writer(&rc); + writer.writeStartDocument(QLatin1String(xmlVersionC)); + writer.writeStartElement(QLatin1String(rootElementC)); + writeElement(writer, QLatin1String(nameElementC), d.m_name); + + if (!d.m_fontFamily.isEmpty()) + writeElement(writer, QLatin1String(fontFamilyElementC), d.m_fontFamily); + if (d.m_fontPointSize >= 0) + writeElement(writer, QLatin1String(fontPointSizeElementC), QString::number(d.m_fontPointSize)); + if (d.m_dpiX > 0) + writeElement(writer, QLatin1String(dPIXElementC), QString::number(d.m_dpiX)); + if (d.m_dpiY > 0) + writeElement(writer, QLatin1String(dPIYElementC), QString::number(d.m_dpiY)); + if (!d.m_style.isEmpty()) + writeElement(writer, QLatin1String(styleElementC), d.m_style); + + writer.writeEndElement(); + writer.writeEndDocument(); + return rc; +} + +/* Switch stages when encountering a start element (state table) */ +enum ParseStage { ParseBeginning, ParseWithinRoot, + ParseName, ParseFontFamily, ParseFontPointSize, ParseDPIX, ParseDPIY, ParseStyle, + ParseError }; + +static ParseStage nextStage(ParseStage currentStage, const QStringRef &startElement) +{ + switch (currentStage) { + case ParseBeginning: + if (startElement == QLatin1String(rootElementC)) + return ParseWithinRoot; + break; + case ParseWithinRoot: + case ParseName: + case ParseFontFamily: + case ParseFontPointSize: + case ParseDPIX: + case ParseDPIY: + case ParseStyle: + if (startElement == QLatin1String(nameElementC)) + return ParseName; + if (startElement == QLatin1String(fontFamilyElementC)) + return ParseFontFamily; + if (startElement == QLatin1String(fontPointSizeElementC)) + return ParseFontPointSize; + if (startElement == QLatin1String(dPIXElementC)) + return ParseDPIX; + if (startElement == QLatin1String(dPIYElementC)) + return ParseDPIY; + if (startElement == QLatin1String(styleElementC)) + return ParseStyle; + break; + case ParseError: + break; + } + return ParseError; +} + +static bool readIntegerElement(QXmlStreamReader &reader, int *v) +{ + const QString e = reader.readElementText(); + bool ok; + *v = e.toInt(&ok); + //: Reading a number for an embedded device profile + if (!ok) + reader.raiseError(QApplication::translate("DeviceProfile", "'%1' is not a number.").arg(e)); + return ok; +} + +bool DeviceProfile::fromXml(const QString &xml, QString *errorMessage) +{ + DeviceProfileData &d = *m_d; + d.fromSystem(); + + QXmlStreamReader reader(xml); + + ParseStage ps = ParseBeginning; + QXmlStreamReader::TokenType tt = QXmlStreamReader::NoToken; + int iv = 0; + do { + tt = reader.readNext(); + if (tt == QXmlStreamReader::StartElement) { + ps = nextStage(ps, reader.name()); + switch (ps) { + case ParseBeginning: + case ParseWithinRoot: + break; + case ParseError: + reader.raiseError(QApplication::translate("DeviceProfile", "An invalid tag <%1> was encountered.").arg(reader.name().toString())); + tt = QXmlStreamReader::Invalid; + break; + case ParseName: + d.m_name = reader.readElementText(); + break; + case ParseFontFamily: + d.m_fontFamily = reader.readElementText(); + break; + case ParseFontPointSize: + if (readIntegerElement(reader, &iv)) { + d.m_fontPointSize = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseDPIX: + if (readIntegerElement(reader, &iv)) { + d.m_dpiX = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseDPIY: + if (readIntegerElement(reader, &iv)) { + d.m_dpiY = iv; + } else { + tt = QXmlStreamReader::Invalid; + } + break; + case ParseStyle: + d.m_style = reader.readElementText(); + break; + } + } + } while (tt != QXmlStreamReader::Invalid && tt != QXmlStreamReader::EndDocument); + + if (reader.hasError()) { + *errorMessage = reader.errorString(); + return false; + } + + return true; +} +} + +QT_END_NAMESPACE + diff --git a/tools/designer/src/lib/shared/deviceprofile_p.h b/tools/designer/src/lib/shared/deviceprofile_p.h new file mode 100644 index 0000000..a1a36e3 --- /dev/null +++ b/tools/designer/src/lib/shared/deviceprofile_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DEVICEPROFILE_H +#define DEVICEPROFILE_H + +#include "shared_global_p.h" + +#include <QtCore/QString> +#include <QtCore/QSharedDataPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QWidget; +class QStyle; + +namespace qdesigner_internal { + +class DeviceProfileData; + +/* DeviceProfile for embedded design. They influence + * default properties (for example, fonts), dpi and + * style of the form. This class represents a device + * profile. */ + +class QDESIGNER_SHARED_EXPORT DeviceProfile { +public: + DeviceProfile(); + + DeviceProfile(const DeviceProfile&); + DeviceProfile& operator=(const DeviceProfile&); + ~DeviceProfile(); + + void clear(); + + // Device name + QString name() const; + void setName(const QString &); + + // System settings active + bool isEmpty() const; + + // Default font family of the embedded system + QString fontFamily() const; + void setFontFamily(const QString &); + + // Default font size of the embedded system + int fontPointSize() const; + void setFontPointSize(int p); + + // Display resolution of the embedded system + int dpiX() const; + void setDpiX(int d); + int dpiY() const; + void setDpiY(int d); + + // Style + QString style() const; + void setStyle(const QString &); + + // Initialize from desktop system + void fromSystem(); + + static void systemResolution(int *dpiX, int *dpiY); + static void widgetResolution(const QWidget *w, int *dpiX, int *dpiY); + + bool equals(const DeviceProfile& rhs) const; + + // Apply to form/preview (using font inheritance) + enum ApplyMode { + /* Pre-Apply to parent widget of form being edited: Apply font + * and make use of property inheritance to be able to modify the + * font property freely. */ + ApplyFormParent, + /* Post-Apply to preview widget: Change only inherited font + * sub properties. */ + ApplyPreview + }; + void apply(const QDesignerFormEditorInterface *core, QWidget *widget, ApplyMode am) const; + + static void applyDPI(int dpiX, int dpiY, QWidget *widget); + + QString toString() const; + + QString toXml() const; + bool fromXml(const QString &xml, QString *errorMessage); + +private: + QSharedDataPointer<DeviceProfileData> m_d; +}; + +inline bool operator==(const DeviceProfile &s1, const DeviceProfile &s2) + { return s1.equals(s2); } +inline bool operator!=(const DeviceProfile &s1, const DeviceProfile &s2) + { return !s1.equals(s2); } + +} + + +QT_END_NAMESPACE + +#endif // DEVICEPROFILE_H diff --git a/tools/designer/src/lib/shared/dialoggui.cpp b/tools/designer/src/lib/shared/dialoggui.cpp new file mode 100644 index 0000000..ef9dbae --- /dev/null +++ b/tools/designer/src/lib/shared/dialoggui.cpp @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "dialoggui_p.h" + +#include <QtGui/QFileIconProvider> +#include <QtGui/QIcon> +#include <QtGui/QImage> +#include <QtGui/QImageReader> +#include <QtGui/QPixmap> + +#include <QtCore/QFileInfo> +#include <QtCore/QFile> +#include <QtCore/QSet> + +// QFileDialog on X11 does not provide an image preview. Display icons. +#ifdef Q_WS_X11 +# define IMAGE_PREVIEW +#endif + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Icon provider that reads out the known image formats +class IconProvider : public QFileIconProvider { + Q_DISABLE_COPY(IconProvider) + +public: + IconProvider(); + virtual QIcon icon (const QFileInfo &info) const; + + inline bool loadCheck(const QFileInfo &info) const; + QImage loadImage(const QString &fileName) const; + +private: + QSet<QString> m_imageFormats; +}; + +IconProvider::IconProvider() +{ + // Determine a list of readable extensions (upper and lower case) + typedef QList<QByteArray> ByteArrayList; + const ByteArrayList fmts = QImageReader::supportedImageFormats(); + const ByteArrayList::const_iterator cend = fmts.constEnd(); + for (ByteArrayList::const_iterator it = fmts.constBegin(); it != cend; ++it) { + const QString suffix = QString::fromUtf8(it->constData()); + m_imageFormats.insert(suffix.toLower()); + m_imageFormats.insert(suffix.toUpper()); + + } +} + +// Check by extension and type if this appears to be a loadable image +bool IconProvider::loadCheck(const QFileInfo &info) const +{ + if (info.isFile() && info.isReadable()) { + const QString suffix = info.suffix(); + if (!suffix.isEmpty()) + return m_imageFormats.contains(suffix); + } + return false; +} + +QImage IconProvider::loadImage(const QString &fileName) const +{ + QFile file(fileName); + if (file.open(QIODevice::ReadOnly)) { + QImageReader imgReader(&file); + if (imgReader.canRead()) { + QImage image; + if (imgReader.read(&image)) + return image; + } + } + return QImage(); +} + +QIcon IconProvider::icon (const QFileInfo &info) const +{ + // Don't get stuck on large images. + const qint64 maxSize = 131072; + if (loadCheck(info) && info.size() < maxSize) { + const QImage image = loadImage(info.absoluteFilePath()); + if (!image.isNull()) + return QIcon(QPixmap::fromImage(image, Qt::ThresholdDither|Qt::AutoColor)); + } + return QFileIconProvider::icon(info); +} + +// ---------------- DialogGui +DialogGui::DialogGui() : + m_iconProvider(0) +{ +} + +DialogGui::~DialogGui() +{ + delete m_iconProvider; +} + +QFileIconProvider *DialogGui::ensureIconProvider() +{ + if (!m_iconProvider) + m_iconProvider = new IconProvider; + return m_iconProvider; +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) +{ + QMessageBox::StandardButton rc = QMessageBox::NoButton; + switch (icon) { + case QMessageBox::Information: + rc = QMessageBox::information(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Warning: + rc = QMessageBox::warning(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Critical: + rc = QMessageBox::critical(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::Question: + rc = QMessageBox::question(parent, title, text, buttons, defaultButton); + break; + case QMessageBox::NoIcon: + break; + } + return rc; +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) +{ + QMessageBox msgBox(icon, title, text, buttons, parent); + msgBox.setDefaultButton(defaultButton); + msgBox.setInformativeText(informativeText); + return static_cast<QMessageBox::StandardButton>(msgBox.exec()); +} + +QMessageBox::StandardButton + DialogGui::message(QWidget *parent, Message /*context*/, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, const QString &detailedText, + QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton) +{ + QMessageBox msgBox(icon, title, text, buttons, parent); + msgBox.setDefaultButton(defaultButton); + msgBox.setInformativeText(informativeText); + msgBox.setDetailedText(detailedText); + return static_cast<QMessageBox::StandardButton>(msgBox.exec()); +} + +QString DialogGui::getExistingDirectory(QWidget *parent, const QString &caption, const QString &dir, QFileDialog::Options options) +{ + return QFileDialog::getExistingDirectory(parent, caption, dir, options); +} + +QString DialogGui::getOpenFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +} + +QStringList DialogGui::getOpenFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); +} + +QString DialogGui::getSaveFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options) +{ + return QFileDialog::getSaveFileName(parent, caption, dir, filter, selectedFilter, options); +} + +void DialogGui::initializeImageFileDialog(QFileDialog &fileDialog, QFileDialog::Options options, QFileDialog::FileMode fm) +{ + fileDialog.setConfirmOverwrite( !(options & QFileDialog::DontConfirmOverwrite) ); + fileDialog.setResolveSymlinks( !(options & QFileDialog::DontResolveSymlinks) ); + fileDialog.setIconProvider(ensureIconProvider()); + fileDialog.setFileMode(fm); +} + +QString DialogGui::getOpenImageFileName(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options ) +{ + +#ifdef IMAGE_PREVIEW + QFileDialog fileDialog(parent, caption, dir, filter); + initializeImageFileDialog(fileDialog, options, QFileDialog::ExistingFile); + if (fileDialog.exec() != QDialog::Accepted) + return QString(); + + const QStringList selectedFiles = fileDialog.selectedFiles(); + if (selectedFiles.empty()) + return QString(); + + if (selectedFilter) + *selectedFilter = fileDialog.selectedFilter(); + + return selectedFiles.front(); +#else + return getOpenFileName(parent, caption, dir, filter, selectedFilter, options); +#endif +} + +QStringList DialogGui::getOpenImageFileNames(QWidget *parent, const QString &caption, const QString &dir, const QString &filter, QString *selectedFilter, QFileDialog::Options options ) +{ +#ifdef IMAGE_PREVIEW + QFileDialog fileDialog(parent, caption, dir, filter); + initializeImageFileDialog(fileDialog, options, QFileDialog::ExistingFiles); + if (fileDialog.exec() != QDialog::Accepted) + return QStringList(); + + const QStringList selectedFiles = fileDialog.selectedFiles(); + if (!selectedFiles.empty() && selectedFilter) + *selectedFilter = fileDialog.selectedFilter(); + + return selectedFiles; +#else + return getOpenFileNames(parent, caption, dir, filter, selectedFilter, options); +#endif +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/dialoggui_p.h b/tools/designer/src/lib/shared/dialoggui_p.h new file mode 100644 index 0000000..753e130 --- /dev/null +++ b/tools/designer/src/lib/shared/dialoggui_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIALOGGUI +#define DIALOGGUI + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "shared_global_p.h" +#include <abstractdialoggui_p.h> + +QT_BEGIN_NAMESPACE + +class QFileIconProvider; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT DialogGui : public QDesignerDialogGuiInterface +{ +public: + DialogGui(); + virtual ~DialogGui(); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QMessageBox::StandardButton + message(QWidget *parent, Message context, QMessageBox::Icon icon, + const QString &title, const QString &text, const QString &informativeText, const QString &detailedText, + QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton); + + virtual QString getExistingDirectory(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), QFileDialog::Options options = QFileDialog::ShowDirsOnly); + virtual QString getOpenFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QStringList getOpenFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QString getSaveFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + + virtual QString getOpenImageFileName(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + virtual QStringList getOpenImageFileNames(QWidget *parent = 0, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = 0, QFileDialog::Options options = 0); + +private: + QFileIconProvider *ensureIconProvider(); + void initializeImageFileDialog(QFileDialog &fd, QFileDialog::Options options, QFileDialog::FileMode); + + QFileIconProvider *m_iconProvider; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DIALOGGUI diff --git a/tools/designer/src/lib/shared/extensionfactory_p.h b/tools/designer/src/lib/shared/extensionfactory_p.h new file mode 100644 index 0000000..10649c4 --- /dev/null +++ b/tools/designer/src/lib/shared/extensionfactory_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_EXTENSIONFACTORY_H +#define SHARED_EXTENSIONFACTORY_H + +#include <QtDesigner/default_extensionfactory.h> +#include <QtDesigner/QExtensionManager> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Extension factory for registering an extension for an object type. +template <class ExtensionInterface, class Object, class Extension> +class ExtensionFactory: public QExtensionFactory +{ +public: + explicit ExtensionFactory(const QString &iid, QExtensionManager *parent = 0); + + // Convenience for registering the extension. Do not use for derived classes. + static void registerExtension(QExtensionManager *mgr, const QString &iid); + +protected: + virtual QObject *createExtension(QObject *qObject, const QString &iid, QObject *parent) const; + +private: + // Can be overwritten to perform checks on the object. + // Default does a qobject_cast to the desired class. + virtual Object *checkObject(QObject *qObject) const; + + const QString m_iid; +}; + +template <class ExtensionInterface, class Object, class Extension> +ExtensionFactory<ExtensionInterface, Object, Extension>::ExtensionFactory(const QString &iid, QExtensionManager *parent) : + QExtensionFactory(parent), + m_iid(iid) +{ +} + +template <class ExtensionInterface, class Object, class Extension> +Object *ExtensionFactory<ExtensionInterface, Object, Extension>::checkObject(QObject *qObject) const +{ + return qobject_cast<Object*>(qObject); +} + +template <class ExtensionInterface, class Object, class Extension> +QObject *ExtensionFactory<ExtensionInterface, Object, Extension>::createExtension(QObject *qObject, const QString &iid, QObject *parent) const +{ + if (iid != m_iid) + return 0; + + Object *object = checkObject(qObject); + if (!object) + return 0; + + return new Extension(object, parent); +} + +template <class ExtensionInterface, class Object, class Extension> +void ExtensionFactory<ExtensionInterface, Object, Extension>::registerExtension(QExtensionManager *mgr, const QString &iid) +{ + ExtensionFactory *factory = new ExtensionFactory(iid, mgr); + mgr->registerExtensions(factory, iid); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHARED_EXTENSIONFACTORY_H diff --git a/tools/designer/src/lib/shared/filterwidget.cpp b/tools/designer/src/lib/shared/filterwidget.cpp new file mode 100644 index 0000000..f0a77c3 --- /dev/null +++ b/tools/designer/src/lib/shared/filterwidget.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "filterwidget_p.h" +#include "iconloader_p.h" + +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QPushButton> +#include <QtGui/QLineEdit> +#include <QtGui/QFocusEvent> +#include <QtGui/QPalette> +#include <QtGui/QCursor> + +#include <QtCore/QDebug> + +enum { debugFilter = 0 }; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +HintLineEdit::HintLineEdit(QWidget *parent) : + QLineEdit(parent), + m_defaultFocusPolicy(focusPolicy()), + m_hintColor(QColor(0xbbbbbb)), + m_refuseFocus(false), + m_showingHintText(false) +{ +} + +bool HintLineEdit::refuseFocus() const +{ + return m_refuseFocus; +} + +void HintLineEdit::setRefuseFocus(bool v) +{ + if (v == m_refuseFocus) + return; + m_refuseFocus = v; + setFocusPolicy(m_refuseFocus ? Qt::NoFocus : m_defaultFocusPolicy); +} + +void HintLineEdit::mousePressEvent(QMouseEvent *e) +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + // Explicitly focus on click. + if (m_refuseFocus && !hasFocus()) + setFocus(Qt::OtherFocusReason); + QLineEdit::mousePressEvent(e); +} + +void HintLineEdit::focusInEvent(QFocusEvent *e) +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + if (m_refuseFocus) { + // Refuse the focus if the mouse it outside. In addition to the mouse + // press logic, this prevents a re-focussing which occurs once + // we actually had focus + const Qt::FocusReason reason = e->reason(); + if (reason == Qt::ActiveWindowFocusReason || reason == Qt::PopupFocusReason) { + const QPoint mousePos = mapFromGlobal(QCursor::pos()); + const bool refuse = !geometry().contains(mousePos); + if (debugFilter) + qDebug() << Q_FUNC_INFO << refuse ; + if (refuse) { + e->ignore(); + return; + } + } + } + + hideHintText(); + QLineEdit::focusInEvent(e); +} + +void HintLineEdit::focusOutEvent(QFocusEvent *e) +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + // Focus out: Switch to displaying the hint text unless there is user input + showHintText(); + QLineEdit::focusOutEvent(e); +} + +QString HintLineEdit::hintText() const +{ + return m_hintText; +} + +void HintLineEdit::setHintText(const QString &ht) +{ + if (ht == m_hintText) + return; + hideHintText(); + m_hintText = ht; + if (!hasFocus() && !ht.isEmpty()) + showHintText(); +} + +void HintLineEdit::showHintText(bool force) +{ + if (m_showingHintText || m_hintText.isEmpty()) + return; + if (force || text().isEmpty()) { + m_showingHintText = true; + setText(m_hintText); + setTextColor(m_hintColor, &m_textColor); + } +} +void HintLineEdit::hideHintText() +{ + if (m_showingHintText && !m_hintText.isEmpty()) { + m_showingHintText = false; + setText(QString()); + setTextColor(m_textColor); + } +} + +bool HintLineEdit::isShowingHintText() const +{ + return m_showingHintText; +} + +QString HintLineEdit::typedText() const +{ + return m_showingHintText ? QString() : text(); +} + +void HintLineEdit::setTextColor(const QColor &newColor, QColor *oldColor) +{ + QPalette pal = palette(); + if (oldColor) + *oldColor = pal.color(QPalette::Text); + pal.setColor(QPalette::Text, newColor); + setPalette(pal);} + +// ------------------- FilterWidget +FilterWidget::FilterWidget(QWidget *parent, LayoutMode lm) : + QWidget(parent), + m_button(new QPushButton), + m_editor(new HintLineEdit) +{ + m_editor->setHintText(tr("<Filter>")); + QHBoxLayout *l = new QHBoxLayout(this); + l->setMargin(0); + l->setSpacing(0); + + if (lm == LayoutAlignRight) + l->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum)); + + l->addWidget(m_editor); + + m_button->setIcon(createIconSet(QLatin1String("resetproperty.png"))); + m_button->setIconSize(QSize(8, 8)); + m_button->setFlat(true); + l->addWidget(m_button); + + connect(m_button, SIGNAL(clicked()), this, SLOT(reset())); + connect(m_editor, SIGNAL(textChanged(QString)), this, SLOT(checkButton(QString))); + connect(m_editor, SIGNAL(textEdited(QString)), this, SIGNAL(filterChanged(QString))); +} + +QString FilterWidget::text() const +{ + return m_editor->typedText(); +} + +void FilterWidget::checkButton(const QString &) +{ + m_button->setEnabled(!text().isEmpty()); +} + +void FilterWidget::reset() +{ + if (debugFilter) + qDebug() << Q_FUNC_INFO; + if (!text().isEmpty()) { + // Editor has lost focus once this is pressed + m_editor->showHintText(true); + emit filterChanged(QString()); + } +} + +bool FilterWidget::refuseFocus() const +{ + return m_editor->refuseFocus(); +} + +void FilterWidget::setRefuseFocus(bool v) +{ + m_editor->setRefuseFocus(v); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/filterwidget_p.h b/tools/designer/src/lib/shared/filterwidget_p.h new file mode 100644 index 0000000..3d1f64e --- /dev/null +++ b/tools/designer/src/lib/shared/filterwidget_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef FILTERWIDGET_H +#define FILTERWIDGET_H + +#include "shared_global_p.h" + +#include <QtGui/QWidget> +#include <QtGui/QLineEdit> +#include <QtGui/QColor> + +QT_BEGIN_NAMESPACE + +class QPushButton; + +namespace qdesigner_internal { + +/* A line edit that displays a grayed hintText (like "Type Here to Filter") + * when not focussed and empty. When connecting to the changed signals and + * querying text, one has to be aware that the text is set to that hint + * text if isShowingHintText() returns true (that is, does not contain + * valid user input). This widget should never have initial focus + * (ie, be the first widget of a dialog, else, the hint cannot be displayed. + * For situations, where it is the only focusable control (widget box), + * there is a special "refuseFocus()" mode, in which it clears the focus + * policy and focusses explicitly on click (note that setting Qt::ClickFocus + * is not sufficient for that as an ActivationFocus will occur). */ + +class QDESIGNER_SHARED_EXPORT HintLineEdit : public QLineEdit { + Q_OBJECT +public: + explicit HintLineEdit(QWidget *parent = 0); + + QString hintText() const; + + bool isShowingHintText() const; + + // Convenience for accessing the text that returns "" in case of isShowingHintText(). + QString typedText() const; + + bool refuseFocus() const; + void setRefuseFocus(bool v); + +public slots: + void setHintText(const QString &ht); + void showHintText(bool force = false); + void hideHintText(); + +protected: + virtual void mousePressEvent(QMouseEvent *event); + virtual void focusInEvent(QFocusEvent *e); + virtual void focusOutEvent(QFocusEvent *e); + +private: + void setTextColor(const QColor &newColor, QColor *oldColor = 0); + + const Qt::FocusPolicy m_defaultFocusPolicy; + const QColor m_hintColor; + QColor m_textColor; + bool m_refuseFocus; + QString m_hintText; + bool m_showingHintText; +}; + +// FilterWidget: For filtering item views, with reset button Uses HintLineEdit. + +class QDESIGNER_SHARED_EXPORT FilterWidget : public QWidget +{ + Q_OBJECT +public: + enum LayoutMode { + // For use in toolbars: Expand to the right + LayoutAlignRight, + // No special alignment + LayoutAlignNone + }; + + explicit FilterWidget(QWidget *parent = 0, LayoutMode lm = LayoutAlignRight); + + QString text() const; + + bool refuseFocus() const; // see HintLineEdit + void setRefuseFocus(bool v); + +signals: + void filterChanged(const QString &); + +public slots: + void reset(); + +private slots: + void checkButton(const QString &text); + +private: + QPushButton *m_button; + HintLineEdit *m_editor; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/formlayoutmenu.cpp b/tools/designer/src/lib/shared/formlayoutmenu.cpp new file mode 100644 index 0000000..c2966a1 --- /dev/null +++ b/tools/designer/src/lib/shared/formlayoutmenu.cpp @@ -0,0 +1,534 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "formlayoutmenu_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_propertycommand_p.h" +#include "ui_formlayoutrowdialog.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QDesignerLanguageExtension> + +#include <QtGui/QAction> +#include <QtGui/QWidget> +#include <QtGui/QFormLayout> +#include <QtGui/QUndoStack> +#include <QtGui/QDialog> +#include <QtGui/QPushButton> +#include <QtGui/QRegExpValidator> + +#include <QtCore/QPair> +#include <QtCore/QCoreApplication> +#include <QtCore/QRegExp> +#include <QtCore/QMultiHash> +#include <QtCore/QDebug> + +static const char *buddyPropertyC = "buddy"; +static const char *fieldWidgetBaseClasses[] = { + "QLineEdit", "QComboBox", "QSpinBox", "QDoubleSpinBox", "QCheckBox", + "QDateEdit", "QTimeEdit", "QDateTimeEdit", "QDial", "QWidget" +}; + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Struct that describes a row of controls (descriptive label and control) to +// be added to a form layout. +struct FormLayoutRow { + FormLayoutRow() : buddy(false) {} + + QString labelName; + QString labelText; + QString fieldClassName; + QString fieldName; + bool buddy; +}; + +// A Dialog to edit a FormLayoutRow. Lets the user input a label text, label +// name, field widget type, field object name and buddy setting. As the +// user types the label text; the object names to be used for label and field +// are updated. It also checks the buddy setting depending on whether the +// label text contains a buddy marker. +class FormLayoutRowDialog : public QDialog { + Q_DISABLE_COPY(FormLayoutRowDialog) + Q_OBJECT +public: + explicit FormLayoutRowDialog(QDesignerFormEditorInterface *core, + QWidget *parent); + + FormLayoutRow formLayoutRow() const; + + bool buddy() const; + void setBuddy(bool); + + // Accessors for form layout row numbers using 0..[n-1] convention + int row() const; + void setRow(int); + void setRowRange(int, int); + + QString fieldClass() const; + QString labelText() const; + + static QStringList fieldWidgetClasses(QDesignerFormEditorInterface *core); + +private slots: + void labelTextEdited(const QString &text); + void labelNameEdited(const QString &text); + void fieldNameEdited(const QString &text); + void buddyClicked(); + void fieldClassChanged(int); + +private: + bool isValid() const; + void updateObjectNames(bool updateLabel, bool updateField); + void updateOkButton(); + + // Check for buddy marker in string + const QRegExp m_buddyMarkerRegexp; + + Ui::FormLayoutRowDialog m_ui; + bool m_labelNameEdited; + bool m_fieldNameEdited; + bool m_buddyClicked; +}; + +FormLayoutRowDialog::FormLayoutRowDialog(QDesignerFormEditorInterface *core, + QWidget *parent) : + QDialog(parent), + m_buddyMarkerRegexp(QLatin1String("\\&[^&]")), + m_labelNameEdited(false), + m_fieldNameEdited(false), + m_buddyClicked(false) +{ + Q_ASSERT(m_buddyMarkerRegexp.isValid()); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + m_ui.setupUi(this); + connect(m_ui.labelTextLineEdit, SIGNAL(textEdited(QString)), this, SLOT(labelTextEdited(QString))); + + QRegExpValidator *nameValidator = new QRegExpValidator(QRegExp(QLatin1String("^[a-zA-Z0-9_]+$")), this); + Q_ASSERT(nameValidator->regExp().isValid()); + + m_ui.labelNameLineEdit->setValidator(nameValidator); + connect(m_ui.labelNameLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(labelNameEdited(QString))); + + m_ui.fieldNameLineEdit->setValidator(nameValidator); + connect(m_ui.fieldNameLineEdit, SIGNAL(textEdited(QString)), + this, SLOT(fieldNameEdited(QString))); + + connect(m_ui.buddyCheckBox, SIGNAL(clicked()), this, SLOT(buddyClicked())); + + m_ui.fieldClassComboBox->addItems(fieldWidgetClasses(core)); + m_ui.fieldClassComboBox->setCurrentIndex(0); + connect(m_ui.fieldClassComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(fieldClassChanged(int))); + + updateOkButton(); +} + +FormLayoutRow FormLayoutRowDialog::formLayoutRow() const +{ + FormLayoutRow rc; + rc.labelText = labelText(); + rc.labelName = m_ui.labelNameLineEdit->text(); + rc.fieldClassName = fieldClass(); + rc.fieldName = m_ui.fieldNameLineEdit->text(); + rc.buddy = buddy(); + return rc; +} + +bool FormLayoutRowDialog::buddy() const +{ + return m_ui.buddyCheckBox->checkState() == Qt::Checked; +} + +void FormLayoutRowDialog::setBuddy(bool b) +{ + m_ui.buddyCheckBox->setCheckState(b ? Qt::Checked : Qt::Unchecked); +} + +// Convert rows to 1..n convention for users +int FormLayoutRowDialog::row() const +{ + return m_ui.rowSpinBox->value() - 1; +} + +void FormLayoutRowDialog::setRow(int row) +{ + m_ui.rowSpinBox->setValue(row + 1); +} + +void FormLayoutRowDialog::setRowRange(int from, int to) +{ + m_ui.rowSpinBox->setMinimum(from + 1); + m_ui.rowSpinBox->setMaximum(to + 1); + m_ui.rowSpinBox->setEnabled(to - from > 0); +} + +QString FormLayoutRowDialog::fieldClass() const +{ + return m_ui.fieldClassComboBox->itemText(m_ui.fieldClassComboBox->currentIndex()); +} + +QString FormLayoutRowDialog::labelText() const +{ + return m_ui.labelTextLineEdit->text(); +} + +bool FormLayoutRowDialog::isValid() const +{ + // Check for non-empty names and presence of buddy marker if checked + const QString name = labelText(); + if (name.isEmpty() || m_ui.labelNameLineEdit->text().isEmpty() || m_ui.fieldNameLineEdit->text().isEmpty()) + return false; + if (buddy() && !name.contains(m_buddyMarkerRegexp)) + return false; + return true; +} + +void FormLayoutRowDialog::updateOkButton() +{ + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(isValid()); +} + +void FormLayoutRowDialog::labelTextEdited(const QString &text) +{ + updateObjectNames(true, true); + // Set buddy if '&' is present unless the user changed it + if (!m_buddyClicked) + setBuddy(text.contains(m_buddyMarkerRegexp)); + + updateOkButton(); +} + +// Get a suitable object name postfix from a class name: +// "namespace::QLineEdit"->"LineEdit" +static inline QString postFixFromClassName(QString className) +{ + const int index = className.lastIndexOf(QLatin1String("::")); + if (index != -1) + className.remove(0, index + 2); + if (className.size() > 2) + if (className.at(0) == QLatin1Char('Q') || className.at(0) == QLatin1Char('K')) + if (className.at(1).isUpper()) + className.remove(0, 1); + return className; +} + +// Helper routines to filter out characters for converting texts into +// class name prefixes. Only accepts ASCII characters/digits and underscores. + +enum PrefixCharacterKind { PC_Digit, PC_UpperCaseLetter, PC_LowerCaseLetter, + PC_Other, PC_Invalid }; + +static inline PrefixCharacterKind prefixCharacterKind(const QChar &c) +{ + switch (c.category()) { + case QChar::Number_DecimalDigit: + return PC_Digit; + case QChar::Letter_Lowercase: { + const char a = c.toAscii(); + if (a >= 'a' && a <= 'z') + return PC_LowerCaseLetter; + } + break; + case QChar::Letter_Uppercase: { + const char a = c.toAscii(); + if (a >= 'A' && a <= 'Z') + return PC_UpperCaseLetter; + } + break; + case QChar::Punctuation_Connector: + if (c.toAscii() == '_') + return PC_Other; + break; + default: + break; + } + return PC_Invalid; +} + +// Convert the text the user types into a usable class name prefix by filtering +// characters, lower-casing the first character and camel-casing subsequent +// words. ("zip code:") --> ("zipCode"). + +static QString prefixFromLabel(const QString &prefix) +{ + QString rc; + const int length = prefix.size(); + bool lastWasAcceptable = false; + for (int i = 0 ; i < length; i++) { + const QChar c = prefix.at(i); + const PrefixCharacterKind kind = prefixCharacterKind(c); + const bool acceptable = kind != PC_Invalid; + if (acceptable) { + if (rc.isEmpty()) { + // Lower-case first character + rc += kind == PC_UpperCaseLetter ? c.toLower() : c; + } else { + // Camel-case words + rc += !lastWasAcceptable && kind == PC_LowerCaseLetter ? c.toUpper() : c; + } + } + lastWasAcceptable = acceptable; + } + return rc; +} + +void FormLayoutRowDialog::updateObjectNames(bool updateLabel, bool updateField) +{ + // Generate label + field object names from the label text, that is, + // "&Zip code:" -> "zipcodeLabel", "zipcodeLineEdit" unless the user + // edited it. + const bool doUpdateLabel = !m_labelNameEdited && updateLabel; + const bool doUpdateField = !m_fieldNameEdited && updateField; + if (!doUpdateLabel && !doUpdateField) + return; + + const QString prefix = prefixFromLabel(labelText()); + // Set names + if (doUpdateLabel) + m_ui.labelNameLineEdit->setText(prefix + QLatin1String("Label")); + if (doUpdateField) + m_ui.fieldNameLineEdit->setText(prefix + postFixFromClassName(fieldClass())); +} + +void FormLayoutRowDialog::fieldClassChanged(int) +{ + updateObjectNames(false, true); +} + +void FormLayoutRowDialog::labelNameEdited(const QString & /*text*/) +{ + m_labelNameEdited = true; // stop auto-updating after user change + updateOkButton(); +} + +void FormLayoutRowDialog::fieldNameEdited(const QString & /*text*/) +{ + m_fieldNameEdited = true; // stop auto-updating after user change + updateOkButton(); +} + +void FormLayoutRowDialog::buddyClicked() +{ + m_buddyClicked = true; // stop auto-updating after user change + updateOkButton(); +} + +/* Create a list of classes suitable for field widgets. Take the fixed base + * classes provided and look in the widget database for custom widgets derived + * from them ("QLineEdit", "CustomLineEdit", "QComboBox"...). */ +QStringList FormLayoutRowDialog::fieldWidgetClasses(QDesignerFormEditorInterface *core) +{ + // Base class -> custom widgets map + typedef QMultiHash<QString, QString> ClassMap; + + static QStringList rc; + if (rc.empty()) { + const int fwCount = sizeof(fieldWidgetBaseClasses)/sizeof(const char*); + // Turn known base classes into list + QStringList baseClasses; + for (int i = 0; i < fwCount; i++) + baseClasses.push_back(QLatin1String(fieldWidgetBaseClasses[i])); + // Scan for custom widgets that inherit them and store them in a + // multimap of base class->custom widgets unless we have a language + // extension installed which might do funny things with custom widgets. + ClassMap customClassMap; + if (qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core) == 0) { + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int wdbCount = wdb->count(); + for (int w = 0; w < wdbCount; ++w) { + // Check for non-container custom types that extend the + // respective base class. + const QDesignerWidgetDataBaseItemInterface *dbItem = wdb->item(w); + if (!dbItem->isPromoted() && !dbItem->isContainer() && dbItem->isCustom()) { + const int index = baseClasses.indexOf(dbItem->extends()); + if (index != -1) + customClassMap.insert(baseClasses.at(index), dbItem->name()); + } + } + } + // Compile final list, taking each base class and append custom widgets + // based on it. + for (int i = 0; i < fwCount; i++) { + rc.push_back(baseClasses.at(i)); + rc += customClassMap.values(baseClasses.at(i)); + } + } + return rc; +} + +// ------------------ Utilities + +static QFormLayout *managedFormLayout(const QDesignerFormEditorInterface *core, const QWidget *w) +{ + QLayout *l = 0; + if (LayoutInfo::managedLayoutType(core, w, &l) == LayoutInfo::Form) + return qobject_cast<QFormLayout *>(l); + return 0; +} + +// Create the widgets of a control row and apply text properties contained +// in the struct, called by addFormLayoutRow() +static QPair<QWidget *,QWidget *> + createWidgets(const FormLayoutRow &row, QWidget *parent, + QDesignerFormWindowInterface *formWindow) +{ + QDesignerFormEditorInterface *core = formWindow->core(); + QDesignerWidgetFactoryInterface *wf = core->widgetFactory(); + + QPair<QWidget *,QWidget *> rc = QPair<QWidget *,QWidget *>(wf->createWidget(QLatin1String("QLabel"), parent), + wf->createWidget(row.fieldClassName, parent)); + // Set up properties of the label + const QString objectNameProperty = QLatin1String("objectName"); + QDesignerPropertySheetExtension *labelSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), rc.first); + int nameIndex = labelSheet->indexOf(objectNameProperty); + labelSheet->setProperty(nameIndex, qVariantFromValue(PropertySheetStringValue(row.labelName))); + labelSheet->setChanged(nameIndex, true); + formWindow->ensureUniqueObjectName(rc.first); + const int textIndex = labelSheet->indexOf(QLatin1String("text")); + labelSheet->setProperty(textIndex, qVariantFromValue(PropertySheetStringValue(row.labelText))); + labelSheet->setChanged(textIndex, true); + // Set up properties of the control + QDesignerPropertySheetExtension *controlSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), rc.second); + nameIndex = controlSheet->indexOf(objectNameProperty); + controlSheet->setProperty(nameIndex, qVariantFromValue(PropertySheetStringValue(row.fieldName))); + controlSheet->setChanged(nameIndex, true); + formWindow->ensureUniqueObjectName(rc.second); + return rc; +} + +// Create a command sequence on the undo stack of the form window that creates +// the widgets of the row and inserts them into the form layout. +static void addFormLayoutRow(const FormLayoutRow &formLayoutRow, int row, QWidget *w, + QDesignerFormWindowInterface *formWindow) +{ + QFormLayout *formLayout = managedFormLayout(formWindow->core(), w); + Q_ASSERT(formLayout); + QUndoStack *undoStack = formWindow->commandHistory(); + const QString macroName = QCoreApplication::translate("Command", "Add '%1' to '%2'").arg(formLayoutRow.labelText, formLayout->objectName()); + undoStack->beginMacro(macroName); + + // Create a list of widget insertion commands and pass them a cell position + const QPair<QWidget *,QWidget *> widgetPair = createWidgets(formLayoutRow, w, formWindow); + + InsertWidgetCommand *labelCmd = new InsertWidgetCommand(formWindow); + labelCmd->init(widgetPair.first, false, row, 0); + undoStack->push(labelCmd); + InsertWidgetCommand *controlCmd = new InsertWidgetCommand(formWindow); + controlCmd->init(widgetPair.second, false, row, 1); + undoStack->push(controlCmd); + if (formLayoutRow.buddy) { + SetPropertyCommand *buddyCommand = new SetPropertyCommand(formWindow); + buddyCommand->init(widgetPair.first, QLatin1String(buddyPropertyC), widgetPair.second->objectName()); + undoStack->push(buddyCommand); + } + undoStack->endMacro(); +} + +// ---------------- FormLayoutMenu +FormLayoutMenu::FormLayoutMenu(QObject *parent) : + QObject(parent), + m_separator1(new QAction(this)), + m_populateFormAction(new QAction(tr("Add form layout row..."), this)), + m_separator2(new QAction(this)) +{ + m_separator1->setSeparator(true); + connect(m_populateFormAction, SIGNAL(triggered()), this, SLOT(slotAddRow())); + m_separator2->setSeparator(true); +} + +void FormLayoutMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList &actions) +{ + switch (LayoutInfo::managedLayoutType(fw->core(), w)) { + case LayoutInfo::Form: + if (!actions.empty() && !actions.back()->isSeparator()) + actions.push_back(m_separator1); + actions.push_back(m_populateFormAction); + actions.push_back(m_separator2); + m_widget = w; + break; + default: + m_widget = 0; + break; + } +} + +void FormLayoutMenu::slotAddRow() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_widget); + Q_ASSERT(m_widget && fw); + const int rowCount = managedFormLayout(fw->core(), m_widget)->rowCount(); + + FormLayoutRowDialog dialog(fw->core(), fw); + dialog.setRowRange(0, rowCount); + dialog.setRow(rowCount); + + if (dialog.exec() != QDialog::Accepted) + return; + addFormLayoutRow(dialog.formLayoutRow(), dialog.row(), m_widget, fw); +} + +QAction *FormLayoutMenu::preferredEditAction(QWidget *w, QDesignerFormWindowInterface *fw) +{ + if (LayoutInfo::managedLayoutType(fw->core(), w) == LayoutInfo::Form) { + m_widget = w; + return m_populateFormAction; + } + return 0; +} +} + +QT_END_NAMESPACE + +#include "formlayoutmenu.moc" + diff --git a/tools/designer/src/lib/shared/formlayoutmenu_p.h b/tools/designer/src/lib/shared/formlayoutmenu_p.h new file mode 100644 index 0000000..a575468 --- /dev/null +++ b/tools/designer/src/lib/shared/formlayoutmenu_p.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef FORMLAYOUTMENU +#define FORMLAYOUTMENU + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#include "shared_global_p.h" +#include <QtCore/QObject> +#include <QtCore/QList> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QAction; +class QWidget; + +namespace qdesigner_internal { + +// Task menu to be used for form layouts. Offers an options "Add row" which +// pops up a dialog in which the user can specify label name, text and buddy. +class QDESIGNER_SHARED_EXPORT FormLayoutMenu : public QObject +{ + Q_DISABLE_COPY(FormLayoutMenu) + Q_OBJECT +public: + typedef QList<QAction *> ActionList; + + explicit FormLayoutMenu(QObject *parent); + + // Populate a list of actions with the form layout actions. + void populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList &actions); + // For implementing QDesignerTaskMenuExtension::preferredEditAction(): + // Return appropriate action for double clicking. + QAction *preferredEditAction(QWidget *w, QDesignerFormWindowInterface *fw); + +private slots: + void slotAddRow(); + +private: + QAction *m_separator1; + QAction *m_populateFormAction; + QAction *m_separator2; + QPointer<QWidget> m_widget; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMLAYOUTMENU diff --git a/tools/designer/src/lib/shared/formlayoutrowdialog.ui b/tools/designer/src/lib/shared/formlayoutrowdialog.ui new file mode 100644 index 0000000..c0e0cfe --- /dev/null +++ b/tools/designer/src/lib/shared/formlayoutrowdialog.ui @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>FormLayoutRowDialog</class> + <widget class="QDialog" name="FormLayoutRowDialog"> + <property name="windowTitle"> + <string>Add Form Layout Row</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <property name="fieldGrowthPolicy"> + <enum>QFormLayout::ExpandingFieldsGrow</enum> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="labelTextLabel"> + <property name="text"> + <string>&Label text:</string> + </property> + <property name="buddy"> + <cstring>labelTextLineEdit</cstring> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="labelTextLineEdit"> + <property name="minimumSize"> + <size> + <width>180</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="labelNameLineEdit"/> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="fieldClassLabel"> + <property name="text"> + <string>Field &type:</string> + </property> + <property name="buddy"> + <cstring>fieldClassComboBox</cstring> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="fieldClassComboBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="fieldNameLabel"> + <property name="text"> + <string>&Field name:</string> + </property> + <property name="buddy"> + <cstring>fieldNameLineEdit</cstring> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="buddyLabel"> + <property name="text"> + <string>&Buddy:</string> + </property> + <property name="buddy"> + <cstring>buddyCheckBox</cstring> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="buddyCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="rowLabel"> + <property name="text"> + <string>&Row:</string> + </property> + <property name="buddy"> + <cstring>rowSpinBox</cstring> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QSpinBox" name="rowSpinBox"/> + </item> + <item row="3" column="1"> + <widget class="QLineEdit" name="fieldNameLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="labelNameLabel"> + <property name="text"> + <string>Label &name:</string> + </property> + <property name="buddy"> + <cstring>labelNameLineEdit</cstring> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>FormLayoutRowDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>FormLayoutRowDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/formwindowbase.cpp b/tools/designer/src/lib/shared/formwindowbase.cpp new file mode 100644 index 0000000..3e7e17b --- /dev/null +++ b/tools/designer/src/lib/shared/formwindowbase.cpp @@ -0,0 +1,487 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::FormWindowBase +*/ + +#include "formwindowbase_p.h" +#include "connectionedit_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "shared_settings_p.h" +#include "grid_p.h" +#include "deviceprofile_p.h" +#include "qdesigner_utils_p.h" + +#include <abstractformbuilder.h> + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerTaskMenuExtension> + +#include <QtCore/qdebug.h> +#include <QtCore/QList> +#include <QtCore/QTimer> +#include <QtGui/QMenu> +#include <QtGui/QListWidget> +#include <QtGui/QTreeWidget> +#include <QtGui/QTableWidget> +#include <QtGui/QComboBox> +#include <QtGui/QTabWidget> +#include <QtGui/QToolBox> +#include <QtGui/QToolBar> +#include <QtGui/QStatusBar> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class FormWindowBasePrivate { +public: + explicit FormWindowBasePrivate(QDesignerFormEditorInterface *core); + + static Grid m_defaultGrid; + + QDesignerFormWindowInterface::Feature m_feature; + Grid m_grid; + bool m_hasFormGrid; + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + QtResourceSet *m_resourceSet; + QMap<QDesignerPropertySheet *, QMap<int, bool> > m_reloadableResources; // bool is dummy, QMap used as QSet + QMap<QDesignerPropertySheet *, QObject *> m_reloadablePropertySheets; + const DeviceProfile m_deviceProfile; + FormWindowBase::LineTerminatorMode m_lineTerminatorMode; + FormWindowBase::SaveResourcesBehaviour m_saveResourcesBehaviour; +}; + +FormWindowBasePrivate::FormWindowBasePrivate(QDesignerFormEditorInterface *core) : + m_feature(QDesignerFormWindowInterface::DefaultFeature), + m_grid(m_defaultGrid), + m_hasFormGrid(false), + m_pixmapCache(0), + m_iconCache(0), + m_resourceSet(0), + m_deviceProfile(QDesignerSharedSettings(core).currentDeviceProfile()), + m_lineTerminatorMode(FormWindowBase::NativeLineTerminator), + m_saveResourcesBehaviour(FormWindowBase::SaveAll) +{ +} + +Grid FormWindowBasePrivate::m_defaultGrid; + +FormWindowBase::FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent, Qt::WindowFlags flags) : + QDesignerFormWindowInterface(parent, flags), + m_d(new FormWindowBasePrivate(core)) +{ + syncGridFeature(); + m_d->m_pixmapCache = new DesignerPixmapCache(this); + m_d->m_iconCache = new DesignerIconCache(m_d->m_pixmapCache, this); +} + +FormWindowBase::~FormWindowBase() +{ + delete m_d; +} + +DesignerPixmapCache *FormWindowBase::pixmapCache() const +{ + return m_d->m_pixmapCache; +} + +DesignerIconCache *FormWindowBase::iconCache() const +{ + return m_d->m_iconCache; +} + +QtResourceSet *FormWindowBase::resourceSet() const +{ + return m_d->m_resourceSet; +} + +void FormWindowBase::setResourceSet(QtResourceSet *resourceSet) +{ + m_d->m_resourceSet = resourceSet; +} + +void FormWindowBase::addReloadableProperty(QDesignerPropertySheet *sheet, int index) +{ + m_d->m_reloadableResources[sheet][index] = true; +} + +void FormWindowBase::removeReloadableProperty(QDesignerPropertySheet *sheet, int index) +{ + m_d->m_reloadableResources[sheet].remove(index); + if (m_d->m_reloadableResources[sheet].count() == 0) + m_d->m_reloadableResources.remove(sheet); +} + +void FormWindowBase::addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object) +{ + if (qobject_cast<QTreeWidget *>(object) || + qobject_cast<QTableWidget *>(object) || + qobject_cast<QListWidget *>(object) || + qobject_cast<QComboBox *>(object)) + m_d->m_reloadablePropertySheets[sheet] = object; +} + +void FormWindowBase::removeReloadablePropertySheet(QDesignerPropertySheet *sheet) +{ + m_d->m_reloadablePropertySheets.remove(sheet); +} + +void FormWindowBase::reloadProperties() +{ + pixmapCache()->clear(); + iconCache()->clear(); + QMapIterator<QDesignerPropertySheet *, QMap<int, bool> > itSheet(m_d->m_reloadableResources); + while (itSheet.hasNext()) { + QDesignerPropertySheet *sheet = itSheet.next().key(); + QMapIterator<int, bool> itIndex(itSheet.value()); + while (itIndex.hasNext()) { + const int index = itIndex.next().key(); + sheet->setProperty(index, sheet->property(index)); + } + if (QTabWidget *tabWidget = qobject_cast<QTabWidget *>(sheet->object())) { + const int count = tabWidget->count(); + const int current = tabWidget->currentIndex(); + const QString currentTabIcon = QLatin1String("currentTabIcon"); + for (int i = 0; i < count; i++) { + tabWidget->setCurrentIndex(i); + const int index = sheet->indexOf(currentTabIcon); + sheet->setProperty(index, sheet->property(index)); + } + tabWidget->setCurrentIndex(current); + } else if (QToolBox *toolBox = qobject_cast<QToolBox *>(sheet->object())) { + const int count = toolBox->count(); + const int current = toolBox->currentIndex(); + const QString currentItemIcon = QLatin1String("currentItemIcon"); + for (int i = 0; i < count; i++) { + toolBox->setCurrentIndex(i); + const int index = sheet->indexOf(currentItemIcon); + sheet->setProperty(index, sheet->property(index)); + } + toolBox->setCurrentIndex(current); + } + } + QMapIterator<QDesignerPropertySheet *, QObject *> itSh(m_d->m_reloadablePropertySheets); + while (itSh.hasNext()) { + QObject *object = itSh.next().value(); + reloadIconResources(iconCache(), object); + } +} + +void FormWindowBase::resourceSetActivated(QtResourceSet *resource, bool resourceSetChanged) +{ + if (resource == resourceSet() && resourceSetChanged) { + reloadProperties(); + emit pixmapCache()->reloaded(); + emit iconCache()->reloaded(); + if (QDesignerPropertyEditor *propertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor())) + propertyEditor->reloadResourceProperties(); + } +} + +QVariantMap FormWindowBase::formData() +{ + QVariantMap rc; + if (m_d->m_hasFormGrid) + m_d->m_grid.addToVariantMap(rc, true); + return rc; +} + +void FormWindowBase::setFormData(const QVariantMap &vm) +{ + Grid formGrid; + m_d->m_hasFormGrid = formGrid.fromVariantMap(vm); + if (m_d->m_hasFormGrid) + m_d->m_grid = formGrid; +} + +QPoint FormWindowBase::grid() const +{ + return QPoint(m_d->m_grid.deltaX(), m_d->m_grid.deltaY()); +} + +void FormWindowBase::setGrid(const QPoint &grid) +{ + m_d->m_grid.setDeltaX(grid.x()); + m_d->m_grid.setDeltaY(grid.y()); +} + +bool FormWindowBase::hasFeature(Feature f) const +{ + return f & m_d->m_feature; +} + +static void recursiveUpdate(QWidget *w) +{ + w->update(); + + const QObjectList &l = w->children(); + const QObjectList::const_iterator cend = l.constEnd(); + for (QObjectList::const_iterator it = l.constBegin(); it != cend; ++it) { + if (QWidget *w = qobject_cast<QWidget*>(*it)) + recursiveUpdate(w); + } +} + +void FormWindowBase::setFeatures(Feature f) +{ + m_d->m_feature = f; + const bool enableGrid = f & GridFeature; + m_d->m_grid.setVisible(enableGrid); + m_d->m_grid.setSnapX(enableGrid); + m_d->m_grid.setSnapY(enableGrid); + emit featureChanged(f); + recursiveUpdate(this); +} + +FormWindowBase::Feature FormWindowBase::features() const +{ + return m_d->m_feature; +} + +bool FormWindowBase::gridVisible() const +{ + return m_d->m_grid.visible() && currentTool() == 0; +} + +FormWindowBase::SaveResourcesBehaviour FormWindowBase::saveResourcesBehaviour() const +{ + return m_d->m_saveResourcesBehaviour; +} + +void FormWindowBase::setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour) +{ + m_d->m_saveResourcesBehaviour = behaviour; +} + +void FormWindowBase::syncGridFeature() +{ + if (m_d->m_grid.snapX() || m_d->m_grid.snapY()) + m_d->m_feature |= GridFeature; + else + m_d->m_feature &= ~GridFeature; +} + +void FormWindowBase::setDesignerGrid(const Grid& grid) +{ + m_d->m_grid = grid; + syncGridFeature(); + recursiveUpdate(this); +} + +const Grid &FormWindowBase::designerGrid() const +{ + return m_d->m_grid; +} + +bool FormWindowBase::hasFormGrid() const +{ + return m_d->m_hasFormGrid; +} + +void FormWindowBase::setHasFormGrid(bool b) +{ + m_d->m_hasFormGrid = b; +} + +void FormWindowBase::setDefaultDesignerGrid(const Grid& grid) +{ + FormWindowBasePrivate::m_defaultGrid = grid; +} + +const Grid &FormWindowBase::defaultDesignerGrid() +{ + return FormWindowBasePrivate::m_defaultGrid; +} + +QMenu *FormWindowBase::initializePopupMenu(QWidget * /*managedWidget*/) +{ + return 0; +} + +// Widget under mouse for finding the Widget to highlight +// when doing DnD. Restricts to pages by geometry if a container with +// a container extension (or one of its helper widgets) is hit; otherwise +// returns the widget as such (be it managed/unmanaged) + +QWidget *FormWindowBase::widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode /* wum */) +{ + // widget_under_mouse might be some temporary thing like the dropLine. We need + // the actual widget that's part of the edited GUI. + QWidget *rc = widgetAt(formPos); + if (!rc || qobject_cast<ConnectionEdit*>(rc)) + return 0; + + if (rc == mainContainer()) { + // Refuse main container areas if the main container has a container extension, + // for example when hitting QToolBox/QTabWidget empty areas. + if (qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), rc)) + return 0; + return rc; + } + + // If we hit on container extension type container, make sure + // we use the top-most current page + if (QWidget *container = findContainer(rc, false)) + if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), container)) { + // For container that do not have a "stacked" nature (QToolBox, QMdiArea), + // make sure the position is within the current page + const int ci = c->currentIndex(); + if (ci < 0) + return 0; + QWidget *page = c->widget(ci); + QRect pageGeometry = page->geometry(); + pageGeometry.moveTo(page->mapTo(this, pageGeometry.topLeft())); + if (!pageGeometry.contains(formPos)) + return 0; + return page; + } + + return rc; +} + +void FormWindowBase::deleteWidgetList(const QWidgetList &widget_list) +{ + // We need a macro here even for single widgets because the some components (for example, + // the signal slot editor are connected to widgetRemoved() and add their + // own commands (for example, to delete w's connections) + const QString description = widget_list.size() == 1 ? + tr("Delete '%1'").arg(widget_list.front()->objectName()) : tr("Delete"); + + commandHistory()->beginMacro(description); + foreach (QWidget *w, widget_list) { + emit widgetRemoved(w); + DeleteWidgetCommand *cmd = new DeleteWidgetCommand(this); + cmd->init(w); + commandHistory()->push(cmd); + } + commandHistory()->endMacro(); +} + +QMenu *FormWindowBase::createExtensionTaskMenu(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator) +{ + typedef QList<QAction *> ActionList; + ActionList actions; + // 1) Standard public extension + QExtensionManager *em = fw->core()->extensionManager(); + if (const QDesignerTaskMenuExtension *extTaskMenu = qt_extension<QDesignerTaskMenuExtension*>(em, o)) + actions += extTaskMenu->taskActions(); + if (const QDesignerTaskMenuExtension *intTaskMenu = qobject_cast<QDesignerTaskMenuExtension *>(em->extension(o, QLatin1String("QDesignerInternalTaskMenuExtension")))) { + if (!actions.empty()) { + QAction *a = new QAction(fw); + a->setSeparator(true); + actions.push_back(a); + } + actions += intTaskMenu->taskActions(); + } + if (actions.empty()) + return 0; + if (trailingSeparator && !actions.back()->isSeparator()) { + QAction *a = new QAction(fw); + a->setSeparator(true); + actions.push_back(a); + } + QMenu *rc = new QMenu; + const ActionList::const_iterator cend = actions.constEnd(); + for (ActionList::const_iterator it = actions.constBegin(); it != cend; ++it) + rc->addAction(*it); + return rc; +} + +void FormWindowBase::emitObjectRemoved(QObject *o) +{ + emit objectRemoved(o); +} + +DeviceProfile FormWindowBase::deviceProfile() const +{ + return m_d->m_deviceProfile; +} + +QString FormWindowBase::styleName() const +{ + return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.style(); +} + +void FormWindowBase::emitWidgetRemoved(QWidget *w) +{ + emit widgetRemoved(w); +} + +QString FormWindowBase::deviceProfileName() const +{ + return m_d->m_deviceProfile.isEmpty() ? QString() : m_d->m_deviceProfile.name(); +} + +void FormWindowBase::setLineTerminatorMode(FormWindowBase::LineTerminatorMode mode) +{ + m_d->m_lineTerminatorMode = mode; +} + +FormWindowBase::LineTerminatorMode FormWindowBase::lineTerminatorMode() const +{ + return m_d->m_lineTerminatorMode; +} + +void FormWindowBase::triggerDefaultAction(QWidget *widget) +{ + if (QAction *action = qdesigner_internal::preferredEditAction(core(), widget)) + QTimer::singleShot(0, action, SIGNAL(triggered())); +} + +void FormWindowBase::setupDefaultAction(QDesignerFormWindowInterface *fw) +{ + QObject::connect(fw, SIGNAL(activated(QWidget*)), fw, SLOT(triggerDefaultAction(QWidget*))); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/formwindowbase_p.h b/tools/designer/src/lib/shared/formwindowbase_p.h new file mode 100644 index 0000000..68e977e --- /dev/null +++ b/tools/designer/src/lib/shared/formwindowbase_p.h @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef FORMWINDOWBASE_H +#define FORMWINDOWBASE_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtCore/QVariantMap> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QDesignerDnDItemInterface; +class QMenu; +class QtResourceSet; +class QDesignerPropertySheet; + +namespace qdesigner_internal { + +class QEditorFormBuilder; +class DeviceProfile; +class Grid; + +class DesignerPixmapCache; +class DesignerIconCache; +class FormWindowBasePrivate; + +class QDESIGNER_SHARED_EXPORT FormWindowBase: public QDesignerFormWindowInterface +{ + Q_OBJECT +public: + enum HighlightMode { Restore, Highlight }; + enum SaveResourcesBehaviour { SaveAll, SaveOnlyUsedQrcFiles, DontSaveQrcFiles }; + + explicit FormWindowBase(QDesignerFormEditorInterface *core, QWidget *parent = 0, Qt::WindowFlags flags = 0); + virtual ~FormWindowBase(); + + QVariantMap formData(); + void setFormData(const QVariantMap &vm); + + // Return the widget containing the form. This is used to + // apply embedded design settings to that are inherited (for example font). + // These are meant to be applied to the form only and not to the other editors + // in the widget stack. + virtual QWidget *formContainer() const = 0; + + // Deprecated + virtual QPoint grid() const; + + // Deprecated + virtual void setGrid(const QPoint &grid); + + virtual bool hasFeature(Feature f) const; + virtual Feature features() const; + virtual void setFeatures(Feature f); + + const Grid &designerGrid() const; + void setDesignerGrid(const Grid& grid); + + bool hasFormGrid() const; + void setHasFormGrid(bool b); + + bool gridVisible() const; + + SaveResourcesBehaviour saveResourcesBehaviour() const; + void setSaveResourcesBehaviour(SaveResourcesBehaviour behaviour); + + static const Grid &defaultDesignerGrid(); + static void setDefaultDesignerGrid(const Grid& grid); + + // Overwrite to initialize and return a full popup menu for a managed widget + virtual QMenu *initializePopupMenu(QWidget *managedWidget); + // Helper to create a basic popup menu from task menu extensions (internal/public) + static QMenu *createExtensionTaskMenu(QDesignerFormWindowInterface *fw, QObject *o, bool trailingSeparator = true); + + virtual bool dropWidgets(const QList<QDesignerDnDItemInterface*> &item_list, QWidget *target, + const QPoint &global_mouse_pos) = 0; + + // Helper to find the widget at the mouse position with some flags. + enum WidgetUnderMouseMode { FindSingleSelectionDropTarget, FindMultiSelectionDropTarget }; + QWidget *widgetUnderMouse(const QPoint &formPos, WidgetUnderMouseMode m); + + virtual QWidget *widgetAt(const QPoint &pos) = 0; + virtual QWidget *findContainer(QWidget *w, bool excludeLayout) const = 0; + + void deleteWidgetList(const QWidgetList &widget_list); + + virtual void highlightWidget(QWidget *w, const QPoint &pos, HighlightMode mode = Highlight) = 0; + + enum PasteMode { PasteAll, PasteActionsOnly }; + virtual void paste(PasteMode pasteMode) = 0; + + // Factory method to create a form builder + virtual QEditorFormBuilder *createFormBuilder() = 0; + + virtual bool blockSelectionChanged(bool blocked) = 0; + virtual void emitSelectionChanged() = 0; + + DesignerPixmapCache *pixmapCache() const; + DesignerIconCache *iconCache() const; + QtResourceSet *resourceSet() const; + void setResourceSet(QtResourceSet *resourceSet); + void addReloadableProperty(QDesignerPropertySheet *sheet, int index); + void removeReloadableProperty(QDesignerPropertySheet *sheet, int index); + void addReloadablePropertySheet(QDesignerPropertySheet *sheet, QObject *object); + void removeReloadablePropertySheet(QDesignerPropertySheet *sheet); + void reloadProperties(); + + void emitWidgetRemoved(QWidget *w); + void emitObjectRemoved(QObject *o); + + DeviceProfile deviceProfile() const; + QString styleName() const; + QString deviceProfileName() const; + + enum LineTerminatorMode { + LFLineTerminator, + CRLFLineTerminator, + NativeLineTerminator = +#if defined (Q_OS_WIN) + CRLFLineTerminator +#else + LFLineTerminator +#endif + }; + + void setLineTerminatorMode(LineTerminatorMode mode); + LineTerminatorMode lineTerminatorMode() const; + + // Connect the 'activated' (doubleclicked) signal of the form window to a + // slot triggering the default action (of the task menu) + static void setupDefaultAction(QDesignerFormWindowInterface *fw); + +public slots: + void resourceSetActivated(QtResourceSet *resourceSet, bool resourceSetChanged); + +private slots: + void triggerDefaultAction(QWidget *w); + +private: + void syncGridFeature(); + + FormWindowBasePrivate *m_d; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // FORMWINDOWBASE_H diff --git a/tools/designer/src/lib/shared/grid.cpp b/tools/designer/src/lib/shared/grid.cpp new file mode 100644 index 0000000..99dbcee --- /dev/null +++ b/tools/designer/src/lib/shared/grid.cpp @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "grid_p.h" + +#include <QtCore/QString> +#include <QtCore/QVector> +#include <QtGui/QPainter> +#include <QtGui/QWidget> +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +static const bool defaultSnap = true; +static const bool defaultVisible = true; +static const int DEFAULT_GRID = 10; +static const char* KEY_VISIBLE = "gridVisible"; +static const char* KEY_SNAPX = "gridSnapX"; +static const char* KEY_SNAPY = "gridSnapY"; +static const char* KEY_DELTAX = "gridDeltaX"; +static const char* KEY_DELTAY = "gridDeltaY"; + +// Insert a value into the serialization map unless default +template <class T> + static inline void valueToVariantMap(T value, T defaultValue, const QString &key, QVariantMap &v, bool forceKey) { + if (forceKey || value != defaultValue) + v.insert(key, QVariant(value)); + } + +// Obtain a value form QVariantMap +template <class T> + static inline bool valueFromVariantMap(const QVariantMap &v, const QString &key, T &value) { + const QVariantMap::const_iterator it = v.constFind(key); + const bool found = it != v.constEnd(); + if (found) + value = qVariantValue<T>(it.value()); + return found; + } + +namespace qdesigner_internal +{ + +Grid::Grid() : + m_visible(defaultVisible), + m_snapX(defaultSnap), + m_snapY(defaultSnap), + m_deltaX(DEFAULT_GRID), + m_deltaY(DEFAULT_GRID) +{ +} + +bool Grid::fromVariantMap(const QVariantMap& vm) +{ + *this = Grid(); + valueFromVariantMap(vm, QLatin1String(KEY_VISIBLE), m_visible); + valueFromVariantMap(vm, QLatin1String(KEY_SNAPX), m_snapX); + valueFromVariantMap(vm, QLatin1String(KEY_SNAPY), m_snapY); + valueFromVariantMap(vm, QLatin1String(KEY_DELTAX), m_deltaX); + return valueFromVariantMap(vm, QLatin1String(KEY_DELTAY), m_deltaY); +} + +QVariantMap Grid::toVariantMap(bool forceKeys) const +{ + QVariantMap rc; + addToVariantMap(rc, forceKeys); + return rc; +} + +void Grid::addToVariantMap(QVariantMap& vm, bool forceKeys) const +{ + valueToVariantMap(m_visible, defaultVisible, QLatin1String(KEY_VISIBLE), vm, forceKeys); + valueToVariantMap(m_snapX, defaultSnap, QLatin1String(KEY_SNAPX), vm, forceKeys); + valueToVariantMap(m_snapY, defaultSnap, QLatin1String(KEY_SNAPY), vm, forceKeys); + valueToVariantMap(m_deltaX, DEFAULT_GRID, QLatin1String(KEY_DELTAX), vm, forceKeys); + valueToVariantMap(m_deltaY, DEFAULT_GRID, QLatin1String(KEY_DELTAY), vm, forceKeys); +} + +void Grid::paint(QWidget *widget, QPaintEvent *e) const +{ + QPainter p(widget); + paint(p, widget, e); +} + +void Grid::paint(QPainter &p, const QWidget *widget, QPaintEvent *e) const +{ + p.setPen(widget->palette().dark().color()); + + if (m_visible) { + const int xstart = (e->rect().x() / m_deltaX) * m_deltaX; + const int ystart = (e->rect().y() / m_deltaY) * m_deltaY; + + const int xend = e->rect().right(); + const int yend = e->rect().bottom(); + + typedef QVector<QPointF> Points; + static Points points; + points.clear(); + + for (int x = xstart; x <= xend; x += m_deltaX) { + points.reserve((yend - ystart) / m_deltaY + 1); + for (int y = ystart; y <= yend; y += m_deltaY) + points.push_back(QPointF(x, y)); + p.drawPoints( &(*points.begin()), points.count()); + points.clear(); + } + } +} + +int Grid::snapValue(int value, int grid) const +{ + const int rest = value % grid; + const int absRest = (rest < 0) ? -rest : rest; + int offset = 0; + if (2 * absRest > grid) + offset = 1; + if (rest < 0) + offset *= -1; + return (value / grid + offset) * grid; +} + +QPoint Grid::snapPoint(const QPoint &p) const +{ + const int sx = m_snapX ? snapValue(p.x(), m_deltaX) : p.x(); + const int sy = m_snapY ? snapValue(p.y(), m_deltaY) : p.y(); + return QPoint(sx, sy); +} + +int Grid::widgetHandleAdjustX(int x) const +{ + return m_snapX ? (x / m_deltaX) * m_deltaX + 1 : x; +} + +int Grid::widgetHandleAdjustY(int y) const +{ + return m_snapY ? (y / m_deltaY) * m_deltaY + 1 : y; +} + +bool Grid::equals(const Grid &rhs) const +{ + return m_visible == rhs.m_visible && + m_snapX == rhs.m_snapX && + m_snapY == rhs.m_snapY && + m_deltaX == rhs.m_deltaX && + m_deltaY == rhs.m_deltaY; +} +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/grid_p.h b/tools/designer/src/lib/shared/grid_p.h new file mode 100644 index 0000000..b07217f --- /dev/null +++ b/tools/designer/src/lib/shared/grid_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_GRID_H +#define QDESIGNER_GRID_H + +#include "shared_global_p.h" + +#include <QtCore/QVariantMap> + +QT_BEGIN_NAMESPACE + +class QWidget; +class QPaintEvent; +class QPainter; + +namespace qdesigner_internal { + +// Designer grid which is able to serialize to QVariantMap +class QDESIGNER_SHARED_EXPORT Grid +{ +public: + Grid(); + + bool fromVariantMap(const QVariantMap& vm); + + void addToVariantMap(QVariantMap& vm, bool forceKeys = false) const; + QVariantMap toVariantMap(bool forceKeys = false) const; + + inline bool visible() const { return m_visible; } + void setVisible(bool visible) { m_visible = visible; } + + inline bool snapX() const { return m_snapX; } + void setSnapX(bool snap) { m_snapX = snap; } + + inline bool snapY() const { return m_snapY; } + void setSnapY(bool snap) { m_snapY = snap; } + + inline int deltaX() const { return m_deltaX; } + void setDeltaX(int dx) { m_deltaX = dx; } + + inline int deltaY() const { return m_deltaY; } + void setDeltaY(int dy) { m_deltaY = dy; } + + void paint(QWidget *widget, QPaintEvent *e) const; + void paint(QPainter &p, const QWidget *widget, QPaintEvent *e) const; + + QPoint snapPoint(const QPoint &p) const; + + int widgetHandleAdjustX(int x) const; + int widgetHandleAdjustY(int y) const; + + inline bool operator==(const Grid &rhs) const { return equals(rhs); } + inline bool operator!=(const Grid &rhs) const { return !equals(rhs); } + +private: + bool equals(const Grid &rhs) const; + int snapValue(int value, int grid) const; + bool m_visible; + bool m_snapX; + bool m_snapY; + int m_deltaX; + int m_deltaY; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_GRID_H diff --git a/tools/designer/src/lib/shared/gridpanel.cpp b/tools/designer/src/lib/shared/gridpanel.cpp new file mode 100644 index 0000000..f7658ba --- /dev/null +++ b/tools/designer/src/lib/shared/gridpanel.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gridpanel_p.h" +#include "ui_gridpanel.h" +#include "grid_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +GridPanel::GridPanel(QWidget *parentWidget) : + QWidget(parentWidget) +{ + m_ui = new Ui::GridPanel; + m_ui->setupUi(this); + + connect(m_ui->m_resetButton, SIGNAL(clicked()), this, SLOT(reset())); +} + +GridPanel::~GridPanel() +{ + delete m_ui; +} + +void GridPanel::setGrid(const Grid &g) +{ + m_ui->m_deltaXSpinBox->setValue(g.deltaX()); + m_ui->m_deltaYSpinBox->setValue(g.deltaY()); + m_ui->m_visibleCheckBox->setCheckState(g.visible() ? Qt::Checked : Qt::Unchecked); + m_ui->m_snapXCheckBox->setCheckState(g.snapX() ? Qt::Checked : Qt::Unchecked); + m_ui->m_snapYCheckBox->setCheckState(g.snapY() ? Qt::Checked : Qt::Unchecked); +} + +void GridPanel::setTitle(const QString &title) +{ + m_ui->m_gridGroupBox->setTitle(title); +} + +Grid GridPanel::grid() const +{ + Grid rc; + rc.setDeltaX(m_ui->m_deltaXSpinBox->value()); + rc.setDeltaY(m_ui->m_deltaYSpinBox->value()); + rc.setSnapX(m_ui->m_snapXCheckBox->checkState() == Qt::Checked); + rc.setSnapY(m_ui->m_snapYCheckBox->checkState() == Qt::Checked); + rc.setVisible(m_ui->m_visibleCheckBox->checkState() == Qt::Checked); + return rc; +} + +void GridPanel::reset() +{ + setGrid(Grid()); +} + +void GridPanel::setCheckable (bool c) +{ + m_ui->m_gridGroupBox->setCheckable(c); +} + +bool GridPanel::isCheckable () const +{ + return m_ui->m_gridGroupBox->isCheckable (); +} + +bool GridPanel::isChecked () const +{ + return m_ui->m_gridGroupBox->isChecked (); +} + +void GridPanel::setChecked(bool c) +{ + m_ui->m_gridGroupBox->setChecked(c); +} + +void GridPanel::setResetButtonVisible(bool v) +{ + m_ui->m_resetButton->setVisible(v); +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/gridpanel.ui b/tools/designer/src/lib/shared/gridpanel.ui new file mode 100644 index 0000000..adfdd36 --- /dev/null +++ b/tools/designer/src/lib/shared/gridpanel.ui @@ -0,0 +1,144 @@ +<ui version="4.0" > + <class>qdesigner_internal::GridPanel</class> + <widget class="QWidget" name="qdesigner_internal::GridPanel" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>393</width> + <height>110</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QVBoxLayout" > + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QGroupBox" name="m_gridGroupBox" > + <property name="title" > + <string>Grid</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QCheckBox" name="m_visibleCheckBox" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Visible</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Grid &X</string> + </property> + <property name="buddy" > + <cstring>m_deltaXSpinBox</cstring> + </property> + </widget> + </item> + <item row="0" column="2" > + <widget class="QSpinBox" name="m_deltaXSpinBox" > + <property name="minimum" > + <number>2</number> + </property> + <property name="maximum" > + <number>100</number> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QCheckBox" name="m_snapXCheckBox" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Snap</string> + </property> + </widget> + </item> + <item row="1" column="0" > + <layout class="QHBoxLayout" > + <item> + <widget class="QPushButton" name="m_resetButton" > + <property name="text" > + <string>Reset</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>20</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="1" column="1" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Grid &Y</string> + </property> + <property name="buddy" > + <cstring>m_deltaYSpinBox</cstring> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QSpinBox" name="m_deltaYSpinBox" > + <property name="minimum" > + <number>2</number> + </property> + <property name="maximum" > + <number>100</number> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QCheckBox" name="m_snapYCheckBox" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="MinimumExpanding" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Snap</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/gridpanel_p.h b/tools/designer/src/lib/shared/gridpanel_p.h new file mode 100644 index 0000000..c51b08c --- /dev/null +++ b/tools/designer/src/lib/shared/gridpanel_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of the Qt tools. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef GRIDPANEL_H +#define GRIDPANEL_H + +#include "shared_global_p.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class Grid; + +namespace Ui { + class GridPanel; +} + +class QDESIGNER_SHARED_EXPORT GridPanel : public QWidget +{ + Q_OBJECT +public: + GridPanel(QWidget *parent = 0); + ~GridPanel(); + + void setTitle(const QString &title); + + void setGrid(const Grid &g); + Grid grid() const; + + void setCheckable (bool c); + bool isCheckable () const; + + bool isChecked () const; + void setChecked(bool c); + + void setResetButtonVisible(bool v); + +private slots: + void reset(); + +private: + Ui::GridPanel *m_ui; +}; + +} // qdesigner_internal + +QT_END_NAMESPACE + +#endif // GRIDPANEL_H diff --git a/tools/designer/src/lib/shared/htmlhighlighter.cpp b/tools/designer/src/lib/shared/htmlhighlighter.cpp new file mode 100644 index 0000000..ba07673 --- /dev/null +++ b/tools/designer/src/lib/shared/htmlhighlighter.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/QTextStream> + +#include "htmlhighlighter_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +HtmlHighlighter::HtmlHighlighter(QTextEdit *textEdit) + : QSyntaxHighlighter(textEdit) +{ + QTextCharFormat entityFormat; + entityFormat.setForeground(Qt::red); + setFormatFor(Entity, entityFormat); + + QTextCharFormat tagFormat; + tagFormat.setForeground(Qt::darkMagenta); + tagFormat.setFontWeight(QFont::Bold); + setFormatFor(Tag, tagFormat); + + QTextCharFormat commentFormat; + commentFormat.setForeground(Qt::gray); + commentFormat.setFontItalic(true); + setFormatFor(Comment, commentFormat); + + QTextCharFormat attributeFormat; + attributeFormat.setForeground(Qt::black); + attributeFormat.setFontWeight(QFont::Bold); + setFormatFor(Attribute, attributeFormat); + + QTextCharFormat valueFormat; + valueFormat.setForeground(Qt::blue); + setFormatFor(Value, valueFormat); +} + +void HtmlHighlighter::setFormatFor(Construct construct, + const QTextCharFormat &format) +{ + m_formats[construct] = format; + rehighlight(); +} + +void HtmlHighlighter::highlightBlock(const QString &text) +{ + static const QLatin1Char tab = QLatin1Char('\t'); + static const QLatin1Char space = QLatin1Char(' '); + static const QLatin1Char amp = QLatin1Char('&'); + static const QLatin1Char startTag = QLatin1Char('<'); + static const QLatin1Char endTag = QLatin1Char('>'); + static const QLatin1Char quot = QLatin1Char('"'); + static const QLatin1Char apos = QLatin1Char('\''); + static const QLatin1Char semicolon = QLatin1Char(';'); + static const QLatin1Char equals = QLatin1Char('='); + static const QLatin1String startComment = QLatin1String("<!--"); + static const QLatin1String endComment = QLatin1String("-->"); + static const QLatin1String endElement = QLatin1String("/>"); + + int state = previousBlockState(); + int len = text.length(); + int start = 0; + int pos = 0; + + while (pos < len) { + switch (state) { + case NormalState: + default: + while (pos < len) { + QChar ch = text.at(pos); + if (ch == startTag) { + if (text.mid(pos, 4) == startComment) { + state = InComment; + } else { + state = InTag; + start = pos; + while (pos < len && text.at(pos) != space + && text.at(pos) != endTag + && text.at(pos) != tab + && text.mid(pos, 2) != endElement) + ++pos; + if (text.mid(pos, 2) == endElement) + ++pos; + setFormat(start, pos - start, + m_formats[Tag]); + break; + } + break; + } else if (ch == amp) { + start = pos; + while (pos < len && text.at(pos++) != semicolon) + ; + setFormat(start, pos - start, + m_formats[Entity]); + } else { + // No tag, comment or entity started, continue... + ++pos; + } + } + break; + case InComment: + start = pos; + while (pos < len) { + if (text.mid(pos, 3) == endComment) { + pos += 3; + state = NormalState; + break; + } else { + ++pos; + } + } + setFormat(start, pos - start, m_formats[Comment]); + break; + case InTag: + QChar quote = QChar::Null; + while (pos < len) { + QChar ch = text.at(pos); + if (quote.isNull()) { + start = pos; + if (ch == apos || ch == quot) { + quote = ch; + } else if (ch == endTag) { + ++pos; + setFormat(start, pos - start, m_formats[Tag]); + state = NormalState; + break; + } else if (text.mid(pos, 2) == endElement) { + pos += 2; + setFormat(start, pos - start, m_formats[Tag]); + state = NormalState; + break; + } else if (ch != space && text.at(pos) != tab) { + // Tag not ending, not a quote and no whitespace, so + // we must be dealing with an attribute. + ++pos; + while (pos < len && text.at(pos) != space + && text.at(pos) != tab + && text.at(pos) != equals) + ++pos; + setFormat(start, pos - start, m_formats[Attribute]); + start = pos; + } + } else if (ch == quote) { + quote = QChar::Null; + + // Anything quoted is a value + setFormat(start, pos - start, m_formats[Value]); + } + ++pos; + } + break; + } + } + setCurrentBlockState(state); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/htmlhighlighter_p.h b/tools/designer/src/lib/shared/htmlhighlighter_p.h new file mode 100644 index 0000000..e841ec7 --- /dev/null +++ b/tools/designer/src/lib/shared/htmlhighlighter_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef HTMLHIGHLIGHTER_H +#define HTMLHIGHLIGHTER_H + +#include <QtGui/QSyntaxHighlighter> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +/* HTML syntax highlighter based on Qt Quarterly example */ +class HtmlHighlighter : public QSyntaxHighlighter +{ + Q_OBJECT + +public: + enum Construct { + Entity, + Tag, + Comment, + Attribute, + Value, + LastConstruct = Value + }; + + HtmlHighlighter(QTextEdit *textEdit); + + void setFormatFor(Construct construct, const QTextCharFormat &format); + + QTextCharFormat formatFor(Construct construct) const + { return m_formats[construct]; } + +protected: + enum State { + NormalState = -1, + InComment, + InTag + }; + + void highlightBlock(const QString &text); + +private: + QTextCharFormat m_formats[LastConstruct + 1]; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // HTMLHIGHLIGHTER_H diff --git a/tools/designer/src/lib/shared/iconloader.cpp b/tools/designer/src/lib/shared/iconloader.cpp new file mode 100644 index 0000000..b2df262 --- /dev/null +++ b/tools/designer/src/lib/shared/iconloader.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "iconloader_p.h" + +#include <QtCore/QFile> +#include <QtGui/QIcon> +#include <QtGui/QPixmap> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDESIGNER_SHARED_EXPORT QIcon createIconSet(const QString &name) +{ + QStringList candidates = QStringList() + << (QString::fromUtf8(":/trolltech/formeditor/images/") + name) +#ifdef Q_WS_MAC + << (QString::fromUtf8(":/trolltech/formeditor/images/mac/") + name) +#else + << (QString::fromUtf8(":/trolltech/formeditor/images/win/") + name) +#endif + << (QString::fromUtf8(":/trolltech/formeditor/images/designer_") + name); + + foreach (QString f, candidates) { + if (QFile::exists(f)) + return QIcon(f); + } + + return QIcon(); +} + +QDESIGNER_SHARED_EXPORT QIcon emptyIcon() +{ + static const QIcon empty_icon(QLatin1String(":/trolltech/formeditor/images/emptyicon.png")); + return empty_icon; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + diff --git a/tools/designer/src/lib/shared/iconloader_p.h b/tools/designer/src/lib/shared/iconloader_p.h new file mode 100644 index 0000000..572aab9 --- /dev/null +++ b/tools/designer/src/lib/shared/iconloader_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ICONLOADER_H +#define ICONLOADER_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QString; +class QIcon; + +namespace qdesigner_internal { + +QDESIGNER_SHARED_EXPORT QIcon createIconSet(const QString &name); +QDESIGNER_SHARED_EXPORT QIcon emptyIcon(); + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ICONLOADER_H diff --git a/tools/designer/src/lib/shared/iconselector.cpp b/tools/designer/src/lib/shared/iconselector.cpp new file mode 100644 index 0000000..02a3dee --- /dev/null +++ b/tools/designer/src/lib/shared/iconselector.cpp @@ -0,0 +1,546 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "iconselector_p.h" +#include "qdesigner_utils_p.h" +#include "qtresourcemodel_p.h" +#include "qtresourceview_p.h" +#include "iconloader_p.h" +#include "qdesigner_integration_p.h" +#include "formwindowbase_p.h" + +#include <abstractdialoggui_p.h> +#include <qdesigner_integration_p.h> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerResourceBrowserInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QToolButton> +#include <QtCore/QSignalMapper> +#include <QtGui/QComboBox> +#include <QtGui/QAction> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QPushButton> +#include <QtGui/QDialog> +#include <QtGui/QMenu> +#include <QtGui/QApplication> +#include <QtGui/QVBoxLayout> +#include <QtGui/QImageReader> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QVBoxLayout> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// -------------------- LanguageResourceDialogPrivate +class LanguageResourceDialogPrivate { + LanguageResourceDialog *q_ptr; + Q_DECLARE_PUBLIC(LanguageResourceDialog) + +public: + LanguageResourceDialogPrivate(QDesignerResourceBrowserInterface *rb); + void init(LanguageResourceDialog *p); + + void setCurrentPath(const QString &filePath); + QString currentPath() const; + + void slotAccepted(); + void slotPathChanged(const QString &); + +private: + void setOkButtonEnabled(bool v) { m_dialogButtonBox->button(QDialogButtonBox::Ok)->setEnabled(v); } + static bool checkPath(const QString &p); + + QDesignerResourceBrowserInterface *m_browser; + QDialogButtonBox *m_dialogButtonBox; +}; + +LanguageResourceDialogPrivate::LanguageResourceDialogPrivate(QDesignerResourceBrowserInterface *rb) : + q_ptr(0), + m_browser(rb), + m_dialogButtonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) +{ + setOkButtonEnabled(false); +} + +void LanguageResourceDialogPrivate::init(LanguageResourceDialog *p) +{ + q_ptr = p; + QLayout *layout = new QVBoxLayout(p); + layout->addWidget(m_browser); + layout->addWidget(m_dialogButtonBox); + QObject::connect(m_dialogButtonBox, SIGNAL(accepted()), p, SLOT(slotAccepted())); + QObject::connect(m_dialogButtonBox, SIGNAL(rejected()), p, SLOT(reject())); + QObject::connect(m_browser, SIGNAL(currentPathChanged(QString)), p, SLOT(slotPathChanged(QString))); + QObject::connect(m_browser, SIGNAL(pathActivated(QString)), p, SLOT(slotAccepted())); + p->setModal(true); + p->setWindowTitle(LanguageResourceDialog::tr("Choose Resource")); + p->setWindowFlags(p->windowFlags() & ~Qt::WindowContextHelpButtonHint); + setOkButtonEnabled(false); +} + +void LanguageResourceDialogPrivate::setCurrentPath(const QString &filePath) +{ + m_browser->setCurrentPath(filePath); + setOkButtonEnabled(checkPath(filePath)); +} + +QString LanguageResourceDialogPrivate::currentPath() const +{ + return m_browser->currentPath(); +} + +bool LanguageResourceDialogPrivate::checkPath(const QString &p) +{ + return p.isEmpty() ? false : IconSelector::checkPixmap(p, IconSelector::CheckFast); +} + +void LanguageResourceDialogPrivate::slotAccepted() +{ + if (checkPath(currentPath())) + q_ptr->accept(); +} + +void LanguageResourceDialogPrivate::slotPathChanged(const QString &p) +{ + setOkButtonEnabled(checkPath(p)); +} + +// ------------ LanguageResourceDialog +LanguageResourceDialog::LanguageResourceDialog(QDesignerResourceBrowserInterface *rb, QWidget *parent) : + QDialog(parent), + d_ptr(new LanguageResourceDialogPrivate(rb)) +{ + d_ptr->init( this); +} + +LanguageResourceDialog::~LanguageResourceDialog() +{ + delete d_ptr; +} + +void LanguageResourceDialog::setCurrentPath(const QString &filePath) +{ + d_ptr->setCurrentPath(filePath); +} + +QString LanguageResourceDialog::currentPath() const +{ + return d_ptr->currentPath(); +} + +LanguageResourceDialog* LanguageResourceDialog::create(QDesignerFormEditorInterface *core, QWidget *parent) +{ + if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) + if (QDesignerResourceBrowserInterface *rb = lang->createResourceBrowser(0)) + return new LanguageResourceDialog(rb, parent); + if (QDesignerIntegration *di = qobject_cast<QDesignerIntegration*>(core->integration())) + if (QDesignerResourceBrowserInterface *rb = di->createResourceBrowser(0)) + return new LanguageResourceDialog(rb, parent); + return 0; +} + +// ------------ IconSelectorPrivate +class IconSelectorPrivate +{ + IconSelector *q_ptr; + Q_DECLARE_PUBLIC(IconSelector) +public: + IconSelectorPrivate(); + + void slotStateActivated(); + void slotSetActivated(); + void slotSetResourceActivated(); + void slotSetFileActivated(); + void slotResetActivated(); + void slotResetAllActivated(); + void slotUpdate(); + + QList<QPair<QPair<QIcon::Mode, QIcon::State>, QString> > m_stateToName; // could be static map + + QMap<QPair<QIcon::Mode, QIcon::State>, int> m_stateToIndex; + QMap<int, QPair<QIcon::Mode, QIcon::State> > m_indexToState; + + QIcon m_emptyIcon; + QComboBox *m_stateComboBox; + QToolButton *m_iconButton; + QAction *m_resetAction; + QAction *m_resetAllAction; + PropertySheetIconValue m_icon; + DesignerIconCache *m_iconCache; + DesignerPixmapCache *m_pixmapCache; + QtResourceModel *m_resourceModel; + QDesignerFormEditorInterface *m_core; +}; + +IconSelectorPrivate::IconSelectorPrivate() : + q_ptr(0), + m_stateComboBox(0), + m_iconButton(0), + m_resetAction(0), + m_resetAllAction(0), + m_iconCache(0), + m_pixmapCache(0), + m_resourceModel(0), + m_core(0) +{ +} +void IconSelectorPrivate::slotUpdate() +{ + QIcon icon; + if (m_iconCache) + icon = m_iconCache->icon(m_icon); + + QMap<QPair<QIcon::Mode, QIcon::State>, PropertySheetPixmapValue> paths = m_icon.paths(); + QMapIterator<QPair<QIcon::Mode, QIcon::State>, int> itIndex(m_stateToIndex); + while (itIndex.hasNext()) { + const QPair<QIcon::Mode, QIcon::State> state = itIndex.next().key(); + const PropertySheetPixmapValue pixmap = paths.value(state); + const int index = itIndex.value(); + + QIcon pixmapIcon = QIcon(icon.pixmap(16, 16, state.first, state.second)); + if (pixmapIcon.isNull()) + pixmapIcon = m_emptyIcon; + m_stateComboBox->setItemIcon(index, pixmapIcon); + QFont font = q_ptr->font(); + if (!pixmap.path().isEmpty()) + font.setBold(true); + m_stateComboBox->setItemData(index, font, Qt::FontRole); + } + + QPair<QIcon::Mode, QIcon::State> state = m_indexToState.value(m_stateComboBox->currentIndex()); + PropertySheetPixmapValue currentPixmap = paths.value(state); + m_resetAction->setEnabled(!currentPixmap.path().isEmpty()); + m_resetAllAction->setEnabled(!paths.isEmpty()); + m_stateComboBox->update(); +} + +void IconSelectorPrivate::slotStateActivated() +{ + slotUpdate(); +} + +void IconSelectorPrivate::slotSetActivated() +{ + QPair<QIcon::Mode, QIcon::State> state = m_indexToState.value(m_stateComboBox->currentIndex()); + const PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + // Default to resource + const PropertySheetPixmapValue::PixmapSource ps = pixmap.path().isEmpty() ? PropertySheetPixmapValue::ResourcePixmap : pixmap.pixmapSource(m_core); + switch (ps) { + case PropertySheetPixmapValue::LanguageResourcePixmap: + case PropertySheetPixmapValue::ResourcePixmap: + slotSetResourceActivated(); + break; + case PropertySheetPixmapValue::FilePixmap: + slotSetFileActivated(); + break; + } +} + +// Choose a pixmap from resource; use language-dependent resource browser if present +QString IconSelector::choosePixmapResource(QDesignerFormEditorInterface *core, QtResourceModel *resourceModel, const QString &oldPath, QWidget *parent) +{ + Q_UNUSED(resourceModel) + QString rc; + + if (LanguageResourceDialog* ldlg = LanguageResourceDialog::create(core, parent)) { + ldlg->setCurrentPath(oldPath); + if (ldlg->exec() == QDialog::Accepted) + rc = ldlg->currentPath(); + delete ldlg; + } else { + QtResourceViewDialog dlg(core, parent); + + QDesignerIntegration *designerIntegration = qobject_cast<QDesignerIntegration *>(core->integration()); + if (designerIntegration) + dlg.setResourceEditingEnabled(designerIntegration->isResourceEditingEnabled()); + + dlg.selectResource(oldPath); + if (dlg.exec() == QDialog::Accepted) + rc = dlg.selectedResource(); + } + return rc; +} + +void IconSelectorPrivate::slotSetResourceActivated() +{ + const QPair<QIcon::Mode, QIcon::State> state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const QString oldPath = pixmap.path(); + const QString newPath = IconSelector::choosePixmapResource(m_core, m_resourceModel, oldPath, q_ptr); + if (newPath.isEmpty() || newPath == oldPath) + return; + const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath); + if (newPixmap != pixmap) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +// Helpers for choosing image files: Check for valid image. +bool IconSelector::checkPixmap(const QString &fileName, CheckMode cm, QString *errorMessage) +{ + const QFileInfo fi(fileName); + if (!fi.exists() || !fi.isFile() || !fi.isReadable()) { + if (errorMessage) + *errorMessage = tr("The pixmap file '%1' cannot be read.").arg(fileName); + return false; + } + QImageReader reader(fileName); + if (!reader.canRead()) { + if (errorMessage) + *errorMessage = tr("The file '%1' does not appear to be a valid pixmap file: %2").arg(fileName).arg(reader.errorString()); + return false; + } + if (cm == CheckFast) + return true; + + const QImage image = reader.read(); + if (image.isNull()) { + if (errorMessage) + *errorMessage = tr("The file '%1' could not be read: %2").arg(fileName).arg(reader.errorString()); + return false; + } + return true; +} + +// Helpers for choosing image files: Return an image filter for QFileDialog, courtesy of StyledButton +static QString imageFilter() +{ + QString filter = QApplication::translate("IconSelector", "All Pixmaps ("); + const QList<QByteArray> supportedImageFormats = QImageReader::supportedImageFormats(); + const QString jpeg = QLatin1String("JPEG"); + const int count = supportedImageFormats.count(); + for (int i = 0; i< count; ++i) { + if (i) + filter += QLatin1Char(' '); + filter += QLatin1String("*."); + const QString outputFormat = QString::fromUtf8(supportedImageFormats.at(i)); + if (outputFormat != jpeg) + filter += outputFormat.toLower(); + else + filter += QLatin1String("jpg *.jpeg"); + } + filter += QLatin1Char(')'); + return filter; +} + +// Helpers for choosing image files: Choose a file +QString IconSelector::choosePixmapFile(const QString &directory, QDesignerDialogGuiInterface *dlgGui,QWidget *parent) +{ + QString errorMessage; + QString newPath; + do { + const QString title = tr("Choose a Pixmap"); + static const QString filter = imageFilter(); + newPath = dlgGui->getOpenImageFileName(parent, title, directory, filter); + if (newPath.isEmpty()) + break; + if (checkPixmap(newPath, CheckFully, &errorMessage)) + break; + dlgGui->message(parent, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, tr("Pixmap Read Error"), errorMessage); + } while(true); + return newPath; +} + +void IconSelectorPrivate::slotSetFileActivated() +{ + QPair<QIcon::Mode, QIcon::State> state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const QString newPath = IconSelector::choosePixmapFile(pixmap.path(), m_core->dialogGui(), q_ptr); + if (!newPath.isEmpty()) { + const PropertySheetPixmapValue newPixmap = PropertySheetPixmapValue(newPath); + if (!(newPixmap == pixmap)) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } + } +} + +void IconSelectorPrivate::slotResetActivated() +{ + QPair<QIcon::Mode, QIcon::State> state = m_indexToState.value(m_stateComboBox->currentIndex()); + + PropertySheetPixmapValue pixmap = m_icon.pixmap(state.first, state.second); + const PropertySheetPixmapValue newPixmap; + if (!(newPixmap == pixmap)) { + m_icon.setPixmap(state.first, state.second, newPixmap); + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +void IconSelectorPrivate::slotResetAllActivated() +{ + const PropertySheetIconValue newIcon; + if (!(m_icon == newIcon)) { + m_icon = newIcon; + slotUpdate(); + emit q_ptr->iconChanged(m_icon); + } +} + +// ------------- IconSelector +IconSelector::IconSelector(QWidget *parent) : + QWidget(parent) +{ + d_ptr = new IconSelectorPrivate(); + d_ptr->q_ptr = this; + + d_ptr->m_stateComboBox = new QComboBox(this); + + QHBoxLayout *l = new QHBoxLayout(this); + d_ptr->m_iconButton = new QToolButton(this); + d_ptr->m_iconButton->setText(tr("...")); + d_ptr->m_iconButton->setPopupMode(QToolButton::MenuButtonPopup); + l->addWidget(d_ptr->m_stateComboBox); + l->addWidget(d_ptr->m_iconButton); + l->setMargin(0); + + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Normal, QIcon::Off), tr("Normal Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Normal, QIcon::On), tr("Normal On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Disabled, QIcon::Off), tr("Disabled Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Disabled, QIcon::On), tr("Disabled On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Active, QIcon::Off), tr("Active Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Active, QIcon::On), tr("Active On") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Selected, QIcon::Off), tr("Selected Off") ); + d_ptr->m_stateToName << qMakePair(qMakePair(QIcon::Selected, QIcon::On), tr("Selected On") ); + + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + d_ptr->m_emptyIcon = QIcon(QPixmap::fromImage(img)); + + QMenu *setMenu = new QMenu(this); + + QAction *setResourceAction = new QAction(tr("Choose Resource..."), this); + QAction *setFileAction = new QAction(tr("Choose File..."), this); + d_ptr->m_resetAction = new QAction(tr("Reset"), this); + d_ptr->m_resetAllAction = new QAction(tr("Reset All"), this); + d_ptr->m_resetAction->setEnabled(false); + d_ptr->m_resetAllAction->setEnabled(false); + //d_ptr->m_resetAction->setIcon(createIconSet(QString::fromUtf8("resetproperty.png"))); + + setMenu->addAction(setResourceAction); + setMenu->addAction(setFileAction); + setMenu->addSeparator(); + setMenu->addAction(d_ptr->m_resetAction); + setMenu->addAction(d_ptr->m_resetAllAction); + + int index = 0; + QStringList items; + QListIterator<QPair<QPair<QIcon::Mode, QIcon::State>, QString> > itName(d_ptr->m_stateToName); + while (itName.hasNext()) { + QPair<QPair<QIcon::Mode, QIcon::State>, QString> item = itName.next(); + const QPair<QIcon::Mode, QIcon::State> state = item.first; + const QString name = item.second; + + items.append(name); + d_ptr->m_stateToIndex[state] = index; + d_ptr->m_indexToState[index] = state; + index++; + } + d_ptr->m_stateComboBox->addItems(items); + + d_ptr->m_iconButton->setMenu(setMenu); + + connect(d_ptr->m_stateComboBox, SIGNAL(activated(int)), this, SLOT(slotStateActivated())); + connect(d_ptr->m_iconButton, SIGNAL(clicked()), this, SLOT(slotSetActivated())); + connect(setResourceAction, SIGNAL(triggered()), this, SLOT(slotSetResourceActivated())); + connect(setFileAction, SIGNAL(triggered()), this, SLOT(slotSetFileActivated())); + connect(d_ptr->m_resetAction, SIGNAL(triggered()), this, SLOT(slotResetActivated())); + connect(d_ptr->m_resetAllAction, SIGNAL(triggered()), this, SLOT(slotResetAllActivated())); + + d_ptr->slotUpdate(); +} + +IconSelector::~IconSelector() +{ + delete d_ptr; +} + +void IconSelector::setIcon(const PropertySheetIconValue &icon) +{ + if (d_ptr->m_icon == icon) + return; + + d_ptr->m_icon = icon; + d_ptr->slotUpdate(); +} + +PropertySheetIconValue IconSelector::icon() const +{ + return d_ptr->m_icon; +} + +void IconSelector::setFormEditor(QDesignerFormEditorInterface *core) +{ + d_ptr->m_core = core; + d_ptr->m_resourceModel = core->resourceModel(); + d_ptr->slotUpdate(); +} + +void IconSelector::setIconCache(DesignerIconCache *iconCache) +{ + d_ptr->m_iconCache = iconCache; + connect(iconCache, SIGNAL(reloaded()), this, SLOT(slotUpdate())); + d_ptr->slotUpdate(); +} + +void IconSelector::setPixmapCache(DesignerPixmapCache *pixmapCache) +{ + d_ptr->m_pixmapCache = pixmapCache; + connect(pixmapCache, SIGNAL(reloaded()), this, SLOT(slotUpdate())); + d_ptr->slotUpdate(); +} + +} // qdesigner_internal + +QT_END_NAMESPACE + +#include "moc_iconselector_p.cpp" + diff --git a/tools/designer/src/lib/shared/iconselector_p.h b/tools/designer/src/lib/shared/iconselector_p.h new file mode 100644 index 0000000..6b70cd2 --- /dev/null +++ b/tools/designer/src/lib/shared/iconselector_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef ICONSELECTOR_H +#define ICONSELECTOR_H + +#include "shared_global_p.h" +#include <QtGui/QWidget> +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QDesignerFormEditorInterface; +class QDesignerDialogGuiInterface; +class QDesignerResourceBrowserInterface; + +namespace qdesigner_internal { + +class DesignerIconCache; +class DesignerPixmapCache; +class PropertySheetIconValue; + +// Resource Dialog that embeds the language-dependent resource widget as returned by the language extension +class QDESIGNER_SHARED_EXPORT LanguageResourceDialog : public QDialog +{ + Q_OBJECT + + LanguageResourceDialog(QDesignerResourceBrowserInterface *rb, QWidget *parent = 0); + +public: + virtual ~LanguageResourceDialog(); + // Factory: Returns 0 if the language extension does not provide a resource browser. + static LanguageResourceDialog* create(QDesignerFormEditorInterface *core, QWidget *parent); + + void setCurrentPath(const QString &filePath); + QString currentPath() const; + +private: + class LanguageResourceDialogPrivate *d_ptr; + Q_DECLARE_PRIVATE(LanguageResourceDialog) + Q_DISABLE_COPY(LanguageResourceDialog) + Q_PRIVATE_SLOT(d_func(), void slotAccepted()) + Q_PRIVATE_SLOT(d_func(), void slotPathChanged(QString)) + +}; + +class QDESIGNER_SHARED_EXPORT IconSelector: public QWidget +{ + Q_OBJECT +public: + IconSelector(QWidget *parent = 0); + virtual ~IconSelector(); + + void setFormEditor(QDesignerFormEditorInterface *core); // required for dialog gui. + void setIconCache(DesignerIconCache *iconCache); + void setPixmapCache(DesignerPixmapCache *pixmapCache); + + void setIcon(const PropertySheetIconValue &icon); + PropertySheetIconValue icon() const; + + // Check whether a pixmap may be read + enum CheckMode { CheckFast, CheckFully }; + static bool checkPixmap(const QString &fileName, CheckMode cm = CheckFully, QString *errorMessage = 0); + // Choose a pixmap from file + static QString choosePixmapFile(const QString &directory, QDesignerDialogGuiInterface *dlgGui, QWidget *parent); + // Choose a pixmap from resource; use language-dependent resource browser if present + static QString choosePixmapResource(QDesignerFormEditorInterface *core, QtResourceModel *resourceModel, const QString &oldPath, QWidget *parent); + +signals: + void iconChanged(const PropertySheetIconValue &icon); +private: + class IconSelectorPrivate *d_ptr; + Q_DECLARE_PRIVATE(IconSelector) + Q_DISABLE_COPY(IconSelector) + + Q_PRIVATE_SLOT(d_func(), void slotStateActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetResourceActivated()) + Q_PRIVATE_SLOT(d_func(), void slotSetFileActivated()) + Q_PRIVATE_SLOT(d_func(), void slotResetActivated()) + Q_PRIVATE_SLOT(d_func(), void slotResetAllActivated()) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) +}; + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ICONSELECTOR_H + diff --git a/tools/designer/src/lib/shared/invisible_widget.cpp b/tools/designer/src/lib/shared/invisible_widget.cpp new file mode 100644 index 0000000..f88022c --- /dev/null +++ b/tools/designer/src/lib/shared/invisible_widget.cpp @@ -0,0 +1,57 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "invisible_widget_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +InvisibleWidget::InvisibleWidget(QWidget *parent) + : QWidget() +{ + setAttribute(Qt::WA_NoChildEventsForParent); + setParent(parent); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/invisible_widget_p.h b/tools/designer/src/lib/shared/invisible_widget_p.h new file mode 100644 index 0000000..11c8f81 --- /dev/null +++ b/tools/designer/src/lib/shared/invisible_widget_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef INVISIBLE_WIDGET_H +#define INVISIBLE_WIDGET_H + +#include "shared_global_p.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT InvisibleWidget: public QWidget +{ + Q_OBJECT +public: + InvisibleWidget(QWidget *parent = 0); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // INVISIBLE_WIDGET_H diff --git a/tools/designer/src/lib/shared/layout.cpp b/tools/designer/src/lib/shared/layout.cpp new file mode 100644 index 0000000..b1fe02a --- /dev/null +++ b/tools/designer/src/lib/shared/layout.cpp @@ -0,0 +1,1326 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layout_p.h" +#include "qdesigner_utils_p.h" +#include "qlayout_widget_p.h" +#include "spacer_widget_p.h" +#include "layoutdecoration.h" +#include "widgetfactory_p.h" +#include "qdesigner_widgetitem_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> + +#include <QtCore/qdebug.h> +#include <QtCore/QVector> + +#include <QtGui/qevent.h> +#include <QtGui/QGridLayout> +#include <QtGui/QPainter> +#include <QtGui/QBitmap> +#include <QtGui/QSplitter> +#include <QtGui/QMainWindow> +#include <QtGui/QApplication> +#include <QtGui/QScrollArea> +#include <QtGui/QFormLayout> +#include <QtGui/QLabel> +#include <QtGui/QWizardPage> +#include <QtGui/QWizard> +#include <QtCore/QDebug> +#include <QtCore/QSet> + +QT_BEGIN_NAMESPACE + +enum { FormLayoutColumns = 2 }; + +namespace qdesigner_internal { + +/* The wizard has a policy of setting a size policy of its external children + * according to the page being expanding or not (in the latter case, the + * page will be pushed to the top). When setting/breaking layouts, this needs + * to be updated, which happens via a fake style change event. */ + +void updateWizardLayout(QWidget *layoutBase); + +class FriendlyWizardPage : public QWizardPage { + friend void updateWizardLayout(QWidget *); +}; + +void updateWizardLayout(QWidget *layoutBase) +{ + if (QWizardPage *wizardPage = qobject_cast<QWizardPage*>(layoutBase)) + if (QWizard *wizard = static_cast<FriendlyWizardPage*>(wizardPage)->wizard()) { + QEvent event(QEvent::StyleChange); + QApplication::sendEvent(wizard, &event); + } +} + +/*! + \class Layout layout.h + \brief Baseclass for layouting widgets in the Designer (Helper for Layout commands) + \internal + + Classes derived from this abstract base class are used for layouting + operations in the Designer (creating/breaking layouts). + + Instances live in the Layout/BreakLayout commands. +*/ + +/*! \a p specifies the parent of the layoutBase \a lb. The parent + might be changed in setup(). If the layoutBase is a + container, the parent and the layoutBase are the same. Also they + always have to be a widget known to the designer (e.g. in the case + of the tabwidget parent and layoutBase are the tabwidget and not the + page which actually gets laid out. For actual usage the correct + widget is found later by Layout.) + */ + +Layout::Layout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, LayoutInfo::Type layoutType) : + m_widgets(wl), + m_parentWidget(p), + m_layoutBase(lb), + m_formWindow(fw), + m_layoutType(layoutType), + m_reparentLayoutWidget(true), + m_isBreak(false) +{ + if (m_layoutBase) + m_oldGeometry = m_layoutBase->geometry(); +} + +Layout::~Layout() +{ +} + +/*! The widget list we got in the constructor might contain too much + widgets (like widgets with different parents, already laid out + widgets, etc.). Here we set up the list and so the only the "best" + widgets get laid out. +*/ + +void Layout::setup() +{ + m_startPoint = QPoint(32767, 32767); + + // Go through all widgets of the list we got. As we can only + // layout widgets which have the same parent, we first do some + // sorting which means create a list for each parent containing + // its child here. After that we keep working on the list of + // children which has the most entries. + // Widgets which are already laid out are thrown away here too + + QMultiMap<QWidget*, QWidget*> lists; + foreach (QWidget *w, m_widgets) { + QWidget *p = w->parentWidget(); + + if (p && LayoutInfo::layoutType(m_formWindow->core(), p) != LayoutInfo::NoLayout + && m_formWindow->core()->metaDataBase()->item(p->layout()) != 0) + continue; + + lists.insert(p, w); + } + + QWidgetList lastList; + QWidgetList parents = lists.keys(); + foreach (QWidget *p, parents) { + QWidgetList children = lists.values(p); + + if (children.count() > lastList.count()) + lastList = children; + } + + + // If we found no list (because no widget did fit at all) or the + // best list has only one entry and we do not layout a container, + // we leave here. + QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase(); + if (lastList.count() < 2 && + (!m_layoutBase || + (!widgetDataBase->isContainer(m_layoutBase, false) && + m_layoutBase != m_formWindow->mainContainer())) + ) { + m_widgets.clear(); + m_startPoint = QPoint(0, 0); + return; + } + + // Now we have a new and clean widget list, which makes sense + // to layout + m_widgets = lastList; + // Also use the only correct parent later, so store it + + Q_ASSERT(m_widgets.isEmpty() == false); + + m_parentWidget = m_formWindow->core()->widgetFactory()->widgetOfContainer(m_widgets.first()->parentWidget()); + // Now calculate the position where the layout-meta-widget should + // be placed and connect to widgetDestroyed() signals of the + // widgets to get informed if one gets deleted to be able to + // handle that and do not crash in this case + foreach (QWidget *w, m_widgets) { + connect(w, SIGNAL(destroyed()), this, SLOT(widgetDestroyed())); + m_startPoint = QPoint(qMin(m_startPoint.x(), w->x()), qMin(m_startPoint.y(), w->y())); + const QRect rc(w->geometry()); + + m_geometries.insert(w, rc); + // Change the Z-order, as saving/loading uses the Z-order for + // writing/creating widgets and this has to be the same as in + // the layout. Else saving + loading will give different results + w->raise(); + } + + sort(); +} + +void Layout::widgetDestroyed() +{ + if (QWidget *w = qobject_cast<QWidget *>(sender())) { + m_widgets.removeAt(m_widgets.indexOf(w)); + m_geometries.remove(w); + } +} + +bool Layout::prepareLayout(bool &needMove, bool &needReparent) +{ + foreach (QWidget *widget, m_widgets) { + widget->raise(); + } + + needMove = !m_layoutBase; + needReparent = needMove || (m_reparentLayoutWidget && qobject_cast<QLayoutWidget*>(m_layoutBase)) || qobject_cast<QSplitter*>(m_layoutBase); + + QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory(); + QDesignerMetaDataBaseInterface *metaDataBase = m_formWindow->core()->metaDataBase(); + + if (m_layoutBase == 0) { + const bool useSplitter = m_layoutType == LayoutInfo::HSplitter || m_layoutType == LayoutInfo::VSplitter; + const QString baseWidgetClassName = useSplitter ? QLatin1String("QSplitter") : QLatin1String("QLayoutWidget"); + m_layoutBase = widgetFactory->createWidget(baseWidgetClassName, widgetFactory->containerOfWidget(m_parentWidget)); + if (useSplitter) { + m_layoutBase->setObjectName(QLatin1String("splitter")); + m_formWindow->ensureUniqueObjectName(m_layoutBase); + } + } else { + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + } + + metaDataBase->add(m_layoutBase); + + Q_ASSERT(m_layoutBase->layout() == 0 || metaDataBase->item(m_layoutBase->layout()) == 0); + + return true; +} + +static bool isMainContainer(QDesignerFormWindowInterface *fw, const QWidget *w) +{ + return w && (w == fw || w == fw->mainContainer()); +} + +static bool isPageOfContainerWidget(QDesignerFormWindowInterface *fw, QWidget *widget) +{ + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>( + fw->core()->extensionManager(), widget->parentWidget()); + + if (c != 0) { + for (int i = 0; i<c->count(); ++i) { + if (widget == c->widget(i)) + return true; + } + } + + return false; +} +void Layout::finishLayout(bool needMove, QLayout *layout) +{ + if (m_parentWidget == m_layoutBase) { + QWidget *widget = m_layoutBase; + m_oldGeometry = widget->geometry(); + + bool done = false; + while (!isMainContainer(m_formWindow, widget) && !done) { + if (!m_formWindow->isManaged(widget)) { + widget = widget->parentWidget(); + continue; + } else if (LayoutInfo::isWidgetLaidout(m_formWindow->core(), widget)) { + widget = widget->parentWidget(); + continue; + } else if (isPageOfContainerWidget(m_formWindow, widget)) { + widget = widget->parentWidget(); + continue; + } else if (widget->parentWidget()) { + QScrollArea *area = qobject_cast<QScrollArea*>(widget->parentWidget()->parentWidget()); + if (area && area->widget() == widget) { + widget = area; + continue; + } + } + + done = true; + } + updateWizardLayout(m_layoutBase); + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + // We don't want to resize the form window + if (!Utils::isCentralWidget(m_formWindow, widget)) + widget->adjustSize(); + + return; + } + + if (needMove) + m_layoutBase->move(m_startPoint); + + const QRect g(m_layoutBase->pos(), m_layoutBase->size()); + + if (LayoutInfo::layoutType(m_formWindow->core(), m_layoutBase->parentWidget()) == LayoutInfo::NoLayout && !m_isBreak) + m_layoutBase->adjustSize(); + else if (m_isBreak) + m_layoutBase->setGeometry(m_oldGeometry); + + m_oldGeometry = g; + if (layout) + layout->invalidate(); + m_layoutBase->show(); + + if (qobject_cast<QLayoutWidget*>(m_layoutBase) || qobject_cast<QSplitter*>(m_layoutBase)) { + m_formWindow->clearSelection(false); + m_formWindow->manageWidget(m_layoutBase); + m_formWindow->selectWidget(m_layoutBase); + } +} + +void Layout::undoLayout() +{ + if (!m_widgets.count()) + return; + + m_formWindow->selectWidget(m_layoutBase, false); + + QDesignerWidgetFactoryInterface *widgetFactory = m_formWindow->core()->widgetFactory(); + QHashIterator<QWidget *, QRect> it(m_geometries); + while (it.hasNext()) { + it.next(); + + if (!it.key()) + continue; + + QWidget* w = it.key(); + const QRect rc = it.value(); + + const bool showIt = w->isVisibleTo(m_formWindow); + QWidget *container = widgetFactory->containerOfWidget(m_parentWidget); + + // ### remove widget here + QWidget *parentWidget = w->parentWidget(); + QDesignerFormEditorInterface *core = m_formWindow->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget); + + if (deco) + deco->removeWidget(w); + + w->setParent(container); + w->setGeometry(rc); + + if (showIt) + w->show(); + } + + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + + if (m_parentWidget != m_layoutBase && !qobject_cast<QMainWindow*>(m_layoutBase)) { + m_formWindow->unmanageWidget(m_layoutBase); + m_layoutBase->hide(); + } else { + QMainWindow *mw = qobject_cast<QMainWindow*>(m_formWindow->mainContainer()); + if (m_layoutBase != m_formWindow->mainContainer() && + (!mw || mw->centralWidget() != m_layoutBase)) + m_layoutBase->setGeometry(m_oldGeometry); + } +} + +void Layout::breakLayout() +{ + typedef QMap<QWidget *, QRect> WidgetRectMap; + WidgetRectMap rects; + /* Store the geometry of the widgets. The idea is to give the user space + * to rearrange them, so, we do a adjustSize() on them, unless they want + * to grow (expanding widgets like QTextEdit), in which the geometry is + * preserved. Note that historically, geometries were re-applied + * only after breaking splitters. */ + foreach (QWidget *w, m_widgets) { + const QRect geom = w->geometry(); + const QSize sizeHint = w->sizeHint(); + const bool restoreGeometry = sizeHint.isEmpty() || sizeHint.width() > geom.width() || sizeHint.height() > geom.height(); + rects.insert(w, restoreGeometry ? w->geometry() : QRect(geom.topLeft(), QSize())); + } + const QPoint m_layoutBasePos = m_layoutBase->pos(); + QDesignerWidgetDataBaseInterface *widgetDataBase = m_formWindow->core()->widgetDataBase(); + + LayoutInfo::deleteLayout(m_formWindow->core(), m_layoutBase); + + const bool needReparent = (m_reparentLayoutWidget && qobject_cast<QLayoutWidget*>(m_layoutBase)) || + qobject_cast<QSplitter*>(m_layoutBase) || + (!widgetDataBase->isContainer(m_layoutBase, false) && + m_layoutBase != m_formWindow->mainContainer()); + const bool add = m_geometries.isEmpty(); + + QMapIterator<QWidget*, QRect> it(rects); + while (it.hasNext()) { + it.next(); + + QWidget *w = it.key(); + if (needReparent) { + w->setParent(m_layoutBase->parentWidget(), 0); + w->move(m_layoutBasePos + it.value().topLeft()); + w->show(); + } + + const QRect oldGeometry = it.value(); + if (oldGeometry.isEmpty()) { + w->adjustSize(); + } else { + w->resize(oldGeometry.size()); + } + + if (add) + m_geometries.insert(w, QRect(w->pos(), w->size())); + } + + if (needReparent) { + m_layoutBase->hide(); + m_parentWidget = m_layoutBase->parentWidget(); + m_formWindow->unmanageWidget(m_layoutBase); + } else { + m_parentWidget = m_layoutBase; + } + updateWizardLayout(m_layoutBase); + + if (!m_widgets.isEmpty() && m_widgets.first() && m_widgets.first()->isVisibleTo(m_formWindow)) + m_formWindow->selectWidget(m_widgets.first()); + else + m_formWindow->selectWidget(m_formWindow); +} + +static QString suggestLayoutName(const char *className) +{ + // Legacy + if (!qstrcmp(className, "QHBoxLayout")) + return QLatin1String("horizontalLayout"); + if (!qstrcmp(className, "QVBoxLayout")) + return QLatin1String("verticalLayout"); + if (!qstrcmp(className, "QGridLayout")) + return QLatin1String("gridLayout"); + + return qtify(QString::fromUtf8(className)); +} +QLayout *Layout::createLayout(int type) +{ + Q_ASSERT(m_layoutType != LayoutInfo::HSplitter && m_layoutType != LayoutInfo::VSplitter); + QLayout *layout = m_formWindow->core()->widgetFactory()->createLayout(m_layoutBase, 0, type); + // set a name + layout->setObjectName(suggestLayoutName(layout->metaObject()->className())); + m_formWindow->ensureUniqueObjectName(layout); + // QLayoutWidget + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_formWindow->core()->extensionManager(), layout); + if (sheet && qobject_cast<QLayoutWidget*>(m_layoutBase)) { + sheet->setProperty(sheet->indexOf(QLatin1String("leftMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("topMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("rightMargin")), 0); + sheet->setProperty(sheet->indexOf(QLatin1String("bottomMargin")), 0); + } + return layout; +} + +void Layout::reparentToLayoutBase(QWidget *w) +{ + if (w->parent() != m_layoutBase) { + w->setParent(m_layoutBase, 0); + w->move(QPoint(0,0)); + } +} + +namespace { // within qdesigner_internal + +// ----- PositionSortPredicate: Predicate to be usable as LessThan function to sort widgets by position +class PositionSortPredicate { +public: + PositionSortPredicate(Qt::Orientation orientation) : m_orientation(orientation) {} + bool operator()(const QWidget* w1, const QWidget* w2) { + return m_orientation == Qt::Horizontal ? w1->x() < w2->x() : w1->y() < w2->y(); + } + private: + const Qt::Orientation m_orientation; +}; + +// -------- BoxLayout +class BoxLayout : public Layout +{ +public: + BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation); + + virtual void doLayout(); + virtual void sort(); + +private: + const Qt::Orientation m_orientation; +}; + +BoxLayout::BoxLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation) : + Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox), + m_orientation(orientation) +{ +} + +void BoxLayout::sort() +{ + QWidgetList wl = widgets(); + qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation)); + setWidgets(wl); +} + +void BoxLayout::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + QBoxLayout *layout = static_cast<QBoxLayout *>(createLayout(m_orientation == Qt::Horizontal ? LayoutInfo::HBox : LayoutInfo::VBox)); + + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (needReparent) + reparentToLayoutBase(w); + + if (const Spacer *spacer = qobject_cast<const Spacer*>(w)) + layout->addWidget(w, 0, spacer->alignment()); + else + layout->addWidget(w); + w->show(); + } + finishLayout(needMove, layout); +} + +// -------- SplitterLayout +class SplitterLayout : public Layout +{ +public: + SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation); + + virtual void doLayout(); + virtual void sort(); + +private: + const Qt::Orientation m_orientation; +}; + +SplitterLayout::SplitterLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, + Qt::Orientation orientation) : + Layout(wl, p, fw, lb, orientation == Qt::Horizontal ? LayoutInfo::HSplitter : LayoutInfo::VSplitter), + m_orientation(orientation) +{ +} + +void SplitterLayout::sort() +{ + QWidgetList wl = widgets(); + qStableSort(wl.begin(), wl.end(), PositionSortPredicate(m_orientation)); + setWidgets(wl); +} + +void SplitterLayout::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + QSplitter *splitter = qobject_cast<QSplitter*>(layoutBaseWidget()); + Q_ASSERT(splitter != 0); + + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (needReparent) + reparentToLayoutBase(w); + splitter->addWidget(w); + w->show(); + } + + splitter->setOrientation(m_orientation); + finishLayout(needMove); +} + +// ---------- Grid: Helper for laying out grids + +class Grid +{ +public: + enum Mode { + GridLayout, // Arbitrary size/supports span + FormLayout // 2-column/no span + }; + + Grid(Mode mode); + void resize(int nrows, int ncols); + + ~Grid(); + + QWidget* cell(int row, int col) const { return m_cells[ row * m_ncols + col]; } + + void setCells(const QRect &c, QWidget* w); + + bool empty() const { return m_nrows * m_ncols; } + int numRows() const { return m_nrows; } + int numCols() const { return m_ncols; } + + void simplify(); + bool locateWidget(QWidget* w, int& row, int& col, int& rowspan, int& colspan) const; + + QDebug debug(QDebug str) const; + +private: + void setCell(int row, int col, QWidget* w) { m_cells[ row * m_ncols + col] = w; } + void swapCells(int r1, int c1, int r2, int c2); + void shrink(); + void reallocFormLayout(); + int countRow(int r, int c) const; + int countCol(int r, int c) const; + void setRow(int r, int c, QWidget* w, int count); + void setCol(int r, int c, QWidget* w, int count); + bool isWidgetStartCol(int c) const; + bool isWidgetEndCol(int c) const; + bool isWidgetStartRow(int r) const; + bool isWidgetEndRow(int r) const; + bool isWidgetTopLeft(int r, int c) const; + void extendLeft(); + void extendRight(); + void extendUp(); + void extendDown(); + bool shrinkFormLayoutSpans(); + + const Mode m_mode; + int m_nrows; + int m_ncols; + + QWidget** m_cells; // widget matrix w11, w12, w21... +}; + +Grid::Grid(Mode mode) : + m_mode(mode), + m_nrows(0), + m_ncols(0), + m_cells(0) +{ +} + +Grid::~Grid() +{ + delete [] m_cells; +} + +void Grid::resize(int nrows, int ncols) +{ + delete [] m_cells; + m_cells = 0; + m_nrows = nrows; + m_ncols = ncols; + if (const int allocSize = m_nrows * m_ncols) { + m_cells = new QWidget*[allocSize]; + qFill(m_cells, m_cells + allocSize, static_cast<QWidget *>(0)); + } +} + +QDebug Grid::debug(QDebug str) const +{ + str << m_nrows << 'x' << m_ncols << '\n'; + QSet<QWidget *> widgets; + const int cellCount = m_nrows * m_ncols; + int row, col, rowspan, colspan; + for (int c = 0; c < cellCount; c++) + if (QWidget *w = m_cells[c]) + if (!widgets.contains(w)) { + widgets.insert(w); + locateWidget(w, row, col, rowspan, colspan); + str << w << " at " << row << col << rowspan << 'x' << colspan << '\n'; + } + for (int r = 0; r < m_nrows; r++) + for (int c = 0; c < m_ncols; c++) + str << "At " << r << c << cell(r, c) << '\n'; + + return str; +} + +static inline QDebug operator<<(QDebug str, const Grid &g) { return g.debug(str); } + +void Grid::setCells(const QRect &c, QWidget* w) +{ + const int bottom = c.top() + c.height(); + const int width = c.width(); + + for (int r = c.top(); r < bottom; r++) { + QWidget **pos = m_cells + r * m_ncols + c.left(); + qFill(pos, pos + width, w); + } +} + + +void Grid::swapCells(int r1, int c1, int r2, int c2) +{ + QWidget *w1 = cell(r1, c1); + setCell(r1, c1, cell(r2, c2)); + setCell(r2, c2, w1); +} + +int Grid::countRow(int r, int c) const +{ + QWidget* w = cell(r, c); + int i = c + 1; + while (i < m_ncols && cell(r, i) == w) + i++; + return i - c; +} + +int Grid::countCol(int r, int c) const +{ + QWidget* w = cell(r, c); + int i = r + 1; + while (i < m_nrows && cell(i, c) == w) + i++; + return i - r; +} + +void Grid::setCol(int r, int c, QWidget* w, int count) +{ + for (int i = 0; i < count; i++) + setCell(r + i, c, w); +} + +void Grid::setRow(int r, int c, QWidget* w, int count) +{ + for (int i = 0; i < count; i++) + setCell(r, c + i, w); +} + +bool Grid::isWidgetStartCol(int c) const +{ + for (int r = 0; r < m_nrows; r++) { + if (cell(r, c) && ((c==0) || (cell(r, c) != cell(r, c-1)))) { + return true; + } + } + return false; +} + +bool Grid::isWidgetEndCol(int c) const +{ + for (int r = 0; r < m_nrows; r++) { + if (cell(r, c) && ((c == m_ncols-1) || (cell(r, c) != cell(r, c+1)))) + return true; + } + return false; +} + +bool Grid::isWidgetStartRow(int r) const +{ + for ( int c = 0; c < m_ncols; c++) { + if (cell(r, c) && ((r==0) || (cell(r, c) != cell(r-1, c)))) + return true; + } + return false; +} + +bool Grid::isWidgetEndRow(int r) const +{ + for (int c = 0; c < m_ncols; c++) { + if (cell(r, c) && ((r == m_nrows-1) || (cell(r, c) != cell(r+1, c)))) + return true; + } + return false; +} + + +bool Grid::isWidgetTopLeft(int r, int c) const +{ + QWidget* w = cell(r, c); + if (!w) + return false; + return (!r || cell(r-1, c) != w) && (!c || cell(r, c-1) != w); +} + +void Grid::extendLeft() +{ + for (int c = 1; c < m_ncols; c++) { + for (int r = 0; r < m_nrows; r++) { + QWidget* w = cell(r, c); + if (!w) + continue; + + const int cc = countCol(r, c); + int stretch = 0; + for (int i = c-1; i >= 0; i--) { + if (cell(r, i)) + break; + if (countCol(r, i) < cc) + break; + if (isWidgetEndCol(i)) + break; + if (isWidgetStartCol(i)) { + stretch = c - i; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setCol(r, c-i-1, w, cc); + } + } + } +} + + +void Grid::extendRight() +{ + for (int c = m_ncols - 2; c >= 0; c--) { + for (int r = 0; r < m_nrows; r++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cc = countCol(r, c); + int stretch = 0; + for (int i = c+1; i < m_ncols; i++) { + if (cell(r, i)) + break; + if (countCol(r, i) < cc) + break; + if (isWidgetStartCol(i)) + break; + if (isWidgetEndCol(i)) { + stretch = i - c; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setCol(r, c+i+1, w, cc); + } + } + } + +} + +void Grid::extendUp() +{ + for (int r = 1; r < m_nrows; r++) { + for (int c = 0; c < m_ncols; c++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cr = countRow(r, c); + int stretch = 0; + for (int i = r-1; i >= 0; i--) { + if (cell(i, c)) + break; + if (countRow(i, c) < cr) + break; + if (isWidgetEndRow(i)) + break; + if (isWidgetStartRow(i)) { + stretch = r - i; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setRow(r-i-1, c, w, cr); + } + } + } +} + +void Grid::extendDown() +{ + for (int r = m_nrows - 2; r >= 0; r--) { + for (int c = 0; c < m_ncols; c++) { + QWidget* w = cell(r, c); + if (!w) + continue; + const int cr = countRow(r, c); + int stretch = 0; + for (int i = r+1; i < m_nrows; i++) { + if (cell(i, c)) + break; + if (countRow(i, c) < cr) + break; + if (isWidgetStartRow(i)) + break; + if (isWidgetEndRow(i)) { + stretch = i - r; + break; + } + } + if (stretch) { + for (int i = 0; i < stretch; i++) + setRow(r+i+1, c, w, cr); + } + } + } +} + +void Grid::simplify() +{ + switch (m_mode) { + case GridLayout: + // Grid: Extend all widgets to occupy most space and delete + // rows/columns that are not bordering on a widget + extendLeft(); + extendRight(); + extendUp(); + extendDown(); + shrink(); + break; + case FormLayout: + // Form: First treat it as a grid to get the same behaviour + // regarding spanning and shrinking. Then restrict the span to + // the horizontal span possible in the form, simplify again + // and spread the widgets over a 2-column layout + extendLeft(); + extendRight(); + extendUp(); + extendDown(); + shrink(); + if (shrinkFormLayoutSpans()) + shrink(); + reallocFormLayout(); + break; + } + +} + +void Grid::shrink() +{ + // tick off the occupied cols/rows (bordering on widget edges) + QVector<bool> columns(m_ncols, false); + QVector<bool> rows(m_nrows, false); + + for (int c = 0; c < m_ncols; c++) + for (int r = 0; r < m_nrows; r++) + if (isWidgetTopLeft(r, c)) + rows[r] = columns[c] = true; + + // remove empty cols/rows + const int simplifiedNCols = columns.count(true); + const int simplifiedNRows = rows.count(true); + if (simplifiedNCols == m_ncols && simplifiedNRows == m_nrows) + return; + // reallocate and copy omitting the empty cells + QWidget **simplifiedCells = new QWidget*[simplifiedNCols * simplifiedNRows]; + qFill(simplifiedCells, simplifiedCells + simplifiedNCols * simplifiedNRows, static_cast<QWidget *>(0)); + QWidget **simplifiedPtr = simplifiedCells; + + for (int r = 0; r < m_nrows; r++) + if (rows[r]) + for (int c = 0; c < m_ncols; c++) + if (columns[c]) { + if (QWidget *w = cell(r, c)) + *simplifiedPtr = w; + simplifiedPtr++; + } + Q_ASSERT(simplifiedPtr == simplifiedCells + simplifiedNCols * simplifiedNRows); + delete [] m_cells; + m_cells = simplifiedCells; + m_nrows = simplifiedNRows; + m_ncols = simplifiedNCols; +} + +bool Grid::shrinkFormLayoutSpans() +{ + bool shrunk = false; + typedef QSet<QWidget*> WidgetSet; + // Determine unique set of widgets + WidgetSet widgets; + QWidget **end = m_cells + m_ncols * m_nrows; + for (QWidget **wptr = m_cells; wptr < end; wptr++) + if (QWidget *w = *wptr) + widgets.insert(w); + // Restrict the widget span: max horizontal span at column 0: 2, anything else: 1 + const int maxRowSpan = 1; + const WidgetSet::const_iterator cend = widgets.constEnd(); + for (WidgetSet::const_iterator it = widgets.constBegin(); it != cend ; ++it) { + QWidget *w = *it; + int row, col, rowspan, colspan; + locateWidget(w, row, col, rowspan, colspan); + const int maxColSpan = col == 0 ? 2 : 1; + const int newColSpan = qMin(colspan, maxColSpan); + const int newRowSpan = qMin(rowspan, maxRowSpan); + if (newColSpan != colspan || newRowSpan != rowspan) { + setCells(QRect(col, row, colspan, rowspan), 0); + setCells(QRect(col, row, newColSpan, newRowSpan), w); + shrunk = true; + } + } + return shrunk; +} + +void Grid::reallocFormLayout() +{ + // Columns matching? -> happy! + if (m_ncols == FormLayoutColumns) + return; + + // If there are offset columns (starting past the field column), + // move them to the left and squeeze them. This also prevents the + // following reallocation from creating empty form rows. + int pastRightWidgetCount = 0; + if (m_ncols > FormLayoutColumns) { + for (int r = 0; r < m_nrows; r++) { + // Try to find a column where the form columns are empty and + // there are widgets further to the right. + if (cell(r, 0) == 0 && cell(r, 1) == 0) { + int sourceCol = FormLayoutColumns; + QWidget *firstWidget = 0; + for ( ; sourceCol < m_ncols; sourceCol++) + if (QWidget *w = cell(r, sourceCol)) { + firstWidget = w; + break; + } + if (firstWidget) { + // Move/squeeze. Copy to beginning of column if it is a label, else field + int targetCol = qobject_cast<QLabel*>(firstWidget) ? 0 : 1; + for ( ; sourceCol < m_ncols; sourceCol++) + if (QWidget *w = cell(r, sourceCol)) + setCell(r, targetCol++, w); + // Pad with zero + for ( ; targetCol < m_ncols; targetCol++) + setCell(r, targetCol, 0); + } + } + // Any protruding widgets left on that row? + for (int c = FormLayoutColumns; c < m_ncols; c++) + if (cell(r, c)) + pastRightWidgetCount++; + } + } + // Reallocate with 2 columns. Just insert the protruding ones as fields. + const int formNRows = m_nrows + pastRightWidgetCount; + QWidget **formCells = new QWidget*[FormLayoutColumns * formNRows]; + qFill(formCells, formCells + FormLayoutColumns * formNRows, static_cast<QWidget *>(0)); + QWidget **formPtr = formCells; + const int matchingColumns = qMin(m_ncols, static_cast<int>(FormLayoutColumns)); + for (int r = 0; r < m_nrows; r++) { + int c = 0; + for ( ; c < matchingColumns; c++) // Just copy over matching columns + *formPtr++ = cell(r, c); + formPtr += FormLayoutColumns - matchingColumns; // In case old format was 1 column + // protruding widgets: Insert as single-field rows + for ( ; c < m_ncols; c++) + if (QWidget *w = cell(r, c)) { + formPtr++; + *formPtr++ = w; + } + } + Q_ASSERT(formPtr == formCells + FormLayoutColumns * formNRows); + delete [] m_cells; + m_cells = formCells; + m_nrows = formNRows; + m_ncols = FormLayoutColumns; +} + +bool Grid::locateWidget(QWidget *w, int &row, int &col, int &rowspan, int &colspan) const +{ + const int end = m_nrows * m_ncols; + const int startIndex = qFind(m_cells, m_cells + end, w) - m_cells; + if (startIndex == end) + return false; + + row = startIndex / m_ncols; + col = startIndex % m_ncols; + for (rowspan = 1; row + rowspan < m_nrows && cell(row + rowspan, col) == w; rowspan++) {} + for (colspan = 1; col + colspan < m_ncols && cell(row, col + colspan) == w; colspan++) {} + return true; +} + +// QGridLayout/QFormLayout Helpers: get item position/add item (overloads to make templates work) + +void getGridItemPosition(QGridLayout *gridLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + gridLayout->getItemPosition(index, row, column, rowspan, colspan); +} + +void addWidgetToGrid(QGridLayout *lt, QWidget * widget, int row, int column, int rowSpan, int columnSpan, Qt::Alignment alignment) +{ + lt->addWidget(widget, row, column, rowSpan, columnSpan, alignment); +} + +inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan); +} + +inline void addWidgetToGrid(QFormLayout *lt, QWidget * widget, int row, int column, int, int columnSpan, Qt::Alignment) +{ + formLayoutAddWidget(lt, widget, QRect(column, row, columnSpan, 1), false); +} + +// ----------- Base template for grid like layouts +template <class GridLikeLayout, int LayoutType, int GridMode> +class GridLayout : public Layout +{ +public: + GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb); + + virtual void doLayout(); + virtual void sort() { setWidgets(buildGrid(widgets())); } + +protected: + QWidget *widgetAt(GridLikeLayout *layout, int row, int column) const; + +protected: + QWidgetList buildGrid(const QWidgetList &); + Grid m_grid; +}; + +template <class GridLikeLayout, int LayoutType, int GridMode> +GridLayout<GridLikeLayout, LayoutType, GridMode>::GridLayout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb) : + Layout(wl, p, fw, lb, LayoutInfo::Grid), + m_grid(static_cast<Grid::Mode>(GridMode)) +{ +} + +template <class GridLikeLayout, int LayoutType, int GridMode> +QWidget *GridLayout<GridLikeLayout, LayoutType, GridMode>::widgetAt(GridLikeLayout *layout, int row, int column) const +{ + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + if (item->widget()) { + int r, c, rowspan, colspan; + getGridItemPosition(layout, index, &r, &c, &rowspan, &colspan); + if (row == r && column == c) + return item->widget(); + } + ++index; + } + return 0; +} + +template <class GridLikeLayout, int LayoutType, int GridMode> +void GridLayout<GridLikeLayout, LayoutType, GridMode>::doLayout() +{ + bool needMove, needReparent; + if (!prepareLayout(needMove, needReparent)) + return; + + GridLikeLayout *layout = static_cast<GridLikeLayout *>(createLayout(LayoutType)); + + if (m_grid.empty()) + sort(); + + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + + const QWidgetList::const_iterator cend = widgets().constEnd(); + for (QWidgetList::const_iterator it = widgets().constBegin(); it != cend; ++it) { + QWidget *w = *it; + int r = 0, c = 0, rs = 0, cs = 0; + + if (m_grid.locateWidget(w, r, c, rs, cs)) { + if (needReparent) + reparentToLayoutBase(w); + + Qt::Alignment alignment = Qt::Alignment(0); + if (const Spacer *spacer = qobject_cast<const Spacer*>(w)) + alignment = spacer->alignment(); + + if (rs * cs == 1) { + addWidgetToGrid(layout, w, r, c, 1, 1, alignment); + } else { + addWidgetToGrid(layout, w, r, c, rs, cs, alignment); + } + + w->show(); + } else { + qDebug("ooops, widget '%s' does not fit in layout", w->objectName().toUtf8().constData()); + } + } + + QLayoutSupport::createEmptyCells(layout); + + finishLayout(needMove, layout); +} + +// Remove duplicate entries (Remove next, if equal to current) +void removeIntVecDuplicates(QVector<int> &v) +{ + if (v.size() < 2) + return; + + for (QVector<int>::iterator current = v.begin() ; (current != v.end()) && ((current+1) != v.end()) ; ) + if ( (*current == *(current+1)) ) + v.erase(current+1); + else + ++current; +} + +// Ensure a non-zero size for a widget geometry (squeezed spacers) +inline QRect expandGeometry(const QRect &rect) +{ + return rect.isEmpty() ? QRect(rect.topLeft(), rect.size().expandedTo(QSize(1, 1))) : rect; +} + +template <class GridLikeLayout, int LayoutType, int GridMode> +QWidgetList GridLayout<GridLikeLayout, LayoutType, GridMode>::buildGrid(const QWidgetList &widgetList) +{ + if (widgetList.empty()) + return QWidgetList(); + + // Pixel to cell conversion: + // By keeping a list of start'n'stop values (x & y) for each widget, + // it is possible to create a very small grid of cells to represent + // the widget layout. + // ----------------------------------------------------------------- + + // We need a list of both start and stop values for x- & y-axis + const int widgetCount = widgetList.size(); + QVector<int> x( widgetCount * 2 ); + QVector<int> y( widgetCount * 2 ); + + // Using push_back would look nicer, but operator[] is much faster + int index = 0; + for (int i = 0; i < widgetCount; ++i) { + const QRect widgetPos = expandGeometry(widgetList.at(i)->geometry()); + x[index] = widgetPos.left(); + x[index+1] = widgetPos.right(); + y[index] = widgetPos.top(); + y[index+1] = widgetPos.bottom(); + index += 2; + } + + qSort(x); + qSort(y); + + // Remove duplicate x entries (Remove next, if equal to current) + removeIntVecDuplicates(x); + removeIntVecDuplicates(y); + + // Note that left == right and top == bottom for size 1 items; reserve + // enough space + m_grid.resize(y.size(), x.size()); + + const QWidgetList::const_iterator cend = widgetList.constEnd(); + for (QWidgetList::const_iterator it = widgetList.constBegin(); it != cend; ++it) { + QWidget *w = *it; + // Mark the cells in the grid that contains a widget + const QRect widgetPos = expandGeometry(w->geometry()); + QRect c(0, 0, 0, 0); // rect of columns/rows + + // From left til right (not including) + const int leftIdx = x.indexOf(widgetPos.left()); + Q_ASSERT(leftIdx != -1); + c.setLeft(leftIdx); + c.setRight(leftIdx); + for (int cw=leftIdx; cw<x.size(); cw++) + if (x[cw] < widgetPos.right()) + c.setRight(cw); + else + break; + // From top til bottom (not including) + const int topIdx = y.indexOf(widgetPos.top()); + Q_ASSERT(topIdx != -1); + c.setTop(topIdx); + c.setBottom(topIdx); + for (int ch=topIdx; ch<y.size(); ch++) + if (y[ch] < widgetPos.bottom()) + c.setBottom(ch); + else + break; + m_grid.setCells(c, w); // Mark cellblock + } + + m_grid.simplify(); + + QWidgetList ordered; + for (int i = 0; i < m_grid.numRows(); i++) + for (int j = 0; j < m_grid.numCols(); j++) { + QWidget *w = m_grid.cell(i, j); + if (w && !ordered.contains(w)) + ordered.append(w); + } + return ordered; +} +} // anonymous + +Layout* Layout::createLayout(const QWidgetList &widgets, QWidget *parentWidget, + QDesignerFormWindowInterface *fw, + QWidget *layoutBase, LayoutInfo::Type layoutType) +{ + switch (layoutType) { + case LayoutInfo::Grid: + return new GridLayout<QGridLayout, LayoutInfo::Grid, Grid::GridLayout>(widgets, parentWidget, fw, layoutBase); + case LayoutInfo::HBox: + case LayoutInfo::VBox: { + const Qt::Orientation orientation = layoutType == LayoutInfo::HBox ? Qt::Horizontal : Qt::Vertical; + return new BoxLayout(widgets, parentWidget, fw, layoutBase, orientation); + } + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + const Qt::Orientation orientation = layoutType == LayoutInfo::HSplitter ? Qt::Horizontal : Qt::Vertical; + return new SplitterLayout(widgets, parentWidget, fw, layoutBase, orientation); + } + case LayoutInfo::Form: + return new GridLayout<QFormLayout, LayoutInfo::Form, Grid::FormLayout>(widgets, parentWidget, fw, layoutBase); + default: + break; + } + Q_ASSERT(0); + return 0; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/layout_p.h b/tools/designer/src/lib/shared/layout_p.h new file mode 100644 index 0000000..8396876 --- /dev/null +++ b/tools/designer/src/lib/shared/layout_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef LAYOUT_H +#define LAYOUT_H + +#include "shared_global_p.h" +#include "layoutinfo_p.h" + +#include <QtCore/QPointer> +#include <QtCore/QObject> +#include <QtCore/QMap> +#include <QtCore/QHash> + +#include <QtGui/QLayout> +#include <QtGui/QGridLayout> +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { +class QDESIGNER_SHARED_EXPORT Layout : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(Layout) +protected: + Layout(const QWidgetList &wl, QWidget *p, QDesignerFormWindowInterface *fw, QWidget *lb, LayoutInfo::Type layoutType); + +public: + static Layout* createLayout(const QWidgetList &widgets, QWidget *parentWidget, + QDesignerFormWindowInterface *fw, + QWidget *layoutBase, LayoutInfo::Type layoutType); + + virtual ~Layout(); + + virtual void sort() = 0; + virtual void doLayout() = 0; + + virtual void setup(); + virtual void undoLayout(); + virtual void breakLayout(); + + const QWidgetList &widgets() const { return m_widgets; } + QWidget *parentWidget() const { return m_parentWidget; } + QWidget *layoutBaseWidget() const { return m_layoutBase; } + + /* Determines whether instances of QLayoutWidget are unmanaged/hidden + * after breaking a layout. Default is true. Can be turned off when + * morphing */ + bool reparentLayoutWidget() const { return m_reparentLayoutWidget; } + void setReparentLayoutWidget(bool v) { m_reparentLayoutWidget = v; } + +protected: + virtual void finishLayout(bool needMove, QLayout *layout = 0); + virtual bool prepareLayout(bool &needMove, bool &needReparent); + + void setWidgets(const QWidgetList &widgets) { m_widgets = widgets; } + QLayout *createLayout(int type); + void reparentToLayoutBase(QWidget *w); + +private slots: + void widgetDestroyed(); + +private: + QWidgetList m_widgets; + QWidget *m_parentWidget; + typedef QHash<QWidget *, QRect> WidgetGeometryHash; + WidgetGeometryHash m_geometries; + QWidget *m_layoutBase; + QDesignerFormWindowInterface *m_formWindow; + const LayoutInfo::Type m_layoutType; + QPoint m_startPoint; + QRect m_oldGeometry; + + bool m_reparentLayoutWidget; + const bool m_isBreak; +}; + +namespace Utils +{ + +inline int indexOfWidget(QLayout *layout, QWidget *widget) +{ + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + if (item->widget() == widget) + return index; + + ++index; + } + + return -1; +} + +} // namespace Utils + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LAYOUT_H diff --git a/tools/designer/src/lib/shared/layoutinfo.cpp b/tools/designer/src/lib/shared/layoutinfo.cpp new file mode 100644 index 0000000..01eb43a --- /dev/null +++ b/tools/designer/src/lib/shared/layoutinfo.cpp @@ -0,0 +1,312 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "layoutinfo_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QHBoxLayout> +#include <QtGui/QFormLayout> +#include <QtGui/QSplitter> +#include <QtCore/QDebug> +#include <QtCore/QHash> +#include <QtCore/QRect> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +/*! + \overload +*/ +LayoutInfo::Type LayoutInfo::layoutType(const QDesignerFormEditorInterface *core, const QLayout *layout) +{ + Q_UNUSED(core) + if (!layout) + return NoLayout; + else if (qobject_cast<const QHBoxLayout*>(layout)) + return HBox; + else if (qobject_cast<const QVBoxLayout*>(layout)) + return VBox; + else if (qobject_cast<const QGridLayout*>(layout)) + return Grid; + else if (qobject_cast<const QFormLayout*>(layout)) + return Form; + return UnknownLayout; +} + +static const QHash<QString, LayoutInfo::Type> &layoutNameTypeMap() +{ + static QHash<QString, LayoutInfo::Type> nameTypeMap; + if (nameTypeMap.empty()) { + nameTypeMap.insert(QLatin1String("QVBoxLayout"), LayoutInfo::VBox); + nameTypeMap.insert(QLatin1String("QHBoxLayout"), LayoutInfo::HBox); + nameTypeMap.insert(QLatin1String("QGridLayout"), LayoutInfo::Grid); + nameTypeMap.insert(QLatin1String("QFormLayout"), LayoutInfo::Form); + } + return nameTypeMap; +} + +LayoutInfo::Type LayoutInfo::layoutType(const QString &typeName) +{ + return layoutNameTypeMap().value(typeName, NoLayout); +} + +QString LayoutInfo::layoutName(Type t) +{ + return layoutNameTypeMap().key(t); +} + +/*! + \overload +*/ +LayoutInfo::Type LayoutInfo::layoutType(const QDesignerFormEditorInterface *core, const QWidget *w) +{ + if (const QSplitter *splitter = qobject_cast<const QSplitter *>(w)) + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + return layoutType(core, w->layout()); +} + +LayoutInfo::Type LayoutInfo::managedLayoutType(const QDesignerFormEditorInterface *core, + const QWidget *w, + QLayout **ptrToLayout) +{ + if (ptrToLayout) + *ptrToLayout = 0; + if (const QSplitter *splitter = qobject_cast<const QSplitter *>(w)) + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + QLayout *layout = managedLayout(core, w); + if (!layout) + return NoLayout; + if (ptrToLayout) + *ptrToLayout = layout; + return layoutType(core, layout); +} + +QWidget *LayoutInfo::layoutParent(const QDesignerFormEditorInterface *core, QLayout *layout) +{ + Q_UNUSED(core) + + QObject *o = layout; + while (o) { + if (QWidget *widget = qobject_cast<QWidget*>(o)) + return widget; + + o = o->parent(); + } + return 0; +} + +void LayoutInfo::deleteLayout(const QDesignerFormEditorInterface *core, QWidget *widget) +{ + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), widget)) + widget = container->widget(container->currentIndex()); + + Q_ASSERT(widget != 0); + + QLayout *layout = managedLayout(core, widget); + + if (layout == 0 || core->metaDataBase()->item(layout) != 0) { + delete layout; + widget->updateGeometry(); + return; + } + + qDebug() << "trying to delete an unmanaged layout:" << "widget:" << widget << "layout:" << layout; +} + +LayoutInfo::Type LayoutInfo::laidoutWidgetType(const QDesignerFormEditorInterface *core, + QWidget *widget, + bool *isManaged, + QLayout **ptrToLayout) +{ + if (isManaged) + *isManaged = false; + if (ptrToLayout) + *ptrToLayout = 0; + + QWidget *parent = widget->parentWidget(); + if (!parent) + return NoLayout; + + // 1) Splitter + if (QSplitter *splitter = qobject_cast<QSplitter*>(parent)) { + if (isManaged) + *isManaged = core->metaDataBase()->item(splitter); + return splitter->orientation() == Qt::Horizontal ? HSplitter : VSplitter; + } + + // 2) Layout of parent + QLayout *parentLayout = parent->layout(); + if (!parentLayout) + return NoLayout; + + if (parentLayout->indexOf(widget) != -1) { + if (isManaged) + *isManaged = core->metaDataBase()->item(parentLayout); + if (ptrToLayout) + *ptrToLayout = parentLayout; + return layoutType(core, parentLayout); + } + + // 3) Some child layout (see below comment about Q3GroupBox) + const QList<QLayout*> childLayouts = qFindChildren<QLayout*>(parentLayout); + if (childLayouts.empty()) + return NoLayout; + const QList<QLayout*>::const_iterator lcend = childLayouts.constEnd(); + for (QList<QLayout*>::const_iterator it = childLayouts.constBegin(); it != lcend; ++it) { + QLayout *layout = *it; + if (layout->indexOf(widget) != -1) { + if (isManaged) + *isManaged = core->metaDataBase()->item(layout); + if (ptrToLayout) + *ptrToLayout = layout; + return layoutType(core, layout); + } + } + + return NoLayout; +} + +QLayout *LayoutInfo::internalLayout(const QWidget *widget) +{ + QLayout *widgetLayout = widget->layout(); + if (widgetLayout && widget->inherits("Q3GroupBox")) { + if (widgetLayout->count()) { + widgetLayout = widgetLayout->itemAt(0)->layout(); + } else { + widgetLayout = 0; + } + } + return widgetLayout; +} + + +QLayout *LayoutInfo::managedLayout(const QDesignerFormEditorInterface *core, const QWidget *widget) +{ + if (widget == 0) + return 0; + + QLayout *layout = widget->layout(); + if (!layout) + return 0; + + return managedLayout(core, layout); +} + +QLayout *LayoutInfo::managedLayout(const QDesignerFormEditorInterface *core, QLayout *layout) +{ + QDesignerMetaDataBaseInterface *metaDataBase = core->metaDataBase(); + + if (!metaDataBase) + return layout; + /* This code exists mainly for the Q3GroupBox class, for which + * widget->layout() returns an internal VBoxLayout. */ + const QDesignerMetaDataBaseItemInterface *item = metaDataBase->item(layout); + if (item == 0) { + layout = qFindChild<QLayout*>(layout); + item = metaDataBase->item(layout); + } + if (!item) + return 0; + return layout; +} + +// Is it a a dummy grid placeholder created by Designer? +bool LayoutInfo::isEmptyItem(QLayoutItem *item) +{ + if (item == 0) { + qDebug() << "** WARNING Zero-item passed on to isEmptyItem(). This indicates a layout inconsistency."; + return true; + } + return item->spacerItem() != 0; +} + +QDESIGNER_SHARED_EXPORT void getFormLayoutItemPosition(const QFormLayout *formLayout, int index, int *rowPtr, int *columnPtr, int *rowspanPtr, int *colspanPtr) +{ + int row; + QFormLayout::ItemRole role; + formLayout->getItemPosition(index, &row, &role); + const int columnspan = role == QFormLayout::SpanningRole ? 2 : 1; + const int column = (columnspan > 1 || role == QFormLayout::LabelRole) ? 0 : 1; + if (rowPtr) + *rowPtr = row; + if (columnPtr) + *columnPtr = column; + if (rowspanPtr) + *rowspanPtr = 1; + if (colspanPtr) + *colspanPtr = columnspan; +} + +static inline QFormLayout::ItemRole formLayoutRole(int column, int colspan) +{ + if (colspan > 1) + return QFormLayout::SpanningRole; + return column == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; +} + +QDESIGNER_SHARED_EXPORT void formLayoutAddWidget(QFormLayout *formLayout, QWidget *w, const QRect &r, bool insert) +{ + // Consistent API galore... + if (insert) { + const bool spanning = r.width() > 1; + if (spanning) { + formLayout->insertRow(r.y(), w); + } else { + QWidget *label = 0, *field = 0; + if (r.x() == 0) { + label = w; + } else { + field = w; + } + formLayout->insertRow(r.y(), label, field); + } + } else { + formLayout->setWidget(r.y(), formLayoutRole(r.x(), r.width()), w); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/layoutinfo_p.h b/tools/designer/src/lib/shared/layoutinfo_p.h new file mode 100644 index 0000000..64f3f04 --- /dev/null +++ b/tools/designer/src/lib/shared/layoutinfo_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef LAYOUTINFO_H +#define LAYOUTINFO_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QWidget; +class QLayout; +class QLayoutItem; +class QDesignerFormEditorInterface; +class QFormLayout; +class QRect; +class QString; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT LayoutInfo +{ +public: + enum Type + { + NoLayout, + HSplitter, + VSplitter, + HBox, + VBox, + Grid, + Form, + UnknownLayout // QDockWindow inside QMainWindow is inside QMainWindowLayout - it doesn't mean there is no layout + }; + + static void deleteLayout(const QDesignerFormEditorInterface *core, QWidget *widget); + + // Examines the immediate layout of the widget (will fail for Q3Group Box). + static Type layoutType(const QDesignerFormEditorInterface *core, const QWidget *w); + // Examines the managed layout of the widget + static Type managedLayoutType(const QDesignerFormEditorInterface *core, const QWidget *w, QLayout **layout = 0); + static Type layoutType(const QDesignerFormEditorInterface *core, const QLayout *layout); + static Type layoutType(const QString &typeName); + static QString layoutName(Type t); + + static QWidget *layoutParent(const QDesignerFormEditorInterface *core, QLayout *layout); + + static Type laidoutWidgetType(const QDesignerFormEditorInterface *core, QWidget *widget, bool *isManaged = 0, QLayout **layout = 0); + static bool inline isWidgetLaidout(const QDesignerFormEditorInterface *core, QWidget *widget) { return laidoutWidgetType(core, widget) != NoLayout; } + + static QLayout *managedLayout(const QDesignerFormEditorInterface *core, const QWidget *widget); + static QLayout *managedLayout(const QDesignerFormEditorInterface *core, QLayout *layout); + static QLayout *internalLayout(const QWidget *widget); + + // Is it a a dummy grid placeholder created by Designer? + static bool isEmptyItem(QLayoutItem *item); +}; + +QDESIGNER_SHARED_EXPORT void getFormLayoutItemPosition(const QFormLayout *formLayout, int index, int *rowPtr, int *columnPtr = 0, int *rowspanPtr = 0, int *colspanPtr = 0); +QDESIGNER_SHARED_EXPORT void formLayoutAddWidget(QFormLayout *formLayout, QWidget *w, const QRect &r, bool insert); +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // LAYOUTINFO_H diff --git a/tools/designer/src/lib/shared/metadatabase.cpp b/tools/designer/src/lib/shared/metadatabase.cpp new file mode 100644 index 0000000..1403440 --- /dev/null +++ b/tools/designer/src/lib/shared/metadatabase.cpp @@ -0,0 +1,295 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +// sdk +#include <QtDesigner/QDesignerFormEditorInterface> + +// Qt +#include <QtGui/QWidget> +#include <QtCore/qalgorithms.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace { + const bool debugMetaDatabase = false; +} + +namespace qdesigner_internal { + +MetaDataBaseItem::MetaDataBaseItem(QObject *object) + : m_object(object), + m_enabled(true) +{ +} + +MetaDataBaseItem::~MetaDataBaseItem() +{ +} + +QString MetaDataBaseItem::name() const +{ + Q_ASSERT(m_object); + return m_object->objectName(); +} + +void MetaDataBaseItem::setName(const QString &name) +{ + Q_ASSERT(m_object); + m_object->setObjectName(name); +} + +QString MetaDataBaseItem::customClassName() const +{ + return m_customClassName; +} +void MetaDataBaseItem::setCustomClassName(const QString &customClassName) +{ + m_customClassName = customClassName; +} + + +MetaDataBaseItem::TabOrder MetaDataBaseItem::tabOrder() const +{ + return m_tabOrder; +} + +void MetaDataBaseItem::setTabOrder(const TabOrder &tabOrder) +{ + m_tabOrder = tabOrder; +} + +bool MetaDataBaseItem::enabled() const +{ + return m_enabled; +} + +void MetaDataBaseItem::setEnabled(bool b) +{ + m_enabled = b; +} + +QString MetaDataBaseItem::script() const +{ + return m_script; +} + +void MetaDataBaseItem::setScript(const QString &script) +{ + m_script = script; +} + +QStringList MetaDataBaseItem::fakeSlots() const +{ + return m_fakeSlots; +} + +void MetaDataBaseItem::setFakeSlots(const QStringList &fs) +{ + m_fakeSlots = fs; +} + +QStringList MetaDataBaseItem::fakeSignals() const +{ + return m_fakeSignals; +} + +void MetaDataBaseItem::setFakeSignals(const QStringList &fs) +{ + m_fakeSignals = fs; +} + +// ----------------------------------------------------- +MetaDataBase::MetaDataBase(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerMetaDataBaseInterface(parent), + m_core(core) +{ +} + +MetaDataBase::~MetaDataBase() +{ + qDeleteAll(m_items); +} + +MetaDataBaseItem *MetaDataBase::metaDataBaseItem(QObject *object) const +{ + MetaDataBaseItem *i = m_items.value(object); + if (i == 0 || !i->enabled()) + return 0; + return i; +} + +void MetaDataBase::add(QObject *object) +{ + MetaDataBaseItem *item = m_items.value(object); + if (item != 0) { + item->setEnabled(true); + if (debugMetaDatabase) { + qDebug() << "MetaDataBase::add: Existing item for " << object->metaObject()->className() << item->name(); + } + return; + } + + item = new MetaDataBaseItem(object); + m_items.insert(object, item); + if (debugMetaDatabase) { + qDebug() << "MetaDataBase::add: New item " << object->metaObject()->className() << item->name(); + } + connect(object, SIGNAL(destroyed(QObject*)), + this, SLOT(slotDestroyed(QObject*))); + + emit changed(); +} + +void MetaDataBase::remove(QObject *object) +{ + Q_ASSERT(object); + + if (MetaDataBaseItem *item = m_items.value(object)) { + item->setEnabled(false); + emit changed(); + } +} + +QList<QObject*> MetaDataBase::objects() const +{ + QList<QObject*> result; + + ItemMap::const_iterator it = m_items.begin(); + for (; it != m_items.end(); ++it) { + if (it.value()->enabled()) + result.append(it.key()); + } + + return result; +} + +QDesignerFormEditorInterface *MetaDataBase::core() const +{ + return m_core; +} + +void MetaDataBase::slotDestroyed(QObject *object) +{ + if (m_items.contains(object)) { + MetaDataBaseItem *item = m_items.value(object); + delete item; + m_items.remove(object); + } +} + +// promotion convenience +QDESIGNER_SHARED_EXPORT bool promoteWidget(QDesignerFormEditorInterface *core,QWidget *widget,const QString &customClassName) +{ + + MetaDataBase *db = qobject_cast<MetaDataBase *>(core->metaDataBase()); + if (!db) + return false; + MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) { + db ->add(widget); + item = db->metaDataBaseItem(widget); + } + // Recursive promotion occurs if there is a plugin missing. + const QString oldCustomClassName = item->customClassName(); + if (!oldCustomClassName.isEmpty()) { + qDebug() << "WARNING: Recursive promotion of " << oldCustomClassName << " to " << customClassName + << ". A plugin is missing."; + } + item->setCustomClassName(customClassName); + if (debugMetaDatabase) { + qDebug() << "Promoting " << widget->metaObject()->className() << " to " << customClassName; + } + return true; +} + +QDESIGNER_SHARED_EXPORT void demoteWidget(QDesignerFormEditorInterface *core,QWidget *widget) +{ + MetaDataBase *db = qobject_cast<MetaDataBase *>(core->metaDataBase()); + if (!db) + return; + MetaDataBaseItem *item = db->metaDataBaseItem(widget); + item->setCustomClassName(QString()); + if (debugMetaDatabase) { + qDebug() << "Demoting " << widget; + } +} + +QDESIGNER_SHARED_EXPORT bool isPromoted(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const MetaDataBase *db = qobject_cast<const MetaDataBase *>(core->metaDataBase()); + if (!db) + return false; + const MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) + return false; + return !item->customClassName().isEmpty(); +} + +QDESIGNER_SHARED_EXPORT QString promotedCustomClassName(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const MetaDataBase *db = qobject_cast<const MetaDataBase *>(core->metaDataBase()); + if (!db) + return QString(); + const MetaDataBaseItem *item = db->metaDataBaseItem(widget); + if (!item) + return QString(); + return item->customClassName(); +} + +QDESIGNER_SHARED_EXPORT QString promotedExtends(QDesignerFormEditorInterface *core, QWidget* widget) +{ + const QString customClassName = promotedCustomClassName(core,widget); + if (customClassName.isEmpty()) + return QString(); + const int i = core->widgetDataBase()->indexOfClassName(customClassName); + if (i == -1) + return QString(); + return core->widgetDataBase()->item(i)->extends(); +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/metadatabase_p.h b/tools/designer/src/lib/shared/metadatabase_p.h new file mode 100644 index 0000000..874b540 --- /dev/null +++ b/tools/designer/src/lib/shared/metadatabase_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef METADATABASE_H +#define METADATABASE_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerMetaDataBaseInterface> + +#include <QtCore/QHash> +#include <QtCore/QStringList> +#include <QtGui/QCursor> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT MetaDataBaseItem: public QDesignerMetaDataBaseItemInterface +{ +public: + explicit MetaDataBaseItem(QObject *object); + virtual ~MetaDataBaseItem(); + + virtual QString name() const; + virtual void setName(const QString &name); + + typedef QList<QWidget*> TabOrder; + virtual TabOrder tabOrder() const; + virtual void setTabOrder(const TabOrder &tabOrder); + + virtual bool enabled() const; + virtual void setEnabled(bool b); + + QString customClassName() const; + void setCustomClassName(const QString &customClassName); + + QString script() const; + void setScript(const QString &script); + + QStringList fakeSlots() const; + void setFakeSlots(const QStringList &); + + QStringList fakeSignals() const; + void setFakeSignals(const QStringList &); + +private: + QObject *m_object; + TabOrder m_tabOrder; + bool m_enabled; + QString m_customClassName; + QString m_script; + QStringList m_fakeSlots; + QStringList m_fakeSignals; +}; + +class QDESIGNER_SHARED_EXPORT MetaDataBase: public QDesignerMetaDataBaseInterface +{ + Q_OBJECT +public: + explicit MetaDataBase(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~MetaDataBase(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual QDesignerMetaDataBaseItemInterface *item(QObject *object) const { return metaDataBaseItem(object); } + virtual MetaDataBaseItem *metaDataBaseItem(QObject *object) const; + virtual void add(QObject *object); + virtual void remove(QObject *object); + + virtual QList<QObject*> objects() const; + +private slots: + void slotDestroyed(QObject *object); + +private: + QDesignerFormEditorInterface *m_core; + typedef QHash<QObject *, MetaDataBaseItem*> ItemMap; + ItemMap m_items; +}; + + // promotion convenience + QDESIGNER_SHARED_EXPORT bool promoteWidget(QDesignerFormEditorInterface *core,QWidget *widget,const QString &customClassName); + QDESIGNER_SHARED_EXPORT void demoteWidget(QDesignerFormEditorInterface *core,QWidget *widget); + QDESIGNER_SHARED_EXPORT bool isPromoted(QDesignerFormEditorInterface *core, QWidget* w); + QDESIGNER_SHARED_EXPORT QString promotedCustomClassName(QDesignerFormEditorInterface *core, QWidget* w); + QDESIGNER_SHARED_EXPORT QString promotedExtends(QDesignerFormEditorInterface *core, QWidget* w); + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // METADATABASE_H diff --git a/tools/designer/src/lib/shared/morphmenu.cpp b/tools/designer/src/lib/shared/morphmenu.cpp new file mode 100644 index 0000000..0d41af0 --- /dev/null +++ b/tools/designer/src/lib/shared/morphmenu.cpp @@ -0,0 +1,635 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "morphmenu_p.h" +#include "formwindowbase_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_formwindowcommand_p.h" +#include "qlayout_widget_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_propertycommand_p.h" + +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> + +#include <QtGui/QWidget> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QApplication> +#include <QtGui/QLayout> +#include <QtGui/QUndoStack> + +#include <QtGui/QFrame> +#include <QtGui/QGroupBox> +#include <QtGui/QTabWidget> +#include <QtGui/QStackedWidget> +#include <QtGui/QToolBox> +#include <QtGui/QAbstractItemView> +#include <QtGui/QAbstractButton> +#include <QtGui/QAbstractSpinBox> +#include <QtGui/QTextEdit> +#include <QtGui/QPlainTextEdit> +#include <QtGui/QLabel> + +#include <QtCore/QStringList> +#include <QtCore/QMap> +#include <QtCore/QVariant> +#include <QtCore/QSignalMapper> +#include <QtCore/QDebug> + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +// Helpers for the dynamic properties that store Z/Widget order +static const char *widgetOrderPropertyC = "_q_widgetOrder"; +static const char *zOrderPropertyC = "_q_zOrder"; + +/* Morphing in Designer: + * It is possible to morph: + * - Non-Containers into similar widgets by category + * - Simple page containers into similar widgets or page-based containers with + * a single page (in theory also into a QLayoutWidget, but this might + * not always be appropriate). + * - Page-based containers into page-based containers or simple containers if + * they have just one page + * [Page based containers meaning here having a container extension] + * Morphing types are restricted to the basic Qt types. Morphing custom + * widgets is considered risky since they might have unmanaged layouts + * or the like. + * + * Requirements: + * - The widget must be on a non-laid out parent or in a layout managed + * by Designer + * - Its child widgets must be non-laid out or in a layout managed + * by Designer + * Note that child widgets can be + * - On the widget itself in the case of simple containers + * - On several pages in the case of page-based containers + * This is what is called 'childContainers' in the code (the widget itself + * or the list of container extension pages). + * + * The Morphing process encompasses: + * - Create a target widget and apply properties as far as applicable + * If the target widget has a container extension, add a sufficient + * number of pages. + * - Transferring the child widgets over to the new childContainers. + * In the case of a managed layout on a childContainer, this is simply + * set on the target childContainer, which is a new Qt 4.5 + * functionality. + * - Replace the widget itself in the parent layout + */ + +namespace qdesigner_internal { + +enum MorphCategory { + MorphCategoryNone, MorphSimpleContainer, MorphPageContainer, MorphItemView, + MorphButton, MorphSpinBox, MorphTextEdit +}; + +// Determine category of a widget +static MorphCategory category(const QWidget *w) +{ + // Simple containers: Exact match + const QMetaObject *mo = w->metaObject(); + if (mo == &QWidget::staticMetaObject || mo == &QFrame::staticMetaObject || mo == &QGroupBox::staticMetaObject || mo == &QLayoutWidget::staticMetaObject) + return MorphSimpleContainer; + if (mo == &QTabWidget::staticMetaObject || mo == &QStackedWidget::staticMetaObject || mo == &QToolBox::staticMetaObject) + return MorphPageContainer; + if (qobject_cast<const QAbstractItemView*>(w)) + return MorphItemView; + if (qobject_cast<const QAbstractButton *>(w)) + return MorphButton; + if (qobject_cast<const QAbstractSpinBox *>(w)) + return MorphSpinBox; + if (qobject_cast<const QPlainTextEdit *>(w) || qobject_cast<const QTextEdit*>(w)) + return MorphTextEdit; + + return MorphCategoryNone; +} + +/* Return the similar classes of a category. This is currently restricted + * to the known Qt classes with no precautions to parse the Widget Database + * (which is too risky, custom classes might have container extensions + * or non-managed layouts, etc.). */ + +static QStringList classesOfCategory(MorphCategory cat) +{ + typedef QMap<MorphCategory, QStringList> CandidateCache; + static CandidateCache candidateCache; + CandidateCache::iterator it = candidateCache.find(cat); + if (it == candidateCache.end()) { + it = candidateCache.insert(cat, QStringList()); + QStringList &l = it.value(); + switch (cat) { + case MorphCategoryNone: + break; + case MorphSimpleContainer: + // Do not generally allow to morph into a layout. + // This can be risky in case of container pages,etc. + l << QLatin1String("QWidget") << QLatin1String("QFrame") << QLatin1String("QGroupBox"); + break; + case MorphPageContainer: + l << QLatin1String("QTabWidget") << QLatin1String("QStackedWidget") << QLatin1String("QToolBox"); + break; + case MorphItemView: + l << QLatin1String("QListView") << QLatin1String("QListWidget") + << QLatin1String("QTreeView") << QLatin1String("QTreeWidget") + << QLatin1String("QTableView") << QLatin1String("QTableWidget") + << QLatin1String("QColumnView"); + break; + case MorphButton: + l << QLatin1String("QCheckBox") << QLatin1String("QRadioButton") + << QLatin1String("QPushButton") << QLatin1String("QToolButton") + << QLatin1String("QCommandLinkButton"); + break; + case MorphSpinBox: + l << QLatin1String("QDateTimeEdit") << QLatin1String("QDateEdit") + << QLatin1String("QTimeEdit") + << QLatin1String("QSpinBox") << QLatin1String("QDoubleSpinBox"); + break; + case MorphTextEdit: + l << QLatin1String("QTextEdit") << QLatin1String("QPlainTextEdit"); + break; + } + } + return it.value(); +} + +// Return the widgets containing the children to be transferred to. This is the +// widget itself in most cases, except for QDesignerContainerExtension cases +static QWidgetList childContainers(const QDesignerFormEditorInterface *core, QWidget *w) +{ + if (const QDesignerContainerExtension *ce = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), w)) { + QWidgetList children; + if (const int count = ce->count()) { + for (int i = 0; i < count; i++) + children.push_back(ce->widget(i)); + } + return children; + } + QWidgetList self; + self.push_back(w); + return self; +} + +// Suggest a suitable objectname for the widget to be morphed into +// Replace the class name parts: 'xxFrame' -> 'xxGroupBox', 'frame' -> 'groupBox' +static QString suggestObjectName(const QString &oldClassName, const QString &newClassName, const QString &oldName) +{ + QString oldClassPart = oldClassName; + QString newClassPart = newClassName; + if (oldClassPart.startsWith(QLatin1Char('Q'))) + oldClassPart.remove(0, 1); + if (newClassPart.startsWith(QLatin1Char('Q'))) + newClassPart.remove(0, 1); + + QString newName = oldName; + newName.replace(oldClassPart, newClassPart); + oldClassPart[0] = oldClassPart.at(0).toLower(); + newClassPart[0] = newClassPart.at(0).toLower(); + newName.replace(oldClassPart, newClassPart); + return newName; +} + +// Find the label whose buddy the widget is. +QLabel *buddyLabelOf(QDesignerFormWindowInterface *fw, QWidget *w) +{ + typedef QList<QLabel*> LabelList; + const LabelList labelList = qFindChildren<QLabel*>(fw); + if (labelList.empty()) + return 0; + const LabelList::const_iterator cend = labelList.constEnd(); + for (LabelList::const_iterator it = labelList.constBegin(); it != cend; ++it ) + if ( (*it)->buddy() == w) + return *it; + return 0; +} + +// Replace widgets in a widget-list type dynamic property of the parent +// used for Z-order, etc. +static void replaceWidgetListDynamicProperty(QWidget *parentWidget, + QWidget *oldWidget, QWidget *newWidget, + const char *name) +{ + QWidgetList list = qVariantValue<QWidgetList>(parentWidget->property(name)); + const int index = list.indexOf(oldWidget); + if (index != -1) { + list.replace(index, newWidget); + parentWidget->setProperty(name, qVariantFromValue(list)); + } +} + +/* Morph a widget into another class. Use the static addMorphMacro() to + * add a respective command sequence to the undo stack as it emits signals + * which cause other commands to be added. */ +class MorphWidgetCommand : public QDesignerFormWindowCommand +{ + Q_DISABLE_COPY(MorphWidgetCommand) +public: + + explicit MorphWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~MorphWidgetCommand(); + + // Convenience to add a morph command sequence macro + static bool addMorphMacro(QDesignerFormWindowInterface *formWindow, QWidget *w, const QString &newClass); + + bool init(QWidget *widget, const QString &newClass); + + QString newWidgetName() const { return m_afterWidget->objectName(); } + + virtual void redo(); + virtual void undo(); + + static QStringList candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w); + +private: + static bool canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *childContainerCount = 0, MorphCategory *cat = 0); + void morph(QWidget *before, QWidget *after); + + QWidget *m_beforeWidget; + QWidget *m_afterWidget; +}; + +bool MorphWidgetCommand::addMorphMacro(QDesignerFormWindowInterface *fw, QWidget *w, const QString &newClass) +{ + MorphWidgetCommand *morphCmd = new MorphWidgetCommand(fw); + if (!morphCmd->init(w, newClass)) { + qWarning("*** Unable to create a MorphWidgetCommand"); + delete morphCmd; + return false; + } + QLabel *buddyLabel = buddyLabelOf(fw, w); + // Need a macro since it adds further commands + QUndoStack *us = fw->commandHistory(); + us->beginMacro(morphCmd->text()); + // Have the signal slot/buddy editors add their commands to delete widget + if (FormWindowBase *fwb = qobject_cast<FormWindowBase*>(fw)) + fwb->emitWidgetRemoved(w); + + const QString newWidgetName = morphCmd->newWidgetName(); + us->push(morphCmd); + + // restore buddy using the QByteArray name. + if (buddyLabel) { + SetPropertyCommand *buddyCmd = new SetPropertyCommand(fw); + buddyCmd->init(buddyLabel, QLatin1String("buddy"), QVariant(newWidgetName.toUtf8())); + us->push(buddyCmd); + } + us->endMacro(); + return true; +} + +MorphWidgetCommand::MorphWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_beforeWidget(0), + m_afterWidget(0) +{ +} + +MorphWidgetCommand::~MorphWidgetCommand() +{ +} + +bool MorphWidgetCommand::init(QWidget *widget, const QString &newClassName) +{ + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + + if (!canMorph(fw, widget)) + return false; + + const QString oldClassName = WidgetFactory::classNameOf(core, widget); + const QString oldName = widget->objectName(); + //: MorphWidgetCommand description + setText(QApplication::translate("Command", "Morph %1/'%2' into %3").arg(oldClassName, oldName, newClassName)); + + m_beforeWidget = widget; + m_afterWidget = core->widgetFactory()->createWidget(newClassName, fw); + if (!m_afterWidget) + return false; + + // Set object name. Do not unique it (as to maintain it). + m_afterWidget->setObjectName(suggestObjectName(oldClassName, newClassName, oldName)); + + // If the target has a container extension, we add enough new pages to take + // up the children of the before widget + if (QDesignerContainerExtension* c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_afterWidget)) { + if (const int pageCount = childContainers(core, m_beforeWidget).size()) { + const QString qWidget = QLatin1String("QWidget"); + const QString containerName = m_afterWidget->objectName(); + for (int i = 0; i < pageCount; i++) { + QString name = containerName; + name += QLatin1String("Page"); + name += QString::number(i + 1); + QWidget *page = core->widgetFactory()->createWidget(qWidget); + page->setObjectName(name); + fw->ensureUniqueObjectName(page); + c->addWidget(page); + core->metaDataBase()->add(page); + } + } + } + + // Copy over applicable properties + const QDesignerPropertySheetExtension *beforeSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), widget); + QDesignerPropertySheetExtension *afterSheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), m_afterWidget); + const QString objectNameProperty = QLatin1String("objectName"); + const int count = beforeSheet->count(); + for (int i = 0; i < count; i++) + if (beforeSheet->isVisible(i) && beforeSheet->isChanged(i)) { + const QString name = beforeSheet->propertyName(i); + if (name != objectNameProperty) { + const int afterIndex = afterSheet->indexOf(name); + if (afterIndex != -1 && afterSheet->isVisible(afterIndex) && afterSheet->propertyGroup(afterIndex) == beforeSheet->propertyGroup(i)) { + afterSheet->setProperty(i, beforeSheet->property(i)); + afterSheet->setChanged(i, true); + } else { + // Some mismatch. The rest won't match, either + break; + } + } + } + return true; +} + +void MorphWidgetCommand::redo() +{ + morph(m_beforeWidget, m_afterWidget); +} + +void MorphWidgetCommand::undo() +{ + morph(m_afterWidget, m_beforeWidget); +} + +void MorphWidgetCommand::morph(QWidget *before, QWidget *after) +{ + QDesignerFormWindowInterface *fw = formWindow(); + + fw->unmanageWidget(before); + + const QRect oldGeom = before->geometry(); + QWidget *parent = before->parentWidget(); + Q_ASSERT(parent); + /* Morphing consists of main 2 steps + * 1) Move over children (laid out, non-laid out) + * 2) Register self with new parent (laid out, non-laid out) */ + + // 1) Move children. Loop over child containers + QWidgetList beforeChildContainers = childContainers(fw->core(), before); + QWidgetList afterChildContainers = childContainers(fw->core(), after); + Q_ASSERT(beforeChildContainers.size() == afterChildContainers.size()); + const int childContainerCount = beforeChildContainers.size(); + for (int i = 0; i < childContainerCount; i++) { + QWidget *beforeChildContainer = beforeChildContainers.at(i); + QWidget *afterChildContainer = afterChildContainers.at(i); + if (QLayout *childLayout = beforeChildContainer->layout()) { + // Laid-out: Move the layout (since 4.5) + afterChildContainer->setLayout(childLayout); + } else { + // Non-Laid-out: Reparent, move over + const QObjectList c = beforeChildContainer->children(); + const QObjectList::const_iterator cend = c.constEnd(); + for (QObjectList::const_iterator it = c.constBegin(); it != cend; ++it) { + if ( (*it)->isWidgetType()) { + QWidget *w = static_cast<QWidget*>(*it); + if (fw->isManaged(w)) { + const QRect geom = w->geometry(); + w->setParent(afterChildContainer); + w->setGeometry(geom); + } + } + } + } + afterChildContainer->setProperty(widgetOrderPropertyC, beforeChildContainer->property(widgetOrderPropertyC)); + afterChildContainer->setProperty(zOrderPropertyC, beforeChildContainer->property(zOrderPropertyC)); + } + + // 2) Replace the actual widget in the parent layout + after->setGeometry(oldGeom); + if (QLayout *containingLayout = LayoutInfo::managedLayout(fw->core(), parent)) { + LayoutHelper *lh = LayoutHelper::createLayoutHelper(LayoutInfo::layoutType(fw->core(), containingLayout)); + Q_ASSERT(lh); + lh->replaceWidget(containingLayout, before, after); + delete lh; + } else { + before->hide(); + before->setParent(0); + after->setParent(parent); + after->setGeometry(oldGeom); + } + + // Check various properties: Z order, form tab order + replaceWidgetListDynamicProperty(parent, before, after, widgetOrderPropertyC); + replaceWidgetListDynamicProperty(parent, before, after, zOrderPropertyC); + + QDesignerMetaDataBaseItemInterface *formItem = fw->core()->metaDataBase()->item(fw); + QWidgetList tabOrder = formItem->tabOrder(); + const int tabIndex = tabOrder.indexOf(before); + if (tabIndex != -1) { + tabOrder.replace(tabIndex, after); + formItem->setTabOrder(tabOrder); + } + + after->show(); + fw->manageWidget(after); + + fw->clearSelection(false); + fw->selectWidget(after); +} + +/* Check if morphing is possible. It must be a valid category and the parent/ + * child relationships must be either non-laidout or directly on + * Designer-managed layouts. */ +bool MorphWidgetCommand::canMorph(QDesignerFormWindowInterface *fw, QWidget *w, int *ptrToChildContainerCount, MorphCategory *ptrToCat) +{ + if (ptrToChildContainerCount) + *ptrToChildContainerCount = 0; + const MorphCategory cat = category(w); + if (ptrToCat) + *ptrToCat = cat; + if (cat == MorphCategoryNone) + return false; + + QDesignerFormEditorInterface *core = fw->core(); + // Don't know how to fiddle class names in Jambi.. + if (qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) + return false; + if (!fw->isManaged(w) || w == fw->mainContainer()) + return false; + // Check the parent relationship. We accept only managed parent widgets + // with a single, managed layout in which widget is a member. + QWidget *parent = w->parentWidget(); + if (parent == 0) + return false; + if (QLayout *pl = LayoutInfo::managedLayout(core, parent)) + if (pl->indexOf(w) < 0 || !core->metaDataBase()->item(pl)) + return false; + // Check Widget database + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int wdbindex = wdb->indexOfObject(w); + if (wdbindex == -1) + return false; + const bool isContainer = wdb->item(wdbindex)->isContainer(); + if (!isContainer) + return true; + // Check children. All child containers must be non-laid-out or have managed layouts + const QWidgetList pages = childContainers(core, w); + const int pageCount = pages.size(); + if (ptrToChildContainerCount) + *ptrToChildContainerCount = pageCount; + if (pageCount) { + for (int i = 0; i < pageCount; i++) + if (QLayout *cl = pages.at(i)->layout()) + if (!core->metaDataBase()->item(cl)) + return false; + } + return true; +} + +QStringList MorphWidgetCommand::candidateClasses(QDesignerFormWindowInterface *fw, QWidget *w) +{ + int childContainerCount; + MorphCategory cat; + if (!canMorph(fw, w, &childContainerCount, &cat)) + return QStringList(); + + QStringList rc = classesOfCategory(cat); + switch (cat) { + // Frames, etc can always be morphed into one-page page containers + case MorphSimpleContainer: + rc += classesOfCategory(MorphPageContainer); + break; + // Multipage-Containers can be morphed into simple containers if they + // have 1 page. + case MorphPageContainer: + if (childContainerCount == 1) + rc += classesOfCategory(MorphSimpleContainer); + break; + default: + break; + } + return rc; +} + +// MorphMenu +MorphMenu::MorphMenu(QObject *parent) : + QObject(parent), + m_subMenuAction(0), + m_menu(0), + m_mapper(0), + m_widget(0), + m_formWindow(0) +{ +} + +void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al) +{ + if (populateMenu(w, fw)) + al.push_back(m_subMenuAction); +} + +void MorphMenu::populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m) +{ + if (populateMenu(w, fw)) + m.addAction(m_subMenuAction); +} + +void MorphMenu::slotMorph(const QString &newClassName) +{ + MorphWidgetCommand::addMorphMacro(m_formWindow, m_widget, newClassName); +} + +bool MorphMenu::populateMenu(QWidget *w, QDesignerFormWindowInterface *fw) +{ + m_widget = 0; + m_formWindow = 0; + + // Clear menu + if (m_subMenuAction) { + m_subMenuAction->setVisible(false); + m_menu->clear(); + } + + // Checks: Must not be main container + if (w == fw->mainContainer()) + return false; + + const QStringList c = MorphWidgetCommand::candidateClasses(fw, w); + if (c.empty()) + return false; + + // Pull up + m_widget = w; + m_formWindow = fw; + const QString oldClassName = WidgetFactory::classNameOf(fw->core(), w); + + if (!m_subMenuAction) { + m_subMenuAction = new QAction(tr("Morph into"), this); + m_menu = new QMenu; + m_subMenuAction->setMenu(m_menu); + m_mapper = new QSignalMapper(this); + connect(m_mapper , SIGNAL(mapped(QString)), this, SLOT(slotMorph(QString))); + } + + // Add actions + const QStringList::const_iterator cend = c.constEnd(); + for (QStringList::const_iterator it = c.constBegin(); it != cend; ++it) { + if (*it != oldClassName) { + QAction *a = m_menu->addAction(*it); + m_mapper->setMapping (a, *it); + connect(a, SIGNAL(triggered()), m_mapper, SLOT(map())); + } + } + m_subMenuAction->setVisible(true); + return true; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/morphmenu_p.h b/tools/designer/src/lib/shared/morphmenu_p.h new file mode 100644 index 0000000..92389a0 --- /dev/null +++ b/tools/designer/src/lib/shared/morphmenu_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef MORPH_COMMAND_H +#define MORPH_COMMAND_H + +#include "shared_global_p.h" +#include "qdesigner_formwindowcommand_p.h" + +QT_BEGIN_NAMESPACE + +class QAction; +class QSignalMapper; +class QMenu; + +namespace qdesigner_internal { + +/* Conveniene morph menu that acts on a single widget. */ +class QDESIGNER_SHARED_EXPORT MorphMenu : public QObject { + Q_DISABLE_COPY(MorphMenu) + Q_OBJECT +public: + typedef QList<QAction *> ActionList; + + explicit MorphMenu(QObject *parent = 0); + + void populate(QWidget *w, QDesignerFormWindowInterface *fw, ActionList& al); + void populate(QWidget *w, QDesignerFormWindowInterface *fw, QMenu& m); + +private slots: + void slotMorph(const QString &newClassName); + +private: + bool populateMenu(QWidget *w, QDesignerFormWindowInterface *fw); + + QAction *m_subMenuAction; + QMenu *m_menu; + QSignalMapper *m_mapper; + + QWidget *m_widget; + QDesignerFormWindowInterface *m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // MORPH_COMMAND_H diff --git a/tools/designer/src/lib/shared/newactiondialog.cpp b/tools/designer/src/lib/shared/newactiondialog.cpp new file mode 100644 index 0000000..53aec4b --- /dev/null +++ b/tools/designer/src/lib/shared/newactiondialog.cpp @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newactiondialog_p.h" +#include "ui_newactiondialog.h" +#include "richtexteditor_p.h" +#include "actioneditor_p.h" +#include "formwindowbase_p.h" +#include "qdesigner_utils_p.h" +#include "iconloader_p.h" + +#include <QtDesigner/abstractformwindow.h> +#include <QtDesigner/abstractformeditor.h> + +#include <QtGui/QPushButton> +#include <QtCore/QRegExp> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// -------------------- ActionData + +ActionData::ActionData() : + checkable(false) +{ +} + +// Returns a combination of ChangeMask flags +unsigned ActionData::compare(const ActionData &rhs) const +{ + unsigned rc = 0; + if (text != rhs.text) + rc |= TextChanged; + if (name != rhs.name) + rc |= NameChanged; + if (toolTip != rhs.toolTip) + rc |= ToolTipChanged ; + if (icon != rhs.icon) + rc |= IconChanged ; + if (checkable != rhs.checkable) + rc |= CheckableChanged; + if (keysequence != rhs.keysequence) + rc |= KeysequenceChanged ; + return rc; +} + +// -------------------- NewActionDialog +NewActionDialog::NewActionDialog(ActionEditor *parent) : + QDialog(parent, Qt::Sheet), + m_ui(new Ui::NewActionDialog), + m_actionEditor(parent) +{ + m_ui->setupUi(this); + + m_ui->tooltipEditor->setTextPropertyValidationMode(ValidationRichText); + connect(m_ui->toolTipToolButton, SIGNAL(clicked()), this, SLOT(slotEditToolTip())); + + m_ui->keysequenceResetToolButton->setIcon(createIconSet(QLatin1String("resetproperty.png"))); + connect(m_ui->keysequenceResetToolButton, SIGNAL(clicked()), this, SLOT(slotResetKeySequence())); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->editActionText->setFocus(); + m_auto_update_object_name = true; + updateButtons(); + + QDesignerFormWindowInterface *form = parent->formWindow(); + m_ui->iconSelector->setFormEditor(form->core()); + FormWindowBase *formBase = qobject_cast<FormWindowBase *>(form); + + if (formBase) { + m_ui->iconSelector->setPixmapCache(formBase->pixmapCache()); + m_ui->iconSelector->setIconCache(formBase->iconCache()); + } +} + +NewActionDialog::~NewActionDialog() +{ + delete m_ui; +} + +QString NewActionDialog::actionText() const +{ + return m_ui->editActionText->text(); +} + +QString NewActionDialog::actionName() const +{ + return m_ui->editObjectName->text(); +} + +ActionData NewActionDialog::actionData() const +{ + ActionData rc; + rc.text = actionText(); + rc.name = actionName(); + rc.toolTip = m_ui->tooltipEditor->text(); + rc.icon = m_ui->iconSelector->icon(); + rc.checkable = m_ui->checkableCheckBox->checkState() == Qt::Checked; + rc.keysequence = m_ui->keySequenceEdit->keySequence(); + return rc; +} + +void NewActionDialog::setActionData(const ActionData &d) +{ + m_ui->editActionText->setText(d.text); + m_ui->editObjectName->setText(d.name); + m_ui->iconSelector->setIcon(d.icon); + m_ui->tooltipEditor->setText(d.toolTip); + m_ui->keySequenceEdit->setKeySequence(d.keysequence); + m_ui->checkableCheckBox->setCheckState(d.checkable ? Qt::Checked : Qt::Unchecked); + + m_auto_update_object_name = false; + updateButtons(); +} + +void NewActionDialog::on_editActionText_textEdited(const QString &text) +{ + if (text.isEmpty()) + m_auto_update_object_name = true; + + if (m_auto_update_object_name) + m_ui->editObjectName->setText(ActionEditor::actionTextToName(text)); + + updateButtons(); +} + +void NewActionDialog::on_editObjectName_textEdited(const QString&) +{ + updateButtons(); + m_auto_update_object_name = false; +} + +void NewActionDialog::slotEditToolTip() +{ + const QString oldToolTip = m_ui->tooltipEditor->text(); + RichTextEditorDialog richTextDialog(m_actionEditor->core(), this); + richTextDialog.setText(oldToolTip); + if (richTextDialog.showDialog() == QDialog::Rejected) + return; + const QString newToolTip = richTextDialog.text(); + if (newToolTip != oldToolTip) + m_ui->tooltipEditor->setText(newToolTip); +} + +void NewActionDialog::slotResetKeySequence() +{ + m_ui->keySequenceEdit->setKeySequence(QKeySequence()); + m_ui->keySequenceEdit->setFocus(Qt::MouseFocusReason); +} + +void NewActionDialog::updateButtons() +{ + QPushButton *okButton = m_ui->buttonBox->button(QDialogButtonBox::Ok); + okButton->setEnabled(!actionText().isEmpty() && !actionName().isEmpty()); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/newactiondialog.ui b/tools/designer/src/lib/shared/newactiondialog.ui new file mode 100644 index 0000000..77eda28 --- /dev/null +++ b/tools/designer/src/lib/shared/newactiondialog.ui @@ -0,0 +1,277 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>qdesigner_internal::NewActionDialog</class> + <widget class="QDialog" name="qdesigner_internal::NewActionDialog"> + <property name="windowTitle"> + <string>New Action...</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="textLabel"> + <property name="text"> + <string>&Text:</string> + </property> + <property name="buddy"> + <cstring>editActionText</cstring> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="editActionText"> + <property name="minimumSize"> + <size> + <width>255</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="objectNameLabel"> + <property name="text"> + <string>Object &name:</string> + </property> + <property name="buddy"> + <cstring>editObjectName</cstring> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="editObjectName"/> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="iconLabel"> + <property name="text"> + <string>&Icon:</string> + </property> + <property name="buddy"> + <cstring>iconSelector</cstring> + </property> + </widget> + </item> + <item row="3" column="1"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="qdesigner_internal::IconSelector" name="iconSelector" native="true"/> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="shortcutLabel"> + <property name="text"> + <string>Shortcut:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QCheckBox" name="checkableCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="checkableLabel"> + <property name="text"> + <string>Checkable:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="toolTipLabel"> + <property name="text"> + <string>ToolTip:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <layout class="QHBoxLayout" name="toolTipLayout"> + <item> + <widget class="TextPropertyEditor" name="tooltipEditor" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="toolTipToolButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="5" column="1"> + <layout class="QHBoxLayout" name="keysequenceLayout"> + <item> + <widget class="QtKeySequenceEdit" name="keySequenceEdit" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="keysequenceResetToolButton"> + <property name="text"> + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="Line" name="line"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>qdesigner_internal::IconSelector</class> + <extends>QWidget</extends> + <header>iconselector_p.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>QtKeySequenceEdit</class> + <extends>QWidget</extends> + <header>qtpropertybrowserutils_p.h</header> + <container>1</container> + </customwidget> + <customwidget> + <class>TextPropertyEditor</class> + <extends>QWidget</extends> + <header>textpropertyeditor_p.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>editActionText</tabstop> + <tabstop>editObjectName</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>qdesigner_internal::NewActionDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>165</x> + <y>162</y> + </hint> + <hint type="destinationlabel"> + <x>291</x> + <y>94</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>qdesigner_internal::NewActionDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>259</x> + <y>162</y> + </hint> + <hint type="destinationlabel"> + <x>293</x> + <y>128</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/newactiondialog_p.h b/tools/designer/src/lib/shared/newactiondialog_p.h new file mode 100644 index 0000000..c8bd34c --- /dev/null +++ b/tools/designer/src/lib/shared/newactiondialog_p.h @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWACTIONDIALOG_P_H +#define NEWACTIONDIALOG_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qdesigner_utils_p.h" // PropertySheetIconValue + +#include <QtGui/QDialog> +#include <QtGui/QKeySequence> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +namespace Ui { + class NewActionDialog; +} + +class ActionEditor; + +struct ActionData { + + enum ChangeMask { + TextChanged = 0x1, NameChanged = 0x2, ToolTipChanged = 0x4, + IconChanged = 0x8, CheckableChanged = 0x10, KeysequenceChanged = 0x20 + }; + + ActionData(); + // Returns a combination of ChangeMask flags + unsigned compare(const ActionData &rhs) const; + + QString text; + QString name; + QString toolTip; + PropertySheetIconValue icon; + bool checkable; + QKeySequence keysequence; +}; + +inline bool operator==(const ActionData &a1, const ActionData &a2) { return a1.compare(a2) == 0u; } +inline bool operator!=(const ActionData &a1, const ActionData &a2) { return a1.compare(a2) != 0u; } + +class NewActionDialog: public QDialog +{ + Q_OBJECT +public: + explicit NewActionDialog(ActionEditor *parent); + virtual ~NewActionDialog(); + + ActionData actionData() const; + void setActionData(const ActionData &d); + + QString actionText() const; + QString actionName() const; + +private slots: + void on_editActionText_textEdited(const QString &text); + void on_editObjectName_textEdited(const QString &text); + void slotEditToolTip(); + void slotResetKeySequence(); + +private: + Ui::NewActionDialog *m_ui; + ActionEditor *m_actionEditor; + bool m_auto_update_object_name; + + void updateButtons(); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // NEWACTIONDIALOG_P_H diff --git a/tools/designer/src/lib/shared/newformwidget.cpp b/tools/designer/src/lib/shared/newformwidget.cpp new file mode 100644 index 0000000..d79d77a --- /dev/null +++ b/tools/designer/src/lib/shared/newformwidget.cpp @@ -0,0 +1,587 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "newformwidget_p.h" +#include "ui_newformwidget.h" +#include "qdesigner_formbuilder_p.h" +#include "sheet_delegate_p.h" +#include "widgetdatabase_p.h" +#include "shared_settings_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QDebug> +#include <QtCore/QByteArray> +#include <QtCore/QBuffer> +#include <QtCore/QDir> +#include <QtCore/QTextStream> + +#include <QtGui/QHeaderView> +#include <QtGui/QTreeWidgetItem> +#include <QtGui/QPainter> +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +enum { profileComboIndexOffset = 1 }; +enum { debugNewFormWidget = 0 }; + +enum NewForm_CustomRole { + // File name (templates from resources, paths) + TemplateNameRole = Qt::UserRole + 100, + // Class name (widgets from Widget data base) + ClassNameRole = Qt::UserRole + 101 +}; + +static const char *newFormObjectNameC = "Form"; + +// Create a form name for an arbitrary class. If it is Qt, qtify it, +// else return "Form". +static QString formName(const QString &className) +{ + if (!className.startsWith(QLatin1Char('Q'))) + return QLatin1String(newFormObjectNameC); + QString rc = className; + rc.remove(0, 1); + return rc; +} + +namespace qdesigner_internal { + +struct TemplateSize { + const char *name; + int width; + int height; +}; + +static const struct TemplateSize templateSizes[] = +{ + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "Default size"), 0, 0 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA portrait (240x320)"), 240, 320 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "QVGA landscape (320x240)"), 320, 240 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA portrait (480x640)"), 480, 640 }, + { QT_TRANSLATE_NOOP("qdesigner_internal::NewFormWidget", "VGA landscape (640x480)"), 640, 480 } +}; + +/* -------------- NewForm dialog. + * Designer takes new form templates from: + * 1) Files located in directories specified in resources + * 2) Files located in directories specified as user templates + * 3) XML from container widgets deemed usable for form templates by the widget + * database + * 4) XML from custom container widgets deemed usable for form templates by the + * widget database + * + * The widget database provides helper functions to obtain lists of names + * and xml for 3,4. + * + * Fixed-size forms for embedded platforms are obtained as follows: + * 1) If the origin is a file: + * - Check if the file exists in the subdirectory "/<width>x<height>/" of + * the path (currently the case for the dialog box because the button box + * needs to be positioned) + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * 2) If the origin is XML: + * - Scale the form using the QWidgetDatabase::scaleFormTemplate routine. + * + * The tree widget item roles indicate which type of entry it is + * (TemplateNameRole = file name 1,2, ClassNameRole = class name 3,4) + */ + +NewFormWidget::NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget) : + QDesignerNewFormWidgetInterface(parentWidget), + m_core(core), + m_ui(new Ui::NewFormWidget), + m_currentItem(0), + m_acceptedItem(0) +{ + typedef QList<qdesigner_internal::DeviceProfile> DeviceProfileList; + + m_ui->setupUi(this); + m_ui->treeWidget->setItemDelegate(new qdesigner_internal::SheetDelegate(m_ui->treeWidget, this)); + m_ui->treeWidget->header()->hide(); + m_ui->treeWidget->header()->setStretchLastSection(true); + m_ui->lblPreview->setBackgroundRole(QPalette::Base); + QDesignerSharedSettings settings(m_core); + + QString uiExtension = QLatin1String("ui"); + QString templatePath = QLatin1String(":/trolltech/designer/templates/forms"); + + QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core); + if (lang) { + templatePath = QLatin1String(":/templates/forms"); + uiExtension = lang->uiExtension(); + } + + // Resource templates + const QString formTemplate = settings.formTemplate(); + QTreeWidgetItem *selectedItem = 0; + loadFrom(templatePath, true, uiExtension, formTemplate, selectedItem); + // Additional template paths + const QStringList formTemplatePaths = settings.formTemplatePaths(); + const QStringList::const_iterator ftcend = formTemplatePaths.constEnd(); + for (QStringList::const_iterator it = formTemplatePaths.constBegin(); it != ftcend; ++it) + loadFrom(*it, false, uiExtension, formTemplate, selectedItem); + + // Widgets/custom widgets + if (!lang) { + //: New Form Dialog Categories + loadFrom(tr("Widgets"), qdesigner_internal::WidgetDataBase::formWidgetClasses(core), formTemplate, selectedItem); + loadFrom(tr("Custom Widgets"), qdesigner_internal::WidgetDataBase::customFormWidgetClasses(core), formTemplate, selectedItem); + } + + // Still no selection - default to first item + if (selectedItem == 0 && m_ui->treeWidget->topLevelItemCount() != 0) { + QTreeWidgetItem *firstTopLevel = m_ui->treeWidget->topLevelItem(0); + if (firstTopLevel->childCount() > 0) + selectedItem = firstTopLevel->child(0); + } + + // Open parent, select and make visible + if (selectedItem) { + m_ui->treeWidget->setCurrentItem(selectedItem); + m_ui->treeWidget->setItemSelected(selectedItem, true); + m_ui->treeWidget->scrollToItem(selectedItem->parent()); + } + // Fill profile combo + m_deviceProfiles = settings.deviceProfiles(); + m_ui->profileComboBox->addItem(tr("None")); + connect(m_ui->profileComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(slotDeviceProfileIndexChanged(int))); + if (m_deviceProfiles.empty()) { + m_ui->profileComboBox->setEnabled(false); + } else { + const DeviceProfileList::const_iterator dcend = m_deviceProfiles.constEnd(); + for (DeviceProfileList::const_iterator it = m_deviceProfiles.constBegin(); it != dcend; ++it) + m_ui->profileComboBox->addItem(it->name()); + const int ci = settings.currentDeviceProfileIndex(); + if (ci >= 0) + m_ui->profileComboBox->setCurrentIndex(ci + profileComboIndexOffset); + } + // Fill size combo + const int sizeCount = sizeof(templateSizes)/ sizeof(TemplateSize); + for (int i = 0; i < sizeCount; i++) { + const QSize size = QSize(templateSizes[i].width, templateSizes[i].height); + m_ui->sizeComboBox->addItem(tr(templateSizes[i].name), size); + } + + setTemplateSize(settings.newFormSize()); + + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << "Leaving"; +} + +NewFormWidget::~NewFormWidget() +{ + QDesignerSharedSettings settings (m_core); + settings.setNewFormSize(templateSize()); + // Do not change previously stored item if dialog was rejected + if (m_acceptedItem) + settings.setFormTemplate(m_acceptedItem->text(0)); + delete m_ui; +} + +void NewFormWidget::on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << current; + if (!current) + return; + + if (!current->parent()) { // Top level item: Ensure expanded when browsing down + return; + } + + m_currentItem = current; + + emit currentTemplateChanged(showCurrentItemPixmap()); +} + +bool NewFormWidget::showCurrentItemPixmap() +{ + bool rc = false; + if (m_currentItem) { + const QPixmap pixmap = formPreviewPixmap(m_currentItem); + if (pixmap.isNull()) { + m_ui->lblPreview->setText(tr("Error loading form")); + } else { + m_ui->lblPreview->setPixmap(pixmap); + rc = true; + } + } + return rc; +} + +void NewFormWidget::on_treeWidget_itemActivated(QTreeWidgetItem *item) +{ + if (debugNewFormWidget) + qDebug() << Q_FUNC_INFO << item; + + if (item->data(0, TemplateNameRole).isValid() || item->data(0, ClassNameRole).isValid()) + emit templateActivated(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QTreeWidgetItem *item) +{ + // Cache pixmaps per item/device profile + const ItemPixmapCacheKey cacheKey(item, profileComboIndex()); + ItemPixmapCache::iterator it = m_itemPixmapCache.find(cacheKey); + if (it == m_itemPixmapCache.end()) { + // file or string? + const QVariant fileName = item->data(0, TemplateNameRole); + QPixmap rc; + if (fileName.type() == QVariant::String) { + rc = formPreviewPixmap(fileName.toString()); + } else { + const QVariant classNameV = item->data(0, ClassNameRole); + Q_ASSERT(classNameV.type() == QVariant::String); + const QString className = classNameV.toString(); + QByteArray data = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)).toUtf8(); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + rc = formPreviewPixmap(buffer); + } + if (rc.isNull()) // Retry invalid ones + return rc; + it = m_itemPixmapCache.insert(cacheKey, rc); + } + return it.value(); +} + +QPixmap NewFormWidget::formPreviewPixmap(const QString &fileName) const +{ + QFile f(fileName); + if (f.open(QFile::ReadOnly)) { + QFileInfo fi(fileName); + const QPixmap rc = formPreviewPixmap(f, fi.absolutePath()); + f.close(); + return rc; + } + qWarning() << "The file " << fileName << " could not be opened: " << f.errorString(); + return QPixmap(); +} + +QImage NewFormWidget::grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp) +{ + qdesigner_internal::QDesignerFormBuilder formBuilder(core, + qdesigner_internal::QDesignerFormBuilder::DisableScripts, + dp); + if (!workingDir.isEmpty()) + formBuilder.setWorkingDirectory(workingDir); + + QWidget *widget = formBuilder.load(&file, 0); + if (!widget) + return QImage(); + + const QPixmap pixmap = QPixmap::grabWidget(widget); + widget->deleteLater(); + return pixmap.toImage(); +} + +QPixmap NewFormWidget::formPreviewPixmap(QIODevice &file, const QString &workingDir) const +{ + const int margin = 7; + const int shadow = 7; + const int previewSize = 256; + + const QImage wimage = grabForm(m_core, file, workingDir, currentDeviceProfile()); + if (wimage.isNull()) + return QPixmap(); + const QImage image = wimage.scaled(previewSize - margin * 2, previewSize - margin * 2, + Qt::KeepAspectRatio, + Qt::SmoothTransformation); + + QImage dest(previewSize, previewSize, QImage::Format_ARGB32_Premultiplied); + dest.fill(0); + + QPainter p(&dest); + p.drawImage(margin, margin, image); + + p.setPen(QPen(palette().brush(QPalette::WindowText), 0)); + + p.drawRect(margin-1, margin-1, image.width() + 1, image.height() + 1); + + const QColor dark(Qt::darkGray); + const QColor light(Qt::transparent); + + // right shadow + { + const QRect rect(margin + image.width() + 1, margin + shadow, shadow, image.height() - shadow + 1); + QLinearGradient lg(rect.topLeft(), rect.topRight()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom shadow + { + const QRect rect(margin + shadow, margin + image.height() + 1, image.width() - shadow + 1, shadow); + QLinearGradient lg(rect.topLeft(), rect.bottomLeft()); + lg.setColorAt(0, dark); + lg.setColorAt(1, light); + p.fillRect(rect, lg); + } + + // bottom/right corner shadow + { + const QRect rect(margin + image.width() + 1, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // top/right corner + { + const QRect rect(margin + image.width() + 1, margin, shadow, shadow); + QRadialGradient g(rect.bottomLeft(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + // bottom/left corner + { + const QRect rect(margin, margin + image.height() + 1, shadow, shadow); + QRadialGradient g(rect.topRight(), shadow); + g.setColorAt(0, dark); + g.setColorAt(1, light); + p.fillRect(rect, g); + } + + p.end(); + + return QPixmap::fromImage(dest); +} + +void NewFormWidget::loadFrom(const QString &path, bool resourceFile, const QString &uiExtension, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + const QDir dir(path); + + if (!dir.exists()) + return; + + // Iterate through the directory and add the templates + const QFileInfoList list = dir.entryInfoList(QStringList(QLatin1String("*.") + uiExtension), + QDir::Files); + + if (list.isEmpty()) + return; + + const QChar separator = resourceFile ? QChar(QLatin1Char('/')) + : QDir::separator(); + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + // Try to get something that is easy to read. + QString visiblePath = path; + int index = visiblePath.lastIndexOf(separator); + if (index != -1) { + // try to find a second slash, just to be a bit better. + const int index2 = visiblePath.lastIndexOf(separator, index - 1); + if (index2 != -1) + index = index2; + visiblePath = visiblePath.mid(index + 1); + visiblePath = QDir::toNativeSeparators(visiblePath); + } + + const QChar underscore = QLatin1Char('_'); + const QChar blank = QLatin1Char(' '); + root->setText(0, visiblePath.replace(underscore, blank)); + root->setToolTip(0, path); + + const QFileInfoList::const_iterator lcend = list.constEnd(); + for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) { + if (!it->isFile()) + continue; + + QTreeWidgetItem *item = new QTreeWidgetItem(root); + const QString text = it->baseName().replace(underscore, blank); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setText(0, text); + item->setData(0, TemplateNameRole, it->absoluteFilePath()); + } +} + +void NewFormWidget::loadFrom(const QString &title, const QStringList &nameList, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound) +{ + if (nameList.empty()) + return; + QTreeWidgetItem *root = new QTreeWidgetItem(m_ui->treeWidget); + root->setFlags(root->flags() & ~Qt::ItemIsSelectable); + root->setText(0, title); + const QStringList::const_iterator cend = nameList.constEnd(); + for (QStringList::const_iterator it = nameList.constBegin(); it != cend; ++it) { + const QString text = *it; + QTreeWidgetItem *item = new QTreeWidgetItem(root); + item->setText(0, text); + if (selectedItemFound == 0 && text == selectedItem) + selectedItemFound = item; + item->setData(0, ClassNameRole, *it); + } +} + +void NewFormWidget::on_treeWidget_itemPressed(QTreeWidgetItem *item) +{ + if (item && !item->parent()) + m_ui->treeWidget->setItemExpanded(item, !m_ui->treeWidget->isItemExpanded(item)); +} + +QSize NewFormWidget::templateSize() const +{ + return m_ui->sizeComboBox->itemData(m_ui->sizeComboBox->currentIndex()).toSize(); +} + +void NewFormWidget::setTemplateSize(const QSize &s) +{ + const int index = s.isNull() ? 0 : m_ui->sizeComboBox->findData(s); + if (index != -1) + m_ui->sizeComboBox->setCurrentIndex(index); +} + +static QString readAll(const QString &fileName, QString *errorMessage) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly|QIODevice::Text)) { + *errorMessage = NewFormWidget::tr("Unable to open the form template file '%1': %2").arg(fileName, file.errorString()); + return QString(); + } + return QString::fromUtf8(file.readAll()); +} + +QString NewFormWidget::itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const +{ + const QSize size = templateSize(); + // file name or string contents? + const QVariant templateFileName = item->data(0, TemplateNameRole); + if (templateFileName.type() == QVariant::String) { + const QString fileName = templateFileName.toString(); + // No fixed size: just open. + if (size.isNull()) + return readAll(fileName, errorMessage); + // try to find a file matching the size, like "../640x480/xx.ui" + const QFileInfo fiBase(fileName); + QString sizeFileName; + QTextStream(&sizeFileName) << fiBase.path() << QDir::separator() + << size.width() << QLatin1Char('x') << size.height() << QDir::separator() + << fiBase.fileName(); + if (QFileInfo(sizeFileName).isFile()) + return readAll(sizeFileName, errorMessage); + // Nothing found, scale via DOM/temporary file + QString contents = readAll(fileName, errorMessage); + if (!contents.isEmpty()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; + } + // Content. + const QString className = item->data(0, ClassNameRole).toString(); + QString contents = qdesigner_internal::WidgetDataBase::formTemplate(m_core, className, formName(className)); + if (!size.isNull()) + contents = qdesigner_internal::WidgetDataBase::scaleFormTemplate(contents, size, false); + return contents; +} + +void NewFormWidget::slotDeviceProfileIndexChanged(int idx) +{ + // Store index for form windows to take effect and refresh pixmap + QDesignerSharedSettings settings(m_core); + settings.setCurrentDeviceProfileIndex(idx - profileComboIndexOffset); + showCurrentItemPixmap(); +} + +int NewFormWidget::profileComboIndex() const +{ + return m_ui->profileComboBox->currentIndex(); +} + +qdesigner_internal::DeviceProfile NewFormWidget::currentDeviceProfile() const +{ + const int ci = profileComboIndex(); + if (ci > 0) + return m_deviceProfiles.at(ci - profileComboIndexOffset); + return qdesigner_internal::DeviceProfile(); +} + +bool NewFormWidget::hasCurrentTemplate() const +{ + return m_currentItem != 0; +} + +QString NewFormWidget::currentTemplateI(QString *ptrToErrorMessage) +{ + if (m_currentItem == 0) { + *ptrToErrorMessage = tr("Internal error: No template selected."); + return QString(); + } + const QString contents = itemToTemplate(m_currentItem, ptrToErrorMessage); + if (contents.isEmpty()) + return contents; + + m_acceptedItem = m_currentItem; + return contents; +} + +QString NewFormWidget::currentTemplate(QString *ptrToErrorMessage) +{ + if (ptrToErrorMessage) + return currentTemplateI(ptrToErrorMessage); + // Do not loose the error + QString errorMessage; + const QString contents = currentTemplateI(&errorMessage); + if (!errorMessage.isEmpty()) + qWarning("%s", errorMessage.toUtf8().constData()); + return contents; +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/newformwidget.ui b/tools/designer/src/lib/shared/newformwidget.ui new file mode 100644 index 0000000..c062602 --- /dev/null +++ b/tools/designer/src/lib/shared/newformwidget.ui @@ -0,0 +1,192 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>qdesigner_internal::NewFormWidget</class> + <widget class="QWidget" name="qdesigner_internal::NewFormWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>194</height> + </rect> + </property> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>1</number> + </property> + <item> + <widget class="QTreeWidget" name="treeWidget"> + <property name="minimumSize"> + <size> + <width>200</width> + <height>0</height> + </size> + </property> + <property name="iconSize"> + <size> + <width>128</width> + <height>128</height> + </size> + </property> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="columnCount"> + <number>1</number> + </property> + <column> + <property name="text"> + <string>0</string> + </property> + </column> + </widget> + </item> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="lblPreview"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="lineWidth"> + <number>1</number> + </property> + <property name="text"> + <string>Choose a template for a preview</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="margin"> + <number>5</number> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>7</width> + <height>0</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QGroupBox" name="embeddedGroup"> + <property name="title"> + <string>Embedded Design</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="1"> + <widget class="QComboBox" name="profileComboBox"/> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="sizeComboBox"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Device:</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Screen Size:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/newformwidget_p.h b/tools/designer/src/lib/shared/newformwidget_p.h new file mode 100644 index 0000000..f94f952 --- /dev/null +++ b/tools/designer/src/lib/shared/newformwidget_p.h @@ -0,0 +1,143 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef NEWFORMWIDGET_H +#define NEWFORMWIDGET_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "shared_global_p.h" +#include "deviceprofile_p.h" + +#include <abstractnewformwidget_p.h> + +#include <QtGui/QWidget> +#include <QtGui/QPixmap> + +#include <QtCore/QStringList> +#include <QtCore/QPair> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE + +class QIODevice; +class QTreeWidgetItem; + +namespace qdesigner_internal { + +namespace Ui { + class NewFormWidget; +} + +class QDesignerWorkbench; + +class QDESIGNER_SHARED_EXPORT NewFormWidget : public QDesignerNewFormWidgetInterface +{ + Q_OBJECT + Q_DISABLE_COPY(NewFormWidget) + +public: + typedef QList<qdesigner_internal::DeviceProfile> DeviceProfileList; + + explicit NewFormWidget(QDesignerFormEditorInterface *core, QWidget *parentWidget); + virtual ~NewFormWidget(); + + virtual bool hasCurrentTemplate() const; + virtual QString currentTemplate(QString *errorMessage = 0); + + // Convenience for implementing file dialogs with preview + static QImage grabForm(QDesignerFormEditorInterface *core, + QIODevice &file, + const QString &workingDir, + const qdesigner_internal::DeviceProfile &dp); + +private slots: + void on_treeWidget_itemActivated(QTreeWidgetItem *item); + void on_treeWidget_currentItemChanged(QTreeWidgetItem *current, QTreeWidgetItem *); + void on_treeWidget_itemPressed(QTreeWidgetItem *item); + void slotDeviceProfileIndexChanged(int idx); + +private: + QPixmap formPreviewPixmap(const QString &fileName) const; + QPixmap formPreviewPixmap(QIODevice &file, const QString &workingDir = QString()) const; + QPixmap formPreviewPixmap(const QTreeWidgetItem *item); + + void loadFrom(const QString &path, bool resourceFile, const QString &uiExtension, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound); + void loadFrom(const QString &title, const QStringList &nameList, + const QString &selectedItem, QTreeWidgetItem *&selectedItemFound); + +private: + QString itemToTemplate(const QTreeWidgetItem *item, QString *errorMessage) const; + QString currentTemplateI(QString *ptrToErrorMessage); + + QSize templateSize() const; + void setTemplateSize(const QSize &s); + int profileComboIndex() const; + qdesigner_internal::DeviceProfile currentDeviceProfile() const; + bool showCurrentItemPixmap(); + + // Pixmap cache (item, profile combo index) + typedef QPair<const QTreeWidgetItem *, int> ItemPixmapCacheKey; + typedef QMap<ItemPixmapCacheKey, QPixmap> ItemPixmapCache; + ItemPixmapCache m_itemPixmapCache; + + QDesignerFormEditorInterface *m_core; + Ui::NewFormWidget *m_ui; + QTreeWidgetItem *m_currentItem; + QTreeWidgetItem *m_acceptedItem; + DeviceProfileList m_deviceProfiles; +}; + +} + +QT_END_NAMESPACE + +#endif // NEWFORMWIDGET_H diff --git a/tools/designer/src/lib/shared/orderdialog.cpp b/tools/designer/src/lib/shared/orderdialog.cpp new file mode 100644 index 0000000..3f14ca6 --- /dev/null +++ b/tools/designer/src/lib/shared/orderdialog.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::OrderDialog +*/ + +#include "orderdialog_p.h" +#include "iconloader_p.h" +#include "ui_orderdialog.h" + +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtCore/QAbstractItemModel> +#include <QtCore/QModelIndex> +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +// OrderDialog: Used to reorder the pages of QStackedWidget and QToolBox. +// Provides up and down buttons as well as DnD via QAbstractItemView::InternalMove mode +namespace qdesigner_internal { + +OrderDialog::OrderDialog(QWidget *parent) : + QDialog(parent), + m_ui(new Ui::OrderDialog), + m_format(PageOrderFormat) +{ + m_ui->setupUi(this); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + m_ui->upButton->setIcon(createIconSet(QString::fromUtf8("up.png"))); + m_ui->downButton->setIcon(createIconSet(QString::fromUtf8("down.png"))); + m_ui->buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + connect(m_ui->buttonBox->button(QDialogButtonBox::Reset), SIGNAL(clicked()), this, SLOT(slotReset())); + // Catch the remove operation of a DnD operation in QAbstractItemView::InternalMove mode to enable buttons + // Selection mode is 'contiguous' to enable DnD of groups + connect(m_ui->pageList->model(), SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SLOT(slotEnableButtonsAfterDnD())); + + m_ui->upButton->setEnabled(false); + m_ui->downButton->setEnabled(false); +} + +OrderDialog::~OrderDialog() +{ + delete m_ui; +} + +void OrderDialog::setDescription(const QString &d) +{ + m_ui->groupBox->setTitle(d); +} + +void OrderDialog::setPageList(const QWidgetList &pages) +{ + // The QWidget* are stored in a map indexed by the old index. + // The old index is set as user data on the item instead of the QWidget* + // because DnD is enabled which requires the user data to serializable + m_orderMap.clear(); + const int count = pages.count(); + for (int i=0; i < count; ++i) + m_orderMap.insert(i, pages.at(i)); + buildList(); +} + +void OrderDialog::buildList() +{ + m_ui->pageList->clear(); + const OrderMap::const_iterator cend = m_orderMap.constEnd(); + for (OrderMap::const_iterator it = m_orderMap.constBegin(); it != cend; ++it) { + QListWidgetItem *item = new QListWidgetItem(); + const int index = it.key(); + switch (m_format) { + case PageOrderFormat: + item->setText(tr("Index %1 (%2)").arg(index).arg(it.value()->objectName())); + break; + case TabOrderFormat: + item->setText(tr("%1 %2").arg(index+1).arg(it.value()->objectName())); + break; + } + item->setData(Qt::UserRole, QVariant(index)); + m_ui->pageList->addItem(item); + } + + if (m_ui->pageList->count() > 0) + m_ui->pageList->setCurrentRow(0); +} + +void OrderDialog::slotReset() +{ + buildList(); +} + +QWidgetList OrderDialog::pageList() const +{ + QWidgetList rc; + const int count = m_ui->pageList->count(); + for (int i=0; i < count; ++i) { + const int oldIndex = m_ui->pageList->item(i)->data(Qt::UserRole).toInt(); + rc.append(m_orderMap.value(oldIndex)); + } + return rc; +} + +void OrderDialog::on_upButton_clicked() +{ + const int row = m_ui->pageList->currentRow(); + if (row <= 0) + return; + + m_ui->pageList->insertItem(row - 1, m_ui->pageList->takeItem(row)); + m_ui->pageList->setCurrentRow(row - 1); +} + +void OrderDialog::on_downButton_clicked() +{ + const int row = m_ui->pageList->currentRow(); + if (row == -1 || row == m_ui->pageList->count() - 1) + return; + + m_ui->pageList->insertItem(row + 1, m_ui->pageList->takeItem(row)); + m_ui->pageList->setCurrentRow(row + 1); +} + +void OrderDialog::slotEnableButtonsAfterDnD() +{ + enableButtons(m_ui->pageList->currentRow()); +} + +void OrderDialog::on_pageList_currentRowChanged(int r) +{ + enableButtons(r); +} + +void OrderDialog::enableButtons(int r) +{ + m_ui->upButton->setEnabled(r > 0); + m_ui->downButton->setEnabled(r >= 0 && r < m_ui->pageList->count() - 1); +} + +QWidgetList OrderDialog::pagesOfContainer(const QDesignerFormEditorInterface *core, QWidget *container) +{ + QWidgetList rc; + if (QDesignerContainerExtension* ce = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), container)) { + const int count = ce->count(); + for (int i = 0; i < count ;i ++) + rc.push_back(ce->widget(i)); + } + return rc; +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/orderdialog.ui b/tools/designer/src/lib/shared/orderdialog.ui new file mode 100644 index 0000000..dcecb31 --- /dev/null +++ b/tools/designer/src/lib/shared/orderdialog.ui @@ -0,0 +1,198 @@ +<ui version="4.0" > + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>qdesigner_internal::OrderDialog</class> + <widget class="QDialog" name="qdesigner_internal::OrderDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>467</width> + <height>310</height> + </rect> + </property> + <property name="windowTitle" > + <string>Change Page Order</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="groupBox" > + <property name="title" > + <string>Page Order</string> + </property> + <layout class="QHBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>9</number> + </property> + <property name="topMargin" > + <number>9</number> + </property> + <property name="rightMargin" > + <number>9</number> + </property> + <property name="bottomMargin" > + <number>9</number> + </property> + <item> + <widget class="QListWidget" name="pageList" > + <property name="minimumSize" > + <size> + <width>344</width> + <height>0</height> + </size> + </property> + <property name="dragDropMode" > + <enum>QAbstractItemView::InternalMove</enum> + </property> + <property name="selectionMode" > + <enum>QAbstractItemView::ContiguousSelection</enum> + </property> + <property name="movement" > + <enum>QListView::Snap</enum> + </property> + </widget> + </item> + <item> + <layout class="QVBoxLayout" > + <property name="spacing" > + <number>6</number> + </property> + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item> + <widget class="QToolButton" name="upButton" > + <property name="toolTip" > + <string>Move page up</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="downButton" > + <property name="toolTip" > + <string>Move page down</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Fixed" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>20</width> + <height>99</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok|QDialogButtonBox::Reset</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>qdesigner_internal::OrderDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>50</x> + <y>163</y> + </hint> + <hint type="destinationlabel" > + <x>6</x> + <y>151</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>qdesigner_internal::OrderDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>300</x> + <y>160</y> + </hint> + <hint type="destinationlabel" > + <x>348</x> + <y>148</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/orderdialog_p.h b/tools/designer/src/lib/shared/orderdialog_p.h new file mode 100644 index 0000000..e8897e6 --- /dev/null +++ b/tools/designer/src/lib/shared/orderdialog_p.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ORDERDIALOG_P_H +#define ORDERDIALOG_P_H + +#include "shared_global_p.h" + +#include <QtGui/QDialog> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +namespace Ui { + class OrderDialog; +} + +class QDESIGNER_SHARED_EXPORT OrderDialog: public QDialog +{ + Q_OBJECT +public: + OrderDialog(QWidget *parent); + virtual ~OrderDialog(); + + static QWidgetList pagesOfContainer(const QDesignerFormEditorInterface *core, QWidget *container); + + void setPageList(const QWidgetList &pages); + QWidgetList pageList() const; + + void setDescription(const QString &d); + + enum Format { // Display format + PageOrderFormat, // Container pages, ranging 0..[n-1] + TabOrderFormat // List of widgets, ranging 1..1 + }; + + void setFormat(Format f) { m_format = f; } + Format format() const { return m_format; } + +private slots: + void on_upButton_clicked(); + void on_downButton_clicked(); + void on_pageList_currentRowChanged(int row); + void slotEnableButtonsAfterDnD(); + void slotReset(); + +private: + void buildList(); + void enableButtons(int r); + + typedef QMap<int, QWidget*> OrderMap; + OrderMap m_orderMap; + Ui::OrderDialog* m_ui; + Format m_format; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // ORDERDIALOG_P_H diff --git a/tools/designer/src/lib/shared/plaintexteditor.cpp b/tools/designer/src/lib/shared/plaintexteditor.cpp new file mode 100644 index 0000000..ce5cd5b --- /dev/null +++ b/tools/designer/src/lib/shared/plaintexteditor.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::PlainTextEditorDialog +*/ + +#include "plaintexteditor_p.h" +#include "abstractsettings_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtGui/QPlainTextEdit> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QVBoxLayout> +#include <QtGui/QPushButton> + +QT_BEGIN_NAMESPACE + +static const char *PlainTextDialogC = "PlainTextDialog"; +static const char *Geometry = "Geometry"; + + +namespace qdesigner_internal { + +PlainTextEditorDialog::PlainTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + m_editor(new QPlainTextEdit), + m_core(core) +{ + setWindowTitle(tr("Edit text")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vlayout = new QVBoxLayout(this); + vlayout->addWidget(m_editor); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + QPushButton *ok_button = buttonBox->button(QDialogButtonBox::Ok); + ok_button->setDefault(true); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + vlayout->addWidget(buttonBox); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(PlainTextDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +PlainTextEditorDialog::~PlainTextEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(PlainTextDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +int PlainTextEditorDialog::showDialog() +{ + m_editor->setFocus(); + return exec(); +} + +void PlainTextEditorDialog::setDefaultFont(const QFont &font) +{ + m_editor->setFont(font); +} + +void PlainTextEditorDialog::setText(const QString &text) +{ + m_editor->setPlainText(text); +} + +QString PlainTextEditorDialog::text() const +{ + return m_editor->toPlainText(); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/plaintexteditor_p.h b/tools/designer/src/lib/shared/plaintexteditor_p.h new file mode 100644 index 0000000..d227db8 --- /dev/null +++ b/tools/designer/src/lib/shared/plaintexteditor_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PLAINTEXTEDITOR_H +#define PLAINTEXTEDITOR_H + +#include <QtGui/QDialog> +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QPlainTextEdit; +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT PlainTextEditorDialog : public QDialog +{ + Q_OBJECT +public: + explicit PlainTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~PlainTextEditorDialog(); + + int showDialog(); + + void setDefaultFont(const QFont &font); + + void setText(const QString &text); + QString text() const; + +private: + QPlainTextEdit *m_editor; + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // RITCHTEXTEDITOR_H diff --git a/tools/designer/src/lib/shared/plugindialog.cpp b/tools/designer/src/lib/shared/plugindialog.cpp new file mode 100644 index 0000000..447f1e2 --- /dev/null +++ b/tools/designer/src/lib/shared/plugindialog.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "plugindialog_p.h" +#include "pluginmanager_p.h" +#include "qdesigner_integration_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerCustomWidgetCollectionInterface> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtGui/QStyle> +#include <QtGui/QHeaderView> +#include <QtGui/QPushButton> +#include <QtCore/QFileInfo> +#include <QtCore/QPluginLoader> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +PluginDialog::PluginDialog(QDesignerFormEditorInterface *core, QWidget *parent) + : QDialog(parent +#ifdef Q_WS_MAC + , Qt::Tool +#endif + ), m_core(core) +{ + ui.setupUi(this); + + ui.message->hide(); + + const QStringList headerLabels(tr("Components")); + + ui.treeWidget->setAlternatingRowColors(false); + ui.treeWidget->setSelectionMode(QAbstractItemView::NoSelection); + ui.treeWidget->setHeaderLabels(headerLabels); + ui.treeWidget->header()->hide(); + + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirOpenIcon), + QIcon::Normal, QIcon::On); + interfaceIcon.addPixmap(style()->standardPixmap(QStyle::SP_DirClosedIcon), + QIcon::Normal, QIcon::Off); + featureIcon.addPixmap(style()->standardPixmap(QStyle::SP_FileIcon)); + + setWindowTitle(tr("Plugin Information")); + populateTreeWidget(); + + if (qobject_cast<qdesigner_internal::QDesignerIntegration *>(m_core->integration())) { + QPushButton *updateButton = new QPushButton(tr("Refresh")); + const QString tooltip = tr("Scan for newly installed custom widget plugins."); + updateButton->setToolTip(tooltip); + updateButton->setWhatsThis(tooltip); + connect(updateButton, SIGNAL(clicked()), this, SLOT(updateCustomWidgetPlugins())); + ui.buttonBox->addButton(updateButton, QDialogButtonBox::ActionRole); + } +} + +void PluginDialog::populateTreeWidget() +{ + ui.treeWidget->clear(); + QDesignerPluginManager *pluginManager = m_core->pluginManager(); + const QStringList fileNames = pluginManager->registeredPlugins(); + + if (!fileNames.isEmpty()) { + QTreeWidgetItem *topLevelItem = setTopLevelItem(QLatin1String("Loaded Plugins")); + QFont boldFont = topLevelItem->font(0); + + foreach (QString fileName, fileNames) { + QPluginLoader loader(fileName); + const QFileInfo fileInfo(fileName); + + QTreeWidgetItem *pluginItem = setPluginItem(topLevelItem, fileInfo.fileName(), boldFont); + + if (QObject *plugin = loader.instance()) { + if (const QDesignerCustomWidgetCollectionInterface *c = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(plugin)) { + foreach (const QDesignerCustomWidgetInterface *p, c->customWidgets()) + setItem(pluginItem, p->name(), p->toolTip(), p->whatsThis(), p->icon()); + } else { + if (const QDesignerCustomWidgetInterface *p = qobject_cast<QDesignerCustomWidgetInterface*>(plugin)) + setItem(pluginItem, p->name(), p->toolTip(), p->whatsThis(), p->icon()); + } + } + } + } + + const QStringList notLoadedPlugins = pluginManager->failedPlugins(); + if (!notLoadedPlugins.isEmpty()) { + QTreeWidgetItem *topLevelItem = setTopLevelItem(QLatin1String("Failed Plugins")); + const QFont boldFont = topLevelItem->font(0); + foreach (const QString plugin, notLoadedPlugins) { + const QString failureReason = pluginManager->failureReason(plugin); + QTreeWidgetItem *pluginItem = setPluginItem(topLevelItem, plugin, boldFont); + setItem(pluginItem, failureReason, failureReason, QString(), QIcon()); + } + } + + if (ui.treeWidget->topLevelItemCount() == 0) { + ui.label->setText(tr("Qt Designer couldn't find any plugins")); + ui.treeWidget->hide(); + } else { + ui.label->setText(tr("Qt Designer found the following plugins")); + } +} + +QIcon PluginDialog::pluginIcon(const QIcon &icon) +{ + if (icon.isNull()) + return QIcon(QLatin1String(":/trolltech/formeditor/images/qtlogo.png")); + + return icon; +} + +QTreeWidgetItem* PluginDialog::setTopLevelItem(const QString &itemName) +{ + QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(ui.treeWidget); + topLevelItem->setText(0, itemName); + ui.treeWidget->setItemExpanded(topLevelItem, true); + topLevelItem->setIcon(0, style()->standardPixmap(QStyle::SP_DirOpenIcon)); + + QFont boldFont = topLevelItem->font(0); + boldFont.setBold(true); + topLevelItem->setFont(0, boldFont); + + return topLevelItem; +} + +QTreeWidgetItem* PluginDialog::setPluginItem(QTreeWidgetItem *topLevelItem, + const QString &itemName, const QFont &font) +{ + QTreeWidgetItem *pluginItem = new QTreeWidgetItem(topLevelItem); + pluginItem->setFont(0, font); + pluginItem->setText(0, itemName); + ui.treeWidget->setItemExpanded(pluginItem, true); + pluginItem->setIcon(0, style()->standardPixmap(QStyle::SP_DirOpenIcon)); + + return pluginItem; +} + +void PluginDialog::setItem(QTreeWidgetItem *pluginItem, const QString &name, + const QString &toolTip, const QString &whatsThis, const QIcon &icon) +{ + QTreeWidgetItem *item = new QTreeWidgetItem(pluginItem); + item->setText(0, name); + item->setToolTip(0, toolTip); + item->setWhatsThis(0, whatsThis); + item->setIcon(0, pluginIcon(icon)); +} + +void PluginDialog::updateCustomWidgetPlugins() +{ + if (qdesigner_internal::QDesignerIntegration *integration = qobject_cast<qdesigner_internal::QDesignerIntegration *>(m_core->integration())) { + const int before = m_core->widgetDataBase()->count(); + integration->updateCustomWidgetPlugins(); + const int after = m_core->widgetDataBase()->count(); + if (after > before) { + ui.message->setText(tr("New custom widget plugins have been found.")); + ui.message->show(); + } else { + ui.message->setText(QString()); + } + populateTreeWidget(); + } +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/plugindialog.ui b/tools/designer/src/lib/shared/plugindialog.ui new file mode 100644 index 0000000..b12a42d --- /dev/null +++ b/tools/designer/src/lib/shared/plugindialog.ui @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <comment>********************************************************************* +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +*********************************************************************</comment> + <class>PluginDialog</class> + <widget class="QDialog" name="PluginDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>401</width> + <height>331</height> + </rect> + </property> + <property name="windowTitle"> + <string>Plugin Information</string> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>8</number> + </property> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string notr="true">TextLabel</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QTreeWidget" name="treeWidget"> + <property name="textElideMode"> + <enum>Qt::ElideNone</enum> + </property> + <column> + <property name="text"> + <string>1</string> + </property> + </column> + </widget> + </item> + <item> + <widget class="QLabel" name="message"> + <property name="text"> + <string notr="true">TextLabel</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + </layout> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>PluginDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>154</x> + <y>307</y> + </hint> + <hint type="destinationlabel"> + <x>401</x> + <y>280</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/plugindialog_p.h b/tools/designer/src/lib/shared/plugindialog_p.h new file mode 100644 index 0000000..d50a804 --- /dev/null +++ b/tools/designer/src/lib/shared/plugindialog_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLUGINDIALOG_H +#define PLUGINDIALOG_H + +#include "ui_plugindialog.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class PluginDialog : public QDialog +{ + Q_OBJECT +public: + explicit PluginDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +private slots: + void updateCustomWidgetPlugins(); + +private: + void populateTreeWidget(); + QIcon pluginIcon(const QIcon &icon); + QTreeWidgetItem* setTopLevelItem(const QString &itemName); + QTreeWidgetItem* setPluginItem(QTreeWidgetItem *topLevelItem, + const QString &itemName, const QFont &font); + void setItem(QTreeWidgetItem *pluginItem, const QString &name, + const QString &toolTip, const QString &whatsThis, const QIcon &icon); + + QDesignerFormEditorInterface *m_core; + Ui::PluginDialog ui; + QIcon interfaceIcon; + QIcon featureIcon; +}; + +} + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/pluginmanager.cpp b/tools/designer/src/lib/shared/pluginmanager.cpp new file mode 100644 index 0000000..9dc8c7b --- /dev/null +++ b/tools/designer/src/lib/shared/pluginmanager.cpp @@ -0,0 +1,670 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "pluginmanager_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_qsettings_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerCustomWidgetInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerLanguageExtension> + +#include <QtCore/QDir> +#include <QtCore/QFile> +#include <QtCore/QFileInfo> +#include <QtCore/QSet> +#include <QtCore/QPluginLoader> +#include <QtCore/QLibrary> +#include <QtCore/QLibraryInfo> +#include <QtCore/qdebug.h> +#include <QtCore/QMap> +#include <QtCore/QSettings> +#include <QtCore/QCoreApplication> + +#include <QtCore/QXmlStreamReader> +#include <QtCore/QXmlStreamAttributes> +#include <QtCore/QXmlStreamAttribute> + +static const char *uiElementC = "ui"; +static const char *languageAttributeC = "language"; +static const char *widgetElementC = "widget"; +static const char *displayNameAttributeC = "displayname"; +static const char *classAttributeC = "class"; +static const char *customwidgetElementC = "customwidget"; +static const char *extendsElementC = "extends"; +static const char *addPageMethodC = "addpagemethod"; +static const char *jambiLanguageC = "jambi"; + +enum { debugPluginManager = 0 }; + +/* Custom widgets: Loading custom widgets is a 2-step process: PluginManager + * scans for its plugins in the constructor. At this point, it might not be safe + * to immediately initialize the custom widgets it finds, because the rest of + * Designer is not initialized yet. + * Later on, in ensureInitialized(), the plugin instances (including static ones) + * are iterated and the custom widget plugins are initialized and added to internal + * list of custom widgets and parsed data. Should there be a parse error or a language + * mismatch, it kicks out the respective custom widget. The m_initialized flag + * is used to indicate the state. + * Later, someone might call registerNewPlugins(), which agains clears the flag via + * registerPlugin() and triggers the process again. + * Also note that Jambi fakes a custom widget collection that changes its contents + * every time the project is switched. So, custom widget plugins can actually + * disappear, and the custom widget list must be cleared and refilled in + * ensureInitialized() after registerNewPlugins. */ + +QT_BEGIN_NAMESPACE + +static QStringList unique(const QStringList &lst) +{ + const QSet<QString> s = QSet<QString>::fromList(lst); + return s.toList(); +} + +QStringList QDesignerPluginManager::defaultPluginPaths() +{ + QStringList result; + + const QStringList path_list = QCoreApplication::libraryPaths(); + + const QString designer = QLatin1String("designer"); + foreach (const QString &path, path_list) { + QString libPath = path; + libPath += QDir::separator(); + libPath += designer; + result.append(libPath); + } + + QString homeLibPath = QDir::homePath(); + homeLibPath += QDir::separator(); + homeLibPath += QLatin1String(".designer"); + homeLibPath += QDir::separator(); + homeLibPath += QLatin1String("plugins"); + + result.append(homeLibPath); + return result; +} + +// Figure out the language designer is running. ToDo: Introduce some +// Language name API to QDesignerLanguageExtension? + +static inline QString getDesignerLanguage(QDesignerFormEditorInterface *core) +{ + if (QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) { + if (lang->uiExtension() == QLatin1String("jui")) + return QLatin1String(jambiLanguageC); + return QLatin1String("unknown"); + } + return QLatin1String("c++"); +} + +// ---------------- QDesignerCustomWidgetSharedData + +class QDesignerCustomWidgetSharedData : public QSharedData { +public: + explicit QDesignerCustomWidgetSharedData(const QString &thePluginPath) : pluginPath(thePluginPath) {} + void clearXML(); + + QString pluginPath; + + QString xmlClassName; + QString xmlDisplayName; + QString xmlLanguage; + QString xmlAddPageMethod; + QString xmlExtends; + +}; + +void QDesignerCustomWidgetSharedData::clearXML() +{ + xmlClassName.clear(); + xmlDisplayName.clear(); + xmlLanguage.clear(); + xmlAddPageMethod.clear(); + xmlExtends.clear(); +} + +// ---------------- QDesignerCustomWidgetData + +QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QString &pluginPath) : + m_d(new QDesignerCustomWidgetSharedData(pluginPath)) +{ +} + +QDesignerCustomWidgetData::QDesignerCustomWidgetData(const QDesignerCustomWidgetData &o) : + m_d(o.m_d) +{ +} + +QDesignerCustomWidgetData& QDesignerCustomWidgetData::operator=(const QDesignerCustomWidgetData &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +QDesignerCustomWidgetData::~QDesignerCustomWidgetData() +{ +} + +bool QDesignerCustomWidgetData::isNull() const +{ + return m_d->xmlClassName.isEmpty() || m_d->pluginPath.isEmpty(); +} + +QString QDesignerCustomWidgetData::xmlClassName() const +{ + return m_d->xmlClassName; +} + +QString QDesignerCustomWidgetData::xmlLanguage() const +{ + return m_d->xmlLanguage; +} + +QString QDesignerCustomWidgetData::xmlAddPageMethod() const +{ + return m_d->xmlAddPageMethod; +} + +QString QDesignerCustomWidgetData::xmlExtends() const +{ + return m_d->xmlExtends; +} + +QString QDesignerCustomWidgetData::xmlDisplayName() const +{ + return m_d->xmlDisplayName; +} + +QString QDesignerCustomWidgetData::pluginPath() const +{ + return m_d->pluginPath; +} + +// Wind a QXmlStreamReader until it finds an element. Returns index or one of FindResult +enum FindResult { FindError = -2, ElementNotFound = -1 }; + +static int findElement(const QStringList &desiredElts, QXmlStreamReader &sr) +{ + while (true) { + switch(sr.readNext()) { + case QXmlStreamReader::EndDocument: + return ElementNotFound; + case QXmlStreamReader::Invalid: + return FindError; + case QXmlStreamReader::StartElement: { + const int index = desiredElts.indexOf(sr.name().toString().toLower()); + if (index >= 0) + return index; + } + break; + default: + break; + } + } + return FindError; +} + +static inline QString msgXmlError(const QString &name, const QString &errorMessage) +{ + return QDesignerPluginManager::tr("An XML error was encountered when parsing the XML of the custom widget %1: %2").arg(name, errorMessage); +} + +QDesignerCustomWidgetData::ParseResult + QDesignerCustomWidgetData::parseXml(const QString &xml, const QString &name, QString *errorMessage) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << name; + + QDesignerCustomWidgetSharedData &data = *m_d; + data.clearXML(); + + QXmlStreamReader sr(xml); + + bool foundUI = false; + bool foundWidget = false; + ParseResult rc = ParseOk; + // Parse for the (optional) <ui> or the first <widget> element + QStringList elements; + elements.push_back(QLatin1String(uiElementC)); + elements.push_back(QLatin1String(widgetElementC)); + for (int i = 0; i < 2 && !foundWidget; i++) { + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + *errorMessage = QDesignerPluginManager::tr("The XML of the custom widget %1 does not contain any of the elements <widget> or <ui>.").arg(name); + return ParseError; + case 0: { // <ui> + const QXmlStreamAttributes attributes = sr.attributes(); + data.xmlLanguage = attributes.value(QLatin1String(languageAttributeC)).toString(); + data.xmlDisplayName = attributes.value(QLatin1String(displayNameAttributeC)).toString(); + foundUI = true; + } + break; + case 1: // <widget>: Do some sanity checks + data.xmlClassName = sr.attributes().value(QLatin1String(classAttributeC)).toString(); + if (data.xmlClassName.isEmpty()) { + *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 is missing.").arg(name); + rc = ParseWarning; + } else { + if (data.xmlClassName != name) { + *errorMessage = QDesignerPluginManager::tr("The class attribute for the class %1 does not match the class name %2.").arg(data.xmlClassName, name); + rc = ParseWarning; + } + } + foundWidget = true; + break; + } + } + // Parse out the <customwidget> element which might be present if <ui> was there + if (!foundUI) + return rc; + elements.clear(); + elements.push_back(QLatin1String(customwidgetElementC)); + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + return rc; + default: + break; + } + // Find <extends>, <addPageMethod> + elements.clear(); + elements.push_back(QLatin1String(extendsElementC)); + elements.push_back(QLatin1String(addPageMethodC)); + while (true) { + switch (findElement(elements, sr)) { + case FindError: + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + case ElementNotFound: + return rc; + case 0: // <extends> + data.xmlExtends = sr.readElementText(); + if (sr.tokenType() != QXmlStreamReader::EndElement) { + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + } + break; + case 1: // <addPageMethod> + data.xmlAddPageMethod = sr.readElementText(); + if (sr.tokenType() != QXmlStreamReader::EndElement) { + *errorMessage = msgXmlError(name, sr.errorString()); + return ParseError; + } + break; + } + } + return rc; +} + +// ---------------- QDesignerPluginManagerPrivate + +class QDesignerPluginManagerPrivate { + public: + QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core); + + void clearCustomWidgets(); + bool addCustomWidget(QDesignerCustomWidgetInterface *c, + const QString &pluginPath, + const QString &designerLanguage); + void addCustomWidgets(const QObject *o, + const QString &pluginPath, + const QString &designerLanguage); + + QDesignerFormEditorInterface *m_core; + QStringList m_pluginPaths; + QStringList m_registeredPlugins; + // TODO: QPluginLoader also caches invalid plugins -> This seems to be dead code + QStringList m_disabledPlugins; + + typedef QMap<QString, QString> FailedPluginMap; + FailedPluginMap m_failedPlugins; + + // Synced lists of custom widgets and their data. Note that the list + // must be ordered for collections to appear in order. + QList<QDesignerCustomWidgetInterface *> m_customWidgets; + QList<QDesignerCustomWidgetData> m_customWidgetData; + + QStringList defaultPluginPaths() const; + + bool m_initialized; +}; + +QDesignerPluginManagerPrivate::QDesignerPluginManagerPrivate(QDesignerFormEditorInterface *core) : + m_core(core), + m_initialized(false) +{ +} + +void QDesignerPluginManagerPrivate::clearCustomWidgets() +{ + m_customWidgets.clear(); + m_customWidgetData.clear(); +} + +// Add a custom widget to the list if it parses correctly +// and is of the right language +bool QDesignerPluginManagerPrivate::addCustomWidget(QDesignerCustomWidgetInterface *c, + const QString &pluginPath, + const QString &designerLanguage) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << c->name(); + + if (!c->isInitialized()) + c->initialize(m_core); + // Parse the XML even if the plugin is initialized as Jambi might play tricks here + QDesignerCustomWidgetData data(pluginPath); + const QString domXml = c->domXml(); + if (!domXml.isEmpty()) { // Legacy: Empty XML means: Do not show up in widget box. + QString errorMessage; + const QDesignerCustomWidgetData::ParseResult pr = data.parseXml(domXml, c->name(), &errorMessage); + switch (pr) { + case QDesignerCustomWidgetData::ParseOk: + break; + case QDesignerCustomWidgetData::ParseWarning: + qdesigner_internal::designerWarning(errorMessage); + break; + case QDesignerCustomWidgetData::ParseError: + qdesigner_internal::designerWarning(errorMessage); + return false; + } + // Does the language match? + const QString pluginLanguage = data.xmlLanguage(); + if (!pluginLanguage.isEmpty() && pluginLanguage.compare(designerLanguage, Qt::CaseInsensitive)) + return false; + } + m_customWidgets.push_back(c); + m_customWidgetData.push_back(data); + return true; +} + +// Check the plugin interface for either a custom widget or a collection and +// add all contained custom widgets. +void QDesignerPluginManagerPrivate::addCustomWidgets(const QObject *o, + const QString &pluginPath, + const QString &designerLanguage) +{ + if (QDesignerCustomWidgetInterface *c = qobject_cast<QDesignerCustomWidgetInterface*>(o)) { + addCustomWidget(c, pluginPath, designerLanguage); + return; + } + if (const QDesignerCustomWidgetCollectionInterface *coll = qobject_cast<QDesignerCustomWidgetCollectionInterface*>(o)) { + foreach(QDesignerCustomWidgetInterface *c, coll->customWidgets()) + addCustomWidget(c, pluginPath, designerLanguage); + } +} + + +// ---------------- QDesignerPluginManager +// As of 4.4, the header will be distributed with the Eclipse plugin. + +QDesignerPluginManager::QDesignerPluginManager(QDesignerFormEditorInterface *core) : + QObject(core), + m_d(new QDesignerPluginManagerPrivate(core)) +{ + m_d->m_pluginPaths = defaultPluginPaths(); + const QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); + m_d->m_disabledPlugins = unique(settings.value(QLatin1String("PluginManager/DisabledPlugins")).toStringList()); + + // Register plugins + updateRegisteredPlugins(); + + if (debugPluginManager) + qDebug() << "QDesignerPluginManager::disabled: " << m_d->m_disabledPlugins << " static " << m_d->m_customWidgets.size(); +} + +QDesignerPluginManager::~QDesignerPluginManager() +{ + syncSettings(); + delete m_d; +} + +QDesignerFormEditorInterface *QDesignerPluginManager::core() const +{ + return m_d->m_core; +} + +QStringList QDesignerPluginManager::findPlugins(const QString &path) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << path; + const QDir dir(path); + if (!dir.exists()) + return QStringList(); + + const QFileInfoList infoList = dir.entryInfoList(QDir::Files); + if (infoList.empty()) + return QStringList(); + + // Load symbolic links but make sure all file names are unique as not + // to fall for something like 'libplugin.so.1 -> libplugin.so' + QStringList result; + const QFileInfoList::const_iterator icend = infoList.constEnd(); + for (QFileInfoList::const_iterator it = infoList.constBegin(); it != icend; ++it) { + QString fileName; + if (it->isSymLink()) { + const QFileInfo linkTarget = QFileInfo(it->symLinkTarget()); + if (linkTarget.exists() && linkTarget.isFile()) + fileName = linkTarget.absoluteFilePath(); + } else { + fileName = it->absoluteFilePath(); + } + if (!fileName.isEmpty() && QLibrary::isLibrary(fileName) && !result.contains(fileName)) + result += fileName; + } + return result; +} + +void QDesignerPluginManager::setDisabledPlugins(const QStringList &disabled_plugins) +{ + m_d->m_disabledPlugins = disabled_plugins; + updateRegisteredPlugins(); +} + +void QDesignerPluginManager::setPluginPaths(const QStringList &plugin_paths) +{ + m_d->m_pluginPaths = plugin_paths; + updateRegisteredPlugins(); +} + +QStringList QDesignerPluginManager::disabledPlugins() const +{ + return m_d->m_disabledPlugins; +} + +QStringList QDesignerPluginManager::failedPlugins() const +{ + return m_d->m_failedPlugins.keys(); +} + +QString QDesignerPluginManager::failureReason(const QString &pluginName) const +{ + return m_d->m_failedPlugins.value(pluginName); +} + +QStringList QDesignerPluginManager::registeredPlugins() const +{ + return m_d->m_registeredPlugins; +} + +QStringList QDesignerPluginManager::pluginPaths() const +{ + return m_d->m_pluginPaths; +} + +QObject *QDesignerPluginManager::instance(const QString &plugin) const +{ + if (m_d->m_disabledPlugins.contains(plugin)) + return 0; + + QPluginLoader loader(plugin); + return loader.instance(); +} + +void QDesignerPluginManager::updateRegisteredPlugins() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO; + m_d->m_registeredPlugins.clear(); + foreach (const QString &path, m_d->m_pluginPaths) + registerPath(path); +} + +bool QDesignerPluginManager::registerNewPlugins() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO; + + const int before = m_d->m_registeredPlugins.size(); + foreach (const QString &path, m_d->m_pluginPaths) + registerPath(path); + const bool newPluginsFound = m_d->m_registeredPlugins.size() > before; + // We force a re-initialize as Jambi collection might return + // different widget lists when switching projects. + m_d->m_initialized = false; + ensureInitialized(); + + return newPluginsFound; +} + +void QDesignerPluginManager::registerPath(const QString &path) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << path; + QStringList candidates = findPlugins(path); + + foreach (const QString &plugin, candidates) + registerPlugin(plugin); +} + +void QDesignerPluginManager::registerPlugin(const QString &plugin) +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << plugin; + if (m_d->m_disabledPlugins.contains(plugin)) + return; + if (m_d->m_registeredPlugins.contains(plugin)) + return; + + QPluginLoader loader(plugin); + if (loader.isLoaded() || loader.load()) { + m_d->m_registeredPlugins += plugin; + QDesignerPluginManagerPrivate::FailedPluginMap::iterator fit = m_d->m_failedPlugins.find(plugin); + if (fit != m_d->m_failedPlugins.end()) + m_d->m_failedPlugins.erase(fit); + return; + } + + const QString errorMessage = loader.errorString(); + m_d->m_failedPlugins.insert(plugin, errorMessage); +} + + + +bool QDesignerPluginManager::syncSettings() +{ + QSettings settings(qApp->organizationName(), QDesignerQSettings::settingsApplicationName()); + settings.beginGroup(QLatin1String("PluginManager")); + settings.setValue(QLatin1String("DisabledPlugins"), m_d->m_disabledPlugins); + settings.endGroup(); + return settings.status() == QSettings::NoError; +} + +void QDesignerPluginManager::ensureInitialized() +{ + if (debugPluginManager) + qDebug() << Q_FUNC_INFO << m_d->m_initialized << m_d->m_customWidgets.size(); + + if (m_d->m_initialized) + return; + + const QString designerLanguage = getDesignerLanguage(m_d->m_core); + + m_d->clearCustomWidgets(); + // Add the static custom widgets + const QObjectList staticPluginObjects = QPluginLoader::staticInstances(); + if (!staticPluginObjects.empty()) { + const QString staticPluginPath = QCoreApplication::applicationFilePath(); + foreach(QObject *o, staticPluginObjects) + m_d->addCustomWidgets(o, staticPluginPath, designerLanguage); + } + foreach (const QString &plugin, m_d->m_registeredPlugins) + if (QObject *o = instance(plugin)) + m_d->addCustomWidgets(o, plugin, designerLanguage); + + m_d->m_initialized = true; +} + +QDesignerPluginManager::CustomWidgetList QDesignerPluginManager::registeredCustomWidgets() const +{ + const_cast<QDesignerPluginManager*>(this)->ensureInitialized(); + return m_d->m_customWidgets; +} + +QDesignerCustomWidgetData QDesignerPluginManager::customWidgetData(QDesignerCustomWidgetInterface *w) const +{ + const int index = m_d->m_customWidgets.indexOf(w); + if (index == -1) + return QDesignerCustomWidgetData(); + return m_d->m_customWidgetData.at(index); +} + +QObjectList QDesignerPluginManager::instances() const +{ + QStringList plugins = registeredPlugins(); + + QObjectList lst; + foreach (const QString &plugin, plugins) { + if (QObject *o = instance(plugin)) + lst.append(o); + } + + return lst; +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/pluginmanager_p.h b/tools/designer/src/lib/shared/pluginmanager_p.h new file mode 100644 index 0000000..e374f42 --- /dev/null +++ b/tools/designer/src/lib/shared/pluginmanager_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PLUGINMANAGER_H +#define PLUGINMANAGER_H + +#include "shared_global_p.h" + +#include <QtCore/QSharedDataPointer> +#include <QtCore/QMap> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerCustomWidgetInterface; +class QDesignerPluginManagerPrivate; + +class QDesignerCustomWidgetSharedData; + +/* Information contained in the Dom XML of a custom widget. */ +class QDESIGNER_SHARED_EXPORT QDesignerCustomWidgetData { +public: + explicit QDesignerCustomWidgetData(const QString &pluginPath = QString()); + + enum ParseResult { ParseOk, ParseWarning, ParseError }; + ParseResult parseXml(const QString &xml, const QString &name, QString *errorMessage); + + QDesignerCustomWidgetData(const QDesignerCustomWidgetData&); + QDesignerCustomWidgetData& operator=(const QDesignerCustomWidgetData&); + ~QDesignerCustomWidgetData(); + + bool isNull() const; + + QString pluginPath() const; + + // Data as parsed from the widget's domXML(). + QString xmlClassName() const; + // Optional. The language the plugin is supposed to be used with. + QString xmlLanguage() const; + // Optional. method used to add pages to a container with a container extension + QString xmlAddPageMethod() const; + // Optional. Base class + QString xmlExtends() const; + // Optional. The name to be used in the widget box. + QString xmlDisplayName() const; + +private: + QSharedDataPointer<QDesignerCustomWidgetSharedData> m_d; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerPluginManager: public QObject +{ + Q_OBJECT +public: + typedef QList<QDesignerCustomWidgetInterface*> CustomWidgetList; + + explicit QDesignerPluginManager(QDesignerFormEditorInterface *core); + virtual ~QDesignerPluginManager(); + + QDesignerFormEditorInterface *core() const; + + QObject *instance(const QString &plugin) const; + + QStringList registeredPlugins() const; + + QStringList findPlugins(const QString &path); + + QStringList pluginPaths() const; + void setPluginPaths(const QStringList &plugin_paths); + + QStringList disabledPlugins() const; + void setDisabledPlugins(const QStringList &disabled_plugins); + + QStringList failedPlugins() const; + QString failureReason(const QString &pluginName) const; + + QObjectList instances() const; + + CustomWidgetList registeredCustomWidgets() const; + QDesignerCustomWidgetData customWidgetData(QDesignerCustomWidgetInterface *w) const; + + bool registerNewPlugins(); + +public slots: + bool syncSettings(); + void ensureInitialized(); + +private: + void updateRegisteredPlugins(); + void registerPath(const QString &path); + void registerPlugin(const QString &plugin); + +private: + static QStringList defaultPluginPaths(); + + QDesignerPluginManagerPrivate *m_d; +}; + +QT_END_NAMESPACE + +#endif // PLUGINMANAGER_H diff --git a/tools/designer/src/lib/shared/previewconfigurationwidget.cpp b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp new file mode 100644 index 0000000..2cf362f --- /dev/null +++ b/tools/designer/src/lib/shared/previewconfigurationwidget.cpp @@ -0,0 +1,382 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* It is possible to link the skins as resources into Designer by specifying: + * QVFB_ROOT=$$QT_SOURCE_TREE/tools/qvfb + * RESOURCES += $$QVFB_ROOT/ClamshellPhone.qrc $$QVFB_ROOT/PDAPhone.qrc ... + * in lib/shared/shared.pri. However, this exceeds a limit of Visual Studio 6. */ + +#include "previewconfigurationwidget_p.h" +#include "ui_previewconfigurationwidget.h" +#include "previewmanager_p.h" +#include "abstractsettings_p.h" +#include "shared_settings_p.h" + +#include <iconloader_p.h> +#include <stylesheeteditor_p.h> + +#include <deviceskin.h> + +#include <QtGui/QFileDialog> +#include <QtGui/QStyleFactory> +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> +#include <QtCore/QPair> +#include <QtCore/QList> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QSharedData> + +// #define DEFAULT_SKINS_FROM_RESOURCE +#ifdef DEFAULT_SKINS_FROM_RESOURCE +QT_BEGIN_NAMESPACE +static const char *skinResourcePathC = ":/skins/"; +QT_END_NAMESPACE +#else +# include <QtCore/QLibraryInfo> +#endif + +QT_BEGIN_NAMESPACE + +static const char *skinExtensionC = "skin"; + +namespace { + // Pair of skin name, path + typedef QPair<QString, QString> SkinNamePath; + typedef QList<SkinNamePath> Skins; + enum { SkinComboNoneIndex = 0 }; +} + +// find default skins (resources) +static const Skins &defaultSkins() { + static Skins rc; + if (rc.empty()) { +#ifdef DEFAULT_SKINS_FROM_RESOURCE + const QString skinPath = QLatin1String(skinResourcePathC); +#else + QString skinPath = QLibraryInfo::location(QLibraryInfo::PrefixPath); + skinPath += QDir::separator(); + skinPath += QLatin1String("tools"); + skinPath += QDir::separator(); + skinPath += QLatin1String("qvfb"); +#endif + QString pattern = QLatin1String("*."); + pattern += QLatin1String(skinExtensionC); + const QDir dir(skinPath, pattern); + const QFileInfoList list = dir.entryInfoList(); + if (list.empty()) + return rc; + const QFileInfoList::const_iterator lcend = list.constEnd(); + for (QFileInfoList::const_iterator it = list.constBegin(); it != lcend; ++it) + rc.push_back(SkinNamePath(it->baseName(), it->filePath())); + } + return rc; +} + +namespace qdesigner_internal { + +// ------------- PreviewConfigurationWidgetPrivate +class PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate { +public: + PreviewConfigurationWidgetPrivate(QDesignerFormEditorInterface *core, QGroupBox *g); + + void slotEditAppStyleSheet(); + void slotDeleteSkinEntry(); + void slotSkinChanged(int index); + + void retrieveSettings(); + void storeSettings() const; + + QAbstractButton *appStyleSheetChangeButton() const { return m_ui.m_appStyleSheetChangeButton; } + QAbstractButton *skinRemoveButton() const { return m_ui.m_skinRemoveButton; } + QComboBox *skinCombo() const { return m_ui.m_skinCombo; } + + QDesignerFormEditorInterface *m_core; + +private: + PreviewConfiguration previewConfiguration() const; + void setPreviewConfiguration(const PreviewConfiguration &pc); + + QStringList userSkins() const; + void addUserSkins(const QStringList &files); + bool canRemoveSkin(int index) const; + int browseSkin(); + + const QString m_defaultStyle; + QGroupBox *m_parent; + Ui::PreviewConfigurationWidget m_ui; + + int m_firstUserSkinIndex; + int m_browseSkinIndex; + int m_lastSkinIndex; // required in case browse fails +}; + +PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::PreviewConfigurationWidgetPrivate( + QDesignerFormEditorInterface *core, QGroupBox *g) : + m_core(core), + m_defaultStyle(PreviewConfigurationWidget::tr("Default")), + m_parent(g), + m_firstUserSkinIndex(0), + m_browseSkinIndex(0), + m_lastSkinIndex(0) +{ + m_ui.setupUi(g); + // styles + m_ui.m_styleCombo->setEditable(false); + QStringList styleItems(m_defaultStyle); + styleItems += QStyleFactory::keys(); + m_ui.m_styleCombo->addItems(styleItems); + + // sheet + m_ui.m_appStyleSheetLineEdit->setTextPropertyValidationMode(qdesigner_internal::ValidationStyleSheet); + m_ui.m_appStyleSheetClearButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("resetproperty.png"))); + QObject::connect(m_ui.m_appStyleSheetClearButton, SIGNAL(clicked()), m_ui.m_appStyleSheetLineEdit, SLOT(clear())); + + m_ui.m_skinRemoveButton->setIcon(qdesigner_internal::createIconSet(QString::fromUtf8("editdelete.png"))); + // skins: find default skins (resources) + m_ui.m_skinRemoveButton->setEnabled(false); + Skins skins = defaultSkins(); + skins.push_front(SkinNamePath(PreviewConfigurationWidget::tr("None"), QString())); + + const Skins::const_iterator scend = skins.constEnd(); + for (Skins::const_iterator it = skins.constBegin(); it != scend; ++it) + m_ui.m_skinCombo->addItem (it->first, QVariant(it->second)); + m_browseSkinIndex = m_firstUserSkinIndex = skins.size(); + m_ui.m_skinCombo->addItem(PreviewConfigurationWidget::tr("Browse..."), QString()); + + m_ui.m_skinCombo->setMaxVisibleItems (qMax(15, 2 * m_browseSkinIndex)); + m_ui.m_skinCombo->setEditable(false); + + retrieveSettings(); +} + +bool PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::canRemoveSkin(int index) const +{ + return index >= m_firstUserSkinIndex && index != m_browseSkinIndex; +} + +QStringList PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::userSkins() const +{ + QStringList rc; + for (int i = m_firstUserSkinIndex; i < m_browseSkinIndex; i++) + rc.push_back(m_ui.m_skinCombo->itemData(i).toString()); + return rc; +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::addUserSkins(const QStringList &files) +{ + if (files.empty()) + return; + const QStringList ::const_iterator fcend = files.constEnd(); + for (QStringList::const_iterator it = files.constBegin(); it != fcend; ++it) { + const QFileInfo fi(*it); + if (fi.isDir() && fi.isReadable()) { + m_ui.m_skinCombo->insertItem(m_browseSkinIndex++, fi.baseName(), QVariant(*it)); + } else { + qWarning() << "Unable to access the skin directory '" << *it << "'."; + } + } +} + +PreviewConfiguration PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::previewConfiguration() const +{ + PreviewConfiguration rc; + QString style = m_ui.m_styleCombo->currentText(); + if (style == m_defaultStyle) + style.clear(); + const QString applicationStyleSheet = m_ui.m_appStyleSheetLineEdit->text(); + // Figure out skin. 0 is None by definition.. + const int skinIndex = m_ui.m_skinCombo->currentIndex(); + QString deviceSkin; + if (skinIndex != SkinComboNoneIndex && skinIndex != m_browseSkinIndex) + deviceSkin = m_ui.m_skinCombo->itemData(skinIndex).toString(); + + return PreviewConfiguration(style, applicationStyleSheet, deviceSkin); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::setPreviewConfiguration(const PreviewConfiguration &pc) +{ + int styleIndex = m_ui.m_styleCombo->findText(pc.style()); + if (styleIndex == -1) + styleIndex = m_ui.m_styleCombo->findText(m_defaultStyle); + m_ui.m_styleCombo->setCurrentIndex(styleIndex); + m_ui.m_appStyleSheetLineEdit->setText(pc.applicationStyleSheet()); + // find skin by file name. 0 is "none" + const QString deviceSkin = pc.deviceSkin(); + int skinIndex = deviceSkin.isEmpty() ? 0 : m_ui.m_skinCombo->findData(QVariant(deviceSkin)); + if (skinIndex == -1) { + qWarning() << "Unable to find skin '" << deviceSkin << "'."; + skinIndex = 0; + } + m_ui.m_skinCombo->setCurrentIndex(skinIndex); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotEditAppStyleSheet() +{ + StyleSheetEditorDialog dlg(m_core, m_parent, StyleSheetEditorDialog::ModeGlobal); + dlg.setText(m_ui.m_appStyleSheetLineEdit->text()); + if (dlg.exec() == QDialog::Accepted) + m_ui.m_appStyleSheetLineEdit->setText(dlg.text()); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotDeleteSkinEntry() +{ + const int index = m_ui.m_skinCombo->currentIndex(); + if (canRemoveSkin(index)) { + m_ui.m_skinCombo->setCurrentIndex(SkinComboNoneIndex); + m_ui.m_skinCombo->removeItem(index); + m_browseSkinIndex--; + } +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::slotSkinChanged(int index) +{ + if (index == m_browseSkinIndex) { + m_ui.m_skinCombo->setCurrentIndex(browseSkin()); + } else { + m_lastSkinIndex = index; + m_ui.m_skinRemoveButton->setEnabled(canRemoveSkin(index)); + m_ui.m_skinCombo->setToolTip(index != SkinComboNoneIndex ? m_ui.m_skinCombo->itemData(index).toString() : QString()); + } +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::retrieveSettings() +{ + QDesignerSharedSettings settings(m_core); + m_parent->setChecked(settings.isCustomPreviewConfigurationEnabled()); + setPreviewConfiguration(settings.customPreviewConfiguration()); + addUserSkins(settings.userDeviceSkins()); +} + +void PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::storeSettings() const +{ + QDesignerSharedSettings settings(m_core); + settings.setCustomPreviewConfigurationEnabled(m_parent->isChecked()); + settings.setCustomPreviewConfiguration(previewConfiguration()); + settings.setUserDeviceSkins(userSkins()); +} + +int PreviewConfigurationWidget::PreviewConfigurationWidgetPrivate::browseSkin() +{ + QFileDialog dlg(m_parent); + dlg.setFileMode(QFileDialog::DirectoryOnly); + const QString title = tr("Load Custom Device Skin"); + dlg.setWindowTitle(title); + dlg.setFilter(tr("All QVFB Skins (*.%1)").arg(QLatin1String(skinExtensionC))); + + int rc = m_lastSkinIndex; + do { + if (!dlg.exec()) + break; + + const QStringList directories = dlg.selectedFiles(); + if (directories.size() != 1) + break; + + // check: 1) name already there + const QString directory = directories.front(); + const QString name = QFileInfo(directory).baseName(); + const int existingIndex = m_ui.m_skinCombo->findText(name); + if (existingIndex != -1 && existingIndex != SkinComboNoneIndex && existingIndex != m_browseSkinIndex) { + const QString msgTitle = tr("%1 - Duplicate Skin").arg(title); + const QString msg = tr("The skin '%1' already exists.").arg(name); + QMessageBox::information(m_parent, msgTitle, msg); + break; + } + // check: 2) can read + DeviceSkinParameters parameters; + QString readError; + if (parameters.read(directory, DeviceSkinParameters::ReadSizeOnly, &readError)) { + const QString name = QFileInfo(directory).baseName(); + m_ui.m_skinCombo->insertItem(m_browseSkinIndex, name, QVariant(directory)); + rc = m_browseSkinIndex++; + + break; + } else { + const QString msgTitle = tr("%1 - Error").arg(title); + const QString msg = tr("%1 is not a valid skin directory:\n%2").arg(directory).arg(readError); + QMessageBox::warning (m_parent, msgTitle, msg); + } + } while (true); + return rc; +} + +// ------------- PreviewConfigurationWidget +PreviewConfigurationWidget::PreviewConfigurationWidget(QDesignerFormEditorInterface *core, + QWidget *parent) : + QGroupBox(parent), + m_impl(new PreviewConfigurationWidgetPrivate(core, this)) +{ + connect(m_impl->appStyleSheetChangeButton(), SIGNAL(clicked()), this, SLOT(slotEditAppStyleSheet())); + connect(m_impl->skinRemoveButton(), SIGNAL(clicked()), this, SLOT(slotDeleteSkinEntry())); + connect(m_impl->skinCombo(), SIGNAL(currentIndexChanged(int)), this, SLOT(slotSkinChanged(int))); + + m_impl->retrieveSettings(); +} + +PreviewConfigurationWidget::~PreviewConfigurationWidget() +{ + delete m_impl; +} + +void PreviewConfigurationWidget::saveState() +{ + m_impl->storeSettings(); +} + +void PreviewConfigurationWidget::slotEditAppStyleSheet() +{ + m_impl->slotEditAppStyleSheet(); +} + +void PreviewConfigurationWidget::slotDeleteSkinEntry() +{ + m_impl->slotDeleteSkinEntry(); +} + +void PreviewConfigurationWidget::slotSkinChanged(int index) +{ + m_impl->slotSkinChanged(index); +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/previewconfigurationwidget.ui b/tools/designer/src/lib/shared/previewconfigurationwidget.ui new file mode 100644 index 0000000..2f18766 --- /dev/null +++ b/tools/designer/src/lib/shared/previewconfigurationwidget.ui @@ -0,0 +1,91 @@ +<ui version="4.0" > + <class>PreviewConfigurationWidget</class> + <widget class="QGroupBox" name="PreviewConfigurationWidget" > + <property name="windowTitle" > + <string>Form</string> + </property> + <property name="title" > + <string>Print/Preview Configuration</string> + </property> + <property name="checkable" > + <bool>true</bool> + </property> + <layout class="QFormLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="m_styleLabel" > + <property name="text" > + <string>Style</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QComboBox" name="m_styleCombo" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="m_appStyleSheetLabel" > + <property name="text" > + <string>Style sheet</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <layout class="QHBoxLayout" > + <item> + <widget class="qdesigner_internal::TextPropertyEditor" name="m_appStyleSheetLineEdit" > + <property name="minimumSize" > + <size> + <width>149</width> + <height>0</height> + </size> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="m_appStyleSheetChangeButton" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="m_appStyleSheetClearButton" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="m_skinLabel" > + <property name="text" > + <string>Device skin</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <layout class="QHBoxLayout" > + <item> + <widget class="QComboBox" name="m_skinCombo" /> + </item> + <item> + <widget class="QToolButton" name="m_skinRemoveButton" > + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>qdesigner_internal::TextPropertyEditor</class> + <extends>QLineEdit</extends> + <header location="global" >textpropertyeditor_p.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/previewconfigurationwidget_p.h b/tools/designer/src/lib/shared/previewconfigurationwidget_p.h new file mode 100644 index 0000000..897ec4e --- /dev/null +++ b/tools/designer/src/lib/shared/previewconfigurationwidget_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PREVIEWCONFIGURATIONWIDGET_H +#define PREVIEWCONFIGURATIONWIDGET_H + +#include "shared_global_p.h" + +#include <QtGui/QGroupBox> +#include <QtCore/QSharedDataPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettingsInterface; + +namespace qdesigner_internal { + +// ----------- PreviewConfigurationWidget: Widget to edit the preview configuration. + +class QDESIGNER_SHARED_EXPORT PreviewConfigurationWidget : public QGroupBox +{ + Q_OBJECT +public: + explicit PreviewConfigurationWidget(QDesignerFormEditorInterface *core, + QWidget *parent = 0); + virtual ~PreviewConfigurationWidget(); + void saveState(); + +private slots: + void slotEditAppStyleSheet(); + void slotDeleteSkinEntry(); + void slotSkinChanged(int); + +private: + class PreviewConfigurationWidgetPrivate; + PreviewConfigurationWidgetPrivate *m_impl; + + PreviewConfigurationWidget(const PreviewConfigurationWidget &other); + PreviewConfigurationWidget &operator =(const PreviewConfigurationWidget &other); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PREVIEWCONFIGURATIONWIDGET_H diff --git a/tools/designer/src/lib/shared/previewmanager.cpp b/tools/designer/src/lib/shared/previewmanager.cpp new file mode 100644 index 0000000..8ed1772 --- /dev/null +++ b/tools/designer/src/lib/shared/previewmanager.cpp @@ -0,0 +1,815 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "previewmanager_p.h" +#include "qdesigner_formbuilder_p.h" +#include "shared_settings_p.h" +#include "shared_settings_p.h" +#include "zoomwidget_p.h" +#include "formwindowbase_p.h" +#include "widgetfactory_p.h" + +#include <deviceskin.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> + +#include <QtGui/QWidget> +#include <QtGui/qevent.h> +#include <QtGui/QDesktopWidget> +#include <QtGui/QMainWindow> +#include <QtGui/QDockWidget> +#include <QtGui/QApplication> +#include <QtGui/QPixmap> +#include <QtGui/QVBoxLayout> +#include <QtGui/QDialog> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QCursor> + +#include <QtCore/QMap> +#include <QtCore/QDebug> +#include <QtCore/QSharedData> + +QT_BEGIN_NAMESPACE + +static inline int compare(const qdesigner_internal::PreviewConfiguration &pc1, const qdesigner_internal::PreviewConfiguration &pc2) +{ + int rc = pc1.style().compare(pc2.style()); + if (rc) + return rc; + rc = pc1.applicationStyleSheet().compare(pc2.applicationStyleSheet()); + if (rc) + return rc; + return pc1.deviceSkin().compare(pc2.deviceSkin()); +} + +namespace { + // ------ PreviewData (data associated with a preview window) + struct PreviewData { + PreviewData(const QPointer<QWidget> &widget, const QDesignerFormWindowInterface *formWindow, const qdesigner_internal::PreviewConfiguration &pc); + QPointer<QWidget> m_widget; + const QDesignerFormWindowInterface *m_formWindow; + qdesigner_internal::PreviewConfiguration m_configuration; + }; + + PreviewData::PreviewData(const QPointer<QWidget>& widget, + const QDesignerFormWindowInterface *formWindow, + const qdesigner_internal::PreviewConfiguration &pc) : + m_widget(widget), + m_formWindow(formWindow), + m_configuration(pc) + { + } +} + +namespace qdesigner_internal { + +/* In designer, we have the situation that laid-out maincontainers have + * a geometry set (which might differ from their sizeHint()). The QGraphicsItem + * should return that in its size hint, else such cases won't work */ + +class DesignerZoomProxyWidget : public ZoomProxyWidget { + Q_DISABLE_COPY(DesignerZoomProxyWidget) +public: + DesignerZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); +protected: + virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const; +}; + +DesignerZoomProxyWidget::DesignerZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) : + ZoomProxyWidget(parent, wFlags) +{ +} + +QSizeF DesignerZoomProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const +{ + if (const QWidget *w = widget()) + return QSizeF(w->size()); + return ZoomProxyWidget::sizeHint(which, constraint); +} + +// DesignerZoomWidget which returns DesignerZoomProxyWidget in its factory function +class DesignerZoomWidget : public ZoomWidget { + Q_DISABLE_COPY(DesignerZoomWidget) +public: + DesignerZoomWidget(QWidget *parent = 0); +private: + virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const; +}; + +DesignerZoomWidget::DesignerZoomWidget(QWidget *parent) : + ZoomWidget(parent) +{ +} + +QGraphicsProxyWidget *DesignerZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const +{ + return new DesignerZoomProxyWidget(parent, wFlags); +} + +// --------- Widget Preview skin: Forward the key events to the window +class PreviewDeviceSkin : public DeviceSkin +{ + Q_OBJECT +public: + explicit PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); + virtual void setPreview(QWidget *w); + QSize screenSize() const { return m_screenSize; } + +private slots: + void slotSkinKeyPressEvent(int code, const QString& text, bool autorep); + void slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep); + void slotPopupMenu(); + +protected: + virtual void populateContextMenu(QMenu *m); + +private: + const QSize m_screenSize; +}; + +PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : + DeviceSkin(parameters, parent), + m_screenSize(parameters.screenSize()) +{ + connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)), + this, SLOT(slotSkinKeyPressEvent(int,QString,bool))); + connect(this, SIGNAL(skinKeyReleaseEvent(int,QString,bool)), + this, SLOT(slotSkinKeyReleaseEvent(int,QString,bool))); + connect(this, SIGNAL(popupMenu()), this, SLOT(slotPopupMenu())); +} + +void PreviewDeviceSkin::setPreview(QWidget *formWidget) +{ + formWidget->setFixedSize(m_screenSize); + formWidget->setParent(this, Qt::SubWindow); + formWidget->setAutoFillBackground(true); + setView(formWidget); +} + +void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyPress,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } + +} + +void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep) +{ + if (QWidget *focusWidget = QApplication::focusWidget()) { + QKeyEvent e(QEvent::KeyRelease,code,0,text,autorep); + QApplication::sendEvent(focusWidget, &e); + } +} + +void PreviewDeviceSkin::slotPopupMenu() +{ + QMenu menu(this); + populateContextMenu(&menu); + menu.exec(QCursor::pos()); +} + +void PreviewDeviceSkin::populateContextMenu(QMenu *menu) +{ + connect(menu->addAction(tr("&Close")), SIGNAL(triggered()), parentWidget(), SLOT(close())); +} + +// ------------ PreviewConfigurationPrivate +class PreviewConfigurationData : public QSharedData { +public: + PreviewConfigurationData() {} + explicit PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin); + + QString m_style; + // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). + QString m_applicationStyleSheet; + QString m_deviceSkin; +}; + +PreviewConfigurationData::PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin) : + m_style(style), + m_applicationStyleSheet(applicationStyleSheet), + m_deviceSkin(deviceSkin) +{ +} + +/* ZoomablePreviewDeviceSkin: A Zoomable Widget Preview skin. Embeds preview + * into a ZoomWidget and this in turn into the DeviceSkin view and keeps + * Device skin zoom + ZoomWidget zoom in sync. */ + +class ZoomablePreviewDeviceSkin : public PreviewDeviceSkin +{ + Q_OBJECT +public: + explicit ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent); + virtual void setPreview(QWidget *w); + + int zoomPercent() const; // Device Skins have a double 'zoom' property + +public slots: + void setZoomPercent(int); + +signals: + void zoomPercentChanged(int); + +protected: + virtual void populateContextMenu(QMenu *m); + +private: + ZoomMenu *m_zoomMenu; + ZoomWidget *m_zoomWidget; +}; + +ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters ¶meters, QWidget *parent) : + PreviewDeviceSkin(parameters, parent), + m_zoomMenu(new ZoomMenu(this)), + m_zoomWidget(new DesignerZoomWidget) +{ + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int))); + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SIGNAL(zoomPercentChanged(int))); + m_zoomWidget->setZoomContextMenuEnabled(false); + m_zoomWidget->setWidgetZoomContextMenuEnabled(false); + m_zoomWidget->resize(screenSize()); + m_zoomWidget->setParent(this, Qt::SubWindow); + m_zoomWidget->setAutoFillBackground(true); + setView(m_zoomWidget); +} + +void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget) +{ + formWidget->setFixedSize(screenSize()); + m_zoomWidget->setWidget(formWidget); +} + +int ZoomablePreviewDeviceSkin::zoomPercent() const +{ + return m_zoomWidget->zoom(); +} + +void ZoomablePreviewDeviceSkin::setZoomPercent(int z) +{ + if (z == zoomPercent()) + return; + + // If not triggered by the menu itself: Update it + if (m_zoomMenu->zoom() != z) + m_zoomMenu->setZoom(z); + + const QCursor oldCursor = cursor(); + QApplication::setOverrideCursor(Qt::WaitCursor); + // DeviceSkin has double, not qreal. + const double hundred = 100.0; + setZoom(static_cast<double>(z) / hundred); + m_zoomWidget->setZoom(z); + QApplication::restoreOverrideCursor(); +} + +void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu) +{ + m_zoomMenu->addActions(menu); + menu->addSeparator(); + PreviewDeviceSkin::populateContextMenu(menu); + menu->addSeparator(); +} + +// ------------- PreviewConfiguration + +static const char *styleKey = "Style"; +static const char *appStyleSheetKey = "AppStyleSheet"; +static const char *skinKey = "Skin"; + +PreviewConfiguration::PreviewConfiguration() : + m_d(new PreviewConfigurationData) +{ +} + +PreviewConfiguration::PreviewConfiguration(const QString &sty, const QString &applicationSheet, const QString &skin) : + m_d(new PreviewConfigurationData(sty, applicationSheet, skin)) +{ +} + +PreviewConfiguration::PreviewConfiguration(const PreviewConfiguration &o) : + m_d(o.m_d) +{ +} + +PreviewConfiguration &PreviewConfiguration::operator=(const PreviewConfiguration &o) +{ + m_d.operator=(o.m_d); + return *this; +} + +PreviewConfiguration::~PreviewConfiguration() +{ +} + +void PreviewConfiguration::clear() +{ + PreviewConfigurationData &d = *m_d; + d.m_style.clear(); + d.m_applicationStyleSheet.clear(); + d.m_deviceSkin.clear(); +} + +QString PreviewConfiguration::style() const +{ + return m_d->m_style; +} + +void PreviewConfiguration::setStyle(const QString &s) +{ + m_d->m_style = s; +} + +// Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). +QString PreviewConfiguration::applicationStyleSheet() const +{ + return m_d->m_applicationStyleSheet; +} + +void PreviewConfiguration::setApplicationStyleSheet(const QString &as) +{ + m_d->m_applicationStyleSheet = as; +} + +QString PreviewConfiguration::deviceSkin() const +{ + return m_d->m_deviceSkin; +} + +void PreviewConfiguration::setDeviceSkin(const QString &s) +{ + m_d->m_deviceSkin = s; +} + +void PreviewConfiguration::toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const +{ + const PreviewConfigurationData &d = *m_d; + settings->beginGroup(prefix); + settings->setValue(QLatin1String(styleKey), d.m_style); + settings->setValue(QLatin1String(appStyleSheetKey), d.m_applicationStyleSheet); + settings->setValue(QLatin1String(skinKey), d.m_deviceSkin); + settings->endGroup(); +} + +void PreviewConfiguration::fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings) +{ + clear(); + QString key = prefix; + key += QLatin1Char('/'); + const int prefixSize = key.size(); + + PreviewConfigurationData &d = *m_d; + + const QVariant emptyString = QVariant(QString()); + + key += QLatin1String(styleKey); + d.m_style = settings->value(key, emptyString).toString(); + + key.replace(prefixSize, key.size() - prefixSize, QLatin1String(appStyleSheetKey)); + d.m_applicationStyleSheet = settings->value(key, emptyString).toString(); + + key.replace(prefixSize, key.size() - prefixSize, QLatin1String(skinKey)); + d.m_deviceSkin = settings->value(key, emptyString).toString(); +} + + +QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) < 0; +} + +QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) == 0; +} + +QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2) +{ + return compare(pc1, pc2) != 0; +} + +// ------------- PreviewManagerPrivate +class PreviewManagerPrivate { +public: + PreviewManagerPrivate(PreviewManager::PreviewMode mode); + + const PreviewManager::PreviewMode m_mode; + + QPointer<QWidget> m_activePreview; + + typedef QList<PreviewData> PreviewDataList; + + PreviewDataList m_previews; + + typedef QMap<QString, DeviceSkinParameters> DeviceSkinConfigCache; + DeviceSkinConfigCache m_deviceSkinConfigCache; + + QDesignerFormEditorInterface *m_core; + bool m_updateBlocked; +}; + +PreviewManagerPrivate::PreviewManagerPrivate(PreviewManager::PreviewMode mode) : + m_mode(mode), + m_core(0), + m_updateBlocked(false) +{ +} + +// ------------- PreviewManager + +PreviewManager::PreviewManager(PreviewMode mode, QObject *parent) : + QObject(parent), + d(new PreviewManagerPrivate(mode)) +{ +} + +PreviewManager:: ~PreviewManager() +{ + delete d; +} + + +Qt::WindowFlags PreviewManager::previewWindowFlags(const QWidget *widget) const +{ +#ifdef Q_WS_WIN + Qt::WindowFlags windowFlags = (widget->windowType() == Qt::Window) ? Qt::Window | Qt::WindowMaximizeButtonHint : Qt::WindowFlags(Qt::Dialog); +#else + Q_UNUSED(widget) + // Only Dialogs have close buttons on Mac. + // On Linux, we don't want an additional task bar item and we don't want a minimize button; + // we want the preview to be on top. + Qt::WindowFlags windowFlags = Qt::Dialog; +#endif + return windowFlags; +} + +QWidget *PreviewManager::createDeviceSkinContainer(const QDesignerFormWindowInterface *fw) const +{ + return new QDialog(fw->window()); +} + +// Some widgets might require fake containers + +static QWidget *fakeContainer(QWidget *w) +{ + // Prevent a dock widget from trying to dock to Designer's main window + // (which can be found in the parent hierarchy in MDI mode) by + // providing a fake mainwindow + if (QDockWidget *dock = qobject_cast<QDockWidget *>(w)) { + // Reparent: Clear modality, propagate title and resize outer container + const QSize size = w->size(); + w->setWindowModality(Qt::NonModal); + dock->setFeatures(dock->features() & ~(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetClosable)); + dock->setAllowedAreas(Qt::LeftDockWidgetArea); + QMainWindow *mw = new QMainWindow; + int leftMargin, topMargin, rightMargin, bottomMargin; + mw->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); + mw->addDockWidget(Qt::LeftDockWidgetArea, dock); + mw->resize(size + QSize(leftMargin + rightMargin, topMargin + bottomMargin)); + return mw; + } + return w; +} + +static PreviewConfiguration configurationFromSettings(QDesignerFormEditorInterface *core, const QString &style) +{ + qdesigner_internal::PreviewConfiguration pc; + const QDesignerSharedSettings settings(core); + if (settings.isCustomPreviewConfigurationEnabled()) + pc = settings.customPreviewConfiguration(); + if (!style.isEmpty()) + pc.setStyle(style); + return pc; +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage) +{ + return showPreview(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage); +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage) +{ + return showPreview(fw, style, -1, errorMessage); +} + +QWidget *PreviewManager::createPreview(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage, + int initialZoom) +{ + if (!d->m_core) + d->m_core = fw->core(); + + const bool zoomable = initialZoom > 0; + // Figure out which profile to apply + DeviceProfile deviceProfile; + if (deviceProfileIndex >= 0) { + deviceProfile = QDesignerSharedSettings(fw->core()).deviceProfileAt(deviceProfileIndex); + } else { + if (const FormWindowBase *fwb = qobject_cast<const FormWindowBase *>(fw)) + deviceProfile = fwb->deviceProfile(); + } + // Create + QWidget *formWidget = QDesignerFormBuilder::createPreview(fw, pc.style(), pc.applicationStyleSheet(), deviceProfile, errorMessage); + if (!formWidget) + return 0; + + const QString title = tr("%1 - [Preview]").arg(formWidget->windowTitle()); + formWidget = fakeContainer(formWidget); + + // Clear any modality settings, child widget modalities must not be higher than parent's + formWidget->setWindowModality(Qt::NonModal); + // No skin + const QString deviceSkin = pc.deviceSkin(); + if (deviceSkin.isEmpty()) { + if (zoomable) { // Embed into ZoomWidget + ZoomWidget *zw = new DesignerZoomWidget; + connect(zw->zoomMenu(), SIGNAL(zoomChanged(int)), this, SLOT(slotZoomChanged(int))); + zw->setWindowTitle(title); + zw->setWidget(formWidget); + // Keep any widgets' context menus working, do not use global menu + zw->setWidgetZoomContextMenuEnabled(true); + zw->setParent(fw->window(), previewWindowFlags(formWidget)); + // Make preview close when Widget closes (Dialog/accept, etc) + formWidget->setAttribute(Qt::WA_DeleteOnClose, true); + connect(formWidget, SIGNAL(destroyed()), zw, SLOT(close())); + zw->setZoom(initialZoom); + zw->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return zw; + } + formWidget->setParent(fw->window(), previewWindowFlags(formWidget)); + formWidget->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return formWidget; + } + // Embed into skin. find config in cache + PreviewManagerPrivate::DeviceSkinConfigCache::iterator it = d->m_deviceSkinConfigCache.find(deviceSkin); + if (it == d->m_deviceSkinConfigCache.end()) { + DeviceSkinParameters parameters; + if (!parameters.read(deviceSkin, DeviceSkinParameters::ReadAll, errorMessage)) { + formWidget->deleteLater(); + return 0; + } + it = d->m_deviceSkinConfigCache.insert(deviceSkin, parameters); + } + + QWidget *skinContainer = createDeviceSkinContainer(fw); + PreviewDeviceSkin *skin = 0; + if (zoomable) { + ZoomablePreviewDeviceSkin *zds = new ZoomablePreviewDeviceSkin(it.value(), skinContainer); + zds->setZoomPercent(initialZoom); + connect(zds, SIGNAL(zoomPercentChanged(int)), this, SLOT(slotZoomChanged(int))); + skin = zds; + } else { + skin = new PreviewDeviceSkin(it.value(), skinContainer); + } + skin->setPreview(formWidget); + // Make preview close when Widget closes (Dialog/accept, etc) + formWidget->setAttribute(Qt::WA_DeleteOnClose, true); + connect(formWidget, SIGNAL(destroyed()), skinContainer, SLOT(close())); + skinContainer->setWindowTitle(title); + skinContainer->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true)); + return skinContainer; +} + +QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage) +{ + enum { Spacing = 10 }; + if (QWidget *existingPreviewWidget = raise(fw, pc)) + return existingPreviewWidget; + + const QDesignerSharedSettings settings(fw->core()); + const int initialZoom = settings.zoomEnabled() ? settings.zoom() : -1; + + QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage, initialZoom); + if (!widget) + return 0; + // Install filter for Escape key + widget->setAttribute(Qt::WA_DeleteOnClose, true); + widget->installEventFilter(this); + + switch (d->m_mode) { + case ApplicationModalPreview: + // Cannot do this on the Mac as the dialog would have no close button + widget->setWindowModality(Qt::ApplicationModal); + break; + case SingleFormNonModalPreview: + case MultipleFormNonModalPreview: + widget->setWindowModality(Qt::NonModal); + connect(fw, SIGNAL(changed()), widget, SLOT(close())); + connect(fw, SIGNAL(destroyed()), widget, SLOT(close())); + if (d->m_mode == SingleFormNonModalPreview) + connect(fw->core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), widget, SLOT(close())); + break; + } + // Semi-smart algorithm to position previews: + // If its the first one, position relative to form. + // 2nd, attempt to tile right (for comparing styles) or cascade + const QSize size = widget->size(); + const bool firstPreview = d->m_previews.empty(); + if (firstPreview) { + widget->move(fw->mapToGlobal(QPoint(Spacing, Spacing))); + } else { + if (QWidget *lastPreview = d->m_previews.back().m_widget) { + QDesktopWidget *desktop = qApp->desktop(); + const QRect lastPreviewGeometry = lastPreview->frameGeometry(); + const QRect availGeometry = desktop->availableGeometry(desktop->screenNumber(lastPreview)); + const QPoint newPos = lastPreviewGeometry.topRight() + QPoint(Spacing, 0); + if (newPos.x() + size.width() < availGeometry.right()) + widget->move(newPos); + else + widget->move(lastPreviewGeometry.topLeft() + QPoint(Spacing, Spacing)); + } + + } + d->m_previews.push_back(PreviewData(widget, fw, pc)); + widget->show(); + if (firstPreview) + emit firstPreviewOpened(); + return widget; +} + +QWidget *PreviewManager::raise(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc) +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (d->m_previews.empty()) + return false; + + // find matching window + const PreviewDataList::const_iterator cend = d->m_previews.constEnd(); + for (PreviewDataList::const_iterator it = d->m_previews.constBegin(); it != cend ;++it) { + QWidget * w = it->m_widget; + if (w && it->m_formWindow == fw && it->m_configuration == pc) { + w->raise(); + w->activateWindow(); + return w; + } + } + return 0; +} + +void PreviewManager::closeAllPreviews() +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (!d->m_previews.empty()) { + d->m_updateBlocked = true; + d->m_activePreview = 0; + const PreviewDataList::iterator cend = d->m_previews.end(); + for (PreviewDataList::iterator it = d->m_previews.begin(); it != cend ;++it) { + if (it->m_widget) + it->m_widget->close(); + } + d->m_previews.clear(); + d->m_updateBlocked = false; + emit lastPreviewClosed(); + } +} + +void PreviewManager::updatePreviewClosed(QWidget *w) +{ + typedef PreviewManagerPrivate::PreviewDataList PreviewDataList; + if (d->m_updateBlocked) + return; + // Purge out all 0 or widgets to be deleted + for (PreviewDataList::iterator it = d->m_previews.begin(); it != d->m_previews.end() ; ) { + QWidget *iw = it->m_widget; // Might be 0 when catching QEvent::Destroyed + if (iw == 0 || iw == w) { + it = d->m_previews.erase(it); + } else { + ++it; + } + } + if (d->m_previews.empty()) + emit lastPreviewClosed(); +} + +bool PreviewManager::eventFilter(QObject *watched, QEvent *event) +{ + // Courtesy of designer + do { + if (!watched->isWidgetType()) + break; + QWidget *previewWindow = qobject_cast<QWidget *>(watched); + if (!previewWindow || !previewWindow->isWindow()) + break; + + switch (event->type()) { + case QEvent::KeyPress: + case QEvent::ShortcutOverride: { + const QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event); + const int key = keyEvent->key(); + if ((key == Qt::Key_Escape +#ifdef Q_WS_MAC + || (keyEvent->modifiers() == Qt::ControlModifier && key == Qt::Key_Period) +#endif + )) { + previewWindow->close(); + return true; + } + } + break; + case QEvent::WindowActivate: + d->m_activePreview = previewWindow; + break; + case QEvent::Destroy: // We don't get QEvent::Close if someone accepts a QDialog. + updatePreviewClosed(previewWindow); + break; + case QEvent::Close: + updatePreviewClosed(previewWindow); + previewWindow->removeEventFilter (this); + break; + default: + break; + } + } while(false); + return QObject::eventFilter(watched, event); +} + +int PreviewManager::previewCount() const +{ + return d->m_previews.size(); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage) +{ + return createPreviewPixmap(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage) +{ + return createPreviewPixmap(fw, style, -1, errorMessage); +} + +QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, + const PreviewConfiguration &pc, + int deviceProfileIndex, + QString *errorMessage) +{ + QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage); + if (!widget) + return QPixmap(); + const QPixmap rc = QPixmap::grabWidget(widget); + widget->deleteLater(); + return rc; +} + +void PreviewManager::slotZoomChanged(int z) +{ + if (d->m_core) { // Save the last zoom chosen by the user. + QDesignerSharedSettings settings(d->m_core); + settings.setZoom(z); + } +} +} + +QT_END_NAMESPACE + +#include "previewmanager.moc" diff --git a/tools/designer/src/lib/shared/previewmanager_p.h b/tools/designer/src/lib/shared/previewmanager_p.h new file mode 100644 index 0000000..1060f3c --- /dev/null +++ b/tools/designer/src/lib/shared/previewmanager_p.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PREVIEWMANAGER_H +#define PREVIEWMANAGER_H + +#include "shared_global_p.h" + +#include <QtCore/QObject> +#include <QtCore/QString> +#include <QtCore/QSharedDataPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QWidget; +class QPixmap; +class QAction; +class QActionGroup; +class QMenu; +class QWidget; +class QDesignerSettingsInterface; + +namespace qdesigner_internal { + +// ----------- PreviewConfiguration + +class PreviewConfigurationData; + +class QDESIGNER_SHARED_EXPORT PreviewConfiguration { +public: + PreviewConfiguration(); + explicit PreviewConfiguration(const QString &style, + const QString &applicationStyleSheet = QString(), + const QString &deviceSkin = QString()); + + PreviewConfiguration(const PreviewConfiguration&); + PreviewConfiguration& operator=(const PreviewConfiguration&); + ~PreviewConfiguration(); + + QString style() const; + void setStyle(const QString &); + + // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()). + QString applicationStyleSheet() const; + void setApplicationStyleSheet(const QString &); + + QString deviceSkin() const; + void setDeviceSkin(const QString &); + + void clear(); + void toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const; + void fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings); + +private: + QSharedDataPointer<PreviewConfigurationData> m_d; +}; + +QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); +QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); +QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2); + +// ----------- Preview window manager. +// Maintains a list of preview widgets with their associated form windows and configuration. + +class PreviewManagerPrivate; + +class QDESIGNER_SHARED_EXPORT PreviewManager : public QObject +{ + Q_OBJECT +public: + + enum PreviewMode { + // Modal preview. Do not use on Macs as dialogs would have no close button + ApplicationModalPreview, + // Non modal previewing of one form in different configurations (closes if form window changes) + SingleFormNonModalPreview, + // Non modal previewing of several forms in different configurations + MultipleFormNonModalPreview }; + + explicit PreviewManager(PreviewMode mode, QObject *parent); + virtual ~PreviewManager(); + + // Show preview. Raise existing preview window if there is one with a matching + // configuration, else create a new preview. + QWidget *showPreview(const QDesignerFormWindowInterface *, const PreviewConfiguration &pc, int deviceProfileIndex /*=-1*/, QString *errorMessage); + // Convenience that creates a preview using a configuration taken from the settings. + QWidget *showPreview(const QDesignerFormWindowInterface *, const QString &style, int deviceProfileIndex /*=-1*/, QString *errorMessage); + QWidget *showPreview(const QDesignerFormWindowInterface *, const QString &style, QString *errorMessage); + + int previewCount() const; + + // Create a pixmap for printing. + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc, int deviceProfileIndex /*=-1*/, QString *errorMessage); + // Convenience that creates a pixmap using a configuration taken from the settings. + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex /*=-1*/, QString *errorMessage); + QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage); + + virtual bool eventFilter(QObject *watched, QEvent *event); + +public slots: + void closeAllPreviews(); + +signals: + void firstPreviewOpened(); + void lastPreviewClosed(); + +private slots: + void slotZoomChanged(int); + +private: + + virtual Qt::WindowFlags previewWindowFlags(const QWidget *widget) const; + virtual QWidget *createDeviceSkinContainer(const QDesignerFormWindowInterface *) const; + + QWidget *raise(const QDesignerFormWindowInterface *, const PreviewConfiguration &pc); + QWidget *createPreview(const QDesignerFormWindowInterface *, + const PreviewConfiguration &pc, + int deviceProfileIndex /* = -1 */, + QString *errorMessage, + /*Disabled by default, <0 */ + int initialZoom = -1); + + void updatePreviewClosed(QWidget *w); + + PreviewManagerPrivate *d; + + PreviewManager(const PreviewManager &other); + PreviewManager &operator =(const PreviewManager &other); +}; +} + +QT_END_NAMESPACE + +#endif // PREVIEWMANAGER_H diff --git a/tools/designer/src/lib/shared/promotionmodel.cpp b/tools/designer/src/lib/shared/promotionmodel.cpp new file mode 100644 index 0000000..77d0bbb --- /dev/null +++ b/tools/designer/src/lib/shared/promotionmodel.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::PromotionModel +*/ + +#include "promotionmodel_p.h" +#include "widgetdatabase_p.h" + +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QDesignerPromotionInterface> +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtGui/QStandardItem> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList<QStandardItem *> StandardItemList; + + // Model columns. + enum { ClassNameColumn, IncludeFileColumn, IncludeTypeColumn, ReferencedColumn, NumColumns }; + + // Create a model row. + StandardItemList modelRow() { + StandardItemList rc; + for (int i = 0; i < NumColumns; i++) { + rc.push_back(new QStandardItem()); + } + return rc; + } + + // Create a model row for a base class (read-only, cannot be selected). + StandardItemList baseModelRow(const QDesignerWidgetDataBaseItemInterface *dbItem) { + StandardItemList rc = modelRow(); + + rc[ClassNameColumn]->setText(dbItem->name()); + for (int i = 0; i < NumColumns; i++) { + rc[i]->setFlags(Qt::ItemIsEnabled); + } + return rc; + } + + // Create an editable model row for a promoted class. + StandardItemList promotedModelRow(const QDesignerWidgetDataBaseInterface *widgetDataBase, + QDesignerWidgetDataBaseItemInterface *dbItem, + bool referenced = false) { + + const int index = widgetDataBase->indexOf(dbItem); + + // Associate user data: database index and enabled flag + QVariantList userDataList; + userDataList.push_back(QVariant(index)); + userDataList.push_back(QVariant(referenced)); + const QVariant userData(userDataList); + + StandardItemList rc = modelRow(); + // name + rc[ClassNameColumn]->setText(dbItem->name()); + rc[ClassNameColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable); + rc[ClassNameColumn]->setData(userData); + // header + const qdesigner_internal::IncludeSpecification spec = qdesigner_internal::includeSpecification(dbItem->includeFile()); + rc[IncludeFileColumn]->setText(spec.first); + rc[IncludeFileColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable); + rc[IncludeFileColumn]->setData(userData); + // global include + rc[IncludeTypeColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsUserCheckable); + rc[IncludeTypeColumn]->setData(userData); + rc[IncludeTypeColumn]->setCheckState(spec.second == qdesigner_internal::IncludeGlobal ? Qt::Checked : Qt::Unchecked); + // referenced + rc[ReferencedColumn]->setFlags(Qt::ItemIsSelectable|Qt::ItemIsEnabled); + rc[ClassNameColumn]->setData(userData); + if (!referenced) { + //: Usage of promoted widgets + static const QString notUsed = QCoreApplication::translate("PromotionModel", "Not used"); + rc[ReferencedColumn]->setText(notUsed); + } + return rc; + } +} + +namespace qdesigner_internal { + + PromotionModel::PromotionModel(QDesignerFormEditorInterface *core) : + m_core(core) + { + connect(this, SIGNAL(itemChanged(QStandardItem *)), this, SLOT(slotItemChanged(QStandardItem *))); + } + + void PromotionModel::initializeHeaders() { + setColumnCount(NumColumns); + QStringList horizontalLabels(tr("Name")); + horizontalLabels += tr("Header file"); + horizontalLabels += tr("Global include"); + horizontalLabels += tr("Usage"); + setHorizontalHeaderLabels (horizontalLabels); + } + + void PromotionModel::updateFromWidgetDatabase() { + typedef QDesignerPromotionInterface::PromotedClasses PromotedClasses; + + clear(); + initializeHeaders(); + + // retrieve list of pairs from DB and convert into a tree structure. + // Set the item index as user data on the item. + const PromotedClasses promotedClasses = m_core->promotion()->promotedClasses(); + + if (promotedClasses.empty()) + return; + + const QSet<QString> usedPromotedClasses = m_core->promotion()->referencedPromotedClassNames(); + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *baseClass = 0; + QStandardItem *baseItem = 0; + + const PromotedClasses::const_iterator bcend = promotedClasses.constEnd(); + for (PromotedClasses::const_iterator it = promotedClasses.constBegin(); it != bcend; ++it) { + // Start a new base class? + if (baseClass != it->baseItem) { + baseClass = it->baseItem; + const StandardItemList baseRow = baseModelRow(it->baseItem); + baseItem = baseRow.front(); + appendRow(baseRow); + } + Q_ASSERT(baseItem); + // Append derived + baseItem->appendRow(promotedModelRow(widgetDataBase, it->promotedItem, usedPromotedClasses.contains(it->promotedItem->name()))); + } + } + + void PromotionModel::slotItemChanged(QStandardItem * changedItem) { + // Retrieve DB item + bool referenced; + QDesignerWidgetDataBaseItemInterface *dbItem = databaseItem(changedItem, &referenced); + Q_ASSERT(dbItem); + // Change header or type + switch (changedItem->column()) { + case ClassNameColumn: + emit classNameChanged(dbItem, changedItem->text()); + break; + case IncludeTypeColumn: + case IncludeFileColumn: { + // Get both file and type items via parent. + const QStandardItem *baseClassItem = changedItem->parent(); + const QStandardItem *fileItem = baseClassItem->child(changedItem->row(), IncludeFileColumn); + const QStandardItem *typeItem = baseClassItem->child(changedItem->row(), IncludeTypeColumn); + emit includeFileChanged(dbItem, buildIncludeFile(fileItem->text(), typeItem->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal)); + } + break; + } + } + + QDesignerWidgetDataBaseItemInterface *PromotionModel::databaseItemAt(const QModelIndex &index, bool *referenced) const { + if (const QStandardItem *item = itemFromIndex (index)) + return databaseItem(item, referenced); + + *referenced = false; + return 0; + } + + QDesignerWidgetDataBaseItemInterface *PromotionModel::databaseItem(const QStandardItem * item, bool *referenced) const { + // Decode user data associated with item. + const QVariant data = item->data(); + if (data.type() != QVariant::List) { + *referenced = false; + return 0; + } + + const QVariantList dataList = data.toList(); + const int index = dataList[0].toInt(); + *referenced = dataList[1].toBool(); + return m_core->widgetDataBase()->item(index); + } + + QModelIndex PromotionModel::indexOfClass(const QString &className) const { + const StandardItemList matches = findItems (className, Qt::MatchFixedString|Qt::MatchCaseSensitive|Qt::MatchRecursive); + return matches.empty() ? QModelIndex() : indexFromItem (matches.front()); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/promotionmodel_p.h b/tools/designer/src/lib/shared/promotionmodel_p.h new file mode 100644 index 0000000..d5843fc --- /dev/null +++ b/tools/designer/src/lib/shared/promotionmodel_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONMODEL_H +#define PROMOTIONMODEL_H + +#include <QtGui/QStandardItemModel> +#include <QtCore/QSet> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerWidgetDataBaseItemInterface; + +namespace qdesigner_internal { + + // Item model representing the promoted widgets. + class PromotionModel : public QStandardItemModel { + Q_OBJECT + + public: + explicit PromotionModel(QDesignerFormEditorInterface *core); + + void updateFromWidgetDatabase(); + + // Return item at model index or 0. + QDesignerWidgetDataBaseItemInterface *databaseItemAt(const QModelIndex &, bool *referenced) const; + + QModelIndex indexOfClass(const QString &className) const; + + signals: + void includeFileChanged(QDesignerWidgetDataBaseItemInterface *, const QString &includeFile); + void classNameChanged(QDesignerWidgetDataBaseItemInterface *, const QString &newName); + + private slots: + void slotItemChanged(QStandardItem * item); + + private: + void initializeHeaders(); + // Retrieve data base item of item or return 0. + QDesignerWidgetDataBaseItemInterface *databaseItem(const QStandardItem * item, bool *referenced) const; + + QDesignerFormEditorInterface *m_core; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONMODEL_H diff --git a/tools/designer/src/lib/shared/promotiontaskmenu.cpp b/tools/designer/src/lib/shared/promotiontaskmenu.cpp new file mode 100644 index 0000000..e458aab --- /dev/null +++ b/tools/designer/src/lib/shared/promotiontaskmenu.cpp @@ -0,0 +1,361 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "promotiontaskmenu_p.h" +#include "qdesigner_promotiondialog_p.h" +#include "widgetfactory_p.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" +#include "qdesigner_command_p.h" +#include "signalslotdialog_p.h" +#include "qdesigner_objectinspector_p.h" +#include "abstractintrospection_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QAction> +#include <QtGui/QWidget> +#include <QtGui/QMenu> +#include <QtCore/QSignalMapper> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +static QAction *separatorAction(QObject *parent) +{ + QAction *rc = new QAction(parent); + rc->setSeparator(true); + return rc; +} + +static inline QDesignerLanguageExtension *languageExtension(QDesignerFormEditorInterface *core) +{ + return qt_extension<QDesignerLanguageExtension*>(core->extensionManager(), core); +} + +namespace qdesigner_internal { + +PromotionTaskMenu::PromotionTaskMenu(QWidget *widget,Mode mode, QObject *parent) : + QObject(parent), + m_mode(mode), + m_widget(widget), + m_promotionMapper(0), + m_globalEditAction(new QAction(tr("Promoted widgets..."), this)), + m_EditPromoteToAction(new QAction(tr("Promote to ..."), this)), + m_EditSignalsSlotsAction(new QAction(tr("Change signals/slots..."), this)), + m_promoteLabel(tr("Promote to")), + m_demoteLabel(tr("Demote to %1")) +{ + connect(m_globalEditAction, SIGNAL(triggered()), this, SLOT(slotEditPromotedWidgets())); + connect(m_EditPromoteToAction, SIGNAL(triggered()), this, SLOT(slotEditPromoteTo())); + connect(m_EditSignalsSlotsAction, SIGNAL(triggered()), this, SLOT(slotEditSignalsSlots())); +} + +PromotionTaskMenu::Mode PromotionTaskMenu::mode() const +{ + return m_mode; +} + +void PromotionTaskMenu::setMode(Mode m) +{ + m_mode = m; +} + +void PromotionTaskMenu::setWidget(QWidget *widget) +{ + m_widget = widget; +} + +void PromotionTaskMenu::setPromoteLabel(const QString &promoteLabel) +{ + m_promoteLabel = promoteLabel; +} + +void PromotionTaskMenu::setEditPromoteToLabel(const QString &promoteEditLabel) +{ + m_EditPromoteToAction->setText(promoteEditLabel); +} + +void PromotionTaskMenu::setDemoteLabel(const QString &demoteLabel) +{ + m_demoteLabel = demoteLabel; +} + +PromotionTaskMenu::PromotionState PromotionTaskMenu::createPromotionActions(QDesignerFormWindowInterface *formWindow) +{ + // clear out old + if (!m_promotionActions.empty()) { + qDeleteAll(m_promotionActions); + m_promotionActions.clear(); + } + // No promotion of main container + if (formWindow->mainContainer() == m_widget) + return NotApplicable; + + // Check for a homogenous selection + const PromotionSelectionList promotionSelection = promotionSelectionList(formWindow); + + if (promotionSelection.empty()) + return NoHomogenousSelection; + + QDesignerFormEditorInterface *core = formWindow->core(); + // if it is promoted: demote only. + if (isPromoted(formWindow->core(), m_widget)) { + const QString label = m_demoteLabel.arg( promotedExtends(core , m_widget)); + QAction *demoteAction = new QAction(label, this); + connect(demoteAction, SIGNAL(triggered()), this, SLOT(slotDemoteFromCustomWidget())); + m_promotionActions.push_back(demoteAction); + return CanDemote; + } + // figure out candidates + const QString baseClassName = WidgetFactory::classNameOf(core, m_widget); + const WidgetDataBaseItemList candidates = promotionCandidates(core->widgetDataBase(), baseClassName ); + if (candidates.empty()) { + // Is this thing promotable at all? + return QDesignerPromotionDialog::baseClassNames(core->promotion()).contains(baseClassName) ? CanPromote : NotApplicable; + } + // Set up a signal mapper to associate class names + if (!m_promotionMapper) { + m_promotionMapper = new QSignalMapper(this); + connect(m_promotionMapper, SIGNAL(mapped(QString)), this, SLOT(slotPromoteToCustomWidget(QString))); + } + + QMenu *candidatesMenu = new QMenu(); + // Create a sub menu + const WidgetDataBaseItemList::const_iterator cend = candidates.constEnd(); + // Set up actions and map class names + for (WidgetDataBaseItemList::const_iterator it = candidates.constBegin(); it != cend; ++it) { + const QString customClassName = (*it)->name(); + QAction *action = new QAction((*it)->name(), this); + connect(action, SIGNAL(triggered()), m_promotionMapper, SLOT(map())); + m_promotionMapper->setMapping(action, customClassName); + candidatesMenu->addAction(action); + } + // Sub menu action + QAction *subMenuAction = new QAction(m_promoteLabel, this); + subMenuAction->setMenu(candidatesMenu); + m_promotionActions.push_back(subMenuAction); + return CanPromote; +} + +void PromotionTaskMenu::addActions(unsigned separatorFlags, ActionList &actionList) +{ + addActions(formWindow(), separatorFlags, actionList); +} + +void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags, + ActionList &actionList) +{ + Q_ASSERT(m_widget); + const int previousSize = actionList.size(); + const PromotionState promotionState = createPromotionActions(fw); + + // Promotion candidates/demote + actionList += m_promotionActions; + + // Edit action depending on context + switch (promotionState) { + case CanPromote: + actionList += m_EditPromoteToAction; + break; + case CanDemote: + if (!(flags & SuppressGlobalEdit)) + actionList += m_globalEditAction; + if (!languageExtension(fw->core())) { + actionList += separatorAction(this); + actionList += m_EditSignalsSlotsAction; + } + break; + default: + if (!(flags & SuppressGlobalEdit)) + actionList += m_globalEditAction; + break; + } + // Add separators if required + if (actionList.size() > previousSize) { + if (flags & LeadingSeparator) + actionList.insert(previousSize, separatorAction(this)); + if (flags & TrailingSeparator) + actionList += separatorAction(this); + } +} + +void PromotionTaskMenu::addActions(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *menu) +{ + ActionList actionList; + addActions(fw, flags, actionList); + menu->addActions(actionList); +} + +void PromotionTaskMenu::addActions(unsigned flags, QMenu *menu) +{ + addActions(formWindow(), flags, menu); +} + +void PromotionTaskMenu::promoteTo(QDesignerFormWindowInterface *fw, const QString &customClassName) +{ + Q_ASSERT(m_widget); + PromoteToCustomWidgetCommand *cmd = new PromoteToCustomWidgetCommand(fw); + cmd->init(promotionSelectionList(fw), customClassName); + fw->commandHistory()->push(cmd); +} + + +void PromotionTaskMenu::slotPromoteToCustomWidget(const QString &customClassName) +{ + promoteTo(formWindow(), customClassName); +} + +void PromotionTaskMenu::slotDemoteFromCustomWidget() +{ + QDesignerFormWindowInterface *fw = formWindow(); + const PromotionSelectionList promotedWidgets = promotionSelectionList(fw); + Q_ASSERT(!promotedWidgets.empty() && isPromoted(fw->core(), promotedWidgets.front())); + + // ### use the undo stack + DemoteFromCustomWidgetCommand *cmd = new DemoteFromCustomWidgetCommand(fw); + cmd->init(promotedWidgets); + fw->commandHistory()->push(cmd); +} + +void PromotionTaskMenu::slotEditPromoteTo() +{ + Q_ASSERT(m_widget); + // Check whether invoked over a promotable widget + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + const QString base_class_name = WidgetFactory::classNameOf(core, m_widget); + Q_ASSERT(QDesignerPromotionDialog::baseClassNames(core->promotion()).contains(base_class_name)); + // Show over promotable widget + QString promoteToClassName; + QDialog *promotionEditor = 0; + if (QDesignerLanguageExtension *lang = languageExtension(core)) + promotionEditor = lang->createPromotionDialog(core, base_class_name, &promoteToClassName, fw); + if (!promotionEditor) + promotionEditor = new QDesignerPromotionDialog(core, fw, base_class_name, &promoteToClassName); + if (promotionEditor->exec() == QDialog::Accepted && !promoteToClassName.isEmpty()) { + promoteTo(fw, promoteToClassName); + } + delete promotionEditor; +} + +void PromotionTaskMenu::slotEditPromotedWidgets() +{ + // Global context, show over non-promotable widget + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + editPromotedWidgets(fw->core(), fw); +} + +PromotionTaskMenu::PromotionSelectionList PromotionTaskMenu::promotionSelectionList(QDesignerFormWindowInterface *formWindow) const +{ + // In multi selection mode, check for a homogenous selection (same class, same promotion state) + // and return the list if this is the case. Also make sure m_widget + // is the last widget in the list so that it is re-selected as the last + // widget by the promotion commands. + + PromotionSelectionList rc; + + if (m_mode != ModeSingleWidget) { + QDesignerFormEditorInterface *core = formWindow->core(); + const QDesignerIntrospectionInterface *intro = core->introspection(); + const QString className = intro->metaObject(m_widget)->className(); + const bool promoted = isPromoted(formWindow->core(), m_widget); + // Just in case someone plugged an old-style Object Inspector + if (QDesignerObjectInspector *designerObjectInspector = qobject_cast<QDesignerObjectInspector *>(core->objectInspector())) { + Selection s; + designerObjectInspector->getSelection(s); + // Find objects of similar state + const QWidgetList &source = m_mode == ModeManagedMultiSelection ? s.managed : s.unmanaged; + const QWidgetList::const_iterator cend = source.constEnd(); + for (QWidgetList::const_iterator it = source.constBegin(); it != cend; ++it) { + QWidget *w = *it; + if (w != m_widget) { + // Selection state mismatch + if (intro->metaObject(w)->className() != className || isPromoted(core, w) != promoted) + return PromotionSelectionList(); + rc.push_back(w); + } + } + } + } + + rc.push_back(m_widget); + return rc; +} + +QDesignerFormWindowInterface *PromotionTaskMenu::formWindow() const +{ + // Use the QObject overload of QDesignerFormWindowInterface::findFormWindow since that works + // for QDesignerMenus also. + QObject *o = m_widget; + QDesignerFormWindowInterface *result = QDesignerFormWindowInterface::findFormWindow(o); + Q_ASSERT(result != 0); + return result; +} + +void PromotionTaskMenu::editPromotedWidgets(QDesignerFormEditorInterface *core, QWidget* parent) { + QDesignerLanguageExtension *lang = languageExtension(core); + // Show over non-promotable widget + QDialog *promotionEditor = 0; + if (lang) + lang->createPromotionDialog(core, parent); + if (!promotionEditor) + promotionEditor = new QDesignerPromotionDialog(core, parent); + promotionEditor->exec(); + delete promotionEditor; +} + +void PromotionTaskMenu::slotEditSignalsSlots() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + SignalSlotDialog::editPromotedClass(fw->core(), m_widget, fw); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/promotiontaskmenu_p.h b/tools/designer/src/lib/shared/promotiontaskmenu_p.h new file mode 100644 index 0000000..223ce48 --- /dev/null +++ b/tools/designer/src/lib/shared/promotiontaskmenu_p.h @@ -0,0 +1,151 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONTASKMENU_H +#define PROMOTIONTASKMENU_H + +#include "shared_global_p.h" + +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QAction; +class QMenu; +class QWidget; +class QSignalMapper; + +namespace qdesigner_internal { + +// A helper class for creating promotion context menus and handling promotion actions. + +class QDESIGNER_SHARED_EXPORT PromotionTaskMenu: public QObject +{ + Q_OBJECT +public: + enum Mode { + ModeSingleWidget, + ModeManagedMultiSelection, + ModeUnmanagedMultiSelection + }; + + explicit PromotionTaskMenu(QWidget *widget,Mode mode = ModeManagedMultiSelection, QObject *parent = 0); + + Mode mode() const; + void setMode(Mode m); + + void setWidget(QWidget *widget); + + // Set menu labels + void setPromoteLabel(const QString &promoteLabel); + void setEditPromoteToLabel(const QString &promoteEditLabel); + // Defaults to "Demote to %1".arg(class). + void setDemoteLabel(const QString &demoteLabel); + + typedef QList<QAction*> ActionList; + + enum AddFlags { LeadingSeparator = 1, TrailingSeparator = 2, SuppressGlobalEdit = 4}; + + // Adds a list of promotion actions according to the current promotion state of the widget. + void addActions(QDesignerFormWindowInterface *fw, unsigned flags, ActionList &actionList); + // Convenience that finds the form window. + void addActions(unsigned flags, ActionList &actionList); + + void addActions(QDesignerFormWindowInterface *fw, unsigned flags, QMenu *menu); + void addActions(unsigned flags, QMenu *menu); + + // Pop up the editor in a global context. + static void editPromotedWidgets(QDesignerFormEditorInterface *core, QWidget* parent); + +private slots: + void slotPromoteToCustomWidget(const QString &customClassName); + void slotDemoteFromCustomWidget(); + void slotEditPromotedWidgets(); + void slotEditPromoteTo(); + void slotEditSignalsSlots(); + +private: + void promoteTo(QDesignerFormWindowInterface *fw, const QString &customClassName); + + enum PromotionState { NotApplicable, NoHomogenousSelection, CanPromote, CanDemote }; + PromotionState createPromotionActions(QDesignerFormWindowInterface *formWindow); + QDesignerFormWindowInterface *formWindow() const; + + typedef QList<QPointer<QWidget> > PromotionSelectionList; + PromotionSelectionList promotionSelectionList(QDesignerFormWindowInterface *formWindow) const; + + Mode m_mode; + + QPointer<QWidget> m_widget; + + QSignalMapper *m_promotionMapper; + // Per-Widget actions + QList<QAction *> m_promotionActions; + + QAction *m_globalEditAction; + QAction *m_EditPromoteToAction; + QAction *m_EditSignalsSlotsAction; + + QString m_promoteLabel; + QString m_demoteLabel; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONTASKMENU_H diff --git a/tools/designer/src/lib/shared/propertylineedit.cpp b/tools/designer/src/lib/shared/propertylineedit.cpp new file mode 100644 index 0000000..a3b1efa --- /dev/null +++ b/tools/designer/src/lib/shared/propertylineedit.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "propertylineedit_p.h" + +#include <QtGui/QContextMenuEvent> +#include <QtGui/QKeyEvent> +#include <QtGui/QMenu> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + PropertyLineEdit::PropertyLineEdit(QWidget *parent) : + QLineEdit(parent), m_wantNewLine(false) + { + } + + bool PropertyLineEdit::event(QEvent *e) + { + // handle 'Select all' here as it is not done in the QLineEdit + if (e->type() == QEvent::ShortcutOverride && !isReadOnly()) { + QKeyEvent* ke = static_cast<QKeyEvent*> (e); + if (ke->modifiers() & Qt::ControlModifier) { + if(ke->key() == Qt::Key_A) { + ke->accept(); + return true; + } + } + } + return QLineEdit::event(e); + } + + void PropertyLineEdit::insertNewLine() { + insertText(QLatin1String("\\n")); + } + + void PropertyLineEdit::insertText(const QString &text) { + // position cursor after new text and grab focus + const int oldCursorPosition = cursorPosition (); + insert(text); + setCursorPosition (oldCursorPosition + text.length()); + setFocus(Qt::OtherFocusReason); + } + + void PropertyLineEdit::contextMenuEvent(QContextMenuEvent *event) { + QMenu *menu = createStandardContextMenu (); + + if (m_wantNewLine) { + menu->addSeparator(); + QAction* nlAction = menu->addAction(tr("Insert line break")); + connect(nlAction, SIGNAL(triggered()), this, SLOT(insertNewLine())); + } + + menu->exec(event->globalPos()); + } +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/propertylineedit_p.h b/tools/designer/src/lib/shared/propertylineedit_p.h new file mode 100644 index 0000000..42f0b3c --- /dev/null +++ b/tools/designer/src/lib/shared/propertylineedit_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROPERTYLINEEDIT_H +#define PROPERTYLINEEDIT_H + +#include "shared_global_p.h" + +#include <QtGui/QLineEdit> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // A line edit with a special context menu allowing for adding (escaped) new lines + class PropertyLineEdit : public QLineEdit { + Q_OBJECT + public: + explicit PropertyLineEdit(QWidget *parent); + void setWantNewLine(bool nl) { m_wantNewLine = nl; } + bool wantNewLine() const { return m_wantNewLine; } + + bool event(QEvent *e); + protected: + void contextMenuEvent (QContextMenuEvent *event ); + private slots: + void insertNewLine(); + private: + void insertText(const QString &); + bool m_wantNewLine; + }; +} + +QT_END_NAMESPACE + +#endif // PROPERTYLINEEDIT_H diff --git a/tools/designer/src/lib/shared/qdesigner_command.cpp b/tools/designer/src/lib/shared/qdesigner_command.cpp new file mode 100644 index 0000000..81d5917 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_command.cpp @@ -0,0 +1,2968 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "layout_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_widget_p.h" +#include "qdesigner_menu_p.h" +#include "shared_enums_p.h" +#include "metadatabase_p.h" +#include "formwindowbase_p.h" +#include <abstractformbuilder.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerActionEditorInterface> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerLayoutDecorationExtension> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtDesigner/QDesignerIntegrationInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtCore/qdebug.h> +#include <QtCore/QTextStream> +#include <QtCore/QQueue> + +#include <QtGui/QMenuBar> +#include <QtGui/QStatusBar> +#include <QtGui/QToolBar> +#include <QtGui/QToolBox> +#include <QtGui/QStackedWidget> +#include <QtGui/QTabWidget> +#include <QtGui/QTableWidget> +#include <QtGui/QTreeWidget> +#include <QtGui/QListWidget> +#include <QtGui/QComboBox> +#include <QtGui/QSplitter> +#include <QtGui/QDockWidget> +#include <QtGui/QMainWindow> +#include <QtGui/QWizardPage> +#include <QtGui/QApplication> +#include <QtGui/QFormLayout> + +Q_DECLARE_METATYPE(QWidgetList) + +QT_BEGIN_NAMESPACE + +static inline void setPropertySheetWindowTitle(const QDesignerFormEditorInterface *core, QObject *o, const QString &t) +{ + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), o)) { + const int idx = sheet->indexOf(QLatin1String("windowTitle")); + if (idx != -1) { + sheet->setProperty(idx, t); + sheet->setChanged(idx, true); + } + } +} + +namespace qdesigner_internal { + +// Helpers for the dynamic properties that store Z/Widget order +static const char *widgetOrderPropertyC = "_q_widgetOrder"; +static const char *zOrderPropertyC = "_q_zOrder"; + +static void addToWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name, int index = -1) +{ + QWidgetList list = qVariantValue<QWidgetList>(parentWidget->property(name)); + list.removeAll(widget); + if (index >= 0 && index < list.size()) { + list.insert(index, widget); + } else { + list.append(widget); + } + parentWidget->setProperty(name, qVariantFromValue(list)); +} + +static int removeFromWidgetListDynamicProperty(QWidget *parentWidget, QWidget *widget, const char *name) +{ + QWidgetList list = qVariantValue<QWidgetList>(parentWidget->property(name)); + const int firstIndex = list.indexOf(widget); + if (firstIndex != -1) { + list.removeAll(widget); + parentWidget->setProperty(name, qVariantFromValue(list)); + } + return firstIndex; +} + +// ---- InsertWidgetCommand ---- +InsertWidgetCommand::InsertWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_insertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode), + m_layoutHelper(0), + m_widgetWasManaged(false) +{ +} + +InsertWidgetCommand::~InsertWidgetCommand() +{ + delete m_layoutHelper; +} + +void InsertWidgetCommand::init(QWidget *widget, bool already_in_form, int layoutRow, int layoutColumn) +{ + m_widget = widget; + + setText(QApplication::translate("Command", "Insert '%1'").arg(widget->objectName())); + + QWidget *parentWidget = m_widget->parentWidget(); + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget); + + m_insertMode = deco ? deco->currentInsertMode() : QDesignerLayoutDecorationExtension::InsertWidgetMode; + if (layoutRow >= 0 && layoutColumn >= 0) { + m_cell.first = layoutRow; + m_cell.second = layoutColumn; + } else { + m_cell = deco ? deco->currentCell() : qMakePair(0, 0); + } + m_widgetWasManaged = already_in_form; +} + +static void recursiveUpdate(QWidget *w) +{ + w->update(); + + const QObjectList &l = w->children(); + const QObjectList::const_iterator cend = l.end(); + for ( QObjectList::const_iterator it = l.begin(); it != cend; ++it) { + if (QWidget *w = qobject_cast<QWidget*>(*it)) + recursiveUpdate(w); + } +} + +void InsertWidgetCommand::redo() +{ + QWidget *parentWidget = m_widget->parentWidget(); + Q_ASSERT(parentWidget); + + addToWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC); + addToWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC); + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget); + + if (deco != 0) { + const LayoutInfo::Type type = LayoutInfo::layoutType(core, LayoutInfo::managedLayout(core, parentWidget)); + m_layoutHelper = LayoutHelper::createLayoutHelper(type); + m_layoutHelper->pushState(core, parentWidget); + if (type == LayoutInfo::Grid) { + switch (m_insertMode) { + case QDesignerLayoutDecorationExtension::InsertRowMode: { + deco->insertRow(m_cell.first); + } break; + + case QDesignerLayoutDecorationExtension::InsertColumnMode: { + deco->insertColumn(m_cell.second); + } break; + + default: break; + } // end switch + } + deco->insertWidget(m_widget, m_cell); + } + + if (!m_widgetWasManaged) + formWindow()->manageWidget(m_widget); + m_widget->show(); + formWindow()->emitSelectionChanged(); + + if (parentWidget && parentWidget->layout()) { + recursiveUpdate(parentWidget); + parentWidget->layout()->invalidate(); + } + + refreshBuddyLabels(); +} + +void InsertWidgetCommand::undo() +{ + QWidget *parentWidget = m_widget->parentWidget(); + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), parentWidget); + + if (deco) { + deco->removeWidget(m_widget); + m_layoutHelper->popState(core, parentWidget); + } + + if (!m_widgetWasManaged) { + formWindow()->unmanageWidget(m_widget); + m_widget->hide(); + } + + removeFromWidgetListDynamicProperty(parentWidget, m_widget, widgetOrderPropertyC); + removeFromWidgetListDynamicProperty(parentWidget, m_widget, zOrderPropertyC); + + formWindow()->emitSelectionChanged(); + + refreshBuddyLabels(); +} + +void InsertWidgetCommand::refreshBuddyLabels() +{ + typedef QList<QLabel*> LabelList; + + const LabelList label_list = qFindChildren<QLabel*>(formWindow()); + if (label_list.empty()) + return; + + const QString buddyProperty = QLatin1String("buddy"); + const QByteArray objectNameU8 = m_widget->objectName().toUtf8(); + // Re-set the buddy (The sheet locates the object by name and sets it) + const LabelList::const_iterator cend = label_list.constEnd(); + for (LabelList::const_iterator it = label_list.constBegin(); it != cend; ++it ) { + if (QDesignerPropertySheetExtension* sheet = propertySheet(*it)) { + const int idx = sheet->indexOf(buddyProperty); + if (idx != -1) { + const QVariant value = sheet->property(idx); + if (value.toByteArray() == objectNameU8) + sheet->setProperty(idx, value); + } + } + } +} + +// ---- ChangeZOrderCommand ---- +ChangeZOrderCommand::ChangeZOrderCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void ChangeZOrderCommand::init(QWidget *widget) +{ + Q_ASSERT(widget); + + m_widget = widget; + + setText(QApplication::translate("Command", "Change Z-order of '%1'").arg(widget->objectName())); + + m_oldParentZOrder = qVariantValue<QWidgetList>(widget->parentWidget()->property("_q_zOrder")); + const int index = m_oldParentZOrder.indexOf(m_widget); + if (index != -1 && index + 1 < m_oldParentZOrder.count()) + m_oldPreceding = m_oldParentZOrder.at(index + 1); +} + +void ChangeZOrderCommand::redo() +{ + m_widget->parentWidget()->setProperty("_q_zOrder", qVariantFromValue(reorderWidget(m_oldParentZOrder, m_widget))); + + reorder(m_widget); +} + +void ChangeZOrderCommand::undo() +{ + m_widget->parentWidget()->setProperty("_q_zOrder", qVariantFromValue(m_oldParentZOrder)); + + if (m_oldPreceding) + m_widget->stackUnder(m_oldPreceding); + else + m_widget->raise(); +} + +// ---- RaiseWidgetCommand ---- +RaiseWidgetCommand::RaiseWidgetCommand(QDesignerFormWindowInterface *formWindow) + : ChangeZOrderCommand(formWindow) +{ +} + +void RaiseWidgetCommand::init(QWidget *widget) +{ + ChangeZOrderCommand::init(widget); + setText(QApplication::translate("Command", "Raise '%1'").arg(widget->objectName())); +} + +QWidgetList RaiseWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const +{ + QWidgetList l = list; + l.removeAll(widget); + l.append(widget); + return l; +} + +void RaiseWidgetCommand::reorder(QWidget *widget) const +{ + widget->raise(); +} + +// ---- LowerWidgetCommand ---- +LowerWidgetCommand::LowerWidgetCommand(QDesignerFormWindowInterface *formWindow) + : ChangeZOrderCommand(formWindow) +{ +} + +QWidgetList LowerWidgetCommand::reorderWidget(const QWidgetList &list, QWidget *widget) const +{ + QWidgetList l = list; + l.removeAll(widget); + l.prepend(widget); + return l; +} + +void LowerWidgetCommand::init(QWidget *widget) +{ + ChangeZOrderCommand::init(widget); + setText(QApplication::translate("Command", "Lower '%1'").arg(widget->objectName())); +} + +void LowerWidgetCommand::reorder(QWidget *widget) const +{ + widget->lower(); +} + +// ---- ManageWidgetCommandHelper +ManageWidgetCommandHelper::ManageWidgetCommandHelper() : + m_widget(0) +{ +} + +void ManageWidgetCommandHelper::init(const QDesignerFormWindowInterface *fw, QWidget *widget) +{ + m_widget = widget; + m_managedChildren.clear(); + + const QWidgetList children = qFindChildren<QWidget *>(m_widget); + if (children.empty()) + return; + + m_managedChildren.reserve(children.size()); + const QWidgetList::const_iterator lcend = children.constEnd(); + for (QWidgetList::const_iterator it = children.constBegin(); it != lcend; ++it) + if (fw->isManaged(*it)) + m_managedChildren.push_back(*it); +} + +void ManageWidgetCommandHelper::init(QWidget *widget, const WidgetVector &managedChildren) +{ + m_widget = widget; + m_managedChildren = managedChildren; +} + +void ManageWidgetCommandHelper::manage(QDesignerFormWindowInterface *fw) +{ + // Manage the managed children after parent + fw->manageWidget(m_widget); + if (!m_managedChildren.empty()) { + const WidgetVector::const_iterator lcend = m_managedChildren.constEnd(); + for (WidgetVector::const_iterator it = m_managedChildren.constBegin(); it != lcend; ++it) + fw->manageWidget(*it); + } +} + +void ManageWidgetCommandHelper::unmanage(QDesignerFormWindowInterface *fw) +{ + // Unmanage the managed children first + if (!m_managedChildren.empty()) { + const WidgetVector::const_iterator lcend = m_managedChildren.constEnd(); + for (WidgetVector::const_iterator it = m_managedChildren.constBegin(); it != lcend; ++it) + fw->unmanageWidget(*it); + } + fw->unmanageWidget(m_widget); +} + +// ---- DeleteWidgetCommand ---- +DeleteWidgetCommand::DeleteWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_layoutType(LayoutInfo::NoLayout), + m_layoutHelper(0), + m_flags(0), + m_splitterIndex(-1), + m_layoutSimplified(false), + m_formItem(0), + m_tabOrderIndex(-1), + m_widgetOrderIndex(-1), + m_zOrderIndex(-1) +{ +} + +DeleteWidgetCommand::~DeleteWidgetCommand() +{ + delete m_layoutHelper; +} + +void DeleteWidgetCommand::init(QWidget *widget, unsigned flags) +{ + m_widget = widget; + m_parentWidget = widget->parentWidget(); + m_geometry = widget->geometry(); + m_flags = flags; + m_layoutType = LayoutInfo::NoLayout; + m_splitterIndex = -1; + bool isManaged; // Check for a managed layout + QLayout *layout; + m_layoutType = LayoutInfo::laidoutWidgetType(formWindow()->core(), m_widget, &isManaged, &layout); + if (!isManaged) + m_layoutType = LayoutInfo::NoLayout; + switch (m_layoutType) { + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + QSplitter *splitter = qobject_cast<QSplitter *>(m_parentWidget); + Q_ASSERT(splitter); + m_splitterIndex = splitter->indexOf(widget); + } + break; + case LayoutInfo::NoLayout: + break; + default: + m_layoutHelper = LayoutHelper::createLayoutHelper(m_layoutType); + m_layoutPosition = m_layoutHelper->itemInfo(layout, m_widget); + break; + } + + m_formItem = formWindow()->core()->metaDataBase()->item(formWindow()); + m_tabOrderIndex = m_formItem->tabOrder().indexOf(widget); + + // Build the list of managed children + m_manageHelper.init(formWindow(), m_widget); + + setText(QApplication::translate("Command", "Delete '%1'").arg(widget->objectName())); +} + +void DeleteWidgetCommand::redo() +{ + formWindow()->clearSelection(); + QDesignerFormEditorInterface *core = formWindow()->core(); + + if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_parentWidget)) { + const int count = c->count(); + for (int i=0; i<count; ++i) { + if (c->widget(i) == m_widget) { + c->remove(i); + return; + } + } + } + + m_widgetOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC); + m_zOrderIndex = removeFromWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC); + + if (QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), m_parentWidget)) + deco->removeWidget(m_widget); + + if (m_layoutHelper) + switch (m_layoutType) { + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + break; + default: + // Attempt to simplify grids if a row/column becomes empty + m_layoutSimplified = (m_flags & DoNotSimplifyLayout) ? false : m_layoutHelper->canSimplify(core, m_parentWidget, m_layoutPosition); + if (m_layoutSimplified) { + m_layoutHelper->pushState(core, m_parentWidget); + m_layoutHelper->simplify(core, m_parentWidget, m_layoutPosition); + } + break; + } + + if (!(m_flags & DoNotUnmanage)) + m_manageHelper.unmanage(formWindow()); + + m_widget->setParent(formWindow()); + m_widget->hide(); + + if (m_tabOrderIndex != -1) { + QList<QWidget*> tab_order = m_formItem->tabOrder(); + tab_order.removeAt(m_tabOrderIndex); + m_formItem->setTabOrder(tab_order); + } +} + +void DeleteWidgetCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + formWindow()->clearSelection(); + + m_widget->setParent(m_parentWidget); + + if (QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_parentWidget)) { + c->addWidget(m_widget); + return; + } + + addToWidgetListDynamicProperty(m_parentWidget, m_widget, widgetOrderPropertyC, m_widgetOrderIndex); + addToWidgetListDynamicProperty(m_parentWidget, m_widget, zOrderPropertyC, m_zOrderIndex); + + m_widget->setGeometry(m_geometry); + + if (!(m_flags & DoNotUnmanage)) + m_manageHelper.manage(formWindow()); + // ### set up alignment + switch (m_layoutType) { + case LayoutInfo::NoLayout: + break; + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: { + QSplitter *splitter = qobject_cast<QSplitter *>(m_widget->parent()); + Q_ASSERT(splitter); + splitter->insertWidget(m_splitterIndex, m_widget); + } break; + default: { + Q_ASSERT(m_layoutHelper); + if (m_layoutSimplified) + m_layoutHelper->popState(core, m_parentWidget); + QLayout *layout = LayoutInfo::managedLayout(core, m_parentWidget); + Q_ASSERT(m_layoutType == LayoutInfo::layoutType(core, layout)); + m_layoutHelper->insertWidget(layout, m_layoutPosition, m_widget); + } + break; + } + + m_widget->show(); + + if (m_tabOrderIndex != -1) { + QList<QWidget*> tab_order = m_formItem->tabOrder(); + tab_order.insert(m_tabOrderIndex, m_widget); + m_formItem->setTabOrder(tab_order); + } +} + +// ---- ReparentWidgetCommand ---- +ReparentWidgetCommand::ReparentWidgetCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void ReparentWidgetCommand::init(QWidget *widget, QWidget *parentWidget) +{ + Q_ASSERT(widget); + + m_widget = widget; + m_oldParentWidget = widget->parentWidget(); + m_newParentWidget = parentWidget; + + m_oldPos = m_widget->pos(); + m_newPos = m_newParentWidget->mapFromGlobal(m_oldParentWidget->mapToGlobal(m_oldPos)); + + setText(QApplication::translate("Command", "Reparent '%1'").arg(widget->objectName())); + + m_oldParentList = qVariantValue<QWidgetList>(m_oldParentWidget->property("_q_widgetOrder")); + m_oldParentZOrder = qVariantValue<QWidgetList>(m_oldParentWidget->property("_q_zOrder")); +} + +void ReparentWidgetCommand::redo() +{ + m_widget->setParent(m_newParentWidget); + m_widget->move(m_newPos); + + QWidgetList oldList = m_oldParentList; + oldList.removeAll(m_widget); + m_oldParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(oldList)); + + QWidgetList newList = qVariantValue<QWidgetList>(m_newParentWidget->property("_q_widgetOrder")); + newList.append(m_widget); + m_newParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(newList)); + + QWidgetList oldZOrder = m_oldParentZOrder; + oldZOrder.removeAll(m_widget); + m_oldParentWidget->setProperty("_q_zOrder", qVariantFromValue(oldZOrder)); + + QWidgetList newZOrder = qVariantValue<QWidgetList>(m_newParentWidget->property("_q_zOrder")); + newZOrder.append(m_widget); + m_newParentWidget->setProperty("_q_zOrder", qVariantFromValue(newZOrder)); + + m_widget->show(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +void ReparentWidgetCommand::undo() +{ + m_widget->setParent(m_oldParentWidget); + m_widget->move(m_oldPos); + + m_oldParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(m_oldParentList)); + + QWidgetList newList = qVariantValue<QWidgetList>(m_newParentWidget->property("_q_widgetOrder")); + newList.removeAll(m_widget); + m_newParentWidget->setProperty("_q_widgetOrder", qVariantFromValue(newList)); + + m_oldParentWidget->setProperty("_q_zOrder", qVariantFromValue(m_oldParentZOrder)); + + QWidgetList newZOrder = qVariantValue<QWidgetList>(m_newParentWidget->property("_q_zOrder")); + m_newParentWidget->setProperty("_q_zOrder", qVariantFromValue(newZOrder)); + + m_widget->show(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +PromoteToCustomWidgetCommand::PromoteToCustomWidgetCommand + (QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Promote to custom widget"), formWindow) +{ +} + +void PromoteToCustomWidgetCommand::init(const WidgetList &widgets,const QString &customClassName) +{ + m_widgets = widgets; + m_customClassName = customClassName; +} + +void PromoteToCustomWidgetCommand::redo() +{ + foreach (QWidget *w, m_widgets) { + if (w) + promoteWidget(core(), w, m_customClassName); + } + updateSelection(); +} + +void PromoteToCustomWidgetCommand::updateSelection() +{ + // Update class names in ObjectInspector, PropertyEditor + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = fw->core(); + core->objectInspector()->setFormWindow(fw); + if (QObject *o = core->propertyEditor()->object()) + core->propertyEditor()->setObject(o); +} + +void PromoteToCustomWidgetCommand::undo() +{ + foreach (QWidget *w, m_widgets) { + if (w) + demoteWidget(core(), w); + } + updateSelection(); +} + +// ---- DemoteFromCustomWidgetCommand ---- + +DemoteFromCustomWidgetCommand::DemoteFromCustomWidgetCommand + (QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Demote from custom widget"), formWindow), + m_promote_cmd(formWindow) +{ +} + +void DemoteFromCustomWidgetCommand::init(const WidgetList &promoted) +{ + m_promote_cmd.init(promoted, promotedCustomClassName(core(), promoted.front())); +} + +void DemoteFromCustomWidgetCommand::redo() +{ + m_promote_cmd.undo(); +} + +void DemoteFromCustomWidgetCommand::undo() +{ + m_promote_cmd.redo(); +} + +// ---------- CursorSelectionState +CursorSelectionState::CursorSelectionState() +{ +} + +void CursorSelectionState::save(const QDesignerFormWindowInterface *formWindow) +{ + const QDesignerFormWindowCursorInterface *cursor = formWindow->cursor(); + m_selection.clear(); + m_current = cursor->current(); + if (cursor->hasSelection()) { + const int count = cursor->selectedWidgetCount(); + for(int i = 0; i < count; i++) + m_selection.push_back(cursor->selectedWidget(i)); + } +} + +void CursorSelectionState::restore(QDesignerFormWindowInterface *formWindow) const +{ + if (m_selection.empty()) { + formWindow->clearSelection(true); + } else { + // Select current as last + formWindow->clearSelection(false); + const WidgetPointerList::const_iterator cend = m_selection.constEnd(); + for (WidgetPointerList::const_iterator it = m_selection.constBegin(); it != cend; ++it) + if (QWidget *w = *it) + if (w != m_current) + formWindow->selectWidget(*it, true); + if (m_current) + formWindow->selectWidget(m_current, true); + } +} + +// ---- LayoutCommand ---- + +LayoutCommand::LayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_setup(false) +{ +} + +LayoutCommand::~LayoutCommand() +{ + delete m_layout; +} + +void LayoutCommand::init(QWidget *parentWidget, const QWidgetList &widgets, + LayoutInfo::Type layoutType, QWidget *layoutBase, + bool reparentLayoutWidget) +{ + m_parentWidget = parentWidget; + m_widgets = widgets; + formWindow()->simplifySelection(&m_widgets); + m_layout = Layout::createLayout(widgets, parentWidget, formWindow(), layoutBase, layoutType); + m_layout->setReparentLayoutWidget(reparentLayoutWidget); + + switch (layoutType) { + case LayoutInfo::Grid: + setText(QApplication::translate("Command", "Lay out using grid")); + break; + case LayoutInfo::VBox: + setText(QApplication::translate("Command", "Lay out vertically")); + break; + case LayoutInfo::HBox: + setText(QApplication::translate("Command", "Lay out horizontally")); + break; + default: + break; + } + // Delayed setup to avoid confusion in case we are chained + // with a BreakLayout in a morph layout macro + m_setup = false; +} + +void LayoutCommand::redo() +{ + if (!m_setup) { + m_layout->setup(); + m_cursorSelectionState.save(formWindow()); + m_setup = true; + } + m_layout->doLayout(); + core()->objectInspector()->setFormWindow(formWindow()); +} + +void LayoutCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + + QWidget *lb = m_layout->layoutBaseWidget(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), lb); + m_layout->undoLayout(); + delete deco; // release the extension + + // ### generalize (put in function) + if (!m_layoutBase && lb != 0 && !(qobject_cast<QLayoutWidget*>(lb) || qobject_cast<QSplitter*>(lb))) { + core->metaDataBase()->add(lb); + lb->show(); + } + m_cursorSelectionState.restore(formWindow()); + core->objectInspector()->setFormWindow(formWindow()); +} + +// ---- BreakLayoutCommand ---- +BreakLayoutCommand::BreakLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Break layout"), formWindow), + m_layoutHelper(0), + m_properties(0), + m_propertyMask(0) +{ +} + +BreakLayoutCommand::~BreakLayoutCommand() +{ + delete m_layoutHelper; + delete m_layout; + delete m_properties; +} + +const LayoutProperties *BreakLayoutCommand::layoutProperties() const +{ + return m_properties; +} + +int BreakLayoutCommand::propertyMask() const +{ + return m_propertyMask; +} + +void BreakLayoutCommand::init(const QWidgetList &widgets, QWidget *layoutBase, bool reparentLayoutWidget) +{ + enum Type { SplitterLayout, LayoutHasMarginSpacing, LayoutHasState }; + + const QDesignerFormEditorInterface *core = formWindow()->core(); + m_widgets = widgets; + m_layoutBase = core->widgetFactory()->containerOfWidget(layoutBase); + QLayout *layoutToBeBroken; + const LayoutInfo::Type layoutType = LayoutInfo::managedLayoutType(core, m_layoutBase, &layoutToBeBroken); + m_layout = Layout::createLayout(widgets, m_layoutBase, formWindow(), layoutBase, layoutType); + m_layout->setReparentLayoutWidget(reparentLayoutWidget); + + Type type = LayoutHasState; + switch (layoutType) { + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + type = SplitterLayout; + break; + case LayoutInfo::HBox: + case LayoutInfo::VBox: // Margin/spacing need to be saved + type = LayoutHasMarginSpacing; + break; + default: // Margin/spacing need to be saved + has a state (empty rows/columns of a grid) + type = LayoutHasState; + break; + } + Q_ASSERT(m_layout != 0); + m_layout->sort(); + + + if (type >= LayoutHasMarginSpacing) { + m_properties = new LayoutProperties; + m_propertyMask = m_properties->fromPropertySheet(core, layoutToBeBroken, LayoutProperties::AllProperties); + } + if (type >= LayoutHasState) + m_layoutHelper = LayoutHelper::createLayoutHelper(layoutType); + m_cursorSelectionState.save(formWindow()); +} + +void BreakLayoutCommand::redo() +{ + if (!m_layout) + return; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QWidget *lb = m_layout->layoutBaseWidget(); + QDesignerLayoutDecorationExtension *deco = qt_extension<QDesignerLayoutDecorationExtension*>(core->extensionManager(), lb); + formWindow()->clearSelection(false); + if (m_layoutHelper) + m_layoutHelper->pushState(core, m_layoutBase); + m_layout->breakLayout(); + delete deco; // release the extension + + foreach (QWidget *widget, m_widgets) { + widget->resize(widget->size().expandedTo(QSize(16, 16))); + } + // Update unless we are in an intermediate state of morphing layout + // in which a QLayoutWidget will have no layout at all. + if (m_layout->reparentLayoutWidget()) + core->objectInspector()->setFormWindow(formWindow()); +} + +void BreakLayoutCommand::undo() +{ + if (!m_layout) + return; + + formWindow()->clearSelection(false); + m_layout->doLayout(); + if (m_layoutHelper) + m_layoutHelper->popState(formWindow()->core(), m_layoutBase); + + QLayout *layoutToRestored = LayoutInfo::managedLayout(formWindow()->core(), m_layoutBase); + if (m_properties && m_layoutBase && layoutToRestored) + m_properties->toPropertySheet(formWindow()->core(), layoutToRestored, m_propertyMask); + m_cursorSelectionState.restore(formWindow()); + core()->objectInspector()->setFormWindow(formWindow()); +} +// ---- SimplifyLayoutCommand +SimplifyLayoutCommand::SimplifyLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Simplify Grid Layout"), formWindow), + m_area(0, 0, 32767, 32767), + m_layoutBase(0), + m_layoutHelper(0), + m_layoutSimplified(false) +{ +} + +SimplifyLayoutCommand::~SimplifyLayoutCommand() +{ + delete m_layoutHelper; +} + +bool SimplifyLayoutCommand::canSimplify(QDesignerFormEditorInterface *core, const QWidget *w, int *layoutType) +{ + if (!w) + return false; + QLayout *layout; + const LayoutInfo::Type type = LayoutInfo::managedLayoutType(core, w, &layout); + if (layoutType) + *layoutType = type; + if (!layout) + return false; + switch (type) { // Known negatives + case LayoutInfo::NoLayout: + case LayoutInfo::UnknownLayout: + case LayoutInfo::HSplitter: + case LayoutInfo::VSplitter: + case LayoutInfo::HBox: + case LayoutInfo::VBox: + return false; + default: + break; + } + switch (type) { + case LayoutInfo::Grid: + return QLayoutSupport::canSimplifyQuickCheck(qobject_cast<QGridLayout*>(layout)); + case LayoutInfo::Form: + return QLayoutSupport::canSimplifyQuickCheck(qobject_cast<const QFormLayout*>(layout)); + default: + break; + } + return false; +} + +bool SimplifyLayoutCommand::init(QWidget *layoutBase) +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + m_layoutSimplified = false; + int type; + if (canSimplify(core, layoutBase, &type)) { + m_layoutBase = layoutBase; + m_layoutHelper = LayoutHelper::createLayoutHelper(type); + m_layoutSimplified = m_layoutHelper->canSimplify(core, layoutBase, m_area); + } + return m_layoutSimplified; +} + +void SimplifyLayoutCommand::redo() +{ + const QDesignerFormEditorInterface *core = formWindow()->core(); + if (m_layoutSimplified) { + m_layoutHelper->pushState(core, m_layoutBase); + m_layoutHelper->simplify(core, m_layoutBase, m_area); + } +} +void SimplifyLayoutCommand::undo() +{ + if (m_layoutSimplified) + m_layoutHelper->popState(formWindow()->core(), m_layoutBase); +} + +// ---- ToolBoxCommand ---- +ToolBoxCommand::ToolBoxCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +ToolBoxCommand::~ToolBoxCommand() +{ +} + +void ToolBoxCommand::init(QToolBox *toolBox) +{ + m_toolBox = toolBox; + m_index = m_toolBox->currentIndex(); + m_widget = m_toolBox->widget(m_index); + m_itemText = m_toolBox->itemText(m_index); + m_itemIcon = m_toolBox->itemIcon(m_index); +} + +void ToolBoxCommand::removePage() +{ + m_toolBox->removeItem(m_index); + + m_widget->hide(); + m_widget->setParent(formWindow()); + formWindow()->clearSelection(); + formWindow()->selectWidget(m_toolBox, true); + +} + +void ToolBoxCommand::addPage() +{ + m_widget->setParent(m_toolBox); + m_toolBox->insertItem(m_index, m_widget, m_itemIcon, m_itemText); + m_toolBox->setCurrentIndex(m_index); + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(formWindow()->core()->extensionManager(), m_toolBox); + if (sheet) { + qdesigner_internal::PropertySheetStringValue itemText(m_itemText); + sheet->setProperty(sheet->indexOf(QLatin1String("currentItemText")), qVariantFromValue(itemText)); + } + + m_widget->show(); + formWindow()->clearSelection(); + formWindow()->selectWidget(m_toolBox, true); +} + +// ---- MoveToolBoxPageCommand ---- +MoveToolBoxPageCommand::MoveToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) : + ToolBoxCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveToolBoxPageCommand::~MoveToolBoxPageCommand() +{ +} + +void MoveToolBoxPageCommand::init(QToolBox *toolBox, QWidget *page, int newIndex) +{ + ToolBoxCommand::init(toolBox); + setText(QApplication::translate("Command", "Move Page")); + + m_widget = page; + m_oldIndex = m_toolBox->indexOf(m_widget); + m_itemText = m_toolBox->itemText(m_oldIndex); + m_itemIcon = m_toolBox->itemIcon(m_oldIndex); + m_newIndex = newIndex; +} + +void MoveToolBoxPageCommand::redo() +{ + m_toolBox->removeItem(m_oldIndex); + m_toolBox->insertItem(m_newIndex, m_widget, m_itemIcon, m_itemText); +} + +void MoveToolBoxPageCommand::undo() +{ + m_toolBox->removeItem(m_newIndex); + m_toolBox->insertItem(m_oldIndex, m_widget, m_itemIcon, m_itemText); +} + +// ---- DeleteToolBoxPageCommand ---- +DeleteToolBoxPageCommand::DeleteToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) + : ToolBoxCommand(formWindow) +{ +} + +DeleteToolBoxPageCommand::~DeleteToolBoxPageCommand() +{ +} + +void DeleteToolBoxPageCommand::init(QToolBox *toolBox) +{ + ToolBoxCommand::init(toolBox); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteToolBoxPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteToolBoxPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddToolBoxPageCommand ---- +AddToolBoxPageCommand::AddToolBoxPageCommand(QDesignerFormWindowInterface *formWindow) + : ToolBoxCommand(formWindow) +{ +} + +AddToolBoxPageCommand::~AddToolBoxPageCommand() +{ +} + +void AddToolBoxPageCommand::init(QToolBox *toolBox) +{ + init(toolBox, InsertBefore); +} + +void AddToolBoxPageCommand::init(QToolBox *toolBox, InsertionMode mode) +{ + m_toolBox = toolBox; + + m_index = m_toolBox->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_toolBox); + m_itemText = QApplication::translate("Command", "Page"); + m_itemIcon = QIcon(); + m_widget->setObjectName(QApplication::translate("Command", "page")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddToolBoxPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddToolBoxPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- TabWidgetCommand ---- +TabWidgetCommand::TabWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +TabWidgetCommand::~TabWidgetCommand() +{ +} + +void TabWidgetCommand::init(QTabWidget *tabWidget) +{ + m_tabWidget = tabWidget; + m_index = m_tabWidget->currentIndex(); + m_widget = m_tabWidget->widget(m_index); + m_itemText = m_tabWidget->tabText(m_index); + m_itemIcon = m_tabWidget->tabIcon(m_index); +} + +void TabWidgetCommand::removePage() +{ + m_tabWidget->removeTab(m_index); + + m_widget->hide(); + m_widget->setParent(formWindow()); + m_tabWidget->setCurrentIndex(qMin(m_index, m_tabWidget->count())); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_tabWidget, true); +} + +void TabWidgetCommand::addPage() +{ + m_widget->setParent(0); + m_tabWidget->insertTab(m_index, m_widget, m_itemIcon, m_itemText); + m_widget->show(); + m_tabWidget->setCurrentIndex(m_index); + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(formWindow()->core()->extensionManager(), m_tabWidget); + if (sheet) { + qdesigner_internal::PropertySheetStringValue itemText(m_itemText); + sheet->setProperty(sheet->indexOf(QLatin1String("currentTabText")), qVariantFromValue(itemText)); + } + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_tabWidget, true); +} + +// ---- DeleteTabPageCommand ---- +DeleteTabPageCommand::DeleteTabPageCommand(QDesignerFormWindowInterface *formWindow) + : TabWidgetCommand(formWindow) +{ +} + +DeleteTabPageCommand::~DeleteTabPageCommand() +{ +} + +void DeleteTabPageCommand::init(QTabWidget *tabWidget) +{ + TabWidgetCommand::init(tabWidget); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteTabPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteTabPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddTabPageCommand ---- +AddTabPageCommand::AddTabPageCommand(QDesignerFormWindowInterface *formWindow) + : TabWidgetCommand(formWindow) +{ +} + +AddTabPageCommand::~AddTabPageCommand() +{ +} + +void AddTabPageCommand::init(QTabWidget *tabWidget) +{ + init(tabWidget, InsertBefore); +} + +void AddTabPageCommand::init(QTabWidget *tabWidget, InsertionMode mode) +{ + m_tabWidget = tabWidget; + + m_index = m_tabWidget->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_tabWidget); + m_itemText = QApplication::translate("Command", "Page"); + m_itemIcon = QIcon(); + m_widget->setObjectName(QApplication::translate("Command", "tab")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddTabPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddTabPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- MoveTabPageCommand ---- +MoveTabPageCommand::MoveTabPageCommand(QDesignerFormWindowInterface *formWindow) : + TabWidgetCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveTabPageCommand::~MoveTabPageCommand() +{ +} + +void MoveTabPageCommand::init(QTabWidget *tabWidget, QWidget *page, + const QIcon &icon, const QString &label, + int index, int newIndex) +{ + TabWidgetCommand::init(tabWidget); + setText(QApplication::translate("Command", "Move Page")); + + m_page = page; + m_newIndex = newIndex; + m_oldIndex = index; + m_label = label; + m_icon = icon; +} + +void MoveTabPageCommand::redo() +{ + m_tabWidget->removeTab(m_oldIndex); + m_tabWidget->insertTab(m_newIndex, m_page, m_icon, m_label); + m_tabWidget->setCurrentIndex(m_newIndex); +} + +void MoveTabPageCommand::undo() +{ + m_tabWidget->removeTab(m_newIndex); + m_tabWidget->insertTab(m_oldIndex, m_page, m_icon, m_label); + m_tabWidget->setCurrentIndex(m_oldIndex); +} + +// ---- StackedWidgetCommand ---- +StackedWidgetCommand::StackedWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +StackedWidgetCommand::~StackedWidgetCommand() +{ +} + +void StackedWidgetCommand::init(QStackedWidget *stackedWidget) +{ + m_stackedWidget = stackedWidget; + m_index = m_stackedWidget->currentIndex(); + m_widget = m_stackedWidget->widget(m_index); +} + +void StackedWidgetCommand::removePage() +{ + m_stackedWidget->removeWidget(m_stackedWidget->widget(m_index)); + + m_widget->hide(); + m_widget->setParent(formWindow()); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_stackedWidget, true); +} + +void StackedWidgetCommand::addPage() +{ + m_stackedWidget->insertWidget(m_index, m_widget); + + m_widget->show(); + m_stackedWidget->setCurrentIndex(m_index); + + formWindow()->clearSelection(); + formWindow()->selectWidget(m_stackedWidget, true); +} + +// ---- MoveStackedWidgetCommand ---- +MoveStackedWidgetCommand::MoveStackedWidgetCommand(QDesignerFormWindowInterface *formWindow) : + StackedWidgetCommand(formWindow), + m_newIndex(-1), + m_oldIndex(-1) +{ +} + +MoveStackedWidgetCommand::~MoveStackedWidgetCommand() +{ +} + +void MoveStackedWidgetCommand::init(QStackedWidget *stackedWidget, QWidget *page, int newIndex) +{ + StackedWidgetCommand::init(stackedWidget); + setText(QApplication::translate("Command", "Move Page")); + + m_widget = page; + m_newIndex = newIndex; + m_oldIndex = m_stackedWidget->indexOf(m_widget); +} + +void MoveStackedWidgetCommand::redo() +{ + m_stackedWidget->removeWidget(m_widget); + m_stackedWidget->insertWidget(m_newIndex, m_widget); +} + +void MoveStackedWidgetCommand::undo() +{ + m_stackedWidget->removeWidget(m_widget); + m_stackedWidget->insertWidget(m_oldIndex, m_widget); +} + +// ---- DeleteStackedWidgetPageCommand ---- +DeleteStackedWidgetPageCommand::DeleteStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : StackedWidgetCommand(formWindow) +{ +} + +DeleteStackedWidgetPageCommand::~DeleteStackedWidgetPageCommand() +{ +} + +void DeleteStackedWidgetPageCommand::init(QStackedWidget *stackedWidget) +{ + StackedWidgetCommand::init(stackedWidget); + setText(QApplication::translate("Command", "Delete Page")); +} + +void DeleteStackedWidgetPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteStackedWidgetPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddStackedWidgetPageCommand ---- +AddStackedWidgetPageCommand::AddStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : StackedWidgetCommand(formWindow) +{ +} + +AddStackedWidgetPageCommand::~AddStackedWidgetPageCommand() +{ +} + +void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget) +{ + init(stackedWidget, InsertBefore); +} + +void AddStackedWidgetPageCommand::init(QStackedWidget *stackedWidget, InsertionMode mode) +{ + m_stackedWidget = stackedWidget; + + m_index = m_stackedWidget->currentIndex(); + if (mode == InsertAfter) + m_index++; + m_widget = new QDesignerWidget(formWindow(), m_stackedWidget); + m_widget->setObjectName(QApplication::translate("Command", "page")); + formWindow()->ensureUniqueObjectName(m_widget); + + setText(QApplication::translate("Command", "Insert Page")); + + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_widget); +} + +void AddStackedWidgetPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddStackedWidgetPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +// ---- TabOrderCommand ---- +TabOrderCommand::TabOrderCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tab order"), formWindow), + m_widgetItem(0) +{ +} + +void TabOrderCommand::init(const QList<QWidget*> &newTabOrder) +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + Q_ASSERT(core); + + m_widgetItem = core->metaDataBase()->item(formWindow()); + Q_ASSERT(m_widgetItem); + m_oldTabOrder = m_widgetItem->tabOrder(); + m_newTabOrder = newTabOrder; +} + +void TabOrderCommand::redo() +{ + m_widgetItem->setTabOrder(m_newTabOrder); +} + +void TabOrderCommand::undo() +{ + m_widgetItem->setTabOrder(m_oldTabOrder); +} + +// ---- CreateMenuBarCommand ---- +CreateMenuBarCommand::CreateMenuBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Menu Bar"), formWindow) +{ +} + +void CreateMenuBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_menuBar = qobject_cast<QMenuBar*>(core->widgetFactory()->createWidget(QLatin1String("QMenuBar"), m_mainWindow)); + core->widgetFactory()->initialize(m_menuBar); +} + +void CreateMenuBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + c->addWidget(m_menuBar); + + m_menuBar->setObjectName(QLatin1String("menuBar")); + formWindow()->ensureUniqueObjectName(m_menuBar); + core->metaDataBase()->add(m_menuBar); + formWindow()->emitSelectionChanged(); + m_menuBar->setFocus(); +} + +void CreateMenuBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_menuBar) { + c->remove(i); + break; + } + } + + core->metaDataBase()->remove(m_menuBar); + formWindow()->emitSelectionChanged(); +} + +// ---- DeleteMenuBarCommand ---- +DeleteMenuBarCommand::DeleteMenuBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Menu Bar"), formWindow) +{ +} + +void DeleteMenuBarCommand::init(QMenuBar *menuBar) +{ + m_menuBar = menuBar; + m_mainWindow = qobject_cast<QMainWindow*>(menuBar->parentWidget()); +} + +void DeleteMenuBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; i<c->count(); ++i) { + if (c->widget(i) == m_menuBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_menuBar); + m_menuBar->hide(); + m_menuBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteMenuBarCommand::undo() +{ + if (m_mainWindow) { + m_menuBar->setParent(m_mainWindow); + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_menuBar); + + core()->metaDataBase()->add(m_menuBar); + m_menuBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +// ---- CreateStatusBarCommand ---- +CreateStatusBarCommand::CreateStatusBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Create Status Bar"), formWindow) +{ +} + +void CreateStatusBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_statusBar = qobject_cast<QStatusBar*>(core->widgetFactory()->createWidget(QLatin1String("QStatusBar"), m_mainWindow)); + core->widgetFactory()->initialize(m_statusBar); +} + +void CreateStatusBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + c->addWidget(m_statusBar); + + m_statusBar->setObjectName(QLatin1String("statusBar")); + formWindow()->ensureUniqueObjectName(m_statusBar); + core->metaDataBase()->add(m_statusBar); + formWindow()->emitSelectionChanged(); +} + +void CreateStatusBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_statusBar) { + c->remove(i); + break; + } + } + + core->metaDataBase()->remove(m_statusBar); + formWindow()->emitSelectionChanged(); +} + +// ---- DeleteStatusBarCommand ---- +DeleteStatusBarCommand::DeleteStatusBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Status Bar"), formWindow) +{ +} + +void DeleteStatusBarCommand::init(QStatusBar *statusBar) +{ + m_statusBar = statusBar; + m_mainWindow = qobject_cast<QMainWindow*>(statusBar->parentWidget()); +} + +void DeleteStatusBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; i<c->count(); ++i) { + if (c->widget(i) == m_statusBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_statusBar); + m_statusBar->hide(); + m_statusBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteStatusBarCommand::undo() +{ + if (m_mainWindow) { + m_statusBar->setParent(m_mainWindow); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_statusBar); + + core()->metaDataBase()->add(m_statusBar); + m_statusBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +// ---- AddToolBarCommand ---- +AddToolBarCommand::AddToolBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Tool Bar"), formWindow) +{ +} + +void AddToolBarCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerWidgetFactoryInterface * wf = formWindow()->core()->widgetFactory(); + // Pass on 0 parent first to avoid reparenting flicker. + m_toolBar = qobject_cast<QToolBar*>(wf->createWidget(QLatin1String("QToolBar"), 0)); + wf->initialize(m_toolBar); + m_toolBar->hide(); +} + +void AddToolBarCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->add(m_toolBar); + + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + c->addWidget(m_toolBar); + + m_toolBar->setObjectName(QLatin1String("toolBar")); + formWindow()->ensureUniqueObjectName(m_toolBar); + setPropertySheetWindowTitle(core, m_toolBar, m_toolBar->objectName()); + formWindow()->emitSelectionChanged(); +} + +void AddToolBarCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->remove(m_toolBar); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_toolBar) { + c->remove(i); + break; + } + } + formWindow()->emitSelectionChanged(); +} + +// ---- DockWidgetCommand:: ---- +DockWidgetCommand::DockWidgetCommand(const QString &description, QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(description, formWindow) +{ +} + +DockWidgetCommand::~DockWidgetCommand() +{ +} + +void DockWidgetCommand::init(QDockWidget *dockWidget) +{ + m_dockWidget = dockWidget; +} + +// ---- AddDockWidgetCommand ---- +AddDockWidgetCommand::AddDockWidgetCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Add Dock Window"), formWindow) +{ +} + +void AddDockWidgetCommand::init(QMainWindow *mainWindow, QDockWidget *dockWidget) +{ + m_mainWindow = mainWindow; + m_dockWidget = dockWidget; +} + +void AddDockWidgetCommand::init(QMainWindow *mainWindow) +{ + m_mainWindow = mainWindow; + QDesignerFormEditorInterface *core = formWindow()->core(); + m_dockWidget = qobject_cast<QDockWidget*>(core->widgetFactory()->createWidget(QLatin1String("QDockWidget"), m_mainWindow)); +} + +void AddDockWidgetCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + c->addWidget(m_dockWidget); + + m_dockWidget->setObjectName(QLatin1String("dockWidget")); + formWindow()->ensureUniqueObjectName(m_dockWidget); + formWindow()->manageWidget(m_dockWidget); + formWindow()->emitSelectionChanged(); +} + +void AddDockWidgetCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), m_mainWindow); + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == m_dockWidget) { + c->remove(i); + break; + } + } + + formWindow()->unmanageWidget(m_dockWidget); + formWindow()->emitSelectionChanged(); +} + +// ---- AdjustWidgetSizeCommand ---- +AdjustWidgetSizeCommand::AdjustWidgetSizeCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +void AdjustWidgetSizeCommand::init(QWidget *widget) +{ + m_widget = widget; + setText(QApplication::translate("Command", "Adjust Size of '%1'").arg(widget->objectName())); +} + +QWidget *AdjustWidgetSizeCommand::widgetForAdjust() const +{ + QDesignerFormWindowInterface *fw = formWindow(); + // Return the outer, embedding widget if it is the main container + if (Utils::isCentralWidget(fw, m_widget)) + return fw->core()->integration()->containerWindow(m_widget); + return m_widget; +} + +void AdjustWidgetSizeCommand::redo() +{ + QWidget *aw = widgetForAdjust(); + m_geometry = aw->geometry(); + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + aw->adjustSize(); + const bool isMainContainer = aw != m_widget; + if (!isMainContainer) { + /* When doing adjustsize on a selected non-laid out child that has been enlarged + * and pushed partially over the top/left edge[s], it is possible that it "disappears" + * when shrinking. In that case, move it back so that it remains visible. */ + if (aw->parentWidget()->layout() == 0) { + const QRect contentsRect = aw->parentWidget()->contentsRect(); + const QRect newGeometry = aw->geometry(); + QPoint newPos = m_geometry.topLeft(); + if (newGeometry.bottom() <= contentsRect.y()) + newPos.setY(contentsRect.y()); + if (newGeometry.right() <= contentsRect.x()) + newPos.setX(contentsRect.x()); + if (newPos != m_geometry.topLeft()) + aw->move(newPos); + } + } + updatePropertyEditor(); +} + +void AdjustWidgetSizeCommand::undo() +{ + QWidget *aw = widgetForAdjust(); + aw->resize(m_geometry.size()); + if (m_geometry.topLeft() != aw->geometry().topLeft()) + aw->move(m_geometry.topLeft()); + updatePropertyEditor(); +} + +void AdjustWidgetSizeCommand::updatePropertyEditor() const +{ + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == m_widget) + propertyEditor->setPropertyValue(QLatin1String("geometry"), m_widget->geometry(), true); + } +} +// ------------ ChangeFormLayoutItemRoleCommand + +ChangeFormLayoutItemRoleCommand::ChangeFormLayoutItemRoleCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Change Form Layout Item Geometry"), formWindow), + m_operation(SpanningToLabel) +{ +} + +void ChangeFormLayoutItemRoleCommand::init(QWidget *widget, Operation op) +{ + m_widget = widget; + m_operation = op; +} + +void ChangeFormLayoutItemRoleCommand::redo() +{ + doOperation(m_operation); +} + +void ChangeFormLayoutItemRoleCommand::undo() +{ + doOperation(reverseOperation(m_operation)); +} + +ChangeFormLayoutItemRoleCommand::Operation ChangeFormLayoutItemRoleCommand::reverseOperation(Operation op) +{ + switch (op) { + case SpanningToLabel: + return LabelToSpanning; + case SpanningToField: + return FieldToSpanning; + case LabelToSpanning: + return SpanningToLabel; + case FieldToSpanning: + return SpanningToField; + } + return SpanningToField; +} + +void ChangeFormLayoutItemRoleCommand::doOperation(Operation op) +{ + QFormLayout *fl = ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(formWindow()->core(), m_widget); + const int index = fl->indexOf(m_widget); + Q_ASSERT(index != -1); + int row; + QFormLayout::ItemRole role; + fl->getItemPosition (index, &row, &role); + Q_ASSERT(index != -1); + QLayoutItem *item = fl->takeAt(index); + const QRect area = QRect(0, row, 2, 1); + switch (op) { + case SpanningToLabel: + fl->setItem(row, QFormLayout::LabelRole, item); + QLayoutSupport::createEmptyCells(fl); + break; + case SpanningToField: + fl->setItem(row, QFormLayout::FieldRole, item); + QLayoutSupport::createEmptyCells(fl); + break; + case LabelToSpanning: + case FieldToSpanning: + QLayoutSupport::removeEmptyCells(fl, area); + fl->setItem(row, QFormLayout::SpanningRole, item); + break; + } +} + +unsigned ChangeFormLayoutItemRoleCommand::possibleOperations(QDesignerFormEditorInterface *core, QWidget *w) +{ + QFormLayout *fl = managedFormLayoutOf(core, w); + if (!fl) + return 0; + const int index = fl->indexOf(w); + if (index == -1) + return 0; + int row, col, colspan; + getFormLayoutItemPosition(fl, index, &row, &col, 0, &colspan); + // Spanning item? + if (colspan > 1) + return SpanningToLabel|SpanningToField; + // Is the neighbouring column free, that is, can the current item be expanded? + const QFormLayout::ItemRole neighbouringRole = col == 0 ? QFormLayout::FieldRole : QFormLayout::LabelRole; + const bool empty = LayoutInfo::isEmptyItem(fl->itemAt(row, neighbouringRole)); + if (empty) + return col == 0 ? LabelToSpanning : FieldToSpanning; + return 0; +} + +QFormLayout *ChangeFormLayoutItemRoleCommand::managedFormLayoutOf(QDesignerFormEditorInterface *core, QWidget *w) +{ + if (QLayout *layout = LayoutInfo::managedLayout(core, w->parentWidget())) + if (QFormLayout *fl = qobject_cast<QFormLayout *>(layout)) + return fl; + return 0; +} + +// ---- ChangeLayoutItemGeometry ---- +ChangeLayoutItemGeometry::ChangeLayoutItemGeometry(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Layout Item Geometry"), formWindow) +{ +} + +void ChangeLayoutItemGeometry::init(QWidget *widget, int row, int column, int rowspan, int colspan) +{ + m_widget = widget; + Q_ASSERT(m_widget->parentWidget() != 0); + + QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget()); + Q_ASSERT(layout != 0); + + QGridLayout *grid = qobject_cast<QGridLayout*>(layout); + Q_ASSERT(grid != 0); + + const int itemIndex = grid->indexOf(m_widget); + Q_ASSERT(itemIndex != -1); + + int current_row, current_column, current_rowspan, current_colspan; + grid->getItemPosition(itemIndex, ¤t_row, ¤t_column, ¤t_rowspan, ¤t_colspan); + + m_oldInfo.setRect(current_column, current_row, current_colspan, current_rowspan); + m_newInfo.setRect(column, row, colspan, rowspan); +} + +void ChangeLayoutItemGeometry::changeItemPosition(const QRect &g) +{ + QLayout *layout = LayoutInfo::managedLayout(formWindow()->core(), m_widget->parentWidget()); + Q_ASSERT(layout != 0); + + QGridLayout *grid = qobject_cast<QGridLayout*>(layout); + Q_ASSERT(grid != 0); + + const int itemIndex = grid->indexOf(m_widget); + Q_ASSERT(itemIndex != -1); + + QLayoutItem *item = grid->takeAt(itemIndex); + delete item; + + if (!QLayoutSupport::removeEmptyCells(grid, g)) + qWarning() << "ChangeLayoutItemGeometry::changeItemPosition: Nonempty cell at " << g << '.'; + + grid->addWidget(m_widget, g.top(), g.left(), g.height(), g.width()); + + grid->invalidate(); + grid->activate(); + + QLayoutSupport::createEmptyCells(grid); + + formWindow()->clearSelection(false); + formWindow()->selectWidget(m_widget, true); +} + +void ChangeLayoutItemGeometry::redo() +{ + changeItemPosition(m_newInfo); +} + +void ChangeLayoutItemGeometry::undo() +{ + changeItemPosition(m_oldInfo); +} + +// ---- ContainerWidgetCommand ---- +ContainerWidgetCommand::ContainerWidgetCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_index(-1) +{ +} + +ContainerWidgetCommand::~ContainerWidgetCommand() +{ +} + +QDesignerContainerExtension *ContainerWidgetCommand::containerExtension() const +{ + QExtensionManager *mgr = core()->extensionManager(); + return qt_extension<QDesignerContainerExtension*>(mgr, m_containerWidget); +} + +void ContainerWidgetCommand::init(QWidget *containerWidget) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_index = c->currentIndex(); + m_widget = c->widget(m_index); + } +} + +void ContainerWidgetCommand::removePage() +{ + if (QDesignerContainerExtension *c = containerExtension()) { + if (const int count = c->count()) { + // Undo add after last? + const int deleteIndex = m_index >= 0 ? m_index : count - 1; + c->remove(deleteIndex); + m_widget->hide(); + m_widget->setParent(formWindow()); + } + } +} + +void ContainerWidgetCommand::addPage() +{ + if (QDesignerContainerExtension *c = containerExtension()) { + int newCurrentIndex; + if (m_index >= 0) { + c->insertWidget(m_index, m_widget); + newCurrentIndex = m_index; + } else { + c->addWidget(m_widget); + newCurrentIndex = c->count() -1 ; + } + m_widget->show(); + c->setCurrentIndex(newCurrentIndex); + } +} + +// ---- DeleteContainerWidgetPageCommand ---- +DeleteContainerWidgetPageCommand::DeleteContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : ContainerWidgetCommand(formWindow) +{ +} + +DeleteContainerWidgetPageCommand::~DeleteContainerWidgetPageCommand() +{ +} + +void DeleteContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct) +{ + ContainerWidgetCommand::init(containerWidget); + switch (ct) { + case WizardContainer: + case PageContainer: + setText(QApplication::translate("Command", "Delete Page")); + break; + case MdiContainer: + setText(QApplication::translate("Command", "Delete Subwindow")); + break; + } +} + +void DeleteContainerWidgetPageCommand::redo() +{ + removePage(); + cheapUpdate(); +} + +void DeleteContainerWidgetPageCommand::undo() +{ + addPage(); + cheapUpdate(); +} + +// ---- AddContainerWidgetPageCommand ---- +AddContainerWidgetPageCommand::AddContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow) + : ContainerWidgetCommand(formWindow) +{ +} + +AddContainerWidgetPageCommand::~AddContainerWidgetPageCommand() +{ +} + +void AddContainerWidgetPageCommand::init(QWidget *containerWidget, ContainerType ct, InsertionMode mode) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_index = c->currentIndex(); + if (m_index >= 0 && mode == InsertAfter) + m_index++; + m_widget = 0; + const QDesignerFormEditorInterface *core = formWindow()->core(); + switch (ct) { + case PageContainer: + setText(QApplication::translate("Command", "Insert Page")); + m_widget = new QDesignerWidget(formWindow(), m_containerWidget); + m_widget->setObjectName(QApplication::translate("Command", "page")); + break; + case MdiContainer: + setText(QApplication::translate("Command", "Insert Subwindow")); + m_widget = new QDesignerWidget(formWindow(), m_containerWidget); + m_widget->setObjectName(QApplication::translate("Command", "subwindow")); + setPropertySheetWindowTitle(core, m_widget, QApplication::translate("Command", "Subwindow")); + break; + case WizardContainer: // Apply style, don't manage + m_widget = core->widgetFactory()->createWidget(QLatin1String("QWizardPage"), 0); + break; + } + formWindow()->ensureUniqueObjectName(m_widget); + core->metaDataBase()->add(m_widget); + } +} + +void AddContainerWidgetPageCommand::redo() +{ + addPage(); + cheapUpdate(); +} + +void AddContainerWidgetPageCommand::undo() +{ + removePage(); + cheapUpdate(); +} + +ChangeCurrentPageCommand::ChangeCurrentPageCommand(QDesignerFormWindowInterface *formWindow) + : + QDesignerFormWindowCommand(QString(), formWindow), m_oldIndex(0), m_newIndex(0) +{ +} + +ChangeCurrentPageCommand::~ChangeCurrentPageCommand() +{ +} + +QDesignerContainerExtension *ChangeCurrentPageCommand::containerExtension() const +{ + QExtensionManager *mgr = core()->extensionManager(); + return qt_extension<QDesignerContainerExtension*>(mgr, m_containerWidget); +} + +void ChangeCurrentPageCommand::init(QWidget *containerWidget, int newIndex) +{ + m_containerWidget = containerWidget; + + if (QDesignerContainerExtension *c = containerExtension()) { + m_newIndex = newIndex; + m_oldIndex = c->currentIndex(); + m_widget = c->widget(m_oldIndex); + } +} + +void ChangeCurrentPageCommand::redo() +{ + containerExtension()->setCurrentIndex(m_newIndex); +} + +void ChangeCurrentPageCommand::undo() +{ + containerExtension()->setCurrentIndex(m_oldIndex); +} + +static int itemRoles[] = { + Qt::DecorationPropertyRole, + Qt::DisplayPropertyRole, + Qt::ToolTipPropertyRole, + Qt::StatusTipPropertyRole, + Qt::WhatsThisPropertyRole, + Qt::FontRole, + Qt::TextAlignmentRole, + Qt::BackgroundRole, + Qt::ForegroundRole, + Qt::CheckStateRole, + -1 +}; + +template<class T> +static void copyRoleFromItem(ItemData *id, int role, const T *item) +{ + QVariant v = item->data(role); + if (v.isValid()) + id->m_properties.insert(role, v); +} + +template<class T> +static void copyRolesFromItem(ItemData *id, const T *item, bool editor) +{ + static const int defaultFlags = T().flags(); + + for (int i = 0; itemRoles[i] != -1; i++) + copyRoleFromItem<T>(id, itemRoles[i], item); + + if (editor) + copyRoleFromItem<T>(id, ItemFlagsShadowRole, item); + else if (item->flags() != defaultFlags) + id->m_properties.insert(ItemFlagsShadowRole, qVariantFromValue((int)item->flags())); +} + +template<class T> +static void copyRolesToItem(const ItemData *id, T *item, DesignerIconCache *iconCache, bool editor) +{ + QHash<int, QVariant>::const_iterator it = id->m_properties.constBegin(), + end = id->m_properties.constEnd(); + for (; it != end; ++it) + if (it.value().isValid()) { + if (!editor && it.key() == ItemFlagsShadowRole) { + item->setFlags((Qt::ItemFlags)it.value().toInt()); + } else { + item->setData(it.key(), it.value()); + switch (it.key()) { + case Qt::DecorationPropertyRole: + if (iconCache) + item->setIcon(iconCache->icon(qVariantValue<PropertySheetIconValue>(it.value()))); + break; + case Qt::DisplayPropertyRole: + item->setText(qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::ToolTipPropertyRole: + item->setToolTip(qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::StatusTipPropertyRole: + item->setStatusTip(qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::WhatsThisPropertyRole: + item->setWhatsThis(qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + } + } + } + + if (editor) + item->setFlags(item->flags() | Qt::ItemIsEditable); +} + +ItemData::ItemData(const QListWidgetItem *item, bool editor) +{ + copyRolesFromItem<QListWidgetItem>(this, item, editor); +} + +QListWidgetItem *ItemData::createListItem(DesignerIconCache *iconCache, bool editor) const +{ + QListWidgetItem *item = new QListWidgetItem(); + copyRolesToItem(this, item, iconCache, editor); + return item; +} + +ItemData::ItemData(const QTableWidgetItem *item, bool editor) +{ + copyRolesFromItem(this, item, editor); +} + +QTableWidgetItem *ItemData::createTableItem(DesignerIconCache *iconCache, bool editor) const +{ + QTableWidgetItem *item = new QTableWidgetItem; + copyRolesToItem(this, item, iconCache, editor); + return item; +} + +static void copyRoleFromItem(ItemData *id, int role, const QTreeWidgetItem *item, int column) +{ + QVariant v = item->data(column, role); + if (v.isValid()) + id->m_properties.insert(role, v); +} + +ItemData::ItemData(const QTreeWidgetItem *item, int column) +{ + copyRoleFromItem(this, Qt::EditRole, item, column); + PropertySheetStringValue str(item->text(column)); + m_properties.insert(Qt::DisplayPropertyRole, qVariantFromValue(str)); + + for (int i = 0; itemRoles[i] != -1; i++) + copyRoleFromItem(this, itemRoles[i], item, column); +} + +void ItemData::fillTreeItemColumn(QTreeWidgetItem *item, int column, DesignerIconCache *iconCache) const +{ + QHash<int, QVariant>::const_iterator it = m_properties.constBegin(), end = m_properties.constEnd(); + for (; it != end; ++it) + if (it.value().isValid()) { + item->setData(column, it.key(), it.value()); + switch (it.key()) { + case Qt::DecorationPropertyRole: + if (iconCache) + item->setIcon(column, iconCache->icon(qVariantValue<PropertySheetIconValue>(it.value()))); + break; + case Qt::DisplayPropertyRole: + item->setText(column, qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::ToolTipPropertyRole: + item->setToolTip(column, qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::StatusTipPropertyRole: + item->setStatusTip(column, qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + case Qt::WhatsThisPropertyRole: + item->setWhatsThis(column, qVariantValue<PropertySheetStringValue>(it.value()).value()); + break; + } + } +} + +ListContents::ListContents(const QTreeWidgetItem *item) +{ + for (int i = 0; i < item->columnCount(); i++) + m_items.append(ItemData(item, i)); +} + +QTreeWidgetItem *ListContents::createTreeItem(DesignerIconCache *iconCache) const +{ + QTreeWidgetItem *item = new QTreeWidgetItem; + int i = 0; + foreach (const ItemData &id, m_items) + id.fillTreeItemColumn(item, i++, iconCache); + return item; +} + +void ListContents::createFromListWidget(const QListWidget *listWidget, bool editor) +{ + m_items.clear(); + + for (int i = 0; i < listWidget->count(); i++) + m_items.append(ItemData(listWidget->item(i), editor)); +} + +void ListContents::applyToListWidget(QListWidget *listWidget, DesignerIconCache *iconCache, bool editor) const +{ + listWidget->clear(); + + int i = 0; + foreach (const ItemData &entry, m_items) { + if (!entry.isValid()) + new QListWidgetItem(TableWidgetContents::defaultHeaderText(i), listWidget); + else + listWidget->addItem(entry.createListItem(iconCache, editor)); + i++; + } +} + +void ListContents::createFromComboBox(const QComboBox *comboBox) +{ + m_items.clear(); + + const int count = comboBox->count(); + for (int i = 0; i < count; i++) { + // We might encounter items added in a custom combo + // constructor. Ignore those. + const QVariant textValue = comboBox->itemData(i, Qt::DisplayPropertyRole); + if (!textValue.isNull()) { + ItemData entry; + entry.m_properties.insert(Qt::DisplayPropertyRole, textValue); + const QVariant iconValue = comboBox->itemData(i, Qt::DecorationPropertyRole); + if (!iconValue.isNull()) + entry.m_properties.insert(Qt::DecorationPropertyRole, iconValue); + m_items.append(entry); + } + } +} + +void ListContents::applyToComboBox(QComboBox *comboBox, DesignerIconCache *iconCache) const +{ + comboBox->clear(); + + foreach (const ItemData &hash, m_items) { + QIcon icon; + if (iconCache) + icon = iconCache->icon(qVariantValue<PropertySheetIconValue>( + hash.m_properties[Qt::DecorationPropertyRole])); + QVariant var = hash.m_properties[Qt::DisplayPropertyRole]; + PropertySheetStringValue str = qVariantValue<PropertySheetStringValue>(var); + comboBox->addItem(icon, str.value()); + comboBox->setItemData(comboBox->count() - 1, + var, + Qt::DisplayPropertyRole); + comboBox->setItemData(comboBox->count() - 1, + hash.m_properties[Qt::DecorationPropertyRole], + Qt::DecorationPropertyRole); + } +} + +// --------- TableWidgetContents + +TableWidgetContents::TableWidgetContents() : + m_columnCount(0), + m_rowCount(0) +{ +} + +void TableWidgetContents::clear() +{ + m_horizontalHeader.m_items.clear(); + m_verticalHeader.m_items.clear(); + m_items.clear(); + m_columnCount = 0; + m_rowCount = 0; +} + +QString TableWidgetContents::defaultHeaderText(int i) +{ + return QString::number(i + 1); +} + +bool TableWidgetContents::nonEmpty(const QTableWidgetItem *item, int headerColumn) +{ + static int defaultFlags = QTableWidgetItem().flags(); + + if (item->flags() != defaultFlags) + return true; + + QString text = qVariantValue<PropertySheetStringValue>(item->data(Qt::DisplayPropertyRole)).value(); + if (!text.isEmpty()) { + if (headerColumn < 0 || text != defaultHeaderText(headerColumn)) + return true; + } else { + // FIXME: This doesn't seem to make sense + return true; + } + + for (int i = 0; itemRoles[i] != -1; i++) + if (itemRoles[i] != Qt::DisplayPropertyRole && item->data(itemRoles[i]).isValid()) + return true; + + return false; +} + +void TableWidgetContents::insertHeaderItem(const QTableWidgetItem *item, int i, ListContents *header, bool editor) +{ + if (nonEmpty(item, i)) + header->m_items.append(ItemData(item, editor)); + else + header->m_items.append(ItemData()); +} + +void TableWidgetContents::fromTableWidget(const QTableWidget *tableWidget, bool editor) +{ + clear(); + m_columnCount = tableWidget->columnCount(); + m_rowCount = tableWidget->rowCount(); + // horiz header: Legacy behaviour: auto-generate number for empty items + for (int col = 0; col < m_columnCount; col++) + if (const QTableWidgetItem *item = tableWidget->horizontalHeaderItem(col)) + insertHeaderItem(item, col, &m_horizontalHeader, editor); + // vertical header: Legacy behaviour: auto-generate number for empty items + for (int row = 0; row < m_rowCount; row++) + if (const QTableWidgetItem *item = tableWidget->verticalHeaderItem(row)) + insertHeaderItem(item, row, &m_verticalHeader, editor); + // cell data + for (int col = 0; col < m_columnCount; col++) + for (int row = 0; row < m_rowCount; row++) + if (const QTableWidgetItem *item = tableWidget->item(row, col)) + if (nonEmpty(item, -1)) + m_items.insert(CellRowColumnAddress(row, col), ItemData(item, editor)); +} + +void TableWidgetContents::applyToTableWidget(QTableWidget *tableWidget, DesignerIconCache *iconCache, bool editor) const +{ + tableWidget->clear(); + + tableWidget->setColumnCount(m_columnCount); + tableWidget->setRowCount(m_rowCount); + + // horiz header + int col = 0; + foreach (const ItemData &id, m_horizontalHeader.m_items) { + if (id.isValid()) + tableWidget->setHorizontalHeaderItem(col, id.createTableItem(iconCache, editor)); + col++; + } + // vertical header + int row = 0; + foreach (const ItemData &id, m_verticalHeader.m_items) { + if (id.isValid()) + tableWidget->setVerticalHeaderItem(row, id.createTableItem(iconCache, editor)); + row++; + } + // items + const TableItemMap::const_iterator icend = m_items.constEnd(); + for (TableItemMap::const_iterator it = m_items.constBegin(); it != icend; ++ it) + tableWidget->setItem(it.key().first, it.key().second, it.value().createTableItem(iconCache, editor)); +} + +bool TableWidgetContents::operator==(const TableWidgetContents &rhs) const +{ + if (m_columnCount != rhs.m_columnCount || m_rowCount != rhs.m_rowCount) + return false; + + return m_horizontalHeader.m_items == rhs.m_horizontalHeader.m_items && + m_verticalHeader.m_items == rhs.m_verticalHeader.m_items && + m_items == rhs.m_items; +} + +// ---- ChangeTableContentsCommand ---- +ChangeTableContentsCommand::ChangeTableContentsCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Change Table Contents"), + formWindow), m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeTableContentsCommand::init(QTableWidget *tableWidget, + const TableWidgetContents &oldCont, const TableWidgetContents &newCont) +{ + m_tableWidget = tableWidget; + m_oldContents = oldCont; + m_newContents = newCont; +} + +void ChangeTableContentsCommand::redo() +{ + m_newContents.applyToTableWidget(m_tableWidget, m_iconCache, false); + QMetaObject::invokeMethod(m_tableWidget, "updateGeometries"); +} + +void ChangeTableContentsCommand::undo() +{ + m_oldContents.applyToTableWidget(m_tableWidget, m_iconCache, false); + QMetaObject::invokeMethod(m_tableWidget, "updateGeometries"); +} + +// --------- TreeWidgetContents +TreeWidgetContents::ItemContents::ItemContents(const QTreeWidgetItem *item, bool editor) : + ListContents(item) +{ + static const int defaultFlags = QTreeWidgetItem().flags(); + + if (editor) { + QVariant v = item->data(0, ItemFlagsShadowRole); + m_itemFlags = v.isValid() ? v.toInt() : -1; + } else { + m_itemFlags = (item->flags() != defaultFlags) ? (int)item->flags() : -1; + } + + for (int i = 0; i < item->childCount(); i++) + m_children.append(ItemContents(item->child(i), editor)); +} + +QTreeWidgetItem *TreeWidgetContents::ItemContents::createTreeItem(DesignerIconCache *iconCache, bool editor) const +{ + QTreeWidgetItem *item = ListContents::createTreeItem(iconCache); + + if (editor) + item->setFlags(item->flags() | Qt::ItemIsEditable); + + if (m_itemFlags != -1) { + if (editor) + item->setData(0, ItemFlagsShadowRole, qVariantFromValue(m_itemFlags)); + else + item->setFlags((Qt::ItemFlags)m_itemFlags); + } + + foreach (const ItemContents &ic, m_children) + item->addChild(ic.createTreeItem(iconCache, editor)); + + return item; +} + +bool TreeWidgetContents::ItemContents::operator==(const TreeWidgetContents::ItemContents &rhs) const +{ + return + m_itemFlags == rhs.m_itemFlags && + m_items == rhs.m_items && + m_children == rhs.m_children; +} + +void TreeWidgetContents::clear() +{ + m_headerItem.m_items.clear(); + m_rootItems.clear(); +} + +void TreeWidgetContents::fromTreeWidget(const QTreeWidget *treeWidget, bool editor) +{ + clear(); + m_headerItem = ListContents(treeWidget->headerItem()); + for (int col = 0; col < treeWidget->topLevelItemCount(); col++) + m_rootItems.append(ItemContents(treeWidget->topLevelItem(col), editor)); +} + +void TreeWidgetContents::applyToTreeWidget(QTreeWidget *treeWidget, DesignerIconCache *iconCache, bool editor) const +{ + treeWidget->clear(); + + treeWidget->setColumnCount(m_headerItem.m_items.count()); + treeWidget->setHeaderItem(m_headerItem.createTreeItem(iconCache)); + foreach (const ItemContents &ic, m_rootItems) + treeWidget->addTopLevelItem(ic.createTreeItem(iconCache, editor)); + treeWidget->expandAll(); +} + +bool TreeWidgetContents::operator==(const TreeWidgetContents &rhs) const +{ + return + m_headerItem == rhs.m_headerItem && + m_rootItems == rhs.m_rootItems; +} + +// ---- ChangeTreeContentsCommand ---- +ChangeTreeContentsCommand::ChangeTreeContentsCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Change Tree Contents"), formWindow), + m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeTreeContentsCommand::init(QTreeWidget *treeWidget, + const TreeWidgetContents &oldState, const TreeWidgetContents &newState) +{ + m_treeWidget = treeWidget; + m_oldState = oldState; + m_newState = newState; +} + +void ChangeTreeContentsCommand::redo() +{ + m_newState.applyToTreeWidget(m_treeWidget, m_iconCache, false); +} + +void ChangeTreeContentsCommand::undo() +{ + m_oldState.applyToTreeWidget(m_treeWidget, m_iconCache, false); +} + +// ---- ChangeListContentsCommand ---- +ChangeListContentsCommand::ChangeListContentsCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow), m_iconCache(0) +{ + FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow); + if (fwb) + m_iconCache = fwb->iconCache(); +} + +void ChangeListContentsCommand::init(QListWidget *listWidget, + const ListContents &oldItems, const ListContents &items) +{ + m_listWidget = listWidget; + m_comboBox = 0; + + m_newItemsState = items; + m_oldItemsState = oldItems; +} + +void ChangeListContentsCommand::init(QComboBox *comboBox, + const ListContents &oldItems, const ListContents &items) +{ + m_listWidget = 0; + m_comboBox = comboBox; + + m_newItemsState = items; + m_oldItemsState = oldItems; +} + +void ChangeListContentsCommand::redo() +{ + if (m_listWidget) + m_newItemsState.applyToListWidget(m_listWidget, m_iconCache, false); + else if (m_comboBox) + m_newItemsState.applyToComboBox(m_comboBox, m_iconCache); +} + +void ChangeListContentsCommand::undo() +{ + if (m_listWidget) + m_oldItemsState.applyToListWidget(m_listWidget, m_iconCache, false); + else if (m_comboBox) + m_oldItemsState.applyToComboBox(m_comboBox, m_iconCache); +} + +// ---- AddActionCommand ---- + +AddActionCommand::AddActionCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Add action"), formWindow) +{ + m_action = 0; +} + +void AddActionCommand::init(QAction *action) +{ + Q_ASSERT(m_action == 0); + m_action = action; +} + +void AddActionCommand::redo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->manageAction(m_action); +} + +void AddActionCommand::undo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->unmanageAction(m_action); +} + +// ---- RemoveActionCommand ---- + +RemoveActionCommand::RemoveActionCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Remove action"), formWindow), + m_action(0) +{ +} + +static RemoveActionCommand::ActionData findActionIn(QAction *action) +{ + RemoveActionCommand::ActionData result; + // We only want menus and toolbars, no toolbuttons. + foreach (QWidget *widget, action->associatedWidgets()) + if (qobject_cast<const QMenu *>(widget) || qobject_cast<const QToolBar *>(widget)) { + const QList<QAction*> actionList = widget->actions(); + const int size = actionList.size(); + for (int i = 0; i < size; ++i) { + if (actionList.at(i) == action) { + QAction *before = 0; + if (i + 1 < size) + before = actionList.at(i + 1); + result.append(RemoveActionCommand::ActionDataItem(before, widget)); + break; + } + } + } + return result; +} + +void RemoveActionCommand::init(QAction *action) +{ + Q_ASSERT(m_action == 0); + m_action = action; + + m_actionData = findActionIn(action); +} + +void RemoveActionCommand::redo() +{ + QDesignerFormWindowInterface *fw = formWindow(); + foreach (const ActionDataItem &item, m_actionData) { + item.widget->removeAction(m_action); + } + // Notify components (for example, signal slot editor) + if (qdesigner_internal::FormWindowBase *fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(fw)) + fwb->emitObjectRemoved(m_action); + + core()->actionEditor()->setFormWindow(fw); + core()->actionEditor()->unmanageAction(m_action); + if (!m_actionData.empty()) + core()->objectInspector()->setFormWindow(fw); +} + +void RemoveActionCommand::undo() +{ + core()->actionEditor()->setFormWindow(formWindow()); + core()->actionEditor()->manageAction(m_action); + foreach (const ActionDataItem &item, m_actionData) { + item.widget->insertAction(item.before, m_action); + } + if (!m_actionData.empty()) + core()->objectInspector()->setFormWindow(formWindow()); +} + +// ---- ActionInsertionCommand ---- + +ActionInsertionCommand::ActionInsertionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(text, formWindow), + m_parentWidget(0), + m_action(0), + m_beforeAction(0), + m_update(false) +{ +} + +void ActionInsertionCommand::init(QWidget *parentWidget, QAction *action, QAction *beforeAction, bool update) +{ + Q_ASSERT(m_parentWidget == 0); + Q_ASSERT(m_action == 0); + + m_parentWidget = parentWidget; + m_action = action; + m_beforeAction = beforeAction; + m_update = update; +} + +void ActionInsertionCommand::insertAction() +{ + Q_ASSERT(m_action != 0); + Q_ASSERT(m_parentWidget != 0); + + if (m_beforeAction) + m_parentWidget->insertAction(m_beforeAction, m_action); + else + m_parentWidget->addAction(m_action); + + if (m_update) { + cheapUpdate(); + if (QMenu *menu = m_action->menu()) + selectUnmanagedObject(menu); + else + selectUnmanagedObject(m_action); + PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor. + } +} +void ActionInsertionCommand::removeAction() +{ + Q_ASSERT(m_action != 0); + Q_ASSERT(m_parentWidget != 0); + + if (QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(m_parentWidget)) + menu->hideSubMenu(); + + m_parentWidget->removeAction(m_action); + + if (m_update) { + cheapUpdate(); + selectUnmanagedObject(m_parentWidget); + PropertyHelper::triggerActionChanged(m_action); // Update Used column in action editor. + } +} + +InsertActionIntoCommand::InsertActionIntoCommand(QDesignerFormWindowInterface *formWindow) : + ActionInsertionCommand(QApplication::translate("Command", "Add action"), formWindow) +{ +} +// ---- RemoveActionFromCommand ---- + +RemoveActionFromCommand::RemoveActionFromCommand(QDesignerFormWindowInterface *formWindow) : + ActionInsertionCommand(QApplication::translate("Command", "Remove action"), formWindow) +{ +} + +// ---- AddMenuActionCommand ---- + +MenuActionCommand::MenuActionCommand(const QString &text, QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(text, formWindow), + m_action(0), + m_actionBefore(0), + m_menuParent(0), + m_associatedWidget(0), + m_objectToSelect(0) +{ +} + +void MenuActionCommand::init(QAction *action, QAction *actionBefore, + QWidget *associatedWidget, QWidget *objectToSelect) +{ + QMenu *menu = action->menu(); + Q_ASSERT(menu); + m_menuParent = menu->parentWidget(); + m_action = action; + m_actionBefore = actionBefore; + m_associatedWidget = associatedWidget; + m_objectToSelect = objectToSelect; +} + +void MenuActionCommand::insertMenu() +{ + core()->metaDataBase()->add(m_action); + QMenu *menu = m_action->menu(); + if (m_menuParent && menu->parentWidget() != m_menuParent) + menu->setParent(m_menuParent); + core()->metaDataBase()->add(menu); + m_associatedWidget->insertAction(m_actionBefore, m_action); + cheapUpdate(); + selectUnmanagedObject(menu); +} + +void MenuActionCommand::removeMenu() +{ + m_action->menu()->setParent(0); + QMenu *menu = m_action->menu(); + core()->metaDataBase()->remove(menu); + menu->setParent(0); + core()->metaDataBase()->remove(m_action); + m_associatedWidget->removeAction(m_action); + cheapUpdate(); + selectUnmanagedObject(m_objectToSelect); +} + +AddMenuActionCommand::AddMenuActionCommand(QDesignerFormWindowInterface *formWindow) : + MenuActionCommand(QApplication::translate("Command", "Add menu"), formWindow) +{ +} + +// ---- RemoveMenuActionCommand ---- +RemoveMenuActionCommand::RemoveMenuActionCommand(QDesignerFormWindowInterface *formWindow) : + MenuActionCommand(QApplication::translate("Command", "Remove menu"), formWindow) +{ +} + +// ---- CreateSubmenuCommand ---- +CreateSubmenuCommand::CreateSubmenuCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QApplication::translate("Command", "Create submenu"), formWindow), + m_action(0), + m_menu(0), + m_objectToSelect(0) +{ +} + +void CreateSubmenuCommand::init(QDesignerMenu *menu, QAction *action, QObject *objectToSelect) +{ + m_menu = menu; + m_action = action; + m_objectToSelect = objectToSelect; +} + +void CreateSubmenuCommand::redo() +{ + m_menu->createRealMenuAction(m_action); + cheapUpdate(); + if (m_objectToSelect) + selectUnmanagedObject(m_objectToSelect); +} + +void CreateSubmenuCommand::undo() +{ + m_menu->removeRealMenu(m_action); + cheapUpdate(); + selectUnmanagedObject(m_menu); +} + +// ---- DeleteToolBarCommand ---- +DeleteToolBarCommand::DeleteToolBarCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QApplication::translate("Command", "Delete Tool Bar"), formWindow) +{ +} + +void DeleteToolBarCommand::init(QToolBar *toolBar) +{ + m_toolBar = toolBar; + m_mainWindow = qobject_cast<QMainWindow*>(toolBar->parentWidget()); +} + +void DeleteToolBarCommand::redo() +{ + if (m_mainWindow) { + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + Q_ASSERT(c != 0); + for (int i=0; i<c->count(); ++i) { + if (c->widget(i) == m_toolBar) { + c->remove(i); + break; + } + } + } + + core()->metaDataBase()->remove(m_toolBar); + m_toolBar->hide(); + m_toolBar->setParent(formWindow()); + formWindow()->emitSelectionChanged(); +} + +void DeleteToolBarCommand::undo() +{ + if (m_mainWindow) { + m_toolBar->setParent(m_mainWindow); + QDesignerContainerExtension *c = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), m_mainWindow); + + c->addWidget(m_toolBar); + + core()->metaDataBase()->add(m_toolBar); + m_toolBar->show(); + formWindow()->emitSelectionChanged(); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_command2.cpp b/tools/designer/src/lib/shared/qdesigner_command2.cpp new file mode 100644 index 0000000..db534c0 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_command2.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_command2_p.h" +#include "formwindowbase_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_command_p.h" +#include "widgetfactory_p.h" +#include "qlayout_widget_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> + +#include <QtGui/QApplication> +#include <QtGui/QLayout> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +MorphLayoutCommand::MorphLayoutCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow), + m_breakLayoutCommand(new BreakLayoutCommand(formWindow)), + m_layoutCommand(new LayoutCommand(formWindow)), + m_newType(LayoutInfo::VBox), + m_layoutBase(0) +{ +} + +MorphLayoutCommand::~MorphLayoutCommand() +{ + delete m_layoutCommand; + delete m_breakLayoutCommand; +} + +bool MorphLayoutCommand::init(QWidget *w, int newType) +{ + int oldType; + QDesignerFormWindowInterface *fw = formWindow(); + if (!canMorph(fw, w, &oldType) || oldType == newType) + return false; + m_layoutBase = w; + m_newType = newType; + // Find all managed widgets + m_widgets.clear(); + const QLayout *layout = LayoutInfo::managedLayout(fw->core(), w); + const int count = layout->count(); + for (int i = 0; i < count ; i++) { + if (QWidget *w = layout->itemAt(i)->widget()) + if (fw->isManaged(w)) + m_widgets.push_back(w); + } + const bool reparentLayoutWidget = false; // leave QLayoutWidget intact + m_breakLayoutCommand->init(m_widgets, m_layoutBase, reparentLayoutWidget); + m_layoutCommand->init(m_layoutBase, m_widgets, static_cast<LayoutInfo::Type>(m_newType), m_layoutBase, reparentLayoutWidget); + setText(formatDescription(core(), m_layoutBase, oldType, newType)); + return true; +} + +bool MorphLayoutCommand::canMorph(const QDesignerFormWindowInterface *formWindow, QWidget *w, int *ptrToCurrentType) +{ + if (ptrToCurrentType) + *ptrToCurrentType = LayoutInfo::NoLayout; + // We want a managed widget or a container page + // with a level-0 managed layout + QDesignerFormEditorInterface *core = formWindow->core(); + QLayout *layout = LayoutInfo::managedLayout(core, w); + if (!layout) + return false; + const LayoutInfo::Type type = LayoutInfo::layoutType(core, layout); + if (ptrToCurrentType) + *ptrToCurrentType = type; + switch (type) { + case LayoutInfo::HBox: + case LayoutInfo::VBox: + case LayoutInfo::Grid: + case LayoutInfo::Form: + return true; + break; + case LayoutInfo::NoLayout: + case LayoutInfo::HSplitter: // Nothing doing + case LayoutInfo::VSplitter: + case LayoutInfo::UnknownLayout: + break; + } + return false; +} + +void MorphLayoutCommand::redo() +{ + m_breakLayoutCommand->redo(); + m_layoutCommand->redo(); + /* Transfer applicable properties which is a cross-section of the modified + * properties except object name. */ + if (const LayoutProperties *properties = m_breakLayoutCommand->layoutProperties()) { + const int oldMask = m_breakLayoutCommand->propertyMask(); + QLayout *newLayout = LayoutInfo::managedLayout(core(), m_layoutBase); + const int newMask = LayoutProperties::visibleProperties(newLayout); + const int applicableMask = (oldMask & newMask) & ~LayoutProperties::ObjectNameProperty; + if (applicableMask) + properties->toPropertySheet(core(), newLayout, applicableMask); + } +} + +void MorphLayoutCommand::undo() +{ + m_layoutCommand->undo(); + m_breakLayoutCommand->undo(); +} + +QString MorphLayoutCommand::formatDescription(QDesignerFormEditorInterface * /* core*/, const QWidget *w, int oldType, int newType) +{ + const QString oldName = LayoutInfo::layoutName(static_cast<LayoutInfo::Type>(oldType)); + const QString newName = LayoutInfo::layoutName(static_cast<LayoutInfo::Type>(newType)); + const QString widgetName = qobject_cast<const QLayoutWidget*>(w) ? w->layout()->objectName() : w->objectName(); + return QApplication::translate("Command", "Change layout of '%1' from %2 to %3").arg(widgetName, oldName, newName); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_command2_p.h b/tools/designer/src/lib/shared/qdesigner_command2_p.h new file mode 100644 index 0000000..e61222b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_command2_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_COMMAND2_H +#define QDESIGNER_COMMAND2_H + +#include "shared_global_p.h" +#include "qdesigner_formwindowcommand_p.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class LayoutCommand; +class BreakLayoutCommand; + +/* This command changes the type of a managed layout on a widget (including + * red layouts of type 'QLayoutWidget') into another type, maintaining the + * applicable properties. It does this by chaining BreakLayoutCommand and + * LayoutCommand, parametrizing them not to actually delete/reparent + * QLayoutWidget's. */ + +class QDESIGNER_SHARED_EXPORT MorphLayoutCommand : public QDesignerFormWindowCommand { + Q_DISABLE_COPY(MorphLayoutCommand) +public: + explicit MorphLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MorphLayoutCommand(); + + bool init(QWidget *w, int newType); + + static bool canMorph(const QDesignerFormWindowInterface *formWindow, QWidget *w, int *ptrToCurrentType = 0); + + virtual void redo(); + virtual void undo(); + +private: + static QString formatDescription(QDesignerFormEditorInterface *core, const QWidget *w, int oldType, int newType); + + BreakLayoutCommand *m_breakLayoutCommand; + LayoutCommand *m_layoutCommand; + int m_newType; + QWidgetList m_widgets; + QWidget *m_layoutBase; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND2_H diff --git a/tools/designer/src/lib/shared/qdesigner_command_p.h b/tools/designer/src/lib/shared/qdesigner_command_p.h new file mode 100644 index 0000000..3a4ce2d --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_command_p.h @@ -0,0 +1,1136 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_COMMAND_H +#define QDESIGNER_COMMAND_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" +#include "layoutinfo_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_formwindowcommand_p.h" +#include "qdesigner_formeditorcommand_p.h" + +#include <QtDesigner/layoutdecoration.h> + +#include <QtGui/QIcon> +#include <QtCore/QObject> +#include <QtCore/QPair> +#include <QtCore/QMap> +#include <QtCore/QHash> +#include <QtCore/QPoint> +#include <QtCore/QRect> + +QT_BEGIN_NAMESPACE + +class QDesignerContainerExtension; +class QDesignerMetaDataBaseItemInterface; +class QDesignerMenu; + +class QMenuBar; +class QStatusBar; +class QToolBar; +class QToolBox; +class QTabWidget; +class QTableWidget; +class QTableWidgetItem; +class QTreeWidget; +class QTreeWidgetItem; +class QListWidget; +class QListWidgetItem; +class QComboBox; +class QStackedWidget; +class QDockWidget; +class QMainWindow; +class QFormLayout; + +namespace qdesigner_internal { + +class Layout; +class LayoutHelper; +class PropertySheetIconValue; +class DesignerIconCache; +struct LayoutProperties; + +class QDESIGNER_SHARED_EXPORT InsertWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit InsertWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~InsertWidgetCommand(); + + void init(QWidget *widget, bool already_in_form = false, int layoutRow = -1, int layoutColumn = -1); + + virtual void redo(); + virtual void undo(); + +private: + void refreshBuddyLabels(); + + QPointer<QWidget> m_widget; + QDesignerLayoutDecorationExtension::InsertMode m_insertMode; + QPair<int, int> m_cell; + LayoutHelper* m_layoutHelper; + bool m_widgetWasManaged; +}; + +class QDESIGNER_SHARED_EXPORT ChangeZOrderCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeZOrderCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + + virtual void redo(); + virtual void undo(); +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const = 0; + virtual void reorder(QWidget *widget) const = 0; + +private: + QPointer<QWidget> m_widget; + QPointer<QWidget> m_oldPreceding; + QList<QWidget *> m_oldParentZOrder; +}; + +class QDESIGNER_SHARED_EXPORT RaiseWidgetCommand: public ChangeZOrderCommand +{ + +public: + explicit RaiseWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const; + virtual void reorder(QWidget *widget) const; +}; + +class QDESIGNER_SHARED_EXPORT LowerWidgetCommand: public ChangeZOrderCommand +{ + +public: + explicit LowerWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + +protected: + virtual QWidgetList reorderWidget(const QWidgetList &list, QWidget *widget) const; + virtual void reorder(QWidget *widget) const; +}; + +class QDESIGNER_SHARED_EXPORT AdjustWidgetSizeCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AdjustWidgetSizeCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget); + + virtual void redo(); + virtual void undo(); + +private: + QWidget *widgetForAdjust() const; + bool adjustNonLaidOutMainContainer(QWidget *integrationContainer); + void updatePropertyEditor() const; + + QPointer<QWidget> m_widget; + QRect m_geometry; +}; + +// Helper to correctly unmanage a widget and its children for delete operations +class QDESIGNER_SHARED_EXPORT ManageWidgetCommandHelper { +public: + typedef QVector<QWidget*> WidgetVector; + + ManageWidgetCommandHelper(); + void init(const QDesignerFormWindowInterface *fw, QWidget *widget); + void init(QWidget *widget, const WidgetVector &managedChildren); + + void manage(QDesignerFormWindowInterface *fw); + void unmanage(QDesignerFormWindowInterface *fw); + + const WidgetVector &managedChildren() const { return m_managedChildren; } +private: + QWidget *m_widget; + WidgetVector m_managedChildren; +}; + +class QDESIGNER_SHARED_EXPORT DeleteWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteWidgetCommand(QDesignerFormWindowInterface *formWindow); + ~DeleteWidgetCommand(); + + enum DeleteFlags { DoNotUnmanage = 0x1, DoNotSimplifyLayout = 0x2 }; + + void init(QWidget *widget, unsigned flags = 0); + + virtual void redo(); + virtual void undo(); + +private: + QPointer<QWidget> m_widget; + QPointer<QWidget> m_parentWidget; + QRect m_geometry; + LayoutInfo::Type m_layoutType; + LayoutHelper* m_layoutHelper; + unsigned m_flags; + QRect m_layoutPosition; + int m_splitterIndex; + bool m_layoutSimplified; + QDesignerMetaDataBaseItemInterface *m_formItem; + int m_tabOrderIndex; + int m_widgetOrderIndex; + int m_zOrderIndex; + ManageWidgetCommandHelper m_manageHelper; +}; + +class QDESIGNER_SHARED_EXPORT ReparentWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ReparentWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, QWidget *parentWidget); + + virtual void redo(); + virtual void undo(); + +private: + QPointer<QWidget> m_widget; + QPoint m_oldPos; + QPoint m_newPos; + QPointer<QWidget> m_oldParentWidget; + QPointer<QWidget> m_newParentWidget; + QList<QWidget *> m_oldParentList; + QList<QWidget *> m_oldParentZOrder; +}; + +class QDESIGNER_SHARED_EXPORT ChangeFormLayoutItemRoleCommand : public QDesignerFormWindowCommand +{ +public: + enum Operation { SpanningToLabel = 0x1, SpanningToField = 0x2, LabelToSpanning = 0x4, FieldToSpanning =0x8 }; + + explicit ChangeFormLayoutItemRoleCommand(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, Operation op); + + virtual void redo(); + virtual void undo(); + + // Return a mask of possible operations of that item + static unsigned possibleOperations(QDesignerFormEditorInterface *core, QWidget *w); + +private: + static QFormLayout *managedFormLayoutOf(QDesignerFormEditorInterface *core, QWidget *w); + static Operation reverseOperation(Operation op); + void doOperation(Operation op); + + QPointer<QWidget> m_widget; + Operation m_operation; +}; + +class QDESIGNER_SHARED_EXPORT ChangeLayoutItemGeometry: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeLayoutItemGeometry(QDesignerFormWindowInterface *formWindow); + + void init(QWidget *widget, int row, int column, int rowspan, int colspan); + + virtual void redo(); + virtual void undo(); + +protected: + void changeItemPosition(const QRect &g); + +private: + QPointer<QWidget> m_widget; + QRect m_oldInfo; + QRect m_newInfo; +}; + +class QDESIGNER_SHARED_EXPORT TabOrderCommand: public QDesignerFormWindowCommand +{ + +public: + explicit TabOrderCommand(QDesignerFormWindowInterface *formWindow); + + void init(const QList<QWidget*> &newTabOrder); + + inline QList<QWidget*> oldTabOrder() const + { return m_oldTabOrder; } + + inline QList<QWidget*> newTabOrder() const + { return m_newTabOrder; } + + virtual void redo(); + virtual void undo(); + +private: + QDesignerMetaDataBaseItemInterface *m_widgetItem; + QList<QWidget*> m_oldTabOrder; + QList<QWidget*> m_newTabOrder; +}; + +class QDESIGNER_SHARED_EXPORT PromoteToCustomWidgetCommand : public QDesignerFormWindowCommand +{ +public: + typedef QList<QPointer<QWidget> > WidgetList; + + explicit PromoteToCustomWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(const WidgetList &widgets, const QString &customClassName); + virtual void redo(); + virtual void undo(); + +private: + void updateSelection(); + WidgetList m_widgets; + QString m_customClassName; +}; + +class QDESIGNER_SHARED_EXPORT DemoteFromCustomWidgetCommand : public QDesignerFormWindowCommand +{ +public: + typedef PromoteToCustomWidgetCommand::WidgetList WidgetList; + + explicit DemoteFromCustomWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(const WidgetList &promoted); + virtual void redo(); + virtual void undo(); +private: + PromoteToCustomWidgetCommand m_promote_cmd; +}; + +// Mixin class for storing the selection state +class QDESIGNER_SHARED_EXPORT CursorSelectionState { + Q_DISABLE_COPY(CursorSelectionState) +public: + CursorSelectionState(); + + void save(const QDesignerFormWindowInterface *formWindow); + void restore(QDesignerFormWindowInterface *formWindow) const; + +private: + typedef QList<QPointer<QWidget> > WidgetPointerList; + WidgetPointerList m_selection; + QPointer<QWidget> m_current; +}; + +class QDESIGNER_SHARED_EXPORT LayoutCommand: public QDesignerFormWindowCommand +{ + +public: + explicit LayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~LayoutCommand(); + + inline QWidgetList widgets() const { return m_widgets; } + + void init(QWidget *parentWidget, const QWidgetList &widgets, LayoutInfo::Type layoutType, + QWidget *layoutBase = 0, + // Reparent/Hide instances of QLayoutWidget. + bool reparentLayoutWidget = true); + + virtual void redo(); + virtual void undo(); + +private: + QPointer<QWidget> m_parentWidget; + QWidgetList m_widgets; + QPointer<QWidget> m_layoutBase; + QPointer<Layout> m_layout; + CursorSelectionState m_cursorSelectionState; + bool m_setup; +}; + +class QDESIGNER_SHARED_EXPORT BreakLayoutCommand: public QDesignerFormWindowCommand +{ + +public: + explicit BreakLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~BreakLayoutCommand(); + + inline QWidgetList widgets() const { return m_widgets; } + + void init(const QWidgetList &widgets, QWidget *layoutBase, + // Reparent/Hide instances of QLayoutWidget. + bool reparentLayoutWidget = true); + + virtual void redo(); + virtual void undo(); + + // Access the properties of the layout, 0 in case of splitters. + const LayoutProperties *layoutProperties() const; + int propertyMask() const; + +private: + QWidgetList m_widgets; + QPointer<QWidget> m_layoutBase; + QPointer<Layout> m_layout; + LayoutHelper* m_layoutHelper; + LayoutProperties *m_properties; + int m_propertyMask; + CursorSelectionState m_cursorSelectionState; +}; + +class QDESIGNER_SHARED_EXPORT SimplifyLayoutCommand: public QDesignerFormWindowCommand +{ +public: + explicit SimplifyLayoutCommand(QDesignerFormWindowInterface *formWindow); + virtual ~SimplifyLayoutCommand(); + + bool init(QWidget *layoutBase); + + // Quick check + static bool canSimplify(QDesignerFormEditorInterface *core, const QWidget *w, int *layoutType = 0); + + virtual void redo(); + virtual void undo(); + +private: + const QRect m_area; + QWidget *m_layoutBase; + LayoutHelper* m_layoutHelper; + bool m_layoutSimplified; +}; + +class QDESIGNER_SHARED_EXPORT ToolBoxCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ToolBoxCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ToolBoxCommand(); + + void init(QToolBox *toolBox); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer<QToolBox> m_toolBox; + QPointer<QWidget> m_widget; + int m_index; + QString m_itemText; + QIcon m_itemIcon; +}; + +class QDESIGNER_SHARED_EXPORT MoveToolBoxPageCommand: public ToolBoxCommand +{ + +public: + explicit MoveToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveToolBoxPageCommand(); + + void init(QToolBox *toolBox, QWidget *page, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; +}; + +class QDESIGNER_SHARED_EXPORT DeleteToolBoxPageCommand: public ToolBoxCommand +{ + +public: + explicit DeleteToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteToolBoxPageCommand(); + + void init(QToolBox *toolBox); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddToolBoxPageCommand: public ToolBoxCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddToolBoxPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddToolBoxPageCommand(); + + void init(QToolBox *toolBox); + void init(QToolBox *toolBox, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT TabWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit TabWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~TabWidgetCommand(); + + void init(QTabWidget *tabWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer<QTabWidget> m_tabWidget; + QPointer<QWidget> m_widget; + int m_index; + QString m_itemText; + QIcon m_itemIcon; +}; + +class QDESIGNER_SHARED_EXPORT DeleteTabPageCommand: public TabWidgetCommand +{ + +public: + explicit DeleteTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteTabPageCommand(); + + void init(QTabWidget *tabWidget); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddTabPageCommand: public TabWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddTabPageCommand(); + + void init(QTabWidget *tabWidget); + void init(QTabWidget *tabWidget, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT MoveTabPageCommand: public TabWidgetCommand +{ + +public: + explicit MoveTabPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveTabPageCommand(); + + void init(QTabWidget *tabWidget, QWidget *page, + const QIcon &icon, const QString &label, + int index, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; + QPointer<QWidget> m_page; + QString m_label; + QIcon m_icon; +}; + +class QDESIGNER_SHARED_EXPORT StackedWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit StackedWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~StackedWidgetCommand(); + + void init(QStackedWidget *stackedWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer<QStackedWidget> m_stackedWidget; + QPointer<QWidget> m_widget; + int m_index; +}; + +class QDESIGNER_SHARED_EXPORT MoveStackedWidgetCommand: public StackedWidgetCommand +{ + +public: + explicit MoveStackedWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~MoveStackedWidgetCommand(); + + void init(QStackedWidget *stackedWidget, QWidget *page, int newIndex); + + virtual void redo(); + virtual void undo(); + +private: + int m_newIndex; + int m_oldIndex; +}; + +class QDESIGNER_SHARED_EXPORT DeleteStackedWidgetPageCommand: public StackedWidgetCommand +{ + +public: + explicit DeleteStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteStackedWidgetPageCommand(); + + void init(QStackedWidget *stackedWidget); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddStackedWidgetPageCommand: public StackedWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddStackedWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddStackedWidgetPageCommand(); + + void init(QStackedWidget *stackedWidget); + void init(QStackedWidget *stackedWidget, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT CreateMenuBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit CreateMenuBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QMenuBar> m_menuBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteMenuBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteMenuBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMenuBar *menuBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QMenuBar> m_menuBar; +}; + +class QDESIGNER_SHARED_EXPORT CreateStatusBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit CreateStatusBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QStatusBar> m_statusBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteStatusBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteStatusBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QStatusBar *statusBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QStatusBar> m_statusBar; +}; + +class QDESIGNER_SHARED_EXPORT AddToolBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddToolBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QToolBar> m_toolBar; +}; + +class QDESIGNER_SHARED_EXPORT DeleteToolBarCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DeleteToolBarCommand(QDesignerFormWindowInterface *formWindow); + + void init(QToolBar *toolBar); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QToolBar> m_toolBar; +}; + +class QDESIGNER_SHARED_EXPORT DockWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit DockWidgetCommand(const QString &description, QDesignerFormWindowInterface *formWindow); + virtual ~DockWidgetCommand(); + + void init(QDockWidget *dockWidget); + +protected: + QPointer<QDockWidget> m_dockWidget; +}; + +class QDESIGNER_SHARED_EXPORT AddDockWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddDockWidgetCommand(QDesignerFormWindowInterface *formWindow); + + void init(QMainWindow *mainWindow, QDockWidget *dockWidget); + void init(QMainWindow *mainWindow); + + virtual void undo(); + virtual void redo(); + +private: + QPointer<QMainWindow> m_mainWindow; + QPointer<QDockWidget> m_dockWidget; +}; + +class QDESIGNER_SHARED_EXPORT ContainerWidgetCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ContainerWidgetCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ContainerWidgetCommand(); + + QDesignerContainerExtension *containerExtension() const; + + void init(QWidget *containerWidget); + + virtual void removePage(); + virtual void addPage(); + +protected: + QPointer<QWidget> m_containerWidget; + QPointer<QWidget> m_widget; + int m_index; +}; + +class QDESIGNER_SHARED_EXPORT DeleteContainerWidgetPageCommand: public ContainerWidgetCommand +{ + +public: + explicit DeleteContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~DeleteContainerWidgetPageCommand(); + + void init(QWidget *containerWidget, ContainerType ct); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT AddContainerWidgetPageCommand: public ContainerWidgetCommand +{ + +public: + enum InsertionMode { + InsertBefore, + InsertAfter + }; + explicit AddContainerWidgetPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~AddContainerWidgetPageCommand(); + + void init(QWidget *containerWidget, ContainerType ct, InsertionMode mode); + + virtual void redo(); + virtual void undo(); +}; + +class QDESIGNER_SHARED_EXPORT ChangeCurrentPageCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeCurrentPageCommand(QDesignerFormWindowInterface *formWindow); + virtual ~ChangeCurrentPageCommand(); + + QDesignerContainerExtension *containerExtension() const; + + void init(QWidget *containerWidget, int newIndex); + + virtual void redo(); + virtual void undo(); + +protected: + QPointer<QWidget> m_containerWidget; + QPointer<QWidget> m_widget; + int m_oldIndex; + int m_newIndex; +}; + +struct QDESIGNER_SHARED_EXPORT ItemData { + ItemData() {} + + ItemData(const QListWidgetItem *item, bool editor); + ItemData(const QTableWidgetItem *item, bool editor); + ItemData(const QTreeWidgetItem *item, int column); + QListWidgetItem *createListItem(DesignerIconCache *iconCache, bool editor) const; + QTableWidgetItem *createTableItem(DesignerIconCache *iconCache, bool editor) const; + void fillTreeItemColumn(QTreeWidgetItem *item, int column, DesignerIconCache *iconCache) const; + + bool isValid() const { return !m_properties.isEmpty(); } + bool operator==(const ItemData &rhs) const { return m_properties == rhs.m_properties; } + bool operator!=(const ItemData &rhs) const { return m_properties != rhs.m_properties; } + + QHash<int, QVariant> m_properties; +}; + +struct QDESIGNER_SHARED_EXPORT ListContents { + ListContents() {} + + ListContents(const QTreeWidgetItem *item); + QTreeWidgetItem *createTreeItem(DesignerIconCache *iconCache) const; + + void createFromListWidget(const QListWidget *listWidget, bool editor); + void applyToListWidget(QListWidget *listWidget, DesignerIconCache *iconCache, bool editor) const; + void createFromComboBox(const QComboBox *listWidget); + void applyToComboBox(QComboBox *listWidget, DesignerIconCache *iconCache) const; + + bool operator==(const ListContents &rhs) const { return m_items == rhs.m_items; } + bool operator!=(const ListContents &rhs) const { return m_items != rhs.m_items; } + + QList<ItemData> m_items; +}; + +// Data structure representing the contents of a QTableWidget with +// methods to retrieve and apply for ChangeTableContentsCommand +struct QDESIGNER_SHARED_EXPORT TableWidgetContents { + + typedef QPair<int, int> CellRowColumnAddress; + typedef QMap<CellRowColumnAddress, ItemData> TableItemMap; + + TableWidgetContents(); + void clear(); + + void fromTableWidget(const QTableWidget *tableWidget, bool editor); + void applyToTableWidget(QTableWidget *tableWidget, DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const TableWidgetContents &rhs) const; + bool operator!=(const TableWidgetContents &rhs) const { return !(*this == rhs); } + + static bool nonEmpty(const QTableWidgetItem *item, int headerColumn); + static QString defaultHeaderText(int i); + static void insertHeaderItem(const QTableWidgetItem *item, int i, ListContents *header, bool editor); + + int m_columnCount; + int m_rowCount; + ListContents m_horizontalHeader; + ListContents m_verticalHeader; + TableItemMap m_items; +}; + +class QDESIGNER_SHARED_EXPORT ChangeTableContentsCommand: public QDesignerFormWindowCommand +{ +public: + explicit ChangeTableContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QTableWidget *tableWidget, const TableWidgetContents &oldCont, const TableWidgetContents &newCont); + virtual void redo(); + virtual void undo(); + +private: + QPointer<QTableWidget> m_tableWidget; + TableWidgetContents m_oldContents; + TableWidgetContents m_newContents; + DesignerIconCache *m_iconCache; +}; + +// Data structure representing the contents of a QTreeWidget with +// methods to retrieve and apply for ChangeTreeContentsCommand +struct QDESIGNER_SHARED_EXPORT TreeWidgetContents { + + struct ItemContents : public ListContents { + ItemContents() : m_itemFlags(-1) {} + ItemContents(const QTreeWidgetItem *item, bool editor); + QTreeWidgetItem *createTreeItem(DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const ItemContents &rhs) const; + bool operator!=(const ItemContents &rhs) const { return !(*this == rhs); } + + int m_itemFlags; + //bool m_firstColumnSpanned:1; + //bool m_hidden:1; + //bool m_expanded:1; + QList<ItemContents> m_children; + }; + + void clear(); + + void fromTreeWidget(const QTreeWidget *treeWidget, bool editor); + void applyToTreeWidget(QTreeWidget *treeWidget, DesignerIconCache *iconCache, bool editor) const; + + bool operator==(const TreeWidgetContents &rhs) const; + bool operator!=(const TreeWidgetContents &rhs) const { return !(*this == rhs); } + + ListContents m_headerItem; + QList<ItemContents> m_rootItems; +}; + +class QDESIGNER_SHARED_EXPORT ChangeTreeContentsCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeTreeContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QTreeWidget *treeWidget, const TreeWidgetContents &oldState, const TreeWidgetContents &newState); + virtual void redo(); + virtual void undo(); + enum ApplyIconStrategy { + SetIconStrategy, + ResetIconStrategy + }; +private: + QPointer<QTreeWidget> m_treeWidget; + TreeWidgetContents m_oldState; + TreeWidgetContents m_newState; + DesignerIconCache *m_iconCache; +}; + +class QDESIGNER_SHARED_EXPORT ChangeListContentsCommand: public QDesignerFormWindowCommand +{ + +public: + explicit ChangeListContentsCommand(QDesignerFormWindowInterface *formWindow); + + void init(QListWidget *listWidget, const ListContents &oldItems, const ListContents &items); + void init(QComboBox *comboBox, const ListContents &oldItems, const ListContents &items); + virtual void redo(); + virtual void undo(); +private: + QPointer<QListWidget> m_listWidget; + QPointer<QComboBox> m_comboBox; + ListContents m_oldItemsState; + ListContents m_newItemsState; + DesignerIconCache *m_iconCache; +}; + +class QDESIGNER_SHARED_EXPORT AddActionCommand : public QDesignerFormWindowCommand +{ + +public: + explicit AddActionCommand(QDesignerFormWindowInterface *formWindow); + void init(QAction *action); + virtual void redo(); + virtual void undo(); +private: + QAction *m_action; +}; + +// Note: This command must be executed within a macro since it +// makes the form emit objectRemoved() which might cause other components +// to add commands (for example, removal of signals and slots +class QDESIGNER_SHARED_EXPORT RemoveActionCommand : public QDesignerFormWindowCommand +{ + +public: + explicit RemoveActionCommand(QDesignerFormWindowInterface *formWindow); + void init(QAction *action); + virtual void redo(); + virtual void undo(); + + struct ActionDataItem { + ActionDataItem(QAction *_before = 0, QWidget *_widget = 0) + : before(_before), widget(_widget) {} + QAction *before; + QWidget *widget; + }; + typedef QList<ActionDataItem> ActionData; + +private: + QAction *m_action; + + ActionData m_actionData; +}; + +class QDESIGNER_SHARED_EXPORT ActionInsertionCommand : public QDesignerFormWindowCommand +{ + +protected: + ActionInsertionCommand(const QString &text, QDesignerFormWindowInterface *formWindow); + +public: + void init(QWidget *parentWidget, QAction *action, QAction *beforeAction = 0, bool update = true); + +protected: + void insertAction(); + void removeAction(); + +private: + QWidget *m_parentWidget; + QAction *m_action; + QAction *m_beforeAction; + bool m_update; +}; + +class QDESIGNER_SHARED_EXPORT InsertActionIntoCommand : public ActionInsertionCommand +{ + +public: + explicit InsertActionIntoCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { insertAction(); } + virtual void undo() { removeAction(); } +}; + +class QDESIGNER_SHARED_EXPORT RemoveActionFromCommand : public ActionInsertionCommand +{ + +public: + explicit RemoveActionFromCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { removeAction(); } + virtual void undo() { insertAction(); } +}; + +class QDESIGNER_SHARED_EXPORT MenuActionCommand : public QDesignerFormWindowCommand +{ +public: + void init(QAction *action, QAction *actionBefore, QWidget *associatedWidget, QWidget *objectToSelect); + +protected: + MenuActionCommand(const QString &text, QDesignerFormWindowInterface *formWindow); + void insertMenu(); + void removeMenu(); + +private: + QAction *m_action; + QAction *m_actionBefore; + QWidget *m_menuParent; + QWidget *m_associatedWidget; + QWidget *m_objectToSelect; +}; + +class QDESIGNER_SHARED_EXPORT AddMenuActionCommand : public MenuActionCommand +{ + +public: + explicit AddMenuActionCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { insertMenu(); } + virtual void undo() { removeMenu(); } +}; + +class QDESIGNER_SHARED_EXPORT RemoveMenuActionCommand : public MenuActionCommand +{ + +public: + explicit RemoveMenuActionCommand(QDesignerFormWindowInterface *formWindow); + + virtual void redo() { removeMenu(); } + virtual void undo() { insertMenu(); } +}; + +class QDESIGNER_SHARED_EXPORT CreateSubmenuCommand : public QDesignerFormWindowCommand +{ + +public: + explicit CreateSubmenuCommand(QDesignerFormWindowInterface *formWindow); + void init(QDesignerMenu *menu, QAction *action, QObject *m_objectToSelect = 0); + virtual void redo(); + virtual void undo(); +private: + QAction *m_action; + QDesignerMenu *m_menu; + QObject *m_objectToSelect; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND_H diff --git a/tools/designer/src/lib/shared/qdesigner_dnditem.cpp b/tools/designer/src/lib/shared/qdesigner_dnditem.cpp new file mode 100644 index 0000000..5dfbb65 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_dnditem.cpp @@ -0,0 +1,300 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_dnditem_p.h" +#include "formwindowbase_p.h" +#include "ui4_p.h" + +#include <QtGui/QPainter> +#include <QtGui/QBitmap> +#include <QtGui/QPixmap> +#include <QtGui/QImage> +#include <QtGui/QLabel> +#include <QtGui/QDrag> +#include <QtGui/QCursor> +#include <QtGui/QDropEvent> +#include <QtGui/QRgb> + +#include <QtCore/QMultiMap> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerDnDItem::QDesignerDnDItem(DropType type, QWidget *source) : + m_source(source), + m_type(type), + m_dom_ui(0), + m_widget(0), + m_decoration(0) +{ +} + +void QDesignerDnDItem::init(DomUI *ui, QWidget *widget, QWidget *decoration, + const QPoint &global_mouse_pos) +{ + Q_ASSERT(widget != 0 || ui != 0); + Q_ASSERT(decoration != 0); + + m_dom_ui = ui; + m_widget = widget; + m_decoration = decoration; + + const QRect geometry = m_decoration->geometry(); + m_hot_spot = global_mouse_pos - m_decoration->geometry().topLeft(); +} + +QDesignerDnDItem::~QDesignerDnDItem() +{ + if (m_decoration != 0) + m_decoration->deleteLater(); + delete m_dom_ui; +} + +DomUI *QDesignerDnDItem::domUi() const +{ + return m_dom_ui; +} + +QWidget *QDesignerDnDItem::decoration() const +{ + return m_decoration; +} + +QPoint QDesignerDnDItem::hotSpot() const +{ + return m_hot_spot; +} + +QWidget *QDesignerDnDItem::widget() const +{ + return m_widget; +} + +QDesignerDnDItem::DropType QDesignerDnDItem::type() const +{ + return m_type; +} + +QWidget *QDesignerDnDItem::source() const +{ + return m_source; +} + +void QDesignerDnDItem::setDomUi(DomUI *dom_ui) +{ + delete m_dom_ui; + m_dom_ui = dom_ui; +} + +// ---------- QDesignerMimeData + +// Make pixmap transparent on Windows only. Mac is transparent by default, Unix usually does not work. +#ifdef Q_WS_WIN +# define TRANSPARENT_DRAG_PIXMAP +#endif + +QDesignerMimeData::QDesignerMimeData(const QDesignerDnDItems &items, QDrag *drag) : + m_items(items) +{ + enum { Alpha = 200 }; + QPoint decorationTopLeft; + switch (m_items.size()) { + case 0: + break; + case 1: { + QWidget *deco = m_items.first()->decoration(); + decorationTopLeft = deco->pos(); + const QPixmap widgetPixmap = QPixmap::grabWidget(deco); +#ifdef TRANSPARENT_DRAG_PIXMAP + QImage image(widgetPixmap.size(), QImage::Format_ARGB32); + image.fill(QColor(Qt::transparent).rgba()); + QPainter painter(&image); + painter.drawPixmap(QPoint(0, 0), widgetPixmap); + painter.end(); + setImageTransparency(image, Alpha); + drag->setPixmap(QPixmap::fromImage(image)); +#else + drag->setPixmap(widgetPixmap); +#endif + } + break; + default: { + // determine size of drag decoration by uniting all geometries + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + QDesignerDnDItems::const_iterator it =m_items.constBegin(); + QRect unitedGeometry = (*it)->decoration()->geometry(); + for (++it; it != cend; ++it ) + unitedGeometry = unitedGeometry .united((*it)->decoration()->geometry()); + + // paint with offset. At the same time, create a mask bitmap, containing widget rectangles. + QImage image(unitedGeometry.size(), QImage::Format_ARGB32); + image.fill(QColor(Qt::transparent).rgba()); + QBitmap mask(unitedGeometry.size()); + mask.clear(); + // paint with offset, determine action + QPainter painter(&image); + QPainter maskPainter(&mask); + decorationTopLeft = unitedGeometry.topLeft(); + for (it = m_items.constBegin() ; it != cend; ++it ) { + QWidget *w = (*it)->decoration(); + const QPixmap wp = QPixmap::grabWidget(w); + const QPoint pos = w->pos() - decorationTopLeft; + painter.drawPixmap(pos, wp); + maskPainter.fillRect(QRect(pos, wp.size()), Qt::color1); + } + painter.end(); + maskPainter.end(); +#ifdef TRANSPARENT_DRAG_PIXMAP + setImageTransparency(image, Alpha); +#endif + QPixmap pixmap = QPixmap::fromImage(image); + pixmap.setMask(mask); + drag->setPixmap(pixmap); + } + break; + } + // determine hot spot and reconstruct the exact starting position as form window + // introduces some offset when detecting DnD + m_globalStartPos = m_items.first()->decoration()->pos() + m_items.first()->hotSpot(); + m_hotSpot = m_globalStartPos - decorationTopLeft; + drag->setHotSpot(m_hotSpot); + + drag->setMimeData(this); +} + +QDesignerMimeData::~QDesignerMimeData() +{ + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + for (QDesignerDnDItems::const_iterator it = m_items.constBegin(); it != cend; ++it ) + delete *it; +} + +Qt::DropAction QDesignerMimeData::proposedDropAction() const +{ + return m_items.first()->type() == QDesignerDnDItemInterface::CopyDrop ? Qt::CopyAction : Qt::MoveAction; +} + +Qt::DropAction QDesignerMimeData::execDrag(const QDesignerDnDItems &items, QWidget * dragSource) +{ + if (items.empty()) + return Qt::IgnoreAction; + + QDrag *drag = new QDrag(dragSource); + QDesignerMimeData *mimeData = new QDesignerMimeData(items, drag); + + // Store pointers to widgets that are to be re-shown if a move operation is canceled + QWidgetList reshowWidgets; + const QDesignerDnDItems::const_iterator cend = items.constEnd(); + for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it ) + if (QWidget *w = (*it)->widget()) + if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop) + reshowWidgets.push_back(w); + + const Qt::DropAction executedAction = drag->exec(Qt::CopyAction|Qt::MoveAction, mimeData->proposedDropAction()); + + if (executedAction == Qt::IgnoreAction && !reshowWidgets.empty()) + foreach (QWidget *w, reshowWidgets) + w->show(); + + return executedAction; +} + + +void QDesignerMimeData::moveDecoration(const QPoint &globalPos) const +{ + const QPoint relativeDistance = globalPos - m_globalStartPos; + const QDesignerDnDItems::const_iterator cend = m_items.constEnd(); + for (QDesignerDnDItems::const_iterator it =m_items.constBegin(); it != cend; ++it ) { + QWidget *w = (*it)->decoration(); + w->move(w->pos() + relativeDistance); + } +} + +void QDesignerMimeData::removeMovedWidgetsFromSourceForm(const QDesignerDnDItems &items) +{ + typedef QMultiMap<FormWindowBase *, QWidget *> FormWidgetMap; + FormWidgetMap formWidgetMap; + // Find moved widgets per form + const QDesignerDnDItems::const_iterator cend = items.constEnd(); + for (QDesignerDnDItems::const_iterator it = items.constBegin(); it != cend; ++it ) + if ((*it)->type() == QDesignerDnDItemInterface::MoveDrop) + if (QWidget *w = (*it)->widget()) + if (FormWindowBase *fb = qobject_cast<FormWindowBase *>((*it)->source())) + formWidgetMap.insert(fb, w); + if (formWidgetMap.empty()) + return; + + foreach (FormWindowBase * fb, formWidgetMap.keys()) + fb->deleteWidgetList(formWidgetMap.values(fb)); +} + +void QDesignerMimeData::acceptEventWithAction(Qt::DropAction desiredAction, QDropEvent *e) +{ + if (e->proposedAction() == desiredAction) { + e->acceptProposedAction(); + } else { + e->setDropAction(desiredAction); + e->accept(); + } +} + +void QDesignerMimeData::acceptEvent(QDropEvent *e) const +{ + acceptEventWithAction(proposedDropAction(), e); +} + +void QDesignerMimeData::setImageTransparency(QImage &image, int alpha) +{ + const int height = image.height(); + for (int l = 0; l < height; l++) { + QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(l)); + QRgb *lineEnd = line + image.width(); + for ( ; line < lineEnd; line++) { + const QRgb rgba = *line; + *line = qRgba(qRed(rgba), qGreen(rgba), qBlue(rgba), alpha); + } + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_dnditem_p.h b/tools/designer/src/lib/shared/qdesigner_dnditem_p.h new file mode 100644 index 0000000..cf36b39 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_dnditem_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_DNDITEM_H +#define QDESIGNER_DNDITEM_H + +#include "shared_global_p.h" +#include <QtDesigner/abstractdnditem.h> + +#include <QtCore/QPoint> +#include <QtCore/QList> +#include <QtCore/QMimeData> + +QT_BEGIN_NAMESPACE + +class QDrag; +class QImage; +class QDropEvent; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerDnDItem: public QDesignerDnDItemInterface +{ +public: + explicit QDesignerDnDItem(DropType type, QWidget *source = 0); + virtual ~QDesignerDnDItem(); + + virtual DomUI *domUi() const; + virtual QWidget *decoration() const; + virtual QWidget *widget() const; + virtual QPoint hotSpot() const; + virtual QWidget *source() const; + + virtual DropType type() const; + +protected: + void setDomUi(DomUI *dom_ui); + void init(DomUI *ui, QWidget *widget, QWidget *decoration, const QPoint &global_mouse_pos); + +private: + QWidget *m_source; + const DropType m_type; + const QPoint m_globalStartPos; + DomUI *m_dom_ui; + QWidget *m_widget; + QWidget *m_decoration; + QPoint m_hot_spot; + + Q_DISABLE_COPY(QDesignerDnDItem) +}; + +// Mime data for use with designer drag and drop operations. + +class QDESIGNER_SHARED_EXPORT QDesignerMimeData : public QMimeData { + Q_OBJECT + +public: + typedef QList<QDesignerDnDItemInterface *> QDesignerDnDItems; + + virtual ~QDesignerMimeData(); + + const QDesignerDnDItems &items() const { return m_items; } + + // Execute a drag and drop operation. + static Qt::DropAction execDrag(const QDesignerDnDItems &items, QWidget * dragSource); + + QPoint hotSpot() const { return m_hotSpot; } + + // Move the decoration. Required for drops over form windows as the position + // is derived from the decoration position. + void moveDecoration(const QPoint &globalPos) const; + + // For a move operation, create the undo command sequence to remove + // the widgets from the source form. + static void removeMovedWidgetsFromSourceForm(const QDesignerDnDItems &items); + + // Accept an event with the proper action. + void acceptEvent(QDropEvent *e) const; + + // Helper to accept an event with the desired action. + static void acceptEventWithAction(Qt::DropAction desiredAction, QDropEvent *e); + +private: + QDesignerMimeData(const QDesignerDnDItems &items, QDrag *drag); + Qt::DropAction proposedDropAction() const; + + static void setImageTransparency(QImage &image, int alpha); + + const QDesignerDnDItems m_items; + QPoint m_globalStartPos; + QPoint m_hotSpot; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_DNDITEM_H diff --git a/tools/designer/src/lib/shared/qdesigner_dockwidget.cpp b/tools/designer/src/lib/shared/qdesigner_dockwidget.cpp new file mode 100644 index 0000000..b678982 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_dockwidget.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_dockwidget_p.h" +#include "layoutinfo_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormWindowCursorInterface> + +#include <QtGui/QMainWindow> +#include <QtGui/QLayout> + +QT_BEGIN_NAMESPACE + +QDesignerDockWidget::QDesignerDockWidget(QWidget *parent) + : QDockWidget(parent) +{ +} + +QDesignerDockWidget::~QDesignerDockWidget() +{ +} + +bool QDesignerDockWidget::docked() const +{ + return qobject_cast<QMainWindow*>(parentWidget()) != 0; +} + +void QDesignerDockWidget::setDocked(bool b) +{ + if (QMainWindow *mainWindow = findMainWindow()) { + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerContainerExtension *c; + c = qt_extension<QDesignerContainerExtension*>(core->extensionManager(), mainWindow); + if (b && !docked()) { + // Dock it + // ### undo/redo stack + setParent(0); + c->addWidget(this); + formWindow()->selectWidget(this, formWindow()->cursor()->isWidgetSelected(this)); + } else if (!b && docked()) { + // Undock it + for (int i = 0; i < c->count(); ++i) { + if (c->widget(i) == this) { + c->remove(i); + break; + } + } + // #### restore the position + setParent(mainWindow->centralWidget()); + show(); + formWindow()->selectWidget(this, formWindow()->cursor()->isWidgetSelected(this)); + } + } +} + +Qt::DockWidgetArea QDesignerDockWidget::dockWidgetArea() const +{ + if (QMainWindow *mainWindow = qobject_cast<QMainWindow*>(parentWidget())) + return mainWindow->dockWidgetArea(const_cast<QDesignerDockWidget*>(this)); + + return Qt::LeftDockWidgetArea; +} + +void QDesignerDockWidget::setDockWidgetArea(Qt::DockWidgetArea dockWidgetArea) +{ + if (QMainWindow *mainWindow = qobject_cast<QMainWindow*>(parentWidget())) { + if ((dockWidgetArea != Qt::NoDockWidgetArea) + && isAreaAllowed(dockWidgetArea)) { + mainWindow->addDockWidget(dockWidgetArea, this); + } + } +} + +bool QDesignerDockWidget::inMainWindow() const +{ + QMainWindow *mw = findMainWindow(); + if (mw && !mw->centralWidget()->layout()) { + if (mw == parentWidget()) + return true; + if (mw->centralWidget() == parentWidget()) + return true; + } + return false; +} + +QDesignerFormWindowInterface *QDesignerDockWidget::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast<QDesignerDockWidget*>(this)); +} + +QMainWindow *QDesignerDockWidget::findMainWindow() const +{ + if (QDesignerFormWindowInterface *fw = formWindow()) + return qobject_cast<QMainWindow*>(fw->mainContainer()); + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_dockwidget_p.h b/tools/designer/src/lib/shared/qdesigner_dockwidget_p.h new file mode 100644 index 0000000..f055908 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_dockwidget_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_DOCKWIDGET_H +#define QDESIGNER_DOCKWIDGET_H + +#include "shared_global_p.h" +#include <QtGui/QDockWidget> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QDESIGNER_SHARED_EXPORT QDesignerDockWidget: public QDockWidget +{ + Q_OBJECT + Q_PROPERTY(Qt::DockWidgetArea dockWidgetArea READ dockWidgetArea WRITE setDockWidgetArea DESIGNABLE docked STORED false) + Q_PROPERTY(bool docked READ docked WRITE setDocked DESIGNABLE inMainWindow STORED false) +public: + QDesignerDockWidget(QWidget *parent = 0); + virtual ~QDesignerDockWidget(); + + bool docked() const; + void setDocked(bool b); + + Qt::DockWidgetArea dockWidgetArea() const; + void setDockWidgetArea(Qt::DockWidgetArea dockWidgetArea); + + bool inMainWindow() const; + +private: + QDesignerFormWindowInterface *formWindow() const; + QMainWindow *findMainWindow() const; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_DOCKWIDGET_H diff --git a/tools/designer/src/lib/shared/qdesigner_formbuilder.cpp b/tools/designer/src/lib/shared/qdesigner_formbuilder.cpp new file mode 100644 index 0000000..6394847 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formbuilder.cpp @@ -0,0 +1,478 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_formbuilder_p.h" +#include "dynamicpropertysheet.h" +#include "qsimpleresource_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_introspection_p.h" + +#include <ui4_p.h> +#include <formbuilderextra_p.h> +// sdk +#include <QtDesigner/container.h> +#include <QtDesigner/customwidget.h> +#include <QtDesigner/propertysheet.h> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QDesignerCustomWidgetInterface> +#include <abstractdialoggui_p.h> + +// shared +#include <qdesigner_propertysheet_p.h> +#include <qdesigner_utils_p.h> +#include <formwindowbase_p.h> +#include <qtresourcemodel_p.h> +#include <scripterrordialog_p.h> + +#include <QtGui/QWidget> +#include <QtGui/QMenu> +#include <QtGui/QToolBar> +#include <QtGui/QMenuBar> +#include <QtGui/QMainWindow> +#include <QtGui/QStyleFactory> +#include <QtGui/QStyle> +#include <QtGui/QApplication> +#include <QtGui/QAbstractScrollArea> +#include <QtGui/QMessageBox> +#include <QtGui/QPixmap> + +#include <QtCore/QBuffer> +#include <QtCore/qdebug.h> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +static QString summarizeScriptErrors(const QFormScriptRunner::Errors &errors) +{ + QString rc = QCoreApplication::translate("QDesignerFormBuilder", "Script errors occurred:"); + foreach (QFormScriptRunner::Error error, errors) { + rc += QLatin1Char('\n'); + rc += error.errorMessage; + } + return rc; +} + +namespace qdesigner_internal { + +QDesignerFormBuilder::QDesignerFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile) : + m_core(core), + m_mode(mode), + m_deviceProfile(deviceProfile), + m_pixmapCache(0), + m_iconCache(0), + m_ignoreCreateResources(false), + m_tempResourceSet(0), + m_mainWidget(true) +{ + Q_ASSERT(m_core); + // Disable scripting in the editors. + QFormScriptRunner::Options options = formScriptRunner()->options(); + switch (m_mode) { + case DisableScripts: + options |= QFormScriptRunner::DisableScripts; + break; + case EnableScripts: + options |= QFormScriptRunner::DisableWarnings; + options &= ~QFormScriptRunner::DisableScripts; + break; + } + formScriptRunner()-> setOptions(options); +} + +QString QDesignerFormBuilder::systemStyle() const +{ + return m_deviceProfile.isEmpty() ? + QString::fromUtf8(QApplication::style()->metaObject()->className()) : + m_deviceProfile.style(); +} + +QWidget *QDesignerFormBuilder::createWidgetFromContents(const QString &contents, QWidget *parentWidget) +{ + QByteArray data = contents.toUtf8(); + QBuffer buffer(&data); + buffer.open(QIODevice::ReadOnly); + return load(&buffer, parentWidget); +} + +QWidget *QDesignerFormBuilder::create(DomUI *ui, QWidget *parentWidget) +{ + m_mainWidget = true; + QtResourceSet *resourceSet = core()->resourceModel()->currentResourceSet(); + + // reload resource properties; + createResources(ui->elementResources()); + core()->resourceModel()->setCurrentResourceSet(m_tempResourceSet); + + m_ignoreCreateResources = true; + DesignerPixmapCache pixmapCache; + DesignerIconCache iconCache(&pixmapCache); + m_pixmapCache = &pixmapCache; + m_iconCache = &iconCache; + + QWidget *widget = QFormBuilder::create(ui, parentWidget); + + core()->resourceModel()->setCurrentResourceSet(resourceSet); + core()->resourceModel()->removeResourceSet(m_tempResourceSet); + m_tempResourceSet = 0; + m_ignoreCreateResources = false; + m_pixmapCache = 0; + m_iconCache = 0; + + m_customWidgetsWithScript.clear(); + return widget; +} + +QWidget *QDesignerFormBuilder::createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name) +{ + QWidget *widget = 0; + + if (widgetName == QLatin1String("QToolBar")) { + widget = new QToolBar(parentWidget); + } else if (widgetName == QLatin1String("QMenu")) { + widget = new QMenu(parentWidget); + } else if (widgetName == QLatin1String("QMenuBar")) { + widget = new QMenuBar(parentWidget); + } else { + widget = core()->widgetFactory()->createWidget(widgetName, parentWidget); + } + + if (widget) { + widget->setObjectName(name); + if (QSimpleResource::hasCustomWidgetScript(m_core, widget)) + m_customWidgetsWithScript.insert(widget); + } + + if (m_mainWidget) { // We need to apply the DPI here to take effect on size hints, etc. + m_deviceProfile.apply(m_core, widget, DeviceProfile::ApplyPreview); + m_mainWidget = false; + } + return widget; +} + +bool QDesignerFormBuilder::addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + // Use container extension or rely on scripts unless main window. + if (QFormBuilder::addItem(ui_widget, widget, parentWidget)) + return true; + + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), parentWidget)) { + container->addWidget(widget); + return true; + } + return false; +} + +bool QDesignerFormBuilder::addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout) +{ + return QFormBuilder::addItem(ui_item, item, layout); +} + +QIcon QDesignerFormBuilder::nameToIcon(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QDesignerFormBuilder::nameToIcon() is obsoleted"; + return QIcon(); +} + +QPixmap QDesignerFormBuilder::nameToPixmap(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QDesignerFormBuilder::nameToPixmap() is obsoleted"; + return QPixmap(); +} + +/* If the property is a enum or flag value, retrieve + * the existing enum/flag type via property sheet and use it to convert */ + +static bool readDomEnumerationValue(const DomProperty *p, + const QDesignerPropertySheetExtension* sheet, + QVariant &v) +{ + switch (p->kind()) { + case DomProperty::Set: { + const int index = sheet->indexOf(p->attributeName()); + if (index == -1) + return false; + const QVariant sheetValue = sheet->property(index); + if (qVariantCanConvert<PropertySheetFlagValue>(sheetValue)) { + const PropertySheetFlagValue f = qvariant_cast<PropertySheetFlagValue>(sheetValue); + bool ok = false; + v = f.metaFlags.parseFlags(p->elementSet(), &ok); + if (!ok) + designerWarning(f.metaFlags.messageParseFailed(p->elementSet())); + return true; + } + } + break; + case DomProperty::Enum: { + const int index = sheet->indexOf(p->attributeName()); + if (index == -1) + return false; + const QVariant sheetValue = sheet->property(index); + if (qVariantCanConvert<PropertySheetEnumValue>(sheetValue)) { + const PropertySheetEnumValue e = qvariant_cast<PropertySheetEnumValue>(sheetValue); + bool ok = false; + v = e.metaEnum.parseEnum(p->elementEnum(), &ok); + if (!ok) + designerWarning(e.metaEnum.messageParseFailed(p->elementEnum())); + return true; + } + } + break; + default: + break; + } + return false; +} + +void QDesignerFormBuilder::applyProperties(QObject *o, const QList<DomProperty*> &properties) +{ + typedef QList<DomProperty*> DomPropertyList; + + if (properties.empty()) + return; + + QFormBuilderExtra *formBuilderExtra = QFormBuilderExtra::instance(this); + const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), o); + const QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core()->extensionManager(), o); + const bool changingMetaObject = WidgetFactory::classNameOf(core(), o) == QLatin1String("QAxWidget"); + const QDesignerMetaObjectInterface *meta = core()->introspection()->metaObject(o); + const bool dynamicPropertiesAllowed = dynamicSheet && dynamicSheet->dynamicPropertiesAllowed(); + + QDesignerPropertySheet *designerPropertySheet = qobject_cast<QDesignerPropertySheet *>( + core()->extensionManager()->extension(o, Q_TYPEID(QDesignerPropertySheetExtension))); + + if (designerPropertySheet) { + if (designerPropertySheet->pixmapCache()) + designerPropertySheet->setPixmapCache(m_pixmapCache); + if (designerPropertySheet->iconCache()) + designerPropertySheet->setIconCache(m_iconCache); + } + + const DomPropertyList::const_iterator cend = properties.constEnd(); + for (DomPropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + DomProperty *p = *it; + QVariant v; + if (!readDomEnumerationValue(p, sheet, v)) + v = toVariant(o->metaObject(), p); + + if (v.isNull()) + continue; + + const QString attributeName = p->attributeName(); + if (formBuilderExtra->applyPropertyInternally(o, attributeName, v)) + continue; + + // refuse fake properties like current tab name (weak test) + if (!dynamicPropertiesAllowed) { + if (changingMetaObject) // Changes after setting control of QAxWidget + meta = core()->introspection()->metaObject(o); + if (meta->indexOfProperty(attributeName) == -1) + continue; + } + + QObject *obj = o; + QAbstractScrollArea *scroll = qobject_cast<QAbstractScrollArea *>(o); + if (scroll && attributeName == QLatin1String("cursor") && scroll->viewport()) + obj = scroll->viewport(); + + // a real property + obj->setProperty(attributeName.toUtf8(), v); + } +} + +DomWidget *QDesignerFormBuilder::createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive) +{ + DomWidget *ui_widget = QFormBuilder::createDom(widget, ui_parentWidget, recursive); + QSimpleResource::addExtensionDataToDOM(this, m_core, ui_widget, widget); + return ui_widget; +} + +QWidget *QDesignerFormBuilder::create(DomWidget *ui_widget, QWidget *parentWidget) +{ + QWidget *widget = QFormBuilder::create(ui_widget, parentWidget); + // Do not apply state if scripts are to be run in preview mode + QSimpleResource::applyExtensionDataFromDOM(this, m_core, ui_widget, widget, m_mode == DisableScripts); + return widget; +} + +void QDesignerFormBuilder::createResources(DomResources *resources) +{ + if (m_ignoreCreateResources) + return; + QStringList paths; + if (resources != 0) { + const QList<DomResource*> dom_include = resources->elementInclude(); + foreach (DomResource *res, dom_include) { + QString path = QDir::cleanPath(workingDirectory().absoluteFilePath(res->attributeLocation())); + paths << path; + } + } + + m_tempResourceSet = core()->resourceModel()->addResourceSet(paths); +} + +QLayout *QDesignerFormBuilder::create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget) +{ + return QFormBuilder::create(ui_layout, layout, parentWidget); +} + +void QDesignerFormBuilder::loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget) +{ + QFormBuilder::loadExtraInfo(ui_widget, widget, parentWidget); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + const DeviceProfile &deviceProfile, + ScriptErrors *scriptErrors, + QString *errorMessage) +{ + scriptErrors->clear(); + + // load + QDesignerFormBuilder builder(fw->core(), EnableScripts, deviceProfile); + builder.setWorkingDirectory(fw->absoluteDir()); + + const bool warningsEnabled = QSimpleResource::setWarningsEnabled(false); + QByteArray bytes = fw->contents().toUtf8(); + QSimpleResource::setWarningsEnabled(warningsEnabled); + + QBuffer buffer(&bytes); + buffer.open(QIODevice::ReadOnly); + + QWidget *widget = builder.load(&buffer, 0); + if (!widget) { // Shouldn't happen + *errorMessage = QCoreApplication::translate("QDesignerFormBuilder", "The preview failed to build."); + return 0; + } + // Make sure palette is applied + const QString styleToUse = styleName.isEmpty() ? builder.deviceProfile().style() : styleName; + if (!styleToUse.isEmpty()) { + if (WidgetFactory *wf = qobject_cast<qdesigner_internal::WidgetFactory *>(fw->core()->widgetFactory())) { + if (styleToUse != wf->styleName()) + WidgetFactory::applyStyleToTopLevel(wf->getStyle(styleToUse), widget); + } + } + // Check for script errors + *scriptErrors = builder.formScriptRunner()->errors(); + if (!scriptErrors->empty()) { + *errorMessage = summarizeScriptErrors(*scriptErrors); + delete widget; + return 0; + } + // Fake application style sheet by prepending. (If this doesn't work, fake by nesting + // into parent widget). + if (!appStyleSheet.isEmpty()) { + QString styleSheet = appStyleSheet; + styleSheet += QLatin1Char('\n'); + styleSheet += widget->styleSheet(); + widget->setStyleSheet(styleSheet); + } + return widget; +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName) +{ + return createPreview(fw, styleName, QString()); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + const DeviceProfile &deviceProfile, + QString *errorMessage) +{ + ScriptErrors scriptErrors; + return createPreview(fw, styleName, appStyleSheet, deviceProfile, &scriptErrors, errorMessage); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, + const QString &styleName, + const QString &appStyleSheet, + QString *errorMessage) +{ + ScriptErrors scriptErrors; + return createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, errorMessage); +} + +QWidget *QDesignerFormBuilder::createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) +{ + ScriptErrors scriptErrors; + QString errorMessage; + QWidget *widget = createPreview(fw, styleName, appStyleSheet, DeviceProfile(), &scriptErrors, &errorMessage); + if (!widget) { + // Display Script errors or message box + QWidget *dialogParent = fw->core()->topLevel(); + if (scriptErrors.empty()) { + fw->core()->dialogGui()->message(dialogParent, QDesignerDialogGuiInterface::PreviewFailureMessage, + QMessageBox::Warning, QCoreApplication::translate("QDesignerFormBuilder", "Designer"), errorMessage, QMessageBox::Ok); + } else { + ScriptErrorDialog scriptErrorDialog(scriptErrors, dialogParent); + scriptErrorDialog.exec(); + } + return 0; + } + return widget; +} + +QPixmap QDesignerFormBuilder::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet) +{ + QWidget *widget = createPreview(fw, styleName, appStyleSheet); + if (!widget) + return QPixmap(); + + const QPixmap rc = QPixmap::grabWidget (widget); + widget->deleteLater(); + return rc; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_formbuilder_p.h b/tools/designer/src/lib/shared/qdesigner_formbuilder_p.h new file mode 100644 index 0000000..b982e1c --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formbuilder_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMBUILDER_H +#define QDESIGNER_FORMBUILDER_H + +#include "shared_global_p.h" +#include "deviceprofile_p.h" + +#include <QtDesigner/private/formscriptrunner_p.h> +#include <QtDesigner/formbuilder.h> + +#include <QtCore/QMap> +#include <QtCore/QSet> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; + +class QPixmap; +class QtResourceSet; + +namespace qdesigner_internal { + +class DesignerPixmapCache; +class DesignerIconCache; + +/* Form builder used for previewing forms, widget box and new form dialog. + * It applies the system settings to its toplevel window. */ + +class QDESIGNER_SHARED_EXPORT QDesignerFormBuilder: public QFormBuilder +{ +public: + enum Mode { + DisableScripts, + EnableScripts + }; + + QDesignerFormBuilder(QDesignerFormEditorInterface *core, + Mode mode, + const DeviceProfile &deviceProfile = DeviceProfile()); + + QWidget *createWidgetFromContents(const QString &contents, QWidget *parentWidget = 0); + + virtual QWidget *createWidget(DomWidget *ui_widget, QWidget *parentWidget = 0) + { return QFormBuilder::create(ui_widget, parentWidget); } + + inline QDesignerFormEditorInterface *core() const + { return m_core; } + + QString systemStyle() const; + + typedef QFormScriptRunner::Errors ScriptErrors; + // Create a preview widget (for integrations) or return 0. The widget has to be embedded into a main window. + // Experimental, depending on script support. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName /* ="" */, + const QString &appStyleSheet /* ="" */, + const DeviceProfile &deviceProfile, + ScriptErrors *scriptErrors, QString *errorMessage); + // Convenience that pops up message boxes in case of failures. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName = QString()); + // Create a preview widget (for integrations) or return 0. The widget has to be embedded into a main window. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet, QString *errorMessage); + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet, const DeviceProfile &deviceProfile, QString *errorMessage); + // Convenience that pops up message boxes in case of failures. + static QWidget *createPreview(const QDesignerFormWindowInterface *fw, const QString &styleName, const QString &appStyleSheet); + + // Create a preview image + static QPixmap createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &styleName = QString(), const QString &appStyleSheet = QString()); + +protected: + using QFormBuilder::createDom; + using QFormBuilder::create; + + virtual QWidget *create(DomUI *ui, QWidget *parentWidget); + virtual DomWidget *createDom(QWidget *widget, DomWidget *ui_parentWidget, bool recursive = true); + virtual QWidget *create(DomWidget *ui_widget, QWidget *parentWidget); + virtual QLayout *create(DomLayout *ui_layout, QLayout *layout, QWidget *parentWidget); + virtual void createResources(DomResources *resources); + + virtual QWidget *createWidget(const QString &widgetName, QWidget *parentWidget, const QString &name); + virtual bool addItem(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + virtual bool addItem(DomLayoutItem *ui_item, QLayoutItem *item, QLayout *layout); + + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath); + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath); + + virtual void applyProperties(QObject *o, const QList<DomProperty*> &properties); + + virtual void loadExtraInfo(DomWidget *ui_widget, QWidget *widget, QWidget *parentWidget); + + QtResourceSet *internalResourceSet() const { return m_tempResourceSet; } + + DeviceProfile deviceProfile() const { return m_deviceProfile; } + +private: + QDesignerFormEditorInterface *m_core; + const Mode m_mode; + + typedef QSet<QWidget *> WidgetSet; + WidgetSet m_customWidgetsWithScript; + + const DeviceProfile m_deviceProfile; + + DesignerPixmapCache *m_pixmapCache; + DesignerIconCache *m_iconCache; + bool m_ignoreCreateResources; + QtResourceSet *m_tempResourceSet; + bool m_mainWidget; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMBUILDER_H diff --git a/tools/designer/src/lib/shared/qdesigner_formeditorcommand.cpp b/tools/designer/src/lib/shared/qdesigner_formeditorcommand.cpp new file mode 100644 index 0000000..5e5e661 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formeditorcommand.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qdesigner_formeditorcommand_p.h" +#include <QtDesigner/QDesignerFormEditorInterface> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---- QDesignerFormEditorCommand ---- +QDesignerFormEditorCommand::QDesignerFormEditorCommand(const QString &description, QDesignerFormEditorInterface *core) + : QUndoCommand(description), + m_core(core) +{ +} + +QDesignerFormEditorInterface *QDesignerFormEditorCommand::core() const +{ + return m_core; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_formeditorcommand_p.h b/tools/designer/src/lib/shared/qdesigner_formeditorcommand_p.h new file mode 100644 index 0000000..b0fdd77 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formeditorcommand_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMEDITORCOMMAND_H +#define QDESIGNER_FORMEDITORCOMMAND_H + +#include "shared_global_p.h" +#include <QtCore/QPointer> +#include <QtGui/QUndoCommand> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerFormEditorCommand: public QUndoCommand +{ + +public: + QDesignerFormEditorCommand(const QString &description, QDesignerFormEditorInterface *core); + +protected: + QDesignerFormEditorInterface *core() const; + +private: + QPointer<QDesignerFormEditorInterface> m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMEDITORCOMMAND_H diff --git a/tools/designer/src/lib/shared/qdesigner_formwindowcommand.cpp b/tools/designer/src/lib/shared/qdesigner_formwindowcommand.cpp new file mode 100644 index 0000000..9d6d57e --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formwindowcommand.cpp @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "qdesigner_formwindowcommand_p.h" +#include "qdesigner_objectinspector_p.h" +#include "layout_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtDesigner/QDesignerActionEditorInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QVariant> +#include <QtGui/QWidget> +#include <QtGui/QLabel> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---- QDesignerFormWindowCommand ---- +QDesignerFormWindowCommand::QDesignerFormWindowCommand(const QString &description, QDesignerFormWindowInterface *formWindow) + : QUndoCommand(description), + m_formWindow(formWindow) +{ +} + +QDesignerFormWindowInterface *QDesignerFormWindowCommand::formWindow() const +{ + return m_formWindow; +} + +QDesignerFormEditorInterface *QDesignerFormWindowCommand::core() const +{ + if (QDesignerFormWindowInterface *fw = formWindow()) + return fw->core(); + + return 0; +} + +void QDesignerFormWindowCommand::undo() +{ + cheapUpdate(); +} + +void QDesignerFormWindowCommand::redo() +{ + cheapUpdate(); +} + +void QDesignerFormWindowCommand::cheapUpdate() +{ + if (core()->objectInspector()) + core()->objectInspector()->setFormWindow(formWindow()); + + if (core()->actionEditor()) + core()->actionEditor()->setFormWindow(formWindow()); +} + +QDesignerPropertySheetExtension* QDesignerFormWindowCommand::propertySheet(QObject *object) const +{ + return qt_extension<QDesignerPropertySheetExtension*>(formWindow()->core()->extensionManager(), object); +} + +void QDesignerFormWindowCommand::updateBuddies(QDesignerFormWindowInterface *form, + const QString &old_name, + const QString &new_name) +{ + QExtensionManager* extensionManager = form->core()->extensionManager(); + + typedef QList<QLabel*> LabelList; + + const LabelList label_list = qFindChildren<QLabel*>(form); + if (label_list.empty()) + return; + + const QString buddyProperty = QLatin1String("buddy"); + const QByteArray oldNameU8 = old_name.toUtf8(); + const QByteArray newNameU8 = new_name.toUtf8(); + + const LabelList::const_iterator cend = label_list.constEnd(); + for (LabelList::const_iterator it = label_list.constBegin(); it != cend; ++it ) { + if (QDesignerPropertySheetExtension* sheet = qt_extension<QDesignerPropertySheetExtension*>(extensionManager, *it)) { + const int idx = sheet->indexOf(buddyProperty); + if (idx != -1) { + const QByteArray oldBuddy = sheet->property(idx).toByteArray(); + if (oldBuddy == oldNameU8) + sheet->setProperty(idx, newNameU8); + } + } + } +} + +void QDesignerFormWindowCommand::selectUnmanagedObject(QObject *unmanagedObject) +{ + // Keep selection in sync + if (QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core()->objectInspector())) { + oi->clearSelection(); + oi->selectObject(unmanagedObject); + } + core()->propertyEditor()->setObject(unmanagedObject); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_formwindowcommand_p.h b/tools/designer/src/lib/shared/qdesigner_formwindowcommand_p.h new file mode 100644 index 0000000..f640e66 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formwindowcommand_p.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMWINDOWCOMMAND_H +#define QDESIGNER_FORMWINDOWCOMMAND_H + +#include "shared_global_p.h" + +#include <QtCore/QPointer> +#include <QtGui/QUndoCommand> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerPropertySheetExtension; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand: public QUndoCommand +{ + +public: + QDesignerFormWindowCommand(const QString &description, QDesignerFormWindowInterface *formWindow); + + virtual void undo(); + virtual void redo(); + + static void updateBuddies(QDesignerFormWindowInterface *form, + const QString &old_name, const QString &new_name); +protected: + QDesignerFormWindowInterface *formWindow() const; + QDesignerFormEditorInterface *core() const; + QDesignerPropertySheetExtension* propertySheet(QObject *object) const; + + void cheapUpdate(); + + void selectUnmanagedObject(QObject *unmanagedObject); +private: + QPointer<QDesignerFormWindowInterface> m_formWindow; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_COMMAND_H diff --git a/tools/designer/src/lib/shared/qdesigner_formwindowmanager.cpp b/tools/designer/src/lib/shared/qdesigner_formwindowmanager.cpp new file mode 100644 index 0000000..87213b4 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formwindowmanager.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_formwindowmanager_p.h" +#include "plugindialog_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +/*! + \class QDesignerFormWindowManager + + Extends QDesignerFormWindowManagerInterface with methods to control + the preview and printing of forms. It provides a facade that simplifies + the complexity of the more general PreviewConfiguration & PreviewManager + interfaces. + + \since 4.5 + */ + + +QDesignerFormWindowManager::QDesignerFormWindowManager(QObject *parent) + : QDesignerFormWindowManagerInterface(parent), m_unused(0) +{ +} + +QDesignerFormWindowManager::~QDesignerFormWindowManager() +{ +} + +/*! + Allows you to intervene and control \QD's form "Preview" action. The + function returns the original action. + + \since 4.5 + */ +QAction *QDesignerFormWindowManager::actionDefaultPreview() const +{ + return 0; +} + +/*! + Allows you to intervene and control \QD's form "Preview in" style action. The + function returns the original list of actions. + + The method calls PreviewManager::createStyleActionGroup() internally. + + \since 4.5 + */ +QActionGroup *QDesignerFormWindowManager::actionGroupPreviewInStyle() const +{ + return 0; +} + +/*! + \fn QPixmap QDesignerFormWindowManager::createPreviewPixmap(QString *errorMessage) + + Creates a pixmap representing the preview of the currently active form. + + The method calls PreviewManager::createPreviewPixmap() internally. + + \since 4.5 + */ + + +/*! + \fn QPixmap QDesignerFormWindowManager::closeAllPreviews() + + Closes all preview windows generated by actionDefaultPreview, actionGroupPreviewInStyle + and the corresponding methods in PreviewManager. + + \since 4.5 + */ + +/*! + \fn PreviewManager *QDesignerFormWindowManager::previewManager() + + Accesses the previewmanager implementation. + + \since 4.5 + \internal + */ + +/*! + \fn QAction *QDesignerFormWindowManager::actionShowFormWindowSettingsDialog() const; + + Allows you to intervene and control \QD's form "Form Settings" action. The + function returns the original action. + + \since 4.5 + \internal + */ + +QAction *QDesignerFormWindowManager::actionShowFormWindowSettingsDialog() const +{ + return 0; +} + +/*! + \fn void QDesignerFormWindowManager::aboutPlugins() + + Pops up an "About plugins" dialog. + + \since 4.5 + \internal + */ + +void QDesignerFormWindowManager::aboutPlugins() +{ + PluginDialog dlg(core(), core()->topLevel()); + dlg.exec(); +} + +/*! + \fn + void QDesignerFormWindowManager::formWindowSettingsChanged(QDesignerFormWindowInterface *fw); + + This signal is emitted when the form settings dialog was shown + and changes have been made to the form. + + \since 4.5 + \internal + */ + + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_formwindowmanager_p.h b/tools/designer/src/lib/shared/qdesigner_formwindowmanager_p.h new file mode 100644 index 0000000..61aee72 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_formwindowmanager_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_FORMWINDOMANAGER_H +#define QDESIGNER_FORMWINDOMANAGER_H + +#include "shared_global_p.h" +#include <QtDesigner/QDesignerFormWindowManagerInterface> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class PreviewManager; + +// +// Convenience methods to manage form previews (ultimately forwarded to PreviewManager). +// +class QDESIGNER_SHARED_EXPORT QDesignerFormWindowManager + : public QDesignerFormWindowManagerInterface +{ + Q_OBJECT +public: + explicit QDesignerFormWindowManager(QObject *parent = 0); + virtual ~QDesignerFormWindowManager(); + + virtual QAction *actionDefaultPreview() const; + virtual QActionGroup *actionGroupPreviewInStyle() const; + virtual QAction *actionShowFormWindowSettingsDialog() const; + + virtual QPixmap createPreviewPixmap(QString *errorMessage) = 0; + + virtual PreviewManager *previewManager() const = 0; + +Q_SIGNALS: + void formWindowSettingsChanged(QDesignerFormWindowInterface *fw); + +public Q_SLOTS: + virtual void closeAllPreviews() = 0; + void aboutPlugins(); + +private: + void *m_unused; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_FORMWINDOMANAGER_H diff --git a/tools/designer/src/lib/shared/qdesigner_integration.cpp b/tools/designer/src/lib/shared/qdesigner_integration.cpp new file mode 100644 index 0000000..02e0246 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_integration.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_integration_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_objectinspector_p.h" +#include "widgetdatabase_p.h" +#include "pluginmanager_p.h" +#include "widgetfactory_p.h" +#include "qdesigner_widgetbox_p.h" +#include "qtgradientmanager.h" +#include "qtgradientutils.h" +#include "qtresourcemodel_p.h" + +// sdk +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerActionEditorInterface> +#include <QtDesigner/QDesignerWidgetBoxInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerResourceBrowserInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> + +#include <QtCore/QVariant> +#include <QtCore/QFile> +#include <QtCore/QDir> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// ---------------- DesignerIntegrationPrivate +class QDesignerIntegrationPrivate { +public: + QDesignerIntegrationPrivate() + : m_gradientManager(0), + m_fileWatcherBehaviour(QDesignerIntegration::PromptAndReload), + m_resourceEditingEnabled(true), + m_slotNavigationEnabled(false) + {} + + QString m_gradientsPath; + QtGradientManager *m_gradientManager; + QDesignerIntegration::ResourceFileWatcherBehaviour m_fileWatcherBehaviour; + bool m_resourceEditingEnabled; + bool m_slotNavigationEnabled; +}; + +// -------------- QDesignerIntegration +// As of 4.4, the header will be distributed with the Eclipse plugin. + +QDesignerIntegration::QDesignerIntegration(QDesignerFormEditorInterface *core, QObject *parent) : + QDesignerIntegrationInterface(core, parent), + m_d(new QDesignerIntegrationPrivate) +{ + initialize(); +} + +QDesignerIntegration::~QDesignerIntegration() +{ + QFile f(m_d->m_gradientsPath); + if (f.open(QIODevice::WriteOnly)) { + f.write(QtGradientUtils::saveState(m_d->m_gradientManager).toUtf8()); + f.close(); + } + delete m_d; +} + +void QDesignerIntegration::initialize() +{ + // + // integrate the `Form Editor component' + // + + // Extensions + if (QDesignerPropertyEditor *designerPropertyEditor= qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor())) { + connect(designerPropertyEditor, SIGNAL(propertyValueChanged(QString, QVariant, bool)), this, SLOT(updateProperty(QString, QVariant, bool))); + connect(designerPropertyEditor, SIGNAL(resetProperty(QString)), this, SLOT(resetProperty(QString))); + connect(designerPropertyEditor, SIGNAL(addDynamicProperty(QString,QVariant)), + this, SLOT(addDynamicProperty(QString,QVariant))); + connect(designerPropertyEditor, SIGNAL(removeDynamicProperty(QString)), + this, SLOT(removeDynamicProperty(QString))); + } else { + connect(core()->propertyEditor(), SIGNAL(propertyChanged(QString,QVariant)), + this, SLOT(updatePropertyPrivate(QString,QVariant))); + } + + connect(core()->formWindowManager(), SIGNAL(formWindowAdded(QDesignerFormWindowInterface*)), + this, SLOT(setupFormWindow(QDesignerFormWindowInterface*))); + + connect(core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), + this, SLOT(updateActiveFormWindow(QDesignerFormWindowInterface*))); + + m_d->m_gradientManager = new QtGradientManager(this); + core()->setGradientManager(m_d->m_gradientManager); + + QString designerFolder = QDir::homePath(); + designerFolder += QDir::separator(); + designerFolder += QLatin1String(".designer"); + m_d->m_gradientsPath = designerFolder; + m_d->m_gradientsPath += QDir::separator(); + m_d->m_gradientsPath += QLatin1String("gradients.xml"); + + QFile f(m_d->m_gradientsPath); + if (f.open(QIODevice::ReadOnly)) { + QtGradientUtils::restoreState(m_d->m_gradientManager, QString::fromAscii(f.readAll())); + f.close(); + } else { + QFile defaultGradients(QLatin1String(":/trolltech/designer/defaultgradients.xml")); + if (defaultGradients.open(QIODevice::ReadOnly)) { + QtGradientUtils::restoreState(m_d->m_gradientManager, QString::fromAscii(defaultGradients.readAll())); + defaultGradients.close(); + } + } + + if (WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase*>(core()->widgetDataBase())) + widgetDataBase->grabStandardWidgetBoxIcons(); +} + +void QDesignerIntegration::updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + SetPropertyCommand *cmd = new SetPropertyCommand(formWindow); + // find a reference object to compare to and to find the right group + if (cmd->init(selection.selection(), name, value, propertyEditorObject(), enableSubPropertyHandling)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "Unable to set property " << name << '.'; + } + + emit propertyChanged(formWindow, name, value); +} + +void QDesignerIntegration::updatePropertyPrivate(const QString &name, const QVariant &value) +{ + updateProperty(name, value, true); +} + +void QDesignerIntegration::resetProperty(const QString &name) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + + ResetPropertyCommand *cmd = new ResetPropertyCommand(formWindow); + // find a reference object to find the right group + if (cmd->init(selection.selection(), name, propertyEditorObject())) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to reset property " << name << '.'; + } +} + +void QDesignerIntegration::addDynamicProperty(const QString &name, const QVariant &value) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + AddDynamicPropertyCommand *cmd = new AddDynamicPropertyCommand(formWindow); + if (cmd->init(selection.selection(), propertyEditorObject(), name, value)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to add dynamic property " << name << '.'; + } +} + +void QDesignerIntegration::removeDynamicProperty(const QString &name) +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + Selection selection; + getSelection(selection); + if (selection.empty()) + return; + + RemoveDynamicPropertyCommand *cmd = new RemoveDynamicPropertyCommand(formWindow); + if (cmd->init(selection.selection(), propertyEditorObject(), name)) { + formWindow->commandHistory()->push(cmd); + } else { + delete cmd; + qDebug() << "** WARNING Unable to remove dynamic property " << name << '.'; + } + +} + + +void QDesignerIntegration::updateActiveFormWindow(QDesignerFormWindowInterface *formWindow) +{ + Q_UNUSED(formWindow); + updateSelection(); +} + +void QDesignerIntegration::setupFormWindow(QDesignerFormWindowInterface *formWindow) +{ + connect(formWindow, SIGNAL(selectionChanged()), this, SLOT(updateSelection())); + connect(formWindow, SIGNAL(activated(QWidget*)), this, SLOT(activateWidget(QWidget*))); +} + +void QDesignerIntegration::updateGeometry() +{ +} + +void QDesignerIntegration::updateSelection() +{ + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + QWidget *selection = 0; + + if (formWindow) { + selection = formWindow->cursor()->current(); + } + + if (QDesignerActionEditorInterface *actionEditor = core()->actionEditor()) + actionEditor->setFormWindow(formWindow); + + if (QDesignerPropertyEditorInterface *propertyEditor = core()->propertyEditor()) + propertyEditor->setObject(selection); + + if (QDesignerObjectInspectorInterface *objectInspector = core()->objectInspector()) + objectInspector->setFormWindow(formWindow); + +} + +void QDesignerIntegration::activateWidget(QWidget *widget) +{ + Q_UNUSED(widget); +} + +QWidget *QDesignerIntegration::containerWindow(QWidget *widget) const +{ + // Find the parent window to apply a geometry to. + while (widget) { + if (widget->isWindow()) + break; + if (!qstrcmp(widget->metaObject()->className(), "QMdiSubWindow")) + break; + + widget = widget->parentWidget(); + } + + return widget; +} + +void QDesignerIntegration::getSelection(Selection &s) +{ + // Get multiselection from object inspector + if (QDesignerObjectInspector *designerObjectInspector = qobject_cast<QDesignerObjectInspector *>(core()->objectInspector())) { + designerObjectInspector->getSelection(s); + // Action editor puts actions that are not on the form yet + // into the property editor only. + if (s.empty()) + if (QObject *object = core()->propertyEditor()->object()) + s.objects.push_back(object); + + } else { + // Just in case someone plugs in an old-style object inspector: Emulate selection + s.clear(); + QDesignerFormWindowInterface *formWindow = core()->formWindowManager()->activeFormWindow(); + if (!formWindow) + return; + + QObject *object = core()->propertyEditor()->object(); + if (object->isWidgetType()) { + QWidget *widget = static_cast<QWidget*>(object); + QDesignerFormWindowCursorInterface *cursor = formWindow->cursor(); + if (cursor->isWidgetSelected(widget)) { + s.managed.push_back(widget); + } else { + s.unmanaged.push_back(widget); + } + } else { + s.objects.push_back(object); + } + } +} + +QObject *QDesignerIntegration::propertyEditorObject() +{ + QDesignerPropertyEditorInterface *propertyEditor = core()->propertyEditor(); + if (!propertyEditor) + return 0; + return propertyEditor->object(); +} + +// Load plugins into widget database and factory. +void QDesignerIntegration::initializePlugins(QDesignerFormEditorInterface *formEditor) +{ + // load the plugins + WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase*>(formEditor->widgetDataBase()); + if (widgetDataBase) { + widgetDataBase->loadPlugins(); + } + + if (WidgetFactory *widgetFactory = qobject_cast<WidgetFactory*>(formEditor->widgetFactory())) { + widgetFactory->loadPlugins(); + } + + if (widgetDataBase) { + widgetDataBase->grabDefaultPropertyValues(); + } +} + +void QDesignerIntegration::updateCustomWidgetPlugins() +{ + QDesignerFormEditorInterface *formEditor = core(); + if (QDesignerPluginManager *pm = formEditor->pluginManager()) + pm->registerNewPlugins(); + + initializePlugins(formEditor); + + // Do not just reload the last file as the WidgetBox merges the compiled-in resources + // and $HOME/.designer/widgetbox.xml. This would also double the scratchpad. + if (QDesignerWidgetBox *wb = qobject_cast<QDesignerWidgetBox*>(formEditor->widgetBox())) { + const QDesignerWidgetBox::LoadMode oldLoadMode = wb->loadMode(); + wb->setLoadMode(QDesignerWidgetBox::LoadCustomWidgetsOnly); + wb->load(); + wb->setLoadMode(oldLoadMode); + } +} + +void QDesignerIntegration::emitObjectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName) +{ + emit objectNameChanged(formWindow, object, newName, oldName); +} + +void QDesignerIntegration::emitNavigateToSlot(const QString &objectName, + const QString &signalSignature, + const QStringList ¶meterNames) +{ + emit navigateToSlot(objectName, signalSignature, parameterNames); +} + +void QDesignerIntegration::emitNavigateToSlot(const QString &slotSignature) +{ + emit navigateToSlot(slotSignature); +} + +void QDesignerIntegration::requestHelp(const QDesignerFormEditorInterface *core, const QString &manual, const QString &document) +{ + if (QDesignerIntegration *di = qobject_cast<QDesignerIntegration *>(core->integration())) + emit di->helpRequested(manual, document); +} + +QDesignerResourceBrowserInterface *QDesignerIntegration::createResourceBrowser(QWidget *) +{ + return 0; +} + +void QDesignerIntegration::setResourceFileWatcherBehaviour(ResourceFileWatcherBehaviour behaviour) +{ + m_d->m_fileWatcherBehaviour = behaviour; + core()->resourceModel()->setWatcherEnabled(behaviour != QDesignerIntegration::NoWatcher); +} + +QDesignerIntegration::ResourceFileWatcherBehaviour QDesignerIntegration::resourceFileWatcherBehaviour() const +{ + return m_d->m_fileWatcherBehaviour; +} + +void QDesignerIntegration::setResourceEditingEnabled(bool enable) +{ + m_d->m_resourceEditingEnabled = enable; +} + +bool QDesignerIntegration::isResourceEditingEnabled() const +{ + return m_d->m_resourceEditingEnabled; +} + +void QDesignerIntegration::setSlotNavigationEnabled(bool enable) +{ + m_d->m_slotNavigationEnabled = enable; +} + +bool QDesignerIntegration::isSlotNavigationEnabled() const +{ + return m_d->m_slotNavigationEnabled; +} + +static QString fixHelpClassName(const QString &className) +{ + // ### generalize using the Widget Data Base + if (className == QLatin1String("Line")) + return QLatin1String("QFrame"); + if (className == QLatin1String("Spacer")) + return QLatin1String("QSpacerItem"); + if (className == QLatin1String("QLayoutWidget")) + return QLatin1String("QLayout"); + return className; +} + +// Return class in which the property is defined +static QString classForProperty(QDesignerFormEditorInterface *core, + QObject *object, + const QString &property) +{ + if (const QDesignerPropertySheetExtension *ps = qt_extension<QDesignerPropertySheetExtension *>(core->extensionManager(), object)) { + const int index = ps->indexOf(property); + if (index >= 0) + return ps->propertyGroup(index); + } + return QString(); +} + +QString QDesignerIntegration::contextHelpId() const +{ + QObject *currentObject = core()->propertyEditor()->object(); + if (!currentObject) + return QString(); + // Return a help index id consisting of "class::property" + QString className; + QString currentPropertyName = core()->propertyEditor()->currentPropertyName(); + if (!currentPropertyName.isEmpty()) + className = classForProperty(core(), currentObject, currentPropertyName); + if (className.isEmpty()) { + currentPropertyName.clear(); // We hit on some fake property. + className = WidgetFactory::classNameOf(core(), currentObject); + } + QString helpId = fixHelpClassName(className); + if (!currentPropertyName.isEmpty()) { + helpId += QLatin1String("::"); + helpId += currentPropertyName; + } + return helpId; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_integration_p.h b/tools/designer/src/lib/shared/qdesigner_integration_p.h new file mode 100644 index 0000000..6938bfb --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_integration_p.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_INTEGRATION_H +#define QDESIGNER_INTEGRATION_H + +#include "shared_global_p.h" +#include <QtDesigner/QDesignerIntegrationInterface> + +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerResourceBrowserInterface; + +class QVariant; +class QWidget; + +namespace qdesigner_internal { + +struct Selection; +class QDesignerIntegrationPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerIntegration: public QDesignerIntegrationInterface +{ + Q_OBJECT +public: + explicit QDesignerIntegration(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~QDesignerIntegration(); + + static void requestHelp(const QDesignerFormEditorInterface *core, const QString &manual, const QString &document); + + virtual QWidget *containerWindow(QWidget *widget) const; + + // Load plugins into widget database and factory. + static void initializePlugins(QDesignerFormEditorInterface *formEditor); + void emitObjectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, + const QString &newName, const QString &oldName); + void emitNavigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList ¶meterNames); + void emitNavigateToSlot(const QString &slotSignature); + + // Create a resource browser specific to integration. Language integration takes precedence + virtual QDesignerResourceBrowserInterface *createResourceBrowser(QWidget *parent = 0); + + enum ResourceFileWatcherBehaviour { + NoWatcher, + ReloadSilently, + PromptAndReload + }; + + ResourceFileWatcherBehaviour resourceFileWatcherBehaviour() const; + bool isResourceEditingEnabled() const; + bool isSlotNavigationEnabled() const; + + QString contextHelpId() const; + +protected: + + void setResourceFileWatcherBehaviour(ResourceFileWatcherBehaviour behaviour); // PromptAndReload by default + void setResourceEditingEnabled(bool enable); // true by default + void setSlotNavigationEnabled(bool enable); // false by default + +signals: + void propertyChanged(QDesignerFormWindowInterface *formWindow, const QString &name, const QVariant &value); + void objectNameChanged(QDesignerFormWindowInterface *formWindow, QObject *object, const QString &newName, const QString &oldName); + void helpRequested(const QString &manual, const QString &document); + + void navigateToSlot(const QString &objectName, const QString &signalSignature, const QStringList ¶meterNames); + void navigateToSlot(const QString &slotSignature); + +public slots: + virtual void updateProperty(const QString &name, const QVariant &value, bool enableSubPropertyHandling); + // Additional signals of designer property editor + virtual void resetProperty(const QString &name); + virtual void addDynamicProperty(const QString &name, const QVariant &value); + virtual void removeDynamicProperty(const QString &name); + + virtual void updateActiveFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void setupFormWindow(QDesignerFormWindowInterface *formWindow); + virtual void updateSelection(); + virtual void updateGeometry(); + virtual void activateWidget(QWidget *widget); + + void updateCustomWidgetPlugins(); + +private slots: + void updatePropertyPrivate(const QString &name, const QVariant &value); + +private: + void initialize(); + void getSelection(Selection &s); + QObject *propertyEditorObject(); + + QDesignerIntegrationPrivate *m_d; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_INTEGRATION_H diff --git a/tools/designer/src/lib/shared/qdesigner_introspection.cpp b/tools/designer/src/lib/shared/qdesigner_introspection.cpp new file mode 100644 index 0000000..c512a97 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_introspection.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_introspection_p.h" + +#include <QtCore/QMetaObject> +#include <QtCore/QMetaEnum> +#include <QtCore/QStringList> +#include <QtCore/QVector> + +QT_BEGIN_NAMESPACE + +// Qt Implementation +static QStringList byteArrayListToStringList(const QList<QByteArray> &l) +{ + if (l.empty()) + return QStringList(); + QStringList rc; + const QList<QByteArray>::const_iterator cend = l.constEnd(); + for (QList<QByteArray>::const_iterator it = l.constBegin(); it != cend; ++it) + rc += QString::fromUtf8(*it); + return rc; +} + +static inline QString charToQString(const char *c) +{ + if (!c) + return QString::null; + return QString::fromUtf8(c); +} + +namespace { + // ------- QDesignerMetaEnum + class QDesignerMetaEnum : public QDesignerMetaEnumInterface { + public: + QDesignerMetaEnum(const QMetaEnum &qEnum); + virtual bool isFlag() const { return m_enum.isFlag(); } + virtual QString key(int index) const { return charToQString(m_enum.key(index)); } + virtual int keyCount() const { return m_enum.keyCount(); } + virtual int keyToValue(const QString &key) const { return m_enum.keyToValue(key.toUtf8()); } + virtual int keysToValue(const QString &keys) const { return m_enum.keysToValue(keys.toUtf8()); } + virtual QString name() const { return m_name; } + virtual QString scope() const { return m_scope; } + virtual QString separator() const; + virtual int value(int index) const { return m_enum.value(index); } + virtual QString valueToKey(int value) const { return charToQString(m_enum.valueToKey(value)); } + virtual QString valueToKeys(int value) const { return charToQString(m_enum.valueToKeys(value)); } + + private: + const QMetaEnum m_enum; + const QString m_name; + const QString m_scope; + }; + + QDesignerMetaEnum::QDesignerMetaEnum(const QMetaEnum &qEnum) : + m_enum(qEnum), + m_name(charToQString(m_enum.name())), + m_scope(charToQString(m_enum.scope())) + { + } + + QString QDesignerMetaEnum::separator() const + { + static const QString rc = QLatin1String("::"); + return rc; + } + + // ------- QDesignerMetaProperty + class QDesignerMetaProperty : public QDesignerMetaPropertyInterface { + public: + QDesignerMetaProperty(const QMetaProperty &property); + virtual ~QDesignerMetaProperty(); + + virtual const QDesignerMetaEnumInterface *enumerator() const { return m_enumerator; } + + virtual Kind kind() const { return m_kind; } + + virtual AccessFlags accessFlags() const { return m_access; } + virtual Attributes attributes(const QObject *object = 0) const; + + virtual QVariant::Type type() const { return m_property.type(); } + virtual QString name() const { return m_name; } + virtual QString typeName() const { return m_typeName; } + virtual int userType() const { return m_property.userType(); } + virtual bool hasSetter() const { return m_property.hasStdCppSet(); } + + virtual QVariant read(const QObject *object) const { return m_property.read(object); } + virtual bool reset(QObject *object) const { return m_property.reset(object); } + virtual bool write(QObject *object, const QVariant &value) const { return m_property.write(object, value); } + + private: + const QMetaProperty m_property; + const QString m_name; + const QString m_typeName; + Kind m_kind; + AccessFlags m_access; + Attributes m_defaultAttributes; + QDesignerMetaEnumInterface *m_enumerator; + }; + + QDesignerMetaProperty::QDesignerMetaProperty(const QMetaProperty &property) : + m_property(property), + m_name(charToQString(m_property.name())), + m_typeName(charToQString(m_property.typeName())), + m_kind(OtherKind), + m_enumerator(0) + { + if (m_property.isFlagType() || m_property.isEnumType()) { + const QMetaEnum metaEnum = m_property.enumerator(); + Q_ASSERT(metaEnum.isValid()); + m_enumerator = new QDesignerMetaEnum(metaEnum); + } + // kind + if (m_property.isFlagType()) + m_kind = FlagKind; + else + if (m_property.isEnumType()) + m_kind = EnumKind; + // flags and attributes + if (m_property.isReadable()) + m_access |= ReadAccess; + if (m_property.isWritable()) + m_access |= WriteAccess; + if (m_property.isResettable()) + m_access |= ResetAccess; + + if (m_property.isDesignable()) + m_defaultAttributes |= DesignableAttribute; + if (m_property.isScriptable()) + m_defaultAttributes |= ScriptableAttribute; + if (m_property.isStored()) + m_defaultAttributes |= StoredAttribute; + if (m_property.isUser()) + m_defaultAttributes |= UserAttribute; + } + + QDesignerMetaProperty::~QDesignerMetaProperty() + { + delete m_enumerator; + } + + QDesignerMetaProperty::Attributes QDesignerMetaProperty::attributes(const QObject *object) const + { + if (!object) + return m_defaultAttributes; + Attributes rc; + if (m_property.isDesignable(object)) + rc |= DesignableAttribute; + if (m_property.isScriptable(object)) + rc |= ScriptableAttribute; + if (m_property.isStored(object)) + rc |= StoredAttribute; + if (m_property.isUser(object)) + rc |= UserAttribute; + return rc; + } + + // -------------- QDesignerMetaMethod + + class QDesignerMetaMethod : public QDesignerMetaMethodInterface { + public: + QDesignerMetaMethod(const QMetaMethod &method); + + virtual Access access() const { return m_access; } + virtual MethodType methodType() const { return m_methodType; } + virtual QStringList parameterNames() const { return m_parameterNames; } + virtual QStringList parameterTypes() const { return m_parameterTypes; } + virtual QString signature() const { return m_signature; } + virtual QString normalizedSignature() const { return m_normalizedSignature; } + virtual QString tag() const { return m_tag; } + virtual QString typeName() const { return m_typeName; } + + private: + Access m_access; + MethodType m_methodType; + const QStringList m_parameterNames; + const QStringList m_parameterTypes; + const QString m_signature; + const QString m_normalizedSignature; + const QString m_tag; + const QString m_typeName; + }; + + QDesignerMetaMethod::QDesignerMetaMethod(const QMetaMethod &method) : + m_parameterNames(byteArrayListToStringList(method.parameterNames())), + m_parameterTypes(byteArrayListToStringList(method.parameterTypes())), + m_signature(charToQString(method.signature())), + m_normalizedSignature(charToQString(QMetaObject::normalizedSignature(method.signature()))), + m_tag(charToQString(method.tag())), + m_typeName(charToQString(method.typeName())) + { + switch (method.access()) { + case QMetaMethod::Public: + m_access = Public; + break; + case QMetaMethod::Protected: + m_access = Protected; + break; + case QMetaMethod::Private: + m_access = Private; + break; + + } + switch (method.methodType()) { + case QMetaMethod::Constructor: + m_methodType = Constructor; + break; + case QMetaMethod::Method: + m_methodType = Method; + break; + case QMetaMethod::Signal: + m_methodType = Signal; + break; + + case QMetaMethod::Slot: + m_methodType = Slot; + break; + } + } + + // ------------- QDesignerMetaObject + class QDesignerMetaObject : public QDesignerMetaObjectInterface { + public: + QDesignerMetaObject(const qdesigner_internal::QDesignerIntrospection *introspection, const QMetaObject *metaObject); + virtual ~QDesignerMetaObject(); + + virtual QString className() const { return m_className; } + virtual const QDesignerMetaEnumInterface *enumerator(int index) const { return m_enumerators[index]; } + virtual int enumeratorCount() const { return m_enumerators.size(); } + virtual int enumeratorOffset() const { return m_metaObject->enumeratorOffset(); } + + virtual int indexOfEnumerator(const QString &name) const { return m_metaObject->indexOfEnumerator(name.toUtf8()); } + virtual int indexOfMethod(const QString &method) const { return m_metaObject->indexOfMethod(method.toUtf8()); } + virtual int indexOfProperty(const QString &name) const { return m_metaObject->indexOfProperty(name.toUtf8()); } + virtual int indexOfSignal(const QString &signal) const { return m_metaObject->indexOfSignal(signal.toUtf8()); } + virtual int indexOfSlot(const QString &slot) const { return m_metaObject->indexOfSlot(slot.toUtf8()); } + + virtual const QDesignerMetaMethodInterface *method(int index) const { return m_methods[index]; } + virtual int methodCount() const { return m_methods.size(); } + virtual int methodOffset() const { return m_metaObject->methodOffset(); } + + virtual const QDesignerMetaPropertyInterface *property(int index) const { return m_properties[index]; } + virtual int propertyCount() const { return m_properties.size(); } + virtual int propertyOffset() const { return m_metaObject->propertyOffset(); } + + const QDesignerMetaObjectInterface *superClass() const; + virtual const QDesignerMetaPropertyInterface *userProperty() const { return m_userProperty; } + + private: + const QString m_className; + const qdesigner_internal::QDesignerIntrospection *m_introspection; + const QMetaObject *m_metaObject; + + typedef QVector<QDesignerMetaEnumInterface *> Enumerators; + Enumerators m_enumerators; + + typedef QVector<QDesignerMetaMethodInterface *> Methods; + Methods m_methods; + + typedef QVector<QDesignerMetaPropertyInterface *> Properties; + Properties m_properties; + + QDesignerMetaPropertyInterface *m_userProperty; + }; + + QDesignerMetaObject::QDesignerMetaObject(const qdesigner_internal::QDesignerIntrospection *introspection, const QMetaObject *metaObject) : + m_className(charToQString(metaObject->className())), + m_introspection(introspection), + m_metaObject(metaObject), + m_userProperty(0) + { + const int numEnumerators = metaObject->enumeratorCount(); + m_enumerators.reserve(numEnumerators); + for (int i = 0; i < numEnumerators; i++) + m_enumerators.push_back(new QDesignerMetaEnum(metaObject->enumerator(i))); + const int numMethods = metaObject->methodCount(); + m_methods.reserve(numMethods); + for (int i = 0; i < numMethods; i++) + m_methods.push_back(new QDesignerMetaMethod(metaObject->method(i))); + + const int numProperties = metaObject->propertyCount(); + m_properties.reserve(numProperties); + for (int i = 0; i < numProperties; i++) + m_properties.push_back(new QDesignerMetaProperty(metaObject->property(i))); + + const QMetaProperty userProperty = metaObject->userProperty(); + if (userProperty.isValid()) + m_userProperty = new QDesignerMetaProperty(userProperty); + } + + QDesignerMetaObject::~QDesignerMetaObject() + { + qDeleteAll(m_enumerators); + qDeleteAll(m_methods); + qDeleteAll(m_properties); + delete m_userProperty; + } + + const QDesignerMetaObjectInterface *QDesignerMetaObject::superClass() const + { + const QMetaObject *qSuperClass = m_metaObject->superClass(); + if (!qSuperClass) + return 0; + return m_introspection->metaObjectForQMetaObject(qSuperClass); + } + +} + +namespace qdesigner_internal { + + QDesignerIntrospection::QDesignerIntrospection() + { + } + + QDesignerIntrospection::~QDesignerIntrospection() + { + qDeleteAll(m_metaObjectMap.values()); + } + + const QDesignerMetaObjectInterface* QDesignerIntrospection::metaObject(const QObject *object) const + { + return metaObjectForQMetaObject(object->metaObject()); + } + + const QDesignerMetaObjectInterface* QDesignerIntrospection::metaObjectForQMetaObject(const QMetaObject *metaObject) const + { + MetaObjectMap::iterator it = m_metaObjectMap.find(metaObject); + if (it == m_metaObjectMap.end()) + it = m_metaObjectMap.insert(metaObject, new QDesignerMetaObject(this, metaObject)); + return it.value(); + } +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_introspection_p.h b/tools/designer/src/lib/shared/qdesigner_introspection_p.h new file mode 100644 index 0000000..89f4c53 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_introspection_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNERINTROSPECTION +#define DESIGNERINTROSPECTION + +#include "shared_global_p.h" +#include <abstractintrospection_p.h> +#include <QtCore/QMap> + +QT_BEGIN_NAMESPACE + +struct QMetaObject; +class QWidget; + +namespace qdesigner_internal { + // Qt C++ introspection with helpers to find core and meta object for an object + class QDESIGNER_SHARED_EXPORT QDesignerIntrospection : public QDesignerIntrospectionInterface { + public: + QDesignerIntrospection(); + virtual ~QDesignerIntrospection(); + + virtual const QDesignerMetaObjectInterface* metaObject(const QObject *object) const; + + const QDesignerMetaObjectInterface* metaObjectForQMetaObject(const QMetaObject *metaObject) const; + private: + typedef QMap<const QMetaObject*, QDesignerMetaObjectInterface*> MetaObjectMap; + mutable MetaObjectMap m_metaObjectMap; + + }; +} + +QT_END_NAMESPACE + +#endif // DESIGNERINTROSPECTION diff --git a/tools/designer/src/lib/shared/qdesigner_membersheet.cpp b/tools/designer/src/lib/shared/qdesigner_membersheet.cpp new file mode 100644 index 0000000..e51d2fe --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_membersheet.cpp @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_membersheet_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <abstractintrospection_p.h> + +#include <QtGui/QWidget> + +namespace { + +class Qt3Members + { + public: + static Qt3Members *instance(); + QMap<QString, QStringList> getSignals() const { return m_classNameToSignals; } + QMap<QString, QStringList> getSlots() const { return m_classNameToSlots; } + private: + Qt3Members(); + static Qt3Members *m_instance; + QMap<QString, QStringList> m_classNameToSignals; + QMap<QString, QStringList> m_classNameToSlots; + }; + +Qt3Members *Qt3Members::m_instance = 0; + +Qt3Members::Qt3Members() +{ + m_classNameToSignals[QLatin1String("QTextEdit")].append(QLatin1String("currentFontChanged(QFont)")); + m_classNameToSignals[QLatin1String("QTextEdit")].append(QLatin1String("currentColorChanged(QColor)")); + m_classNameToSignals[QLatin1String("QTabWidget")].append(QLatin1String("currentChanged(QWidget*)")); + m_classNameToSignals[QLatin1String("QTabWidget")].append(QLatin1String("selected(QString)")); + m_classNameToSignals[QLatin1String("QTabBar")].append(QLatin1String("selected(int)")); + m_classNameToSignals[QLatin1String("QMenuBar")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QMenuBar")].append(QLatin1String("highlighted(int)")); + m_classNameToSignals[QLatin1String("QMenu")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QMenu")].append(QLatin1String("highlighted(int)")); + m_classNameToSignals[QLatin1String("QLineEdit")].append(QLatin1String("lostFocus()")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialPressed()")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialMoved(int)")); + m_classNameToSignals[QLatin1String("QDial")].append(QLatin1String("dialReleased()")); + m_classNameToSignals[QLatin1String("QComboBox")].append(QLatin1String("textChanged(QString)")); + m_classNameToSignals[QLatin1String("QActionGroup")].append(QLatin1String("selected(QAction*)")); + m_classNameToSignals[QLatin1String("QAction")].append(QLatin1String("activated(int)")); + m_classNameToSignals[QLatin1String("QAbstractSocket")].append(QLatin1String("connectionClosed()")); + m_classNameToSignals[QLatin1String("QAbstractSocket")].append(QLatin1String("delayedCloseFinished()")); + + m_classNameToSlots[QLatin1String("QWidget")].append(QLatin1String("setShown(bool)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setTextPosition(QToolButton::TextPosition)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setUsesBigPixmap(bool)")); + m_classNameToSlots[QLatin1String("QToolButton")].append(QLatin1String("setUsesTextLabel(bool)")); + m_classNameToSlots[QLatin1String("QTextEdit")].append(QLatin1String("setModified(bool)")); + m_classNameToSlots[QLatin1String("QTextEdit")].append(QLatin1String("setColor(QColor)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("setCurrentPage(int)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("showPage(QWidget*)")); + m_classNameToSlots[QLatin1String("QTabWidget")].append(QLatin1String("removePage(QWidget*)")); + m_classNameToSlots[QLatin1String("QTabBar")].append(QLatin1String("setCurrentTab(int)")); + m_classNameToSlots[QLatin1String("QStatusBar")].append(QLatin1String("message(QString,int)")); + m_classNameToSlots[QLatin1String("QStatusBar")].append(QLatin1String("clear()")); + m_classNameToSlots[QLatin1String("QSplashScreen")].append(QLatin1String("message(QString,int)")); + m_classNameToSlots[QLatin1String("QSplashScreen")].append(QLatin1String("clear()")); + m_classNameToSlots[QLatin1String("QSlider")].append(QLatin1String("addStep()")); + m_classNameToSlots[QLatin1String("QSlider")].append(QLatin1String("subtractStep()")); + m_classNameToSlots[QLatin1String("QAbstractButton")].append(QLatin1String("setOn(bool)")); + m_classNameToSlots[QLatin1String("QAction")].append(QLatin1String("setOn(bool)")); + m_classNameToSlots[QLatin1String("QErrorMessage")].append(QLatin1String("message(QString)")); + m_classNameToSlots[QLatin1String("QTimer")].append(QLatin1String("changeInterval(int)")); + m_classNameToSlots[QLatin1String("QTimer")].append(QLatin1String("start(int,bool)")); +} + +Qt3Members *Qt3Members::instance() +{ + if (!m_instance) + m_instance = new Qt3Members(); + return m_instance; +} +} + +QT_BEGIN_NAMESPACE + +static QList<QByteArray> stringListToByteArray(const QStringList &l) +{ + if (l.empty()) + return QList<QByteArray>(); + QList<QByteArray> rc; + const QStringList::const_iterator cend = l.constEnd(); + for (QStringList::const_iterator it = l.constBegin(); it != cend; ++it) + rc += it->toUtf8(); + return rc; +} + +// Find the form editor in the hierarchy. +// We know that the parent of the sheet is the extension manager +// whose parent is the core. + +static QDesignerFormEditorInterface *formEditorForObject(QObject *o) { + do { + if (QDesignerFormEditorInterface* core = qobject_cast<QDesignerFormEditorInterface*>(o)) + return core; + o = o->parent(); + } while(o); + Q_ASSERT(o); + return 0; +} + +// ------------ QDesignerMemberSheetPrivate +class QDesignerMemberSheetPrivate { +public: + explicit QDesignerMemberSheetPrivate(QObject *object, QObject *sheetParent); + + QDesignerFormEditorInterface *m_core; + const QDesignerMetaObjectInterface *m_meta; + + class Info { + public: + inline Info() : visible(true) {} + + QString group; + bool visible; + }; + + typedef QHash<int, Info> InfoHash; + + Info &ensureInfo(int index); + + InfoHash m_info; +}; + +QDesignerMemberSheetPrivate::QDesignerMemberSheetPrivate(QObject *object, QObject *sheetParent) : + m_core(formEditorForObject(sheetParent)), + m_meta(m_core->introspection()->metaObject(object)) +{ +} + +QDesignerMemberSheetPrivate::Info &QDesignerMemberSheetPrivate::ensureInfo(int index) +{ + InfoHash::iterator it = m_info.find(index); + if (it == m_info.end()) { + it = m_info.insert(index, Info()); + } + return it.value(); +} + +// --------- QDesignerMemberSheet + +QDesignerMemberSheet::QDesignerMemberSheet(QObject *object, QObject *parent) : + QObject(parent), + d(new QDesignerMemberSheetPrivate(object, parent)) +{ +} + +QDesignerMemberSheet::~QDesignerMemberSheet() +{ + delete d; +} + +int QDesignerMemberSheet::count() const +{ + return d->m_meta->methodCount(); +} + +int QDesignerMemberSheet::indexOf(const QString &name) const +{ + return d->m_meta->indexOfMethod(name); +} + +QString QDesignerMemberSheet::memberName(int index) const +{ + return d->m_meta->method(index)->tag(); +} + +QString QDesignerMemberSheet::declaredInClass(int index) const +{ + const QString member = d->m_meta->method(index)->signature(); + + // Find class whose superclass does not contain the method. + const QDesignerMetaObjectInterface *meta_obj = d->m_meta; + + for (;;) { + const QDesignerMetaObjectInterface *tmp = meta_obj->superClass(); + if (tmp == 0) + break; + if (tmp->indexOfMethod(member) == -1) + break; + meta_obj = tmp; + } + return meta_obj->className(); +} + +QString QDesignerMemberSheet::memberGroup(int index) const +{ + return d->m_info.value(index).group; +} + +void QDesignerMemberSheet::setMemberGroup(int index, const QString &group) +{ + d->ensureInfo(index).group = group; +} + +QString QDesignerMemberSheet::signature(int index) const +{ + return d->m_meta->method(index)->normalizedSignature(); +} + +bool QDesignerMemberSheet::isVisible(int index) const +{ + typedef QDesignerMemberSheetPrivate::InfoHash InfoHash; + const InfoHash::const_iterator it = d->m_info.constFind(index); + if (it != d->m_info.constEnd()) + return it.value().visible; + + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Signal + || d->m_meta->method(index)->access() == QDesignerMetaMethodInterface::Public; +} + +void QDesignerMemberSheet::setVisible(int index, bool visible) +{ + d->ensureInfo(index).visible = visible; +} + +bool QDesignerMemberSheet::isSignal(int index) const +{ + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Signal; +} + +bool QDesignerMemberSheet::isSlot(int index) const +{ + return d->m_meta->method(index)->methodType() == QDesignerMetaMethodInterface::Slot; +} + +bool QDesignerMemberSheet::inheritedFromWidget(int index) const +{ + const QString name = d->m_meta->method(index)->signature(); + return declaredInClass(index) == QLatin1String("QWidget") || declaredInClass(index) == QLatin1String("QObject"); +} + + +QList<QByteArray> QDesignerMemberSheet::parameterTypes(int index) const +{ + return stringListToByteArray(d->m_meta->method(index)->parameterTypes()); +} + +QList<QByteArray> QDesignerMemberSheet::parameterNames(int index) const +{ + return stringListToByteArray(d->m_meta->method(index)->parameterNames()); +} + +bool QDesignerMemberSheet::signalMatchesSlot(const QString &signal, const QString &slot) +{ + bool result = true; + + do { + int signal_idx = signal.indexOf(QLatin1Char('(')); + int slot_idx = slot.indexOf(QLatin1Char('(')); + if (signal_idx == -1 || slot_idx == -1) + break; + + ++signal_idx; ++slot_idx; + + if (slot.at(slot_idx) == QLatin1Char(')')) + break; + + while (signal_idx < signal.size() && slot_idx < slot.size()) { + const QChar signal_c = signal.at(signal_idx); + const QChar slot_c = slot.at(slot_idx); + + if (signal_c == QLatin1Char(',') && slot_c == QLatin1Char(')')) + break; + + if (signal_c == QLatin1Char(')') && slot_c == QLatin1Char(')')) + break; + + if (signal_c != slot_c) { + result = false; + break; + } + + ++signal_idx; ++slot_idx; + } + } while (false); + + return result; +} + +bool QDesignerMemberSheet::isQt3Signal(int index) const +{ + if (!isSignal(index)) + return false; + + const QString className = declaredInClass(index); + const QString signalSignature = signature(index); + + QMap<QString, QStringList> qt3signals = Qt3Members::instance()->getSignals(); + QMap<QString, QStringList>::const_iterator it = qt3signals.constFind(className); + if (it != qt3signals.constEnd() && (*it).contains(signalSignature)) + return true; + + return false; +} + +bool QDesignerMemberSheet::isQt3Slot(int index) const +{ + if (!isSlot(index)) + return false; + + const QString className = declaredInClass(index); + const QString slotSignature = signature(index); + + QMap<QString, QStringList> qt3slots = Qt3Members::instance()->getSlots(); + QMap<QString, QStringList>::const_iterator it = qt3slots.constFind(className); + if (it != qt3slots.constEnd() && (*it).contains(slotSignature)) + return true; + return false; +} + +// ------------ QDesignerMemberSheetFactory + +QDesignerMemberSheetFactory::QDesignerMemberSheetFactory(QExtensionManager *parent) + : QExtensionFactory(parent) +{ +} + +QObject *QDesignerMemberSheetFactory::createExtension(QObject *object, const QString &iid, QObject *parent) const +{ + if (iid == Q_TYPEID(QDesignerMemberSheetExtension)) { + return new QDesignerMemberSheet(object, parent); + } + + return 0; +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_membersheet_p.h b/tools/designer/src/lib/shared/qdesigner_membersheet_p.h new file mode 100644 index 0000000..695b808 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_membersheet_p.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MEMBERSHEET_H +#define QDESIGNER_MEMBERSHEET_H + +#include "shared_global_p.h" + +#include <QtDesigner/membersheet.h> +#include <QtDesigner/default_extensionfactory.h> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +class QDesignerMemberSheetPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerMemberSheet: public QObject, public QDesignerMemberSheetExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerMemberSheetExtension) + +public: + explicit QDesignerMemberSheet(QObject *object, QObject *parent = 0); + virtual ~QDesignerMemberSheet(); + + virtual int indexOf(const QString &name) const; + + virtual int count() const; + virtual QString memberName(int index) const; + + virtual QString memberGroup(int index) const; + virtual void setMemberGroup(int index, const QString &group); + + virtual bool isVisible(int index) const; + virtual void setVisible(int index, bool b); + + virtual bool isSignal(int index) const; + virtual bool isSlot(int index) const; + + virtual bool isQt3Signal(int index) const; + virtual bool isQt3Slot(int index) const; + + virtual bool inheritedFromWidget(int index) const; + + static bool signalMatchesSlot(const QString &signal, const QString &slot); + + virtual QString declaredInClass(int index) const; + + virtual QString signature(int index) const; + virtual QList<QByteArray> parameterTypes(int index) const; + virtual QList<QByteArray> parameterNames(int index) const; + +private: + QDesignerMemberSheetPrivate *d; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerMemberSheetFactory: public QExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) + +public: + QDesignerMemberSheetFactory(QExtensionManager *parent = 0); + +protected: + virtual QObject *createExtension(QObject *object, const QString &iid, QObject *parent) const; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MEMBERSHEET_H diff --git a/tools/designer/src/lib/shared/qdesigner_menu.cpp b/tools/designer/src/lib/shared/qdesigner_menu.cpp new file mode 100644 index 0000000..7aea52d --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_menu.cpp @@ -0,0 +1,1355 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_menu_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "actioneditor_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" + +#include <QtCore/QTimer> +#include <QtCore/qdebug.h> + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QDesignerMetaDataBaseInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QLineEdit> +#include <QtGui/QPainter> +#include <QtGui/QRubberBand> +#include <QtGui/QToolTip> +#include <QtGui/QToolBar> +#include <QtGui/qevent.h> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +using namespace qdesigner_internal; + +QDesignerMenu::QDesignerMenu(QWidget *parent) : + QMenu(parent), + m_currentIndex(0), + m_addItem(new SpecialMenuAction(this)), + m_addSeparator(new SpecialMenuAction(this)), + m_showSubMenuTimer(new QTimer(this)), + m_deactivateWindowTimer(new QTimer(this)), + m_adjustSizeTimer(new QTimer(this)), + m_editor(new QLineEdit(this)), + m_dragging(false), + m_lastSubMenuIndex(-1) +{ + setContextMenuPolicy(Qt::DefaultContextMenu); + setAcceptDrops(true); // ### fake + setSeparatorsCollapsible(false); + + connect(m_adjustSizeTimer, SIGNAL(timeout()), this, SLOT(slotAdjustSizeNow())); + m_addItem->setText(tr("Type Here")); + addAction(m_addItem); + + m_addSeparator->setText(tr("Add Separator")); + addAction(m_addSeparator); + + connect(m_showSubMenuTimer, SIGNAL(timeout()), this, SLOT(slotShowSubMenuNow())); + + connect(m_deactivateWindowTimer, SIGNAL(timeout()), this, SLOT(slotDeactivateNow())); + + m_editor->setObjectName(QLatin1String("__qt__passive_editor")); + m_editor->hide(); + + m_editor->installEventFilter(this); + installEventFilter(this); +} + +QDesignerMenu::~QDesignerMenu() +{ +} + +void QDesignerMenu::slotAdjustSizeNow() +{ + // Not using a single-shot, since we want to compress the timers if many items are being + // adjusted + m_adjustSizeTimer->stop(); + adjustSize(); +} + +bool QDesignerMenu::handleEvent(QWidget *widget, QEvent *event) +{ + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) { + update(); + + if (widget == m_editor) + return false; + } + + switch (event->type()) { + default: break; + + case QEvent::MouseButtonPress: + return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonDblClick: + return handleMouseDoubleClickEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::ContextMenu: + return handleContextMenuEvent(widget, static_cast<QContextMenuEvent*>(event)); + case QEvent::KeyPress: + return handleKeyPressEvent(widget, static_cast<QKeyEvent*>(event)); + } + + return true; +} + +void QDesignerMenu::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers) +{ + const int index = findAction(pos); + if (index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + QDesignerFormWindowInterface *fw = formWindow(); + const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction; + if (dropAction == Qt::MoveAction) { + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, actions().at(index + 1)); + fw->commandHistory()->push(cmd); + } + + QDrag *drag = new QDrag(this); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action)); + drag->setMimeData(new ActionRepositoryMimeData(action, dropAction)); + + const int old_index = m_currentIndex; + m_currentIndex = -1; + + if (drag->start(dropAction) == Qt::IgnoreAction) { + if (dropAction == Qt::MoveAction) { + QAction *previous = safeActionAt(index); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, previous); + fw->commandHistory()->push(cmd); + } + + m_currentIndex = old_index; + } +} + +bool QDesignerMenu::handleKeyPressEvent(QWidget * /*widget*/, QKeyEvent *e) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isHidden() && hasFocus()) { // In navigation mode + switch (e->key()) { + + case Qt::Key_Delete: + if (m_currentIndex == -1 || m_currentIndex >= realActionCount()) + break; + hideSubMenu(); + deleteAction(); + break; + + case Qt::Key_Left: + e->accept(); + moveLeft(); + return true; + + case Qt::Key_Right: + e->accept(); + moveRight(); + return true; // no update + + case Qt::Key_Up: + e->accept(); + moveUp(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_Down: + e->accept(); + moveDown(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_PageUp: + m_currentIndex = 0; + break; + + case Qt::Key_PageDown: + m_currentIndex = actions().count() - 1; + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_F2: + e->accept(); + enterEditMode(); + return true; // no update + + case Qt::Key_Escape: + e->ignore(); + setFocus(); + hide(); + closeMenuChain(); + return true; + + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + e->ignore(); + setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed + return true; // no update + + default: { + QAction *action = currentAction(); + if (!action || action->isSeparator() || action == m_addSeparator) { + e->ignore(); + return true; + } else if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) { + showLineEdit(); + QApplication::sendEvent(m_editor, e); + e->accept(); + } else { + e->ignore(); + } + } + return true; + } + } else if (m_editor->hasFocus()) { // In edit mode + switch (e->key()) { + default: + e->ignore(); + return false; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_editor->text().isEmpty()) { + leaveEditMode(ForceAccept); + m_editor->hide(); + setFocus(); + moveDown(false); + break; + } + // fall through + + case Qt::Key_Escape: + m_editor->hide(); + setFocus(); + break; + } + } + + e->accept(); + update(); + + return true; +} + +static void sendMouseEventTo(QWidget *target, const QPoint &targetPoint, const QMouseEvent *event) +{ + QMouseEvent e(event->type(), targetPoint, event->globalPos(), event->button(), event->buttons(), event->modifiers()); + QApplication::sendEvent(target, &e); +} + +bool QDesignerMenu::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + // special case for menubar + QWidget *target = QApplication::widgetAt(event->globalPos()); + QMenuBar *mb = qobject_cast<QMenuBar*>(target); + QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(target); + if (mb != 0 || menu != 0) { + const QPoint pt = target->mapFromGlobal(event->globalPos()); + QAction *action = mb == 0 ? menu->actionAt(pt) : mb->actionAt(pt); + if (action) + sendMouseEventTo(target, pt, event); + } + return true; + } + + m_currentIndex = findAction(event->pos()); + QAction *action = safeActionAt(m_currentIndex); + + QRect pm_rect; + if (action->menu() || hasSubMenuPixmap(action)) { + pm_rect = subMenuPixmapRect(action); + pm_rect.setLeft(pm_rect.left() - 20); // give the user a little more + // space to click + } + + if (!pm_rect.contains(event->pos()) && m_currentIndex != -1) + enterEditMode(); + + return true; +} + +bool QDesignerMenu::handleMousePressEvent(QWidget * /*widget*/, QMouseEvent *event) +{ + if (!rect().contains(event->pos())) { + QWidget *clickedWidget = QApplication::widgetAt(event->globalPos()); + if (QMenuBar *mb = qobject_cast<QMenuBar*>(clickedWidget)) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + if (QAction *action = mb->actionAt(pt)) { + QMenu * menu = action->menu(); + if (menu == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + } + } + + if (QDesignerMenu *m = qobject_cast<QDesignerMenu *>(clickedWidget)) { + m->hideSubMenu(); + sendMouseEventTo(m, m->mapFromGlobal(event->globalPos()), event); + } else { + QDesignerMenu *root = findRootMenu(); + root->hide(); + root->hideSubMenu(); + } + if (clickedWidget) { + if (QWidget *focusProxy = clickedWidget->focusProxy()) + clickedWidget = focusProxy; + if (clickedWidget->focusPolicy() != Qt::NoFocus) + clickedWidget->setFocus(Qt::OtherFocusReason); + } + return true; + } + + m_showSubMenuTimer->stop(); + m_startPosition = QPoint(); + event->accept(); + + if (event->button() != Qt::LeftButton) + return true; + + m_startPosition = mapFromGlobal(event->globalPos()); + + const int index = findAction(m_startPosition); + + QAction *action = safeActionAt(index); + QRect pm_rect = subMenuPixmapRect(action); + pm_rect.setLeft(pm_rect.left() - 20); // give the user a little more space to click + + const int old_index = m_currentIndex; + m_currentIndex = index; + if ((hasSubMenuPixmap(action) || action->menu() != 0) + && pm_rect.contains(m_startPosition)) { + if (m_currentIndex == m_lastSubMenuIndex) { + hideSubMenu(); + } else + slotShowSubMenuNow(); + } else { + if (index == old_index) { + if (m_currentIndex == m_lastSubMenuIndex) + hideSubMenu(); + } else { + hideSubMenu(); + } + } + + update(); + if (index != old_index) + selectCurrentAction(); + + return true; +} + +bool QDesignerMenu::handleMouseReleaseEvent(QWidget *, QMouseEvent *event) +{ + event->accept(); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleMouseMoveEvent(QWidget *, QMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (!rect().contains(event->pos())) { + + if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::widgetAt(event->globalPos()))) { + const QPoint pt = mb->mapFromGlobal(event->globalPos()); + QAction *action = mb->actionAt(pt); + if (action && action->menu() == findRootMenu()) { + // propagate the mouse press event (but don't close the popup) + sendMouseEventTo(mb, pt, event); + return true; + } + // hide the popup Qt will replay the event + slotDeactivateNow(); + } + return true; + } + + if (m_startPosition.isNull()) + return true; + + event->accept(); + + const QPoint pos = mapFromGlobal(event->globalPos()); + + if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance()) + return true; + + startDrag(m_startPosition, event->modifiers()); + m_startPosition = QPoint(); + + return true; +} + +bool QDesignerMenu::handleContextMenuEvent(QWidget *, QContextMenuEvent *event) +{ + event->accept(); + + const int index = findAction(mapFromGlobal(event->globalPos())); + QAction *action = safeActionAt(index); + if (qobject_cast<SpecialMenuAction*>(action)) + return true; + + QMenu menu; + QVariant itemData; + qVariantSetValue(itemData, action); + + QAction *addSeparatorAction = menu.addAction(tr("Insert separator")); + addSeparatorAction->setData(itemData); + + QAction *removeAction = 0; + if (action->isSeparator()) + removeAction = menu.addAction(tr("Remove separator")); + else + removeAction = menu.addAction(tr("Remove action '%1'").arg(action->objectName())); + removeAction->setData(itemData); + + connect(addSeparatorAction, SIGNAL(triggered(bool)), this, SLOT(slotAddSeparator())); + connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(slotRemoveSelectedAction())); + menu.exec(event->globalPos()); + + return true; +} + +void QDesignerMenu::slotAddSeparator() +{ + QAction *action = qobject_cast<QAction *>(sender()); + if (!action) + return; + + QAction *a = qvariant_cast<QAction*>(action->data()); + Q_ASSERT(a != 0); + + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos); + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, action_before); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); +} + +void QDesignerMenu::slotRemoveSelectedAction() +{ + if (QAction *action = qobject_cast<QAction *>(sender())) + if (QAction *a = qvariant_cast<QAction*>(action->data())) + deleteAction(a); +} + +void QDesignerMenu::deleteAction(QAction *a) +{ + const int pos = actions().indexOf(a); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, a, action_before); + fw->commandHistory()->push(cmd); +} + +QRect QDesignerMenu::subMenuPixmapRect(QAction *action) const +{ + static const QPixmap pm(QLatin1String(":/trolltech/formeditor/images/submenu.png")); + const QRect g = actionGeometry(action); + const int x = g.right() - pm.width() - 2; + const int y = g.top() + (g.height() - pm.height())/2 + 1; + return QRect(x, y, pm.width(), pm.height()); +} + +bool QDesignerMenu::hasSubMenuPixmap(QAction *action) const +{ + return action != 0 + && qobject_cast<SpecialMenuAction*>(action) == 0 + && !action->isSeparator() + && !action->menu() + && canCreateSubMenu(action); +} + +void QDesignerMenu::showEvent ( QShowEvent * event ) +{ + selectCurrentAction(); + QMenu::showEvent (event); +} + +void QDesignerMenu::paintEvent(QPaintEvent *event) +{ + QMenu::paintEvent(event); + + QPainter p(this); + + QAction *current = currentAction(); + + foreach (QAction *a, actions()) { + const QRect g = actionGeometry(a); + + if (qobject_cast<SpecialMenuAction*>(a)) { + QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom()); + lg.setColorAt(0.0, Qt::transparent); + lg.setColorAt(0.7, QColor(0, 0, 0, 32)); + lg.setColorAt(1.0, Qt::transparent); + + p.fillRect(g, lg); + } else if (hasSubMenuPixmap(a)) { + static const QPixmap pm(QLatin1String(":/trolltech/formeditor/images/submenu.png")); + p.drawPixmap(subMenuPixmapRect(a).topLeft(), pm); + } + } + + if (!hasFocus() || !current || m_dragging) + return; + + if (QDesignerMenu *menu = parentMenu()) { + if (menu->dragging()) + return; + } + + if (QDesignerMenuBar *menubar = qobject_cast<QDesignerMenuBar*>(parentWidget())) { + if (menubar->dragging()) + return; + } + + const QRect g = actionGeometry(current); + drawSelection(&p, g.adjusted(1, 1, -3, -3)); +} + +bool QDesignerMenu::dragging() const +{ + return m_dragging; +} + +QDesignerMenu *QDesignerMenu::findRootMenu() const +{ + if (parentMenu()) + return parentMenu()->findRootMenu(); + + return const_cast<QDesignerMenu*>(this); +} + +QDesignerMenu *QDesignerMenu::findActivatedMenu() const +{ + QList<QDesignerMenu*> candidates; + candidates.append(const_cast<QDesignerMenu*>(this)); + candidates += qFindChildren<QDesignerMenu*>(this); + + foreach (QDesignerMenu *m, candidates) { + if (m == qApp->activeWindow()) + return m; + } + + return 0; +} + +bool QDesignerMenu::eventFilter(QObject *object, QEvent *event) +{ + if (object != this && object != m_editor) { + return false; + } + + if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) { + leaveEditMode(Default); + m_editor->hide(); + update(); + return false; + } + + bool dispatch = true; + + switch (event->type()) { + default: break; + + case QEvent::WindowDeactivate: + deactivateMenu(); + break; + case QEvent::ContextMenu: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + + while (QApplication::activePopupWidget() && !qobject_cast<QDesignerMenu*>(QApplication::activePopupWidget())) { + QApplication::activePopupWidget()->close(); + } + + // fall through + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::MouseMove: + dispatch = (object != m_editor); + // no break + + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + if (dispatch) + if (QWidget *widget = qobject_cast<QWidget*>(object)) + if (widget == this || isAncestorOf(widget)) + return handleEvent(widget, event); + break; + } + + return false; +}; + +int QDesignerMenu::findAction(const QPoint &pos) const +{ + const int index = actionIndexAt(this, pos, Qt::Vertical); + if (index == -1) + return realActionCount(); + + return index; +} + +void QDesignerMenu::adjustIndicator(const QPoint &pos) +{ + if (QDesignerActionProviderExtension *a = actionProvider()) { + a->adjustIndicator(pos); + } +} + +QDesignerMenu::ActionDragCheck QDesignerMenu::checkAction(QAction *action) const +{ + if (!action || (action->menu() && action->menu()->parentWidget() != const_cast<QDesignerMenu*>(this))) + return NoActionDrag; // menu action!! nothing to do + + if (!Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) + return NoActionDrag; // the action belongs to another form window + + if (actions().contains(action)) + return ActionDragOnSubMenu; // we already have the action in the menu + + return AcceptActionDrag; +} + +void QDesignerMenu::dragEnterEvent(QDragEnterEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + d->accept(event); + m_dragging = true; + break; + case AcceptActionDrag: + d->accept(event); + m_dragging = true; + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenu::dragMoveEvent(QDragMoveEvent *event) +{ + if (actionGeometry(m_addSeparator).contains(event->pos())) { + event->ignore(); + adjustIndicator(QPoint(-1, -1)); + return; + } + + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + const ActionDragCheck dc = checkAction(action); + switch (dc) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + case AcceptActionDrag: { // Do not pop up submenu of action being dragged + const int newIndex = findAction(event->pos()); + if (safeActionAt(newIndex) != action) { + m_currentIndex = newIndex; + if (m_lastSubMenuIndex != m_currentIndex) + m_showSubMenuTimer->start(300); + } + if (dc == AcceptActionDrag) { + adjustIndicator(event->pos()); + d->accept(event); + } else { + event->ignore(); + } + } + break; + } +} + +void QDesignerMenu::dragLeaveEvent(QDragLeaveEvent *) +{ + m_dragging = false; + adjustIndicator(QPoint(-1, -1)); + m_showSubMenuTimer->stop(); +} + +void QDesignerMenu::dropEvent(QDropEvent *event) +{ + m_showSubMenuTimer->stop(); + hideSubMenu(); + m_dragging = false; + + QDesignerFormWindowInterface *fw = formWindow(); + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + QAction *action = d->actionList().first(); + if (action && checkAction(action) == AcceptActionDrag) { + event->acceptProposedAction(); + int index = findAction(event->pos()); + index = qMin(index, actions().count() - 1); + + fw->beginCommand(tr("Insert action")); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = index; + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + update(); + fw->endCommand(); + } else { + event->ignore(); + } + adjustIndicator(QPoint(-1, -1)); +} + +void QDesignerMenu::actionEvent(QActionEvent *event) +{ + QMenu::actionEvent(event); + m_adjustSizeTimer->start(0); +} + +QDesignerFormWindowInterface *QDesignerMenu::formWindow() const +{ + if (parentMenu()) + return parentMenu()->formWindow(); + + return QDesignerFormWindowInterface::findFormWindow(parentWidget()); +} + +QDesignerActionProviderExtension *QDesignerMenu::actionProvider() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this); + } + + return 0; +} + +void QDesignerMenu::closeMenuChain() +{ + m_showSubMenuTimer->stop(); + + QWidget *w = this; + while (w && qobject_cast<QMenu*>(w)) + w = w->parentWidget(); + + if (w) { + foreach (QMenu *subMenu, qFindChildren<QMenu*>(w)) { + subMenu->hide(); + } + } + + m_lastSubMenuIndex = -1; +} + +void QDesignerMenu::moveLeft() +{ + if (parentMenu()) { + hide(); + } else { + closeMenuChain(); + if (QDesignerMenuBar *mb = parentMenuBar()) { + if (QApplication::layoutDirection() == Qt::LeftToRight) + mb->moveLeft(); + else + mb->moveRight(); + } + } + update(); +} + +void QDesignerMenu::moveRight() +{ + QAction *action = currentAction(); + + if (qobject_cast<SpecialMenuAction*>(action) || action->isSeparator()) { + closeMenuChain(); + if (QDesignerMenuBar *mb = parentMenuBar()) { + if (QApplication::layoutDirection() == Qt::LeftToRight) + mb->moveRight(); + else + mb->moveLeft(); + } + } else { + m_lastSubMenuIndex = -1; // force a refresh + slotShowSubMenuNow(); + } +} + +void QDesignerMenu::moveUp(bool ctrl) +{ + if (m_currentIndex == 0) { + hide(); + return; + } + + if (ctrl) + (void) swap(m_currentIndex, m_currentIndex - 1); + + m_currentIndex = qMax(0, --m_currentIndex); + // Always re-select, swapping destroys order + update(); + selectCurrentAction(); +} + +void QDesignerMenu::moveDown(bool ctrl) +{ + if (m_currentIndex == actions().count() - 1) { + return; + } + + if (ctrl) + (void) swap(m_currentIndex + 1, m_currentIndex); + + m_currentIndex = qMin(actions().count() - 1, ++m_currentIndex); + update(); + if (!ctrl) + selectCurrentAction(); +} + +QAction *QDesignerMenu::currentAction() const +{ + if (m_currentIndex < 0 || m_currentIndex >= actions().count()) + return 0; + + return safeActionAt(m_currentIndex); +} + +int QDesignerMenu::realActionCount() const +{ + return actions().count() - 2; // 2 fake actions +} + +void QDesignerMenu::selectCurrentAction() +{ + QAction *action = currentAction(); + if (!action || action == m_addSeparator || action == m_addItem) + return; + + QDesignerObjectInspector *oi = 0; + if (QDesignerFormWindowInterface *fw = formWindow()) + oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector()); + + if (!oi) + return; + + oi->clearSelection(); + if (QMenu *menu = action->menu()) + oi->selectObject(menu); + else + oi->selectObject(action); +} + +void QDesignerMenu::createRealMenuAction(QAction *action) +{ + if (action->menu()) + return; // nothing to do + + QDesignerFormWindowInterface *fw = formWindow(); + QDesignerFormEditorInterface *core = formWindow()->core(); + + QDesignerMenu *menu = findOrCreateSubMenu(action); + m_subMenus.remove(action); + + action->setMenu(menu); + menu->setTitle(action->text()); + + Q_ASSERT(fw); + + core->widgetFactory()->initialize(menu); + + const QString niceObjectName = ActionEditor::actionTextToName(menu->title(), QLatin1String("menu")); + menu->setObjectName(niceObjectName); + + core->metaDataBase()->add(menu); + fw->ensureUniqueObjectName(menu); + + QAction *menuAction = menu->menuAction(); + core->metaDataBase()->add(menuAction); +} + +void QDesignerMenu::removeRealMenu(QAction *action) +{ + QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu()); + if (menu == 0) + return; + action->setMenu(0); + m_subMenus.insert(action, menu); + QDesignerFormEditorInterface *core = formWindow()->core(); + core->metaDataBase()->remove(menu); +} + +QDesignerMenu *QDesignerMenu::findOrCreateSubMenu(QAction *action) +{ + if (action->menu()) + return qobject_cast<QDesignerMenu*>(action->menu()); + + QDesignerMenu *menu = m_subMenus.value(action); + if (!menu) { + menu = new QDesignerMenu(this); + m_subMenus.insert(action, menu); + } + + return menu; +} + +bool QDesignerMenu::canCreateSubMenu(QAction *action) const // ### improve it's a bit too slow +{ + foreach (const QWidget *aw, action->associatedWidgets()) + if (aw != this) { + if (const QMenu *m = qobject_cast<const QMenu *>(aw)) { + if (m->actions().contains(action)) + return false; // sorry + } else { + if (const QToolBar *tb = qobject_cast<const QToolBar *>(aw)) + if (tb->actions().contains(action)) + return false; // sorry + } + } + return true; +} + +void QDesignerMenu::slotShowSubMenuNow() +{ + m_showSubMenuTimer->stop(); + + if (m_lastSubMenuIndex == m_currentIndex) + return; + + if (m_lastSubMenuIndex != -1) + hideSubMenu(); + + if (m_currentIndex >= realActionCount()) + return; + + QAction *action = currentAction(); + + if (action->isSeparator() || !canCreateSubMenu(action)) + return; + + if (QMenu *menu = findOrCreateSubMenu(action)) { + if (!menu->isVisible()) { + if ((menu->windowFlags() & Qt::Popup) != Qt::Popup) + menu->setWindowFlags(Qt::Popup); + const QRect g = actionGeometry(action); + menu->move(mapToGlobal(g.topRight())); + menu->show(); + menu->setFocus(); + } else { + menu->raise(); + } + menu->setFocus(); + + m_lastSubMenuIndex = m_currentIndex; + } +} + +void QDesignerMenu::showSubMenu(QAction *action) +{ + m_showSubMenuTimer->stop(); + + if (m_editor->isVisible() || !action || qobject_cast<SpecialMenuAction*>(action) + || action->isSeparator() || !isVisible()) + return; + + m_showSubMenuTimer->start(300); +} + +QDesignerMenu *QDesignerMenu::parentMenu() const +{ + return qobject_cast<QDesignerMenu*>(parentWidget()); +} + +QDesignerMenuBar *QDesignerMenu::parentMenuBar() const +{ + if (QDesignerMenuBar *mb = qobject_cast<QDesignerMenuBar*>(parentWidget())) { + return mb; + } else if (QDesignerMenu *m = parentMenu()) { + return m->parentMenuBar(); + } + + return 0; +} + +void QDesignerMenu::setVisible(bool visible) +{ + if (visible) + m_currentIndex = 0; + else + m_lastSubMenuIndex = -1; + + QMenu::setVisible(visible); + +} + +void QDesignerMenu::adjustSpecialActions() +{ + removeAction(m_addItem); + removeAction(m_addSeparator); + addAction(m_addItem); + addAction(m_addSeparator); +} + +bool QDesignerMenu::interactive(bool i) +{ + const bool old = m_interactive; + m_interactive = i; + return old; +} + +void QDesignerMenu::enterEditMode() +{ + if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) { + showLineEdit(); + } else { + hideSubMenu(); + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(tr("Add separator")); + QAction *sep = createAction(QString(), true); + + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, sep, safeActionAt(realActionCount())); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction()); + fw->commandHistory()->push(cmd); + } + } + + fw->endCommand(); + + m_currentIndex = actions().indexOf(m_addItem); + update(); + } +} + +void QDesignerMenu::leaveEditMode(LeaveEditMode mode) +{ + if (mode == Default) + return; + + QAction *action = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + if (m_currentIndex < realActionCount()) { + action = safeActionAt(m_currentIndex); + fw->beginCommand(QApplication::translate("Command", "Set action text")); + } else { + Q_ASSERT(fw != 0); + fw->beginCommand(QApplication::translate("Command", "Insert action")); + action = createAction(ActionEditor::actionTextToName(m_editor->text())); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, currentAction()); + fw->commandHistory()->push(cmd); + } + + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, QLatin1String("text"), m_editor->text()); + fw->commandHistory()->push(cmd); + + if (parentMenu()) { + QAction *parent_action = parentMenu()->currentAction(); + if (parent_action->menu() == 0) { + CreateSubmenuCommand *cmd = new CreateSubmenuCommand(fw); + cmd->init(parentMenu(), parentMenu()->currentAction(), action); + fw->commandHistory()->push(cmd); + } + } + + update(); + fw->endCommand(); +} + +QAction *QDesignerMenu::safeMenuAction(QDesignerMenu *menu) const +{ + QAction *action = menu->menuAction(); + + if (!action) + action = m_subMenus.key(menu); + + return action; +} + +void QDesignerMenu::showLineEdit() +{ + m_showSubMenuTimer->stop(); + + QAction *action = 0; + + if (m_currentIndex < realActionCount()) + action = safeActionAt(m_currentIndex); + else + action = m_addItem; + + if (action->isSeparator()) + return; + + hideSubMenu(); + + // open edit field for item name + setFocus(); + + const QString text = action != m_addItem ? action->text() : QString(); + m_editor->setText(text); + m_editor->selectAll(); + m_editor->setGeometry(actionGeometry(action).adjusted(1, 1, -2, -2)); + m_editor->show(); + m_editor->setFocus(); +} + +QAction *QDesignerMenu::createAction(const QString &objectName, bool separator) +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + return ToolBarEventFilter::createAction(fw, objectName, separator); +} + +// ### share with QDesignerMenu::swap +bool QDesignerMenu::swap(int a, int b) +{ + const int left = qMin(a, b); + int right = qMax(a, b); + + QAction *action_a = safeActionAt(left); + QAction *action_b = safeActionAt(right); + + if (action_a == action_b + || !action_a + || !action_b + || qobject_cast<SpecialMenuAction*>(action_a) + || qobject_cast<SpecialMenuAction*>(action_b)) + return false; // nothing to do + + right = qMin(right, realActionCount()); + if (right < 0) + return false; // nothing to do + + QDesignerFormWindowInterface *fw = formWindow(); + fw->beginCommand(QApplication::translate("Command", "Move action")); + + QAction *action_b_before = safeActionAt(right + 1); + + RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw); + cmd1->init(this, action_b, action_b_before, false); + fw->commandHistory()->push(cmd1); + + QAction *action_a_before = safeActionAt(left + 1); + + InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw); + cmd2->init(this, action_b, action_a_before, false); + fw->commandHistory()->push(cmd2); + + RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw); + cmd3->init(this, action_a, action_b, false); + fw->commandHistory()->push(cmd3); + + InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw); + cmd4->init(this, action_a, action_b_before, true); + fw->commandHistory()->push(cmd4); + + fw->endCommand(); + + return true; +} + +QAction *QDesignerMenu::safeActionAt(int index) const +{ + if (index < 0 || index >= actions().count()) + return 0; + + return actions().at(index); +} + +void QDesignerMenu::hideSubMenu() +{ + m_lastSubMenuIndex = -1; + foreach (QMenu *subMenu, qFindChildren<QMenu*>(this)) { + subMenu->hide(); + } +} + +void QDesignerMenu::deleteAction() +{ + QAction *action = currentAction(); + const int pos = actions().indexOf(action); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, action_before); + fw->commandHistory()->push(cmd); + + update(); +} + +void QDesignerMenu::deactivateMenu() +{ + m_deactivateWindowTimer->start(10); +} + +void QDesignerMenu::slotDeactivateNow() +{ + m_deactivateWindowTimer->stop(); + + if (m_dragging) + return; + + QDesignerMenu *root = findRootMenu(); + + if (! root->findActivatedMenu()) { + root->hide(); + root->hideSubMenu(); + } +} + +void QDesignerMenu::drawSelection(QPainter *p, const QRect &r) +{ + p->save(); + + QColor c = Qt::blue; + p->setPen(QPen(c, 1)); + c.setAlpha(32); + p->setBrush(c); + p->drawRect(r); + + p->restore(); +} + +void QDesignerMenu::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenu::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_menu_p.h b/tools/designer/src/lib/shared/qdesigner_menu_p.h new file mode 100644 index 0000000..ac3ec03 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_menu_p.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MENU_H +#define QDESIGNER_MENU_H + +#include "shared_global_p.h" + +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +class QTimer; +class QLineEdit; + +class QDesignerFormWindowInterface; +class QDesignerActionProviderExtension; +class QDesignerMenu; +class QDesignerMenuBar; +class QPainter; +class QMimeData; + +namespace qdesigner_internal { + class CreateSubmenuCommand; + class ActionInsertionCommand; +} + +class QDESIGNER_SHARED_EXPORT QDesignerMenu: public QMenu +{ + Q_OBJECT +public: + QDesignerMenu(QWidget *parent = 0); + virtual ~QDesignerMenu(); + + bool eventFilter(QObject *object, QEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + QDesignerActionProviderExtension *actionProvider(); + + QDesignerMenu *parentMenu() const; + QDesignerMenuBar *parentMenuBar() const; + + virtual void setVisible(bool visible); + + void adjustSpecialActions(); + + bool interactive(bool i); + void createRealMenuAction(QAction *action); + void removeRealMenu(QAction *action); + + static void drawSelection(QPainter *p, const QRect &r); + + bool dragging() const; + + void closeMenuChain(); + + void moveLeft(); + void moveRight(); + void moveUp(bool ctrl); + void moveDown(bool ctrl); + + // Helper for MenuTaskMenu extension + void deleteAction(QAction *a); + +private slots: + void slotAddSeparator(); + void slotRemoveSelectedAction(); + void slotShowSubMenuNow(); + void slotDeactivateNow(); + void slotAdjustSizeNow(); + +protected: + virtual void actionEvent(QActionEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + virtual void showEvent(QShowEvent *event); + + bool handleEvent(QWidget *widget, QEvent *event); + bool handleMouseDoubleClickEvent(QWidget *widget, QMouseEvent *event); + bool handleMousePressEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseReleaseEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseMoveEvent(QWidget *widget, QMouseEvent *event); + bool handleContextMenuEvent(QWidget *widget, QContextMenuEvent *event); + bool handleKeyPressEvent(QWidget *widget, QKeyEvent *event); + + void startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers); + + void adjustIndicator(const QPoint &pos); + int findAction(const QPoint &pos) const; + + QAction *currentAction() const; + int realActionCount() const; + enum ActionDragCheck { NoActionDrag, ActionDragOnSubMenu, AcceptActionDrag }; + ActionDragCheck checkAction(QAction *action) const; + + void showSubMenu(QAction *action); + + enum LeaveEditMode { + Default = 0, + ForceAccept + }; + + void enterEditMode(); + void leaveEditMode(LeaveEditMode mode); + void showLineEdit(); + + QAction *createAction(const QString &text, bool separator = false); + QDesignerMenu *findOrCreateSubMenu(QAction *action); + + QAction *safeActionAt(int index) const; + QAction *safeMenuAction(QDesignerMenu *menu) const; + bool swap(int a, int b); + + void hideSubMenu(); + void deleteAction(); + void deactivateMenu(); + + bool canCreateSubMenu(QAction *action) const; + QDesignerMenu *findRootMenu() const; + QDesignerMenu *findActivatedMenu() const; + + QRect subMenuPixmapRect(QAction *action) const; + bool hasSubMenuPixmap(QAction *action) const; + + void selectCurrentAction(); + +private: + QPoint m_startPosition; + int m_currentIndex; + QAction *m_addItem; + QAction *m_addSeparator; + QHash<QAction*, QDesignerMenu*> m_subMenus; + QTimer *m_showSubMenuTimer; + QTimer *m_deactivateWindowTimer; + QTimer *m_adjustSizeTimer; + bool m_interactive; + QLineEdit *m_editor; + bool m_dragging; + int m_lastSubMenuIndex; + + friend class qdesigner_internal::CreateSubmenuCommand; + friend class qdesigner_internal::ActionInsertionCommand; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MENU_H diff --git a/tools/designer/src/lib/shared/qdesigner_menubar.cpp b/tools/designer/src/lib/shared/qdesigner_menubar.cpp new file mode 100644 index 0000000..97f1734 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_menubar.cpp @@ -0,0 +1,955 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_menubar_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "actioneditor_p.h" +#include "qdesigner_utils_p.h" +#include "promotiontaskmenu_p.h" +#include "qdesigner_objectinspector_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QMimeData> + +#include <QtCore/qdebug.h> + +#include <QtGui/QApplication> +#include <QtGui/QDrag> +#include <QtGui/QLineEdit> +#include <QtGui/QPainter> +#include <QtGui/qevent.h> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +typedef QList<QAction *> ActionList; + +using namespace qdesigner_internal; + +namespace qdesigner_internal +{ + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +SpecialMenuAction::SpecialMenuAction(QObject *parent) + : QAction(parent) +{ +} + +SpecialMenuAction::~SpecialMenuAction() +{ +} + + +} // namespace qdesigner_internal + + +///////////////////////////////////////////////////////////////////////////////////////////////////////// +QDesignerMenuBar::QDesignerMenuBar(QWidget *parent) : + QMenuBar(parent), + m_addMenu(new SpecialMenuAction(this)), + m_currentIndex(0), + m_interactive(true), + m_editor(new QLineEdit(this)), + m_dragging(false), + m_lastMenuActionIndex( -1), + m_promotionTaskMenu(new PromotionTaskMenu(this, PromotionTaskMenu::ModeSingleWidget, this)) +{ + setContextMenuPolicy(Qt::DefaultContextMenu); + + setAcceptDrops(true); // ### fake + + m_addMenu->setText(tr("Type Here")); + addAction(m_addMenu); + + QFont italic; + italic.setItalic(true); + m_addMenu->setFont(italic); + + m_editor->setObjectName(QLatin1String("__qt__passive_editor")); + m_editor->hide(); + m_editor->installEventFilter(this); + installEventFilter(this); +} + +QDesignerMenuBar::~QDesignerMenuBar() +{ +} + +void QDesignerMenuBar::paintEvent(QPaintEvent *event) +{ + QMenuBar::paintEvent(event); + + QPainter p(this); + + foreach (QAction *a, actions()) { + if (qobject_cast<SpecialMenuAction*>(a)) { + const QRect g = actionGeometry(a); + QLinearGradient lg(g.left(), g.top(), g.left(), g.bottom()); + lg.setColorAt(0.0, Qt::transparent); + lg.setColorAt(0.7, QColor(0, 0, 0, 32)); + lg.setColorAt(1.0, Qt::transparent); + + p.fillRect(g, lg); + } + } + + QAction *action = currentAction(); + + if (m_dragging || !action) + return; + + if (hasFocus()) { + const QRect g = actionGeometry(action); + QDesignerMenu::drawSelection(&p, g.adjusted(1, 1, -1, -1)); + } else if (action->menu() && action->menu()->isVisible()) { + const QRect g = actionGeometry(action); + p.drawRect(g.adjusted(1, 1, -1, -1)); + } +} + +bool QDesignerMenuBar::handleEvent(QWidget *widget, QEvent *event) +{ + if (!formWindow()) + return false; + + if (event->type() == QEvent::FocusIn || event->type() == QEvent::FocusOut) + update(); + + switch (event->type()) { + default: break; + + case QEvent::MouseButtonDblClick: + return handleMouseDoubleClickEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonPress: + return handleMousePressEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(widget, static_cast<QMouseEvent*>(event)); + case QEvent::ContextMenu: + return handleContextMenuEvent(widget, static_cast<QContextMenuEvent*>(event)); + case QEvent::KeyPress: + return handleKeyPressEvent(widget, static_cast<QKeyEvent*>(event)); + case QEvent::FocusIn: + case QEvent::FocusOut: + return widget != m_editor; + } + + return true; +} + +bool QDesignerMenuBar::handleMouseDoubleClickEvent(QWidget *, QMouseEvent *event) +{ + if (!rect().contains(event->pos())) + return true; + + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + event->accept(); + + m_startPosition = QPoint(); + + m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal); + if (m_currentIndex != -1) { + showLineEdit(); + } + + return true; +} + +bool QDesignerMenuBar::handleKeyPressEvent(QWidget *, QKeyEvent *e) +{ + if (m_editor->isHidden()) { // In navigation mode + switch (e->key()) { + + case Qt::Key_Delete: + if (m_currentIndex == -1 || m_currentIndex >= realActionCount()) + break; + hideMenu(); + deleteMenu(); + break; + + case Qt::Key_Left: + e->accept(); + if (QApplication::layoutDirection() == Qt::LeftToRight) + moveLeft(e->modifiers() & Qt::ControlModifier); + else + moveRight(e->modifiers() & Qt::ControlModifier); + return true; + + case Qt::Key_Right: + e->accept(); + if (QApplication::layoutDirection() == Qt::LeftToRight) + moveRight(e->modifiers() & Qt::ControlModifier); + else + moveLeft(e->modifiers() & Qt::ControlModifier); + return true; // no update + + case Qt::Key_Up: + e->accept(); + moveUp(); + return true; + + case Qt::Key_Down: + e->accept(); + moveDown(); + return true; + + case Qt::Key_PageUp: + m_currentIndex = 0; + break; + + case Qt::Key_PageDown: + m_currentIndex = actions().count() - 1; + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + e->accept(); + enterEditMode(); + return true; // no update + + case Qt::Key_Alt: + case Qt::Key_Shift: + case Qt::Key_Control: + case Qt::Key_Escape: + e->ignore(); + setFocus(); // FIXME: this is because some other widget get the focus when CTRL is pressed + return true; // no update + + default: + if (!e->text().isEmpty() && e->text().at(0).toLatin1() >= 32) { + showLineEdit(); + QApplication::sendEvent(m_editor, e); + e->accept(); + } else { + e->ignore(); + } + return true; + } + } else { // In edit mode + switch (e->key()) { + default: + return false; + + case Qt::Key_Control: + e->ignore(); + return true; + + case Qt::Key_Enter: + case Qt::Key_Return: + if (!m_editor->text().isEmpty()) { + leaveEditMode(ForceAccept); + if (m_lastFocusWidget) + m_lastFocusWidget->setFocus(); + + m_editor->hide(); + showMenu(); + break; + } + // fall through + + case Qt::Key_Escape: + update(); + setFocus(); + break; + } + } + + e->accept(); + update(); + + return true; +} + +void QDesignerMenuBar::startDrag(const QPoint &pos) +{ + const int index = findAction(pos); + if (m_currentIndex == -1 || index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(this, action, actions().at(index + 1)); + fw->commandHistory()->push(cmd); + + adjustSize(); + + hideMenu(index); + + QDrag *drag = new QDrag(this); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap(action)); + drag->setMimeData(new ActionRepositoryMimeData(action, Qt::MoveAction)); + + const int old_index = m_currentIndex; + m_currentIndex = -1; + + if (drag->start(Qt::MoveAction) == Qt::IgnoreAction) { + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = old_index; + adjustSize(); + } +} + +bool QDesignerMenuBar::handleMousePressEvent(QWidget *, QMouseEvent *event) +{ + m_startPosition = QPoint(); + event->accept(); + + if (event->button() != Qt::LeftButton) + return true; + + m_startPosition = event->pos(); + const int newIndex = actionIndexAt(this, m_startPosition, Qt::Horizontal); + const bool changed = newIndex != m_currentIndex; + m_currentIndex = newIndex; + updateCurrentAction(changed); + + return true; +} + +bool QDesignerMenuBar::handleMouseReleaseEvent(QWidget *, QMouseEvent *event) +{ + m_startPosition = QPoint(); + + if (event->button() != Qt::LeftButton) + return true; + + event->accept(); + m_currentIndex = actionIndexAt(this, event->pos(), Qt::Horizontal); + if (!m_editor->isVisible() && m_currentIndex != -1 && m_currentIndex < realActionCount()) + showMenu(); + + return true; +} + +bool QDesignerMenuBar::handleMouseMoveEvent(QWidget *, QMouseEvent *event) +{ + if ((event->buttons() & Qt::LeftButton) != Qt::LeftButton) + return true; + + if (m_startPosition.isNull()) + return true; + + const QPoint pos = mapFromGlobal(event->globalPos()); + + if ((pos - m_startPosition).manhattanLength() < qApp->startDragDistance()) + return true; + + const int index = actionIndexAt(this, m_startPosition, Qt::Horizontal); + if (index < actions().count()) { + hideMenu(index); + update(); + } + + startDrag(m_startPosition); + m_startPosition = QPoint(); + + return true; +} + +ActionList QDesignerMenuBar::contextMenuActions() +{ + ActionList rc; + if (QAction *action = safeActionAt(m_currentIndex)) { + if (!qobject_cast<SpecialMenuAction*>(action)) { + QVariant itemData; + qVariantSetValue(itemData, action); + + QAction *remove_action = new QAction(tr("Remove Menu '%1'").arg(action->menu()->objectName()), 0); + remove_action->setData(itemData); + connect(remove_action, SIGNAL(triggered()), this, SLOT(deleteMenu())); + rc.push_back(remove_action); + QAction *sep = new QAction(0); + sep->setSeparator(true); + rc.push_back(sep); + } + } + + m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::TrailingSeparator, rc); + + QAction *remove_menubar = new QAction(tr("Remove Menu Bar"), 0); + connect(remove_menubar, SIGNAL(triggered()), this, SLOT(slotRemoveMenuBar())); + rc.push_back(remove_menubar); + return rc; +} + +bool QDesignerMenuBar::handleContextMenuEvent(QWidget *, QContextMenuEvent *event) +{ + event->accept(); + + m_currentIndex = actionIndexAt(this, mapFromGlobal(event->globalPos()), Qt::Horizontal); + + update(); + + QMenu menu; + const ActionList al = contextMenuActions(); + const ActionList::const_iterator acend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it) + menu.addAction(*it); + menu.exec(event->globalPos()); + return true; +} + +void QDesignerMenuBar::slotRemoveMenuBar() +{ + Q_ASSERT(formWindow() != 0); + + QDesignerFormWindowInterface *fw = formWindow(); + + DeleteMenuBarCommand *cmd = new DeleteMenuBarCommand(fw); + cmd->init(this); + fw->commandHistory()->push(cmd); +} + +void QDesignerMenuBar::focusOutEvent(QFocusEvent *event) +{ + QMenuBar::focusOutEvent(event); +} + +void QDesignerMenuBar::enterEditMode() +{ + if (m_currentIndex >= 0 && m_currentIndex <= realActionCount()) { + showLineEdit(); + } +} + +void QDesignerMenuBar::leaveEditMode(LeaveEditMode mode) +{ + m_editor->releaseKeyboard(); + + if (mode == Default) + return; + + if (m_editor->text().isEmpty()) + return; + + QAction *action = 0; + + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + + if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) { + action = safeActionAt(m_currentIndex); + fw->beginCommand(QApplication::translate("Command", "Change Title")); + } else { + fw->beginCommand(QApplication::translate("Command", "Insert Menu")); + const QString niceObjectName = ActionEditor::actionTextToName(m_editor->text(), QLatin1String("menu")); + QMenu *menu = qobject_cast<QMenu*>(fw->core()->widgetFactory()->createWidget(QLatin1String("QMenu"), this)); + fw->core()->widgetFactory()->initialize(menu); + menu->setObjectName(niceObjectName); + menu->setTitle(tr("Menu")); + fw->ensureUniqueObjectName(menu); + action = menu->menuAction(); + AddMenuActionCommand *cmd = new AddMenuActionCommand(fw); + cmd->init(action, m_addMenu, this, this); + fw->commandHistory()->push(cmd); + } + + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(action, QLatin1String("text"), m_editor->text()); + fw->commandHistory()->push(cmd); + fw->endCommand(); +} + +void QDesignerMenuBar::showLineEdit() +{ + QAction *action = 0; + + if (m_currentIndex >= 0 && m_currentIndex < realActionCount()) + action = safeActionAt(m_currentIndex); + else + action = m_addMenu; + + if (action->isSeparator()) + return; + + // hideMenu(); + + m_lastFocusWidget = qApp->focusWidget(); + + // open edit field for item name + const QString text = action != m_addMenu ? action->text() : QString(); + + m_editor->setText(text); + m_editor->selectAll(); + m_editor->setGeometry(actionGeometry(action)); + m_editor->show(); + qApp->setActiveWindow(m_editor); + m_editor->setFocus(); + m_editor->grabKeyboard(); +} + +bool QDesignerMenuBar::eventFilter(QObject *object, QEvent *event) +{ + if (object != this && object != m_editor) + return false; + + if (!m_editor->isHidden() && object == m_editor && event->type() == QEvent::FocusOut) { + leaveEditMode(Default); + m_editor->hide(); + update(); + return true; + } + + bool dispatch = true; + + switch (event->type()) { + default: break; + + case QEvent::KeyPress: + case QEvent::KeyRelease: + case QEvent::ContextMenu: + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + dispatch = (object != m_editor); + // no break + + case QEvent::Enter: + case QEvent::Leave: + case QEvent::FocusIn: + case QEvent::FocusOut: + { + QWidget *widget = qobject_cast<QWidget*>(object); + + if (dispatch && widget && (widget == this || isAncestorOf(widget))) + return handleEvent(widget, event); + } break; + + case QEvent::Shortcut: + event->accept(); + return true; + } + + return false; +}; + +int QDesignerMenuBar::findAction(const QPoint &pos) const +{ + const int index = actionIndexAt(this, pos, Qt::Horizontal); + if (index == -1) + return realActionCount(); + + return index; +} + +void QDesignerMenuBar::adjustIndicator(const QPoint &pos) +{ + const int index = findAction(pos); + QAction *action = safeActionAt(index); + Q_ASSERT(action != 0); + + if (pos != QPoint(-1, -1)) { + QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu()); + if (!m || m->parentMenu()) { + m_currentIndex = index; + showMenu(index); + } + } + + if (QDesignerActionProviderExtension *a = actionProvider()) { + a->adjustIndicator(pos); + } +} + +QDesignerMenuBar::ActionDragCheck QDesignerMenuBar::checkAction(QAction *action) const +{ + // action belongs to another form + if (!action || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) + return NoActionDrag; + + if (!action->menu()) + return ActionDragOnSubMenu; // simple action only on sub menus + + QDesignerMenu *m = qobject_cast<QDesignerMenu*>(action->menu()); + if (m && m->parentMenu()) + return ActionDragOnSubMenu; // it looks like a submenu + + if (actions().contains(action)) + return ActionDragOnSubMenu; // we already have the action in the menubar + + return AcceptActionDrag; +} + +void QDesignerMenuBar::dragEnterEvent(QDragEnterEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + + QAction *action = d->actionList().first(); + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + m_dragging = true; + d->accept(event); + break; + case AcceptActionDrag: + m_dragging = true; + d->accept(event); + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenuBar::dragMoveEvent(QDragMoveEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d || d->actionList().empty()) { + event->ignore(); + return; + } + QAction *action = d->actionList().first(); + + switch (checkAction(action)) { + case NoActionDrag: + event->ignore(); + break; + case ActionDragOnSubMenu: + event->ignore(); + showMenu(findAction(event->pos())); + break; + case AcceptActionDrag: + d->accept(event); + adjustIndicator(event->pos()); + break; + } +} + +void QDesignerMenuBar::dragLeaveEvent(QDragLeaveEvent *) +{ + m_dragging = false; + + adjustIndicator(QPoint(-1, -1)); +} + +void QDesignerMenuBar::dropEvent(QDropEvent *event) +{ + m_dragging = false; + + if (const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData())) { + + QAction *action = d->actionList().first(); + if (checkAction(action) == AcceptActionDrag) { + event->acceptProposedAction(); + int index = findAction(event->pos()); + index = qMin(index, actions().count() - 1); + + QDesignerFormWindowInterface *fw = formWindow(); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(this, action, safeActionAt(index)); + fw->commandHistory()->push(cmd); + + m_currentIndex = index; + update(); + adjustIndicator(QPoint(-1, -1)); + return; + } + } + event->ignore(); +} + +void QDesignerMenuBar::actionEvent(QActionEvent *event) +{ + QMenuBar::actionEvent(event); +} + +QDesignerFormWindowInterface *QDesignerMenuBar::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast<QDesignerMenuBar*>(this)); +} + +QDesignerActionProviderExtension *QDesignerMenuBar::actionProvider() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + return qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), this); + } + + return 0; +} + +QAction *QDesignerMenuBar::currentAction() const +{ + if (m_currentIndex < 0 || m_currentIndex >= actions().count()) + return 0; + + return safeActionAt(m_currentIndex); +} + +int QDesignerMenuBar::realActionCount() const +{ + return actions().count() - 1; // 1 fake actions +} + +void QDesignerMenuBar::moveLeft(bool ctrl) +{ + if (ctrl) + (void) swap(m_currentIndex, m_currentIndex - 1); + + m_currentIndex = qMax(0, --m_currentIndex); + // Always re-select, swapping destroys order + updateCurrentAction(true); +} + +bool QDesignerMenuBar::dragging() const +{ + return m_dragging; +} + +void QDesignerMenuBar::moveRight(bool ctrl) +{ + if (ctrl) + (void) swap(m_currentIndex + 1, m_currentIndex); + + m_currentIndex = qMin(actions().count() - 1, ++m_currentIndex); + updateCurrentAction(!ctrl); +} + +void QDesignerMenuBar::moveUp() +{ + update(); +} + +void QDesignerMenuBar::moveDown() +{ + showMenu(); +} + +void QDesignerMenuBar::adjustSpecialActions() +{ + removeAction(m_addMenu); + addAction(m_addMenu); +} + +bool QDesignerMenuBar::interactive(bool i) +{ + const bool old = m_interactive; + m_interactive = i; + return old; +} + +void QDesignerMenuBar::hideMenu(int index) +{ + if (index < 0 && m_currentIndex >= 0) + index = m_currentIndex; + + if (index < 0 || index >= realActionCount()) + return; + + QAction *action = safeActionAt(index); + + if (action && action->menu()) { + action->menu()->hide(); + + if (QDesignerMenu *menu = qobject_cast<QDesignerMenu*>(action->menu())) { + menu->closeMenuChain(); + } + } +} + +void QDesignerMenuBar::deleteMenu() +{ + deleteMenuAction(currentAction()); +} + +void QDesignerMenuBar::deleteMenuAction(QAction *action) +{ + if (action && !qobject_cast<SpecialMenuAction*>(action)) { + const int pos = actions().indexOf(action); + QAction *action_before = 0; + if (pos != -1) + action_before = safeActionAt(pos + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveMenuActionCommand *cmd = new RemoveMenuActionCommand(fw); + cmd->init(action, action_before, this, this); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerMenuBar::showMenu(int index) +{ + if (index < 0 && m_currentIndex >= 0) + index = m_currentIndex; + + if (index < 0 || index >= realActionCount()) + return; + + m_currentIndex = index; + QAction *action = currentAction(); + + if (action && action->menu()) { + if (m_lastMenuActionIndex != -1 && m_lastMenuActionIndex != index) { + hideMenu(m_lastMenuActionIndex); + } + + m_lastMenuActionIndex = index; + QMenu *menu = action->menu(); + const QRect g = actionGeometry(action); + + if (!menu->isVisible()) { + if ((menu->windowFlags() & Qt::Popup) != Qt::Popup) + menu->setWindowFlags(Qt::Popup); + menu->adjustSize(); + menu->move(mapToGlobal(g.bottomLeft())); + menu->setFocus(Qt::MouseFocusReason); + menu->raise(); + menu->show(); + } else { + menu->raise(); + } + } +} + +QAction *QDesignerMenuBar::safeActionAt(int index) const +{ + if (index < 0 || index >= actions().count()) + return 0; + + return actions().at(index); +} + +bool QDesignerMenuBar::swap(int a, int b) +{ + const int left = qMin(a, b); + int right = qMax(a, b); + + QAction *action_a = safeActionAt(left); + QAction *action_b = safeActionAt(right); + + if (action_a == action_b + || !action_a + || !action_b + || qobject_cast<SpecialMenuAction*>(action_a) + || qobject_cast<SpecialMenuAction*>(action_b)) + return false; // nothing to do + + right = qMin(right, realActionCount()); + if (right < 0) + return false; // nothing to do + + formWindow()->beginCommand(QApplication::translate("Command", "Move action")); + + QAction *action_b_before = safeActionAt(right + 1); + + QDesignerFormWindowInterface *fw = formWindow(); + RemoveActionFromCommand *cmd1 = new RemoveActionFromCommand(fw); + cmd1->init(this, action_b, action_b_before, false); + fw->commandHistory()->push(cmd1); + + QAction *action_a_before = safeActionAt(left + 1); + + InsertActionIntoCommand *cmd2 = new InsertActionIntoCommand(fw); + cmd2->init(this, action_b, action_a_before, false); + fw->commandHistory()->push(cmd2); + + RemoveActionFromCommand *cmd3 = new RemoveActionFromCommand(fw); + cmd3->init(this, action_a, action_b, false); + fw->commandHistory()->push(cmd3); + + InsertActionIntoCommand *cmd4 = new InsertActionIntoCommand(fw); + cmd4->init(this, action_a, action_b_before, true); + fw->commandHistory()->push(cmd4); + + fw->endCommand(); + + return true; +} + +void QDesignerMenuBar::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenuBar::keyReleaseEvent(QKeyEvent *event) +{ + event->ignore(); +} + +void QDesignerMenuBar::updateCurrentAction(bool selectAction) +{ + update(); + + if (!selectAction) + return; + + QAction *action = currentAction(); + if (!action || action == m_addMenu) + return; + + QMenu *menu = action->menu(); + if (!menu) + return; + + QDesignerObjectInspector *oi = 0; + if (QDesignerFormWindowInterface *fw = formWindow()) + oi = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector()); + + if (!oi) + return; + + oi->clearSelection(); + oi->selectObject(menu); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_menubar_p.h b/tools/designer/src/lib/shared/qdesigner_menubar_p.h new file mode 100644 index 0000000..7c8be4c --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_menubar_p.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_MENUBAR_H +#define QDESIGNER_MENUBAR_H + +#include "shared_global_p.h" + +#include <QtGui/QAction> +#include <QtGui/QMenuBar> + +#include <QtCore/QPointer> +#include <QtCore/QMimeData> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerActionProviderExtension; + +class QLineEdit; +class QMimeData; + +namespace qdesigner_internal { +class PromotionTaskMenu; + +class SpecialMenuAction: public QAction +{ + Q_OBJECT +public: + SpecialMenuAction(QObject *parent = 0); + virtual ~SpecialMenuAction(); +}; + +} // namespace qdesigner_internal + +class QDESIGNER_SHARED_EXPORT QDesignerMenuBar: public QMenuBar +{ + Q_OBJECT +public: + QDesignerMenuBar(QWidget *parent = 0); + virtual ~QDesignerMenuBar(); + + bool eventFilter(QObject *object, QEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + QDesignerActionProviderExtension *actionProvider(); + + void adjustSpecialActions(); + bool interactive(bool i); + bool dragging() const; + + void moveLeft(bool ctrl = false); + void moveRight(bool ctrl = false); + void moveUp(); + void moveDown(); + + // Helpers for MenuTaskMenu/MenuBarTaskMenu extensions + QList<QAction *> contextMenuActions(); + void deleteMenuAction(QAction *action); + +private slots: + void deleteMenu(); + void slotRemoveMenuBar(); + +protected: + virtual void actionEvent(QActionEvent *event); + virtual void dragEnterEvent(QDragEnterEvent *event); + virtual void dragMoveEvent(QDragMoveEvent *event); + virtual void dragLeaveEvent(QDragLeaveEvent *event); + virtual void dropEvent(QDropEvent *event); + virtual void paintEvent(QPaintEvent *event); + virtual void focusOutEvent(QFocusEvent *event); + virtual void keyPressEvent(QKeyEvent *event); + virtual void keyReleaseEvent(QKeyEvent *event); + + bool handleEvent(QWidget *widget, QEvent *event); + bool handleMouseDoubleClickEvent(QWidget *widget, QMouseEvent *event); + bool handleMousePressEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseReleaseEvent(QWidget *widget, QMouseEvent *event); + bool handleMouseMoveEvent(QWidget *widget, QMouseEvent *event); + bool handleContextMenuEvent(QWidget *widget, QContextMenuEvent *event); + bool handleKeyPressEvent(QWidget *widget, QKeyEvent *event); + + void startDrag(const QPoint &pos); + + enum ActionDragCheck { NoActionDrag, ActionDragOnSubMenu, AcceptActionDrag }; + ActionDragCheck checkAction(QAction *action) const; + + void adjustIndicator(const QPoint &pos); + int findAction(const QPoint &pos) const; + + QAction *currentAction() const; + int realActionCount() const; + + enum LeaveEditMode { + Default = 0, + ForceAccept + }; + + void enterEditMode(); + void leaveEditMode(LeaveEditMode mode); + void showLineEdit(); + + void showMenu(int index = -1); + void hideMenu(int index = -1); + + QAction *safeActionAt(int index) const; + + bool swap(int a, int b); + +private: + void updateCurrentAction(bool selectAction); + + QAction *m_addMenu; + QPointer<QMenu> m_activeMenu; + QPoint m_startPosition; + int m_currentIndex; + bool m_interactive; + QLineEdit *m_editor; + bool m_dragging; + int m_lastMenuActionIndex; + QPointer<QWidget> m_lastFocusWidget; + qdesigner_internal::PromotionTaskMenu* m_promotionTaskMenu; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_MENUBAR_H diff --git a/tools/designer/src/lib/shared/qdesigner_objectinspector.cpp b/tools/designer/src/lib/shared/qdesigner_objectinspector.cpp new file mode 100644 index 0000000..9cef55b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_objectinspector.cpp @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_objectinspector_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerObjectInspector::QDesignerObjectInspector(QWidget *parent, Qt::WindowFlags flags) : + QDesignerObjectInspectorInterface(parent, flags) +{ +} + +void QDesignerObjectInspector::mainContainerChanged() +{ +} + +void Selection::clear() +{ + managed.clear(); + unmanaged.clear(); + objects.clear(); +} + +bool Selection::empty() const +{ + return managed.empty() && unmanaged.empty() && objects.empty(); +} + +QObjectList Selection::selection() const +{ + QObjectList rc(objects); + foreach (QObject* o, managed) + rc.push_back(o); + foreach (QObject* o, unmanaged) + rc.push_back(o); + return rc; +} +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_objectinspector_p.h b/tools/designer/src/lib/shared/qdesigner_objectinspector_p.h new file mode 100644 index 0000000..5a4a163 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_objectinspector_p.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNEROBJECTINSPECTOR_H +#define DESIGNEROBJECTINSPECTOR_H + +#include "shared_global_p.h" +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QDesignerDnDItemInterface; + +namespace qdesigner_internal { + +struct QDESIGNER_SHARED_EXPORT Selection { + bool empty() const; + void clear(); + + // Merge all lists + QObjectList selection() const; + + // Selection in cursor (managed widgets) + QWidgetList managed; + // Unmanaged widgets + QWidgetList unmanaged; + // Remaining selected objects (non-widgets) + QObjectList objects; +}; + +// Extends the QDesignerObjectInspectorInterface by functionality +// to access the selection + +class QDESIGNER_SHARED_EXPORT QDesignerObjectInspector: public QDesignerObjectInspectorInterface +{ + Q_OBJECT +public: + QDesignerObjectInspector(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + // Select a qobject unmanaged by form window + virtual bool selectObject(QObject *o) = 0; + virtual void getSelection(Selection &s) const = 0; + virtual void clearSelection() = 0; + +public slots: + virtual void mainContainerChanged(); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DESIGNEROBJECTINSPECTOR_H diff --git a/tools/designer/src/lib/shared/qdesigner_promotion.cpp b/tools/designer/src/lib/shared/qdesigner_promotion.cpp new file mode 100644 index 0000000..526b521 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_promotion.cpp @@ -0,0 +1,373 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_promotion_p.h" +#include "widgetdatabase_p.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtDesigner/QDesignerWidgetBoxInterface> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtCore/QMap> +#include <QtCore/QCoreApplication> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace { + // Return a set of on-promotable classes + const QSet<QString> &nonPromotableClasses() { + static QSet<QString> rc; + if (rc.empty()) { + rc.insert(QLatin1String("Line")); + rc.insert(QLatin1String("QAction")); + rc.insert(QLatin1String("Spacer")); + rc.insert(QLatin1String("QMainWindow")); + rc.insert(QLatin1String("QDialog")); + rc.insert(QLatin1String("QWorkspace")); + rc.insert(QLatin1String("QMdiArea")); + rc.insert(QLatin1String("QMdiSubWindow")); + } + return rc; + } + + // Return widget database index of a promoted class or -1 with error message + int promotedWidgetDataBaseIndex(const QDesignerWidgetDataBaseInterface *widgetDataBase, + const QString &className, + QString *errorMessage) { + const int index = widgetDataBase->indexOfClassName(className); + if (index == -1 || !widgetDataBase->item(index)->isPromoted()) { + *errorMessage = QCoreApplication::tr("%1 is not a promoted class.").arg(className); + return -1; + } + return index; + } + + // Return widget database item of a promoted class or 0 with error message + QDesignerWidgetDataBaseItemInterface *promotedWidgetDataBaseItem(const QDesignerWidgetDataBaseInterface *widgetDataBase, + const QString &className, + QString *errorMessage) { + + const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage); + if (index == -1) + return 0; + return widgetDataBase->item(index); + } + + // extract class name from xml "<widget class="QWidget" ...>". Quite a hack. + QString classNameFromXml(QString xml) { + static const QString tag = QLatin1String("class=\""); + const int pos = xml.indexOf(tag); + if (pos == -1) + return QString(); + xml.remove(0, pos + tag.size()); + const int closingPos = xml.indexOf(QLatin1Char('"')); + if (closingPos == -1) + return QString(); + xml.remove(closingPos, xml.size() - closingPos); + return xml; + } + + // return a list of class names in the scratch pad + QStringList getScratchPadClasses(const QDesignerWidgetBoxInterface *wb) { + QStringList rc; + const int catCount = wb->categoryCount(); + for (int c = 0; c < catCount; c++) { + const QDesignerWidgetBoxInterface::Category category = wb->category(c); + if (category.type() == QDesignerWidgetBoxInterface::Category::Scratchpad) { + const int widgetCount = category.widgetCount(); + for (int w = 0; w < widgetCount; w++) { + const QString className = classNameFromXml( category.widget(w).domXml()); + if (!className.isEmpty()) + rc += className; + } + } + } + return rc; + } +} + +namespace qdesigner_internal { + + QDesignerPromotion::QDesignerPromotion(QDesignerFormEditorInterface *core) : + m_core(core) { + } + + bool QDesignerPromotion::addPromotedClass(const QString &baseClass, + const QString &className, + const QString &includeFile, + QString *errorMessage) + { + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + const int baseClassIndex = widgetDataBase->indexOfClassName(baseClass); + + if (baseClassIndex == -1) { + *errorMessage = QCoreApplication::tr("The base class %1 is invalid.").arg(baseClass); + return false; + } + + const int existingClassIndex = widgetDataBase->indexOfClassName(className); + + if (existingClassIndex != -1) { + *errorMessage = QCoreApplication::tr("The class %1 already exists.").arg(className); + return false; + } + // Clone derived item. + QDesignerWidgetDataBaseItemInterface *promotedItem = WidgetDataBaseItem::clone(widgetDataBase->item(baseClassIndex)); + // Also inherit the container flag in case of QWidget-derived classes + // as it is most likely intended for stacked pages. + // set new props + promotedItem->setName(className); + promotedItem->setGroup(QCoreApplication::tr("Promoted Widgets")); + promotedItem->setCustom(true); + promotedItem->setPromoted(true); + promotedItem->setExtends(baseClass); + promotedItem->setIncludeFile(includeFile); + widgetDataBase->append(promotedItem); + return true; + } + + QList<QDesignerWidgetDataBaseItemInterface *> QDesignerPromotion::promotionBaseClasses() const + { + typedef QMap<QString, QDesignerWidgetDataBaseItemInterface *> SortedDatabaseItemMap; + SortedDatabaseItemMap sortedDatabaseItemMap; + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + + const int cnt = widgetDataBase->count(); + for (int i = 0; i < cnt; i++) { + QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i); + if (canBePromoted(dbItem)) { + sortedDatabaseItemMap.insert(dbItem->name(), dbItem); + } + } + + return sortedDatabaseItemMap.values(); + } + + + bool QDesignerPromotion::canBePromoted(const QDesignerWidgetDataBaseItemInterface *dbItem) const + { + if (dbItem->isPromoted() || !dbItem->extends().isEmpty()) + return false; + + const QString name = dbItem->name(); + + if (nonPromotableClasses().contains(name)) + return false; + + if (name.startsWith(QLatin1String("QDesigner")) || + name.startsWith(QLatin1String("QLayout"))) + return false; + + return true; + } + + QDesignerPromotion::PromotedClasses QDesignerPromotion::promotedClasses() const + { + typedef QMap<QString, QDesignerWidgetDataBaseItemInterface *> ClassNameItemMap; + // A map containing base classes and their promoted classes. + typedef QMap<QString, ClassNameItemMap> BaseClassPromotedMap; + + BaseClassPromotedMap baseClassPromotedMap; + + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + // Look for promoted classes and insert into map according to base class. + const int cnt = widgetDataBase->count(); + for (int i = 0; i < cnt; i++) { + QDesignerWidgetDataBaseItemInterface *dbItem = widgetDataBase->item(i); + if (dbItem->isPromoted()) { + const QString baseClassName = dbItem->extends(); + BaseClassPromotedMap::iterator it = baseClassPromotedMap.find(baseClassName); + if (it == baseClassPromotedMap.end()) { + it = baseClassPromotedMap.insert(baseClassName, ClassNameItemMap()); + } + it.value().insert(dbItem->name(), dbItem); + } + } + // convert map into list. + PromotedClasses rc; + + if (baseClassPromotedMap.empty()) + return rc; + + const BaseClassPromotedMap::const_iterator bcend = baseClassPromotedMap.constEnd(); + for (BaseClassPromotedMap::const_iterator bit = baseClassPromotedMap.constBegin(); bit != bcend; ++bit) { + const int baseIndex = widgetDataBase->indexOfClassName(bit.key()); + Q_ASSERT(baseIndex >= 0); + QDesignerWidgetDataBaseItemInterface *baseItem = widgetDataBase->item(baseIndex); + // promoted + const ClassNameItemMap::const_iterator pcend = bit.value().constEnd(); + for (ClassNameItemMap::const_iterator pit = bit.value().constBegin(); pit != pcend; ++pit) { + PromotedClass item; + item.baseItem = baseItem; + item.promotedItem = pit.value(); + rc.push_back(item); + } + } + + return rc; + } + + QSet<QString> QDesignerPromotion::referencedPromotedClassNames() const { + QSet<QString> rc; + const MetaDataBase *metaDataBase = qobject_cast<const MetaDataBase*>(m_core->metaDataBase()); + if (!metaDataBase) + return rc; + + const QList<QObject*> objs = metaDataBase->objects(); + const QList<QObject*>::const_iterator cend = objs.constEnd(); + for ( QList<QObject*>::const_iterator it = objs.constBegin(); it != cend; ++it) { + const QString customClass = metaDataBase->metaDataBaseItem(*it)->customClassName(); + if (!customClass.isEmpty()) + rc.insert(customClass); + + } + // check the scratchpad of the widget box + if (QDesignerWidgetBoxInterface *widgetBox = m_core->widgetBox()) { + const QStringList scratchPadClasses = getScratchPadClasses(widgetBox); + if (!scratchPadClasses.empty()) { + // Check whether these are actually promoted + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QStringList::const_iterator cend = scratchPadClasses.constEnd(); + for (QStringList::const_iterator it = scratchPadClasses.constBegin(); it != cend; ++it ) { + const int index = widgetDataBase->indexOfClassName(*it); + if (index != -1 && widgetDataBase->item(index)->isPromoted()) + rc += *it; + } + } + } + return rc; + } + + bool QDesignerPromotion::removePromotedClass(const QString &className, QString *errorMessage) { + // check if it exists and is promoted + WidgetDataBase *widgetDataBase = qobject_cast<WidgetDataBase *>(m_core->widgetDataBase()); + if (!widgetDataBase) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be removed").arg(className); + return false; + } + + const int index = promotedWidgetDataBaseIndex(widgetDataBase, className, errorMessage); + if (index == -1) + return false; + + if (referencedPromotedClassNames().contains(className)) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be removed because it is still referenced.").arg(className); + return false; + } + widgetDataBase->remove(index); + return true; + } + + bool QDesignerPromotion::changePromotedClassName(const QString &oldclassName, const QString &newClassName, QString *errorMessage) { + const MetaDataBase *metaDataBase = qobject_cast<const MetaDataBase*>(m_core->metaDataBase()); + if (!metaDataBase) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed").arg(oldclassName); + return false; + } + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + + // check the new name + if (newClassName.isEmpty()) { + *errorMessage = QCoreApplication::tr("The class %1 cannot be renamed to an empty name.").arg(oldclassName); + return false; + } + const int existingIndex = widgetDataBase->indexOfClassName(newClassName); + if (existingIndex != -1) { + *errorMessage = QCoreApplication::tr("There is already a class named %1.").arg(newClassName); + return false; + } + // Check old class + QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, oldclassName, errorMessage); + if (!dbItem) + return false; + + // Change the name in the data base and change all referencing objects in the meta database + dbItem->setName(newClassName); + bool foundReferences = false; + foreach (QObject* object, metaDataBase->objects()) { + MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object); + Q_ASSERT(item); + if (item->customClassName() == oldclassName) { + item->setCustomClassName(newClassName); + foundReferences = true; + } + } + // set state + if (foundReferences) + refreshObjectInspector(); + + return true; + } + + bool QDesignerPromotion::setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage) { + // check file + if (includeFile.isEmpty()) { + *errorMessage = QCoreApplication::tr("Cannot set an empty include file."); + return false; + } + // check item + QDesignerWidgetDataBaseInterface *widgetDataBase = m_core->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *dbItem = promotedWidgetDataBaseItem(widgetDataBase, className, errorMessage); + if (!dbItem) + return false; + + dbItem->setIncludeFile(includeFile); + return true; + } + + void QDesignerPromotion::refreshObjectInspector() { + if (QDesignerFormWindowManagerInterface *fwm = m_core->formWindowManager()) { + if (QDesignerFormWindowInterface *fw = fwm->activeFormWindow()) + if ( QDesignerObjectInspectorInterface *oi = m_core->objectInspector()) { + oi->setFormWindow(fw); + } + } + } +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_promotion_p.h b/tools/designer/src/lib/shared/qdesigner_promotion_p.h new file mode 100644 index 0000000..6806b4c --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_promotion_p.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNERPROMOTION_H +#define QDESIGNERPROMOTION_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerPromotionInterface> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + + class QDESIGNER_SHARED_EXPORT QDesignerPromotion : public QDesignerPromotionInterface + { + public: + explicit QDesignerPromotion(QDesignerFormEditorInterface *core); + + virtual PromotedClasses promotedClasses() const; + + virtual QSet<QString> referencedPromotedClassNames() const; + + virtual bool addPromotedClass(const QString &baseClass, + const QString &className, + const QString &includeFile, + QString *errorMessage); + + virtual bool removePromotedClass(const QString &className, QString *errorMessage); + + virtual bool changePromotedClassName(const QString &oldclassName, const QString &newClassName, QString *errorMessage); + + virtual bool setPromotedClassIncludeFile(const QString &className, const QString &includeFile, QString *errorMessage); + + virtual QList<QDesignerWidgetDataBaseItemInterface *> promotionBaseClasses() const; + + private: + bool canBePromoted(const QDesignerWidgetDataBaseItemInterface *) const; + void refreshObjectInspector(); + + QDesignerFormEditorInterface *m_core; + }; +} + +QT_END_NAMESPACE + +#endif // QDESIGNERPROMOTION_H diff --git a/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp new file mode 100644 index 0000000..8453c16 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_promotiondialog.cpp @@ -0,0 +1,452 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::QDesignerPromotionDialog +*/ + +#include "qdesigner_promotiondialog_p.h" +#include "promotionmodel_p.h" +#include "iconloader_p.h" +#include "widgetdatabase_p.h" +#include "signalslotdialog_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerPromotionInterface> +#include <QtDesigner/QDesignerWidgetDataBaseItemInterface> +#include <abstractdialoggui_p.h> + +#include <QtCore/QTimer> +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QFormLayout> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QTreeView> +#include <QtGui/QHeaderView> +#include <QtGui/QPushButton> +#include <QtGui/QItemSelectionModel> +#include <QtGui/QItemSelection> +#include <QtGui/QComboBox> +#include <QtGui/QLineEdit> +#include <QtGui/QCheckBox> +#include <QtGui/QRegExpValidator> +#include <QtGui/QLabel> +#include <QtGui/QSpacerItem> +#include <QtGui/QMenu> +#include <QtGui/QAction> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + // PromotionParameters + struct PromotionParameters { + QString m_baseClass; + QString m_className; + QString m_includeFile; + }; + + // NewPromotedClassPanel + NewPromotedClassPanel::NewPromotedClassPanel(const QStringList &baseClasses, + int selectedBaseClass, + QWidget *parent) : + QGroupBox(parent), + m_baseClassCombo(new QComboBox), + m_classNameEdit(new QLineEdit), + m_includeFileEdit(new QLineEdit), + m_globalIncludeCheckBox(new QCheckBox), + m_addButton(new QPushButton(tr("Add"))) + { + setTitle(tr("New Promoted Class")); + setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Maximum); + QHBoxLayout *hboxLayout = new QHBoxLayout(this); + + m_classNameEdit->setValidator(new QRegExpValidator(QRegExp(QLatin1String("[_a-zA-Z:][:_a-zA-Z0-9]*")), m_classNameEdit)); + connect(m_classNameEdit, SIGNAL(textChanged(QString)), this, SLOT(slotNameChanged(QString))); + connect(m_includeFileEdit, SIGNAL(textChanged(QString)), this, SLOT(slotIncludeFileChanged(QString))); + + m_baseClassCombo->setEditable(false); + m_baseClassCombo->addItems(baseClasses); + if (selectedBaseClass != -1) + m_baseClassCombo->setCurrentIndex(selectedBaseClass); + + // Grid + QFormLayout *formLayout = new QFormLayout(); + formLayout->addRow(tr("Base class name:"), m_baseClassCombo); + formLayout->addRow(tr("Promoted class name:"), m_classNameEdit); + formLayout->addRow(tr("Header file:"), m_includeFileEdit); + formLayout->addRow(tr("Global include"), m_globalIncludeCheckBox); + hboxLayout->addLayout(formLayout); + hboxLayout->addItem(new QSpacerItem(15, 0, QSizePolicy::Fixed, QSizePolicy::Ignored)); + // Button box + QVBoxLayout *buttonLayout = new QVBoxLayout(); + + m_addButton->setAutoDefault(false); + connect(m_addButton, SIGNAL(clicked()), this, SLOT(slotAdd())); + m_addButton->setEnabled(false); + buttonLayout->addWidget(m_addButton); + + QPushButton *resetButton = new QPushButton(tr("Reset")); + resetButton->setAutoDefault(false); + connect(resetButton, SIGNAL(clicked()), this, SLOT(slotReset())); + + buttonLayout->addWidget(resetButton); + buttonLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Ignored, QSizePolicy::Expanding)); + hboxLayout->addLayout(buttonLayout); + + enableButtons(); + } + + void NewPromotedClassPanel::slotAdd() { + bool ok = false; + emit newPromotedClass(promotionParameters(), &ok); + if (ok) + slotReset(); + } + + void NewPromotedClassPanel::slotReset() { + const QString empty; + m_classNameEdit->setText(empty); + m_includeFileEdit->setText(empty); + m_globalIncludeCheckBox->setCheckState(Qt::Unchecked); + } + + void NewPromotedClassPanel::grabFocus() { + m_classNameEdit->setFocus(Qt::OtherFocusReason); + } + + void NewPromotedClassPanel::slotNameChanged(const QString &className) { + // Suggest a name + if (!className.isEmpty()) { + QString suggestedHeader = className.toLower().replace(QLatin1String("::"), QString(QLatin1Char('_'))); + suggestedHeader += QLatin1String(".h"); + + const bool blocked = m_includeFileEdit->blockSignals(true); + m_includeFileEdit->setText(suggestedHeader); + m_includeFileEdit->blockSignals(blocked); + } + enableButtons(); + } + + void NewPromotedClassPanel::slotIncludeFileChanged(const QString &){ + enableButtons(); + } + + void NewPromotedClassPanel::enableButtons() { + const bool enabled = !m_classNameEdit->text().isEmpty() && !m_includeFileEdit->text().isEmpty(); + m_addButton->setEnabled(enabled); + m_addButton->setDefault(enabled); + } + + PromotionParameters NewPromotedClassPanel::promotionParameters() const { + PromotionParameters rc; + rc.m_baseClass = m_baseClassCombo->currentText(); + rc.m_className = m_classNameEdit->text(); + rc.m_includeFile = buildIncludeFile(m_includeFileEdit->text(), + m_globalIncludeCheckBox->checkState() == Qt::Checked ? IncludeGlobal : IncludeLocal); + return rc; + } + + void NewPromotedClassPanel::chooseBaseClass(const QString &baseClass) { + const int index = m_baseClassCombo->findText (baseClass); + if (index != -1) + m_baseClassCombo->setCurrentIndex (index); + } + + // --------------- QDesignerPromotionDialog + QDesignerPromotionDialog::QDesignerPromotionDialog(QDesignerFormEditorInterface *core, + QWidget *parent, + const QString &promotableWidgetClassName, + QString *promoteTo) : + QDialog(parent), + m_mode(promotableWidgetClassName.isEmpty() || promoteTo == 0 ? ModeEdit : ModeEditChooseClass), + m_promotableWidgetClassName(promotableWidgetClassName), + m_core(core), + m_promoteTo(promoteTo), + m_promotion(core->promotion()), + m_model(new PromotionModel(core)), + m_treeView(new QTreeView), + m_buttonBox(0), + m_removeButton(new QPushButton(createIconSet(QString::fromUtf8("minus.png")), QString())) + { + m_buttonBox = createButtonBox(); + setModal(true); + setWindowTitle(tr("Promoted Widgets")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + // tree view group + QGroupBox *treeViewGroup = new QGroupBox(); + treeViewGroup->setTitle(tr("Promoted Classes")); + QVBoxLayout *treeViewVBoxLayout = new QVBoxLayout(treeViewGroup); + // tree view + m_treeView->setModel (m_model); + m_treeView->setMinimumWidth(450); + m_treeView->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(m_treeView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); + + connect(m_treeView, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotTreeViewContextMenu(QPoint))); + + QHeaderView *headerView = m_treeView->header(); + headerView->setResizeMode(QHeaderView::ResizeToContents); + treeViewVBoxLayout->addWidget(m_treeView); + // remove button + QHBoxLayout *hboxLayout = new QHBoxLayout(); + hboxLayout->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_removeButton->setAutoDefault(false); + connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemove())); + m_removeButton->setEnabled(false); + hboxLayout->addWidget(m_removeButton); + treeViewVBoxLayout->addLayout(hboxLayout); + vboxLayout->addWidget(treeViewGroup); + // Create new panel: Try to be smart and preselect a base class. Default to QFrame + const QStringList &baseClassNameList = baseClassNames(m_promotion); + int preselectedBaseClass = -1; + if (m_mode == ModeEditChooseClass) { + preselectedBaseClass = baseClassNameList.indexOf(m_promotableWidgetClassName); + } + if (preselectedBaseClass == -1) + preselectedBaseClass = baseClassNameList.indexOf(QLatin1String("QFrame")); + + NewPromotedClassPanel *newPromotedClassPanel = new NewPromotedClassPanel(baseClassNameList, preselectedBaseClass); + connect(newPromotedClassPanel, SIGNAL(newPromotedClass(PromotionParameters, bool *)), this, SLOT(slotNewPromotedClass(PromotionParameters, bool *))); + connect(this, SIGNAL(selectedBaseClassChanged(QString)), + newPromotedClassPanel, SLOT(chooseBaseClass(QString))); + vboxLayout->addWidget(newPromotedClassPanel); + // button box + vboxLayout->addWidget(m_buttonBox); + // connect model + connect(m_model, SIGNAL(includeFileChanged(QDesignerWidgetDataBaseItemInterface*, QString)), + this, SLOT(slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface*, QString))); + + connect(m_model, SIGNAL(classNameChanged(QDesignerWidgetDataBaseItemInterface*, QString)), + this, SLOT(slotClassNameChanged(QDesignerWidgetDataBaseItemInterface*, QString))); + + // focus + if (m_mode == ModeEditChooseClass) + newPromotedClassPanel->grabFocus(); + + slotUpdateFromWidgetDatabase(); + } + + QDialogButtonBox *QDesignerPromotionDialog::createButtonBox() { + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Close); + + connect(buttonBox , SIGNAL(accepted()), this, SLOT(slotAcceptPromoteTo())); + buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Promote")); + buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + return buttonBox; + } + + void QDesignerPromotionDialog::slotUpdateFromWidgetDatabase() { + m_model->updateFromWidgetDatabase(); + m_treeView->expandAll(); + m_removeButton->setEnabled(false); + } + + void QDesignerPromotionDialog::delayedUpdateFromWidgetDatabase() { + QTimer::singleShot(0, this, SLOT(slotUpdateFromWidgetDatabase())); + } + + const QStringList &QDesignerPromotionDialog::baseClassNames(const QDesignerPromotionInterface *promotion) { + typedef QList<QDesignerWidgetDataBaseItemInterface *> WidgetDataBaseItemList; + static QStringList rc; + if (rc.empty()) { + // Convert the item list into a string list. + const WidgetDataBaseItemList dbItems = promotion->promotionBaseClasses(); + const WidgetDataBaseItemList::const_iterator cend = dbItems.constEnd(); + for (WidgetDataBaseItemList::const_iterator it = dbItems.constBegin() ; it != cend; ++it) { + rc.push_back( (*it)->name()); + } + } + return rc; + } + + void QDesignerPromotionDialog::slotAcceptPromoteTo() { + Q_ASSERT(m_mode == ModeEditChooseClass); + unsigned flags; + // Ok pressed: Promote to selected class + if (QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags)) { + if (flags & CanPromote) { + *m_promoteTo = dbItem ->name(); + accept(); + } + } + } + + void QDesignerPromotionDialog::slotRemove() { + unsigned flags; + QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem || (flags & Referenced)) + return; + + QString errorMessage; + if (m_promotion->removePromotedClass(dbItem->name(), &errorMessage)) { + slotUpdateFromWidgetDatabase(); + } else { + displayError(errorMessage); + } + } + + void QDesignerPromotionDialog::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) { + // Enable deleting non-referenced items + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(selected, flags); + m_removeButton->setEnabled(dbItem && !(flags & Referenced)); + // In choose mode, can we promote to the class? + if (m_mode == ModeEditChooseClass) { + const bool enablePromoted = flags & CanPromote; + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enablePromoted); + m_buttonBox->button(QDialogButtonBox::Ok)->setDefault(enablePromoted); + } + // different base? + if (dbItem) { + const QString baseClass = dbItem->extends(); + if (baseClass != m_lastSelectedBaseClass) { + m_lastSelectedBaseClass = baseClass; + emit selectedBaseClassChanged(m_lastSelectedBaseClass); + } + } + } + + QDesignerWidgetDataBaseItemInterface *QDesignerPromotionDialog::databaseItemAt(const QItemSelection &selected, unsigned &flags) const { + flags = 0; + const QModelIndexList indexes = selected.indexes(); + if (indexes.empty()) + return 0; + + bool referenced; + QDesignerWidgetDataBaseItemInterface *dbItem = m_model->databaseItemAt(indexes.front(), &referenced); + + if (dbItem) { + if (referenced) + flags |= Referenced; + // In choose mode, can we promote to the class? + if (m_mode == ModeEditChooseClass && dbItem && dbItem->isPromoted() && dbItem->extends() == m_promotableWidgetClassName) + flags |= CanPromote; + + } + return dbItem; + } + + void QDesignerPromotionDialog::slotNewPromotedClass(const PromotionParameters &p, bool *ok) { + QString errorMessage; + *ok = m_promotion->addPromotedClass(p.m_baseClass, p.m_className, p.m_includeFile, &errorMessage); + if (*ok) { + // update and select + slotUpdateFromWidgetDatabase(); + const QModelIndex newClassIndex = m_model->indexOfClass(p.m_className); + if (newClassIndex.isValid()) { + m_treeView->selectionModel()->select(newClassIndex, QItemSelectionModel::SelectCurrent|QItemSelectionModel::Rows); + } + } else { + displayError(errorMessage); + } + } + + void QDesignerPromotionDialog::slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface *dbItem, const QString &includeFile) { + if (includeFile.isEmpty()) { + delayedUpdateFromWidgetDatabase(); + return; + } + + if (dbItem->includeFile() == includeFile) + return; + + QString errorMessage; + if (!m_promotion->setPromotedClassIncludeFile(dbItem->name(), includeFile, &errorMessage)) { + displayError(errorMessage); + delayedUpdateFromWidgetDatabase(); + } + } + + void QDesignerPromotionDialog::slotClassNameChanged(QDesignerWidgetDataBaseItemInterface *dbItem, const QString &newName) { + if (newName.isEmpty()) { + delayedUpdateFromWidgetDatabase(); + return; + } + const QString oldName = dbItem->name(); + if (newName == oldName) + return; + + QString errorMessage; + if (!m_promotion->changePromotedClassName(oldName , newName, &errorMessage)) { + displayError(errorMessage); + delayedUpdateFromWidgetDatabase(); + } + } + + void QDesignerPromotionDialog::slotTreeViewContextMenu(const QPoint &pos) { + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem) + return; + + QMenu menu; + QAction *signalSlotAction = menu.addAction(tr("Change signals/slots...")); + connect(signalSlotAction, SIGNAL(triggered()), this, SLOT(slotEditSignalsSlots())); + + menu.exec(m_treeView->viewport()->mapToGlobal(pos)); + } + + void QDesignerPromotionDialog::slotEditSignalsSlots() { + unsigned flags; + const QDesignerWidgetDataBaseItemInterface *dbItem = databaseItemAt(m_treeView->selectionModel()->selection(), flags); + if (!dbItem) + return; + + SignalSlotDialog::editPromotedClass(m_core, dbItem->name(), this); + } + + void QDesignerPromotionDialog::displayError(const QString &message) { + m_core->dialogGui()->message(this, QDesignerDialogGuiInterface::PromotionErrorMessage, QMessageBox::Warning, + tr("%1 - Error").arg(windowTitle()), message, QMessageBox::Close); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_promotiondialog_p.h b/tools/designer/src/lib/shared/qdesigner_promotiondialog_p.h new file mode 100644 index 0000000..d371df8 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_promotiondialog_p.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef PROMOTIONEDITORDIALOG_H +#define PROMOTIONEDITORDIALOG_H + +#include <QtGui/QDialog> +#include <QtGui/QGroupBox> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerPromotionInterface; +class QDesignerWidgetDataBaseItemInterface; + +class QTreeView; +class QPushButton; +class QItemSelection; +class QDialogButtonBox; +class QComboBox; +class QLineEdit; +class QCheckBox; + +namespace qdesigner_internal { + struct PromotionParameters; + class PromotionModel; + + + // Panel for adding a new promoted class. Separate class for code cleanliness. + class NewPromotedClassPanel : public QGroupBox { + Q_OBJECT + public: + NewPromotedClassPanel(const QStringList &baseClasses, + int selectedBaseClass = -1, + QWidget *parent = 0); + + signals: + void newPromotedClass(const PromotionParameters &, bool *ok); + + public slots: + void grabFocus(); + void chooseBaseClass(const QString &); + private slots: + void slotNameChanged(const QString &); + void slotIncludeFileChanged(const QString &); + void slotAdd(); + void slotReset(); + + private: + PromotionParameters promotionParameters() const; + void enableButtons(); + + QComboBox *m_baseClassCombo; + QLineEdit *m_classNameEdit; + QLineEdit *m_includeFileEdit; + QCheckBox *m_globalIncludeCheckBox; + QPushButton *m_addButton; + }; + + // Dialog for editing promoted classes. + class QDesignerPromotionDialog : public QDialog { + Q_OBJECT + + public: + enum Mode { ModeEdit, ModeEditChooseClass }; + + QDesignerPromotionDialog(QDesignerFormEditorInterface *core, + QWidget *parent = 0, + const QString &promotableWidgetClassName = QString(), + QString *promoteTo = 0); + // Return an alphabetically ordered list of base class names for adding new classes. + static const QStringList &baseClassNames(const QDesignerPromotionInterface *promotion); + + signals: + void selectedBaseClassChanged(const QString &); + private slots: + void slotRemove(); + void slotAcceptPromoteTo(); + void slotSelectionChanged(const QItemSelection &, const QItemSelection &); + void slotNewPromotedClass(const PromotionParameters &, bool *ok); + + void slotIncludeFileChanged(QDesignerWidgetDataBaseItemInterface *, const QString &includeFile); + void slotClassNameChanged(QDesignerWidgetDataBaseItemInterface *, const QString &newName); + void slotUpdateFromWidgetDatabase(); + void slotTreeViewContextMenu(const QPoint &); + void slotEditSignalsSlots(); + + private: + QDialogButtonBox *createButtonBox(); + void delayedUpdateFromWidgetDatabase(); + // Return item at model index and a combination of flags or 0. + enum { Referenced = 1, CanPromote = 2 }; + QDesignerWidgetDataBaseItemInterface *databaseItemAt(const QItemSelection &, unsigned &flags) const; + void displayError(const QString &message); + + const Mode m_mode; + const QString m_promotableWidgetClassName; + QDesignerFormEditorInterface *m_core; + QString *m_promoteTo; + QDesignerPromotionInterface *m_promotion; + PromotionModel *m_model; + QTreeView *m_treeView; + QDialogButtonBox *m_buttonBox; + QPushButton *m_removeButton; + QString m_lastSelectedBaseClass; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // PROMOTIONEDITORDIALOG_H diff --git a/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp b/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp new file mode 100644 index 0000000..ff3b50b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertycommand.cpp @@ -0,0 +1,1479 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "dynamicpropertysheet.h" +#include "qdesigner_propertyeditor_p.h" +#include "qdesigner_integration_p.h" +#include "spacer_widget_p.h" +#include "qdesigner_propertysheet_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerDynamicPropertySheetExtension> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QDesignerObjectInspectorInterface> +#include <QtDesigner/QDesignerIntegrationInterface> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QSize> +#include <QtCore/QTextStream> +#include <QtGui/QWidget> +#include <QtGui/QApplication> +#include <QtGui/QAction> +#include <QtGui/QDialog> +#include <QtGui/QPushButton> +#include <QtGui/QLayout> +#include <qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace { +enum { debugPropertyCommands = 0 }; + +// Debug resolve mask of font +QString fontMask(unsigned m) +{ + QString rc; + if (m & QFont::FamilyResolved) + rc += QLatin1String("Family"); + if (m & QFont::SizeResolved) + rc += QLatin1String("Size "); + if (m & QFont::WeightResolved) + rc += QLatin1String("Bold "); + if (m & QFont::StyleResolved) + rc += QLatin1String("Style "); + if (m & QFont::UnderlineResolved) + rc += QLatin1String("Underline "); + if (m & QFont::StrikeOutResolved) + rc += QLatin1String("StrikeOut "); + if (m & QFont::KerningResolved) + rc += QLatin1String("Kerning "); + if (m & QFont::StyleStrategyResolved) + rc += QLatin1String("StyleStrategy"); + return rc; +} + +// Debug font +QString fontString(const QFont &f) +{ + QString rc; { + const QChar comma = QLatin1Char(','); + QTextStream str(&rc); + str << QLatin1String("QFont(\"") << f.family() << comma << + f.pointSize(); + if (f.bold()) + str << comma << QLatin1String("bold"); + if (f.italic()) + str << comma << QLatin1String("italic"); + if (f.underline()) + str << comma << QLatin1String("underline"); + if (f.strikeOut()) + str << comma << QLatin1String("strikeOut"); + if (f.kerning()) + str << comma << QLatin1String("kerning"); + str << comma << f.styleStrategy() << QLatin1String(" resolve: ") + << fontMask(f.resolve()) << QLatin1Char(')'); + } + return rc; +} +QSize checkSize(const QSize &size) +{ + return size.boundedTo(QSize(0xFFFFFF, 0xFFFFFF)); +} + +QSize diffSize(QDesignerFormWindowInterface *fw) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return QSize(); + + const QSize diff = container->size() - fw->size(); // decoration offset of container window + return diff; +} + +void checkSizes(QDesignerFormWindowInterface *fw, const QSize &size, QSize *formSize, QSize *containerSize) +{ + const QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + const QSize diff = diffSize(fw); // decoration offset of container window + + QSize newFormSize = checkSize(size).expandedTo(fw->mainContainer()->minimumSizeHint()); // don't try to resize to smaller size than minimumSizeHint + QSize newContainerSize = newFormSize + diff; + + newContainerSize = newContainerSize.expandedTo(container->minimumSizeHint()); + newContainerSize = newContainerSize.expandedTo(container->minimumSize()); + + newFormSize = newContainerSize - diff; + + newContainerSize = checkSize(newContainerSize); + + if (formSize) + *formSize = newFormSize; + if (containerSize) + *containerSize = newContainerSize; +} + +/* SubProperties: When applying a changed property to a multiselection, it sometimes makes + * sense to apply only parts (subproperties) of the property. + * For example, if someone changes the x-value of a geometry in the property editor + * and applies it to a multi-selection, y should not be applied as this would cause all + * the widgets to overlap. + * The following routines can be used to find out the changed subproperties of a property, + * which are represented as a mask, and to apply them while leaving the others intact. */ + +enum RectSubPropertyMask { SubPropertyX=1, SubPropertyY = 2, SubPropertyWidth = 4, SubPropertyHeight = 8 }; +enum SizePolicySubPropertyMask { SubPropertyHSizePolicy = 1, SubPropertyHStretch = 2, SubPropertyVSizePolicy = 4, SubPropertyVStretch = 8 }; +enum AlignmentSubPropertyMask { SubPropertyHorizontalAlignment = 1, SubPropertyVerticalAlignment = 2 }; +enum StringSubPropertyMask { SubPropertyStringValue = 1, SubPropertyStringComment = 2, SubPropertyStringTranslatable = 4, SubPropertyStringDisambiguation = 8 }; +enum KeySequenceSubPropertyMask { SubPropertyKeySequenceValue = 1, SubPropertyKeySequenceComment = 2, SubPropertyKeySequenceTranslatable = 4, SubPropertyKeySequenceDisambiguation = 8 }; + +enum CommonSubPropertyMask { SubPropertyAll = 0xFFFFFFFF }; + +// Set the mask flag in mask if the properties do not match. +#define COMPARE_SUBPROPERTY(object1, object2, getter, mask, maskFlag) if (object1.getter() != object2.getter()) mask |= maskFlag; + +// find changed subproperties of a rectangle +unsigned compareSubProperties(const QRect & r1, const QRect & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, x, rc, SubPropertyX) + COMPARE_SUBPROPERTY(r1, r2, y, rc, SubPropertyY) + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} + +// find changed subproperties of a QSize +unsigned compareSubProperties(const QSize & r1, const QSize & r2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(r1, r2, width, rc, SubPropertyWidth) + COMPARE_SUBPROPERTY(r1, r2, height, rc, SubPropertyHeight) + return rc; +} +// find changed subproperties of a QSizePolicy +unsigned compareSubProperties(const QSizePolicy & sp1, const QSizePolicy & sp2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(sp1, sp2, horizontalPolicy, rc, SubPropertyHSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, horizontalStretch, rc, SubPropertyHStretch) + COMPARE_SUBPROPERTY(sp1, sp2, verticalPolicy, rc, SubPropertyVSizePolicy) + COMPARE_SUBPROPERTY(sp1, sp2, verticalStretch, rc, SubPropertyVStretch) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetStringValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetStringValue & str1, const qdesigner_internal::PropertySheetStringValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyStringValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyStringComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyStringTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyStringDisambiguation) + return rc; +} +// find changed subproperties of qdesigner_internal::PropertySheetKeySequenceValue +unsigned compareSubProperties(const qdesigner_internal::PropertySheetKeySequenceValue & str1, const qdesigner_internal::PropertySheetKeySequenceValue & str2) +{ + unsigned rc = 0; + COMPARE_SUBPROPERTY(str1, str2, value, rc, SubPropertyKeySequenceValue) + COMPARE_SUBPROPERTY(str1, str2, comment, rc, SubPropertyKeySequenceComment) + COMPARE_SUBPROPERTY(str1, str2, translatable, rc, SubPropertyKeySequenceTranslatable) + COMPARE_SUBPROPERTY(str1, str2, disambiguation, rc, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Compare font-subproperties taking the [undocumented] +// resolve flag into account +template <class Property> +void compareFontSubProperty(const QFont & f1, + const QFont & f2, + Property (QFont::*getter) () const, + unsigned maskBit, + unsigned &mask) +{ + const bool f1Changed = f1.resolve() & maskBit; + const bool f2Changed = f2.resolve() & maskBit; + // Role has been set/reset in editor + if (f1Changed != f2Changed) { + mask |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (f1Changed && f2Changed && (f1.*getter)() != (f2.*getter)()) + mask |= maskBit; + } +} +// find changed subproperties of a QFont +unsigned compareSubProperties(const QFont & f1, const QFont & f2) +{ + unsigned rc = 0; + compareFontSubProperty(f1, f2, &QFont::family, QFont::FamilyResolved, rc); + compareFontSubProperty(f1, f2, &QFont::pointSize, QFont::SizeResolved, rc); + compareFontSubProperty(f1, f2, &QFont::bold, QFont::WeightResolved, rc); + compareFontSubProperty(f1, f2, &QFont::italic, QFont::StyleResolved, rc); + compareFontSubProperty(f1, f2, &QFont::underline, QFont::UnderlineResolved, rc); + compareFontSubProperty(f1, f2, &QFont::strikeOut, QFont::StrikeOutResolved, rc); + compareFontSubProperty(f1, f2, &QFont::kerning, QFont::KerningResolved, rc); + compareFontSubProperty(f1, f2, &QFont::styleStrategy, QFont::StyleStrategyResolved, rc); + if (debugPropertyCommands) + qDebug() << "compareSubProperties " << fontString(f1) << fontString(f2) << "\n\treturns " << fontMask(rc); + return rc; +} + +// Compare colors of a role +bool roleColorChanged(const QPalette & p1, const QPalette & p2, QPalette::ColorRole role) +{ + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group); + if (p1.color(pgroup, role) != p2.color(pgroup, role)) + return true; + } + return false; +} +// find changed subproperties of a QPalette taking the [undocumented] resolve flags into account +unsigned compareSubProperties(const QPalette & p1, const QPalette & p2) +{ + unsigned rc = 0; + unsigned maskBit = 1u; + // generate a mask for each role + const unsigned p1Changed = p1.resolve(); + const unsigned p2Changed = p2.resolve(); + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + const bool p1RoleChanged = p1Changed & maskBit; + const bool p2RoleChanged = p2Changed & maskBit; + // Role has been set/reset in editor + if (p1RoleChanged != p2RoleChanged) { + rc |= maskBit; + } else { + // Was modified in both palettes: Compare values. + if (p1RoleChanged && p2RoleChanged && roleColorChanged(p1, p2, static_cast<QPalette::ColorRole>(role))) + rc |= maskBit; + } + } + return rc; +} + +// find changed subproperties of a QAlignment which is a flag combination of vertical and horizontal + +unsigned compareSubProperties(Qt::Alignment a1, Qt::Alignment a2) +{ + unsigned rc = 0; + if ((a1 & Qt::AlignHorizontal_Mask) != (a2 & Qt::AlignHorizontal_Mask)) + rc |= SubPropertyHorizontalAlignment; + if ((a1 & Qt::AlignVertical_Mask) != (a2 & Qt::AlignVertical_Mask)) + rc |= SubPropertyVerticalAlignment; + return rc; +} + +Qt::Alignment variantToAlignment(const QVariant & q) +{ + return Qt::Alignment(qdesigner_internal::Utils::valueOf(q)); +} +// find changed subproperties of a variant +unsigned compareSubProperties(const QVariant & q1, const QVariant & q2, qdesigner_internal::SpecialProperty specialProperty) +{ + switch (q1.type()) { + case QVariant::Rect: + return compareSubProperties(q1.toRect(), q2.toRect()); + case QVariant::Size: + return compareSubProperties(q1.toSize(), q2.toSize()); + case QVariant::SizePolicy: + return compareSubProperties(qvariant_cast<QSizePolicy>(q1), qvariant_cast<QSizePolicy>(q2)); + case QVariant::Font: + return compareSubProperties(qvariant_cast<QFont>(q1), qvariant_cast<QFont>(q2)); + case QVariant::Palette: + return compareSubProperties(qvariant_cast<QPalette>(q1), qvariant_cast<QPalette>(q2)); + default: + if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) + return qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q1).compare(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(q2)); + else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) + return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetStringValue>(q2)); + else if (q1.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) + return compareSubProperties(qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q1), qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(q2)); + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: + return compareSubProperties(variantToAlignment(q1), variantToAlignment(q2)); + default: + break; + } + break; + } + return SubPropertyAll; +} + +// Apply the sub property if mask flag is set in mask +#define SET_SUBPROPERTY(rc, newValue, getter, setter, mask, maskFlag) if (mask & maskFlag) rc.setter(newValue.getter()); + +// apply changed subproperties to a rectangle +QRect applyRectSubProperty(const QRect &oldValue, const QRect &newValue, unsigned mask) +{ + QRect rc = oldValue; + SET_SUBPROPERTY(rc, newValue, x, moveLeft, mask, SubPropertyX) + SET_SUBPROPERTY(rc, newValue, y, moveTop, mask, SubPropertyY) + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a rectangle QSize +QSize applySizeSubProperty(const QSize &oldValue, const QSize &newValue, unsigned mask) +{ + QSize rc = oldValue; + SET_SUBPROPERTY(rc, newValue, width, setWidth, mask, SubPropertyWidth) + SET_SUBPROPERTY(rc, newValue, height, setHeight, mask, SubPropertyHeight) + return rc; +} + + +// apply changed subproperties to a SizePolicy +QSizePolicy applySizePolicySubProperty(const QSizePolicy &oldValue, const QSizePolicy &newValue, unsigned mask) +{ + QSizePolicy rc = oldValue; + SET_SUBPROPERTY(rc, newValue, horizontalPolicy, setHorizontalPolicy, mask, SubPropertyHSizePolicy) + SET_SUBPROPERTY(rc, newValue, horizontalStretch, setHorizontalStretch, mask, SubPropertyHStretch) + SET_SUBPROPERTY(rc, newValue, verticalPolicy, setVerticalPolicy, mask, SubPropertyVSizePolicy) + SET_SUBPROPERTY(rc, newValue, verticalStretch, setVerticalStretch, mask, SubPropertyVStretch) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetStringValue +qdesigner_internal::PropertySheetStringValue applyStringSubProperty(const qdesigner_internal::PropertySheetStringValue &oldValue, + const qdesigner_internal::PropertySheetStringValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetStringValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyStringValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyStringComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyStringTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyStringDisambiguation) + return rc; +} + +// apply changed subproperties to a qdesigner_internal::PropertySheetKeySequenceValue +qdesigner_internal::PropertySheetKeySequenceValue applyKeySequenceSubProperty(const qdesigner_internal::PropertySheetKeySequenceValue &oldValue, + const qdesigner_internal::PropertySheetKeySequenceValue &newValue, unsigned mask) +{ + qdesigner_internal::PropertySheetKeySequenceValue rc = oldValue; + SET_SUBPROPERTY(rc, newValue, value, setValue, mask, SubPropertyKeySequenceValue) + SET_SUBPROPERTY(rc, newValue, comment, setComment, mask, SubPropertyKeySequenceComment) + SET_SUBPROPERTY(rc, newValue, translatable, setTranslatable, mask, SubPropertyKeySequenceTranslatable) + SET_SUBPROPERTY(rc, newValue, disambiguation, setDisambiguation, mask, SubPropertyKeySequenceDisambiguation) + return rc; +} + +// Apply the font-subproperties keeping the [undocumented] +// resolve flag in sync (note that PropertySetterType might be something like const T&). +template <class PropertyReturnType, class PropertySetterType> +inline void setFontSubProperty(unsigned mask, + const QFont &newValue, + unsigned maskBit, + PropertyReturnType (QFont::*getter) () const, + void (QFont::*setter) (PropertySetterType), + QFont &value) +{ + if (mask & maskBit) { + (value.*setter)((newValue.*getter)()); + // Set the resolve bit from NewValue in return value + uint r = value.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + value.resolve(r); + if (debugPropertyCommands) + qDebug() << "setFontSubProperty " << fontMask(maskBit) << " resolve=" << origFlag; + } +} +// apply changed subproperties to a QFont +QFont applyFontSubProperty(const QFont &oldValue, const QFont &newValue, unsigned mask) +{ + QFont rc = oldValue; + setFontSubProperty(mask, newValue, QFont::FamilyResolved, &QFont::family, &QFont::setFamily, rc); + setFontSubProperty(mask, newValue, QFont::SizeResolved, &QFont::pointSize, &QFont::setPointSize, rc); + setFontSubProperty(mask, newValue, QFont::WeightResolved, &QFont::bold, &QFont::setBold, rc); + setFontSubProperty(mask, newValue, QFont::StyleResolved, &QFont::italic, &QFont::setItalic, rc); + setFontSubProperty(mask, newValue, QFont::UnderlineResolved, &QFont::underline, &QFont::setUnderline, rc); + setFontSubProperty(mask, newValue, QFont::StrikeOutResolved, &QFont::strikeOut, &QFont::setStrikeOut, rc); + setFontSubProperty(mask, newValue, QFont::KerningResolved, &QFont::kerning, &QFont::setKerning, rc); + setFontSubProperty(mask, newValue, QFont::StyleStrategyResolved, &QFont::styleStrategy, &QFont::setStyleStrategy, rc); + if (debugPropertyCommands) + qDebug() << "applyFontSubProperty old " << fontMask(oldValue.resolve()) << " new " << fontMask(newValue.resolve()) << " return: " << fontMask(rc.resolve()); + return rc; +} + +// apply changed subproperties to a QPalette +QPalette applyPaletteSubProperty(const QPalette &oldValue, const QPalette &newValue, unsigned mask) +{ + QPalette rc = oldValue; + // apply a mask for each role + unsigned maskBit = 1u; + for (int role = QPalette::WindowText; role < QPalette::NColorRoles; role++, maskBit <<= 1u) { + if (mask & maskBit) { + for (int group = QPalette::Active; group < QPalette::NColorGroups; group++) { + const QPalette::ColorGroup pgroup = static_cast<QPalette::ColorGroup>(group); + const QPalette::ColorRole prole = static_cast<QPalette::ColorRole>(role); + rc.setColor(pgroup, prole, newValue.color(pgroup, prole)); + } + // Set the resolve bit from NewValue in return value + uint r = rc.resolve(); + const bool origFlag = newValue.resolve() & maskBit; + if (origFlag) + r |= maskBit; + else + r &= ~maskBit; + rc.resolve(r); + } + } + return rc; +} + +// apply changed subproperties to a QAlignment which is a flag combination of vertical and horizontal +Qt::Alignment applyAlignmentSubProperty(Qt::Alignment oldValue, Qt::Alignment newValue, unsigned mask) +{ + // easy: both changed. + if (mask == (SubPropertyHorizontalAlignment|SubPropertyVerticalAlignment)) + return newValue; + // Change subprop + const Qt::Alignment changeMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignHorizontal_Mask : Qt::AlignVertical_Mask; + const Qt::Alignment takeOverMask = (mask & SubPropertyHorizontalAlignment) ? Qt::AlignVertical_Mask : Qt::AlignHorizontal_Mask; + return (oldValue & takeOverMask) | (newValue & changeMask); +} + +} + +namespace qdesigner_internal { + +// apply changed subproperties to a variant +PropertyHelper::Value applySubProperty(const QVariant &oldValue, const QVariant &newValue, qdesigner_internal::SpecialProperty specialProperty, unsigned mask, bool changed) +{ + if (mask == SubPropertyAll) + return PropertyHelper::Value(newValue, changed); + + switch (oldValue.type()) { + case QVariant::Rect: + return PropertyHelper::Value(applyRectSubProperty(oldValue.toRect(), newValue.toRect(), mask), changed); + case QVariant::Size: + return PropertyHelper::Value(applySizeSubProperty(oldValue.toSize(), newValue.toSize(), mask), changed); + case QVariant::SizePolicy: + return PropertyHelper::Value(qVariantFromValue(applySizePolicySubProperty(qvariant_cast<QSizePolicy>(oldValue), qvariant_cast<QSizePolicy>(newValue), mask)), changed); + case QVariant::Font: { + // Changed flag in case of font and palette depends on resolve mask only, not on the passed "changed" value. + + // The first case: the user changed bold subproperty and then pressed reset button for this subproperty (not for + // the whole font property). We instantiate SetPropertyCommand passing changed=true. But in this case no + // subproperty is changed and the whole property should be marked an unchanged. + + // The second case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset next to bold subproperty. In result the 2nd widget should have the whole + // font property marked as unchanged and the 1st widget should have the font property + // marked as changed and only italic subproperty should be marked as changed (the bold should be reset). + + // The third case: there are 2 pushbuttons, for 1st the user set bold and italic subproperties, + // for the 2nd he set bold only. He does multiselection so that the current widget is the 2nd one. + // He press reset button for the whole font property. In result whole font properties for both + // widgets should be marked as unchanged. + QFont font = applyFontSubProperty(qvariant_cast<QFont>(oldValue), qvariant_cast<QFont>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(font), font.resolve()); + } + case QVariant::Palette: { + QPalette palette = applyPaletteSubProperty(qvariant_cast<QPalette>(oldValue), qvariant_cast<QPalette>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(palette), palette.resolve()); + } + default: + if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetIconValue>()) { + PropertySheetIconValue icon = qvariant_cast<qdesigner_internal::PropertySheetIconValue>(oldValue); + icon.assign(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(icon), icon.mask()); + } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetStringValue>()) { + qdesigner_internal::PropertySheetStringValue str = applyStringSubProperty( + qvariant_cast<qdesigner_internal::PropertySheetStringValue>(oldValue), + qvariant_cast<qdesigner_internal::PropertySheetStringValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(str), changed); + } else if (oldValue.userType() == qMetaTypeId<qdesigner_internal::PropertySheetKeySequenceValue>()) { + qdesigner_internal::PropertySheetKeySequenceValue key = applyKeySequenceSubProperty( + qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(oldValue), + qvariant_cast<qdesigner_internal::PropertySheetKeySequenceValue>(newValue), mask); + return PropertyHelper::Value(qVariantFromValue(key), changed); + } + // Enumerations, flags + switch (specialProperty) { + case qdesigner_internal::SP_Alignment: { + qdesigner_internal::PropertySheetFlagValue f = qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(oldValue); + f.value = applyAlignmentSubProperty(variantToAlignment(oldValue), variantToAlignment(newValue), mask); + QVariant v; + qVariantSetValue(v, f); + return PropertyHelper::Value(v, changed); + } + default: + break; + } + break; + } + return PropertyHelper::Value(newValue, changed); + +} +// figure out special property +enum SpecialProperty getSpecialProperty(const QString& propertyName) +{ + if (propertyName == QLatin1String("objectName")) + return SP_ObjectName; + if (propertyName == QLatin1String("layoutName")) + return SP_LayoutName; + if (propertyName == QLatin1String("spacerName")) + return SP_SpacerName; + if (propertyName == QLatin1String("icon")) + return SP_Icon; + if (propertyName == QLatin1String("currentTabName")) + return SP_CurrentTabName; + if (propertyName == QLatin1String("currentItemName")) + return SP_CurrentItemName; + if (propertyName == QLatin1String("currentPageName")) + return SP_CurrentPageName; + if (propertyName == QLatin1String("geometry")) + return SP_Geometry; + if (propertyName == QLatin1String("windowTitle")) + return SP_WindowTitle; + if (propertyName == QLatin1String("minimumSize")) + return SP_MinimumSize; + if (propertyName == QLatin1String("maximumSize")) + return SP_MaximumSize; + if (propertyName == QLatin1String("alignment")) + return SP_Alignment; + if (propertyName == QLatin1String("autoDefault")) + return SP_AutoDefault; + if (propertyName == QLatin1String("shortcut")) + return SP_Shortcut; + if (propertyName == QLatin1String("orientation")) + return SP_Orientation; + return SP_None; +} + + +PropertyHelper::PropertyHelper(QObject* object, + SpecialProperty specialProperty, + QDesignerPropertySheetExtension *sheet, + int index) : + m_specialProperty(specialProperty), + m_object(object), + m_objectType(OT_Object), + m_propertySheet(sheet), m_index(index), + m_oldValue(m_propertySheet->property(m_index), m_propertySheet->isChanged(m_index)) +{ + if (object->isWidgetType()) { + m_parentWidget = (qobject_cast<QWidget*>(object))->parentWidget(); + m_objectType = OT_Widget; + } else { + if (const QAction *action = qobject_cast<const QAction *>(m_object)) + m_objectType = action->associatedWidgets().empty() ? OT_FreeAction : OT_AssociatedAction; + } + + if(debugPropertyCommands) + qDebug() << "PropertyHelper on " << m_object->objectName() << " index= " << m_index << " type = " << m_objectType; +} + +QDesignerIntegration *PropertyHelper::integration(QDesignerFormWindowInterface *fw) const +{ + return qobject_cast<QDesignerIntegration *>(fw->core()->integration()); +} + +// Set widget value, apply corrections and checks in case of main window. +void PropertyHelper::checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w, + SpecialProperty specialProperty, QVariant &value) +{ + + bool isMainContainer = false; + if (QDesignerFormWindowCursorInterface *cursor = fw->cursor()) { + if (cursor->isWidgetSelected(w)) { + if (cursor->isWidgetSelected(fw->mainContainer())) { + isMainContainer = true; + } + } + } + if (!isMainContainer) + return; + + QWidget *container = fw->core()->integration()->containerWindow(fw); + if (!container) + return; + + + switch (specialProperty) { + case SP_MinimumSize: { + const QSize size = checkSize(value.toSize()); + qVariantSetValue(value, size); + } + + break; + case SP_MaximumSize: { + QSize fs, cs; + checkSizes(fw, value.toSize(), &fs, &cs); + container->setMaximumSize(cs); + fw->mainContainer()->setMaximumSize(fs); + qVariantSetValue(value, fs); + + } + break; + case SP_Geometry: { + QRect r = value.toRect(); + QSize fs, cs; + checkSizes(fw, r.size(), &fs, &cs); + container->resize(cs); + r.setSize(fs); + qVariantSetValue(value, r); + } + break; + default: + break; + } +} + +unsigned PropertyHelper::updateMask() const +{ + unsigned rc = 0; + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + case SP_CurrentTabName: + case SP_CurrentItemName: + case SP_CurrentPageName: + if (m_objectType != OT_FreeAction) + rc |= UpdateObjectInspector; + break; + case SP_Icon: + if (m_objectType == OT_AssociatedAction) + rc |= UpdateObjectInspector; + break; + case SP_Orientation: // for updating splitter icon + rc |= UpdateObjectInspector; + break; + default: + break; + + } + return rc; +} + + +bool PropertyHelper::canMerge(const PropertyHelper &other) const +{ + return m_object == other.m_object && m_index == other.m_index; +} + +void PropertyHelper::triggerActionChanged(QAction *a) +{ + a->setData(QVariant(true)); // this triggers signal "changed" in QAction + a->setData(QVariant(false)); +} + +// Update the object to reflect the changes +void PropertyHelper::updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::updateObject(" << m_object->objectName() << ") " << oldValue << " -> " << newValue; + } + switch (m_objectType) { + case OT_Widget: { + switch (m_specialProperty) { + case SP_ObjectName: { + const QString oldName = qVariantValue<PropertySheetStringValue>(oldValue).value(); + const QString newName = qVariantValue<PropertySheetStringValue>(newValue).value(); + QDesignerFormWindowCommand::updateBuddies(fw, oldName, newName); + } + break; + default: + break; + } + } break; + case OT_AssociatedAction: + case OT_FreeAction: + // SP_Shortcut is a fake property, so, QAction::changed does not trigger. + if (m_specialProperty == SP_ObjectName || m_specialProperty == SP_Shortcut) + triggerActionChanged(qobject_cast<QAction *>(m_object)); + break; + default: + break; + } + + switch (m_specialProperty) { + case SP_ObjectName: + case SP_LayoutName: + case SP_SpacerName: + if (QDesignerIntegration *integr = integration(fw)) { + const QString oldName = qVariantValue<PropertySheetStringValue>(oldValue).value(); + const QString newName = qVariantValue<PropertySheetStringValue>(newValue).value(); + integr->emitObjectNameChanged(fw, m_object, newName, oldName); + } + break; + default: + break; + } +} + +void PropertyHelper::ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const +{ + switch (m_specialProperty) { + case SP_SpacerName: + if (object->isWidgetType()) { + if (Spacer *sp = qobject_cast<Spacer *>(object)) { + fw->ensureUniqueObjectName(sp); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_LayoutName: // Layout name is invoked on the parent widget. + if (object->isWidgetType()) { + const QWidget * w = qobject_cast<const QWidget *>(object); + if (QLayout *wlayout = w->layout()) { + fw->ensureUniqueObjectName(wlayout); + return; + } + } + fw->ensureUniqueObjectName(object); + break; + case SP_ObjectName: + fw->ensureUniqueObjectName(object); + break; + default: + break; + } +} + +PropertyHelper::Value PropertyHelper::setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask) +{ + // Set new whole value + if (subPropertyMask == SubPropertyAll) + return applyValue(fw, m_oldValue.first, Value(value, changed)); + + // apply subproperties + const PropertyHelper::Value maskedNewValue = applySubProperty(m_oldValue.first, value, m_specialProperty, subPropertyMask, changed); + return applyValue(fw, m_oldValue.first, maskedNewValue); +} + +// Apply the value and update. Returns corrected value +PropertyHelper::Value PropertyHelper::applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue) +{ + if(debugPropertyCommands){ + qDebug() << "PropertyHelper::applyValue(" << m_object << ") " << oldValue << " -> " << newValue.first << " changed=" << newValue.second; + } + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, newValue.first); + } + + m_propertySheet->setProperty(m_index, newValue.first); + m_propertySheet->setChanged(m_index, newValue.second); + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + newValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, oldValue, newValue.first); + return newValue; +} + +PropertyHelper::Value PropertyHelper::restoreOldValue(QDesignerFormWindowInterface *fw) +{ + return applyValue(fw, m_propertySheet->property(m_index), m_oldValue); +} + +// find the default value in widget DB in case PropertySheet::reset fails +QVariant PropertyHelper::findDefaultValue(QDesignerFormWindowInterface *fw) const +{ + if (m_specialProperty == SP_AutoDefault && qobject_cast<const QPushButton*>(m_object)) { + // AutoDefault defaults to true on dialogs + const bool isDialog = qobject_cast<const QDialog *>(fw->mainContainer()); + return QVariant(isDialog); + } + + const int item_idx = fw->core()->widgetDataBase()->indexOfObject(m_object); + if (item_idx == -1) + return m_oldValue.first; // We simply don't know the value in this case + + const QDesignerWidgetDataBaseItemInterface *item = fw->core()->widgetDataBase()->item(item_idx); + const QList<QVariant> default_prop_values = item->defaultPropertyValues(); + if (m_index < default_prop_values.size()) + return default_prop_values.at(m_index); + + if (m_oldValue.first.type() == QVariant::Color) + return QColor(); + + return m_oldValue.first; // Again, we just don't know +} + +PropertyHelper::Value PropertyHelper::restoreDefaultValue(QDesignerFormWindowInterface *fw) +{ + + Value defaultValue = qMakePair(QVariant(), false); + const QVariant currentValue = m_propertySheet->property(m_index); + // try to reset sheet, else try to find default + if (m_propertySheet->reset(m_index)) { + defaultValue.first = m_propertySheet->property(m_index); + } else { + defaultValue.first = findDefaultValue(fw); + m_propertySheet->setProperty(m_index, defaultValue.first); + } + + m_propertySheet->setChanged(m_index, defaultValue.second); + + if (m_objectType == OT_Widget) { + checkApplyWidgetValue(fw, qobject_cast<QWidget *>(m_object), m_specialProperty, defaultValue.first); + } + + switch (m_specialProperty) { + case SP_LayoutName: + case SP_ObjectName: + case SP_SpacerName: + ensureUniqueObjectName(fw, m_object); + defaultValue.first = m_propertySheet->property(m_index); + break; + default: + break; + } + + updateObject(fw, currentValue, defaultValue.first); + return defaultValue; +} + +// ---- PropertyListCommand::PropertyDescription( + + +PropertyListCommand::PropertyDescription::PropertyDescription(const QString &propertyName, + QDesignerPropertySheetExtension *propertySheet, + int index) : + m_propertyName(propertyName), + m_propertyGroup(propertySheet->propertyGroup(index)), + m_propertyType(propertySheet->property(index).type()), + m_specialProperty(getSpecialProperty(propertyName)) +{ +} + +PropertyListCommand::PropertyDescription::PropertyDescription() : + m_propertyType(QVariant::Invalid), + m_specialProperty(SP_None) +{ +} + +void PropertyListCommand::PropertyDescription::debug() const +{ + qDebug() << m_propertyName << m_propertyGroup << m_propertyType << m_specialProperty; +} + +bool PropertyListCommand::PropertyDescription::equals(const PropertyDescription &p) const +{ + return m_propertyType == p.m_propertyType && m_specialProperty == p.m_specialProperty && + m_propertyName == p.m_propertyName && m_propertyGroup == p.m_propertyGroup; +} + + +// ---- PropertyListCommand +PropertyListCommand::PropertyListCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QString(), formWindow) +{ +} + +const QString PropertyListCommand::propertyName() const +{ + return m_propertyDescription.m_propertyName; +} + +SpecialProperty PropertyListCommand::specialProperty() const +{ + return m_propertyDescription.m_specialProperty; +} + +// add an object +bool PropertyListCommand::add(QObject *object, const QString &propertyName) +{ + QDesignerPropertySheetExtension* sheet = propertySheet(object); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName); + if (index == -1) + return false; + + if (QDesignerPropertySheet *exSheet = qobject_cast<QDesignerPropertySheet*>(core()->extensionManager()->extension(object, Q_TYPEID(QDesignerPropertySheetExtension)))) + if (!exSheet->isEnabled(index)) + return false; + + const PropertyDescription description(propertyName, sheet, index); + + if (m_propertyHelperList.empty()) { + // first entry + m_propertyDescription = description; + } else { + // checks: mismatch or only one object in case of name + const bool match = m_propertyDescription.equals(description); + if (!match || m_propertyDescription.m_specialProperty == SP_ObjectName) + return false; + } + m_propertyHelperList.push_back(PropertyHelper(object, m_propertyDescription.m_specialProperty, sheet, index)); + return true; +} + + +// Init from a list and make sure referenceObject is added first to obtain the right property group +bool PropertyListCommand::initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + propertyHelperList().clear(); + + // Ensure the referenceObject (property editor) is first, so the right property group is chosen. + if (referenceObject) { + if (!add(referenceObject, apropertyName)) + return false; + } + foreach (QObject *o, list) { + if (o != referenceObject) + add(o, apropertyName); + } + + return !propertyHelperList().empty(); +} + + +QObject* PropertyListCommand::object(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList[index].object(); +} + +QVariant PropertyListCommand::oldValue(int index) const +{ + Q_ASSERT(index < m_propertyHelperList.size()); + return m_propertyHelperList[index].oldValue(); +} + +void PropertyListCommand::setOldValue(const QVariant &oldValue, int index) +{ + Q_ASSERT(index < m_propertyHelperList.size()); + m_propertyHelperList[index].setOldValue(oldValue); +} +// ----- SetValueFunction: Set a new value when applied to a PropertyHelper. +class SetValueFunction { +public: + SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask); + + PropertyHelper::Value operator()(PropertyHelper&); +private: + QDesignerFormWindowInterface *m_formWindow; + const PropertyHelper::Value m_newValue; + const unsigned m_subPropertyMask; +}; + + +SetValueFunction::SetValueFunction(QDesignerFormWindowInterface *formWindow, const PropertyHelper::Value &newValue, unsigned subPropertyMask) : + m_formWindow(formWindow), + m_newValue(newValue), + m_subPropertyMask(subPropertyMask) +{ +} + +PropertyHelper::Value SetValueFunction::operator()(PropertyHelper &ph) { + return ph.setValue(m_formWindow, m_newValue.first, m_newValue.second, m_subPropertyMask); +} + +// ----- UndoSetValueFunction: Restore old value when applied to a PropertyHelper. +class UndoSetValueFunction { +public: + UndoSetValueFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreOldValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- RestoreDefaultFunction: Restore default value when applied to a PropertyHelper. +class RestoreDefaultFunction { +public: + RestoreDefaultFunction(QDesignerFormWindowInterface *formWindow) : m_formWindow(formWindow) {} + PropertyHelper::Value operator()(PropertyHelper& ph) { return ph.restoreDefaultValue(m_formWindow); } +private: + QDesignerFormWindowInterface *m_formWindow; +}; + +// ----- changePropertyList: Iterates over a sequence of PropertyHelpers and +// applies a function to them. +// The function returns the corrected value which is then set in the property editor. +// Returns a combination of update flags. +template <class PropertyListIterator, class Function> + unsigned changePropertyList(QDesignerFormEditorInterface *core, + const QString &propertyName, + PropertyListIterator begin, + PropertyListIterator end, + Function function) +{ + unsigned updateMask = 0; + QDesignerPropertyEditorInterface *propertyEditor = core->propertyEditor(); + bool updatedPropertyEditor = false; + + for (PropertyListIterator it = begin; it != end; ++it) { + if (QObject* object = it->object()) { // Might have been deleted in the meantime + const PropertyHelper::Value newValue = function(*it); + updateMask |= it->updateMask(); + // Update property editor if it is the current object + if (!updatedPropertyEditor && propertyEditor && object == propertyEditor->object()) { + propertyEditor->setPropertyValue(propertyName, newValue.first, newValue.second); + updatedPropertyEditor = true; + } + } + } + if (!updatedPropertyEditor) updateMask |= PropertyHelper::UpdatePropertyEditor; + return updateMask; +} + + +// set a new value, return update mask +unsigned PropertyListCommand::setValue(QVariant value, bool changed, unsigned subPropertyMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::setValue(" << value << changed << subPropertyMask << ')'; + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + SetValueFunction(formWindow(), PropertyHelper::Value(value, changed), subPropertyMask)); +} + +// restore old value, return update mask +unsigned PropertyListCommand::restoreOldValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreOldValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + UndoSetValueFunction(formWindow())); +} +// set default value, return update mask +unsigned PropertyListCommand::restoreDefaultValue() +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::restoreDefaultValue()"; + + return changePropertyList(formWindow()->core(), + m_propertyDescription.m_propertyName, m_propertyHelperList.begin(), m_propertyHelperList.end(), + RestoreDefaultFunction(formWindow())); +} + +// update +void PropertyListCommand::update(unsigned updateMask) +{ + if(debugPropertyCommands) + qDebug() << "PropertyListCommand::update(" << updateMask << ')'; + + if (updateMask & PropertyHelper::UpdateObjectInspector) { + if (QDesignerObjectInspectorInterface *oi = formWindow()->core()->objectInspector()) + oi->setFormWindow(formWindow()); + } + + if (updateMask & PropertyHelper::UpdatePropertyEditor) { + // this is needed when f.ex. undo, changes parent's palette, but + // the child is the active widget, + // TODO: current object? + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + propertyEditor->setObject(propertyEditor->object()); + } + } +} + +void PropertyListCommand::undo() +{ + update(restoreOldValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +// check if lists are aequivalent for command merging (same widgets and props) +bool PropertyListCommand::canMergeLists(const PropertyHelperList& other) const +{ + if (m_propertyHelperList.size() != other.size()) + return false; + for (int i = 0; i < m_propertyHelperList.size(); i++) { + if (!m_propertyHelperList[i].canMerge(other[i])) + return false; + } + return true; +} + +// ---- SetPropertyCommand ---- +SetPropertyCommand::SetPropertyCommand(QDesignerFormWindowInterface *formWindow) + : PropertyListCommand(formWindow), + m_subPropertyMask(SubPropertyAll) +{ +} + +bool SetPropertyCommand::init(QObject *object, const QString &apropertyName, const QVariant &newValue) +{ + Q_ASSERT(object); + + m_newValue = newValue; + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool SetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, const QVariant &newValue, + QObject *referenceObject, bool enableSubPropertyHandling) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + m_newValue = newValue; + + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size() << " reference " << referenceObject; + + setDescription(); + + if (enableSubPropertyHandling) + m_subPropertyMask = subPropertyMask(newValue, referenceObject); + return true; +} + +unsigned SetPropertyCommand::subPropertyMask(const QVariant &newValue, QObject *referenceObject) +{ + // figure out the mask of changed sub properties when comparing newValue to the current value of the reference object. + if (!referenceObject) + return SubPropertyAll; + + QDesignerPropertySheetExtension* sheet = propertySheet(referenceObject); + Q_ASSERT(sheet); + + const int index = sheet->indexOf(propertyName()); + if (index == -1 || !sheet->isVisible(index)) + return SubPropertyAll; + + return compareSubProperties(sheet->property(index), newValue, specialProperty()); +} + +void SetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Changed '%1' of '%2'").arg(propertyName()).arg(propertyHelperList()[0].object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Changed '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void SetPropertyCommand::redo() +{ + update(setValue(m_newValue, true, m_subPropertyMask)); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + + +int SetPropertyCommand::id() const +{ + return 1976; +} + +bool SetPropertyCommand::mergeWith(const QUndoCommand *other) +{ + if (id() != other->id() || !formWindow()->isDirty()) + return false; + + // Merging: When for example when the user types ahead in an inplace-editor, + // it makes sense to merge all the generated commands containing the one-character changes. + // In the case of subproperties, if the user changes the font size from 10 to 30 via 20 + // and then changes to bold, it makes sense to merge the font size commands only. + // This is why the m_subPropertyMask is checked. + + const SetPropertyCommand *cmd = static_cast<const SetPropertyCommand*>(other); + if (!propertyDescription().equals(cmd->propertyDescription()) || + m_subPropertyMask != cmd->m_subPropertyMask || + !canMergeLists(cmd->propertyHelperList())) + return false; + + m_newValue = cmd->newValue(); + m_subPropertyMask |= cmd->m_subPropertyMask; + if(debugPropertyCommands) + qDebug() << "SetPropertyCommand::mergeWith() succeeded " << propertyName(); + + return true; +} + +// ---- ResetPropertyCommand ---- +ResetPropertyCommand::ResetPropertyCommand(QDesignerFormWindowInterface *formWindow) + : PropertyListCommand(formWindow) +{ +} + +bool ResetPropertyCommand::init(QObject *object, const QString &apropertyName) +{ + Q_ASSERT(object); + + propertyHelperList().clear(); + if (!add(object, apropertyName)) + return false; + + setDescription(); + return true; +} + +bool ResetPropertyCommand::init(const ObjectList &list, const QString &apropertyName, QObject *referenceObject) +{ + if (!initList(list, apropertyName, referenceObject)) + return false; + + if(debugPropertyCommands) + qDebug() << "ResetPropertyCommand::init()" << propertyHelperList().size() << '/' << list.size(); + + setDescription(); + return true; +} + +void ResetPropertyCommand::setDescription() +{ + if (propertyHelperList().size() == 1) { + setText(QApplication::translate("Command", "Reset '%1' of '%2'").arg(propertyName()).arg(propertyHelperList()[0].object()->objectName())); + } else { + int count = propertyHelperList().size(); + setText(QApplication::translate("Command", "Reset '%1' of %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(propertyName())); + } +} + +void ResetPropertyCommand::redo() +{ + update(restoreDefaultValue()); + QDesignerPropertyEditor *designerPropertyEditor = qobject_cast<QDesignerPropertyEditor *>(core()->propertyEditor()); + if (designerPropertyEditor) + designerPropertyEditor->updatePropertySheet(); +} + +AddDynamicPropertyCommand::AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool AddDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current, + const QString &propertyName, const QVariant &value) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_selection.clear(); + + if (!value.isValid()) + return false; + + if (!dynamicSheet->canAddDynamicProperty(m_propertyName)) + return false; + + m_selection.append(current); + + m_value = value; + + QListIterator<QObject *> it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_selection.contains(obj)) + continue; + dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + Q_ASSERT(dynamicSheet); + if (dynamicSheet->canAddDynamicProperty(m_propertyName)) + m_selection.append(obj); + } + + setDescription(); + return true; +} + +void AddDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator<QObject *> it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->addDynamicProperty(m_propertyName, m_value); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QListIterator<QObject *> it(m_selection); + while (it.hasNext()) { + QObject *obj = it.next(); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + } +} + +void AddDynamicPropertyCommand::setDescription() +{ + if (m_selection.size() == 1) { + setText(QApplication::translate("Command", "Add dynamic property '%1' to '%2'").arg(m_propertyName).arg(m_selection.first()->objectName())); + } else { + int count = m_selection.size(); + setText(QApplication::translate("Command", "Add dynamic property '%1' to %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +RemoveDynamicPropertyCommand::RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow) + : QDesignerFormWindowCommand(QString(), formWindow) +{ + +} + +bool RemoveDynamicPropertyCommand::init(const QList<QObject *> &selection, QObject *current, + const QString &propertyName) +{ + Q_ASSERT(current); + m_propertyName = propertyName; + + QDesignerFormEditorInterface *core = formWindow()->core(); + QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(propertySheet); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), current); + Q_ASSERT(dynamicSheet); + + m_objectToValueAndChanged.clear(); + + const int index = propertySheet->indexOf(m_propertyName); + if (!dynamicSheet->isDynamicProperty(index)) + return false; + + m_objectToValueAndChanged[current] = qMakePair(propertySheet->property(index), propertySheet->isChanged(index)); + + QListIterator<QObject *> it(selection); + while (it.hasNext()) { + QObject *obj = it.next(); + if (m_objectToValueAndChanged.contains(obj)) + continue; + + propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + const int idx = propertySheet->indexOf(m_propertyName); + if (dynamicSheet->isDynamicProperty(idx)) + m_objectToValueAndChanged[obj] = qMakePair(propertySheet->property(idx), propertySheet->isChanged(idx)); + } + + setDescription(); + return true; +} + +void RemoveDynamicPropertyCommand::redo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + dynamicSheet->removeDynamicProperty(sheet->indexOf(m_propertyName)); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::undo() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + QMap<QObject *, QPair<QVariant, bool> >::ConstIterator it = m_objectToValueAndChanged.constBegin(); + while (it != m_objectToValueAndChanged.constEnd()) { + QObject *obj = it.key(); + QDesignerPropertySheetExtension *propertySheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), obj); + QDesignerDynamicPropertySheetExtension *dynamicSheet = qt_extension<QDesignerDynamicPropertySheetExtension*>(core->extensionManager(), obj); + const int index = dynamicSheet->addDynamicProperty(m_propertyName, it.value().first); + propertySheet->setChanged(index, it.value().second); + if (QDesignerPropertyEditorInterface *propertyEditor = formWindow()->core()->propertyEditor()) { + if (propertyEditor->object() == obj) + propertyEditor->setObject(obj); + } + ++it; + } +} + +void RemoveDynamicPropertyCommand::setDescription() +{ + if (m_objectToValueAndChanged.size() == 1) { + setText(QApplication::translate("Command", "Remove dynamic property '%1' from '%2'").arg(m_propertyName).arg(m_objectToValueAndChanged.constBegin().key()->objectName())); + } else { + int count = m_objectToValueAndChanged.size(); + setText(QApplication::translate("Command", "Remove dynamic property '%1' from %n objects", "", QCoreApplication::UnicodeUTF8, count).arg(m_propertyName)); + } +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_propertycommand_p.h b/tools/designer/src/lib/shared/qdesigner_propertycommand_p.h new file mode 100644 index 0000000..6b9722f --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertycommand_p.h @@ -0,0 +1,301 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_PROPERTYCOMMAND_H +#define QDESIGNER_PROPERTYCOMMAND_H + +#include "qdesigner_formwindowcommand_p.h" + +#include <QtCore/QVariant> +#include <QtCore/QList> +#include <QtCore/QPair> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerPropertySheetExtension; + +namespace qdesigner_internal { + +class QDesignerIntegration; + +enum SpecialProperty { + SP_None, SP_ObjectName, SP_LayoutName, SP_SpacerName,SP_WindowTitle, + SP_MinimumSize, SP_MaximumSize, SP_Geometry, SP_Icon, SP_CurrentTabName, SP_CurrentItemName, SP_CurrentPageName, + SP_AutoDefault, SP_Alignment, SP_Shortcut, SP_Orientation +}; + +//Determine special property +enum SpecialProperty getSpecialProperty(const QString& propertyName); + + +// A helper class for applying properties to objects. +// Can be used for Set commands (setValue(), restoreOldValue()) or +// Reset Commands restoreDefaultValue(), restoreOldValue()). +class PropertyHelper { +public: + // A pair of Value and changed flag + typedef QPair<QVariant, bool> Value; + + enum ObjectType {OT_Object, OT_FreeAction, OT_AssociatedAction, OT_Widget}; + + PropertyHelper(QObject* object, + SpecialProperty specialProperty, + QDesignerPropertySheetExtension *sheet, + int index); + + QObject *object() const { return m_object; } + SpecialProperty specialProperty() const { return m_specialProperty; } + // set a new value + Value setValue(QDesignerFormWindowInterface *fw, const QVariant &value, bool changed, unsigned subPropertyMask); + + // restore old value + Value restoreOldValue(QDesignerFormWindowInterface *fw); + // set default value + Value restoreDefaultValue(QDesignerFormWindowInterface *fw); + + inline QVariant oldValue() const + { return m_oldValue.first; } + + inline void setOldValue(const QVariant &oldValue) + { m_oldValue.first = oldValue; } + + // required updates for this property (bit mask) + enum UpdateMask { UpdatePropertyEditor=1, UpdateObjectInspector=2 }; + unsigned updateMask() const; + + // can be merged into one command (that is, object and name match) + bool canMerge(const PropertyHelper &other) const; + QDesignerIntegration *integration(QDesignerFormWindowInterface *fw) const; + + static void triggerActionChanged(QAction *a); + +private: + // Apply the value and update. Returns corrected value + Value applyValue(QDesignerFormWindowInterface *fw, const QVariant &oldValue, Value newValue); + + static void checkApplyWidgetValue(QDesignerFormWindowInterface *fw, QWidget* w, + SpecialProperty specialProperty, QVariant &v); + + void updateObject(QDesignerFormWindowInterface *fw, const QVariant &oldValue, const QVariant &newValue); + QVariant findDefaultValue(QDesignerFormWindowInterface *fw) const; + void ensureUniqueObjectName(QDesignerFormWindowInterface *fw, QObject *object) const; + SpecialProperty m_specialProperty; + + QPointer<QObject> m_object; + ObjectType m_objectType; + QPointer<QWidget> m_parentWidget; + + QDesignerPropertySheetExtension *m_propertySheet; + int m_index; + + Value m_oldValue; +}; + +// Base class for commands that can be applied to several widgets + +class QDESIGNER_SHARED_EXPORT PropertyListCommand : public QDesignerFormWindowCommand { +public: + typedef QList<QObject *> ObjectList; + + explicit PropertyListCommand(QDesignerFormWindowInterface *formWindow); + + QObject* object(int index = 0) const; + + QVariant oldValue(int index = 0) const; + + void setOldValue(const QVariant &oldValue, int index = 0); + + // Calls restoreDefaultValue() and update() + virtual void undo(); + +protected: + typedef QList<PropertyHelper> PropertyHelperList; + + + // add an object + bool add(QObject *object, const QString &propertyName); + + // Init from a list and make sure referenceObject is added first to obtain the right property group + bool initList(const ObjectList &list, const QString &apropertyName, QObject *referenceObject = 0); + + // set a new value, return update mask + unsigned setValue(QVariant value, bool changed, unsigned subPropertyMask); + + // restore old value, return update mask + unsigned restoreOldValue(); + // set default value, return update mask + unsigned restoreDefaultValue(); + + // update designer + void update(unsigned updateMask); + + // check if lists are aequivalent for command merging (same widgets and props) + bool canMergeLists(const PropertyHelperList& other) const; + + PropertyHelperList& propertyHelperList() { return m_propertyHelperList; } + const PropertyHelperList& propertyHelperList() const { return m_propertyHelperList; } + + const QString propertyName() const; + SpecialProperty specialProperty() const; + + // Helper struct describing a property used for checking whether + // properties of different widgets are equivalent + struct PropertyDescription { + public: + PropertyDescription(); + PropertyDescription(const QString &propertyName, QDesignerPropertySheetExtension *propertySheet, int index); + bool equals(const PropertyDescription &p) const; + void debug() const; + + QString m_propertyName; + QString m_propertyGroup; + QVariant::Type m_propertyType; + SpecialProperty m_specialProperty; + }; + const PropertyDescription &propertyDescription() const { return m_propertyDescription; } + +private: + PropertyDescription m_propertyDescription; + PropertyHelperList m_propertyHelperList; +}; + +class QDESIGNER_SHARED_EXPORT SetPropertyCommand: public PropertyListCommand +{ + +public: + typedef QList<QObject *> ObjectList; + + explicit SetPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(QObject *object, const QString &propertyName, const QVariant &newValue); + bool init(const ObjectList &list, const QString &propertyName, const QVariant &newValue, + QObject *referenceObject = 0, bool enableSubPropertyHandling = true); + + + inline QVariant newValue() const + { return m_newValue; } + + inline void setNewValue(const QVariant &newValue) + { m_newValue = newValue; } + + int id() const; + bool mergeWith(const QUndoCommand *other); + + virtual void redo(); +private: + unsigned subPropertyMask(const QVariant &newValue, QObject *referenceObject); + void setDescription(); + QVariant m_newValue; + unsigned m_subPropertyMask; +}; + +class QDESIGNER_SHARED_EXPORT ResetPropertyCommand: public PropertyListCommand +{ + +public: + typedef QList<QObject *> ObjectList; + + explicit ResetPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(QObject *object, const QString &propertyName); + bool init(const ObjectList &list, const QString &propertyName, QObject *referenceObject = 0); + + virtual void redo(); + +protected: + virtual bool mergeWith(const QUndoCommand *) { return false; } + +private: + void setDescription(); + QString m_propertyName; +}; + + +class QDESIGNER_SHARED_EXPORT AddDynamicPropertyCommand: public QDesignerFormWindowCommand +{ + +public: + explicit AddDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(const QList<QObject *> &selection, QObject *current, const QString &propertyName, const QVariant &value); + + virtual void redo(); + virtual void undo(); +private: + void setDescription(); + QString m_propertyName; + QList<QObject *> m_selection; + QVariant m_value; +}; + +class QDESIGNER_SHARED_EXPORT RemoveDynamicPropertyCommand: public QDesignerFormWindowCommand +{ + +public: + explicit RemoveDynamicPropertyCommand(QDesignerFormWindowInterface *formWindow); + + bool init(const QList<QObject *> &selection, QObject *current, const QString &propertyName); + + virtual void redo(); + virtual void undo(); +private: + void setDescription(); + QString m_propertyName; + QMap<QObject *, QPair<QVariant, bool> > m_objectToValueAndChanged; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_PROPERTYCOMMAND_H diff --git a/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp new file mode 100644 index 0000000..7c7d9d7 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertyeditor.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertyeditor_p.h" +#ifdef Q_OS_WIN +# include <widgetfactory_p.h> +#endif +#include <QAction> +#include <QLineEdit> +#include <QAbstractButton> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +QDesignerPropertyEditor::QDesignerPropertyEditor(QWidget *parent, Qt::WindowFlags flags) : + QDesignerPropertyEditorInterface(parent, flags) +{ + // Make old signal work for compatibility + connect(this, SIGNAL(propertyChanged(QString,QVariant)), this, SLOT(slotPropertyChanged(QString,QVariant))); +} + +QDesignerPropertyEditor::StringPropertyParameters QDesignerPropertyEditor::textPropertyValidationMode( + QDesignerFormEditorInterface *core, const QObject *object, + const QString &propertyName, bool isMainContainer) +{ + // object name - no comment + if (propertyName == QLatin1String("objectName")) { + const TextPropertyValidationMode vm = isMainContainer ? ValidationObjectNameScope : ValidationObjectName; + return StringPropertyParameters(vm, false); + } + + // Accessibility. Both are texts the narrator reads + if (propertyName == QLatin1String("accessibleDescription") || propertyName == QLatin1String("accessibleName")) + return StringPropertyParameters(ValidationRichText, true); + + // Names + if (propertyName == QLatin1String("buddy") + || propertyName == QLatin1String("currentItemName") + || propertyName == QLatin1String("currentPageName") + || propertyName == QLatin1String("currentTabName") + || propertyName == QLatin1String("layoutName") + || propertyName == QLatin1String("spacerName")) + return StringPropertyParameters(ValidationObjectName, false); + + if (propertyName.endsWith(QLatin1String("Name"))) + return StringPropertyParameters(ValidationSingleLine, true); + + // Multi line? + if (propertyName == QLatin1String("styleSheet")) + return StringPropertyParameters(ValidationStyleSheet, false); + + if (propertyName == QLatin1String("description") || propertyName == QLatin1String("iconText")) // QCommandLinkButton + return StringPropertyParameters(ValidationMultiLine, true); + + if (propertyName == QLatin1String("toolTip") || propertyName.endsWith(QLatin1String("ToolTip")) || + propertyName == QLatin1String("whatsThis") || + propertyName == QLatin1String("windowIconText") || propertyName == QLatin1String("html")) + return StringPropertyParameters(ValidationRichText, true); + + // text: Check according to widget type. + if (propertyName == QLatin1String("text")) { + if (qobject_cast<const QAction *>(object) || qobject_cast<const QLineEdit *>(object)) + return StringPropertyParameters(ValidationSingleLine, true); + if (qobject_cast<const QAbstractButton *>(object)) + return StringPropertyParameters(ValidationMultiLine, true); + return StringPropertyParameters(ValidationRichText, true); + } + if (propertyName == QLatin1String("pageId")) // A QWizard page id + return StringPropertyParameters(ValidationSingleLine, false); + + if (propertyName == QLatin1String("plainText")) // QPlainTextEdit + return StringPropertyParameters(ValidationMultiLine, true); + +#ifdef Q_OS_WIN // No translation for the active X "control" property + if (propertyName == QLatin1String("control") && WidgetFactory::classNameOf(core, object) == QLatin1String("QAxWidget")) + return StringPropertyParameters(ValidationSingleLine, false); +#else + Q_UNUSED(core); +#endif + + // default to single + return StringPropertyParameters(ValidationSingleLine, true); +} + +void QDesignerPropertyEditor::slotPropertyChanged(const QString &name, const QVariant &value) +{ + emit propertyValueChanged(name, value, true); +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_propertyeditor_p.h b/tools/designer/src/lib/shared/qdesigner_propertyeditor_p.h new file mode 100644 index 0000000..a979fde --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertyeditor_p.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef DESIGNERPROPERTYEDITOR_H +#define DESIGNERPROPERTYEDITOR_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" +#include <QtDesigner/QDesignerPropertyEditorInterface> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +// Extends the QDesignerPropertyEditorInterface by property comment handling and +// a signal for resetproperty. + +class QDESIGNER_SHARED_EXPORT QDesignerPropertyEditor: public QDesignerPropertyEditorInterface +{ + Q_OBJECT +public: + QDesignerPropertyEditor(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + // A pair <ValidationMode, bool isTranslatable>. + typedef QPair<TextPropertyValidationMode, bool> StringPropertyParameters; + + // Return a pair of validation mode and flag indicating whether property is translatable + // for textual properties. + static StringPropertyParameters textPropertyValidationMode(QDesignerFormEditorInterface *core, + const QObject *object, const QString &propertyName, bool isMainContainer); + + +Q_SIGNALS: + void propertyValueChanged(const QString &name, const QVariant &value, bool enableSubPropertyHandling); + void resetProperty(const QString &name); + void addDynamicProperty(const QString &name, const QVariant &value); + void removeDynamicProperty(const QString &name); + void editorOpened(); + void editorClosed(); + +public Q_SLOTS: + /* Quick update that assumes the actual count of properties has not changed + * (as opposed to setObject()). N/A when for example executing a + * layout command and margin properties appear. */ + virtual void updatePropertySheet() = 0; + virtual void reloadResourceProperties() = 0; + +private Q_SLOTS: + void slotPropertyChanged(const QString &name, const QVariant &value); +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // DESIGNERPROPERTYEDITOR_H diff --git a/tools/designer/src/lib/shared/qdesigner_propertysheet.cpp b/tools/designer/src/lib/shared/qdesigner_propertysheet.cpp new file mode 100644 index 0000000..7abbde7 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertysheet.cpp @@ -0,0 +1,1601 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" +#include "formwindowbase_p.h" +#include "layoutinfo_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_introspection_p.h" + +#include <formbuilderextra_p.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtCore/QDebug> + +#include <QtGui/QLayout> +#include <QtGui/QDockWidget> +#include <QtGui/QDialog> +#include <QtGui/QLabel> +#include <QtGui/QGroupBox> +#include <QtGui/QStyle> +#include <QtGui/QApplication> +#include <QtGui/QToolBar> +#include <QtGui/QMainWindow> + +QT_BEGIN_NAMESPACE + +#define USE_LAYOUT_SIZE_CONSTRAINT + +static const QDesignerMetaObjectInterface *propertyIntroducedBy(const QDesignerMetaObjectInterface *meta, int index) +{ + if (index >= meta->propertyOffset()) + return meta; + + if (meta->superClass()) + return propertyIntroducedBy(meta->superClass(), index); + + return 0; +} + +// Layout fake properties (prefixed by 'layout' to distinguish them from other 'margins' +// that might be around. These are forwarded to the layout sheet (after name transformation). +// +// 'layoutObjectName' is new for 4.4. It is the name of the actual layout. +// Up to 4.3, QLayoutWidget's name was displayed in the objectinspector. +// This changes with 4.4; the layout name is displayed. This means that for +// old forms, QLayoutWidget will show up as ''; however, the uic code will +// still use 'verticalLayout' (in case someone accesses it). New Layouts get autogenerated names, +// legacy forms will keep their empty names (unless someone types in a new name). +static const char *layoutObjectNameC = "layoutName"; +static const char *layoutLeftMarginC = "layoutLeftMargin"; +static const char *layoutTopMarginC = "layoutTopMargin"; +static const char *layoutRightMarginC = "layoutRightMargin"; +static const char *layoutBottomMarginC = "layoutBottomMargin"; +static const char *layoutSpacingC = "layoutSpacing"; +static const char *layoutHorizontalSpacingC = "layoutHorizontalSpacing"; +static const char *layoutVerticalSpacingC = "layoutVerticalSpacing"; +static const char *layoutSizeConstraintC = "layoutSizeConstraint"; +// form layout +static const char *layoutFieldGrowthPolicyC = "layoutFieldGrowthPolicy"; +static const char *layoutRowWrapPolicyC = "layoutRowWrapPolicy"; +static const char *layoutLabelAlignmentC = "layoutLabelAlignment"; +static const char *layoutFormAlignmentC = "layoutFormAlignment"; +// stretches +static const char *layoutboxStretchPropertyC = "layoutStretch"; +static const char *layoutGridRowStretchPropertyC = "layoutRowStretch"; +static const char *layoutGridColumnStretchPropertyC = "layoutColumnStretch"; +static const char *layoutGridRowMinimumHeightC = "layoutRowMinimumHeight"; +static const char *layoutGridColumnMinimumWidthC = "layoutColumnMinimumWidth"; + +// Find the form editor in the hierarchy. +// We know that the parent of the sheet is the extension manager +// whose parent is the core. + +static QDesignerFormEditorInterface *formEditorForObject(QObject *o) { + do { + if (QDesignerFormEditorInterface* core = qobject_cast<QDesignerFormEditorInterface*>(o)) + return core; + o = o->parent(); + } while(o); + Q_ASSERT(o); + return 0; +} + +static bool hasLayoutAttributes(QDesignerFormEditorInterface *core, QObject *object) +{ + if (!object->isWidgetType()) + return false; + + QWidget *w = qobject_cast<QWidget *>(object); + if (const QDesignerWidgetDataBaseInterface *db = core->widgetDataBase()) { + if (db->isContainer(w)) + return true; + } + return false; +} + +// Cache DesignerMetaEnum by scope/name of a QMetaEnum +static const qdesigner_internal::DesignerMetaEnum &designerMetaEnumFor(const QDesignerMetaEnumInterface *me) +{ + typedef QPair<QString, QString> ScopeNameKey; + typedef QMap<ScopeNameKey, qdesigner_internal::DesignerMetaEnum> DesignerMetaEnumCache; + static DesignerMetaEnumCache cache; + + const QString name = me->name(); + const QString scope = me->scope(); + + const ScopeNameKey key = ScopeNameKey(scope, name); + DesignerMetaEnumCache::iterator it = cache.find(key); + if (it == cache.end()) { + qdesigner_internal::DesignerMetaEnum dme = qdesigner_internal::DesignerMetaEnum(name, scope, me->separator()); + const int keyCount = me->keyCount(); + for (int i=0; i < keyCount; ++i) + dme.addKey(me->value(i), me->key(i)); + it = cache.insert(key, dme); + } + return it.value(); +} + +// Cache DesignerMetaFlags by scope/name of a QMetaEnum +static const qdesigner_internal::DesignerMetaFlags &designerMetaFlagsFor(const QDesignerMetaEnumInterface *me) +{ + typedef QPair<QString, QString> ScopeNameKey; + typedef QMap<ScopeNameKey, qdesigner_internal::DesignerMetaFlags> DesignerMetaFlagsCache; + static DesignerMetaFlagsCache cache; + + const QString name = me->name(); + const QString scope = me->scope(); + + const ScopeNameKey key = ScopeNameKey(scope, name); + DesignerMetaFlagsCache::iterator it = cache.find(key); + if (it == cache.end()) { + qdesigner_internal::DesignerMetaFlags dme = qdesigner_internal::DesignerMetaFlags(name, scope, me->separator()); + const int keyCount = me->keyCount(); + for (int i=0; i < keyCount; ++i) + dme.addKey(me->value(i), me->key(i)); + it = cache.insert(key, dme); + } + return it.value(); +} + +// ------------ QDesignerMemberSheetPrivate +class QDesignerPropertySheetPrivate { +public: + typedef QDesignerPropertySheet::PropertyType PropertyType; + typedef QDesignerPropertySheet::ObjectType ObjectType; + + explicit QDesignerPropertySheetPrivate(QDesignerPropertySheet *sheetPublic, QObject *object, QObject *sheetParent); + + bool invalidIndex(const char *functionName, int index) const; + inline int count() const { return m_meta->propertyCount() + m_addProperties.count(); } + + PropertyType propertyType(int index) const; + QString transformLayoutPropertyName(int index) const; + QLayout* layout(QDesignerPropertySheetExtension **layoutPropertySheet = 0) const; + static ObjectType objectType(const QObject *o); + + bool isReloadableProperty(int index) const; + bool isResourceProperty(int index) const; + void addResourceProperty(int index, QVariant::Type type); + QVariant resourceProperty(int index) const; + void setResourceProperty(int index, const QVariant &value); + QVariant emptyResourceProperty(int index) const; // of type PropertySheetPixmapValue / PropertySheetIconValue + QVariant defaultResourceProperty(int index) const; // of type QPixmap / QIcon (maybe it can be generalized for all types, not resource only) + + bool isStringProperty(int index) const; + void addStringProperty(int index); + qdesigner_internal::PropertySheetStringValue stringProperty(int index) const; + void setStringProperty(int index, const qdesigner_internal::PropertySheetStringValue &value); + + bool isKeySequenceProperty(int index) const; + void addKeySequenceProperty(int index); + qdesigner_internal::PropertySheetKeySequenceValue keySequenceProperty(int index) const; + void setKeySequenceProperty(int index, const qdesigner_internal::PropertySheetKeySequenceValue &value); + + enum PropertyKind { NormalProperty, FakeProperty, DynamicProperty, DefaultDynamicProperty }; + class Info { + public: + Info(); + + QString group; + QVariant defaultValue; + bool changed; + bool visible; + bool attribute; + bool reset; + PropertyType propertyType; + PropertyKind kind; + }; + + Info &ensureInfo(int index); + + QDesignerPropertySheet *q; + QDesignerFormEditorInterface *m_core; + const QDesignerMetaObjectInterface *m_meta; + const ObjectType m_objectType; + + typedef QHash<int, Info> InfoHash; + InfoHash m_info; + QHash<int, QVariant> m_fakeProperties; + QHash<int, QVariant> m_addProperties; + QHash<QString, int> m_addIndex; + QHash<int, QVariant> m_resourceProperties; // only PropertySheetPixmapValue snd PropertySheetIconValue here + QHash<int, qdesigner_internal::PropertySheetStringValue> m_stringProperties; // only PropertySheetStringValue + QHash<int, qdesigner_internal::PropertySheetKeySequenceValue> m_keySequenceProperties; // only PropertySheetKeySequenceValue + + const bool m_canHaveLayoutAttributes; + + // Variables used for caching the layout, access via layout(). + QPointer<QObject> m_object; + mutable QPointer<QLayout> m_lastLayout; + mutable QDesignerPropertySheetExtension *m_lastLayoutPropertySheet; + mutable bool m_LastLayoutByDesigner; + + qdesigner_internal::DesignerPixmapCache *m_pixmapCache; + qdesigner_internal::DesignerIconCache *m_iconCache; + QPointer<qdesigner_internal::FormWindowBase> m_fwb; + + // Enable Qt's internal properties starting with prefix "_q_" + static bool m_internalDynamicPropertiesEnabled; +}; + +bool QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled = false; + +/* + The property is reloadable if its contents depends on resource. +*/ +bool QDesignerPropertySheetPrivate::isReloadableProperty(int index) const +{ + return isResourceProperty(index) + || propertyType(index) == QDesignerPropertySheet::PropertyStyleSheet + || q->property(index).type() == QVariant::Url; +} + +/* + Resource properties are those which: + 1) are reloadable + 2) their state is associated with a file which can be taken from resources + 3) we don't store them in Qt meta object system (because designer keeps different data structure for them) +*/ + +bool QDesignerPropertySheetPrivate::isResourceProperty(int index) const +{ + return m_resourceProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addResourceProperty(int index, QVariant::Type type) +{ + if (type == QVariant::Pixmap) + m_resourceProperties.insert(index, qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue())); + else if (type == QVariant::Icon) + m_resourceProperties.insert(index, qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); +} + +QVariant QDesignerPropertySheetPrivate::emptyResourceProperty(int index) const +{ + QVariant v = m_resourceProperties.value(index); + if (qVariantCanConvert<qdesigner_internal::PropertySheetPixmapValue>(v)) + return qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue()); + if (qVariantCanConvert<qdesigner_internal::PropertySheetIconValue>(v)) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + return v; +} + +QVariant QDesignerPropertySheetPrivate::defaultResourceProperty(int index) const +{ + return m_info.value(index).defaultValue; +} + +QVariant QDesignerPropertySheetPrivate::resourceProperty(int index) const +{ + return m_resourceProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setResourceProperty(int index, const QVariant &value) +{ + Q_ASSERT(isResourceProperty(index)); + + QVariant &v = m_resourceProperties[index]; + if ((qVariantCanConvert<qdesigner_internal::PropertySheetPixmapValue>(value) && qVariantCanConvert<qdesigner_internal::PropertySheetPixmapValue>(v)) + || (qVariantCanConvert<qdesigner_internal::PropertySheetIconValue>(value) && qVariantCanConvert<qdesigner_internal::PropertySheetIconValue>(v))) + v = value; +} + +bool QDesignerPropertySheetPrivate::isStringProperty(int index) const +{ + return m_stringProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addStringProperty(int index) +{ + m_stringProperties.insert(index, qdesigner_internal::PropertySheetStringValue()); +} + +qdesigner_internal::PropertySheetStringValue QDesignerPropertySheetPrivate::stringProperty(int index) const +{ + return m_stringProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setStringProperty(int index, const qdesigner_internal::PropertySheetStringValue &value) +{ + Q_ASSERT(isStringProperty(index)); + + m_stringProperties[index] = value; +} + +bool QDesignerPropertySheetPrivate::isKeySequenceProperty(int index) const +{ + return m_keySequenceProperties.contains(index); +} + +void QDesignerPropertySheetPrivate::addKeySequenceProperty(int index) +{ + m_keySequenceProperties.insert(index, qdesigner_internal::PropertySheetKeySequenceValue()); +} + +qdesigner_internal::PropertySheetKeySequenceValue QDesignerPropertySheetPrivate::keySequenceProperty(int index) const +{ + return m_keySequenceProperties.value(index); +} + +void QDesignerPropertySheetPrivate::setKeySequenceProperty(int index, const qdesigner_internal::PropertySheetKeySequenceValue &value) +{ + Q_ASSERT(isKeySequenceProperty(index)); + + m_keySequenceProperties[index] = value; +} + +QDesignerPropertySheetPrivate::Info::Info() : + changed(false), + visible(true), + attribute(false), + reset(true), + propertyType(QDesignerPropertySheet::PropertyNone), + kind(NormalProperty) +{ +} + +QDesignerPropertySheetPrivate::QDesignerPropertySheetPrivate(QDesignerPropertySheet *sheetPublic, QObject *object, QObject *sheetParent) : + q(sheetPublic), + m_core(formEditorForObject(sheetParent)), + m_meta(m_core->introspection()->metaObject(object)), + m_objectType(QDesignerPropertySheet::objectTypeFromObject(object)), + m_canHaveLayoutAttributes(hasLayoutAttributes(m_core, object)), + m_object(object), + m_lastLayout(0), + m_lastLayoutPropertySheet(0), + m_LastLayoutByDesigner(false), + m_pixmapCache(0), + m_iconCache(0) +{ +} + +qdesigner_internal::FormWindowBase *QDesignerPropertySheet::formWindowBase() const +{ + return d->m_fwb; +} + +bool QDesignerPropertySheetPrivate::invalidIndex(const char *functionName, int index) const +{ + if (index < 0 || index >= count()) { + qWarning() << "** WARNING " << functionName << " invoked for " << m_object->objectName() << " was passed an invalid index " << index << '.'; + return true; + } + return false; +} + +QLayout* QDesignerPropertySheetPrivate::layout(QDesignerPropertySheetExtension **layoutPropertySheet) const +{ + // Return the layout and its property sheet + // only if it is managed by designer and not one created on a custom widget. + // (attempt to cache the value as this requires some hoops). + if (layoutPropertySheet) + *layoutPropertySheet = 0; + + if (!m_object->isWidgetType() || !m_canHaveLayoutAttributes) + return 0; + + QWidget *widget = qobject_cast<QWidget*>(m_object); + QLayout *widgetLayout = qdesigner_internal::LayoutInfo::internalLayout(widget); + if (!widgetLayout) { + m_lastLayout = 0; + m_lastLayoutPropertySheet = 0; + return 0; + } + // Smart logic to avoid retrieving the meta DB from the widget every time. + if (widgetLayout != m_lastLayout) { + m_lastLayout = widgetLayout; + m_LastLayoutByDesigner = false; + m_lastLayoutPropertySheet = 0; + // Is this a layout managed by designer or some layout on a custom widget? + if (qdesigner_internal::LayoutInfo::managedLayout(m_core ,widgetLayout)) { + m_LastLayoutByDesigner = true; + m_lastLayoutPropertySheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), m_lastLayout); + } + } + if (!m_LastLayoutByDesigner) + return 0; + + if (layoutPropertySheet) + *layoutPropertySheet = m_lastLayoutPropertySheet; + + return m_lastLayout; +} + +QDesignerPropertySheetPrivate::Info &QDesignerPropertySheetPrivate::ensureInfo(int index) +{ + InfoHash::iterator it = m_info.find(index); + if (it == m_info.end()) + it = m_info.insert(index, Info()); + return it.value(); +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheetPrivate::propertyType(int index) const +{ + const InfoHash::const_iterator it = m_info.constFind(index); + if (it == m_info.constEnd()) + return QDesignerPropertySheet::PropertyNone; + return it.value().propertyType; +} + +QString QDesignerPropertySheetPrivate::transformLayoutPropertyName(int index) const +{ + typedef QMap<QDesignerPropertySheet::PropertyType, QString> TypeNameMap; + static TypeNameMap typeNameMap; + if (typeNameMap.empty()) { + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutObjectName, QLatin1String("objectName")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutLeftMargin, QLatin1String("leftMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutTopMargin, QLatin1String("topMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutRightMargin, QLatin1String("rightMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutBottomMargin, QLatin1String("bottomMargin")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutSpacing, QLatin1String("spacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutHorizontalSpacing, QLatin1String("horizontalSpacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutVerticalSpacing, QLatin1String("verticalSpacing")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutSizeConstraint, QLatin1String("sizeConstraint")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutFieldGrowthPolicy, QLatin1String("fieldGrowthPolicy")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutRowWrapPolicy, QLatin1String("rowWrapPolicy")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutLabelAlignment, QLatin1String("labelAlignment")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutFormAlignment, QLatin1String("formAlignment")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutBoxStretch, QLatin1String("stretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridRowStretch, QLatin1String("rowStretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridColumnStretch, QLatin1String("columnStretch")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridRowMinimumHeight, QLatin1String("rowMinimumHeight")); + typeNameMap.insert(QDesignerPropertySheet::PropertyLayoutGridColumnMinimumWidth, QLatin1String("columnMinimumWidth")); + } + const TypeNameMap::const_iterator it = typeNameMap.constFind(propertyType(index)); + if (it != typeNameMap.constEnd()) + return it.value(); + return QString(); +} + +// ----------- QDesignerPropertySheet + +QDesignerPropertySheet::ObjectType QDesignerPropertySheet::objectTypeFromObject(const QObject *o) +{ + if (qobject_cast<const QLayout *>(o)) + return ObjectLayout; + + if (!o->isWidgetType()) + return ObjectNone; + + if (qobject_cast<const QLayoutWidget *>(o)) + return ObjectLayoutWidget; + + if (qobject_cast<const QLabel*>(o)) + return ObjectLabel; + + if (o->inherits("Q3GroupBox")) + return ObjectQ3GroupBox; + + return ObjectNone; +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheet::propertyTypeFromName(const QString &name) +{ + typedef QHash<QString, PropertyType> PropertyTypeHash; + static PropertyTypeHash propertyTypeHash; + if (propertyTypeHash.empty()) { + propertyTypeHash.insert(QLatin1String(layoutObjectNameC), PropertyLayoutObjectName); + propertyTypeHash.insert(QLatin1String(layoutLeftMarginC), PropertyLayoutLeftMargin); + propertyTypeHash.insert(QLatin1String(layoutTopMarginC), PropertyLayoutTopMargin); + propertyTypeHash.insert(QLatin1String(layoutRightMarginC), PropertyLayoutRightMargin); + propertyTypeHash.insert(QLatin1String(layoutBottomMarginC), PropertyLayoutBottomMargin); + propertyTypeHash.insert(QLatin1String(layoutSpacingC), PropertyLayoutSpacing); + propertyTypeHash.insert(QLatin1String(layoutHorizontalSpacingC), PropertyLayoutHorizontalSpacing); + propertyTypeHash.insert(QLatin1String(layoutVerticalSpacingC), PropertyLayoutVerticalSpacing); + propertyTypeHash.insert(QLatin1String(layoutSizeConstraintC), PropertyLayoutSizeConstraint); + propertyTypeHash.insert(QLatin1String(layoutFieldGrowthPolicyC), PropertyLayoutFieldGrowthPolicy); + propertyTypeHash.insert(QLatin1String(layoutRowWrapPolicyC), PropertyLayoutRowWrapPolicy); + propertyTypeHash.insert(QLatin1String(layoutLabelAlignmentC), PropertyLayoutLabelAlignment); + propertyTypeHash.insert(QLatin1String(layoutFormAlignmentC), PropertyLayoutFormAlignment); + propertyTypeHash.insert(QLatin1String(layoutboxStretchPropertyC), PropertyLayoutBoxStretch); + propertyTypeHash.insert(QLatin1String(layoutGridRowStretchPropertyC), PropertyLayoutGridRowStretch); + propertyTypeHash.insert(QLatin1String(layoutGridColumnStretchPropertyC), PropertyLayoutGridColumnStretch); + propertyTypeHash.insert(QLatin1String(layoutGridRowMinimumHeightC), PropertyLayoutGridRowMinimumHeight); + propertyTypeHash.insert(QLatin1String(layoutGridColumnMinimumWidthC), PropertyLayoutGridColumnMinimumWidth); + propertyTypeHash.insert(QLatin1String("buddy"), PropertyBuddy); + propertyTypeHash.insert(QLatin1String("geometry"), PropertyGeometry); + propertyTypeHash.insert(QLatin1String("checkable"), PropertyCheckable); + propertyTypeHash.insert(QLatin1String("accessibleName"), PropertyAccessibility); + propertyTypeHash.insert(QLatin1String("accessibleDescription"), PropertyAccessibility); + propertyTypeHash.insert(QLatin1String("windowTitle"), PropertyWindowTitle); + propertyTypeHash.insert(QLatin1String("windowIcon"), PropertyWindowIcon); + propertyTypeHash.insert(QLatin1String("windowFilePath"), PropertyWindowFilePath); + propertyTypeHash.insert(QLatin1String("windowOpacity"), PropertyWindowOpacity); + propertyTypeHash.insert(QLatin1String("windowIconText"), PropertyWindowIconText); + propertyTypeHash.insert(QLatin1String("windowModality"), PropertyWindowModality); + propertyTypeHash.insert(QLatin1String("windowModified"), PropertyWindowModified); + propertyTypeHash.insert(QLatin1String("styleSheet"), PropertyStyleSheet); + } + return propertyTypeHash.value(name, PropertyNone); +} + +QDesignerPropertySheet::QDesignerPropertySheet(QObject *object, QObject *parent) : + QObject(parent), + d(new QDesignerPropertySheetPrivate(this, object, parent)) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + const QDesignerMetaObjectInterface *baseMeta = d->m_meta; + + while (baseMeta &&baseMeta->className().startsWith(QLatin1String("QDesigner"))) { + baseMeta = baseMeta->superClass(); + } + Q_ASSERT(baseMeta != 0); + + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(d->m_object); + d->m_fwb = qobject_cast<qdesigner_internal::FormWindowBase *>(formWindow); + if (d->m_fwb) { + d->m_pixmapCache = d->m_fwb->pixmapCache(); + d->m_iconCache = d->m_fwb->iconCache(); + d->m_fwb->addReloadablePropertySheet(this, object); + } + + for (int index=0; index<count(); ++index) { + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + const QString name = p->name(); + if (p->type() == QVariant::KeySequence) { + createFakeProperty(name); + } else { + setVisible(index, false); // use the default for `real' properties + } + + QString pgroup = baseMeta->className(); + + if (const QDesignerMetaObjectInterface *pmeta = propertyIntroducedBy(baseMeta, index)) { + pgroup = pmeta->className(); + } + + Info &info = d->ensureInfo(index); + info.group = pgroup; + info.propertyType = propertyTypeFromName(name); + + if (p->type() == QVariant::Cursor || p->type() == QVariant::Icon || p->type() == QVariant::Pixmap) { + info.defaultValue = p->read(d->m_object); + if (p->type() == QVariant::Icon || p->type() == QVariant::Pixmap) + d->addResourceProperty(index, p->type()); + } else if (p->type() == QVariant::String) { + d->addStringProperty(index); + } else if (p->type() == QVariant::KeySequence) { + d->addKeySequenceProperty(index); + } + } + + if (object->isWidgetType()) { + createFakeProperty(QLatin1String("focusPolicy")); + createFakeProperty(QLatin1String("cursor")); + createFakeProperty(QLatin1String("toolTip")); + createFakeProperty(QLatin1String("whatsThis")); + createFakeProperty(QLatin1String("acceptDrops")); + createFakeProperty(QLatin1String("dragEnabled")); + // windowModality is visible only for the main container, in which case the form windows enables it on loading + setVisible(createFakeProperty(QLatin1String("windowModality")), false); + if (qobject_cast<const QToolBar *>(d->m_object)) // prevent toolbars from being dragged off + createFakeProperty(QLatin1String("floatable"), QVariant(true)); + + if (d->m_canHaveLayoutAttributes) { + static const QString layoutGroup = QLatin1String("Layout"); + const char* fakeLayoutProperties[] = { + layoutObjectNameC, layoutLeftMarginC, layoutTopMarginC, layoutRightMarginC, layoutBottomMarginC, layoutSpacingC, layoutHorizontalSpacingC, layoutVerticalSpacingC, + layoutFieldGrowthPolicyC, layoutRowWrapPolicyC, layoutLabelAlignmentC, layoutFormAlignmentC, + layoutboxStretchPropertyC, layoutGridRowStretchPropertyC, layoutGridColumnStretchPropertyC, + layoutGridRowMinimumHeightC, layoutGridColumnMinimumWidthC +#ifdef USE_LAYOUT_SIZE_CONSTRAINT + , layoutSizeConstraintC +#endif + }; + const int fakeLayoutPropertyCount = sizeof(fakeLayoutProperties)/sizeof(const char*); + const int size = count(); + for (int i = 0; i < fakeLayoutPropertyCount; i++) { + createFakeProperty(QLatin1String(fakeLayoutProperties[i]), 0); + setAttribute(size + i, true); + setPropertyGroup(size + i, layoutGroup); + } + } + + if (d->m_objectType == ObjectLabel) + createFakeProperty(QLatin1String("buddy"), QVariant(QByteArray())); + /* We need to create a fake property since the property does not work + * for non-toplevel windows or on other systems than Mac and only if + * it is above a certain Mac OS version. */ + if (qobject_cast<const QMainWindow *>(d->m_object)) + createFakeProperty(QLatin1String("unifiedTitleAndToolBarOnMac"), false); + } + + if (qobject_cast<const QDialog*>(object)) { + createFakeProperty(QLatin1String("modal")); + } + if (qobject_cast<const QDockWidget*>(object)) { + createFakeProperty(QLatin1String("floating")); + } + + typedef QList<QByteArray> ByteArrayList; + const ByteArrayList names = object->dynamicPropertyNames(); + if (!names.empty()) { + const ByteArrayList::const_iterator cend = names.constEnd(); + for (ByteArrayList::const_iterator it = names.constBegin(); it != cend; ++it) { + const char* cName = it->constData(); + const QString name = QString::fromLatin1(cName); + const int idx = addDynamicProperty(name, object->property(cName)); + if (idx != -1) + d->ensureInfo(idx).kind = QDesignerPropertySheetPrivate::DefaultDynamicProperty; + } + } +} + +QDesignerPropertySheet::~QDesignerPropertySheet() +{ + if (d->m_fwb) + d->m_fwb->removeReloadablePropertySheet(this); + delete d; +} + +QObject *QDesignerPropertySheet::object() const +{ + return d->m_object; +} + +bool QDesignerPropertySheet::dynamicPropertiesAllowed() const +{ + return true; +} + +bool QDesignerPropertySheet::canAddDynamicProperty(const QString &propName) const +{ + const int index = d->m_meta->indexOfProperty(propName); + if (index != -1) + return false; // property already exists and is not a dynamic one + if (d->m_addIndex.contains(propName)) { + const int idx = d->m_addIndex.value(propName); + if (isVisible(idx)) + return false; // dynamic property already exists + else + return true; + } + if (!QDesignerPropertySheet::internalDynamicPropertiesEnabled() && propName.startsWith(QLatin1String("_q_"))) + return false; + return true; +} + +int QDesignerPropertySheet::addDynamicProperty(const QString &propName, const QVariant &value) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + if (!value.isValid()) + return -1; // property has invalid type + if (!canAddDynamicProperty(propName)) + return -1; + + QVariant v = value; + if (value.type() == QVariant::Icon) + v = qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + else if (value.type() == QVariant::Pixmap) + v = qVariantFromValue(qdesigner_internal::PropertySheetPixmapValue()); + else if (value.type() == QVariant::String) + v = qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + else if (value.type() == QVariant::KeySequence) + v = qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue()); + + + if (d->m_addIndex.contains(propName)) { + const int idx = d->m_addIndex.value(propName); + // have to be invisible, this was checked in canAddDynamicProperty() method + setVisible(idx, true); + d->m_addProperties.insert(idx, v); + setChanged(idx, false); + const int index = d->m_meta->indexOfProperty(propName); + Info &info = d->ensureInfo(index); + info.defaultValue = value; + info.kind = QDesignerPropertySheetPrivate::DynamicProperty; + if (value.type() == QVariant::Icon || value.type() == QVariant::Pixmap) + d->addResourceProperty(idx, value.type()); + else if (value.type() == QVariant::String) + d->addStringProperty(idx); + else if (value.type() == QVariant::KeySequence) + d->addKeySequenceProperty(idx); + return idx; + } + + const int index = count(); + d->m_addIndex.insert(propName, index); + d->m_addProperties.insert(index, v); + Info &info = d->ensureInfo(index); + info.visible = true; + info.changed = false; + info.defaultValue = value; + info.kind = QDesignerPropertySheetPrivate::DynamicProperty; + setPropertyGroup(index, tr("Dynamic Properties")); + if (value.type() == QVariant::Icon || value.type() == QVariant::Pixmap) + d->addResourceProperty(index, value.type()); + else if (value.type() == QVariant::String) + d->addStringProperty(index); + else if (value.type() == QVariant::KeySequence) + d->addKeySequenceProperty(index); + return index; +} + +bool QDesignerPropertySheet::removeDynamicProperty(int index) +{ + if (!d->m_addIndex.contains(propertyName(index))) + return false; + + setVisible(index, false); + return true; +} + +bool QDesignerPropertySheet::isDynamic(int index) const +{ + if (!d->m_addProperties.contains(index)) + return false; + + switch (propertyType(index)) { + case PropertyBuddy: + if (d->m_objectType == ObjectLabel) + return false; + break; + case PropertyLayoutLeftMargin: + case PropertyLayoutTopMargin: + case PropertyLayoutRightMargin: + case PropertyLayoutBottomMargin: + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + case PropertyLayoutObjectName: + case PropertyLayoutSizeConstraint: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + if (d->m_object->isWidgetType() && d->m_canHaveLayoutAttributes) + return false; + default: + break; + } + return true; +} + +bool QDesignerPropertySheet::isDynamicProperty(int index) const +{ + // Do not complain here, as an invalid index might be encountered + // if someone implements a property sheet only, omitting the dynamic sheet. + if (index < 0 || index >= count()) + return false; + return d->m_info.value(index).kind == QDesignerPropertySheetPrivate::DynamicProperty; +} + +bool QDesignerPropertySheet::isDefaultDynamicProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + return d->m_info.value(index).kind == QDesignerPropertySheetPrivate::DefaultDynamicProperty; +} + +bool QDesignerPropertySheet::isResourceProperty(int index) const +{ + return d->isResourceProperty(index); +} + +QVariant QDesignerPropertySheet::defaultResourceProperty(int index) const +{ + return d->defaultResourceProperty(index); +} + +qdesigner_internal::DesignerPixmapCache *QDesignerPropertySheet::pixmapCache() const +{ + return d->m_pixmapCache; +} + +void QDesignerPropertySheet::setPixmapCache(qdesigner_internal::DesignerPixmapCache *cache) +{ + d->m_pixmapCache = cache; +} + +qdesigner_internal::DesignerIconCache *QDesignerPropertySheet::iconCache() const +{ + return d->m_iconCache; +} + +void QDesignerPropertySheet::setIconCache(qdesigner_internal::DesignerIconCache *cache) +{ + d->m_iconCache = cache; +} + +int QDesignerPropertySheet::createFakeProperty(const QString &propertyName, const QVariant &value) +{ + typedef QDesignerPropertySheetPrivate::Info Info; + // fake properties + const int index = d->m_meta->indexOfProperty(propertyName); + if (index != -1) { + if (!(d->m_meta->property(index)->attributes() & QDesignerMetaPropertyInterface::DesignableAttribute)) + return -1; + Info &info = d->ensureInfo(index); + info.visible = false; + info.kind = QDesignerPropertySheetPrivate::FakeProperty; + QVariant v = value.isValid() ? value : metaProperty(index); + if (v.type() == QVariant::String) + v = qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (v.type() == QVariant::KeySequence) + v = qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue()); + d->m_fakeProperties.insert(index, v); + return index; + } + if (!value.isValid()) + return -1; + + const int newIndex = count(); + d->m_addIndex.insert(propertyName, newIndex); + d->m_addProperties.insert(newIndex, value); + Info &info = d->ensureInfo(newIndex); + info.propertyType = propertyTypeFromName(propertyName); + info.kind = QDesignerPropertySheetPrivate::FakeProperty; + return newIndex; +} + +bool QDesignerPropertySheet::isAdditionalProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + return d->m_addProperties.contains(index); +} + +bool QDesignerPropertySheet::isFakeProperty(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + // additional properties must be fake + return (d->m_fakeProperties.contains(index) || isAdditionalProperty(index)); +} + +int QDesignerPropertySheet::count() const +{ + return d->count(); +} + +int QDesignerPropertySheet::indexOf(const QString &name) const +{ + int index = d->m_meta->indexOfProperty(name); + + if (index == -1) + index = d->m_addIndex.value(name, -1); + + return index; +} + +QDesignerPropertySheet::PropertyType QDesignerPropertySheet::propertyType(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return PropertyNone; + return d->propertyType(index); +} + +QDesignerPropertySheet::ObjectType QDesignerPropertySheet::objectType() const +{ + return d->m_objectType; +} + +QString QDesignerPropertySheet::propertyName(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QString(); + if (isAdditionalProperty(index)) + return d->m_addIndex.key(index); + + return d->m_meta->property(index)->name(); +} + +QString QDesignerPropertySheet::propertyGroup(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QString(); + const QString g = d->m_info.value(index).group; + + if (!g.isEmpty()) + return g; + + if (propertyType(index) == PropertyAccessibility) + return QString::fromUtf8("Accessibility"); + + if (isAdditionalProperty(index)) + return d->m_meta->className(); + + return g; +} + +void QDesignerPropertySheet::setPropertyGroup(int index, const QString &group) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).group = group; +} + +QVariant QDesignerPropertySheet::property(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return QVariant(); + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + return layoutPropertySheet->property(newIndex); + return QVariant(); + } + } + } + return d->m_addProperties.value(index); + } + + if (isFakeProperty(index)) { + return d->m_fakeProperties.value(index); + } + + if (d->isResourceProperty(index)) + return d->resourceProperty(index); + + if (d->isStringProperty(index)) { + QString strValue = metaProperty(index).toString(); + qdesigner_internal::PropertySheetStringValue value = d->stringProperty(index); + if (strValue != value.value()) { + value.setValue(strValue); + d->setStringProperty(index, value); // cache it + } + return qVariantFromValue(value); + } + + if (d->isKeySequenceProperty(index)) { + QKeySequence keyValue = qVariantValue<QKeySequence>(metaProperty(index)); + qdesigner_internal::PropertySheetKeySequenceValue value = d->keySequenceProperty(index); + if (keyValue != value.value()) { + value.setValue(keyValue); + d->setKeySequenceProperty(index, value); // cache it + } + return qVariantFromValue(value); + } + + return metaProperty(index); +} + +QVariant QDesignerPropertySheet::metaProperty(int index) const +{ + Q_ASSERT(!isFakeProperty(index)); + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + QVariant v = p->read(d->m_object); + switch (p->kind()) { + case QDesignerMetaPropertyInterface::FlagKind: { + qdesigner_internal::PropertySheetFlagValue psflags = qdesigner_internal::PropertySheetFlagValue(v.toInt(), designerMetaFlagsFor(p->enumerator())); + qVariantSetValue(v, psflags); + } + break; + case QDesignerMetaPropertyInterface::EnumKind: { + qdesigner_internal::PropertySheetEnumValue pse = qdesigner_internal::PropertySheetEnumValue(v.toInt(), designerMetaEnumFor(p->enumerator())); + qVariantSetValue(v, pse); + } + break; + case QDesignerMetaPropertyInterface::OtherKind: + break; + } + return v; +} + +QVariant QDesignerPropertySheet::resolvePropertyValue(int index, const QVariant &value) const +{ + if (qVariantCanConvert<qdesigner_internal::PropertySheetEnumValue>(value)) + return qvariant_cast<qdesigner_internal::PropertySheetEnumValue>(value).value; + + if (qVariantCanConvert<qdesigner_internal::PropertySheetFlagValue>(value)) + return qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(value).value; + + if (qVariantCanConvert<qdesigner_internal::PropertySheetStringValue>(value)) + return qVariantValue<qdesigner_internal::PropertySheetStringValue>(value).value(); + + if (qVariantCanConvert<qdesigner_internal::PropertySheetKeySequenceValue>(value)) + return qVariantValue<qdesigner_internal::PropertySheetKeySequenceValue>(value).value(); + + if (qVariantCanConvert<qdesigner_internal::PropertySheetPixmapValue>(value)) { + const QString path = qVariantValue<qdesigner_internal::PropertySheetPixmapValue>(value).path(); + if (path.isEmpty()) + return defaultResourceProperty(index); + if (d->m_pixmapCache) { + return d->m_pixmapCache->pixmap(qvariant_cast<qdesigner_internal::PropertySheetPixmapValue>(value)); + } + } + + if (qVariantCanConvert<qdesigner_internal::PropertySheetIconValue>(value)) { + const int pathCount = qVariantValue<qdesigner_internal::PropertySheetIconValue>(value).paths().count(); + if (pathCount == 0) + return defaultResourceProperty(index); + if (d->m_iconCache) + return d->m_iconCache->icon(qvariant_cast<qdesigner_internal::PropertySheetIconValue>(value)); + } + + return value; +} + +void QDesignerPropertySheet::setFakeProperty(int index, const QVariant &value) +{ + Q_ASSERT(isFakeProperty(index)); + + QVariant &v = d->m_fakeProperties[index]; + + // set resource properties also (if we are going to have fake resource properties) + if (qVariantCanConvert<qdesigner_internal::PropertySheetFlagValue>(value) || qVariantCanConvert<qdesigner_internal::PropertySheetEnumValue>(value)) { + v = value; + } else if (qVariantCanConvert<qdesigner_internal::PropertySheetFlagValue>(v)) { + qdesigner_internal::PropertySheetFlagValue f = qvariant_cast<qdesigner_internal::PropertySheetFlagValue>(v); + f.value = value.toInt(); + qVariantSetValue(v, f); + Q_ASSERT(value.type() == QVariant::Int); + } else if (qVariantCanConvert<qdesigner_internal::PropertySheetEnumValue>(v)) { + qdesigner_internal::PropertySheetEnumValue e = qvariant_cast<qdesigner_internal::PropertySheetEnumValue>(v); + e.value = value.toInt(); + qVariantSetValue(v, e); + Q_ASSERT(value.type() == QVariant::Int); + } else { + v = value; + } +} + +void QDesignerPropertySheet::clearFakeProperties() +{ + d->m_fakeProperties.clear(); +} + +// Buddy needs to be byte array, else uic won't work +static QVariant toByteArray(const QVariant &value) { + if (value.type() == QVariant::ByteArray) + return value; + const QByteArray ba = value.toString().toUtf8(); + return QVariant(ba); +} + +void QDesignerPropertySheet::setProperty(int index, const QVariant &value) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + if (isAdditionalProperty(index)) { + if (d->m_objectType == ObjectLabel && propertyType(index) == PropertyBuddy) { + QFormBuilderExtra::applyBuddy(value.toString(), QFormBuilderExtra::BuddyApplyVisibleOnly, qobject_cast<QLabel *>(d->m_object)); + d->m_addProperties[index] = toByteArray(value); + return; + } + + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + layoutPropertySheet->setProperty(newIndex, value); + } + } + } + + if (isDynamicProperty(index)) { + if (d->isResourceProperty(index)) + d->setResourceProperty(index, value); + if (d->isStringProperty(index)) + d->setStringProperty(index, qVariantValue<qdesigner_internal::PropertySheetStringValue>(value)); + if (d->isKeySequenceProperty(index)) + d->setKeySequenceProperty(index, qVariantValue<qdesigner_internal::PropertySheetKeySequenceValue>(value)); + d->m_object->setProperty(propertyName(index).toUtf8(), resolvePropertyValue(index, value)); + if (d->m_object->isWidgetType()) { + QWidget *w = qobject_cast<QWidget *>(d->m_object); + w->setStyleSheet(w->styleSheet()); + } + } + d->m_addProperties[index] = value; + } else if (isFakeProperty(index)) { + setFakeProperty(index, value); + } else { + if (d->isResourceProperty(index)) + d->setResourceProperty(index, value); + if (d->isStringProperty(index)) + d->setStringProperty(index, qVariantValue<qdesigner_internal::PropertySheetStringValue>(value)); + if (d->isKeySequenceProperty(index)) + d->setKeySequenceProperty(index, qVariantValue<qdesigner_internal::PropertySheetKeySequenceValue>(value)); + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + p->write(d->m_object, resolvePropertyValue(index, value)); + if (qobject_cast<QGroupBox *>(d->m_object) && propertyType(index) == PropertyCheckable) { + const int idx = indexOf(QLatin1String("focusPolicy")); + if (!isChanged(idx)) { + qdesigner_internal::PropertySheetEnumValue e = qVariantValue<qdesigner_internal::PropertySheetEnumValue>(property(idx)); + if (value.toBool()) { + const QDesignerMetaPropertyInterface *p = d->m_meta->property(idx); + p->write(d->m_object, Qt::NoFocus); + e.value = Qt::StrongFocus; + QVariant v; + qVariantSetValue(v, e); + setFakeProperty(idx, v); + } else { + e.value = Qt::NoFocus; + QVariant v; + qVariantSetValue(v, e); + setFakeProperty(idx, v); + } + } + } + } +} + +bool QDesignerPropertySheet::hasReset(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return d->m_info.value(index).reset; + return true; +} + +bool QDesignerPropertySheet::reset(int index) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (d->isStringProperty(index)) + setProperty(index, qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + if (d->isKeySequenceProperty(index)) + setProperty(index, qVariantFromValue(qdesigner_internal::PropertySheetKeySequenceValue())); + if (d->isResourceProperty(index)) { + setProperty(index, d->emptyResourceProperty(index)); + return true; + } else if (isDynamic(index)) { + const QString propName = propertyName(index); + const QVariant oldValue = d->m_addProperties.value(index); + const QVariant newValue = d->m_info.value(index).defaultValue; + if (oldValue == newValue) + return true; + d->m_object->setProperty(propName.toUtf8(), newValue); + d->m_addProperties[index] = newValue; + return true; + } else if (!d->m_info.value(index).defaultValue.isNull()) { + setProperty(index, d->m_info.value(index).defaultValue); + return true; + } + if (isAdditionalProperty(index)) { + const PropertyType pType = propertyType(index); + if (d->m_objectType == ObjectLabel && pType == PropertyBuddy) { + setProperty(index, QVariant(QByteArray())); + return true; + } + if (isFakeLayoutProperty(index)) { + // special properties + switch (pType) { + case PropertyLayoutObjectName: + setProperty(index, QString()); + return true; + case PropertyLayoutSizeConstraint: + setProperty(index, QVariant(QLayout::SetDefaultConstraint)); + return true; + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) + return layoutPropertySheet->reset(layoutPropertySheet->indexOf(d->transformLayoutPropertyName(index))); + } + break; + default: + break; + } + // special margins + int value = -1; + switch (d->m_objectType) { + case ObjectQ3GroupBox: { + const QWidget *w = qobject_cast<const QWidget *>(d->m_object); + switch (pType) { + case PropertyLayoutLeftMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutLeftMargin); + break; + case PropertyLayoutTopMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutTopMargin); + break; + case PropertyLayoutRightMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutRightMargin); + break; + case PropertyLayoutBottomMargin: + value = w->style()->pixelMetric(QStyle::PM_LayoutBottomMargin); + break; + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + value = -1; + break; + default: + break; + } + } + break; + case ObjectLayoutWidget: + if (pType == PropertyLayoutLeftMargin || + pType == PropertyLayoutTopMargin || + pType == PropertyLayoutRightMargin || + pType == PropertyLayoutBottomMargin) + value = 0; + break; + default: + break; + } + setProperty(index, value); + return true; + } + return false; + } else if (isFakeProperty(index)) { + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + const bool result = p->reset(d->m_object); + d->m_fakeProperties[index] = p->read(d->m_object); + return result; + } else if (propertyType(index) == PropertyGeometry && d->m_object->isWidgetType()) { + if (QWidget *w = qobject_cast<QWidget*>(d->m_object)) { + QWidget *widget = w; + if (qdesigner_internal::Utils::isCentralWidget(d->m_fwb, widget) && d->m_fwb->parentWidget()) + widget = d->m_fwb->parentWidget(); + + if (widget != w && widget->parentWidget()) { + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + widget->parentWidget()->adjustSize(); + } + QApplication::processEvents(QEventLoop::ExcludeUserInputEvents); + widget->adjustSize(); + return true; + } + } + // ### TODO: reset for fake properties. + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + return p->reset(d->m_object); +} + +bool QDesignerPropertySheet::isChanged(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + return layoutPropertySheet->isChanged(newIndex); + return false; + } + } + } + } + return d->m_info.value(index).changed; +} + +void QDesignerPropertySheet::setChanged(int index, bool changed) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index)) { + QDesignerPropertySheetExtension *layoutPropertySheet; + if (d->layout(&layoutPropertySheet) && layoutPropertySheet) { + const QString newPropName = d->transformLayoutPropertyName(index); + if (!newPropName.isEmpty()) { + const int newIndex = layoutPropertySheet->indexOf(newPropName); + if (newIndex != -1) + layoutPropertySheet->setChanged(newIndex, changed); + } + } + } + } + if (d->isReloadableProperty(index)) { + if (d->m_fwb) { + if (changed) + d->m_fwb->addReloadableProperty(this, index); + else + d->m_fwb->removeReloadableProperty(this, index); + } + } + d->ensureInfo(index).changed = changed; +} + +bool QDesignerPropertySheet::isFakeLayoutProperty(int index) const +{ + if (!isAdditionalProperty(index)) + return false; + + switch (propertyType(index)) { + case PropertyLayoutObjectName: + case PropertyLayoutSizeConstraint: + return true; + case PropertyLayoutLeftMargin: + case PropertyLayoutTopMargin: + case PropertyLayoutRightMargin: + case PropertyLayoutBottomMargin: + case PropertyLayoutSpacing: + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + case PropertyLayoutFieldGrowthPolicy: + case PropertyLayoutRowWrapPolicy: + case PropertyLayoutLabelAlignment: + case PropertyLayoutFormAlignment: + case PropertyLayoutBoxStretch: + case PropertyLayoutGridRowStretch: + case PropertyLayoutGridColumnStretch: + case PropertyLayoutGridRowMinimumHeight: + case PropertyLayoutGridColumnMinimumWidth: + return d->m_canHaveLayoutAttributes; + default: + break; + } + return false; +} + +bool QDesignerPropertySheet::isVisible(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + + const PropertyType type = propertyType(index); + if (isAdditionalProperty(index)) { + if (isFakeLayoutProperty(index) && d->m_object->isWidgetType()) { + const QLayout *currentLayout = d->layout(); + if (!currentLayout) + return false; + const int visibleMask = qdesigner_internal::LayoutProperties::visibleProperties(currentLayout); + switch (type) { + case PropertyLayoutSpacing: + return visibleMask & qdesigner_internal::LayoutProperties::SpacingProperty; + case PropertyLayoutHorizontalSpacing: + case PropertyLayoutVerticalSpacing: + return visibleMask & qdesigner_internal::LayoutProperties::HorizSpacingProperty; + case PropertyLayoutFieldGrowthPolicy: + return visibleMask & qdesigner_internal::LayoutProperties::FieldGrowthPolicyProperty; + case PropertyLayoutRowWrapPolicy: + return visibleMask & qdesigner_internal::LayoutProperties::RowWrapPolicyProperty; + case PropertyLayoutLabelAlignment: + return visibleMask & qdesigner_internal::LayoutProperties::LabelAlignmentProperty; + case PropertyLayoutFormAlignment: + return visibleMask & qdesigner_internal::LayoutProperties::FormAlignmentProperty; + case PropertyLayoutBoxStretch: + return visibleMask & qdesigner_internal::LayoutProperties::BoxStretchProperty; + case PropertyLayoutGridRowStretch: + return visibleMask & qdesigner_internal::LayoutProperties::GridRowStretchProperty; + case PropertyLayoutGridColumnStretch: + return visibleMask & qdesigner_internal::LayoutProperties::GridColumnStretchProperty; + case PropertyLayoutGridRowMinimumHeight: + return visibleMask & qdesigner_internal::LayoutProperties::GridRowMinimumHeightProperty; + case PropertyLayoutGridColumnMinimumWidth: + return visibleMask & qdesigner_internal::LayoutProperties::GridColumnMinimumWidthProperty; + default: + break; + } + return true; + } + return d->m_info.value(index).visible; + } + + if (isFakeProperty(index)) { + if (type == PropertyWindowModality) // Hidden for child widgets + return d->m_info.value(index).visible; + return true; + } + + const bool visible = d->m_info.value(index).visible; + switch (type) { + case PropertyWindowTitle: + case PropertyWindowIcon: + case PropertyWindowFilePath: + case PropertyWindowOpacity: + case PropertyWindowIconText: + case PropertyWindowModified: + return visible; + default: + if (visible) + return true; + break; + } + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + if (!(p->accessFlags() & QDesignerMetaPropertyInterface::WriteAccess)) + return false; + + // Enabled handling + return (p->attributes(d->m_object) & QDesignerMetaPropertyInterface::DesignableAttribute) || + (p->attributes() & QDesignerMetaPropertyInterface::DesignableAttribute); +} + +void QDesignerPropertySheet::setVisible(int index, bool visible) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).visible = visible; +} + +bool QDesignerPropertySheet::isEnabled(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return true; + + if (isFakeProperty(index)) + return true; + + // Grey out geometry of laid-out widgets (including splitter) + if (propertyType(index) == PropertyGeometry && d->m_object->isWidgetType()) { + bool isManaged; + const qdesigner_internal::LayoutInfo::Type lt = qdesigner_internal::LayoutInfo::laidoutWidgetType(d->m_core, qobject_cast<QWidget *>(d->m_object), &isManaged); + return !isManaged || lt == qdesigner_internal::LayoutInfo::NoLayout; + } + + if (d->m_info.value(index).visible == true) // Sun CC 5.5 oddity, wants true + return true; + + const QDesignerMetaPropertyInterface *p = d->m_meta->property(index); + return (p->accessFlags() & QDesignerMetaPropertyInterface::WriteAccess) && + (p->attributes(d->m_object) & QDesignerMetaPropertyInterface::DesignableAttribute); +} + +bool QDesignerPropertySheet::isAttribute(int index) const +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return false; + if (isAdditionalProperty(index)) + return d->m_info.value(index).attribute; + + if (isFakeProperty(index)) + return false; + + return d->m_info.value(index).attribute; +} + +void QDesignerPropertySheet::setAttribute(int index, bool attribute) +{ + if (d->invalidIndex(Q_FUNC_INFO, index)) + return; + d->ensureInfo(index).attribute = attribute; +} + +QDesignerFormEditorInterface *QDesignerPropertySheet::core() const +{ + return d->m_core; +} + +bool QDesignerPropertySheet::internalDynamicPropertiesEnabled() +{ + return QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled; +} + +void QDesignerPropertySheet::setInternalDynamicPropertiesEnabled(bool v) +{ + QDesignerPropertySheetPrivate::m_internalDynamicPropertiesEnabled = v; +} + +// ---------- QDesignerAbstractPropertySheetFactory + +struct QDesignerAbstractPropertySheetFactory::PropertySheetFactoryPrivate { + PropertySheetFactoryPrivate(); + const QString m_propertySheetId; + const QString m_dynamicPropertySheetId; + + typedef QMap<QObject*, QObject*> ExtensionMap; + ExtensionMap m_extensions; + typedef QHash<QObject*, bool> ExtendedSet; + ExtendedSet m_extended; +}; + +QDesignerAbstractPropertySheetFactory::PropertySheetFactoryPrivate::PropertySheetFactoryPrivate() : + m_propertySheetId(Q_TYPEID(QDesignerPropertySheetExtension)), + m_dynamicPropertySheetId(Q_TYPEID(QDesignerDynamicPropertySheetExtension)) +{ +} + +// ---------- QDesignerAbstractPropertySheetFactory + + +QDesignerAbstractPropertySheetFactory::QDesignerAbstractPropertySheetFactory(QExtensionManager *parent) : + QExtensionFactory(parent), + m_impl(new PropertySheetFactoryPrivate) +{ +} + +QDesignerAbstractPropertySheetFactory::~QDesignerAbstractPropertySheetFactory() +{ + delete m_impl; +} + +QObject *QDesignerAbstractPropertySheetFactory::extension(QObject *object, const QString &iid) const +{ + typedef PropertySheetFactoryPrivate::ExtensionMap ExtensionMap; + if (!object) + return 0; + + if (iid != m_impl->m_propertySheetId && iid != m_impl->m_dynamicPropertySheetId) + return 0; + + ExtensionMap::iterator it = m_impl->m_extensions.find(object); + if (it == m_impl->m_extensions.end()) { + if (QObject *ext = createPropertySheet(object, const_cast<QDesignerAbstractPropertySheetFactory*>(this))) { + connect(ext, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + it = m_impl->m_extensions.insert(object, ext); + } + } + + if (!m_impl->m_extended.contains(object)) { + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(objectDestroyed(QObject*))); + m_impl->m_extended.insert(object, true); + } + + if (it == m_impl->m_extensions.end()) + return 0; + + return it.value(); +} + +void QDesignerAbstractPropertySheetFactory::objectDestroyed(QObject *object) +{ + QMutableMapIterator<QObject*, QObject*> it(m_impl->m_extensions); + while (it.hasNext()) { + it.next(); + + QObject *o = it.key(); + if (o == object || object == it.value()) { + it.remove(); + } + } + + m_impl->m_extended.remove(object); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_propertysheet_p.h b/tools/designer/src/lib/shared/qdesigner_propertysheet_p.h new file mode 100644 index 0000000..8a25baf --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_propertysheet_p.h @@ -0,0 +1,265 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_PROPERTYSHEET_H +#define QDESIGNER_PROPERTYSHEET_H + +#include "shared_global_p.h" +#include "dynamicpropertysheet.h" +#include <QtDesigner/propertysheet.h> +#include <QtDesigner/default_extensionfactory.h> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QVariant> +#include <QtCore/QPair> + +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QLayout; +class QDesignerFormEditorInterface; +class QDesignerPropertySheetPrivate; + +namespace qdesigner_internal +{ + class DesignerPixmapCache; + class DesignerIconCache; + class FormWindowBase; +} + +class QDESIGNER_SHARED_EXPORT QDesignerPropertySheet: public QObject, public QDesignerPropertySheetExtension, public QDesignerDynamicPropertySheetExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerPropertySheetExtension QDesignerDynamicPropertySheetExtension) +public: + explicit QDesignerPropertySheet(QObject *object, QObject *parent = 0); + virtual ~QDesignerPropertySheet(); + + virtual int indexOf(const QString &name) const; + + virtual int count() const; + virtual QString propertyName(int index) const; + + virtual QString propertyGroup(int index) const; + virtual void setPropertyGroup(int index, const QString &group); + + virtual bool hasReset(int index) const; + virtual bool reset(int index); + + virtual bool isAttribute(int index) const; + virtual void setAttribute(int index, bool b); + + virtual bool isVisible(int index) const; + virtual void setVisible(int index, bool b); + + virtual QVariant property(int index) const; + virtual void setProperty(int index, const QVariant &value); + + virtual bool isChanged(int index) const; + + virtual void setChanged(int index, bool changed); + + virtual bool dynamicPropertiesAllowed() const; + virtual int addDynamicProperty(const QString &propertyName, const QVariant &value); + virtual bool removeDynamicProperty(int index); + virtual bool isDynamicProperty(int index) const; + virtual bool canAddDynamicProperty(const QString &propertyName) const; + + bool isDefaultDynamicProperty(int index) const; + + bool isResourceProperty(int index) const; + QVariant defaultResourceProperty(int index) const; + + qdesigner_internal::DesignerPixmapCache *pixmapCache() const; + void setPixmapCache(qdesigner_internal::DesignerPixmapCache *cache); + qdesigner_internal::DesignerIconCache *iconCache() const; + void setIconCache(qdesigner_internal::DesignerIconCache *cache); + int createFakeProperty(const QString &propertyName, const QVariant &value = QVariant()); + + virtual bool isEnabled(int index) const; + QObject *object() const; + + static bool internalDynamicPropertiesEnabled(); + static void setInternalDynamicPropertiesEnabled(bool v); + +protected: + bool isAdditionalProperty(int index) const; + bool isFakeProperty(int index) const; + QVariant resolvePropertyValue(int index, const QVariant &value) const; + QVariant metaProperty(int index) const; + void setFakeProperty(int index, const QVariant &value); + void clearFakeProperties(); + + bool isFakeLayoutProperty(int index) const; + bool isDynamic(int index) const; + qdesigner_internal::FormWindowBase *formWindowBase() const; + QDesignerFormEditorInterface *core() const; + +public: + enum PropertyType { PropertyNone, + PropertyLayoutObjectName, + PropertyLayoutLeftMargin, + PropertyLayoutTopMargin, + PropertyLayoutRightMargin, + PropertyLayoutBottomMargin, + PropertyLayoutSpacing, + PropertyLayoutHorizontalSpacing, + PropertyLayoutVerticalSpacing, + PropertyLayoutSizeConstraint, + PropertyLayoutFieldGrowthPolicy, + PropertyLayoutRowWrapPolicy, + PropertyLayoutLabelAlignment, + PropertyLayoutFormAlignment, + PropertyLayoutBoxStretch, + PropertyLayoutGridRowStretch, + PropertyLayoutGridColumnStretch, + PropertyLayoutGridRowMinimumHeight, + PropertyLayoutGridColumnMinimumWidth, + PropertyBuddy, + PropertyAccessibility, + PropertyGeometry, + PropertyCheckable, + PropertyWindowTitle, + PropertyWindowIcon, + PropertyWindowFilePath, + PropertyWindowOpacity, + PropertyWindowIconText, + PropertyWindowModality, + PropertyWindowModified, + PropertyStyleSheet + }; + + enum ObjectType { ObjectNone, ObjectLabel, ObjectLayout, ObjectLayoutWidget, ObjectQ3GroupBox }; + + static ObjectType objectTypeFromObject(const QObject *o); + static PropertyType propertyTypeFromName(const QString &name); + +protected: + PropertyType propertyType(int index) const; + ObjectType objectType() const; + +private: + QDesignerPropertySheetPrivate *d; +}; + +/* Abstract base class for factories that register a property sheet that implements + * both QDesignerPropertySheetExtension and QDesignerDynamicPropertySheetExtension + * by multiple inheritance. The factory maintains ownership of + * the extension and returns it for both id's. */ + +class QDESIGNER_SHARED_EXPORT QDesignerAbstractPropertySheetFactory: public QExtensionFactory +{ + Q_OBJECT + Q_INTERFACES(QAbstractExtensionFactory) +public: + explicit QDesignerAbstractPropertySheetFactory(QExtensionManager *parent = 0); + virtual ~QDesignerAbstractPropertySheetFactory(); + + QObject *extension(QObject *object, const QString &iid) const; + +private slots: + void objectDestroyed(QObject *object); + +private: + virtual QObject *createPropertySheet(QObject *qObject, QObject *parent) const = 0; + + struct PropertySheetFactoryPrivate; + PropertySheetFactoryPrivate *m_impl; +}; + +/* Convenience factory template for property sheets that implement + * QDesignerPropertySheetExtension and QDesignerDynamicPropertySheetExtension + * by multiple inheritance. */ + +template <class Object, class PropertySheet> +class QDesignerPropertySheetFactory : public QDesignerAbstractPropertySheetFactory { +public: + explicit QDesignerPropertySheetFactory(QExtensionManager *parent = 0); + + static void registerExtension(QExtensionManager *mgr); + +private: + // Does a qobject_cast on the object. + virtual QObject *createPropertySheet(QObject *qObject, QObject *parent) const; +}; + +template <class Object, class PropertySheet> +QDesignerPropertySheetFactory<Object, PropertySheet>::QDesignerPropertySheetFactory(QExtensionManager *parent) : + QDesignerAbstractPropertySheetFactory(parent) +{ +} + +template <class Object, class PropertySheet> +QObject *QDesignerPropertySheetFactory<Object, PropertySheet>::createPropertySheet(QObject *qObject, QObject *parent) const +{ + Object *object = qobject_cast<Object *>(qObject); + if (!object) + return 0; + return new PropertySheet(object, parent); +} + +template <class Object, class PropertySheet> +void QDesignerPropertySheetFactory<Object, PropertySheet>::registerExtension(QExtensionManager *mgr) +{ + QDesignerPropertySheetFactory *factory = new QDesignerPropertySheetFactory(mgr); + mgr->registerExtensions(factory, Q_TYPEID(QDesignerPropertySheetExtension)); + mgr->registerExtensions(factory, Q_TYPEID(QDesignerDynamicPropertySheetExtension)); +} + + +// Standard property sheet +typedef QDesignerPropertySheetFactory<QObject, QDesignerPropertySheet> QDesignerDefaultPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_PROPERTYSHEET_H diff --git a/tools/designer/src/lib/shared/qdesigner_qsettings.cpp b/tools/designer/src/lib/shared/qdesigner_qsettings.cpp new file mode 100644 index 0000000..efea643 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_qsettings.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_qsettings_p.h" + +#include <QtCore/QSettings> +#include <QtCore/QCoreApplication> +#include <QtCore/QTextStream> +#include <QtCore/QString> +#include <QtCore/QDebug> + +/*! + \class QDesignerSettingsSimple + + \brief Implements QDesignerSettingsInterface by calls to QSettings + */ + +QDesignerQSettings::QDesignerQSettings() : + m_settings(qApp->organizationName(), settingsApplicationName()) +{ +} + +QString QDesignerQSettings::settingsApplicationName() +{ + return qApp->applicationName(); +} + +void QDesignerQSettings::beginGroup(const QString &prefix) +{ + m_settings.beginGroup(prefix); +} + +void QDesignerQSettings::endGroup() +{ + m_settings.endGroup(); +} + +bool QDesignerQSettings::contains(const QString &key) const +{ + return m_settings.contains(key); +} + +void QDesignerQSettings::setValue(const QString &key, const QVariant &value) +{ + m_settings.setValue(key, value); +} + +QVariant QDesignerQSettings::value(const QString &key, const QVariant &defaultValue) const +{ + return m_settings.value(key, defaultValue); +} + +void QDesignerQSettings::remove(const QString &key) +{ + m_settings.remove(key); +} diff --git a/tools/designer/src/lib/shared/qdesigner_qsettings_p.h b/tools/designer/src/lib/shared/qdesigner_qsettings_p.h new file mode 100644 index 0000000..2ca749b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_qsettings_p.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_QSETTINGS_H +#define QDESIGNER_QSETTINGS_H + +#include "abstractsettings_p.h" +#include "shared_global_p.h" + +#include <QtCore/QSettings> + +QT_BEGIN_NAMESPACE + +// Implements QDesignerSettingsInterface by calls to QSettings +class QDESIGNER_SHARED_EXPORT QDesignerQSettings : public QDesignerSettingsInterface +{ +public: + QDesignerQSettings(); + + virtual void beginGroup(const QString &prefix); + virtual void endGroup(); + + virtual bool contains(const QString &key) const; + virtual void setValue(const QString &key, const QVariant &value); + virtual QVariant value(const QString &key, const QVariant &defaultValue = QVariant()) const; + virtual void remove(const QString &key); + + // The application name to be used for settings. Allows for including + // the Qt version to prevent settings of different Qt versions from + // interfering. + static QString settingsApplicationName(); + +private: + QSettings m_settings; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_QSETTINGS_H diff --git a/tools/designer/src/lib/shared/qdesigner_stackedbox.cpp b/tools/designer/src/lib/shared/qdesigner_stackedbox.cpp new file mode 100644 index 0000000..c36ab9a --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_stackedbox.cpp @@ -0,0 +1,396 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "orderdialog_p.h" +#include "promotiontaskmenu_p.h" +#include "widgetfactory_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtGui/QToolButton> +#include <QtGui/QAction> +#include <QtGui/qevent.h> +#include <QtGui/QMenu> +#include <QtGui/QStackedWidget> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +static QToolButton *createToolButton(QWidget *parent, Qt::ArrowType at, const QString &name) { + QToolButton *rc = new QToolButton(); + rc->setAttribute(Qt::WA_NoChildEventsForParent, true); + rc->setParent(parent); + rc->setObjectName(name); + rc->setArrowType(at); + rc->setAutoRaise(true); + rc->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + rc->setFixedSize(QSize(15, 15)); + return rc; +} + +// --------------- QStackedWidgetPreviewEventFilter +QStackedWidgetPreviewEventFilter::QStackedWidgetPreviewEventFilter(QStackedWidget *parent) : + QObject(parent), + m_buttonToolTipEnabled(false), // Not on preview + m_stackedWidget(parent), + m_prev(createToolButton(m_stackedWidget, Qt::LeftArrow, QLatin1String("__qt__passive_prev"))), + m_next(createToolButton(m_stackedWidget, Qt::RightArrow, QLatin1String("__qt__passive_next"))) +{ + connect(m_prev, SIGNAL(clicked()), this, SLOT(prevPage())); + connect(m_next, SIGNAL(clicked()), this, SLOT(nextPage())); + + updateButtons(); + m_stackedWidget->installEventFilter(this); + m_prev->installEventFilter(this); + m_next->installEventFilter(this); +} + +void QStackedWidgetPreviewEventFilter::install(QStackedWidget *stackedWidget) +{ + new QStackedWidgetPreviewEventFilter(stackedWidget); +} + +void QStackedWidgetPreviewEventFilter::updateButtons() +{ + m_prev->move(m_stackedWidget->width() - 31, 1); + m_prev->show(); + m_prev->raise(); + + m_next->move(m_stackedWidget->width() - 16, 1); + m_next->show(); + m_next->raise(); +} + +void QStackedWidgetPreviewEventFilter::prevPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + fw->clearSelection(); + fw->selectWidget(stackedWidget(), true); + } + const int count = m_stackedWidget->count(); + if (count > 1) { + int newIndex = m_stackedWidget->currentIndex() - 1; + if (newIndex < 0) + newIndex = count - 1; + gotoPage(newIndex); + } +} + +void QStackedWidgetPreviewEventFilter::nextPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + fw->clearSelection(); + fw->selectWidget(stackedWidget(), true); + } + const int count = m_stackedWidget->count(); + if (count > 1) + gotoPage((m_stackedWidget->currentIndex() + 1) % count); +} + +bool QStackedWidgetPreviewEventFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (watched->isWidgetType()) { + if (watched == m_stackedWidget) { + switch (event->type()) { + case QEvent::LayoutRequest: + updateButtons(); + break; + case QEvent::ChildAdded: + case QEvent::ChildRemoved: + case QEvent::Resize: + case QEvent::Show: + updateButtons(); + break; + default: + break; + } + } + if (m_buttonToolTipEnabled && (watched == m_next || watched == m_prev)) { + switch (event->type()) { + case QEvent::ToolTip: + updateButtonToolTip(watched); // Tooltip includes page number, so, refresh on demand + break; + default: + break; + } + } + } + return QObject::eventFilter(watched, event); +} + +void QStackedWidgetPreviewEventFilter::gotoPage(int page) +{ + m_stackedWidget->setCurrentIndex(page); + updateButtons(); +} + +static inline QString stackedClassName(QStackedWidget *w) +{ + if (const QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w)) + return qdesigner_internal::WidgetFactory::classNameOf(fw->core(), w); + return QLatin1String("Stacked widget"); +} + +void QStackedWidgetPreviewEventFilter::updateButtonToolTip(QObject *o) +{ + QString className = QLatin1String("Stacked widget"); + if (const QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_stackedWidget)) + className = qdesigner_internal::WidgetFactory::classNameOf(fw->core(), m_stackedWidget); + if (o == m_prev) { + const QString msg = tr("Go to previous page of %1 '%2' (%3/%4).").arg(stackedClassName(m_stackedWidget)).arg(m_stackedWidget->objectName()).arg(m_stackedWidget->currentIndex() + 1).arg(m_stackedWidget->count()); + m_prev->setToolTip(msg); + } else { + if (o == m_next) { + const QString msg = tr("Go to next page of %1 '%2' (%3/%4).").arg(stackedClassName(m_stackedWidget)).arg(m_stackedWidget->objectName()).arg(m_stackedWidget->currentIndex() + 1).arg(m_stackedWidget->count()); + m_next->setToolTip(msg); + } + } +} + +// --------------- QStackedWidgetEventFilter +QStackedWidgetEventFilter::QStackedWidgetEventFilter(QStackedWidget *parent) : + QStackedWidgetPreviewEventFilter(parent), + m_actionPreviousPage(new QAction(tr("Previous Page"), this)), + m_actionNextPage(new QAction(tr("Next Page"), this)), + m_actionDeletePage(new QAction(tr("Delete"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_actionChangePageOrder(new QAction(tr("Change Page Order..."), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + setButtonToolTipEnabled(true); + connect(m_actionPreviousPage, SIGNAL(triggered()), this, SLOT(prevPage())); + connect(m_actionNextPage, SIGNAL(triggered()), this, SLOT(nextPage())); + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionChangePageOrder, SIGNAL(triggered()), this, SLOT(changeOrder())); +} + +void QStackedWidgetEventFilter::install(QStackedWidget *stackedWidget) +{ + new QStackedWidgetEventFilter(stackedWidget); +} + +QStackedWidgetEventFilter *QStackedWidgetEventFilter::eventFilterOf(const QStackedWidget *stackedWidget) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = stackedWidget->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QStackedWidgetEventFilter *ef = qobject_cast<QStackedWidgetEventFilter *>(o)) + return ef; + } + return 0; +} + +QMenu *QStackedWidgetEventFilter::addStackedWidgetContextMenuActions(const QStackedWidget *stackedWidget, QMenu *popup) +{ + QStackedWidgetEventFilter *filter = eventFilterOf(stackedWidget); + if (!filter) + return 0; + return filter->addContextMenuActions(popup); +} + +void QStackedWidgetEventFilter::removeCurrentPage() +{ + if (stackedWidget()->currentIndex() == -1) + return; + + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::DeleteStackedWidgetPageCommand *cmd = new qdesigner_internal::DeleteStackedWidgetPageCommand(fw); + cmd->init(stackedWidget()); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::changeOrder() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget()); + + if (!fw) + return; + + const QWidgetList oldPages = qdesigner_internal::OrderDialog::pagesOfContainer(fw->core(), stackedWidget()); + const int pageCount = oldPages.size(); + if (pageCount < 2) + return; + + qdesigner_internal::OrderDialog dlg(fw); + dlg.setPageList(oldPages); + if (dlg.exec() == QDialog::Rejected) + return; + + const QWidgetList newPages = dlg.pageList(); + if (newPages == oldPages) + return; + + fw->beginCommand(tr("Change Page Order")); + for(int i=0; i < pageCount; ++i) { + if (newPages.at(i) == stackedWidget()->widget(i)) + continue; + qdesigner_internal::MoveStackedWidgetCommand *cmd = new qdesigner_internal::MoveStackedWidgetCommand(fw); + cmd->init(stackedWidget(), newPages.at(i), i); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void QStackedWidgetEventFilter::addPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::AddStackedWidgetPageCommand *cmd = new qdesigner_internal::AddStackedWidgetPageCommand(fw); + cmd->init(stackedWidget(), qdesigner_internal::AddStackedWidgetPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::AddStackedWidgetPageCommand *cmd = new qdesigner_internal::AddStackedWidgetPageCommand(fw); + cmd->init(stackedWidget(), qdesigner_internal::AddStackedWidgetPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +void QStackedWidgetEventFilter::gotoPage(int page) { + // Are we on a form or in a preview? + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(stackedWidget())) { + qdesigner_internal::SetPropertyCommand *cmd = new qdesigner_internal::SetPropertyCommand(fw); + cmd->init(stackedWidget(), QLatin1String("currentIndex"), page); + fw->commandHistory()->push(cmd); + fw->emitSelectionChanged(); // Magically prevent an endless loop triggered by auto-repeat. + updateButtons(); + } else { + QStackedWidgetPreviewEventFilter::gotoPage(page); + } +} + +QMenu *QStackedWidgetEventFilter::addContextMenuActions(QMenu *popup) +{ + QMenu *pageMenu = 0; + const int count = stackedWidget()->count(); + const bool hasSeveralPages = count > 1; + m_actionDeletePage->setEnabled(hasSeveralPages); + if (count) { + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(stackedWidget()->currentIndex() + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = stackedWidget()->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(stackedWidget()), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + } + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + popup->addAction(m_actionNextPage); + m_actionNextPage->setEnabled(hasSeveralPages); + popup->addAction(m_actionPreviousPage); + m_actionPreviousPage->setEnabled(hasSeveralPages); + popup->addAction(m_actionChangePageOrder); + m_actionChangePageOrder->setEnabled(hasSeveralPages); + popup->addSeparator(); + return pageMenu; +} + +// -------- QStackedWidgetPropertySheet + +static const char *pagePropertyName = "currentPageName"; + +QStackedWidgetPropertySheet::QStackedWidgetPropertySheet(QStackedWidget *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_stackedWidget(object) +{ + createFakeProperty(QLatin1String(pagePropertyName), QString()); +} + +bool QStackedWidgetPropertySheet::isEnabled(int index) const +{ + if (propertyName(index) != QLatin1String(pagePropertyName)) + return QDesignerPropertySheet::isEnabled(index); + return m_stackedWidget->currentWidget() != 0; +} + +void QStackedWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + if (QWidget *w = m_stackedWidget->currentWidget()) + w->setObjectName(value.toString()); + } else { + QDesignerPropertySheet::setProperty(index, value); + } +} + +QVariant QStackedWidgetPropertySheet::property(int index) const +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + if (const QWidget *w = m_stackedWidget->currentWidget()) + return w->objectName(); + return QString(); + } + return QDesignerPropertySheet::property(index); +} + +bool QStackedWidgetPropertySheet::reset(int index) +{ + if (propertyName(index) == QLatin1String(pagePropertyName)) { + setProperty(index, QString()); + return true; + } + return QDesignerPropertySheet::reset(index); +} + +bool QStackedWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + return propertyName != QLatin1String(pagePropertyName); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_stackedbox_p.h b/tools/designer/src/lib/shared/qdesigner_stackedbox_p.h new file mode 100644 index 0000000..a48dd94 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_stackedbox_p.h @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_STACKEDBOX_H +#define QDESIGNER_STACKEDBOX_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" + +QT_BEGIN_NAMESPACE + +class QStackedWidget; +class QWidget; +class QAction; +class QMenu; +class QToolButton; + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +// Event filter to be installed on a QStackedWidget in preview mode. +// Create two buttons to switch pages. + +class QDESIGNER_SHARED_EXPORT QStackedWidgetPreviewEventFilter : public QObject +{ + Q_OBJECT +public: + explicit QStackedWidgetPreviewEventFilter(QStackedWidget *parent); + + // Install helper on QStackedWidget + static void install(QStackedWidget *stackedWidget); + bool eventFilter(QObject *watched, QEvent *event); + + void setButtonToolTipEnabled(bool v) { m_buttonToolTipEnabled = v; } + bool buttonToolTipEnabled() const { return m_buttonToolTipEnabled; } + +public slots: + void updateButtons(); + void prevPage(); + void nextPage(); + +protected: + QStackedWidget *stackedWidget() const { return m_stackedWidget; } + virtual void gotoPage(int page); + +private: + void updateButtonToolTip(QObject *o); + + bool m_buttonToolTipEnabled; + QStackedWidget *m_stackedWidget; + QToolButton *m_prev; + QToolButton *m_next; +}; + +// Event filter to be installed on a QStackedWidget in editing mode. +// In addition to the browse buttons, handles context menu and everything + +class QDESIGNER_SHARED_EXPORT QStackedWidgetEventFilter : public QStackedWidgetPreviewEventFilter +{ + Q_OBJECT +public: + explicit QStackedWidgetEventFilter(QStackedWidget *parent); + + // Install helper on QStackedWidget + static void install(QStackedWidget *stackedWidget); + static QStackedWidgetEventFilter *eventFilterOf(const QStackedWidget *stackedWidget); + // Convenience to add a menu on a tackedWidget + static QMenu *addStackedWidgetContextMenuActions(const QStackedWidget *stackedWidget, QMenu *popup); + + // Add context menu and return page submenu or 0. + QMenu *addContextMenuActions(QMenu *popup); + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + void changeOrder(); + +protected: + virtual void gotoPage(int page); + +private: + QAction *m_actionPreviousPage; + QAction *m_actionNextPage; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + QAction *m_actionChangePageOrder; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the "currentPageName" property +class QDESIGNER_SHARED_EXPORT QStackedWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QStackedWidgetPropertySheet(QStackedWidget *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + QStackedWidget *m_stackedWidget; +}; + +typedef QDesignerPropertySheetFactory<QStackedWidget, QStackedWidgetPropertySheet> QStackedWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_STACKEDBOX_H diff --git a/tools/designer/src/lib/shared/qdesigner_tabwidget.cpp b/tools/designer/src/lib/shared/qdesigner_tabwidget.cpp new file mode 100644 index 0000000..903b066 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_tabwidget.cpp @@ -0,0 +1,567 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_command_p.h" +#include "qdesigner_propertycommand_p.h" +#include "promotiontaskmenu_p.h" +#include "formwindowbase_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtGui/QApplication> +#include <QtGui/QTabBar> +#include <QtGui/QAction> +#include <QtGui/QMouseEvent> +#include <QtGui/QMenu> +#include <QtGui/QLabel> +#include <QtGui/QTabWidget> + +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +// Store tab widget as drag source +class MyMimeData : public QMimeData +{ + Q_OBJECT +public: + MyMimeData(const QTabWidget *tab) : m_tab(tab) {} + static bool fromMyTab(const QMimeData *mimeData, const QTabWidget *tab) { + if (!mimeData) + return false; + const MyMimeData *m = qobject_cast<const MyMimeData *>(mimeData); + return m && m->m_tab == tab; + } +private: + const QTabWidget *m_tab; +}; + +} // namespace qdesigner_internal + +// ------------- QTabWidgetEventFilter + +QTabWidgetEventFilter::QTabWidgetEventFilter(QTabWidget *parent) : + QObject(parent), + m_tabWidget(parent), + m_dropIndicator(0), + m_dragPage(0), + m_mousePressed(false), + m_actionDeletePage(new QAction(tr("Delete"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + tabBar()->setAcceptDrops(true); + tabBar()->installEventFilter(this); + + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); +} + +QTabWidgetEventFilter::~QTabWidgetEventFilter() +{ +} + +void QTabWidgetEventFilter::install(QTabWidget *tabWidget) +{ + new QTabWidgetEventFilter(tabWidget); +} + +QTabWidgetEventFilter *QTabWidgetEventFilter::eventFilterOf(const QTabWidget *tabWidget) +{ + // Look for 1st order children only..otherwise, we might get filters of nested tab widgets + const QObjectList children = tabWidget->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QTabWidgetEventFilter *ef = qobject_cast<QTabWidgetEventFilter*>(o)) + return ef; + } + return 0; +} + +QMenu *QTabWidgetEventFilter::addTabWidgetContextMenuActions(const QTabWidget *tabWidget, QMenu *popup) +{ + QTabWidgetEventFilter *filter = eventFilterOf(tabWidget); + if (!filter) + return 0; + return filter->addContextMenuActions(popup); +} + +QTabBar *QTabWidgetEventFilter::tabBar() const +{ + // QTabWidget::tabBar() accessor is protected, grmbl... + if (!m_cachedTabBar) { + const QList<QTabBar *> tabBars = qFindChildren<QTabBar *>(m_tabWidget); + Q_ASSERT(tabBars.size() == 1); + m_cachedTabBar = tabBars.front(); + } + return m_cachedTabBar; + +} + +static bool canMove(const QPoint &pressPoint, const QMouseEvent *e) +{ + const QPoint pt = pressPoint - e->pos(); + return pt.manhattanLength() > QApplication::startDragDistance(); +} + +bool QTabWidgetEventFilter::eventFilter(QObject *o, QEvent *e) +{ + const QEvent::Type type = e->type(); + // Do not try to locate tab bar and form window, etc. for uninteresting events and + // avoid asserts about missing tab bars when being destroyed + switch (type) { + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseMove: + case QEvent::DragLeave: + case QEvent::DragEnter: + case QEvent::DragMove: + case QEvent::Drop: + break; + default: + return false; + } + + if (o != tabBar()) + return false; + + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return false; + + switch (type) { + case QEvent::MouseButtonDblClick: + break; + case QEvent::MouseButtonPress: { + QMouseEvent *mev = static_cast<QMouseEvent*>(e); + if (QDesignerFormWindowInterface *fw = formWindow()) { + fw->clearSelection(); + fw->selectWidget(m_tabWidget, true); + } + if (mev->button() & Qt::LeftButton) { + m_mousePressed = true; + m_pressPoint = mev->pos(); + + QTabBar *tabbar = tabBar(); + const int count = tabbar->count(); + for (int i = 0; i < count; ++i) { + if (tabbar->tabRect(i).contains(m_pressPoint)) { + if (i != tabbar->currentIndex()) { + qdesigner_internal::SetPropertyCommand *cmd = new qdesigner_internal::SetPropertyCommand(fw); + cmd->init(m_tabWidget, QLatin1String("currentIndex"), i); + fw->commandHistory()->push(cmd); + } + break; + } + } + } + } break; + + case QEvent::MouseButtonRelease: + m_mousePressed = false; + break; + + case QEvent::MouseMove: { + QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(e); + if (m_mousePressed && canMove(m_pressPoint, mouseEvent)) { + const int index = m_tabWidget->currentIndex(); + if (index == -1) + break; + + m_mousePressed = false; + QDrag *drg = new QDrag(m_tabWidget); + drg->setMimeData(new qdesigner_internal::MyMimeData(m_tabWidget)); + + m_dragIndex = index; + m_dragPage = m_tabWidget->currentWidget(); + m_dragLabel = m_tabWidget->tabText(index); + m_dragIcon = m_tabWidget->tabIcon(index); + if (m_dragIcon.isNull()) { + QLabel *label = new QLabel(m_dragLabel); + label->adjustSize(); + drg->setPixmap(QPixmap::grabWidget(label)); + label->deleteLater(); + } else { + drg->setPixmap(m_dragIcon.pixmap(22, 22)); + } + + m_tabWidget->removeTab(m_dragIndex); + + const Qt::DropActions dropAction = drg->start(Qt::MoveAction); + + if (dropAction == Qt::IgnoreAction) { + // abort + m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel); + m_tabWidget->setCurrentIndex(m_dragIndex); + } + + if (m_dropIndicator) + m_dropIndicator->hide(); + } + } break; + + case QEvent::DragLeave: { + if (m_dropIndicator) + m_dropIndicator->hide(); + } break; + + case QEvent::DragEnter: + case QEvent::DragMove: { + QDragMoveEvent *de = static_cast<QDragMoveEvent*>(e); + if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget)) + return false; + + if (de->proposedAction() == Qt::MoveAction) + de->acceptProposedAction(); + else { + de->setDropAction(Qt::MoveAction); + de->accept(); + } + + QRect rect; + const int index = pageFromPosition(de->pos(), rect); + + if (!m_dropIndicator) { + m_dropIndicator = new QWidget(m_tabWidget); + QPalette p = m_dropIndicator->palette(); + p.setColor(m_tabWidget->backgroundRole(), Qt::red); + m_dropIndicator->setPalette(p); + } + + QPoint pos; + if (index == m_tabWidget->count()) + pos = tabBar()->mapToParent(QPoint(rect.x() + rect.width(), rect.y())); + else + pos = tabBar()->mapToParent(QPoint(rect.x(), rect.y())); + + m_dropIndicator->setGeometry(pos.x(), pos.y() , 3, rect.height()); + m_dropIndicator->show(); + } break; + + case QEvent::Drop: { + QDropEvent *de = static_cast<QDropEvent*>(e); + if (!qdesigner_internal::MyMimeData::fromMyTab(de->mimeData(), m_tabWidget)) + return false; + de->acceptProposedAction(); + de->accept(); + + QRect rect; + const int newIndex = pageFromPosition(de->pos(), rect); + + qdesigner_internal::MoveTabPageCommand *cmd = new qdesigner_internal::MoveTabPageCommand(fw); + m_tabWidget->insertTab(m_dragIndex, m_dragPage, m_dragIcon, m_dragLabel); + cmd->init(m_tabWidget, m_dragPage, m_dragIcon, m_dragLabel, m_dragIndex, newIndex); + fw->commandHistory()->push(cmd); + } break; + + default: + break; + } + + return false; +} + +void QTabWidgetEventFilter::removeCurrentPage() +{ + if (!m_tabWidget->currentWidget()) + return; + + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::DeleteTabPageCommand *cmd = new qdesigner_internal::DeleteTabPageCommand(fw); + cmd->init(m_tabWidget); + fw->commandHistory()->push(cmd); + } +} + +void QTabWidgetEventFilter::addPage() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw); + cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QTabWidgetEventFilter::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + qdesigner_internal::AddTabPageCommand *cmd = new qdesigner_internal::AddTabPageCommand(fw); + cmd->init(m_tabWidget, qdesigner_internal::AddTabPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +QDesignerFormWindowInterface *QTabWidgetEventFilter::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(const_cast<QTabWidget*>(m_tabWidget)); +} + +// Get page from mouse position. Default to new page if in right half of last page? +int QTabWidgetEventFilter::pageFromPosition(const QPoint &pos, QRect &rect) const +{ + int index = 0; + const QTabBar *tabbar = tabBar(); + const int count = m_tabWidget->count(); + for (; index < count; index++) { + const QRect rc = tabbar->tabRect(index); + if (rc.contains(pos)) { + rect = rc; + break; + } + } + + if (index == count -1) { + QRect rect2 = rect; + rect2.setLeft(rect2.left() + rect2.width() / 2); + if (rect2.contains(pos)) + index++; + } + return index; +} + +QMenu *QTabWidgetEventFilter::addContextMenuActions(QMenu *popup) +{ + QMenu *pageMenu = 0; + const int count = m_tabWidget->count(); + m_actionDeletePage->setEnabled(count > 1); + if (count) { + const int currentIndex = m_tabWidget->currentIndex(); + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(currentIndex + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = m_tabWidget->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(m_tabWidget), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + } + + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + popup->addSeparator(); + return pageMenu; +} + +// ----------- QTabWidgetPropertySheet + +static const char *currentTabTextKey = "currentTabText"; +static const char *currentTabNameKey = "currentTabName"; +static const char *currentTabIconKey = "currentTabIcon"; +static const char *currentTabToolTipKey = "currentTabToolTip"; +static const char *currentTabWhatsThisKey = "currentTabWhatsThis"; + +QTabWidgetPropertySheet::QTabWidgetPropertySheet(QTabWidget *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_tabWidget(object) +{ + createFakeProperty(QLatin1String(currentTabTextKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentTabNameKey), QString()); + createFakeProperty(QLatin1String(currentTabIconKey), qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); + if (formWindowBase()) + formWindowBase()->addReloadableProperty(this, indexOf(QLatin1String(currentTabIconKey))); + createFakeProperty(QLatin1String(currentTabToolTipKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentTabWhatsThisKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); +} + +QTabWidgetPropertySheet::TabWidgetProperty QTabWidgetPropertySheet::tabWidgetPropertyFromName(const QString &name) +{ + typedef QHash<QString, TabWidgetProperty> TabWidgetPropertyHash; + static TabWidgetPropertyHash tabWidgetPropertyHash; + if (tabWidgetPropertyHash.empty()) { + tabWidgetPropertyHash.insert(QLatin1String(currentTabTextKey), PropertyCurrentTabText); + tabWidgetPropertyHash.insert(QLatin1String(currentTabNameKey), PropertyCurrentTabName); + tabWidgetPropertyHash.insert(QLatin1String(currentTabIconKey), PropertyCurrentTabIcon); + tabWidgetPropertyHash.insert(QLatin1String(currentTabToolTipKey), PropertyCurrentTabToolTip); + tabWidgetPropertyHash.insert(QLatin1String(currentTabWhatsThisKey), PropertyCurrentTabWhatsThis); + } + return tabWidgetPropertyHash.value(name, PropertyTabWidgetNone); +} + +void QTabWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) { + QDesignerPropertySheet::setProperty(index, value); + return; + } + + // index-dependent + const int currentIndex = m_tabWidget->currentIndex(); + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) + return; + + switch (tabWidgetProperty) { + case PropertyCurrentTabText: + m_tabWidget->setTabText(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].text = qVariantValue<qdesigner_internal::PropertySheetStringValue>(value); + break; + case PropertyCurrentTabName: + currentWidget->setObjectName(value.toString()); + break; + case PropertyCurrentTabIcon: + m_tabWidget->setTabIcon(currentIndex, qvariant_cast<QIcon>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].icon = qVariantValue<qdesigner_internal::PropertySheetIconValue>(value); + break; + case PropertyCurrentTabToolTip: + m_tabWidget->setTabToolTip(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].tooltip = qVariantValue<qdesigner_internal::PropertySheetStringValue>(value); + break; + case PropertyCurrentTabWhatsThis: + m_tabWidget->setTabWhatsThis(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].whatsthis = qVariantValue<qdesigner_internal::PropertySheetStringValue>(value); + break; + case PropertyTabWidgetNone: + break; + } +} + +bool QTabWidgetPropertySheet::isEnabled(int index) const +{ + if (tabWidgetPropertyFromName(propertyName(index)) == PropertyTabWidgetNone) + return QDesignerPropertySheet::isEnabled(index); + return m_tabWidget->currentIndex() != -1; +} + +QVariant QTabWidgetPropertySheet::property(int index) const +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) + return QDesignerPropertySheet::property(index); + + // index-dependent + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) { + if (tabWidgetProperty == PropertyCurrentTabIcon) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + if (tabWidgetProperty == PropertyCurrentTabText) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (tabWidgetProperty == PropertyCurrentTabToolTip) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (tabWidgetProperty == PropertyCurrentTabWhatsThis) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + return QVariant(QString()); + } + + // index-dependent + switch (tabWidgetProperty) { + case PropertyCurrentTabText: + return qVariantFromValue(m_pageToData.value(currentWidget).text); + case PropertyCurrentTabName: + return currentWidget->objectName(); + case PropertyCurrentTabIcon: + return qVariantFromValue(m_pageToData.value(currentWidget).icon); + case PropertyCurrentTabToolTip: + return qVariantFromValue(m_pageToData.value(currentWidget).tooltip); + case PropertyCurrentTabWhatsThis: + return qVariantFromValue(m_pageToData.value(currentWidget).whatsthis); + case PropertyTabWidgetNone: + break; + } + return QVariant(); +} + +bool QTabWidgetPropertySheet::reset(int index) +{ + const TabWidgetProperty tabWidgetProperty = tabWidgetPropertyFromName(propertyName(index)); + if (tabWidgetProperty == PropertyTabWidgetNone) + return QDesignerPropertySheet::reset(index); + + // index-dependent + QWidget *currentWidget = m_tabWidget->currentWidget(); + if (!currentWidget) + return false; + + // index-dependent + switch (tabWidgetProperty) { + case PropertyCurrentTabName: + setProperty(index, QString()); + break; + case PropertyCurrentTabToolTip: + m_pageToData[currentWidget].tooltip = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabWhatsThis: + m_pageToData[currentWidget].whatsthis = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabText: + m_pageToData[currentWidget].text = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentTabIcon: + m_pageToData[currentWidget].icon = qdesigner_internal::PropertySheetIconValue(); + setProperty(index, QIcon()); + break; + case PropertyTabWidgetNone: + break; + } + return true; +} + +bool QTabWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + switch (tabWidgetPropertyFromName(propertyName)) { + case PropertyCurrentTabText: + case PropertyCurrentTabName: + case PropertyCurrentTabToolTip: + case PropertyCurrentTabWhatsThis: + case PropertyCurrentTabIcon: + return false; + default: + break; + } + return true; +} + +QT_END_NAMESPACE + +#include "qdesigner_tabwidget.moc" // required for MyMimeData diff --git a/tools/designer/src/lib/shared/qdesigner_tabwidget_p.h b/tools/designer/src/lib/shared/qdesigner_tabwidget_p.h new file mode 100644 index 0000000..b5af372 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_tabwidget_p.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TABWIDGET_H +#define QDESIGNER_TABWIDGET_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" + +#include <QtCore/QPointer> +#include <QtGui/QIcon> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QTabWidget; +class QTabBar; +class QMenu; +class QAction; + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +class QDESIGNER_SHARED_EXPORT QTabWidgetEventFilter : public QObject +{ + Q_OBJECT +public: + explicit QTabWidgetEventFilter(QTabWidget *parent); + ~QTabWidgetEventFilter(); + + // Install helper on QTabWidget + static void install(QTabWidget *tabWidget); + static QTabWidgetEventFilter *eventFilterOf(const QTabWidget *tabWidget); + // Convenience to add a menu on a tackedWidget + static QMenu *addTabWidgetContextMenuActions(const QTabWidget *tabWidget, QMenu *popup); + + // Add context menu and return page submenu or 0. + QMenu *addContextMenuActions(QMenu *popup); + + virtual bool eventFilter(QObject *o, QEvent *e); + + QDesignerFormWindowInterface *formWindow() const; + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + +private: + int pageFromPosition(const QPoint &pos, QRect &rect) const; + QTabBar *tabBar() const; + + QTabWidget *m_tabWidget; + mutable QPointer<QTabBar> m_cachedTabBar; + QPoint m_pressPoint; + QWidget *m_dropIndicator; + int m_dragIndex; + QWidget *m_dragPage; + QString m_dragLabel; + QIcon m_dragIcon; + bool m_mousePressed; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the page properties +class QDESIGNER_SHARED_EXPORT QTabWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QTabWidgetPropertySheet(QTabWidget *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + enum TabWidgetProperty { PropertyCurrentTabText, PropertyCurrentTabName, PropertyCurrentTabIcon, + PropertyCurrentTabToolTip, PropertyCurrentTabWhatsThis, PropertyTabWidgetNone }; + + static TabWidgetProperty tabWidgetPropertyFromName(const QString &name); + QTabWidget *m_tabWidget; + struct PageData + { + qdesigner_internal::PropertySheetStringValue text; + qdesigner_internal::PropertySheetStringValue tooltip; + qdesigner_internal::PropertySheetStringValue whatsthis; + qdesigner_internal::PropertySheetIconValue icon; + }; + QMap<QWidget *, PageData> m_pageToData; +}; + +typedef QDesignerPropertySheetFactory<QTabWidget, QTabWidgetPropertySheet> QTabWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_TABWIDGET_H diff --git a/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp new file mode 100644 index 0000000..cce0528 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_taskmenu.cpp @@ -0,0 +1,781 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::QDesignerTaskMenu +*/ + +#include "qdesigner_taskmenu_p.h" +#include "qdesigner_command_p.h" +#include "richtexteditor_p.h" +#include "plaintexteditor_p.h" +#include "stylesheeteditor_p.h" +#include "qlayout_widget_p.h" +#include "layout_p.h" +#include "spacer_widget_p.h" +#include "textpropertyeditor_p.h" +#include "promotiontaskmenu_p.h" +#include "metadatabase_p.h" +#include "scriptdialog_p.h" +#include "scriptcommand_p.h" +#include "signalslotdialog_p.h" +#include "qdesigner_membersheet_p.h" +#include "qdesigner_propertycommand_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" +#include "morphmenu_p.h" +#include "qdesigner_integration_p.h" +#include "formlayoutmenu_p.h" +#include "ui_selectsignaldialog.h" +#include "widgetfactory_p.h" +#include "abstractintrospection_p.h" +#include "widgetdatabase_p.h" + +#include <shared_enums_p.h> + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QWidget> +#include <QtGui/QMenuBar> +#include <QtGui/QMainWindow> +#include <QtGui/QStatusBar> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QVBoxLayout> +#include <QtGui/QPushButton> +#include <QtGui/QUndoStack> +#include <QtCore/QDebug> +#include <QtCore/QSignalMapper> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +static QMenuBar *findMenuBar(const QWidget *widget) +{ + const QList<QObject*> children = widget->children(); + foreach (QObject *obj, widget->children()) { + if (QMenuBar *mb = qobject_cast<QMenuBar*>(obj)) { + return mb; + } + } + + return 0; +} + +static QStatusBar *findStatusBar(const QWidget *widget) +{ + const QList<QObject*> children = widget->children(); + foreach (QObject *obj, widget->children()) { + if (QStatusBar *sb = qobject_cast<QStatusBar*>(obj)) { + return sb; + } + } + + return 0; +} + +static inline QAction *createSeparatorHelper(QObject *parent) { + QAction *rc = new QAction(parent); + rc->setSeparator(true); + return rc; +} + +static inline qdesigner_internal::QDesignerIntegration *integration(const QDesignerFormEditorInterface *core) { + return qobject_cast<qdesigner_internal::QDesignerIntegration *>(core->integration()); +} + +static QString objName(const QDesignerFormEditorInterface *core, QObject *object) { + QDesignerPropertySheetExtension *sheet + = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), object); + Q_ASSERT(sheet != 0); + + const QString objectNameProperty = QLatin1String("objectName"); + const int index = sheet->indexOf(objectNameProperty); + const qdesigner_internal::PropertySheetStringValue objectNameValue + = qVariantValue<qdesigner_internal::PropertySheetStringValue>(sheet->property(index)); + return objectNameValue.value(); +} + +enum { ApplyMinimumWidth = 0x1, ApplyMinimumHeight = 0x2, ApplyMaximumWidth = 0x4, ApplyMaximumHeight = 0x8 }; + +namespace { +// --------------- ObjectNameDialog +class ObjectNameDialog : public QDialog +{ + public: + ObjectNameDialog(QWidget *parent, const QString &oldName); + QString newObjectName() const; + + private: + qdesigner_internal::TextPropertyEditor *m_editor; +}; + +ObjectNameDialog::ObjectNameDialog(QWidget *parent, const QString &oldName) + : QDialog(parent), + m_editor( new qdesigner_internal::TextPropertyEditor(this, qdesigner_internal::TextPropertyEditor::EmbeddingNone, + qdesigner_internal::ValidationObjectName)) +{ + setWindowTitle(QCoreApplication::translate("ObjectNameDialog", "Change Object Name")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + vboxLayout->addWidget(new QLabel(QCoreApplication::translate("ObjectNameDialog", "Object Name"))); + + m_editor->setText(oldName); + m_editor->selectAll(); + m_editor->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + vboxLayout->addWidget(m_editor); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + Qt::Horizontal, this); + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + okButton->setDefault(true); + vboxLayout->addWidget(buttonBox); + + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +QString ObjectNameDialog::newObjectName() const +{ + return m_editor->text(); +} + +} + +namespace qdesigner_internal { +// -------------- QDesignerTaskMenuPrivate +class QDesignerTaskMenuPrivate { +public: + QDesignerTaskMenuPrivate(QWidget *widget, QObject *parent); + + QDesignerTaskMenu *m_q; + QPointer<QWidget> m_widget; + QAction *m_separator; + QAction *m_separator2; + QAction *m_separator3; + QAction *m_separator4; + QAction *m_separator5; + QAction *m_separator6; + QAction *m_separator7; + QAction *m_changeObjectNameAction; + QAction *m_changeToolTip; + QAction *m_changeWhatsThis; + QAction *m_changeStyleSheet; + MorphMenu *m_morphMenu; + FormLayoutMenu *m_formLayoutMenu; + + QAction *m_addMenuBar; + QAction *m_addToolBar; + QAction *m_addStatusBar; + QAction *m_removeStatusBar; + QAction *m_changeScript; + QAction *m_containerFakeMethods; + QAction *m_navigateToSlot; + PromotionTaskMenu* m_promotionTaskMenu; + QActionGroup *m_sizeActionGroup; + QAction *m_sizeActionsSubMenu; +}; + +QDesignerTaskMenuPrivate::QDesignerTaskMenuPrivate(QWidget *widget, QObject *parent) : + m_q(0), + m_widget(widget), + m_separator(createSeparatorHelper(parent)), + m_separator2(createSeparatorHelper(parent)), + m_separator3(createSeparatorHelper(parent)), + m_separator4(createSeparatorHelper(parent)), + m_separator5(createSeparatorHelper(parent)), + m_separator6(createSeparatorHelper(parent)), + m_separator7(createSeparatorHelper(parent)), + m_changeObjectNameAction(new QAction(QDesignerTaskMenu::tr("Change objectName..."), parent)), + m_changeToolTip(new QAction(QDesignerTaskMenu::tr("Change toolTip..."), parent)), + m_changeWhatsThis(new QAction(QDesignerTaskMenu::tr("Change whatsThis..."), parent)), + m_changeStyleSheet(new QAction(QDesignerTaskMenu::tr("Change styleSheet..."), parent)), + m_morphMenu(new MorphMenu(parent)), + m_formLayoutMenu(new FormLayoutMenu(parent)), + m_addMenuBar(new QAction(QDesignerTaskMenu::tr("Create Menu Bar"), parent)), + m_addToolBar(new QAction(QDesignerTaskMenu::tr("Add Tool Bar"), parent)), + m_addStatusBar(new QAction(QDesignerTaskMenu::tr("Create Status Bar"), parent)), + m_removeStatusBar(new QAction(QDesignerTaskMenu::tr("Remove Status Bar"), parent)), + m_changeScript(new QAction(QDesignerTaskMenu::tr("Change script..."), parent)), + m_containerFakeMethods(new QAction(QDesignerTaskMenu::tr("Change signals/slots..."), parent)), + m_navigateToSlot(new QAction(QDesignerTaskMenu::tr("Go to slot..."), parent)), + m_promotionTaskMenu(new PromotionTaskMenu(widget, PromotionTaskMenu::ModeManagedMultiSelection, parent)), + m_sizeActionGroup(new QActionGroup(parent)), + m_sizeActionsSubMenu(new QAction(QDesignerTaskMenu::tr("Size Constraints"), parent)) +{ + QMenu *sizeMenu = new QMenu; + m_sizeActionsSubMenu->setMenu(sizeMenu); + QAction *sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Width")); + sizeAction->setData(ApplyMinimumWidth); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Height")); + sizeAction->setData(ApplyMinimumHeight); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Minimum Size")); + sizeAction->setData(ApplyMinimumWidth|ApplyMinimumHeight); + sizeMenu->addAction(sizeAction); + + sizeMenu->addSeparator(); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Width")); + sizeAction->setData(ApplyMaximumWidth); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Height")); + sizeAction->setData(ApplyMaximumHeight); + sizeMenu->addAction(sizeAction); + + sizeAction = m_sizeActionGroup->addAction(QDesignerTaskMenu::tr("Set Maximum Size")); + sizeAction->setData(ApplyMaximumWidth|ApplyMaximumHeight); + sizeMenu->addAction(sizeAction); +} + +// --------- QDesignerTaskMenu +QDesignerTaskMenu::QDesignerTaskMenu(QWidget *widget, QObject *parent) : + QObject(parent), + d(new QDesignerTaskMenuPrivate(widget, parent)) +{ + d->m_q = this; + Q_ASSERT(qobject_cast<QDesignerFormWindowInterface*>(widget) == 0); + + connect(d->m_changeObjectNameAction, SIGNAL(triggered()), this, SLOT(changeObjectName())); + connect(d->m_changeToolTip, SIGNAL(triggered()), this, SLOT(changeToolTip())); + connect(d->m_changeWhatsThis, SIGNAL(triggered()), this, SLOT(changeWhatsThis())); + connect(d->m_changeStyleSheet, SIGNAL(triggered()), this, SLOT(changeStyleSheet())); + connect(d->m_addMenuBar, SIGNAL(triggered()), this, SLOT(createMenuBar())); + connect(d->m_addToolBar, SIGNAL(triggered()), this, SLOT(addToolBar())); + connect(d->m_addStatusBar, SIGNAL(triggered()), this, SLOT(createStatusBar())); + connect(d->m_removeStatusBar, SIGNAL(triggered()), this, SLOT(removeStatusBar())); + connect(d->m_changeScript, SIGNAL(triggered()), this, SLOT(changeScript())); + connect(d->m_containerFakeMethods, SIGNAL(triggered()), this, SLOT(containerFakeMethods())); + connect(d->m_navigateToSlot, SIGNAL(triggered()), this, SLOT(slotNavigateToSlot())); + connect(d->m_sizeActionGroup, SIGNAL(triggered(QAction*)), this, SLOT(applySize(QAction*))); +} + +QDesignerTaskMenu::~QDesignerTaskMenu() +{ + delete d; +} + +QAction *QDesignerTaskMenu::createSeparator() +{ + return createSeparatorHelper(this); +} + +QWidget *QDesignerTaskMenu::widget() const +{ + return d->m_widget; +} + +QDesignerFormWindowInterface *QDesignerTaskMenu::formWindow() const +{ + QDesignerFormWindowInterface *result = QDesignerFormWindowInterface::findFormWindow(widget()); + Q_ASSERT(result != 0); + return result; +} + +void QDesignerTaskMenu::createMenuBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast<QMainWindow*>(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + CreateMenuBarCommand *cmd = new CreateMenuBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::addToolBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast<QMainWindow*>(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + AddToolBarCommand *cmd = new AddToolBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::createStatusBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast<QMainWindow*>(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + CreateStatusBarCommand *cmd = new CreateStatusBarCommand(fw); + cmd->init(mw); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::removeStatusBar() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QMainWindow *mw = qobject_cast<QMainWindow*>(fw->mainContainer()); + if (!mw) { + // ### warning message + return; + } + + DeleteStatusBarCommand *cmd = new DeleteStatusBarCommand(fw); + cmd->init(findStatusBar(mw)); + fw->commandHistory()->push(cmd); + } +} + +QList<QAction*> QDesignerTaskMenu::taskActions() const +{ + QDesignerFormWindowInterface *formWindow = QDesignerFormWindowInterface::findFormWindow(widget()); + Q_ASSERT(formWindow); + + const bool isMainContainer = formWindow->mainContainer() == widget(); + + QList<QAction*> actions; + + if (const QMainWindow *mw = qobject_cast<const QMainWindow*>(formWindow->mainContainer())) { + if (isMainContainer || mw->centralWidget() == widget()) { + if (!findMenuBar(mw)) { + actions.append(d->m_addMenuBar); + } + + actions.append(d->m_addToolBar); + // ### create the status bar + if (!findStatusBar(mw)) + actions.append(d->m_addStatusBar); + else + actions.append(d->m_removeStatusBar); + actions.append(d->m_separator); + } + } + actions.append(d->m_changeObjectNameAction); + d->m_morphMenu->populate(d->m_widget, formWindow, actions); + d->m_formLayoutMenu->populate(d->m_widget, formWindow, actions); + actions.append(d->m_separator2); + actions.append(d->m_changeToolTip); + actions.append(d->m_changeWhatsThis); + actions.append(d->m_changeStyleSheet); + actions.append(d->m_separator6); + actions.append(d->m_sizeActionsSubMenu); + d->m_promotionTaskMenu->setMode(formWindow->isManaged(d->m_widget) ? + PromotionTaskMenu::ModeManagedMultiSelection : PromotionTaskMenu::ModeUnmanagedMultiSelection); + d->m_promotionTaskMenu->addActions(formWindow, PromotionTaskMenu::LeadingSeparator, actions); + +#ifdef WANT_SCRIPT_OPTION + if (!isMainContainer) { + actions.append(d->m_separator4); + actions.append(d->m_changeScript); + } +#endif + if (isMainContainer && !qt_extension<QDesignerLanguageExtension*>(formWindow->core()->extensionManager(), formWindow->core())) { + actions.append(d->m_separator5); + actions.append(d->m_containerFakeMethods); + } + + if (isSlotNavigationEnabled(formWindow->core())) { + actions.append(d->m_separator7); + actions.append(d->m_navigateToSlot); + } + + return actions; +} + +void QDesignerTaskMenu::changeObjectName() +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw != 0); + + const QString oldObjectName = objName(fw->core(), widget()); + + ObjectNameDialog dialog(fw, oldObjectName); + if (dialog.exec() == QDialog::Accepted) { + const QString newObjectName = dialog.newObjectName(); + if (!newObjectName.isEmpty() && newObjectName != oldObjectName ) { + const QString objectNameProperty = QLatin1String("objectName"); + PropertySheetStringValue objectNameValue; + objectNameValue.setValue(newObjectName); + setProperty(fw, CurrentWidgetMode, objectNameProperty, qVariantFromValue(objectNameValue)); + } + } +} + +void QDesignerTaskMenu::changeTextProperty(const QString &propertyName, const QString &windowTitle, PropertyMode pm, Qt::TextFormat desiredFormat) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + Q_ASSERT(d->m_widget->parentWidget() != 0); + + const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(fw->core()->extensionManager(), d->m_widget); + const int index = sheet->indexOf(propertyName); + if (index == -1) { + qDebug() << "** WARNING Invalid property" << propertyName << " passed to changeTextProperty!"; + return; + } + PropertySheetStringValue textValue = qVariantValue<PropertySheetStringValue>(sheet->property(index)); + const QString oldText = textValue.value(); + // Pop up respective dialog + bool accepted = false; + QString newText; + switch (desiredFormat) { + case Qt::PlainText: { + PlainTextEditorDialog dlg(fw->core(), fw); + if (!windowTitle.isEmpty()) + dlg.setWindowTitle(windowTitle); + dlg.setDefaultFont(d->m_widget->font()); + dlg.setText(oldText); + accepted = dlg.showDialog() == QDialog::Accepted; + newText = dlg.text(); + } + break; + default: { + RichTextEditorDialog dlg(fw->core(), fw); + if (!windowTitle.isEmpty()) + dlg.setWindowTitle(windowTitle); + dlg.setDefaultFont(d->m_widget->font()); + dlg.setText(oldText); + accepted = dlg.showDialog() == QDialog::Accepted; + newText = dlg.text(desiredFormat); + } + break; + } + // change property + if (!accepted || oldText == newText) + return; + + + textValue.setValue(newText); + setProperty(fw, pm, propertyName, qVariantFromValue(textValue)); +} + +void QDesignerTaskMenu::changeToolTip() +{ + changeTextProperty(QLatin1String("toolTip"), tr("Edit ToolTip"), MultiSelectionMode, Qt::AutoText); +} + +void QDesignerTaskMenu::changeWhatsThis() +{ + changeTextProperty(QLatin1String("whatsThis"), tr("Edit WhatsThis"), MultiSelectionMode, Qt::AutoText); +} + +void QDesignerTaskMenu::changeStyleSheet() +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + StyleSheetPropertyEditorDialog dlg(fw, fw, d->m_widget); + dlg.exec(); + } +} + +void QDesignerTaskMenu::changeScript() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + MetaDataBase *metaDataBase = qobject_cast<MetaDataBase*>(fw->core()->metaDataBase()); + if (!metaDataBase) + return; + + const MetaDataBaseItem* item = metaDataBase->metaDataBaseItem(d->m_widget); + if (!item) + return; + + const QString oldScript = item->script(); + QString newScript = oldScript; + + ScriptDialog scriptDialog(fw->core()->dialogGui(), fw); + if (!scriptDialog.editScript(newScript)) + return; + + // compile list of selected objects + ScriptCommand *scriptCommand = new ScriptCommand(fw); + if (!scriptCommand->init(applicableObjects(fw, MultiSelectionMode), newScript)) { + delete scriptCommand; + return; + } + + fw->commandHistory()->push(scriptCommand); +} + +void QDesignerTaskMenu::containerFakeMethods() +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + SignalSlotDialog::editMetaDataBase(fw, d->m_widget, fw); +} + +static QString declaredInClass(const QDesignerMetaObjectInterface *metaObject, const QString &member) +{ + // Find class whose superclass does not contain the method. + const QDesignerMetaObjectInterface *meta = metaObject; + + for (;;) { + const QDesignerMetaObjectInterface *tmpMeta = meta->superClass(); + if (tmpMeta == 0) + break; + if (tmpMeta->indexOfMethod(member) == -1) + break; + meta = tmpMeta; + } + return meta->className(); +} + +bool QDesignerTaskMenu::isSlotNavigationEnabled(const QDesignerFormEditorInterface *core) +{ + if (QDesignerIntegration *integr = integration(core)) + return integr->isSlotNavigationEnabled(); + return false; +} + +void QDesignerTaskMenu::slotNavigateToSlot() +{ + QDesignerFormEditorInterface *core = formWindow()->core(); + Q_ASSERT(core); + navigateToSlot(core, widget()); +} + +void QDesignerTaskMenu::navigateToSlot(QDesignerFormEditorInterface *core, + QObject *object, + const QString &defaultSignal) +{ + const QString objectName = objName(core, object); + QMap<QString, QMap<QString, QStringList> > classToSignalList; + + QDesignerIntegration *integr = integration(core); + + // "real" signals + if (const QDesignerMetaObjectInterface *metaObject = core->introspection()->metaObject(object)) { + const int methodCount = metaObject->methodCount(); + for (int i = 0; i < methodCount; ++i) { + const QDesignerMetaMethodInterface *metaMethod = metaObject->method(i); + if (metaMethod->methodType() == QDesignerMetaMethodInterface::Signal) { + const QString signature = metaMethod->signature(); + const QStringList parameterNames = metaMethod->parameterNames(); + classToSignalList[declaredInClass(metaObject, signature)][signature] = parameterNames; + } + } + } + + // fake signals + if (qdesigner_internal::MetaDataBase *metaDataBase + = qobject_cast<qdesigner_internal::MetaDataBase *>(core->metaDataBase())) { + qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(object); + Q_ASSERT(item); + const QStringList fakeSignals = item->fakeSignals(); + foreach (const QString &fakeSignal, fakeSignals) + classToSignalList[item->customClassName()][fakeSignal] = QStringList(); + } + + if (object->isWidgetType()) { + QWidget *widget = static_cast<QWidget *>(object); + if (WidgetDataBase *db = qobject_cast<WidgetDataBase *>(core->widgetDataBase())) { + const QString promotedClassName = promotedCustomClassName(core, widget); + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index >= 0) { + WidgetDataBaseItem* item = static_cast<WidgetDataBaseItem*>(db->item(index)); + const QStringList fakeSignals = item->fakeSignals(); + foreach (const QString &fakeSignal, fakeSignals) + classToSignalList[promotedClassName][fakeSignal] = QStringList(); + } + } + } + + Ui::SelectSignalDialog dialogUi; + QDialog selectSignalDialog(0, Qt::WindowTitleHint | Qt::WindowSystemMenuHint); + dialogUi.setupUi(&selectSignalDialog); + + QMap<QString, QMap<QString, QStringList> >::const_iterator iter(classToSignalList.constBegin()); + for (; iter != classToSignalList.constEnd(); ++iter) { + const QString className = iter.key(); + QMap<QString, QStringList> signalNames = iter.value(); + + QMap<QString, QStringList>::const_iterator itSignal(signalNames.constBegin()); + for (; itSignal != signalNames.constEnd(); ++itSignal) { + const QString signalName = itSignal.key(); + QTreeWidgetItem *row = new QTreeWidgetItem(QStringList() << signalName << className); + row->setData(0, Qt::UserRole, itSignal.value()); + dialogUi.signalList->addTopLevelItem(row); + } + } + if (dialogUi.signalList->topLevelItemCount() == 0) { + QTreeWidgetItem *row = new QTreeWidgetItem(QStringList() << tr("no signals available")); + dialogUi.signalList->addTopLevelItem(row); + dialogUi.buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false); + } else { + connect(dialogUi.signalList, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), + &selectSignalDialog, SLOT(accept())); + } + + if (defaultSignal.isEmpty()) { + dialogUi.signalList->setCurrentItem(dialogUi.signalList->topLevelItem(0)); + } else { + const QList<QTreeWidgetItem *> items = dialogUi.signalList->findItems (defaultSignal, Qt::MatchExactly, 0); + if (!items.empty()) + dialogUi.signalList->setCurrentItem(items.front()); + } + + dialogUi.signalList->resizeColumnToContents(0); + + if (selectSignalDialog.exec() == QDialog::Accepted) { + QTreeWidgetItem *selectedItem = dialogUi.signalList->selectedItems().first(); + const QString signalSignature = selectedItem->text(0); + const QStringList parameterNames = qVariantValue<QStringList>(selectedItem->data(0, Qt::UserRole)); + + // TODO: Check wether signal is connected to slot + integr->emitNavigateToSlot(objectName, signalSignature, parameterNames); + } +} + +// Add a command that takes over the value of the current geometry as +// minimum/maximum size according to the flags. +static void createSizeCommand(QDesignerFormWindowInterface *fw, QWidget *w, int flags) +{ + const QSize size = w->size(); + if (flags & (ApplyMinimumWidth|ApplyMinimumHeight)) { + QSize minimumSize = w-> minimumSize(); + if (flags & ApplyMinimumWidth) + minimumSize.setWidth(size.width()); + if (flags & ApplyMinimumHeight) + minimumSize.setHeight(size.height()); + SetPropertyCommand* cmd = new SetPropertyCommand(fw); + cmd->init(w, QLatin1String("minimumSize"), minimumSize); + fw->commandHistory()->push(cmd); + } + if (flags & (ApplyMaximumWidth|ApplyMaximumHeight)) { + QSize maximumSize = w-> maximumSize(); + if (flags & ApplyMaximumWidth) + maximumSize.setWidth(size.width()); + if (flags & ApplyMaximumHeight) + maximumSize.setHeight(size.height()); + SetPropertyCommand* cmd = new SetPropertyCommand(fw); + cmd->init(w, QLatin1String("maximumSize"), maximumSize); + fw->commandHistory()->push(cmd); + } +} + +void QDesignerTaskMenu::applySize(QAction *a) +{ + QDesignerFormWindowInterface *fw = formWindow(); + if (!fw) + return; + + const QWidgetList selection = applicableWidgets(fw, MultiSelectionMode); + if (selection.isEmpty()) + return; + + const int mask = a->data().toInt(); + const int size = selection.size(); + fw->commandHistory()->beginMacro(tr("Set size constraint on %n widget(s)", 0, size)); + for (int i = 0; i < size; i++) + createSizeCommand(fw, selection.at(i), mask); + fw->commandHistory()->endMacro(); +} + +template <class Container> + static void getApplicableObjects(const QDesignerFormWindowInterface *fw, QWidget *current, + QDesignerTaskMenu::PropertyMode pm, Container *c) +{ + // Current is always first + c->push_back(current); + if (pm == QDesignerTaskMenu::CurrentWidgetMode) + return; + QDesignerObjectInspector *designerObjectInspector = qobject_cast<QDesignerObjectInspector *>(fw->core()->objectInspector()); + if (!designerObjectInspector) + return; // Ooops, someone plugged an old-style Object Inspector + // Add managed or unmanaged selection according to current type, make current first + Selection s; + designerObjectInspector->getSelection(s); + const QWidgetList &source = fw->isManaged(current) ? s.managed : s.unmanaged; + const QWidgetList::const_iterator cend = source.constEnd(); + for ( QWidgetList::const_iterator it = source.constBegin(); it != cend; ++it) + if (*it != current) // was first + c->push_back(*it); +} + +QObjectList QDesignerTaskMenu::applicableObjects(const QDesignerFormWindowInterface *fw, PropertyMode pm) const +{ + QObjectList rc; + getApplicableObjects(fw, d->m_widget, pm, &rc); + return rc; +} + +QWidgetList QDesignerTaskMenu::applicableWidgets(const QDesignerFormWindowInterface *fw, PropertyMode pm) const +{ + QWidgetList rc; + getApplicableObjects(fw, d->m_widget, pm, &rc); + return rc; +} + +void QDesignerTaskMenu::setProperty(QDesignerFormWindowInterface *fw, PropertyMode pm, const QString &name, const QVariant &newValue) +{ + SetPropertyCommand* setPropertyCommand = new SetPropertyCommand(fw); + if (setPropertyCommand->init(applicableObjects(fw, pm), name, newValue, d->m_widget)) { + fw->commandHistory()->push(setPropertyCommand); + } else { + delete setPropertyCommand; + qDebug() << "Unable to set property " << name << '.'; + } +} + + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_taskmenu_p.h b/tools/designer/src/lib/shared/qdesigner_taskmenu_p.h new file mode 100644 index 0000000..db5807b --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_taskmenu_p.h @@ -0,0 +1,132 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TASKMENU_H +#define QDESIGNER_TASKMENU_H + +#include "shared_global_p.h" +#include "extensionfactory_p.h" +#include <QtDesigner/QDesignerTaskMenuExtension> + +#include <QtCore/QObject> +#include <QtCore/QPointer> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QWidget; +class QSignalMapper; + +namespace qdesigner_internal { +class QDesignerTaskMenuPrivate; + +class QDESIGNER_SHARED_EXPORT QDesignerTaskMenu: public QObject, public QDesignerTaskMenuExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerTaskMenuExtension) +public: + QDesignerTaskMenu(QWidget *widget, QObject *parent); + virtual ~QDesignerTaskMenu(); + + QWidget *widget() const; + + virtual QList<QAction*> taskActions() const; + + enum PropertyMode { CurrentWidgetMode, MultiSelectionMode }; + + static bool isSlotNavigationEnabled(const QDesignerFormEditorInterface *core); + static void navigateToSlot(QDesignerFormEditorInterface *core, QObject *o, + const QString &defaultSignal = QString()); + +protected: + + QDesignerFormWindowInterface *formWindow() const; + void changeTextProperty(const QString &propertyName, const QString &windowTitle, PropertyMode pm, Qt::TextFormat desiredFormat); + + QAction *createSeparator(); + + /* Retrieve the list of objects the task menu is supposed to act on. Note that a task menu can be invoked for + * an unmanaged widget [as of 4.5], in which case it must not use the cursor selection, + * but the unmanaged selection of the object inspector. */ + QObjectList applicableObjects(const QDesignerFormWindowInterface *fw, PropertyMode pm) const; + QList<QWidget *> applicableWidgets(const QDesignerFormWindowInterface *fw, PropertyMode pm) const; + + void setProperty(QDesignerFormWindowInterface *fw, PropertyMode pm, const QString &name, const QVariant &newValue); + +private slots: + void changeObjectName(); + void changeToolTip(); + void changeWhatsThis(); + void changeStyleSheet(); + void createMenuBar(); + void addToolBar(); + void createStatusBar(); + void removeStatusBar(); + void changeScript(); + void containerFakeMethods(); + void slotNavigateToSlot(); + void applySize(QAction *a); + +private: + QDesignerTaskMenuPrivate *d; +}; + +typedef ExtensionFactory<QDesignerTaskMenuExtension, QWidget, QDesignerTaskMenu> QDesignerTaskMenuFactory; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_TASKMENU_H diff --git a/tools/designer/src/lib/shared/qdesigner_toolbar.cpp b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp new file mode 100644 index 0000000..cb77801 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_toolbar.cpp @@ -0,0 +1,486 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::Sentinel +*/ + +#include "qdesigner_toolbar_p.h" +#include "qdesigner_command_p.h" +#include "actionrepository_p.h" +#include "actionprovider_p.h" +#include "qdesigner_utils_p.h" +#include "qdesigner_objectinspector_p.h" +#include "promotiontaskmenu_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerPropertyEditorInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <actionprovider_p.h> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerWidgetFactoryInterface> + +#include <QtGui/QAction> +#include <QtGui/QApplication> +#include <QtGui/QToolButton> +#include <QtGui/QToolBar> +#include <QtGui/QMenu> +#include <QtGui/qevent.h> +#include <QtGui/QApplication> +#include <QtGui/private/qtoolbarlayout_p.h> +#include <QtCore/QDebug> + +Q_DECLARE_METATYPE(QAction*) + +QT_BEGIN_NAMESPACE + +typedef QList<QAction*> ActionList; + +namespace qdesigner_internal { +// ------------------- ToolBarEventFilter +void ToolBarEventFilter::install(QToolBar *tb) +{ + ToolBarEventFilter *tf = new ToolBarEventFilter(tb); + tb->installEventFilter(tf); + tb->setAcceptDrops(true); // ### fake +} + +ToolBarEventFilter::ToolBarEventFilter(QToolBar *tb) : + QObject(tb), + m_toolBar(tb), + m_promotionTaskMenu(0) +{ +} + +ToolBarEventFilter *ToolBarEventFilter::eventFilterOf(const QToolBar *tb) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = tb->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (ToolBarEventFilter *ef = qobject_cast<ToolBarEventFilter *>(o)) + return ef; + } + return 0; +} + +bool ToolBarEventFilter::eventFilter (QObject *watched, QEvent *event) +{ + if (watched != m_toolBar) + return QObject::eventFilter (watched, event); + + switch (event->type()) { + case QEvent::ChildAdded: { + // Children should not interact with the mouse + const QChildEvent *ce = static_cast<const QChildEvent *>(event); + if (QWidget *w = qobject_cast<QWidget *>(ce->child())) { + w->setAttribute(Qt::WA_TransparentForMouseEvents, true); + w->setFocusPolicy(Qt::NoFocus); + } + } + break; + case QEvent::ContextMenu: + return handleContextMenuEvent(static_cast<QContextMenuEvent*>(event)); + case QEvent::DragEnter: + case QEvent::DragMove: + return handleDragEnterMoveEvent(static_cast<QDragMoveEvent *>(event)); + case QEvent::DragLeave: + return handleDragLeaveEvent(static_cast<QDragLeaveEvent *>(event)); + case QEvent::Drop: + return handleDropEvent(static_cast<QDropEvent *>(event)); + case QEvent::MouseButtonPress: + return handleMousePressEvent(static_cast<QMouseEvent*>(event)); + case QEvent::MouseButtonRelease: + return handleMouseReleaseEvent(static_cast<QMouseEvent*>(event)); + case QEvent::MouseMove: + return handleMouseMoveEvent(static_cast<QMouseEvent*>(event)); + default: + break; + } + return QObject::eventFilter (watched, event); +} + +ActionList ToolBarEventFilter::contextMenuActions(const QPoint &globalPos) +{ + ActionList rc; + const int index = actionIndexAt(m_toolBar, m_toolBar->mapFromGlobal(globalPos), m_toolBar->orientation()); + const ActionList actions = m_toolBar->actions(); + QAction *action = index != -1 ?actions.at(index) : 0; + QVariant itemData; + + // Insert before + if (action && index != 0 && !action->isSeparator()) { + QAction *newSeperatorAct = new QAction(tr("Insert Separator before '%1'").arg(action->objectName()), 0); + qVariantSetValue(itemData, action); + newSeperatorAct->setData(itemData); + connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator())); + rc.push_back(newSeperatorAct); + } + + // Append separator + if (actions.empty() || !actions.back()->isSeparator()) { + QAction *newSeperatorAct = new QAction(tr("Append Separator"), 0); + qVariantSetValue(itemData, static_cast<QAction*>(0)); + newSeperatorAct->setData(itemData); + connect(newSeperatorAct, SIGNAL(triggered()), this, SLOT(slotInsertSeparator())); + rc.push_back(newSeperatorAct); + } + // Promotion + if (!m_promotionTaskMenu) + m_promotionTaskMenu = new PromotionTaskMenu(m_toolBar, PromotionTaskMenu::ModeSingleWidget, this); + m_promotionTaskMenu->addActions(formWindow(), PromotionTaskMenu::LeadingSeparator|PromotionTaskMenu::TrailingSeparator, rc); + // Remove + if (action) { + QAction *a = new QAction(tr("Remove action '%1'").arg(action->objectName()), 0); + qVariantSetValue(itemData, action); + a->setData(itemData); + connect(a, SIGNAL(triggered()), this, SLOT(slotRemoveSelectedAction())); + rc.push_back(a); + } + + QAction *remove_toolbar = new QAction(tr("Remove Toolbar '%1'").arg(m_toolBar->objectName()), 0); + connect(remove_toolbar, SIGNAL(triggered()), this, SLOT(slotRemoveToolBar())); + rc.push_back(remove_toolbar); + return rc; +} + +bool ToolBarEventFilter::handleContextMenuEvent(QContextMenuEvent * event ) +{ + event->accept(); + + const QPoint globalPos = event->globalPos(); + const ActionList al = contextMenuActions(event->globalPos()); + + QMenu menu(0); + const ActionList::const_iterator acend = al.constEnd(); + for (ActionList::const_iterator it = al.constBegin(); it != acend; ++it) + menu.addAction(*it); + menu.exec(globalPos); + return true; +} + +void ToolBarEventFilter::slotRemoveSelectedAction() +{ + QAction *action = qobject_cast<QAction*>(sender()); + if (!action) + return; + + QAction *a = qvariant_cast<QAction*>(action->data()); + Q_ASSERT(a != 0); + + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + + const ActionList actions = m_toolBar->actions(); + const int pos = actions.indexOf(a); + QAction *action_before = 0; + if (pos != -1 && actions.count() > pos + 1) + action_before = actions.at(pos + 1); + + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + cmd->init(m_toolBar, a, action_before); + fw->commandHistory()->push(cmd); +} + +void ToolBarEventFilter::slotRemoveToolBar() +{ + QDesignerFormWindowInterface *fw = formWindow(); + Q_ASSERT(fw); + DeleteToolBarCommand *cmd = new DeleteToolBarCommand(fw); + cmd->init(m_toolBar); + fw->commandHistory()->push(cmd); +} + +void ToolBarEventFilter::slotInsertSeparator() +{ + QDesignerFormWindowInterface *fw = formWindow(); + QAction *theSender = qobject_cast<QAction*>(sender()); + QAction *previous = qvariant_cast<QAction *>(theSender->data()); + fw->beginCommand(tr("Insert Separator")); + QAction *action = createAction(fw, QLatin1String("separator"), true); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, previous); + fw->commandHistory()->push(cmd); + fw->endCommand(); +} + +QDesignerFormWindowInterface *ToolBarEventFilter::formWindow() const +{ + return QDesignerFormWindowInterface::findFormWindow(m_toolBar); +} + +QAction *ToolBarEventFilter::createAction(QDesignerFormWindowInterface *fw, const QString &objectName, bool separator) +{ + QAction *action = new QAction(fw); + fw->core()->widgetFactory()->initialize(action); + if (separator) + action->setSeparator(true); + + action->setObjectName(objectName); + fw->ensureUniqueObjectName(action); + + qdesigner_internal::AddActionCommand *cmd = new qdesigner_internal::AddActionCommand(fw); + cmd->init(action); + fw->commandHistory()->push(cmd); + + return action; +} + +void ToolBarEventFilter::adjustDragIndicator(const QPoint &pos) +{ + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + if (QDesignerActionProviderExtension *a = qt_extension<QDesignerActionProviderExtension*>(core->extensionManager(), m_toolBar)) + a->adjustIndicator(pos); + } +} + +void ToolBarEventFilter::hideDragIndicator() +{ + adjustDragIndicator(QPoint(-1, -1)); +} + +bool ToolBarEventFilter::handleMousePressEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || withinHandleArea(m_toolBar, event->pos())) + return false; + + if (QDesignerFormWindowInterface *fw = formWindow()) { + QDesignerFormEditorInterface *core = fw->core(); + // Keep selection in sync + fw->clearSelection(false); + if (QDesignerObjectInspector *oi = qobject_cast<QDesignerObjectInspector *>(core->objectInspector())) { + oi->clearSelection(); + oi->selectObject(m_toolBar); + } + core->propertyEditor()->setObject(m_toolBar); + } + m_startPosition = m_toolBar->mapFromGlobal(event->globalPos()); + event->accept(); + return true; +} + +bool ToolBarEventFilter::handleMouseReleaseEvent(QMouseEvent *event) +{ + if (event->button() != Qt::LeftButton || m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos())) + return false; + + // Accept the event, otherwise, form window selection will trigger + m_startPosition = QPoint(); + event->accept(); + return true; +} + +bool ToolBarEventFilter::handleMouseMoveEvent(QMouseEvent *event) +{ + if (m_startPosition.isNull() || withinHandleArea(m_toolBar, event->pos())) + return false; + + const QPoint pos = m_toolBar->mapFromGlobal(event->globalPos()); + if ((pos - m_startPosition).manhattanLength() > qApp->startDragDistance()) { + startDrag(m_startPosition, event->modifiers()); + m_startPosition = QPoint(); + event->accept(); + return true; + } + return false; +} + +bool ToolBarEventFilter::handleDragEnterMoveEvent(QDragMoveEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d) + return false; + + if (d->actionList().isEmpty()) { + event->ignore(); + hideDragIndicator(); + return true; + } + + QAction *action = d->actionList().first(); + if (!action || action->menu() || m_toolBar->actions().contains(action) || !Utils::isObjectAncestorOf(formWindow()->mainContainer(), action)) { + event->ignore(); + hideDragIndicator(); + return true; + } + + d->accept(event); + adjustDragIndicator(event->pos()); + return true; +} + +bool ToolBarEventFilter::handleDragLeaveEvent(QDragLeaveEvent *) +{ + hideDragIndicator(); + return false; +} + +bool ToolBarEventFilter::handleDropEvent(QDropEvent *event) +{ + const ActionRepositoryMimeData *d = qobject_cast<const ActionRepositoryMimeData*>(event->mimeData()); + if (!d) + return false; + + if (d->actionList().isEmpty()) { + event->ignore(); + hideDragIndicator(); + return true; + } + + QAction *action = d->actionList().first(); + + const ActionList actions = m_toolBar->actions(); + if (!action || actions.contains(action)) { + event->ignore(); + hideDragIndicator(); + return true; + } + + // Try to find action to 'insert before'. Click on action or in free area, else ignore. + QAction *beforeAction = 0; + const QPoint pos = event->pos(); + const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation()); + if (index != -1) { + beforeAction = actions.at(index); + } else { + if (!freeArea(m_toolBar).contains(pos)) { + event->ignore(); + hideDragIndicator(); + return true; + } + } + + event->acceptProposedAction(); + QDesignerFormWindowInterface *fw = formWindow(); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, beforeAction); + fw->commandHistory()->push(cmd); + hideDragIndicator(); + return true; +} + +void ToolBarEventFilter::startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers) +{ + const int index = actionIndexAt(m_toolBar, pos, m_toolBar->orientation()); + if (index == - 1) + return; + + const ActionList actions = m_toolBar->actions(); + QAction *action = actions.at(index); + QDesignerFormWindowInterface *fw = formWindow(); + + const Qt::DropAction dropAction = (modifiers & Qt::ControlModifier) ? Qt::CopyAction : Qt::MoveAction; + if (dropAction == Qt::MoveAction) { + RemoveActionFromCommand *cmd = new RemoveActionFromCommand(fw); + const int nextIndex = index + 1; + QAction *nextAction = nextIndex < actions.size() ? actions.at(nextIndex) : 0; + cmd->init(m_toolBar, action, nextAction); + fw->commandHistory()->push(cmd); + } + + QDrag *drag = new QDrag(m_toolBar); + drag->setPixmap(ActionRepositoryMimeData::actionDragPixmap( action)); + drag->setMimeData(new ActionRepositoryMimeData(action, dropAction)); + + if (drag->start(dropAction) == Qt::IgnoreAction) { + hideDragIndicator(); + if (dropAction == Qt::MoveAction) { + const ActionList currentActions = m_toolBar->actions(); + QAction *previous = 0; + if (index >= 0 && index < currentActions.size()) + previous = currentActions.at(index); + InsertActionIntoCommand *cmd = new InsertActionIntoCommand(fw); + cmd->init(m_toolBar, action, previous); + fw->commandHistory()->push(cmd); + } + } +} + +QAction *ToolBarEventFilter::actionAt(const QToolBar *tb, const QPoint &pos) +{ + const int index = actionIndexAt(tb, pos, tb->orientation()); + if (index == -1) + return 0; + return tb->actions().at(index); +} + +QRect ToolBarEventFilter::handleArea(const QToolBar *tb) +{ + const QToolBarLayout *tbl = qobject_cast<QToolBarLayout *>(tb->layout()); + Q_ASSERT(tbl); + return tbl->handleRect(); +} + +bool ToolBarEventFilter::withinHandleArea(const QToolBar *tb, const QPoint &pos) +{ + return handleArea(tb).contains(pos); +} + +// Determine the free area behind the last action. +QRect ToolBarEventFilter::freeArea(const QToolBar *tb) +{ + QRect rc = QRect(QPoint(0, 0), tb->size()); + const ActionList actionList = tb->actions(); + QRect exclusionRectangle = actionList.empty() ? handleArea(tb) : tb->actionGeometry(actionList.back()); + switch (tb->orientation()) { + case Qt::Horizontal: + switch (QApplication::layoutDirection()) { + case Qt::LeftToRight: + rc.setX(exclusionRectangle.right() + 1); + break; + case Qt::RightToLeft: + rc.setRight(exclusionRectangle.x()); + break; + } + break; + case Qt::Vertical: + rc.setY(exclusionRectangle.bottom() + 1); + break; + } + return rc; +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_toolbar_p.h b/tools/designer/src/lib/shared/qdesigner_toolbar_p.h new file mode 100644 index 0000000..fd14eb4 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_toolbar_p.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TOOLBAR_H +#define QDESIGNER_TOOLBAR_H + +#include "shared_global_p.h" + +#include <QtGui/QAction> +#include <QtGui/QToolButton> + +#include <QtCore/QList> +#include <QtCore/QPoint> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QToolBar; +class QRect; +class QAction; + +namespace qdesigner_internal { + +class PromotionTaskMenu; + +// Special event filter for tool bars in designer. +// Handles drag and drop to and from. Ensures that each +// child widget is WA_TransparentForMouseEvents to enable drag and drop. + +class QDESIGNER_SHARED_EXPORT ToolBarEventFilter : public QObject { + Q_OBJECT + +public: + static void install(QToolBar *tb); + + // Find action by position. Note that QToolBar::actionAt() will + // not work as designer sets WA_TransparentForMouseEvents on its tool bar buttons + // to be able to drag them. This function will return the dummy + // sentinel action when applied to tool bars created by designer if the position matches. + static QAction *actionAt(const QToolBar *tb, const QPoint &pos); + + static bool withinHandleArea(const QToolBar *tb, const QPoint &pos); + static QRect handleArea(const QToolBar *tb); + static QRect freeArea(const QToolBar *tb); + + // Utility to create an action + static QAction *createAction(QDesignerFormWindowInterface *fw, const QString &objectName, bool separator); + + virtual bool eventFilter (QObject *watched, QEvent *event); + + // Helper for task menu extension + QList<QAction *> contextMenuActions(const QPoint &globalPos = QPoint(-1, -1)); + + static ToolBarEventFilter *eventFilterOf(const QToolBar *tb); + +private slots: + void slotRemoveSelectedAction(); + void slotRemoveToolBar(); + void slotInsertSeparator(); + +private: + explicit ToolBarEventFilter(QToolBar *tb); + + bool handleContextMenuEvent(QContextMenuEvent * event); + bool handleDragEnterMoveEvent(QDragMoveEvent *event); + bool handleDragLeaveEvent(QDragLeaveEvent *); + bool handleDropEvent(QDropEvent *event); + bool handleMousePressEvent(QMouseEvent *event); + bool handleMouseReleaseEvent(QMouseEvent *event); + bool handleMouseMoveEvent(QMouseEvent *event); + + QDesignerFormWindowInterface *formWindow() const; + void adjustDragIndicator(const QPoint &pos); + void hideDragIndicator(); + void startDrag(const QPoint &pos, Qt::KeyboardModifiers modifiers); + bool withinHandleArea(const QPoint &pos) const; + + QToolBar *m_toolBar; + PromotionTaskMenu *m_promotionTaskMenu; + QPoint m_startPosition; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_TOOLBAR_H diff --git a/tools/designer/src/lib/shared/qdesigner_toolbox.cpp b/tools/designer/src/lib/shared/qdesigner_toolbox.cpp new file mode 100644 index 0000000..46c8ffe --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_toolbox.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_toolbox_p.h" +#include "qdesigner_command_p.h" +#include "orderdialog_p.h" +#include "promotiontaskmenu_p.h" +#include "formwindowbase_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtCore/QEvent> +#include <QtGui/QAction> +#include <QtGui/QToolBox> +#include <QtGui/QMenu> +#include <QtGui/QLayout> +#include <QtGui/QApplication> +#include <QtGui/QContextMenuEvent> +#include <QtCore/QHash> + +QT_BEGIN_NAMESPACE + +QToolBoxHelper::QToolBoxHelper(QToolBox *toolbox) : + QObject(toolbox), + m_toolbox(toolbox), + m_actionDeletePage(new QAction(tr("Delete Page"), this)), + m_actionInsertPage(new QAction(tr("Before Current Page"), this)), + m_actionInsertPageAfter(new QAction(tr("After Current Page"), this)), + m_actionChangePageOrder(new QAction(tr("Change Page Order..."), this)), + m_pagePromotionTaskMenu(new qdesigner_internal::PromotionTaskMenu(0, qdesigner_internal::PromotionTaskMenu::ModeSingleWidget, this)) +{ + connect(m_actionDeletePage, SIGNAL(triggered()), this, SLOT(removeCurrentPage())); + connect(m_actionInsertPage, SIGNAL(triggered()), this, SLOT(addPage())); + connect(m_actionInsertPageAfter, SIGNAL(triggered()), this, SLOT(addPageAfter())); + connect(m_actionChangePageOrder, SIGNAL(triggered()), this, SLOT(changeOrder())); + + m_toolbox->installEventFilter(this); +} + +void QToolBoxHelper::install(QToolBox *toolbox) +{ + new QToolBoxHelper(toolbox); +} + +bool QToolBoxHelper::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::ChildPolished: + // Install on the buttons + if (watched == m_toolbox) { + QChildEvent *ce = static_cast<QChildEvent *>(event); + if (!qstrcmp(ce->child()->metaObject()->className(), "QToolBoxButton")) + ce->child()->installEventFilter(this); + } + break; + case QEvent::ContextMenu: + if (watched != m_toolbox) { + // An action invoked from the passive interactor (ToolBox button) might + // cause its deletion within its event handler, triggering a warning. Re-post + // the event to the toolbox. + QContextMenuEvent *current = static_cast<QContextMenuEvent *>(event); + QContextMenuEvent *copy = new QContextMenuEvent(current->reason(), current->pos(), current-> globalPos(), current->modifiers()); + QApplication::postEvent(m_toolbox, copy); + current->accept(); + return true; + } + break; + case QEvent::MouseButtonRelease: + if (watched != m_toolbox) + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + fw->clearSelection(); + fw->selectWidget(m_toolbox, true); + } + break; + default: + break; + } + return QObject::eventFilter(watched, event); +} + +QToolBoxHelper *QToolBoxHelper::helperOf(const QToolBox *toolbox) +{ + // Look for 1st order children only..otherwise, we might get filters of nested widgets + const QObjectList children = toolbox->children(); + const QObjectList::const_iterator cend = children.constEnd(); + for (QObjectList::const_iterator it = children.constBegin(); it != cend; ++it) { + QObject *o = *it; + if (!o->isWidgetType()) + if (QToolBoxHelper *h = qobject_cast<QToolBoxHelper *>(o)) + return h; + } + return 0; +} + +QMenu *QToolBoxHelper::addToolBoxContextMenuActions(const QToolBox *toolbox, QMenu *popup) +{ + QToolBoxHelper *helper = helperOf(toolbox); + if (!helper) + return 0; + return helper->addContextMenuActions(popup); +} + +void QToolBoxHelper::removeCurrentPage() +{ + if (m_toolbox->currentIndex() == -1 || !m_toolbox->widget(m_toolbox->currentIndex())) + return; + + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::DeleteToolBoxPageCommand *cmd = new qdesigner_internal::DeleteToolBoxPageCommand(fw); + cmd->init(m_toolbox); + fw->commandHistory()->push(cmd); + } +} + +void QToolBoxHelper::addPage() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::AddToolBoxPageCommand *cmd = new qdesigner_internal::AddToolBoxPageCommand(fw); + cmd->init(m_toolbox, qdesigner_internal::AddToolBoxPageCommand::InsertBefore); + fw->commandHistory()->push(cmd); + } +} + +void QToolBoxHelper::changeOrder() +{ + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox); + + if (!fw) + return; + + const QWidgetList oldPages = qdesigner_internal::OrderDialog::pagesOfContainer(fw->core(), m_toolbox); + const int pageCount = oldPages.size(); + if (pageCount < 2) + return; + + qdesigner_internal::OrderDialog dlg(fw); + dlg.setPageList(oldPages); + if (dlg.exec() == QDialog::Rejected) + return; + + const QWidgetList newPages = dlg.pageList(); + if (newPages == oldPages) + return; + + fw->beginCommand(tr("Change Page Order")); + for(int i=0; i < pageCount; ++i) { + if (newPages.at(i) == m_toolbox->widget(i)) + continue; + qdesigner_internal::MoveToolBoxPageCommand *cmd = new qdesigner_internal::MoveToolBoxPageCommand(fw); + cmd->init(m_toolbox, newPages.at(i), i); + fw->commandHistory()->push(cmd); + } + fw->endCommand(); +} + +void QToolBoxHelper::addPageAfter() +{ + if (QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(m_toolbox)) { + qdesigner_internal::AddToolBoxPageCommand *cmd = new qdesigner_internal::AddToolBoxPageCommand(fw); + cmd->init(m_toolbox, qdesigner_internal::AddToolBoxPageCommand::InsertAfter); + fw->commandHistory()->push(cmd); + } +} + +QPalette::ColorRole QToolBoxHelper::currentItemBackgroundRole() const +{ + const QWidget *w = m_toolbox->widget(0); + if (!w) + return QPalette::Window; + return w->backgroundRole(); +} + +void QToolBoxHelper::setCurrentItemBackgroundRole(QPalette::ColorRole role) +{ + const int count = m_toolbox->count(); + for (int i = 0; i < count; ++i) { + QWidget *w = m_toolbox->widget(i); + w->setBackgroundRole(role); + w->update(); + } +} + +QMenu *QToolBoxHelper::addContextMenuActions(QMenu *popup) const +{ + QMenu *pageMenu = 0; + const int count = m_toolbox->count(); + m_actionDeletePage->setEnabled(count > 1); + if (count) { + const QString pageSubMenuLabel = tr("Page %1 of %2").arg(m_toolbox->currentIndex() + 1).arg(count); + pageMenu = popup->addMenu(pageSubMenuLabel); + + pageMenu->addAction(m_actionDeletePage); + // Set up promotion menu for current widget. + if (QWidget *page = m_toolbox->currentWidget ()) { + m_pagePromotionTaskMenu->setWidget(page); + m_pagePromotionTaskMenu->addActions(QDesignerFormWindowInterface::findFormWindow(m_toolbox), + qdesigner_internal::PromotionTaskMenu::SuppressGlobalEdit, + pageMenu); + } + } + QMenu *insertPageMenu = popup->addMenu(tr("Insert Page")); + insertPageMenu->addAction(m_actionInsertPageAfter); + insertPageMenu->addAction(m_actionInsertPage); + if (count > 1) { + popup->addAction(m_actionChangePageOrder); + } + popup->addSeparator(); + return pageMenu; +} + +// -------- QToolBoxWidgetPropertySheet + +static const char *currentItemTextKey = "currentItemText"; +static const char *currentItemNameKey = "currentItemName"; +static const char *currentItemIconKey = "currentItemIcon"; +static const char *currentItemToolTipKey = "currentItemToolTip"; +static const char *tabSpacingKey = "tabSpacing"; + +enum { tabSpacingDefault = -1 }; + +QToolBoxWidgetPropertySheet::QToolBoxWidgetPropertySheet(QToolBox *object, QObject *parent) : + QDesignerPropertySheet(object, parent), + m_toolBox(object) +{ + createFakeProperty(QLatin1String(currentItemTextKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(currentItemNameKey), QString()); + createFakeProperty(QLatin1String(currentItemIconKey), qVariantFromValue(qdesigner_internal::PropertySheetIconValue())); + if (formWindowBase()) + formWindowBase()->addReloadableProperty(this, indexOf(QLatin1String(currentItemIconKey))); + createFakeProperty(QLatin1String(currentItemToolTipKey), qVariantFromValue(qdesigner_internal::PropertySheetStringValue())); + createFakeProperty(QLatin1String(tabSpacingKey), QVariant(tabSpacingDefault)); +} + +QToolBoxWidgetPropertySheet::ToolBoxProperty QToolBoxWidgetPropertySheet::toolBoxPropertyFromName(const QString &name) +{ + typedef QHash<QString, ToolBoxProperty> ToolBoxPropertyHash; + static ToolBoxPropertyHash toolBoxPropertyHash; + if (toolBoxPropertyHash.empty()) { + toolBoxPropertyHash.insert(QLatin1String(currentItemTextKey), PropertyCurrentItemText); + toolBoxPropertyHash.insert(QLatin1String(currentItemNameKey), PropertyCurrentItemName); + toolBoxPropertyHash.insert(QLatin1String(currentItemIconKey), PropertyCurrentItemIcon); + toolBoxPropertyHash.insert(QLatin1String(currentItemToolTipKey), PropertyCurrentItemToolTip); + toolBoxPropertyHash.insert(QLatin1String(tabSpacingKey), PropertyTabSpacing); + } + return toolBoxPropertyHash.value(name, PropertyToolBoxNone); +} + +void QToolBoxWidgetPropertySheet::setProperty(int index, const QVariant &value) +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + m_toolBox->layout()->setSpacing(value.toInt()); + return; + case PropertyToolBoxNone: + QDesignerPropertySheet::setProperty(index, value); + return; + default: + break; + } + // index-dependent + const int currentIndex = m_toolBox->currentIndex(); + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) + return; + + switch (toolBoxProperty) { + case PropertyCurrentItemText: + m_toolBox->setItemText(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].text = qVariantValue<qdesigner_internal::PropertySheetStringValue>(value); + break; + case PropertyCurrentItemName: + currentWidget->setObjectName(value.toString()); + break; + case PropertyCurrentItemIcon: + m_toolBox->setItemIcon(currentIndex, qvariant_cast<QIcon>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].icon = qVariantValue<qdesigner_internal::PropertySheetIconValue>(value); + break; + case PropertyCurrentItemToolTip: + m_toolBox->setItemToolTip(currentIndex, qvariant_cast<QString>(resolvePropertyValue(index, value))); + m_pageToData[currentWidget].tooltip = qVariantValue<qdesigner_internal::PropertySheetStringValue>(value); + break; + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } +} + +bool QToolBoxWidgetPropertySheet::isEnabled(int index) const +{ + switch (toolBoxPropertyFromName(propertyName(index))) { + case PropertyToolBoxNone: // independent of index + case PropertyTabSpacing: + return QDesignerPropertySheet::isEnabled(index); + default: + break; + } + return m_toolBox->currentIndex() != -1; +} + +QVariant QToolBoxWidgetPropertySheet::property(int index) const +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + return m_toolBox->layout()->spacing(); + case PropertyToolBoxNone: + return QDesignerPropertySheet::property(index); + default: + break; + } + // index-dependent + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) { + if (toolBoxProperty == PropertyCurrentItemIcon) + return qVariantFromValue(qdesigner_internal::PropertySheetIconValue()); + if (toolBoxProperty == PropertyCurrentItemText) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + if (toolBoxProperty == PropertyCurrentItemToolTip) + return qVariantFromValue(qdesigner_internal::PropertySheetStringValue()); + return QVariant(QString()); + } + + // index-dependent + switch (toolBoxProperty) { + case PropertyCurrentItemText: + return qVariantFromValue(m_pageToData.value(currentWidget).text); + case PropertyCurrentItemName: + return currentWidget->objectName(); + case PropertyCurrentItemIcon: + return qVariantFromValue(m_pageToData.value(currentWidget).icon); + case PropertyCurrentItemToolTip: + return qVariantFromValue(m_pageToData.value(currentWidget).tooltip); + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } + return QVariant(); +} + +bool QToolBoxWidgetPropertySheet::reset(int index) +{ + const ToolBoxProperty toolBoxProperty = toolBoxPropertyFromName(propertyName(index)); + // independent of index + switch (toolBoxProperty) { + case PropertyTabSpacing: + setProperty(index, QVariant(tabSpacingDefault)); + return true; + case PropertyToolBoxNone: + return QDesignerPropertySheet::reset(index); + default: + break; + } + // index-dependent + QWidget *currentWidget = m_toolBox->currentWidget(); + if (!currentWidget) + return false; + + // index-dependent + switch (toolBoxProperty) { + case PropertyCurrentItemName: + setProperty(index, QString()); + break; + case PropertyCurrentItemToolTip: + m_pageToData[currentWidget].tooltip = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentItemText: + m_pageToData[currentWidget].text = qdesigner_internal::PropertySheetStringValue(); + setProperty(index, QString()); + break; + case PropertyCurrentItemIcon: + m_pageToData[currentWidget].icon = qdesigner_internal::PropertySheetIconValue(); + setProperty(index, QIcon()); + break; + case PropertyTabSpacing: + case PropertyToolBoxNone: + break; + } + return true; +} + +bool QToolBoxWidgetPropertySheet::checkProperty(const QString &propertyName) +{ + switch (toolBoxPropertyFromName(propertyName)) { + case PropertyCurrentItemText: + case PropertyCurrentItemName: + case PropertyCurrentItemToolTip: + case PropertyCurrentItemIcon: + return false; + default: + break; + } + return true; +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_toolbox_p.h b/tools/designer/src/lib/shared/qdesigner_toolbox_p.h new file mode 100644 index 0000000..fdd738a --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_toolbox_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_TOOLBOX_H +#define QDESIGNER_TOOLBOX_H + +#include "shared_global_p.h" +#include "qdesigner_propertysheet_p.h" +#include "qdesigner_utils_p.h" +#include <QtGui/QPalette> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + class PromotionTaskMenu; +} + +class QToolBox; + +class QAction; +class QMenu; + +class QDESIGNER_SHARED_EXPORT QToolBoxHelper : public QObject +{ + Q_OBJECT + + explicit QToolBoxHelper(QToolBox *toolbox); +public: + // Install helper on QToolBox + static void install(QToolBox *toolbox); + static QToolBoxHelper *helperOf(const QToolBox *toolbox); + // Convenience to add a menu on a toolbox + static QMenu *addToolBoxContextMenuActions(const QToolBox *toolbox, QMenu *popup); + + QPalette::ColorRole currentItemBackgroundRole() const; + void setCurrentItemBackgroundRole(QPalette::ColorRole role); + + bool eventFilter(QObject *watched, QEvent *event); + // Add context menu and return page submenu or 0. + + QMenu *addContextMenuActions(QMenu *popup) const; + +private slots: + void removeCurrentPage(); + void addPage(); + void addPageAfter(); + void changeOrder(); + +private: + QToolBox *m_toolbox; + QAction *m_actionDeletePage; + QAction *m_actionInsertPage; + QAction *m_actionInsertPageAfter; + QAction *m_actionChangePageOrder; + qdesigner_internal::PromotionTaskMenu* m_pagePromotionTaskMenu; +}; + +// PropertySheet to handle the page properties +class QDESIGNER_SHARED_EXPORT QToolBoxWidgetPropertySheet : public QDesignerPropertySheet { +public: + explicit QToolBoxWidgetPropertySheet(QToolBox *object, QObject *parent = 0); + + virtual void setProperty(int index, const QVariant &value); + virtual QVariant property(int index) const; + virtual bool reset(int index); + virtual bool isEnabled(int index) const; + + // Check whether the property is to be saved. Returns false for the page + // properties (as the property sheet has no concept of 'stored') + static bool checkProperty(const QString &propertyName); + +private: + enum ToolBoxProperty { PropertyCurrentItemText, PropertyCurrentItemName, PropertyCurrentItemIcon, + PropertyCurrentItemToolTip, PropertyTabSpacing, PropertyToolBoxNone }; + + static ToolBoxProperty toolBoxPropertyFromName(const QString &name); + QToolBox *m_toolBox; + struct PageData + { + qdesigner_internal::PropertySheetStringValue text; + qdesigner_internal::PropertySheetStringValue tooltip; + qdesigner_internal::PropertySheetIconValue icon; + }; + QMap<QWidget *, PageData> m_pageToData; +}; + +typedef QDesignerPropertySheetFactory<QToolBox, QToolBoxWidgetPropertySheet> QToolBoxWidgetPropertySheetFactory; + +QT_END_NAMESPACE + +#endif // QDESIGNER_TOOLBOX_H diff --git a/tools/designer/src/lib/shared/qdesigner_utils.cpp b/tools/designer/src/lib/shared/qdesigner_utils.cpp new file mode 100644 index 0000000..d2f092e --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_utils.cpp @@ -0,0 +1,734 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_utils_p.h" +#include "qdesigner_propertycommand_p.h" +#include "abstractformbuilder.h" +#include "formwindowbase_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerIconCacheInterface> +#include <QtDesigner/QDesignerResourceBrowserInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerTaskMenuExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QIcon> +#include <QtGui/QPixmap> +#include <QtCore/QDir> + +#include <QtGui/QApplication> +#include <QtCore/QProcess> +#include <QtCore/QLibraryInfo> +#include <QtCore/QDebug> +#include <QtCore/QQueue> +#include <QtGui/QListWidget> +#include <QtGui/QTreeWidget> +#include <QtGui/QTableWidget> +#include <QtGui/QComboBox> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal +{ + QDESIGNER_SHARED_EXPORT void designerWarning(const QString &message) + { + qWarning("Designer: %s", qPrintable(message)); + } + + void reloadTreeItem(DesignerIconCache *iconCache, QTreeWidgetItem *item) + { + if (!item) + return; + + for (int c = 0; c < item->columnCount(); c++) { + const QVariant v = item->data(c, Qt::DecorationPropertyRole); + if (qVariantCanConvert<PropertySheetIconValue>(v)) + item->setIcon(c, iconCache->icon(qVariantValue<PropertySheetIconValue>(v))); + } + } + + void reloadListItem(DesignerIconCache *iconCache, QListWidgetItem *item) + { + if (!item) + return; + + const QVariant v = item->data(Qt::DecorationPropertyRole); + if (qVariantCanConvert<PropertySheetIconValue>(v)) + item->setIcon(iconCache->icon(qVariantValue<PropertySheetIconValue>(v))); + } + + void reloadTableItem(DesignerIconCache *iconCache, QTableWidgetItem *item) + { + if (!item) + return; + + const QVariant v = item->data(Qt::DecorationPropertyRole); + if (qVariantCanConvert<PropertySheetIconValue>(v)) + item->setIcon(iconCache->icon(qVariantValue<PropertySheetIconValue>(v))); + } + + void reloadIconResources(DesignerIconCache *iconCache, QObject *object) + { + if (QListWidget *listWidget = qobject_cast<QListWidget *>(object)) { + for (int i = 0; i < listWidget->count(); i++) + reloadListItem(iconCache, listWidget->item(i)); + } else if (QComboBox *comboBox = qobject_cast<QComboBox *>(object)) { + for (int i = 0; i < comboBox->count(); i++) { + const QVariant v = comboBox->itemData(i, Qt::DecorationPropertyRole); + if (qVariantCanConvert<PropertySheetIconValue>(v)) { + QIcon icon = iconCache->icon(qVariantValue<PropertySheetIconValue>(v)); + comboBox->setItemIcon(i, icon); + comboBox->setItemData(i, icon); + } + } + } else if (QTreeWidget *treeWidget = qobject_cast<QTreeWidget *>(object)) { + reloadTreeItem(iconCache, treeWidget->headerItem()); + QQueue<QTreeWidgetItem *> itemsQueue; + for (int i = 0; i < treeWidget->topLevelItemCount(); i++) + itemsQueue.enqueue(treeWidget->topLevelItem(i)); + while (!itemsQueue.isEmpty()) { + QTreeWidgetItem *item = itemsQueue.dequeue(); + for (int i = 0; i < item->childCount(); i++) + itemsQueue.enqueue(item->child(i)); + reloadTreeItem(iconCache, item); + } + } else if (QTableWidget *tableWidget = qobject_cast<QTableWidget *>(object)) { + const int columnCount = tableWidget->columnCount(); + const int rowCount = tableWidget->rowCount(); + for (int c = 0; c < columnCount; c++) + reloadTableItem(iconCache, tableWidget->horizontalHeaderItem(c)); + for (int r = 0; r < rowCount; r++) + reloadTableItem(iconCache, tableWidget->verticalHeaderItem(r)); + for (int c = 0; c < columnCount; c++) + for (int r = 0; r < rowCount; r++) + reloadTableItem(iconCache, tableWidget->item(r, c)); + } + } + + // ------------- DesignerMetaEnum + DesignerMetaEnum::DesignerMetaEnum(const QString &name, const QString &scope, const QString &separator) : + MetaEnum<int>(name, scope, separator) + { + } + + + QString DesignerMetaEnum::toString(int value, SerializationMode sm, bool *ok) const + { + // find value + bool valueOk; + const QString item = valueToKey(value, &valueOk); + if (ok) + *ok = valueOk; + + if (!valueOk || sm == NameOnly) + return item; + + QString qualifiedItem; + appendQualifiedName(item, qualifiedItem); + return qualifiedItem; + } + + QString DesignerMetaEnum::messageToStringFailed(int value) const + { + return QCoreApplication::translate("DesignerMetaEnum", "%1 is not a valid enumeration value of '%2'.").arg(value).arg(name()); + } + + QString DesignerMetaEnum::messageParseFailed(const QString &s) const + { + return QCoreApplication::translate("DesignerMetaEnum", "'%1' could not be converted to an enumeration value of type '%2'.").arg(s).arg(name()); + } + // -------------- DesignerMetaFlags + DesignerMetaFlags::DesignerMetaFlags(const QString &name, const QString &scope, const QString &separator) : + MetaEnum<uint>(name, scope, separator) + { + } + + QStringList DesignerMetaFlags::flags(int ivalue) const + { + typedef MetaEnum<uint>::KeyToValueMap::const_iterator KeyToValueMapIterator; + QStringList rc; + const uint v = static_cast<uint>(ivalue); + const KeyToValueMapIterator cend = keyToValueMap().constEnd(); + for (KeyToValueMapIterator it = keyToValueMap().constBegin();it != cend; ++it ) { + const uint itemValue = it.value(); + // Check for equality first as flag values can be 0 or -1, too. Takes preference over a bitwise flag + if (v == itemValue) { + rc.clear(); + rc.push_back(it.key()); + return rc; + } + // Do not add 0-flags (None-flags) + if (itemValue) + if ((v & itemValue) == itemValue) + rc.push_back(it.key()); + } + return rc; + } + + + QString DesignerMetaFlags::toString(int value, SerializationMode sm) const + { + const QStringList flagIds = flags(value); + if (flagIds.empty()) + return QString(); + + const QChar delimiter = QLatin1Char('|'); + QString rc; + const QStringList::const_iterator cend = flagIds.constEnd(); + for (QStringList::const_iterator it = flagIds.constBegin(); it != cend; ++it) { + if (!rc.isEmpty()) + rc += delimiter ; + if (sm == FullyQualified) + appendQualifiedName(*it, rc); + else + rc += *it; + } + return rc; + } + + + int DesignerMetaFlags::parseFlags(const QString &s, bool *ok) const + { + if (s.isEmpty()) { + if (ok) + *ok = true; + return 0; + } + uint flags = 0; + bool valueOk = true; + QStringList keys = s.split(QString(QLatin1Char('|'))); + const QStringList::iterator cend = keys.end(); + for (QStringList::iterator it = keys.begin(); it != cend; ++it) { + const uint flagValue = keyToValue(*it, &valueOk); + if (!valueOk) { + flags = 0; + break; + } + flags |= flagValue; + } + if (ok) + *ok = valueOk; + return static_cast<int>(flags); + } + + QString DesignerMetaFlags::messageParseFailed(const QString &s) const + { + return QCoreApplication::translate("DesignerMetaFlags", "'%1' could not be converted to a flag value of type '%2'.").arg(s).arg(name()); + } + + // ---------- PropertySheetEnumValue + + PropertySheetEnumValue::PropertySheetEnumValue(int v, const DesignerMetaEnum &me) : + value(v), + metaEnum(me) + { + } + PropertySheetEnumValue::PropertySheetEnumValue() : + value(0) + { + } + + // ---------------- PropertySheetFlagValue + PropertySheetFlagValue::PropertySheetFlagValue(int v, const DesignerMetaFlags &mf) : + value(v), + metaFlags(mf) + { + } + + PropertySheetFlagValue::PropertySheetFlagValue() : + value(0) + { + } + + // ---------------- PropertySheetPixmapValue + PropertySheetPixmapValue::PropertySheetPixmapValue(const QString &path) : m_path(path) + { + } + + PropertySheetPixmapValue::PropertySheetPixmapValue() + { + } + + PropertySheetPixmapValue::PixmapSource PropertySheetPixmapValue::getPixmapSource(QDesignerFormEditorInterface *core, const QString & path) + { + if (const QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension *>(core->extensionManager(), core)) + return lang->isLanguageResource(path) ? LanguageResourcePixmap : FilePixmap; + return path.startsWith(QLatin1Char(':')) ? ResourcePixmap : FilePixmap; + } + + int PropertySheetPixmapValue::compare(const PropertySheetPixmapValue &other) const + { + return m_path.compare(other.m_path); + } + + QString PropertySheetPixmapValue::path() const + { + return m_path; + } + + void PropertySheetPixmapValue::setPath(const QString &path) + { + if (m_path == path) + return; + m_path = path; + } + + // ---------- PropertySheetIconValue + PropertySheetIconValue::PropertySheetIconValue(const PropertySheetPixmapValue &pixmap) + { + setPixmap(QIcon::Normal, QIcon::Off, pixmap); + } + + PropertySheetIconValue::PropertySheetIconValue() + { + } + + bool PropertySheetIconValue::equals(const PropertySheetIconValue &rhs) const + { + return m_paths == rhs.m_paths; + } + + bool PropertySheetIconValue::operator<(const PropertySheetIconValue &other) const + { + QMapIterator<ModeStateKey, PropertySheetPixmapValue> itThis(m_paths); + QMapIterator<ModeStateKey, PropertySheetPixmapValue> itOther(other.m_paths); + while (itThis.hasNext() && itOther.hasNext()) { + const ModeStateKey thisPair = itThis.next().key(); + const ModeStateKey otherPair = itOther.next().key(); + if (thisPair < otherPair) + return true; + else if (otherPair < thisPair) + return false; + const int crc = itThis.value().compare(itOther.value()); + if (crc < 0) + return true; + if (crc > 0) + return false; + } + if (itOther.hasNext()) + return true; + return false; + } + + PropertySheetPixmapValue PropertySheetIconValue::pixmap(QIcon::Mode mode, QIcon::State state) const + { + const ModeStateKey pair = qMakePair(mode, state); + return m_paths.value(pair); + } + + void PropertySheetIconValue::setPixmap(QIcon::Mode mode, QIcon::State state, const PropertySheetPixmapValue &pixmap) + { + const ModeStateKey pair = qMakePair(mode, state); + if (pixmap.path().isEmpty()) + m_paths.remove(pair); + else + m_paths.insert(pair, pixmap); + } + + QPixmap DesignerPixmapCache::pixmap(const PropertySheetPixmapValue &value) const + { + QMap<PropertySheetPixmapValue, QPixmap>::const_iterator it = m_cache.constFind(value); + if (it != m_cache.constEnd()) + return it.value(); + + QPixmap pix = QPixmap(value.path()); + m_cache.insert(value, pix); + return pix; + } + + void DesignerPixmapCache::clear() + { + m_cache.clear(); + } + + DesignerPixmapCache::DesignerPixmapCache(QObject *parent) + : QObject(parent) + { + } + + QIcon DesignerIconCache::icon(const PropertySheetIconValue &value) const + { + QMap<PropertySheetIconValue, QIcon>::const_iterator it = m_cache.constFind(value); + if (it != m_cache.constEnd()) + return it.value(); + + QIcon icon; + QMap<QPair<QIcon::Mode, QIcon::State>, PropertySheetPixmapValue> paths = value.paths(); + QMapIterator<QPair<QIcon::Mode, QIcon::State>, PropertySheetPixmapValue> itPath(paths); + while (itPath.hasNext()) { + QPair<QIcon::Mode, QIcon::State> pair = itPath.next().key(); + icon.addPixmap(m_pixmapCache->pixmap(itPath.value()), pair.first, pair.second); + } + m_cache.insert(value, icon); + return icon; + } + + void DesignerIconCache::clear() + { + m_cache.clear(); + } + + DesignerIconCache::DesignerIconCache(DesignerPixmapCache *pixmapCache, QObject *parent) + : QObject(parent), + m_pixmapCache(pixmapCache) + { + + } + + PropertySheetStringValue::PropertySheetStringValue(const QString &value, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(value), m_translatable(translatable), m_disambiguation(disambiguation), m_comment(comment) + { } + + QString PropertySheetStringValue::value() const + { + return m_value; + } + + void PropertySheetStringValue::setValue(const QString &value) + { + m_value = value; + } + + bool PropertySheetStringValue::translatable() const + { + return m_translatable; + } + + void PropertySheetStringValue::setTranslatable(bool translatable) + { + m_translatable = translatable; + } + + QString PropertySheetStringValue::disambiguation() const + { + return m_disambiguation; + } + + void PropertySheetStringValue::setDisambiguation(const QString &disambiguation) + { + m_disambiguation = disambiguation; + } + + QString PropertySheetStringValue::comment() const + { + return m_comment; + } + + void PropertySheetStringValue::setComment(const QString &comment) + { + m_comment = comment; + } + + bool PropertySheetStringValue::equals(const PropertySheetStringValue &rhs) const + { + return (m_value == rhs.m_value) && (m_translatable == rhs.m_translatable) + && (m_disambiguation == rhs.m_disambiguation) && (m_comment == rhs.m_comment); + } + + PropertySheetKeySequenceValue::PropertySheetKeySequenceValue(const QKeySequence &value, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(value), + m_standardKey(QKeySequence::UnknownKey), + m_translatable(translatable), + m_disambiguation(disambiguation), + m_comment(comment) + { } + + PropertySheetKeySequenceValue::PropertySheetKeySequenceValue(const QKeySequence::StandardKey &standardKey, + bool translatable, const QString &disambiguation, const QString &comment) + : m_value(QKeySequence(standardKey)), + m_standardKey(standardKey), + m_translatable(translatable), + m_disambiguation(disambiguation), + m_comment(comment) + { } + + QKeySequence PropertySheetKeySequenceValue::value() const + { + return m_value; + } + + void PropertySheetKeySequenceValue::setValue(const QKeySequence &value) + { + m_value = value; + m_standardKey = QKeySequence::UnknownKey; + } + + QKeySequence::StandardKey PropertySheetKeySequenceValue::standardKey() const + { + return m_standardKey; + } + + void PropertySheetKeySequenceValue::setStandardKey(const QKeySequence::StandardKey &standardKey) + { + m_value = QKeySequence(standardKey); + m_standardKey = standardKey; + } + + bool PropertySheetKeySequenceValue::isStandardKey() const + { + return m_standardKey != QKeySequence::UnknownKey; + } + + QString PropertySheetKeySequenceValue::comment() const + { + return m_comment; + } + + void PropertySheetKeySequenceValue::setComment(const QString &comment) + { + m_comment = comment; + } + + QString PropertySheetKeySequenceValue::disambiguation() const + { + return m_disambiguation; + } + + void PropertySheetKeySequenceValue::setDisambiguation(const QString &disambiguation) + { + m_disambiguation = disambiguation; + } + + bool PropertySheetKeySequenceValue::translatable() const + { + return m_translatable; + } + + void PropertySheetKeySequenceValue::setTranslatable(bool translatable) + { + m_translatable = translatable; + } + + bool PropertySheetKeySequenceValue::equals(const PropertySheetKeySequenceValue &rhs) const + { + return (m_value == rhs.m_value) && (m_standardKey == rhs.m_standardKey) + && (m_translatable == rhs.m_translatable) && (m_disambiguation == rhs.m_disambiguation) && (m_comment == rhs.m_comment); + } + + class StateMap + { + public: + StateMap() + { + m_stateToFlag.insert(qMakePair(QIcon::Normal, QIcon::Off), 0x01); + m_stateToFlag.insert(qMakePair(QIcon::Normal, QIcon::On), 0x02); + m_stateToFlag.insert(qMakePair(QIcon::Disabled, QIcon::Off), 0x04); + m_stateToFlag.insert(qMakePair(QIcon::Disabled, QIcon::On), 0x08); + m_stateToFlag.insert(qMakePair(QIcon::Active, QIcon::Off), 0x10); + m_stateToFlag.insert(qMakePair(QIcon::Active, QIcon::On), 0x20); + m_stateToFlag.insert(qMakePair(QIcon::Selected, QIcon::Off), 0x40); + m_stateToFlag.insert(qMakePair(QIcon::Selected, QIcon::On), 0x80); + + m_flagToState.insert(0x01, qMakePair(QIcon::Normal, QIcon::Off)); + m_flagToState.insert(0x02, qMakePair(QIcon::Normal, QIcon::On)); + m_flagToState.insert(0x04, qMakePair(QIcon::Disabled, QIcon::Off)); + m_flagToState.insert(0x08, qMakePair(QIcon::Disabled, QIcon::On)); + m_flagToState.insert(0x10, qMakePair(QIcon::Active, QIcon::Off)); + m_flagToState.insert(0x20, qMakePair(QIcon::Active, QIcon::On)); + m_flagToState.insert(0x40, qMakePair(QIcon::Selected, QIcon::Off)); + m_flagToState.insert(0x80, qMakePair(QIcon::Selected, QIcon::On)); + } + uint flag(const QPair<QIcon::Mode, QIcon::State> &pair) const + { + return m_stateToFlag.value(pair); + } + QPair<QIcon::Mode, QIcon::State> state(uint flag) const + { + return m_flagToState.value(flag); + } + private: + QMap<QPair<QIcon::Mode, QIcon::State>, uint > m_stateToFlag; + QMap<uint, QPair<QIcon::Mode, QIcon::State> > m_flagToState; + }; + + Q_GLOBAL_STATIC(StateMap, stateMap) + + uint PropertySheetIconValue::mask() const + { + uint flags = 0; + QMapIterator<ModeStateKey, PropertySheetPixmapValue> itPath(m_paths); + while (itPath.hasNext()) + flags |= stateMap()->flag(itPath.next().key()); + return flags; + } + + uint PropertySheetIconValue::compare(const PropertySheetIconValue &other) const + { + uint diffMask = mask() | other.mask(); + for (int i = 0; i < 8; i++) { + uint flag = 1 << i; + if (diffMask & flag) { // if state is set in both icons, compare the values + const ModeStateKey state = stateMap()->state(flag); + if (pixmap(state.first, state.second) == other.pixmap(state.first, state.second)) + diffMask &= ~flag; + } + } + return diffMask; + } + + void PropertySheetIconValue::assign(const PropertySheetIconValue &other, uint mask) + { + for (int i = 0; i < 8; i++) { + uint flag = 1 << i; + if (mask & flag) { + const ModeStateKey state = stateMap()->state(flag); + setPixmap(state.first, state.second, other.pixmap(state.first, state.second)); + } + } + } + + PropertySheetIconValue::ModeStateToPixmapMap PropertySheetIconValue::paths() const + { + return m_paths; + } + + QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand *createTextPropertyCommand(const QString &propertyName, const QString &text, QObject *object, QDesignerFormWindowInterface *fw) + { + if (text.isEmpty()) { + ResetPropertyCommand *cmd = new ResetPropertyCommand(fw); + cmd->init(object, propertyName); + return cmd; + } + SetPropertyCommand *cmd = new SetPropertyCommand(fw); + cmd->init(object, propertyName, text); + return cmd; + } + + QDESIGNER_SHARED_EXPORT QAction *preferredEditAction(QDesignerFormEditorInterface *core, QWidget *managedWidget) + { + QAction *action = 0; + if (const QDesignerTaskMenuExtension *taskMenu = qt_extension<QDesignerTaskMenuExtension*>(core->extensionManager(), managedWidget)) { + action = taskMenu->preferredEditAction(); + if (!action) { + const QList<QAction *> actions = taskMenu->taskActions(); + if (!actions.isEmpty()) + action = actions.first(); + } + } + if (!action) { + if (const QDesignerTaskMenuExtension *taskMenu = qobject_cast<QDesignerTaskMenuExtension *>( + core->extensionManager()->extension(managedWidget, QLatin1String("QDesignerInternalTaskMenuExtension")))) { + action = taskMenu->preferredEditAction(); + if (!action) { + const QList<QAction *> actions = taskMenu->taskActions(); + if (!actions.isEmpty()) + action = actions.first(); + } + } + } + return action; + } + + QDESIGNER_SHARED_EXPORT bool runUIC(const QString &fileName, UIC_Mode mode, QByteArray& ba, QString &errorMessage) + { + QStringList argv; + QString binary = QLibraryInfo::location(QLibraryInfo::BinariesPath); + binary += QDir::separator(); + switch (mode) { + case UIC_GenerateCode: + binary += QLatin1String("uic"); + break; + case UIC_ConvertV3: + binary += QLatin1String("uic3"); + argv += QLatin1String("-convert"); + break; + } + argv += fileName; + QProcess uic; + uic.start(binary, argv); + if (!uic.waitForStarted()) { + errorMessage = QApplication::translate("Designer", "Unable to launch %1.").arg(binary); + return false; + } + if (!uic.waitForFinished()) { + errorMessage = QApplication::translate("Designer", "%1 timed out.").arg(binary); + return false; + } + if (uic.exitCode()) { + errorMessage = QString::fromAscii(uic.readAllStandardError()); + return false; + } + ba = uic.readAllStandardOutput(); + return true; + } + + QDESIGNER_SHARED_EXPORT QString qtify(const QString &name) + { + QString qname = name; + + Q_ASSERT(qname.isEmpty() == false); + + + if (qname.count() > 1 && qname.at(1).isUpper()) { + const QChar first = qname.at(0); + if (first == QLatin1Char('Q') || first == QLatin1Char('K')) + qname.remove(0, 1); + } + + const int len = qname.count(); + for (int i = 0; i < len && qname.at(i).isUpper(); i++) + qname[i] = qname.at(i).toLower(); + + return qname; + } + + // --------------- UpdateBlocker + UpdateBlocker::UpdateBlocker(QWidget *w) : + m_widget(w), + m_enabled(w->updatesEnabled() && w->isVisible()) + { + if (m_enabled) + m_widget->setUpdatesEnabled(false); + } + + UpdateBlocker::~UpdateBlocker() + { + if (m_enabled) + m_widget->setUpdatesEnabled(true); + } + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_utils_p.h b/tools/designer/src/lib/shared/qdesigner_utils_p.h new file mode 100644 index 0000000..85b3090 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_utils_p.h @@ -0,0 +1,482 @@ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_UTILS_H +#define QDESIGNER_UTILS_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> + +#include <QtCore/QVariant> +#include <QtCore/QMap> +#include <QtGui/QMainWindow> +#include <QtGui/QIcon> +#include <QtGui/QPixmap> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +class QDesignerFormWindowCommand; +class DesignerIconCache; +class FormWindowBase; + + +QDESIGNER_SHARED_EXPORT void designerWarning(const QString &message); + +QDESIGNER_SHARED_EXPORT void reloadIconResources(DesignerIconCache *iconCache, QObject *object); + +/* Flag/Enumeration helpers for the property sheet: Enumeration or flag values are returned by the property sheet + * as a pair of meta type and integer value. + * The meta type carries all the information required for the property editor and serialization + * by the form builders (names, etc). + * Note that the property editor uses unqualified names ("Cancel") while the form builder serialization (uic) + * requires the whole string + * ("QDialogButtonBox::Cancel" or "com.trolltech.qt.gui.QDialogButtonBox.StandardButton.Cancel").*/ + +/* --------- MetaEnum: Base class representing a QMetaEnum with lookup functions + * in both ways. Template of int type since unsigned is more suitable for flags. + * The keyToValue() is ignorant of scopes, it can handle fully qualified or unqualified names. */ + +template <class IntType> +class MetaEnum +{ +public: + typedef QMap<QString, IntType> KeyToValueMap; + + MetaEnum(const QString &name, const QString &scope, const QString &separator); + MetaEnum() {} + void addKey(IntType value, const QString &name); + + QString valueToKey(IntType value, bool *ok = 0) const; + // Ignorant of scopes. + IntType keyToValue(QString key, bool *ok = 0) const; + + const QString &name() const { return m_name; } + const QString &scope() const { return m_scope; } + const QString &separator() const { return m_separator; } + + const QStringList &keys() const { return m_keys; } + const KeyToValueMap &keyToValueMap() const { return m_keyToValueMap; } + +protected: + void appendQualifiedName(const QString &key, QString &target) const; + +private: + QString m_name; + QString m_scope; + QString m_separator; + KeyToValueMap m_keyToValueMap; + QStringList m_keys; +}; + +template <class IntType> +MetaEnum<IntType>::MetaEnum(const QString &name, const QString &scope, const QString &separator) : + m_name(name), + m_scope(scope), + m_separator(separator) +{ +} + +template <class IntType> +void MetaEnum<IntType>::addKey(IntType value, const QString &name) +{ + m_keyToValueMap.insert(name, value); + m_keys.append(name); +} + +template <class IntType> +QString MetaEnum<IntType>::valueToKey(IntType value, bool *ok) const +{ + const QString rc = m_keyToValueMap.key(value); + if (ok) + *ok = !rc.isEmpty(); + return rc; +} + +template <class IntType> +IntType MetaEnum<IntType>::keyToValue(QString key, bool *ok) const +{ + if (!m_scope.isEmpty() && key.startsWith(m_scope)) + key.remove(0, m_scope.size() + m_separator.size()); + const Q_TYPENAME KeyToValueMap::const_iterator it = m_keyToValueMap.find(key); + const bool found = it != m_keyToValueMap.constEnd(); + if (ok) + *ok = found; + return found ? it.value() : IntType(0); +} + +template <class IntType> +void MetaEnum<IntType>::appendQualifiedName(const QString &key, QString &target) const +{ + if (!m_scope.isEmpty()) { + target += m_scope; + target += m_separator; + } + target += key; +} + +// -------------- DesignerMetaEnum: Meta type for enumerations + +class QDESIGNER_SHARED_EXPORT DesignerMetaEnum : public MetaEnum<int> +{ +public: + DesignerMetaEnum(const QString &name, const QString &scope, const QString &separator); + DesignerMetaEnum() {} + + enum SerializationMode { FullyQualified, NameOnly }; + QString toString(int value, SerializationMode sm, bool *ok = 0) const; + + QString messageToStringFailed(int value) const; + QString messageParseFailed(const QString &s) const; + + // parse a string (ignorant of scopes) + int parseEnum(const QString &s, bool *ok = 0) const { return keyToValue(s, ok); } +}; + +// -------------- DesignerMetaFlags: Meta type for flags. +// Note that while the handling of flags is done using unsigned integers, the actual values returned +// by the property system are integers. + +class QDESIGNER_SHARED_EXPORT DesignerMetaFlags : public MetaEnum<uint> +{ +public: + DesignerMetaFlags(const QString &name, const QString &scope, const QString &separator); + DesignerMetaFlags() {} + + enum SerializationMode { FullyQualified, NameOnly }; + QString toString(int value, SerializationMode sm) const; + QStringList flags(int value) const; + + QString messageParseFailed(const QString &s) const; + // parse a string (ignorant of scopes) + int parseFlags(const QString &s, bool *ok = 0) const; +}; + +// -------------- EnumValue: Returned by the property sheet for enumerations + +struct QDESIGNER_SHARED_EXPORT PropertySheetEnumValue +{ + PropertySheetEnumValue(int v, const DesignerMetaEnum &me); + PropertySheetEnumValue(); + + int value; + DesignerMetaEnum metaEnum; +}; + +// -------------- FlagValue: Returned by the property sheet for flags + +struct QDESIGNER_SHARED_EXPORT PropertySheetFlagValue +{ + PropertySheetFlagValue(int v, const DesignerMetaFlags &mf); + PropertySheetFlagValue(); + + int value; + DesignerMetaFlags metaFlags; +}; + +// -------------- PixmapValue: Returned by the property sheet for pixmaps +class QDESIGNER_SHARED_EXPORT PropertySheetPixmapValue +{ +public: + PropertySheetPixmapValue(const QString &path); + PropertySheetPixmapValue(); + + bool operator==(const PropertySheetPixmapValue &other) const { return compare(other) == 0; } + bool operator!=(const PropertySheetPixmapValue &other) const { return compare(other) != 0; } + bool operator<(const PropertySheetPixmapValue &other) const { return compare(other) < 0; } + + // Check where a pixmap comes from + enum PixmapSource { LanguageResourcePixmap , ResourcePixmap, FilePixmap }; + static PixmapSource getPixmapSource(QDesignerFormEditorInterface *core, const QString & path); + + PixmapSource pixmapSource(QDesignerFormEditorInterface *core) const { return getPixmapSource(core, m_path); } + + QString path() const; + void setPath(const QString &path); // passing the empty path resets the pixmap + + int compare(const PropertySheetPixmapValue &other) const; + +private: + QString m_path; +}; + +// -------------- IconValue: Returned by the property sheet for icons + +class QDESIGNER_SHARED_EXPORT PropertySheetIconValue +{ + public: + PropertySheetIconValue(const PropertySheetPixmapValue &pixmap); + PropertySheetIconValue(); + + bool operator==(const PropertySheetIconValue &other) const { return equals(other); } + bool operator!=(const PropertySheetIconValue &other) const { return !equals(other); } + bool operator<(const PropertySheetIconValue &other) const; + + PropertySheetPixmapValue pixmap(QIcon::Mode mode, QIcon::State state) const; + void setPixmap(QIcon::Mode mode, QIcon::State state, const PropertySheetPixmapValue &path); // passing the empty path resets the pixmap + + uint mask() const; + uint compare(const PropertySheetIconValue &other) const; + void assign(const PropertySheetIconValue &other, uint mask); + + typedef QPair<QIcon::Mode, QIcon::State> ModeStateKey; + typedef QMap<ModeStateKey, PropertySheetPixmapValue> ModeStateToPixmapMap; + + ModeStateToPixmapMap paths() const; + +private: + bool equals(const PropertySheetIconValue &rhs) const; + + ModeStateToPixmapMap m_paths; +}; + +class QDESIGNER_SHARED_EXPORT DesignerPixmapCache : public QObject +{ + Q_OBJECT +public: + DesignerPixmapCache(QObject *parent = 0); + QPixmap pixmap(const PropertySheetPixmapValue &value) const; + void clear(); +signals: + void reloaded(); +private: + mutable QMap<PropertySheetPixmapValue, QPixmap> m_cache; + friend class FormWindowBase; +}; + +class QDESIGNER_SHARED_EXPORT DesignerIconCache : public QObject +{ + Q_OBJECT +public: + DesignerIconCache(DesignerPixmapCache *pixmapCache, QObject *parent = 0); + QIcon icon(const PropertySheetIconValue &value) const; + void clear(); +signals: + void reloaded(); +private: + mutable QMap<PropertySheetIconValue, QIcon> m_cache; + DesignerPixmapCache *m_pixmapCache; + friend class FormWindowBase; +}; + +// -------------- StringValue: Returned by the property sheet for strings +class QDESIGNER_SHARED_EXPORT PropertySheetStringValue +{ +public: + PropertySheetStringValue(const QString &value = QString(), + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + + bool operator==(const PropertySheetStringValue &other) const { return equals(other); } + bool operator!=(const PropertySheetStringValue &other) const { return !equals(other); } + + QString value() const; + void setValue(const QString &value); + bool translatable() const; + void setTranslatable(bool translatable); + QString disambiguation() const; + void setDisambiguation(const QString &disambiguation); + QString comment() const; + void setComment(const QString &comment); + +private: + bool equals(const PropertySheetStringValue &rhs) const; + + QString m_value; + bool m_translatable; + QString m_disambiguation; + QString m_comment; +}; + + + +// -------------- StringValue: Returned by the property sheet for strings +class QDESIGNER_SHARED_EXPORT PropertySheetKeySequenceValue +{ +public: + PropertySheetKeySequenceValue(const QKeySequence &value = QKeySequence(), + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + PropertySheetKeySequenceValue(const QKeySequence::StandardKey &standardKey, + bool translatable = true, + const QString &disambiguation = QString(), + const QString &comment = QString()); + + bool operator==(const PropertySheetKeySequenceValue &other) const { return equals(other); } + bool operator!=(const PropertySheetKeySequenceValue &other) const { return !equals(other); } + + QKeySequence value() const; + void setValue(const QKeySequence &value); + QKeySequence::StandardKey standardKey() const; + void setStandardKey(const QKeySequence::StandardKey &standardKey); + bool isStandardKey() const; + + bool translatable() const; + void setTranslatable(bool translatable); + QString disambiguation() const; + void setDisambiguation(const QString &disambiguation); + QString comment() const; + void setComment(const QString &comment); + +private: + bool equals(const PropertySheetKeySequenceValue &rhs) const; + + QKeySequence m_value; + QKeySequence::StandardKey m_standardKey; + bool m_translatable; + QString m_disambiguation; + QString m_comment; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + + +// NOTE: Do not move this code, needed for GCC 3.3 +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetEnumValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetFlagValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetPixmapValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetIconValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetStringValue) +Q_DECLARE_METATYPE(qdesigner_internal::PropertySheetKeySequenceValue) + + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + +// Create a command to change a text property (that is, create a reset property command if the text is empty) +QDESIGNER_SHARED_EXPORT QDesignerFormWindowCommand *createTextPropertyCommand(const QString &propertyName, const QString &text, QObject *object, QDesignerFormWindowInterface *fw); + +// Returns preferred task menu action for managed widget +QDESIGNER_SHARED_EXPORT QAction *preferredEditAction(QDesignerFormEditorInterface *core, QWidget *managedWidget); + +// Convenience to run UIC +enum UIC_Mode { UIC_GenerateCode, UIC_ConvertV3 }; +QDESIGNER_SHARED_EXPORT bool runUIC(const QString &fileName, UIC_Mode mode, QByteArray& ba, QString &errorMessage); + +// Find a suitable variable name for a class. +QDESIGNER_SHARED_EXPORT QString qtify(const QString &name); + +/* UpdateBlocker: Blocks the updates of the widget passed on while in scope. + * Does nothing if the incoming widget already has updatesEnabled==false + * which is important to avoid side-effects when putting it into QStackedLayout. */ + +class QDESIGNER_SHARED_EXPORT UpdateBlocker { + Q_DISABLE_COPY(UpdateBlocker) + +public: + UpdateBlocker(QWidget *w); + ~UpdateBlocker(); + +private: + QWidget *m_widget; + const bool m_enabled; +}; + +namespace Utils { + +inline int valueOf(const QVariant &value, bool *ok = 0) +{ + if (qVariantCanConvert<PropertySheetEnumValue>(value)) { + if (ok) + *ok = true; + return qVariantValue<PropertySheetEnumValue>(value).value; + } + else if (qVariantCanConvert<PropertySheetFlagValue>(value)) { + if (ok) + *ok = true; + return qVariantValue<PropertySheetFlagValue>(value).value; + } + return value.toInt(ok); +} + +inline bool isObjectAncestorOf(QObject *ancestor, QObject *child) +{ + QObject *obj = child; + while (obj != 0) { + if (obj == ancestor) + return true; + obj = obj->parent(); + } + return false; +} + +inline bool isCentralWidget(QDesignerFormWindowInterface *fw, QWidget *widget) +{ + if (! fw || ! widget) + return false; + + if (widget == fw->mainContainer()) + return true; + + // ### generalize for other containers + if (QMainWindow *mw = qobject_cast<QMainWindow*>(fw->mainContainer())) { + return mw->centralWidget() == widget; + } + + return false; +} + +} // namespace Utils + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_UTILS_H diff --git a/tools/designer/src/lib/shared/qdesigner_widget.cpp b/tools/designer/src/lib/shared/qdesigner_widget.cpp new file mode 100644 index 0000000..6d2be17 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widget.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_widget_p.h" +#include "formwindowbase_p.h" +#include "grid_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtGui/QPainter> +#include <QtGui/QStyle> +#include <QtGui/QStyleOption> +#include <QtGui/qevent.h> + +QT_BEGIN_NAMESPACE + +/* QDesignerDialog / QDesignerWidget are used to paint a grid on QDialog and QWidget main containers + * and container extension pages. + * The paint routines work as follows: + * We need to clean the background here (to make the parent grid disappear in case we are a container page + * and to make palette background settings take effect), + * which would normally break style sheets with background settings. + * So, we manually make the style paint after cleaning. On top comes the grid + * In addition, this code works around + * the QStyleSheetStyle setting Qt::WA_StyledBackground to false for subclasses of QWidget. + */ + +QDesignerDialog::QDesignerDialog(QDesignerFormWindowInterface *fw, QWidget *parent) : + QDialog(parent), + m_formWindow(qobject_cast<qdesigner_internal::FormWindowBase*>(fw)) +{ +} + +void QDesignerDialog::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + QStyleOption opt; + opt.initFrom(this); + p.fillRect(e->rect(), palette().brush(backgroundRole())); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + if (m_formWindow && m_formWindow->gridVisible()) + m_formWindow->designerGrid().paint(p, this, e); +} + +QDesignerWidget::QDesignerWidget(QDesignerFormWindowInterface* formWindow, QWidget *parent) : + QWidget(parent), + m_formWindow(qobject_cast<qdesigner_internal::FormWindowBase*>(formWindow)) +{ +} + +QDesignerWidget::~QDesignerWidget() +{ +} + +QDesignerFormWindowInterface* QDesignerWidget::formWindow() const +{ + return m_formWindow; +} + +void QDesignerWidget::paintEvent(QPaintEvent *e) +{ + QPainter p(this); + QStyleOption opt; + opt.initFrom(this); + p.fillRect(e->rect(), palette().brush(backgroundRole())); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); + if (m_formWindow && m_formWindow->gridVisible()) + m_formWindow->designerGrid().paint(p, this, e); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_widget_p.h b/tools/designer/src/lib/shared/qdesigner_widget_p.h new file mode 100644 index 0000000..77d3934 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widget_p.h @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_WIDGET_H +#define QDESIGNER_WIDGET_H + +#include "shared_global_p.h" +#include <QtGui/QDialog> +#include <QtGui/QLabel> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + class FormWindowBase; +} + +class QDESIGNER_SHARED_EXPORT QDesignerWidget : public QWidget +{ + Q_OBJECT +public: + explicit QDesignerWidget(QDesignerFormWindowInterface* formWindow, QWidget *parent = 0); + virtual ~QDesignerWidget(); + + QDesignerFormWindowInterface* formWindow() const; + + void updatePixmap(); + + virtual QSize minimumSizeHint() const + { return QWidget::minimumSizeHint().expandedTo(QSize(16, 16)); } + +protected: + virtual void paintEvent(QPaintEvent *e); + +private: + qdesigner_internal::FormWindowBase* m_formWindow; +}; + +class QDESIGNER_SHARED_EXPORT QDesignerDialog : public QDialog +{ + Q_OBJECT +public: + explicit QDesignerDialog(QDesignerFormWindowInterface *fw, QWidget *parent); + + virtual QSize minimumSizeHint() const + { return QWidget::minimumSizeHint().expandedTo(QSize(16, 16)); } + +protected: + void paintEvent(QPaintEvent *e); + +private: + qdesigner_internal::FormWindowBase* m_formWindow; +}; + +class QDESIGNER_SHARED_EXPORT Line : public QFrame +{ + Q_OBJECT + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) +public: + explicit Line(QWidget *parent) : QFrame(parent) + { setAttribute(Qt::WA_MouseNoMask); setFrameStyle(HLine | Sunken); } + + inline void setOrientation(Qt::Orientation orient) + { setFrameShape(orient == Qt::Horizontal ? HLine : VLine); } + + inline Qt::Orientation orientation() const + { return frameShape() == HLine ? Qt::Horizontal : Qt::Vertical; } +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGET_H diff --git a/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp new file mode 100644 index 0000000..5ba8ad7 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widgetbox.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::QDesignerWidgetBox +*/ + +#include "qdesigner_widgetbox_p.h" +#include "qdesigner_utils_p.h" + +#include <ui4_p.h> + +#include <QtCore/QRegExp> +#include <QtCore/QDebug> +#include <QtCore/QXmlStreamReader> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { +QDesignerWidgetBox::QDesignerWidgetBox(QWidget *parent, Qt::WindowFlags flags) + : QDesignerWidgetBoxInterface(parent, flags), + m_loadMode(LoadMerge) +{ + +} + +QDesignerWidgetBox::LoadMode QDesignerWidgetBox::loadMode() const +{ + return m_loadMode; +} + +void QDesignerWidgetBox::setLoadMode(LoadMode lm) +{ + m_loadMode = lm; +} + +// Convenience to find a widget by class name +bool QDesignerWidgetBox::findWidget(const QDesignerWidgetBoxInterface *wbox, + const QString &className, + const QString &category, + Widget *widgetData) +{ + // Note that entry names do not necessarily match the class name + // (at least, not for the standard widgets), so, + // look in the XML for the class name of the first widget to appear + const QString widgetTag = QLatin1String("<widget"); + QString pattern = QLatin1String("^<widget\\s+class\\s*=\\s*\""); + pattern += className; + pattern += QLatin1String("\".*$"); + const QRegExp regexp(pattern); + Q_ASSERT(regexp.isValid()); + const int catCount = wbox->categoryCount(); + for (int c = 0; c < catCount; c++) { + const Category cat = wbox->category(c); + if (category.isEmpty() || cat.name() == category) { + const int widgetCount = cat.widgetCount(); + for (int w = 0; w < widgetCount; w++) { + const Widget widget = cat.widget(w); + QString xml = widget.domXml(); // Erase the <ui> tag that can be present starting from 4.4 + const int widgetTagIndex = xml.indexOf(widgetTag); + if (widgetTagIndex != -1) { + xml.remove(0, widgetTagIndex); + if (regexp.exactMatch(xml)) { + *widgetData = widget; + return true; + } + } + } + } + } + return false; +} + +// Convenience to create a Dom Widget from widget box xml code. +DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel, + QString *errorMessage) +{ + QXmlStreamReader reader(xml); + DomUI *ui = 0; + + // The xml description must either contain a root element "ui" with a child element "widget" + // or "widget" as the root element (4.3 legacy) + const QString widgetTag = QLatin1String("widget"); + + while (!reader.atEnd()) { + if (reader.readNext() == QXmlStreamReader::StartElement) { + const QStringRef name = reader.name(); + if (ui) { + reader.raiseError(tr("Unexpected element <%1>").arg(name.toString())); + continue; + } + + if (name.compare(QLatin1String("widget"), Qt::CaseInsensitive) == 0) { // 4.3 legacy, wrap into DomUI + ui = new DomUI; + DomWidget *widget = new DomWidget; + widget->read(reader); + ui->setElementWidget(widget); + } else if (name.compare(QLatin1String("ui"), Qt::CaseInsensitive) == 0) { // 4.4 + ui = new DomUI; + ui->read(reader); + } else { + reader.raiseError(tr("Unexpected element <%1>").arg(name.toString())); + } + } + } + + if (reader.hasError()) { + delete ui; + *errorMessage = tr("A parse error occurred at line %1, column %2 of the XML code " + "specified for the widget %3: %4\n%5") + .arg(reader.lineNumber()).arg(reader.columnNumber()).arg(name) + .arg(reader.errorString()).arg(xml); + return 0; + } + + if (!ui || !ui->elementWidget()) { + delete ui; + *errorMessage = tr("The XML code specified for the widget %1 does not contain " + "any widget elements.\n%2").arg(name).arg(xml); + return 0; + } + + if (insertFakeTopLevel) { + DomWidget *fakeTopLevel = new DomWidget; + fakeTopLevel->setAttributeClass(QLatin1String("QWidget")); + QList<DomWidget *> children; + children.push_back(ui->takeElementWidget()); + fakeTopLevel->setElementWidget(children); + ui->setElementWidget(fakeTopLevel); + } + + return ui; +} + +// Convenience to create a Dom Widget from widget box xml code. +DomUI *QDesignerWidgetBox::xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel) +{ + QString errorMessage; + DomUI *rc = xmlToUi(name, xml, insertFakeTopLevel, &errorMessage); + if (!rc) + qdesigner_internal::designerWarning(errorMessage); + return rc; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_widgetbox_p.h b/tools/designer/src/lib/shared/qdesigner_widgetbox_p.h new file mode 100644 index 0000000..bec0081 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widgetbox_p.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_WIDGETBOX_H +#define QDESIGNER_WIDGETBOX_H + +#include "shared_global_p.h" +#include <QtDesigner/QDesignerWidgetBoxInterface> + +QT_BEGIN_NAMESPACE + +class DomUI; + +namespace qdesigner_internal { + +// A widget box with a load mode that allows for updating custom widgets. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetBox : public QDesignerWidgetBoxInterface +{ + Q_OBJECT +public: + enum LoadMode { LoadMerge, LoadReplace, LoadCustomWidgetsOnly }; + + QDesignerWidgetBox(QWidget *parent = 0, Qt::WindowFlags flags = 0); + + LoadMode loadMode() const; + void setLoadMode(LoadMode lm); + + virtual bool loadContents(const QString &contents) = 0; + + // Convenience to access the widget box icon of a widget. Empty category + // matches all + virtual QIcon iconForWidget(const QString &className, + const QString &category = QString()) const = 0; + + // Convenience to find a widget by class name. Empty category matches all + static bool findWidget(const QDesignerWidgetBoxInterface *wbox, + const QString &className, + const QString &category /* = QString() */, + Widget *widgetData); + // Convenience functions to create a DomWidget from widget box xml. + static DomUI *xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel, QString *errorMessage); + static DomUI *xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel); + +private: + LoadMode m_loadMode; +}; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGETBOX_H diff --git a/tools/designer/src/lib/shared/qdesigner_widgetitem.cpp b/tools/designer/src/lib/shared/qdesigner_widgetitem.cpp new file mode 100644 index 0000000..029ee89 --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widgetitem.cpp @@ -0,0 +1,345 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qdesigner_widgetitem_p.h" +#include "qdesigner_widget_p.h" +#include "widgetfactory_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QGridLayout> +#include <QtGui/QFormLayout> +#include <QtGui/QApplication> + +#include <QtCore/QTextStream> +#include <QtCore/QDebug> +#include <private/qlayout_p.h> + +QT_BEGIN_NAMESPACE + +enum { DebugWidgetItem = 0 }; +enum { MinimumLength = 10 }; + +// Widget item creation function to be registered as factory method with +// QLayoutPrivate +static QWidgetItem *createDesignerWidgetItem(const QLayout *layout, QWidget *widget) +{ + Qt::Orientations orientations; + if (qdesigner_internal::QDesignerWidgetItem::check(layout, widget, &orientations)) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItem: Creating on " << layout << widget << orientations; + return new qdesigner_internal::QDesignerWidgetItem(layout, widget, orientations); + } + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItem: Noncontainer: " << layout << widget; + + return 0; +} + +static QString sizePolicyToString(const QSizePolicy &p) +{ + QString rc; { + QTextStream str(&rc); + str << "Control=" << p.controlType() << " expdirs=" << p.expandingDirections() + << " hasHeightForWidth=" << p.hasHeightForWidth() + << " H: Policy=" << p.horizontalPolicy() + << " stretch=" << p.horizontalStretch() + << " V: Policy=" << p.verticalPolicy() + << " stretch=" << p.verticalStretch(); + } + return rc; +} + +// Find the layout the item is contained in, recursing over +// child layouts +static const QLayout *findLayoutOfItem(const QLayout *haystack, const QLayoutItem *needle) +{ + const int count = haystack->count(); + for (int i = 0; i < count; i++) { + QLayoutItem *item = haystack->itemAt(i); + if (item == needle) + return haystack; + if (QLayout *childLayout = item->layout()) + if (const QLayout *containing = findLayoutOfItem(childLayout, needle)) + return containing; + } + return 0; +} + + +namespace qdesigner_internal { + +// ------------------ QDesignerWidgetItem +QDesignerWidgetItem::QDesignerWidgetItem(const QLayout *containingLayout, QWidget *w, Qt::Orientations o) : + QWidgetItemV2(w), + m_orientations(o), + m_nonLaidOutMinSize(w->minimumSizeHint()), + m_nonLaidOutSizeHint(w->sizeHint()), + m_cachedContainingLayout(containingLayout) +{ + // Initialize the minimum size to prevent nonlaid-out frames/widgets + // from being slammed to zero + const QSize minimumSize = w->minimumSize(); + if (!minimumSize.isEmpty()) + m_nonLaidOutMinSize = minimumSize; + expand(&m_nonLaidOutMinSize); + expand(&m_nonLaidOutSizeHint); + w->installEventFilter(this); + connect(containingLayout, SIGNAL(destroyed()), this, SLOT(layoutChanged())); + if (DebugWidgetItem ) + qDebug() << "QDesignerWidgetItem" << w << sizePolicyToString(w->sizePolicy()) << m_nonLaidOutMinSize << m_nonLaidOutSizeHint; +} + +void QDesignerWidgetItem::expand(QSize *s) const +{ + // Expand the size if its too small + if (m_orientations & Qt::Horizontal && s->width() <= 0) + s->setWidth(MinimumLength); + if (m_orientations & Qt::Vertical && s->height() <= 0) + s->setHeight(MinimumLength); +} + +QSize QDesignerWidgetItem::minimumSize() const +{ + // Just track the size in case we are laid-out or stretched. + const QSize baseMinSize = QWidgetItemV2::minimumSize(); + QWidget * w = constWidget(); + if (w->layout() || subjectToStretch(containingLayout(), w)) { + m_nonLaidOutMinSize = baseMinSize; + return baseMinSize; + } + // Nonlaid out: Maintain last laid-out size + const QSize rc = baseMinSize.expandedTo(m_nonLaidOutMinSize); + if (DebugWidgetItem > 1) + qDebug() << "minimumSize" << constWidget() << baseMinSize << rc; + return rc; +} + +QSize QDesignerWidgetItem::sizeHint() const +{ + // Just track the size in case we are laid-out or stretched. + const QSize baseSizeHint = QWidgetItemV2::sizeHint(); + QWidget * w = constWidget(); + if (w->layout() || subjectToStretch(containingLayout(), w)) { + m_nonLaidOutSizeHint = baseSizeHint; + return baseSizeHint; + } + // Nonlaid out: Maintain last laid-out size + const QSize rc = baseSizeHint.expandedTo(m_nonLaidOutSizeHint); + if (DebugWidgetItem > 1) + qDebug() << "sizeHint" << constWidget() << baseSizeHint << rc; + return rc; +} + +bool QDesignerWidgetItem::subjectToStretch(const QLayout *layout, QWidget *w) +{ + if (!layout) + return false; + // Are we under some stretch factor? + if (const QBoxLayout *bl = qobject_cast<const QBoxLayout *>(layout)) { + const int index = bl->indexOf(w); + Q_ASSERT(index != -1); + return bl->stretch(index) != 0; + } + if (const QGridLayout *cgl = qobject_cast<const QGridLayout *>(layout)) { + QGridLayout *gl = const_cast<QGridLayout *>(cgl); + const int index = cgl->indexOf(w); + Q_ASSERT(index != -1); + int row, column, rowSpan, columnSpan; + gl->getItemPosition (index, &row, &column, &rowSpan, &columnSpan); + const int rend = row + rowSpan; + const int cend = column + columnSpan; + for (int r = row; r < rend; r++) + if (cgl->rowStretch(r) != 0) + return true; + for (int c = column; c < cend; c++) + if (cgl->columnStretch(c) != 0) + return true; + } + return false; +} + +/* Return the orientations mask for a layout, specifying + * in which directions squeezing should be prevented. */ +static Qt::Orientations layoutOrientation(const QLayout *layout) +{ + if (const QBoxLayout *bl = qobject_cast<const QBoxLayout *>(layout)) { + const QBoxLayout::Direction direction = bl->direction(); + return direction == QBoxLayout::LeftToRight || direction == QBoxLayout::RightToLeft ? Qt::Horizontal : Qt::Vertical; + } + if (qobject_cast<const QFormLayout*>(layout)) + return Qt::Vertical; + return Qt::Horizontal|Qt::Vertical; +} + +// Check for a non-container extension container +bool QDesignerWidgetItem::isContainer(const QDesignerFormEditorInterface *core, QWidget *w) +{ + if (!WidgetFactory::isFormEditorObject(w)) + return false; + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widx = wdb->indexOfObject(w); + if (widx == -1 || !wdb->item(widx)->isContainer()) + return false; + if (qt_extension<QDesignerContainerExtension*>(core->extensionManager(), w)) + return false; + return true; +} + +bool QDesignerWidgetItem::check(const QLayout *layout, QWidget *w, Qt::Orientations *ptrToOrientations) +{ + // Check for form-editor non-containerextension-containers (QFrame, etc) + // within laid-out form editor widgets. No check for managed() here as we + // want container pages and widgets in the process of being morphed as + // well. Avoid nested layouts (as the effective stretch cannot be easily + // computed and may mess things up). Won't work for Q3 Group boxes. + if (ptrToOrientations) + *ptrToOrientations = 0; + + const QObject *layoutParent = layout->parent(); + if (!layoutParent || !layoutParent->isWidgetType() || !WidgetFactory::isFormEditorObject(layoutParent)) + return false; + + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(w); + if (!fw || !isContainer(fw->core(), w)) + return false; + + // If it is a box, restrict to its orientation + if (ptrToOrientations) + *ptrToOrientations = layoutOrientation(layout); + + return true; +} + +QSize QDesignerWidgetItem::nonLaidOutMinSize() const +{ + return m_nonLaidOutMinSize; +} + +void QDesignerWidgetItem::setNonLaidOutMinSize(const QSize &s) +{ + if (DebugWidgetItem > 1) + qDebug() << "setNonLaidOutMinSize" << constWidget() << s; + m_nonLaidOutMinSize = s; +} + +QSize QDesignerWidgetItem::nonLaidOutSizeHint() const +{ + return m_nonLaidOutSizeHint; +} + +void QDesignerWidgetItem::setNonLaidOutSizeHint(const QSize &s) +{ + if (DebugWidgetItem > 1) + qDebug() << "setNonLaidOutSizeHint" << constWidget() << s; + m_nonLaidOutSizeHint = s; +} + +void QDesignerWidgetItem::install() +{ + QLayoutPrivate::widgetItemFactoryMethod = createDesignerWidgetItem; +} + +void QDesignerWidgetItem::deinstall() +{ + QLayoutPrivate::widgetItemFactoryMethod = 0; +} + +const QLayout *QDesignerWidgetItem::containingLayout() const +{ + if (!m_cachedContainingLayout) { + if (QWidget *parentWidget = constWidget()->parentWidget()) + if (QLayout *parentLayout = parentWidget->layout()) { + m_cachedContainingLayout = findLayoutOfItem(parentLayout, this); + if (m_cachedContainingLayout) + connect(m_cachedContainingLayout, SIGNAL(destroyed()), this, SLOT(layoutChanged())); + } + if (DebugWidgetItem) + qDebug() << Q_FUNC_INFO << " found " << m_cachedContainingLayout << " after reparenting " << constWidget(); + } + return m_cachedContainingLayout; +} + +void QDesignerWidgetItem::layoutChanged() +{ + if (DebugWidgetItem) + qDebug() << Q_FUNC_INFO; + m_cachedContainingLayout = 0; +} + +bool QDesignerWidgetItem::eventFilter(QObject * /* watched */, QEvent *event) +{ + if (event->type() == QEvent::ParentChange) + layoutChanged(); + return false; +} + +// ------------------ QDesignerWidgetItemInstaller + +int QDesignerWidgetItemInstaller::m_instanceCount = 0; + +QDesignerWidgetItemInstaller::QDesignerWidgetItemInstaller() +{ + if (m_instanceCount++ == 0) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItemInstaller: installing"; + QDesignerWidgetItem::install(); + } +} + +QDesignerWidgetItemInstaller::~QDesignerWidgetItemInstaller() +{ + if (--m_instanceCount == 0) { + if (DebugWidgetItem) + qDebug() << "QDesignerWidgetItemInstaller: deinstalling"; + QDesignerWidgetItem::deinstall(); + } +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qdesigner_widgetitem_p.h b/tools/designer/src/lib/shared/qdesigner_widgetitem_p.h new file mode 100644 index 0000000..1d8ff3e --- /dev/null +++ b/tools/designer/src/lib/shared/qdesigner_widgetitem_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef DESIGNERWIDGETITEM_H +#define DESIGNERWIDGETITEM_H + +#include "shared_global_p.h" + +#include <QtGui/QLayoutItem> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +// QDesignerWidgetItem: A Layout Item that is used for non-containerextension- +// containers (QFrame, etc) on Designer forms. It prevents its widget +// from being slammed to size 0 if the widget has no layout: +// Pre 4.5, this item ensured only that QWidgets and QFrames were not squeezed +// to size 0 since they have an invalid size hint when non-laid out. +// Since 4.5, the item is used for every non-containerextension-container. +// In case the container has itself a layout, it merely tracks the minimum +// size. If the container has no layout and is not subject to some stretch +// factor, it will return the last valid size. The effect is that after +// breaking a layout on a container within a layout, it just maintains its +// last size and is not slammed to 0,0. In addition, it can be resized. +// The class keeps track of the containing layout by tracking widget reparent +// and destroyed slots as Designer will for example re-create grid layouts to +// shrink them. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetItem : public QObject, public QWidgetItemV2 { + Q_DISABLE_COPY(QDesignerWidgetItem) + Q_OBJECT +public: + explicit QDesignerWidgetItem(const QLayout *containingLayout, QWidget *w, Qt::Orientations o = Qt::Horizontal|Qt::Vertical); + + const QLayout *containingLayout() const; + + inline QWidget *constWidget() const { return const_cast<QDesignerWidgetItem*>(this)->widget(); } + + virtual QSize minimumSize() const; + virtual QSize sizeHint() const; + + // Resize: Takes effect if the contained widget does not have a layout + QSize nonLaidOutMinSize() const; + void setNonLaidOutMinSize(const QSize &s); + + QSize nonLaidOutSizeHint() const; + void setNonLaidOutSizeHint(const QSize &s); + + // Check whether a QDesignerWidgetItem should be installed + static bool check(const QLayout *layout, QWidget *w, Qt::Orientations *ptrToOrientations = 0); + + // Register itself using QLayoutPrivate's widget item factory method hook + static void install(); + static void deinstall(); + + // Check for a non-container extension container + static bool isContainer(const QDesignerFormEditorInterface *core, QWidget *w); + + static bool subjectToStretch(const QLayout *layout, QWidget *w); + + virtual bool eventFilter(QObject * watched, QEvent * event); + +private slots: + void layoutChanged(); + +private: + void expand(QSize *s) const; + bool subjectToStretch() const; + + const Qt::Orientations m_orientations; + mutable QSize m_nonLaidOutMinSize; + mutable QSize m_nonLaidOutSizeHint; + mutable const QLayout *m_cachedContainingLayout; +}; + +// Helper class that ensures QDesignerWidgetItem is installed while an +// instance is in scope. + +class QDESIGNER_SHARED_EXPORT QDesignerWidgetItemInstaller { + Q_DISABLE_COPY(QDesignerWidgetItemInstaller) + +public: + QDesignerWidgetItemInstaller(); + ~QDesignerWidgetItemInstaller(); + +private: + static int m_instanceCount; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/qlayout_widget.cpp b/tools/designer/src/lib/shared/qlayout_widget.cpp new file mode 100644 index 0000000..c0986ce --- /dev/null +++ b/tools/designer/src/lib/shared/qlayout_widget.cpp @@ -0,0 +1,2103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +** This file is provided AS IS with NO WARRANTY OF ANY KND, INCLUDING THE +** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +** +****************************************************************************/ + +#include "qlayout_widget_p.h" +#include "qdesigner_utils_p.h" +#include "layout_p.h" +#include "layoutinfo_p.h" +#include "invisible_widget_p.h" +#include "qdesigner_widgetitem_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerWidgetFactoryInterface> + +#include <QtGui/QPainter> +#include <QtGui/QHBoxLayout> +#include <QtGui/QVBoxLayout> +#include <QtGui/QGridLayout> +#include <QtGui/QFormLayout> +#include <QtGui/qevent.h> + +#include <QtCore/qdebug.h> +#include <QtCore/QtAlgorithms> +#include <QtCore/QMap> +#include <QtCore/QStack> +#include <QtCore/QPair> +#include <QtCore/QSet> + +enum { ShiftValue = 1 }; +enum { debugLayout = 0 }; +enum { FormLayoutColumns = 2 }; +enum { indicatorSize = 2 }; +// Grid/form Helpers: get info (overloads to make templates work) + +namespace { // Do not use static, will break HP-UX due to templates + +QT_USE_NAMESPACE + +// overloads to make templates over QGridLayout/QFormLayout work +inline int gridRowCount(const QGridLayout *gridLayout) +{ + return gridLayout->rowCount(); +} + +inline int gridColumnCount(const QGridLayout *gridLayout) +{ + return gridLayout->columnCount(); +} + +// QGridLayout/QFormLayout Helpers: get item position (overloads to make templates work) +inline void getGridItemPosition(QGridLayout *gridLayout, int index, + int *row, int *column, int *rowspan, int *colspan) +{ + gridLayout->getItemPosition(index, row, column, rowspan, colspan); +} + +QRect gridItemInfo(QGridLayout *grid, int index) +{ + int row, column, rowSpan, columnSpan; + // getItemPosition is not const, grmbl.. + grid->getItemPosition(index, &row, &column, &rowSpan, &columnSpan); + return QRect(column, row, columnSpan, rowSpan); +} + +inline int gridRowCount(const QFormLayout *formLayout) { return formLayout->rowCount(); } +inline int gridColumnCount(const QFormLayout *) { return FormLayoutColumns; } + +inline void getGridItemPosition(QFormLayout *formLayout, int index, int *row, int *column, int *rowspan, int *colspan) +{ + qdesigner_internal::getFormLayoutItemPosition(formLayout, index, row, column, rowspan, colspan); +} + +QRect gridItemInfo(const QFormLayout *form, int index) +{ + int row; + int column; + int colspan; + qdesigner_internal::getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); + return QRect(column, row, colspan, 1); +} +} // namespace anonymous + +QT_BEGIN_NAMESPACE + +static const char *objectNameC = "objectName"; +static const char *sizeConstraintC = "sizeConstraint"; + +/* A padding spacer element that is used to represent an empty form layout cell. It should grow with its cell. + * Should not be used on a grid as it causes resizing inconsistencies */ +namespace qdesigner_internal { + class PaddingSpacerItem : public QSpacerItem { + public: + PaddingSpacerItem() : QSpacerItem(0, 0, QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding) {} + virtual Qt::Orientations expandingDirections () const { return Qt::Vertical | Qt::Horizontal; } + }; +} + +static inline QSpacerItem *createGridSpacer() +{ + return new QSpacerItem(0, 0); +} + +static inline QSpacerItem *createFormSpacer() +{ + return new qdesigner_internal::PaddingSpacerItem; +} + +// QGridLayout/QFormLayout Helpers: Debug items of GridLikeLayout +template <class GridLikeLayout> +static QDebug debugGridLikeLayout(QDebug str, const GridLikeLayout &gl) +{ + const int count = gl.count(); + str << "Grid: " << gl.objectName() << gridRowCount(&gl) << " rows x " << gridColumnCount(&gl) + << " cols " << count << " items\n"; + for (int i = 0; i < count; i++) { + QLayoutItem *item = gl.itemAt(i); + str << "Item " << i << item << item->widget() << gridItemInfo(const_cast<GridLikeLayout *>(&gl), i) << " empty=" << qdesigner_internal::LayoutInfo::isEmptyItem(item) << "\n"; + } + return str; +} + +static inline QDebug operator<<(QDebug str, const QGridLayout &gl) { return debugGridLikeLayout(str, gl); } +static inline QDebug operator<<(QDebug str, const QFormLayout &fl) { return debugGridLikeLayout(str, fl); } + +static inline bool isEmptyFormLayoutRow(const QFormLayout *fl, int row) +{ + // Spanning can never be empty + if (fl->itemAt(row, QFormLayout::SpanningRole)) + return false; + return qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::LabelRole)) && qdesigner_internal::LayoutInfo::isEmptyItem(fl->itemAt(row, QFormLayout::FieldRole)); +} + +static inline bool canSimplifyFormLayout(const QFormLayout *formLayout, const QRect &restrictionArea) +{ + if (restrictionArea.x() >= FormLayoutColumns) + return false; + // Try to find empty rows + const int bottomCheckRow = qMin(formLayout->rowCount(), restrictionArea.top() + restrictionArea.height()); + for (int r = restrictionArea.y(); r < bottomCheckRow; r++) + if (isEmptyFormLayoutRow(formLayout, r)) + return true; + return false; +} + +// recreate a managed layout (which does not automagically remove +// empty rows/columns like grid or form layout) in case it needs to shrink + +static QLayout *recreateManagedLayout(const QDesignerFormEditorInterface *core, QWidget *w, QLayout *lt) +{ + const qdesigner_internal::LayoutInfo::Type t = qdesigner_internal::LayoutInfo::layoutType(core, lt); + qdesigner_internal::LayoutProperties properties; + const int mask = properties.fromPropertySheet(core, lt, qdesigner_internal::LayoutProperties::AllProperties); + qdesigner_internal::LayoutInfo::deleteLayout(core, w); + QLayout *rc = core->widgetFactory()->createLayout(w, 0, t); + properties.toPropertySheet(core, rc, mask, true); + return rc; +} + +// QGridLayout/QFormLayout Helpers: find an item on a form/grid. Return index +template <class GridLikeLayout> +int findGridItemAt(GridLikeLayout *gridLayout, int at_row, int at_column) +{ + Q_ASSERT(gridLayout); + const int count = gridLayout->count(); + for (int index = 0; index < count; index++) { + int row, column, rowspan, colspan; + getGridItemPosition(gridLayout, index, &row, &column, &rowspan, &colspan); + if (at_row >= row && at_row < (row + rowspan) + && at_column >= column && at_column < (column + colspan)) { + return index; + } + } + return -1; +} +// QGridLayout/QFormLayout Helpers: remove dummy spacers on form/grid +template <class GridLikeLayout> +static bool removeEmptyCellsOnGrid(GridLikeLayout *grid, const QRect &area) +{ + // check if there are any items in the way. Should be only spacers + // Unique out items that span rows/columns. + QVector<int> indexesToBeRemoved; + indexesToBeRemoved.reserve(grid->count()); + const int rightColumn = area.x() + area.width(); + const int bottomRow = area.y() + area.height(); + for (int c = area.x(); c < rightColumn; c++) + for (int r = area.y(); r < bottomRow; r++) { + const int index = findGridItemAt(grid, r ,c); + if (index != -1) + if (QLayoutItem *item = grid->itemAt(index)) { + if (qdesigner_internal::LayoutInfo::isEmptyItem(item)) { + if (indexesToBeRemoved.indexOf(index) == -1) + indexesToBeRemoved.push_back(index); + } else { + return false; + } + } + } + // remove, starting from last + if (!indexesToBeRemoved.empty()) { + qStableSort(indexesToBeRemoved.begin(), indexesToBeRemoved.end()); + for (int i = indexesToBeRemoved.size() - 1; i >= 0; i--) + delete grid->takeAt(indexesToBeRemoved[i]); + } + return true; +} + +namespace qdesigner_internal { +// --------- LayoutProperties + +LayoutProperties::LayoutProperties() +{ + clear(); +} + +void LayoutProperties::clear() +{ + qFill(m_margins, m_margins + MarginCount, 0); + qFill(m_marginsChanged, m_marginsChanged + MarginCount, false); + qFill(m_spacings, m_spacings + SpacingsCount, 0); + qFill(m_spacingsChanged, m_spacingsChanged + SpacingsCount, false); + + m_objectName = QVariant(); + m_objectNameChanged = false; + m_sizeConstraint = QVariant(QLayout::SetDefaultConstraint); + m_sizeConstraintChanged = false; + + m_fieldGrowthPolicyChanged = m_rowWrapPolicyChanged = m_labelAlignmentChanged = m_formAlignmentChanged = false; + m_fieldGrowthPolicy = m_rowWrapPolicy = m_formAlignment = QVariant(); + + m_boxStretchChanged = m_gridRowStretchChanged = m_gridColumnStretchChanged = m_gridRowMinimumHeightChanged = false; + m_boxStretch = m_gridRowStretch = m_gridColumnStretch = m_gridRowMinimumHeight = QVariant(); +} + +int LayoutProperties::visibleProperties(const QLayout *layout) +{ + // Grid like layout have 2 spacings. + const bool isFormLayout = qobject_cast<const QFormLayout*>(layout); + const bool isGridLike = qobject_cast<const QGridLayout*>(layout) || isFormLayout; + int rc = ObjectNameProperty|LeftMarginProperty|TopMarginProperty|RightMarginProperty|BottomMarginProperty| + SizeConstraintProperty; + + rc |= isGridLike ? (HorizSpacingProperty|VertSpacingProperty) : SpacingProperty; + if (isFormLayout) { + rc |= FieldGrowthPolicyProperty|RowWrapPolicyProperty|LabelAlignmentProperty|FormAlignmentProperty; + } else { + if (isGridLike) { + rc |= GridRowStretchProperty|GridColumnStretchProperty|GridRowMinimumHeightProperty|GridColumnMinimumWidthProperty; + } else { + rc |= BoxStretchProperty; + } + } + return rc; +} + +static const char *marginPropertyNamesC[] = {"leftMargin", "topMargin", "rightMargin", "bottomMargin"}; +static const char *spacingPropertyNamesC[] = {"spacing", "horizontalSpacing", "verticalSpacing" }; +static const char *fieldGrowthPolicyPropertyC = "fieldGrowthPolicy"; +static const char *rowWrapPolicyPropertyC = "rowWrapPolicy"; +static const char *labelAlignmentPropertyC = "labelAlignment"; +static const char *formAlignmentPropertyC = "formAlignment"; +static const char *boxStretchPropertyC = "stretch"; +static const char *gridRowStretchPropertyC = "rowStretch"; +static const char *gridColumnStretchPropertyC = "columnStretch"; +static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight"; +static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth"; + +static bool intValueFromSheet(const QDesignerPropertySheetExtension *sheet, const QString &name, int *value, bool *changed) +{ + const int sheetIndex = sheet->indexOf(name); + if (sheetIndex == -1) + return false; + *value = sheet->property(sheetIndex).toInt(); + *changed = sheet->isChanged(sheetIndex); + return true; +} + +static void variantPropertyFromSheet(int mask, int flag, const QDesignerPropertySheetExtension *sheet, const QString &name, + QVariant *value, bool *changed, int *returnMask) +{ + if (mask & flag) { + const int sIndex = sheet->indexOf(name); + if (sIndex != -1) { + *value = sheet->property(sIndex); + *changed = sheet->isChanged(sIndex); + *returnMask |= flag; + } + } +} + +int LayoutProperties::fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask) +{ + int rc = 0; + const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l); + Q_ASSERT(sheet); + // name + if (mask & ObjectNameProperty) { + const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); + Q_ASSERT(nameIndex != -1); + m_objectName = sheet->property(nameIndex); + m_objectNameChanged = sheet->isChanged(nameIndex); + rc |= ObjectNameProperty; + } + // -- Margins + const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; + for (int i = 0; i < MarginCount; i++) + if (mask & marginFlags[i]) + if (intValueFromSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins + i, m_marginsChanged + i)) + rc |= marginFlags[i]; + + const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; + for (int i = 0; i < SpacingsCount; i++) + if (mask & spacingFlags[i]) + if (intValueFromSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings + i, m_spacingsChanged + i)) + rc |= spacingFlags[i]; + // sizeConstraint, flags + variantPropertyFromSheet(mask, SizeConstraintProperty, sheet, QLatin1String(sizeConstraintC), &m_sizeConstraint, &m_sizeConstraintChanged, &rc); + variantPropertyFromSheet(mask, FieldGrowthPolicyProperty, sheet, QLatin1String(fieldGrowthPolicyPropertyC), &m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); + variantPropertyFromSheet(mask, RowWrapPolicyProperty, sheet, QLatin1String(rowWrapPolicyPropertyC), &m_rowWrapPolicy, &m_rowWrapPolicyChanged, &rc); + variantPropertyFromSheet(mask, LabelAlignmentProperty, sheet, QLatin1String(labelAlignmentPropertyC), &m_labelAlignment, &m_labelAlignmentChanged, &rc); + variantPropertyFromSheet(mask, FormAlignmentProperty, sheet, QLatin1String(formAlignmentPropertyC), &m_formAlignment, &m_formAlignmentChanged, &rc); + variantPropertyFromSheet(mask, BoxStretchProperty, sheet, QLatin1String(boxStretchPropertyC), &m_boxStretch, & m_boxStretchChanged, &rc); + variantPropertyFromSheet(mask, GridRowStretchProperty, sheet, QLatin1String(gridRowStretchPropertyC), &m_gridRowStretch, &m_gridRowStretchChanged, &rc); + variantPropertyFromSheet(mask, GridColumnStretchProperty, sheet, QLatin1String(gridColumnStretchPropertyC), &m_gridColumnStretch, &m_gridColumnStretchChanged, &rc); + variantPropertyFromSheet(mask, GridRowMinimumHeightProperty, sheet, QLatin1String(gridRowMinimumHeightPropertyC), &m_gridRowMinimumHeight, &m_gridRowMinimumHeightChanged, &rc); + variantPropertyFromSheet(mask, GridColumnMinimumWidthProperty, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), &m_gridColumnMinimumWidth, &m_gridColumnMinimumWidthChanged, &rc); + return rc; +} + +static bool intValueToSheet(QDesignerPropertySheetExtension *sheet, const QString &name, int value, bool changed, bool applyChanged) + +{ + + const int sheetIndex = sheet->indexOf(name); + if (sheetIndex == -1) { + qWarning() << " LayoutProperties: Attempt to set property " << name << " that does not exist for the layout."; + return false; + } + sheet->setProperty(sheetIndex, QVariant(value)); + if (applyChanged) + sheet->setChanged(sheetIndex, changed); + return true; +} + +static void variantPropertyToSheet(int mask, int flag, bool applyChanged, QDesignerPropertySheetExtension *sheet, const QString &name, + const QVariant &value, bool changed, int *returnMask) +{ + if (mask & flag) { + const int sIndex = sheet->indexOf(name); + if (sIndex != -1) { + sheet->setProperty(sIndex, value); + if (applyChanged) + sheet->setChanged(sIndex, changed); + *returnMask |= flag; + } + } +} + +int LayoutProperties::toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask, bool applyChanged) const +{ + int rc = 0; + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), l); + Q_ASSERT(sheet); + // name + if (mask & ObjectNameProperty) { + const int nameIndex = sheet->indexOf(QLatin1String(objectNameC)); + Q_ASSERT(nameIndex != -1); + sheet->setProperty(nameIndex, m_objectName); + if (applyChanged) + sheet->setChanged(nameIndex, m_objectNameChanged); + rc |= ObjectNameProperty; + } + // margins + const int marginFlags[MarginCount] = { LeftMarginProperty, TopMarginProperty, RightMarginProperty, BottomMarginProperty}; + for (int i = 0; i < MarginCount; i++) + if (mask & marginFlags[i]) + if (intValueToSheet(sheet, QLatin1String(marginPropertyNamesC[i]), m_margins[i], m_marginsChanged[i], applyChanged)) + rc |= marginFlags[i]; + + const int spacingFlags[] = { SpacingProperty, HorizSpacingProperty, VertSpacingProperty}; + for (int i = 0; i < SpacingsCount; i++) + if (mask & spacingFlags[i]) + if (intValueToSheet(sheet, QLatin1String(spacingPropertyNamesC[i]), m_spacings[i], m_spacingsChanged[i], applyChanged)) + rc |= spacingFlags[i]; + // sizeConstraint + variantPropertyToSheet(mask, SizeConstraintProperty, applyChanged, sheet, QLatin1String(sizeConstraintC), m_sizeConstraint, m_sizeConstraintChanged, &rc); + variantPropertyToSheet(mask, FieldGrowthPolicyProperty, applyChanged, sheet, QLatin1String(fieldGrowthPolicyPropertyC), m_fieldGrowthPolicy, &m_fieldGrowthPolicyChanged, &rc); + variantPropertyToSheet(mask, RowWrapPolicyProperty, applyChanged, sheet, QLatin1String(rowWrapPolicyPropertyC), m_rowWrapPolicy, m_rowWrapPolicyChanged, &rc); + variantPropertyToSheet(mask, LabelAlignmentProperty, applyChanged, sheet, QLatin1String(labelAlignmentPropertyC), m_labelAlignment, m_labelAlignmentChanged, &rc); + variantPropertyToSheet(mask, FormAlignmentProperty, applyChanged, sheet, QLatin1String(formAlignmentPropertyC), m_formAlignment, m_formAlignmentChanged, &rc); + variantPropertyToSheet(mask, BoxStretchProperty, applyChanged, sheet, QLatin1String(boxStretchPropertyC), m_boxStretch, m_boxStretchChanged, &rc); + variantPropertyToSheet(mask, GridRowStretchProperty, applyChanged, sheet, QLatin1String(gridRowStretchPropertyC), m_gridRowStretch, m_gridRowStretchChanged, &rc); + variantPropertyToSheet(mask, GridColumnStretchProperty, applyChanged, sheet, QLatin1String(gridColumnStretchPropertyC), m_gridColumnStretch, m_gridColumnStretchChanged, &rc); + variantPropertyToSheet(mask, GridRowMinimumHeightProperty, applyChanged, sheet, QLatin1String(gridRowMinimumHeightPropertyC), m_gridRowMinimumHeight, m_gridRowMinimumHeightChanged, &rc); + variantPropertyToSheet(mask, GridColumnMinimumWidthProperty, applyChanged, sheet, QLatin1String(gridColumnMinimumWidthPropertyC), m_gridColumnMinimumWidth, m_gridColumnMinimumWidthChanged, &rc); + return rc; +} + +// ---------------- LayoutHelper +LayoutHelper::LayoutHelper() +{ +} + +LayoutHelper::~LayoutHelper() +{ +} + +int LayoutHelper::indexOf(const QLayout *lt, const QWidget *widget) +{ + if (!lt) + return -1; + + const int itemCount = lt->count(); + for (int i = 0; i < itemCount; i++) + if (lt->itemAt(i)->widget() == widget) + return i; + return -1; +} + +QRect LayoutHelper::itemInfo(QLayout *lt, const QWidget *widget) const +{ + const int index = indexOf(lt, widget); + if (index == -1) { + qWarning() << "LayoutHelper::itemInfo: " << widget << " not in layout " << lt; + return QRect(0, 0, 1, 1); + } + return itemInfo(lt, index); +} + + // ---------------- BoxLayoutHelper + class BoxLayoutHelper : public LayoutHelper { + public: + BoxLayoutHelper(const Qt::Orientation orientation) : m_orientation(orientation) {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *, const QWidget *); + virtual void popState(const QDesignerFormEditorInterface *, QWidget *); + + virtual bool canSimplify(const QDesignerFormEditorInterface *, const QWidget *, const QRect &) const { return false; } + virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &) {} + + // Helper for restoring layout states + typedef QVector <QLayoutItem *> LayoutItemVector; + static LayoutItemVector disassembleLayout(QLayout *lt); + static QLayoutItem *findItemOfWidget(const LayoutItemVector &lv, QWidget *w); + + private: + typedef QVector<QWidget *> BoxLayoutState; + + static BoxLayoutState state(const QBoxLayout*lt); + + QStack<BoxLayoutState> m_states; + const Qt::Orientation m_orientation; + }; + + QRect BoxLayoutHelper::itemInfo(QLayout * /*lt*/, int index) const + { + return m_orientation == Qt::Horizontal ? QRect(index, 0, 1, 1) : QRect(0, index, 1, 1); + } + + void BoxLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt); + Q_ASSERT(boxLayout); + boxLayout->insertWidget(m_orientation == Qt::Horizontal ? info.x() : info.y(), w); + } + + void BoxLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt); + Q_ASSERT(boxLayout); + boxLayout->removeWidget(widget); + } + + void BoxLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(lt)) { + const int index = boxLayout->indexOf(before); + if (index != -1) { + const bool visible = before->isVisible(); + delete boxLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + boxLayout->insertWidget(index, after); + ok = true; + } + } + if (!ok) + qWarning() << "BoxLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + BoxLayoutHelper::BoxLayoutState BoxLayoutHelper::state(const QBoxLayout*lt) + { + BoxLayoutState rc; + if (const int count = lt->count()) { + rc.reserve(count); + for (int i = 0; i < count; i++) + if (QWidget *w = lt->itemAt(i)->widget()) + rc.push_back(w); + } + return rc; + } + + void BoxLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *w) + { + const QBoxLayout *boxLayout = qobject_cast<const QBoxLayout *>(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(boxLayout); + m_states.push(state(boxLayout)); + } + + QLayoutItem *BoxLayoutHelper::findItemOfWidget(const LayoutItemVector &lv, QWidget *w) + { + const LayoutItemVector::const_iterator cend = lv.constEnd(); + for (LayoutItemVector::const_iterator it = lv.constBegin(); it != cend; ++it) + if ( (*it)->widget() == w) + return *it; + + return 0; + } + + BoxLayoutHelper::LayoutItemVector BoxLayoutHelper::disassembleLayout(QLayout *lt) + { + // Take items + const int count = lt->count(); + if (count == 0) + return LayoutItemVector(); + LayoutItemVector rc; + rc.reserve(count); + for (int i = count - 1; i >= 0; i--) + rc.push_back(lt->takeAt(i)); + return rc; + } + + void BoxLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *w) + { + QBoxLayout *boxLayout = qobject_cast<QBoxLayout *>(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(boxLayout); + const BoxLayoutState savedState = m_states.pop(); + const BoxLayoutState currentState = state(boxLayout); + // Check for equality/empty. Note that this will currently + // always trigger as box layouts do not have a state apart from + // the order and there is no layout order editor yet. + if (savedState == state(boxLayout)) + return; + + const int count = savedState.size(); + Q_ASSERT(count == currentState.size()); + // Take items and reassemble in saved order + const LayoutItemVector items = disassembleLayout(boxLayout); + for (int i = 0; i < count; i++) { + QLayoutItem *item = findItemOfWidget(items, savedState[i]); + Q_ASSERT(item); + boxLayout->addItem(item); + } + } + + // Grid Layout state. Datatypically store the state of a GridLayout as a map of + // widgets to QRect(columns, rows) and size. Used to store the state for undo operations + // that do not change the widgets within the layout; also provides some manipulation + // functions and ability to apply the state to a layout provided its widgets haven't changed. + struct GridLayoutState { + GridLayoutState(); + + void fromLayout(QGridLayout *l); + void applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const; + + void insertRow(int row); + void insertColumn(int column); + + bool simplify(const QRect &r, bool testOnly); + void removeFreeRow(int row); + void removeFreeColumn(int column); + + + // State of a cell in one dimension + enum DimensionCellState { + Free, + Spanned, // Item spans it + Occupied // Item bordering on it + }; + // Horiontal, Vertical pair of state + typedef QPair<DimensionCellState, DimensionCellState> CellState; + typedef QVector<CellState> CellStates; + + // Figure out states of a cell and return as a flat vector of + // [column1, column2,...] (address as row * columnCount + col) + static CellStates cellStates(const QList<QRect> &rects, int numRows, int numColumns); + + typedef QMap<QWidget *, QRect> WidgetItemMap; + WidgetItemMap widgetItemMap; + int rowCount; + int colCount; + }; + + static inline bool needsSpacerItem(const GridLayoutState::CellState &cs) { + return cs.first == GridLayoutState::Free && cs.second == GridLayoutState::Free; + } + + static inline QDebug operator<<(QDebug str, const GridLayoutState &gs) + { + str << "GridLayoutState: " << gs.rowCount << " rows x " << gs.colCount + << " cols " << gs.widgetItemMap.size() << " items\n"; + + const GridLayoutState::WidgetItemMap::const_iterator wcend = gs.widgetItemMap.constEnd(); + for (GridLayoutState::WidgetItemMap::const_iterator it = gs.widgetItemMap.constBegin(); it != wcend; ++it) + str << "Item " << it.key() << it.value() << '\n'; + return str; + } + + GridLayoutState::GridLayoutState() : + rowCount(0), + colCount(0) + { + } + + GridLayoutState::CellStates GridLayoutState::cellStates(const QList<QRect> &rects, int numRows, int numColumns) + { + CellStates rc = CellStates(numRows * numColumns, CellState(Free, Free)); + const QList<QRect>::const_iterator rcend = rects.constEnd(); + for (QList<QRect>::const_iterator it = rects.constBegin(); it != rcend; ++it) { + const int leftColumn = it->x(); + const int topRow = it->y(); + const int rightColumn = leftColumn + it->width() - 1; + const int bottomRow = topRow + it->height() - 1; + for (int r = topRow; r <= bottomRow; r++) + for (int c = leftColumn; c <= rightColumn; c++) { + const int flatIndex = r * numColumns + c; + // Bordering horizontally? + DimensionCellState &horizState = rc[flatIndex].first; + if (c == leftColumn || c == rightColumn) { + horizState = Occupied; + } else { + if (horizState < Spanned) + horizState = Spanned; + } + // Bordering vertically? + DimensionCellState &vertState = rc[flatIndex].second; + if (r == topRow || r == bottomRow) { + vertState = Occupied; + } else { + if (vertState < Spanned) + vertState = Spanned; + } + } + } + if (debugLayout) { + qDebug() << "GridLayoutState::cellStates: " << numRows << " x " << numColumns; + for (int r = 0; r < numRows; r++) + for (int c = 0; c < numColumns; c++) + qDebug() << " Row: " << r << " column: " << c << rc[r * numColumns + c]; + } + return rc; + } + + void GridLayoutState::fromLayout(QGridLayout *l) + { + rowCount = l->rowCount(); + colCount = l->columnCount(); + const int count = l->count(); + for (int i = 0; i < count; i++) { + QLayoutItem *item = l->itemAt(i); + if (!LayoutInfo::isEmptyItem(item)) + widgetItemMap.insert(item->widget(), gridItemInfo(l, i)); + } + } + + void GridLayoutState::applyToLayout(const QDesignerFormEditorInterface *core, QWidget *w) const + { + typedef QMap<QLayoutItem *, QRect> LayoutItemRectMap; + QGridLayout *grid = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, w)); + Q_ASSERT(grid); + if (debugLayout) + qDebug() << ">GridLayoutState::applyToLayout" << *this << *grid; + const bool shrink = grid->rowCount() > rowCount || grid->columnCount() > colCount; + // Build a map of existing items to rectangles via widget map, delete spacers + LayoutItemRectMap itemMap; + while (grid->count()) { + QLayoutItem *item = grid->takeAt(0); + if (!LayoutInfo::isEmptyItem(item)) { + QWidget *itemWidget = item->widget(); + const WidgetItemMap::const_iterator it = widgetItemMap.constFind(itemWidget); + if (it == widgetItemMap.constEnd()) + qFatal("GridLayoutState::applyToLayout: Attempt to apply to a layout that has a widget '%s'/'%s' added after saving the state.", + itemWidget->metaObject()->className(), itemWidget->objectName().toUtf8().constData()); + itemMap.insert(item, it.value()); + } else { + delete item; + } + } + Q_ASSERT(itemMap.size() == widgetItemMap.size()); + // recreate if shrink + if (shrink) + grid = static_cast<QGridLayout*>(recreateManagedLayout(core, w, grid)); + + // Add widgets items + const LayoutItemRectMap::const_iterator icend = itemMap.constEnd(); + for (LayoutItemRectMap::const_iterator it = itemMap.constBegin(); it != icend; ++it) { + const QRect info = it.value(); + grid->addItem(it.key(), info.y(), info.x(), info.height(), info.width()); + } + // create spacers + const CellStates cs = cellStates(itemMap.values(), rowCount, colCount); + for (int r = 0; r < rowCount; r++) + for (int c = 0; c < colCount; c++) + if (needsSpacerItem(cs[r * colCount + c])) + grid->addItem(createGridSpacer(), r, c); + grid->activate(); + if (debugLayout) + qDebug() << "<GridLayoutState::applyToLayout" << *grid; + } + + void GridLayoutState::insertRow(int row) + { + rowCount++; + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int topRow = it.value().y(); + if (topRow >= row) { + it.value().translate(0, 1); + } else { //Over it: Does it span it -> widen? + const int rowSpan = it.value().height(); + if (rowSpan > 1 && topRow + rowSpan > row) + it.value().setHeight(rowSpan + 1); + } + } + } + + void GridLayoutState::insertColumn(int column) + { + colCount++; + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int leftColumn = it.value().x(); + if (leftColumn >= column) { + it.value().translate(1, 0); + } else { // Left of it: Does it span it -> widen? + const int colSpan = it.value().width(); + if (colSpan > 1 && leftColumn + colSpan > column) + it.value().setWidth(colSpan + 1); + } + } + } + + // Simplify: Remove empty columns/rows and such ones that are only spanned (shrink + // spanning items). + // 'AB.C.' 'ABC' + // 'DDDD.' ==> 'DDD' + // 'EF.G.' 'EFG' + bool GridLayoutState::simplify(const QRect &r, bool testOnly) + { + // figure out free rows/columns. + QVector<bool> occupiedRows(rowCount, false); + QVector<bool> occupiedColumns(colCount, false); + // Mark everything outside restriction rectangle as occupied + const int restrictionLeftColumn = r.x(); + const int restrictionRightColumn = restrictionLeftColumn + r.width(); + const int restrictionTopRow = r.y(); + const int restrictionBottomRow = restrictionTopRow + r.height(); + if (restrictionLeftColumn > 0 || restrictionRightColumn < colCount || + restrictionTopRow > 0 || restrictionBottomRow < rowCount) { + for (int r = 0; r < rowCount; r++) + if (r < restrictionTopRow || r >= restrictionBottomRow) + occupiedRows[r] = true; + for (int c = 0; c < colCount; c++) + if (c < restrictionLeftColumn || c >= restrictionRightColumn) + occupiedColumns[c] = true; + } + // figure out free fields and tick off occupied rows and columns + const CellStates cs = cellStates(widgetItemMap.values(), rowCount, colCount); + for (int r = 0; r < rowCount; r++) + for (int c = 0; c < colCount; c++) { + const CellState &state = cs[r * colCount + c]; + if (state.first == Occupied) + occupiedColumns[c] = true; + if (state.second == Occupied) + occupiedRows[r] = true; + } + // Any free rows/columns? + if (occupiedRows.indexOf(false) == -1 && occupiedColumns.indexOf(false) == -1) + return false; + if (testOnly) + return true; + // remove rows + for (int r = rowCount - 1; r >= 0; r--) + if (!occupiedRows[r]) + removeFreeRow(r); + // remove columns + for (int c = colCount - 1; c >= 0; c--) + if (!occupiedColumns[c]) + removeFreeColumn(c); + return true; + } + + void GridLayoutState::removeFreeRow(int removeRow) + { + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int r = it.value().y(); + Q_ASSERT(r != removeRow); // Free rows only + if (r < removeRow) { // Does the item span it? - shrink it + const int rowSpan = it.value().height(); + if (rowSpan > 1) { + const int bottomRow = r + rowSpan; + if (bottomRow > removeRow) + it.value().setHeight(rowSpan - 1); + } + } else + if (r > removeRow) // Item below it? - move. + it.value().translate(0, -1); + } + rowCount--; + } + + void GridLayoutState::removeFreeColumn(int removeColumn) + { + const WidgetItemMap::iterator iend = widgetItemMap.end(); + for (WidgetItemMap::iterator it = widgetItemMap.begin(); it != iend; ++it) { + const int c = it.value().x(); + Q_ASSERT(c != removeColumn); // Free columns only + if (c < removeColumn) { // Does the item span it? - shrink it + const int colSpan = it.value().width(); + if (colSpan > 1) { + const int rightColumn = c + colSpan; + if (rightColumn > removeColumn) + it.value().setWidth(colSpan - 1); + } + } else + if (c > removeColumn) // Item to the right of it? - move. + it.value().translate(-1, 0); + } + colCount--; + } + + // ---------------- GridLayoutHelper + class GridLayoutHelper : public LayoutHelper { + public: + GridLayoutHelper() {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); + + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const; + virtual void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea); + + static void insertRow(QGridLayout *grid, int row); + + private: + QStack<GridLayoutState> m_states; + }; + + void GridLayoutHelper::insertRow(QGridLayout *grid, int row) + { + GridLayoutState state; + state.fromLayout(grid); + state.insertRow(row); + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(grid); + state.applyToLayout(fw->core(), grid->parentWidget()); + } + + QRect GridLayoutHelper::itemInfo(QLayout * lt, int index) const + { + QGridLayout *grid = qobject_cast<QGridLayout *>(lt); + Q_ASSERT(grid); + return gridItemInfo(grid, index); + } + + void GridLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt); + Q_ASSERT(gridLayout); + // check if there are any items. Should be only spacers, else something is wrong + const int row = info.y(); + int column = info.x(); + int colSpan = info.width(); + int rowSpan = info.height(); + // If not empty: A multiselection was dropped on an empty item, insert row + // and spread items along new row + if (!removeEmptyCellsOnGrid(gridLayout, info)) { + int freeColumn = -1; + colSpan = rowSpan = 1; + // First look to the right for a free column + const int columnCount = gridLayout->columnCount(); + for (int c = column; c < columnCount; c++) { + const int idx = findGridItemAt(gridLayout, row, c); + if (idx != -1 && LayoutInfo::isEmptyItem(gridLayout->itemAt(idx))) { + freeColumn = c; + break; + } + } + if (freeColumn != -1) { + removeEmptyCellsOnGrid(gridLayout, QRect(freeColumn, row, 1, 1)); + column = freeColumn; + } else { + GridLayoutHelper::insertRow(gridLayout, row); + column = 0; + } + } + gridLayout->addWidget(w, row , column, rowSpan, colSpan); + } + + void GridLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt); + Q_ASSERT(gridLayout); + const int index = gridLayout->indexOf(widget); + if (index == -1) { + qWarning() << "GridLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; + return; + } + // delete old item and pad with by spacer items + int row, column, rowspan, colspan; + gridLayout->getItemPosition(index, &row, &column, &rowspan, &colspan); + delete gridLayout->takeAt(index); + const int rightColumn = column + colspan; + const int bottomRow = row + rowspan; + for (int c = column; c < rightColumn; c++) + for (int r = row; r < bottomRow; r++) + gridLayout->addItem(createGridSpacer(), r, c); + } + + void GridLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QGridLayout *gridLayout = qobject_cast<QGridLayout *>(lt)) { + const int index = gridLayout->indexOf(before); + if (index != -1) { + int row, column, rowSpan, columnSpan; + gridLayout->getItemPosition (index, &row, &column, &rowSpan, &columnSpan); + const bool visible = before->isVisible(); + delete gridLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + gridLayout->addWidget(after, row, column, rowSpan, columnSpan); + ok = true; + } + } + if (!ok) + qWarning() << "GridLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + void GridLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) + { + QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + m_states.push(gs); + } + + void GridLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) + { + Q_ASSERT(!m_states.empty()); + const GridLayoutState state = m_states.pop(); + state.applyToLayout(core, widgetWithManagedLayout); + } + + bool GridLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const + { + QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + return gs.simplify(restrictionArea, true); + } + + void GridLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) + { + QGridLayout *gridLayout = qobject_cast<QGridLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(gridLayout); + if (debugLayout) + qDebug() << ">GridLayoutHelper::simplify" << *gridLayout; + GridLayoutState gs; + gs.fromLayout(gridLayout); + if (gs.simplify(restrictionArea, false)) + gs.applyToLayout(core, widgetWithManagedLayout); + if (debugLayout) + qDebug() << "<GridLayoutHelper::simplify" << *gridLayout; + } + + // ---------------- FormLayoutHelper + class FormLayoutHelper : public LayoutHelper { + public: + typedef QPair<QWidget *, QWidget *> WidgetPair; + typedef QVector<WidgetPair> FormLayoutState; + + FormLayoutHelper() {} + + virtual QRect itemInfo(QLayout *lt, int index) const; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w); + virtual void removeWidget(QLayout *lt, QWidget *widget); + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after); + + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout); + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout); + + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *, const QRect &) const; + virtual void simplify(const QDesignerFormEditorInterface *, QWidget *, const QRect &); + + private: + static FormLayoutState state(const QFormLayout *lt); + + QStack<FormLayoutState> m_states; + }; + + QRect FormLayoutHelper::itemInfo(QLayout * lt, int index) const + { + QFormLayout *form = qobject_cast<QFormLayout *>(lt); + Q_ASSERT(form); + int row, column, colspan; + getFormLayoutItemPosition(form, index, &row, &column, 0, &colspan); + return QRect(column, row, colspan, 1); + } + + void FormLayoutHelper::insertWidget(QLayout *lt, const QRect &info, QWidget *w) + { + if (debugLayout) + qDebug() << "FormLayoutHelper::insertWidget:" << w << info; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt); + Q_ASSERT(formLayout); + // check if there are any nonspacer items? (Drop on 3rd column or drop of a multiselection + // on an empty item. As the Form layout does not have insert semantics; we need to manually insert a row + const bool insert = !removeEmptyCellsOnGrid(formLayout, info); + formLayoutAddWidget(formLayout, w, info, insert); + QLayoutSupport::createEmptyCells(formLayout); + } + + void FormLayoutHelper::removeWidget(QLayout *lt, QWidget *widget) + { + QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt); + Q_ASSERT(formLayout); + const int index = formLayout->indexOf(widget); + if (index == -1) { + qWarning() << "FormLayoutHelper::removeWidget : Attempt to remove " << widget << " which is not in the layout."; + return; + } + // delete old item and pad with by spacer items + int row, column, colspan; + getFormLayoutItemPosition(formLayout, index, &row, &column, 0, &colspan); + if (debugLayout) + qDebug() << "FormLayoutHelper::removeWidget: #" << index << widget << " at " << row << column << colspan; + delete formLayout->takeAt(index); + if (colspan > 1 || column == 0) + formLayout->setItem(row, QFormLayout::LabelRole, createFormSpacer()); + if (colspan > 1 || column == 1) + formLayout->setItem(row, QFormLayout::FieldRole, createFormSpacer()); + } + + void FormLayoutHelper::replaceWidget(QLayout *lt, QWidget *before, QWidget *after) + { + bool ok = false; + QDesignerWidgetItemInstaller wii; // Make sure we use QDesignerWidgetItem. + if (QFormLayout *formLayout = qobject_cast<QFormLayout *>(lt)) { + const int index = formLayout->indexOf(before); + if (index != -1) { + int row; + QFormLayout::ItemRole role; + formLayout->getItemPosition (index, &row, &role); + const bool visible = before->isVisible(); + delete formLayout->takeAt(index); + if (visible) + before->hide(); + before->setParent(0); + formLayout->setWidget(row, role, after); + ok = true; + } + } + if (!ok) + qWarning() << "FormLayoutHelper::replaceWidget : Unable to replace " << before << " by " << after << " in " << lt; + } + + FormLayoutHelper::FormLayoutState FormLayoutHelper::state(const QFormLayout *lt) + { + const int rowCount = lt->rowCount(); + if (rowCount == 0) + return FormLayoutState(); + FormLayoutState rc(rowCount, WidgetPair(0, 0)); + const int count = lt->count(); + int row, column, colspan; + for (int i = 0; i < count; i++) { + QLayoutItem *item = lt->itemAt(i); + if (!LayoutInfo::isEmptyItem(item)) { + QWidget *w = item->widget(); + Q_ASSERT(w); + getFormLayoutItemPosition(lt, i, &row, &column, 0, &colspan); + if (colspan > 1 || column == 0) + rc[row].first = w; + if (colspan > 1 || column == 1) + rc[row].second = w; + } + } + if (debugLayout) { + qDebug() << "FormLayoutHelper::state: " << rowCount; + for (int r = 0; r < rowCount; r++) + qDebug() << " Row: " << r << rc[r].first << rc[r].second; + } + return rc; + } + + void FormLayoutHelper::pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) + { + QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + m_states.push(state(formLayout)); + } + + void FormLayoutHelper::popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) + { + QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(!m_states.empty() && formLayout); + + const FormLayoutState storedState = m_states.pop(); + const FormLayoutState currentState = state(formLayout); + if (currentState == storedState) + return; + const int rowCount = storedState.size(); + // clear out, shrink if required, but maintain items via map, pad spacers + const BoxLayoutHelper::LayoutItemVector items = BoxLayoutHelper::disassembleLayout(formLayout); + if (rowCount < formLayout->rowCount()) + formLayout = static_cast<QFormLayout*>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout )); + for (int r = 0; r < rowCount; r++) { + QWidget *widgets[FormLayoutColumns] = { storedState[r].first, storedState[r].second }; + const bool spanning = widgets[0] != 0 && widgets[0] == widgets[1]; + if (spanning) { + formLayout->setWidget(r, QFormLayout::SpanningRole, widgets[0]); + } else { + for (int c = 0; c < FormLayoutColumns; c++) { + const QFormLayout::ItemRole role = c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole; + if (widgets[c]) { + Q_ASSERT(BoxLayoutHelper::findItemOfWidget(items, widgets[c])); + formLayout->setWidget(r, role, widgets[c]); + } else { + formLayout->setItem(r, role, createFormSpacer()); + } + } + } + } + } + + bool FormLayoutHelper::canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const + { + const QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + return canSimplifyFormLayout(formLayout, restrictionArea); + } + + void FormLayoutHelper::simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) + { + typedef QPair<QLayoutItem*, QLayoutItem*> LayoutItemPair; + typedef QVector<LayoutItemPair> LayoutItemPairs; + + QFormLayout *formLayout = qobject_cast<QFormLayout *>(LayoutInfo::managedLayout(core, widgetWithManagedLayout)); + Q_ASSERT(formLayout); + if (debugLayout) + qDebug() << "FormLayoutHelper::simplify"; + // Transform into vector of item pairs + const int rowCount = formLayout->rowCount(); + LayoutItemPairs pairs(rowCount, LayoutItemPair(0, 0)); + for (int i = formLayout->count() - 1; i >= 0; i--) { + int row, col,colspan; + getFormLayoutItemPosition(formLayout, i, &row, &col, 0, &colspan); + if (colspan > 1) { + pairs[row].first = pairs[row].second = formLayout->takeAt(i); + } else { + if (col == 0) + pairs[row].first = formLayout->takeAt(i); + else + pairs[row].second = formLayout->takeAt(i); + } + } + // Weed out empty ones + const int bottomCheckRow = qMin(rowCount, restrictionArea.y() + restrictionArea.height()); + for (int r = bottomCheckRow - 1; r >= restrictionArea.y(); r--) + if (LayoutInfo::isEmptyItem(pairs[r].first) && LayoutInfo::isEmptyItem(pairs[r].second)) { + delete pairs[r].first; + delete pairs[r].second; + pairs.remove(r); + } + const int simpleRowCount = pairs.size(); + if (simpleRowCount < rowCount) + formLayout = static_cast<QFormLayout *>(recreateManagedLayout(core, widgetWithManagedLayout, formLayout)); + // repopulate + for (int r = 0; r < simpleRowCount; r++) { + const bool spanning = pairs[r].first == pairs[r].second; + if (spanning) { + formLayout->setItem(r, QFormLayout::SpanningRole, pairs[r].first); + } else { + formLayout->setItem(r, QFormLayout::LabelRole, pairs[r].first); + formLayout->setItem(r, QFormLayout::FieldRole, pairs[r].second); + } + } + } + +LayoutHelper *LayoutHelper::createLayoutHelper(int type) +{ + LayoutHelper *rc = 0; + switch (type) { + case LayoutInfo::HBox: + rc = new BoxLayoutHelper(Qt::Horizontal); + break; + case LayoutInfo::VBox: + rc = new BoxLayoutHelper(Qt::Vertical); + break; + case LayoutInfo::Grid: + rc = new GridLayoutHelper; + break; + case LayoutInfo::Form: + return new FormLayoutHelper; + default: + break; + } + Q_ASSERT(rc); + return rc; +} + +// ---- QLayoutSupport (LayoutDecorationExtension) +QLayoutSupport::QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent) : + QObject(parent), + m_formWindow(formWindow), + m_helper(helper), + m_widget(widget), + m_currentIndex(-1), + m_currentInsertMode(QDesignerLayoutDecorationExtension::InsertWidgetMode) +{ +} + +QLayout * QLayoutSupport::layout() const +{ + return LayoutInfo::managedLayout(m_formWindow->core(), m_widget); +} + +void QLayoutSupport::hideIndicator(Indicator i) +{ + if (m_indicators[i]) + m_indicators[i]->hide(); +} + +void QLayoutSupport::showIndicator(Indicator i, const QRect &geometry, const QPalette &p) +{ + if (!m_indicators[i]) + m_indicators[i] = new qdesigner_internal::InvisibleWidget(m_widget); + QWidget *indicator = m_indicators[i]; + indicator->setAutoFillBackground(true); + indicator->setPalette(p); + indicator->setGeometry(geometry); + indicator->show(); + indicator->raise(); +} + +QLayoutSupport::~QLayoutSupport() +{ + delete m_helper; + for (int i = 0; i < NumIndicators; i++) + if (m_indicators[i]) + m_indicators[i]->deleteLater(); +} + +QGridLayout * QLayoutSupport::gridLayout() const +{ + return qobject_cast<QGridLayout*>(LayoutInfo::managedLayout(m_formWindow->core(), m_widget)); +} + +QRect QLayoutSupport::itemInfo(int index) const +{ + return m_helper->itemInfo(LayoutInfo::managedLayout(m_formWindow->core(), m_widget), index); +} + +void QLayoutSupport::setInsertMode(InsertMode im) +{ + m_currentInsertMode = im; +} + +void QLayoutSupport::setCurrentCell(const QPair<int, int> &cell) +{ + m_currentCell = cell; +} + +void QLayoutSupport::adjustIndicator(const QPoint &pos, int index) +{ + if (index == -1) { // first item goes anywhere + hideIndicator(LeftIndicator); + hideIndicator(TopIndicator); + hideIndicator(RightIndicator); + hideIndicator(BottomIndicator); + return; + } + m_currentIndex = index; + m_currentInsertMode = QDesignerLayoutDecorationExtension::InsertWidgetMode; + + QLayoutItem *item = layout()->itemAt(index); + const QRect g = extendedGeometry(index); + // ### cleanup + if (LayoutInfo::isEmptyItem(item)) { + // Empty grid/form cell. Draw a rectangle + QPalette redPalette; + redPalette.setColor(QPalette::Window, Qt::red); + + showIndicator(LeftIndicator, QRect(g.x(), g.y(), indicatorSize, g.height()), redPalette); + showIndicator(TopIndicator, QRect(g.x(), g.y(), g.width(), indicatorSize), redPalette); + showIndicator(RightIndicator, QRect(g.right(), g.y(), indicatorSize, g.height()), redPalette); + showIndicator(BottomIndicator, QRect(g.x(), g.bottom(), g.width(), indicatorSize), redPalette); + setCurrentCellFromIndicatorOnEmptyCell(m_currentIndex); + } else { + // Append/Insert. Draw a bar left/right or above/below + QPalette bluePalette; + bluePalette.setColor(QPalette::Window, Qt::blue); + hideIndicator(LeftIndicator); + hideIndicator(TopIndicator); + + const int fromRight = g.right() - pos.x(); + const int fromBottom = g.bottom() - pos.y(); + + const int fromLeft = pos.x() - g.x(); + const int fromTop = pos.y() - g.y(); + + const int fromLeftRight = qMin(fromRight, fromLeft ); + const int fromBottomTop = qMin(fromBottom, fromTop); + + const Qt::Orientation indicatorOrientation = fromLeftRight < fromBottomTop ? Qt::Vertical : Qt::Horizontal; + + if (supportsIndicatorOrientation(indicatorOrientation)) { + const QRect r(layout()->geometry().topLeft(), layout()->parentWidget()->size()); + switch (indicatorOrientation) { + case Qt::Vertical: { + hideIndicator(BottomIndicator); + const bool closeToLeft = fromLeftRight == fromLeft; + showIndicator(RightIndicator, QRect(closeToLeft ? g.x() : g.right() + 1 - indicatorSize, 0, indicatorSize, r.height()), bluePalette); + + const int incr = closeToLeft ? 0 : +1; + setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); + } + break; + case Qt::Horizontal: { + hideIndicator(RightIndicator); + const bool closeToTop = fromBottomTop == fromTop; + showIndicator(BottomIndicator, QRect(r.x(), closeToTop ? g.y() : g.bottom() + 1 - indicatorSize, r.width(), indicatorSize), bluePalette); + + const int incr = closeToTop ? 0 : +1; + setCurrentCellFromIndicator(indicatorOrientation, m_currentIndex, incr); + } + break; + } + } else { + hideIndicator(RightIndicator); + hideIndicator(BottomIndicator); + } // can handle indicatorOrientation + } +} + +int QLayoutSupport::indexOf(QLayoutItem *i) const +{ + const QLayout *lt = layout(); + if (!lt) + return -1; + + int index = 0; + + while (QLayoutItem *item = lt->itemAt(index)) { + if (item == i) + return index; + + ++index; + } + + return -1; +} + +int QLayoutSupport::indexOf(QWidget *widget) const +{ + const QLayout *lt = layout(); + if (!lt) + return -1; + + int index = 0; + while (QLayoutItem *item = lt->itemAt(index)) { + if (item->widget() == widget) + return index; + + ++index; + } + + return -1; +} + +QList<QWidget*> QLayoutSupport::widgets(QLayout *layout) const +{ + if (!layout) + return QList<QWidget*>(); + + QList<QWidget*> lst; + int index = 0; + while (QLayoutItem *item = layout->itemAt(index)) { + ++index; + + QWidget *widget = item->widget(); + if (widget && formWindow()->isManaged(widget)) + lst.append(widget); + } + + return lst; +} + +int QLayoutSupport::findItemAt(QGridLayout *gridLayout, int at_row, int at_column) +{ + return findGridItemAt(gridLayout, at_row, at_column); +} + +// Quick check whether simplify should be enabled for grids. May return false positives. +// Note: Calculating the occupied area does not work as spanning items may also be simplified. + +bool QLayoutSupport::canSimplifyQuickCheck(const QGridLayout *gl) +{ + if (!gl) + return false; + const int colCount = gl->columnCount(); + const int rowCount = gl->rowCount(); + if (colCount < 2 || rowCount < 2) + return false; + // try to find a spacer. + const int count = gl->count(); + for (int index = 0; index < count; index++) + if (LayoutInfo::isEmptyItem(gl->itemAt(index))) + return true; + return false; +} + +bool QLayoutSupport::canSimplifyQuickCheck(const QFormLayout *fl) +{ + return canSimplifyFormLayout(fl, QRect(QPoint(0, 0), QSize(32767, 32767))); +} + +// remove dummy spacers +bool QLayoutSupport::removeEmptyCells(QGridLayout *grid, const QRect &area) +{ + return removeEmptyCellsOnGrid(grid, area); +} + +void QLayoutSupport::createEmptyCells(QGridLayout *gridLayout) +{ + Q_ASSERT(gridLayout); + GridLayoutState gs; + gs.fromLayout(gridLayout); + + const GridLayoutState::CellStates cs = GridLayoutState::cellStates(gs.widgetItemMap.values(), gs.rowCount, gs.colCount); + for (int c = 0; c < gs.colCount; c++) + for (int r = 0; r < gs.rowCount; r++) + if (needsSpacerItem(cs[r * gs.colCount + c])) { + const int existingItemIndex = findItemAt(gridLayout, r, c); + if (existingItemIndex == -1) + gridLayout->addItem(createGridSpacer(), r, c); + } +} + +bool QLayoutSupport::removeEmptyCells(QFormLayout *formLayout, const QRect &area) +{ + return removeEmptyCellsOnGrid(formLayout, area); +} + +void QLayoutSupport::createEmptyCells(QFormLayout *formLayout) +{ + // No spanning items here.. + if (const int rowCount = formLayout->rowCount()) + for (int c = 0; c < FormLayoutColumns; c++) + for (int r = 0; r < rowCount; r++) + if (findGridItemAt(formLayout, r, c) == -1) + formLayout->setItem(r, c == 0 ? QFormLayout::LabelRole : QFormLayout::FieldRole, createFormSpacer()); +} + +int QLayoutSupport::findItemAt(const QPoint &pos) const +{ + if (!layout()) + return -1; + + const QLayout *lt = layout(); + const int count = lt->count(); + + if (count == 0) + return -1; + + int best = -1; + int bestIndex = -1; + + for (int index = 0; index < count; index++) { + QLayoutItem *item = lt->itemAt(index); + bool visible = true; + // When dragging widgets within layout, the source widget is invisible and must not be hit + if (const QWidget *w = item->widget()) + visible = w->isVisible(); + if (visible) { + const QRect g = item->geometry(); + + const int dist = (g.center() - pos).manhattanLength(); + if (best == -1 || dist < best) { + best = dist; + bestIndex = index; + } + } + } + return bestIndex; +} + +// ------------ QBoxLayoutSupport (LayoutDecorationExtension) +namespace { +class QBoxLayoutSupport: public QLayoutSupport +{ +public: + QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent = 0); + + virtual void insertWidget(QWidget *widget, const QPair<int, int> &cell); + virtual void removeWidget(QWidget *widget); + virtual void simplify() {} + virtual void insertRow(int /*row*/) {} + virtual void insertColumn(int /*column*/) {} + + virtual int findItemAt(int /*at_row*/, int /*at_column*/) const { return -1; } + +private: + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); + virtual bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const; + virtual QRect extendedGeometry(int index) const; + + const Qt::Orientation m_orientation; +}; + +void QBoxLayoutSupport::removeWidget(QWidget *widget) +{ + QLayout *lt = layout(); + const int index = lt->indexOf(widget); + // Adjust the current cell in case a widget was dragged within the same layout to a position + // of higher index, which happens as follows: + // Drag start: The widget is hidden + // Drop: Current cell is stored, widget is removed and re-added, causing an index offset that needs to be compensated + QPair<int, int> currCell = currentCell(); + switch (m_orientation) { + case Qt::Horizontal: + if (currCell.second > 0 && index < currCell.second ) { + currCell.second--; + setCurrentCell(currCell); + } + break; + case Qt::Vertical: + if (currCell.first > 0 && index < currCell.first) { + currCell.first--; + setCurrentCell(currCell); + } + break; + } + helper()->removeWidget(lt, widget); +} + +QBoxLayoutSupport::QBoxLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, Qt::Orientation orientation, QObject *parent) : + QLayoutSupport(formWindow, widget, new BoxLayoutHelper(orientation), parent), + m_orientation(orientation) +{ +} + +void QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(int index) +{ + qDebug() << "QBoxLayoutSupport::setCurrentCellFromIndicatorOnEmptyCell(): Warning: found a fake spacer inside a vbox layout at " << index; + setCurrentCell(qMakePair(0, 0)); +} + +void QBoxLayoutSupport::insertWidget(QWidget *widget, const QPair<int, int> &cell) +{ + switch (m_orientation) { + case Qt::Horizontal: + helper()->insertWidget(layout(), QRect(cell.second, 0, 1, 1), widget); + break; + case Qt::Vertical: + helper()->insertWidget(layout(), QRect(0, cell.first, 1, 1), widget); + break; + } +} + +void QBoxLayoutSupport::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) +{ + if (m_orientation == Qt::Horizontal && indicatorOrientation == Qt::Vertical) { + setCurrentCell(qMakePair(0, index + increment)); + } else if (m_orientation == Qt::Vertical && indicatorOrientation == Qt::Horizontal) { + setCurrentCell(qMakePair(index + increment, 0)); + } +} + +bool QBoxLayoutSupport::supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const +{ + return m_orientation != indicatorOrientation; +} + +QRect QBoxLayoutSupport::extendedGeometry(int index) const +{ + QLayoutItem *item = layout()->itemAt(index); + // start off with item geometry + QRect g = item->geometry(); + + const QRect info = itemInfo(index); + + // On left border: extend to widget border + if (info.x() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.rx() = layout()->geometry().left(); + g.setTopLeft(topLeft); + } + + // On top border: extend to widget border + if (info.y() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.ry() = layout()->geometry().top(); + g.setTopLeft(topLeft); + } + + // is this the last item? + const QBoxLayout *box = static_cast<const QBoxLayout*>(layout()); + if (index < box->count() -1) + return g; // Nope. + + // extend to widget border + QPoint bottomRight = g.bottomRight(); + switch (m_orientation) { + case Qt::Vertical: + bottomRight.ry() = layout()->geometry().bottom(); + break; + case Qt::Horizontal: + bottomRight.rx() = layout()->geometry().right(); + break; + } + g.setBottomRight(bottomRight); + return g; +} + +// -------------- Base class for QGridLayout-like support classes (LayoutDecorationExtension) +template <class GridLikeLayout> +class GridLikeLayoutSupportBase: public QLayoutSupport +{ +public: + + GridLikeLayoutSupportBase(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = 0) : + QLayoutSupport(formWindow, widget, helper, parent) {} + + void insertWidget(QWidget *widget, const QPair<int, int> &cell); + virtual void removeWidget(QWidget *widget) { helper()->removeWidget(layout(), widget); } + virtual int findItemAt(int row, int column) const; + +protected: + GridLikeLayout *gridLikeLayout() const { + return qobject_cast<GridLikeLayout*>(LayoutInfo::managedLayout(formWindow()->core(), widget())); + } + +private: + + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index); + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment); + virtual bool supportsIndicatorOrientation(Qt::Orientation) const { return true; } + + virtual QRect extendedGeometry(int index) const; + + // Overwrite to check the insertion position (if there are limits) + virtual void checkCellForInsertion(int * /*row*/, int * /*col*/) const {} +}; + +template <class GridLikeLayout> +void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicatorOnEmptyCell(int index) +{ + GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + + setInsertMode(InsertWidgetMode); + int row, column, rowspan, colspan; + + getGridItemPosition(grid, index, &row, &column, &rowspan, &colspan); + setCurrentCell(qMakePair(row, column)); +} + +template <class GridLikeLayout> +void GridLikeLayoutSupportBase<GridLikeLayout>::setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) { + const QRect info = itemInfo(index); + switch (indicatorOrientation) { + case Qt::Vertical: { + setInsertMode(InsertColumnMode); + int row = info.top(); + int column = increment ? info.right() + 1 : info.left(); + checkCellForInsertion(&row, &column); + setCurrentCell(qMakePair(row , column)); + } + break; + case Qt::Horizontal: { + setInsertMode(InsertRowMode); + int row = increment ? info.bottom() + 1 : info.top(); + int column = info.left(); + checkCellForInsertion(&row, &column); + setCurrentCell(qMakePair(row, column)); + } + break; + } +} + +template <class GridLikeLayout> +void GridLikeLayoutSupportBase<GridLikeLayout>::insertWidget(QWidget *widget, const QPair<int, int> &cell) +{ + helper()->insertWidget(layout(), QRect(cell.second, cell.first, 1, 1), widget); +} + +template <class GridLikeLayout> +int GridLikeLayoutSupportBase<GridLikeLayout>::findItemAt(int at_row, int at_column) const +{ + GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + return findGridItemAt(grid, at_row, at_column); +} + +template <class GridLikeLayout> +QRect GridLikeLayoutSupportBase<GridLikeLayout>::extendedGeometry(int index) const +{ + QLayoutItem *item = layout()->itemAt(index); + // start off with item geometry + QRect g = item->geometry(); + + const QRect info = itemInfo(index); + + // On left border: extend to widget border + if (info.x() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.rx() = layout()->geometry().left(); + g.setTopLeft(topLeft); + } + + // On top border: extend to widget border + if (info.y() == 0) { + QPoint topLeft = g.topLeft(); + topLeft.ry() = layout()->geometry().top(); + g.setTopLeft(topLeft); + } + const GridLikeLayout *grid = gridLikeLayout(); + Q_ASSERT(grid); + + // extend to widget border + QPoint bottomRight = g.bottomRight(); + if (gridRowCount(grid) == info.y()) + bottomRight.ry() = layout()->geometry().bottom(); + if (gridColumnCount(grid) == info.x()) + bottomRight.rx() = layout()->geometry().right(); + g.setBottomRight(bottomRight); + return g; +} + +// -------------- QGridLayoutSupport (LayoutDecorationExtension) +class QGridLayoutSupport: public GridLikeLayoutSupportBase<QGridLayout> +{ +public: + + QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + + virtual void simplify(); + virtual void insertRow(int row); + virtual void insertColumn(int column); + +private: +}; + +QGridLayoutSupport::QGridLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : + GridLikeLayoutSupportBase<QGridLayout>(formWindow, widget, new GridLayoutHelper, parent) +{ +} + +void QGridLayoutSupport::insertRow(int row) +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutHelper::insertRow(grid, row); +} + +void QGridLayoutSupport::insertColumn(int column) +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutState state; + state.fromLayout(grid); + state.insertColumn(column); + state.applyToLayout(formWindow()->core(), widget()); +} + +void QGridLayoutSupport::simplify() +{ + QGridLayout *grid = gridLayout(); + Q_ASSERT(grid); + GridLayoutState state; + state.fromLayout(grid); + + const QRect fullArea = QRect(0, 0, state.colCount, state.rowCount); + if (state.simplify(fullArea, false)) + state.applyToLayout(formWindow()->core(), widget()); +} + +// -------------- QFormLayoutSupport (LayoutDecorationExtension) +class QFormLayoutSupport: public GridLikeLayoutSupportBase<QFormLayout> +{ +public: + QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + + virtual void simplify() {} + virtual void insertRow(int /*row*/) {} + virtual void insertColumn(int /*column*/) {} + +private: + virtual void checkCellForInsertion(int * row, int *col) const; +}; + +QFormLayoutSupport::QFormLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) : + GridLikeLayoutSupportBase<QFormLayout>(formWindow, widget, new FormLayoutHelper, parent) +{ +} + +void QFormLayoutSupport::checkCellForInsertion(int *row, int *col) const +{ + if (*col >= FormLayoutColumns) { // Clamp to 2 columns + *col = 1; + (*row)++; + } +} +} // anonymous namespace + +QLayoutSupport *QLayoutSupport::createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent) +{ + const QLayout *layout = LayoutInfo::managedLayout(formWindow->core(), widget); + Q_ASSERT(layout); + QLayoutSupport *rc = 0; + switch (LayoutInfo::layoutType(formWindow->core(), layout)) { + case LayoutInfo::HBox: + rc = new QBoxLayoutSupport(formWindow, widget, Qt::Horizontal, parent); + break; + case LayoutInfo::VBox: + rc = new QBoxLayoutSupport(formWindow, widget, Qt::Vertical, parent); + break; + case LayoutInfo::Grid: + rc = new QGridLayoutSupport(formWindow, widget, parent); + break; + case LayoutInfo::Form: + rc = new QFormLayoutSupport(formWindow, widget, parent); + break; + default: + break; + } + Q_ASSERT(rc); + return rc; +} +} // namespace qdesigner_internal + +// -------------- QLayoutWidget +QLayoutWidget::QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent) + : QWidget(parent), m_formWindow(formWindow), + m_leftMargin(0), m_topMargin(0), m_rightMargin(0), m_bottomMargin(0) +{ +} + +void QLayoutWidget::paintEvent(QPaintEvent*) +{ + if (m_formWindow->currentTool() != 0) + return; + + // only draw red borders if we're editting widgets + + QPainter p(this); + + QMap<int, QMap<int, bool> > excludedRowsForColumn; + QMap<int, QMap<int, bool> > excludedColumnsForRow; + + QLayout *lt = layout(); + QGridLayout *grid = qobject_cast<QGridLayout *>(lt); + if (lt) { + if (const int count = lt->count()) { + p.setPen(QPen(QColor(255, 0, 0, 35), 1)); + for (int i = 0; i < count; i++) { + QLayoutItem *item = lt->itemAt(i); + if (grid) { + int row, column, rowSpan, columnSpan; + grid->getItemPosition(i, &row, &column, &rowSpan, &columnSpan); + QMap<int, bool> rows; + QMap<int, bool> columns; + for (int i = rowSpan; i > 1; i--) + rows[row + i - 2] = true; + for (int i = columnSpan; i > 1; i--) + columns[column + i - 2] = true; + + while (rowSpan > 0) { + excludedColumnsForRow[row + rowSpan - 1].unite(columns); + rowSpan--; + } + while (columnSpan > 0) { + excludedRowsForColumn[column + columnSpan - 1].unite(rows); + columnSpan--; + } + } + if (item->spacerItem()) { + const QRect geometry = item->geometry(); + if (!geometry.isNull()) + p.drawRect(geometry.adjusted(1, 1, -2, -2)); + } + } + } + } + if (grid) { + p.setPen(QPen(QColor(0, 0x80, 0, 0x80), 1)); + const int rowCount = grid->rowCount(); + const int columnCount = grid->columnCount(); + for (int i = 0; i < rowCount; i++) { + for (int j = 0; j < columnCount; j++) { + const QRect cellRect = grid->cellRect(i, j); + if (j < columnCount - 1 && excludedColumnsForRow.value(i).value(j, false) == false) { + const double y0 = (i == 0) + ? 0 : (grid->cellRect(i - 1, j).bottom() + cellRect.top()) / 2.0; + const double y1 = (i == rowCount - 1) + ? height() - 1 : (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; + const double x = (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; + p.drawLine(QPointF(x, y0), QPointF(x, y1)); + } + if (i < rowCount - 1 && excludedRowsForColumn.value(j).value(i, false) == false) { + const double x0 = (j == 0) + ? 0 : (grid->cellRect(i, j - 1).right() + cellRect.left()) / 2.0; + const double x1 = (j == columnCount - 1) + ? width() - 1 : (cellRect.right() + grid->cellRect(i, j + 1).left()) / 2.0; + const double y = (cellRect.bottom() + grid->cellRect(i + 1, j).top()) / 2.0; + p.drawLine(QPointF(x0, y), QPointF(x1, y)); + } + } + } + } + p.setPen(QPen(QColor(255, 0, 0, 128), 1)); + p.drawRect(0, 0, width() - 1, height() - 1); +} + +bool QLayoutWidget::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::LayoutRequest: { + (void) QWidget::event(e); + // Magic: We are layouted, but the parent is not.. + if (layout() && qdesigner_internal::LayoutInfo::layoutType(formWindow()->core(), parentWidget()) == qdesigner_internal::LayoutInfo::NoLayout) { + resize(layout()->totalMinimumSize().expandedTo(size())); + } + + update(); + + return true; + } + + default: + break; + } + + return QWidget::event(e); +} + +int QLayoutWidget::layoutLeftMargin() const +{ + if (m_leftMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(&margin, 0, 0, 0); + return margin; + } + return m_leftMargin; +} + +void QLayoutWidget::setLayoutLeftMargin(int layoutMargin) +{ + m_leftMargin = layoutMargin; + if (layout()) { + int newMargin = m_leftMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(newMargin, top, right, bottom); + } +} + +int QLayoutWidget::layoutTopMargin() const +{ + if (m_topMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, &margin, 0, 0); + return margin; + } + return m_topMargin; +} + +void QLayoutWidget::setLayoutTopMargin(int layoutMargin) +{ + m_topMargin = layoutMargin; + if (layout()) { + int newMargin = m_topMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, newMargin, right, bottom); + } +} + +int QLayoutWidget::layoutRightMargin() const +{ + if (m_rightMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, 0, &margin, 0); + return margin; + } + return m_rightMargin; +} + +void QLayoutWidget::setLayoutRightMargin(int layoutMargin) +{ + m_rightMargin = layoutMargin; + if (layout()) { + int newMargin = m_rightMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, top, newMargin, bottom); + } +} + +int QLayoutWidget::layoutBottomMargin() const +{ + if (m_bottomMargin < 0 && layout()) { + int margin; + layout()->getContentsMargins(0, 0, 0, &margin); + return margin; + } + return m_bottomMargin; +} + +void QLayoutWidget::setLayoutBottomMargin(int layoutMargin) +{ + m_bottomMargin = layoutMargin; + if (layout()) { + int newMargin = m_bottomMargin; + if (newMargin >= 0 && newMargin < ShiftValue) + newMargin = ShiftValue; + int left, top, right, bottom; + layout()->getContentsMargins(&left, &top, &right, &bottom); + layout()->setContentsMargins(left, top, right, newMargin); + } +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qlayout_widget_p.h b/tools/designer/src/lib/shared/qlayout_widget_p.h new file mode 100644 index 0000000..7856088 --- /dev/null +++ b/tools/designer/src/lib/shared/qlayout_widget_p.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QLAYOUT_WIDGET_H +#define QLAYOUT_WIDGET_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerLayoutDecorationExtension> + +#include <QtCore/QPointer> +#include <QtCore/QVariant> +#include <QtGui/QWidget> +#include <QtGui/QLayout> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; +class QGridLayout; +class QFormLayout; + +namespace qdesigner_internal { +// ---- LayoutProperties: Helper struct that stores all layout-relevant properties +// with functions to retrieve and apply to property sheets. Can be used to store the state +// for undo commands and while rebuilding layouts. +struct QDESIGNER_SHARED_EXPORT LayoutProperties +{ + LayoutProperties(); + void clear(); + + enum Margins { LeftMargin, TopMargin, RightMargin, BottomMargin, MarginCount }; + enum Spacings { Spacing, HorizSpacing, VertSpacing, SpacingsCount }; + + enum PropertyMask { + ObjectNameProperty = 0x1, + LeftMarginProperty = 0x2, TopMarginProperty = 0x4, RightMarginProperty = 0x8, BottomMarginProperty = 0x10, + SpacingProperty = 0x20, HorizSpacingProperty = 0x40, VertSpacingProperty = 0x80, + SizeConstraintProperty = 0x100, + FieldGrowthPolicyProperty = 0x200, RowWrapPolicyProperty = 0x400, LabelAlignmentProperty = 0x0800, FormAlignmentProperty = 0x1000, + BoxStretchProperty = 0x2000, GridRowStretchProperty = 0x4000, GridColumnStretchProperty = 0x8000, + GridRowMinimumHeightProperty = 0x10000, GridColumnMinimumWidthProperty = 0x20000, + AllProperties = 0xFFFF}; + + // return a PropertyMask of visible properties + static int visibleProperties(const QLayout *layout); + + // Retrieve from /apply to sheet: A property mask is returned indicating the properties found in the sheet + int fromPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask = AllProperties); + int toPropertySheet(const QDesignerFormEditorInterface *core, QLayout *l, int mask = AllProperties, bool applyChanged = true) const; + + int m_margins[MarginCount]; + bool m_marginsChanged[MarginCount]; + + int m_spacings[SpacingsCount]; + bool m_spacingsChanged[SpacingsCount]; + + QVariant m_objectName; // receives a PropertySheetStringValue + bool m_objectNameChanged; + QVariant m_sizeConstraint; + bool m_sizeConstraintChanged; + + bool m_fieldGrowthPolicyChanged; + QVariant m_fieldGrowthPolicy; + bool m_rowWrapPolicyChanged; + QVariant m_rowWrapPolicy; + bool m_labelAlignmentChanged; + QVariant m_labelAlignment; + bool m_formAlignmentChanged; + QVariant m_formAlignment; + + bool m_boxStretchChanged; + QVariant m_boxStretch; + + bool m_gridRowStretchChanged; + QVariant m_gridRowStretch; + + bool m_gridColumnStretchChanged; + QVariant m_gridColumnStretch; + + bool m_gridRowMinimumHeightChanged; + QVariant m_gridRowMinimumHeight; + + bool m_gridColumnMinimumWidthChanged; + QVariant m_gridColumnMinimumWidth; +}; + +// -- LayoutHelper: For use with the 'insert widget'/'delete widget' command, +// able to store and restore states. +// This could become part of 'QDesignerLayoutDecorationExtensionV2', +// but to keep any existing old extensions working, it is provided as +// separate class with a factory function. +class LayoutHelper { +protected: + LayoutHelper(); + +public: + virtual ~LayoutHelper(); + + static LayoutHelper *createLayoutHelper(int type); + + static int indexOf(const QLayout *lt, const QWidget *widget); + + // Return area of an item (x == columns) + QRect itemInfo(QLayout *lt, const QWidget *widget) const; + + virtual QRect itemInfo(QLayout *lt, int index) const = 0; + virtual void insertWidget(QLayout *lt, const QRect &info, QWidget *w) = 0; + virtual void removeWidget(QLayout *lt, QWidget *widget) = 0; + // Since 4.5: The 'morphing' feature requires an API for replacing widgets on layouts. + virtual void replaceWidget(QLayout *lt, QWidget *before, QWidget *after) = 0; + + // Simplify a grid, remove empty columns, rows within the rectangle + // The DeleteWidget command restricts the area to be simplified. + virtual bool canSimplify(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout, const QRect &restrictionArea) const = 0; + virtual void simplify(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout, const QRect &restrictionArea) = 0; + + // Push and pop a state. Can be used for implementing undo for + // simplify/row, column insertion commands, provided that + // the widgets remain the same. + virtual void pushState(const QDesignerFormEditorInterface *core, const QWidget *widgetWithManagedLayout) = 0; + virtual void popState(const QDesignerFormEditorInterface *core, QWidget *widgetWithManagedLayout) = 0; +}; + +// Base class for layout decoration extensions. +class QDESIGNER_SHARED_EXPORT QLayoutSupport: public QObject, public QDesignerLayoutDecorationExtension +{ + Q_OBJECT + Q_INTERFACES(QDesignerLayoutDecorationExtension) + +protected: + QLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, LayoutHelper *helper, QObject *parent = 0); + +public: + virtual ~QLayoutSupport(); + + inline QDesignerFormWindowInterface *formWindow() const { return m_formWindow; } + + // DecorationExtension V2 + LayoutHelper* helper() const { return m_helper; } + + // DecorationExtension + virtual int currentIndex() const { return m_currentIndex; } + + virtual InsertMode currentInsertMode() const { return m_currentInsertMode; } + + virtual QPair<int, int> currentCell() const { return m_currentCell; } + + virtual int findItemAt(const QPoint &pos) const; + virtual int indexOf(QWidget *widget) const; + virtual int indexOf(QLayoutItem *item) const; + + virtual void adjustIndicator(const QPoint &pos, int index); + + virtual QList<QWidget*> widgets(QLayout *layout) const; + + // Pad empty cells with dummy spacers. Called by layouting commands. + static void createEmptyCells(QGridLayout *gridLayout); + // remove dummy spacers in the area. Returns false if there are non-empty items in the way + static bool removeEmptyCells(QGridLayout *gridLayout, const QRect &area); + static void createEmptyCells(QFormLayout *formLayout); // ditto. + static bool removeEmptyCells(QFormLayout *formLayout, const QRect &area); + + // grid helpers: find item index + static int findItemAt(QGridLayout *, int row, int column); + // grid helpers: Quick check whether simplify should be enabled for grids. May return false positives. + static bool canSimplifyQuickCheck(const QGridLayout *); + static bool canSimplifyQuickCheck(const QFormLayout *fl); + // Factory function, create layout support according to layout type of widget + static QLayoutSupport *createLayoutSupport(QDesignerFormWindowInterface *formWindow, QWidget *widget, QObject *parent = 0); + +protected: + // figure out insertion position and mode from indicator on empty cell if supported + virtual void setCurrentCellFromIndicatorOnEmptyCell(int index) = 0; + // figure out insertion position and mode from indicator + virtual void setCurrentCellFromIndicator(Qt::Orientation indicatorOrientation, int index, int increment) = 0; + + // Overwrite to return the extended geometry of an item, that is, + // if it is a border item, include the widget border for the indicator to work correctly + virtual QRect extendedGeometry(int index) const = 0; + virtual bool supportsIndicatorOrientation(Qt::Orientation indicatorOrientation) const = 0; + + QRect itemInfo(int index) const; + QLayout *layout() const; + QGridLayout *gridLayout() const; + QWidget *widget() const { return m_widget; } + + void setInsertMode(InsertMode im); + void setCurrentCell(const QPair<int, int> &cell); + +private: + enum Indicator { LeftIndicator, TopIndicator, RightIndicator, BottomIndicator, NumIndicators }; + + void hideIndicator(Indicator i); + void showIndicator(Indicator i, const QRect &geometry, const QPalette &); + + QDesignerFormWindowInterface *m_formWindow; + LayoutHelper* m_helper; + + QPointer<QWidget> m_widget; + QPointer<QWidget> m_indicators[NumIndicators]; + int m_currentIndex; + InsertMode m_currentInsertMode; + QPair<int, int> m_currentCell; +}; +} // namespace qdesigner_internal + +// Red layout widget. +class QDESIGNER_SHARED_EXPORT QLayoutWidget: public QWidget +{ + Q_OBJECT +public: + explicit QLayoutWidget(QDesignerFormWindowInterface *formWindow, QWidget *parent = 0); + + int layoutLeftMargin() const; + void setLayoutLeftMargin(int layoutMargin); + + int layoutTopMargin() const; + void setLayoutTopMargin(int layoutMargin); + + int layoutRightMargin() const; + void setLayoutRightMargin(int layoutMargin); + + int layoutBottomMargin() const; + void setLayoutBottomMargin(int layoutMargin); + + inline QDesignerFormWindowInterface *formWindow() const { return m_formWindow; } + +protected: + virtual bool event(QEvent *e); + virtual void paintEvent(QPaintEvent *e); + +private: + QDesignerFormWindowInterface *m_formWindow; + int m_leftMargin; + int m_topMargin; + int m_rightMargin; + int m_bottomMargin; +}; + +QT_END_NAMESPACE + +#endif // QDESIGNER_WIDGET_H diff --git a/tools/designer/src/lib/shared/qscripthighlighter.cpp b/tools/designer/src/lib/shared/qscripthighlighter.cpp new file mode 100644 index 0000000..3f1d638 --- /dev/null +++ b/tools/designer/src/lib/shared/qscripthighlighter.cpp @@ -0,0 +1,468 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qscripthighlighter_p.h" + +#include <QtCore/QSet> + +QT_BEGIN_NAMESPACE + +static const QSet<QString> &qscriptKeywords() { + static QSet<QString> keywords; + if (keywords.empty()) { + keywords.insert(QLatin1String("Infinity")); + keywords.insert(QLatin1String("NaN")); + keywords.insert(QLatin1String("abstract")); + keywords.insert(QLatin1String("boolean")); + keywords.insert(QLatin1String("break")); + keywords.insert(QLatin1String("byte")); + keywords.insert(QLatin1String("case")); + keywords.insert(QLatin1String("catch")); + keywords.insert(QLatin1String("char")); + keywords.insert(QLatin1String("class")); + keywords.insert(QLatin1String("const")); + keywords.insert(QLatin1String("constructor")); + keywords.insert(QLatin1String("continue")); + keywords.insert(QLatin1String("debugger")); + keywords.insert(QLatin1String("default")); + keywords.insert(QLatin1String("delete")); + keywords.insert(QLatin1String("do")); + keywords.insert(QLatin1String("double")); + keywords.insert(QLatin1String("else")); + keywords.insert(QLatin1String("enum")); + keywords.insert(QLatin1String("export")); + keywords.insert(QLatin1String("extends")); + keywords.insert(QLatin1String("false")); + keywords.insert(QLatin1String("final")); + keywords.insert(QLatin1String("finally")); + keywords.insert(QLatin1String("float")); + keywords.insert(QLatin1String("for")); + keywords.insert(QLatin1String("function")); + keywords.insert(QLatin1String("goto")); + keywords.insert(QLatin1String("if")); + keywords.insert(QLatin1String("implements")); + keywords.insert(QLatin1String("import")); + keywords.insert(QLatin1String("in")); + keywords.insert(QLatin1String("instanceof")); + keywords.insert(QLatin1String("int")); + keywords.insert(QLatin1String("interface")); + keywords.insert(QLatin1String("long")); + keywords.insert(QLatin1String("native")); + keywords.insert(QLatin1String("new")); + keywords.insert(QLatin1String("package")); + keywords.insert(QLatin1String("private")); + keywords.insert(QLatin1String("protected")); + keywords.insert(QLatin1String("public")); + keywords.insert(QLatin1String("return")); + keywords.insert(QLatin1String("short")); + keywords.insert(QLatin1String("static")); + keywords.insert(QLatin1String("super")); + keywords.insert(QLatin1String("switch")); + keywords.insert(QLatin1String("synchronized")); + keywords.insert(QLatin1String("this")); + keywords.insert(QLatin1String("throw")); + keywords.insert(QLatin1String("throws")); + keywords.insert(QLatin1String("transient")); + keywords.insert(QLatin1String("true")); + keywords.insert(QLatin1String("try")); + keywords.insert(QLatin1String("typeof")); + keywords.insert(QLatin1String("undefined")); + keywords.insert(QLatin1String("var")); + keywords.insert(QLatin1String("void")); + keywords.insert(QLatin1String("volatile")); + keywords.insert(QLatin1String("while")); + keywords.insert(QLatin1String("with")); // end + } + return keywords; +} + +static QSet<QChar> alphaChars() { + QSet<QChar> rc; + const QString alpha = QLatin1String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + foreach (QChar chr, alpha) + rc.insert(chr); + return rc; +} + +namespace qdesigner_internal { + +QScriptHighlighter::QScriptHighlighter(QTextDocument *parent) + : QSyntaxHighlighter(parent) +{ + m_numberFormat.setForeground(Qt::blue); + m_stringFormat.setForeground(Qt::darkGreen); + m_typeFormat.setForeground(Qt::darkMagenta); + m_keywordFormat.setForeground(Qt::darkYellow); + m_labelFormat.setForeground(Qt::darkRed); + m_commentFormat.setForeground(Qt::red); + //m_commentFormat.setFontFamily("times"); + m_commentFormat.setFontItalic(true); + m_preProcessorFormat.setForeground(Qt::darkBlue); +} + +void QScriptHighlighter::highlightBlock(const QString &text) +{ + // states + enum { + StateStandard, + StateCommentStart1, + StateCCommentStart2, + StateCppCommentStart2, + StateCComment, + StateCppComment, + StateCCommentEnd1, + StateCCommentEnd2, + StateStringStart, + StateString, + StateStringEnd, + StateString2Start, + StateString2, + StateString2End, + StateNumber, + StatePreProcessor, + NumStates + }; + // tokens + enum { + InputAlpha, + InputNumber, + InputAsterix, + InputSlash, + InputParen, + InputSpace, + InputHash, + InputQuotation, + InputApostrophe, + InputSep, + NumInputs + }; + + static const uchar table[NumStates][NumInputs] = { + { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStandard + { StateStandard, StateNumber, StateCCommentStart2, StateCppCommentStart2, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCommentStart1 + { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentStart2 + { StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // CppCommentStart2 + { StateCComment, StateCComment, StateCCommentEnd1, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCComment + { StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment, StateCppComment }, // StateCppComment + { StateCComment, StateCComment, StateCCommentEnd1, StateCCommentEnd2, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment, StateCComment }, // StateCCommentEnd1 + { StateStandard, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateCCommentEnd2 + { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateStringStart + { StateString, StateString, StateString, StateString, StateString, StateString, StateString, StateStringEnd, StateString, StateString }, // StateString + { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateStringEnd + { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2Start + { StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2, StateString2End, StateString2 }, // StateString2 + { StateStandard, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateString2End + { StateNumber, StateNumber, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard }, // StateNumber + { StatePreProcessor, StateStandard, StateStandard, StateCommentStart1, StateStandard, StateStandard, StatePreProcessor, StateStringStart, StateString2Start, StateStandard } // StatePreProcessor + }; + + QString buffer; + buffer.reserve(text.length()); + QTextCharFormat emptyFormat; + + int state = StateStandard; + const int previousState = previousBlockState(); + if (previousState != -1) + state = previousState; + + if (text.isEmpty()) { + setCurrentBlockState(previousState); + return; + } + + int input = -1; + int i = 0; + bool lastWasBackSlash = false; + bool makeLastStandard = false; + + static const QSet<QChar> alphabeth = alphaChars(); + static const QString mathChars = QString::fromLatin1("xXeE"); + static const QString numbers = QString::fromLatin1("0123456789"); + bool questionMark = false; + QChar lastChar; + QString firstWord; + forever { + const QChar c = text.at(i); + + if (lastWasBackSlash) { + input = InputSep; + } else { + switch (c.toLatin1()) { + case '*': + input = InputAsterix; + break; + case '/': + input = InputSlash; + break; + case '(': case '[': case '{': + input = InputParen; + if (state == StateStandard + || state == StateNumber + || state == StatePreProcessor + || state == StateCCommentEnd2 + || state == StateCCommentEnd1 + || state == StateString2End + || state == StateStringEnd + ) + //blockData->parentheses << Parenthesis(Parenthesis::Open, c, i); + break; + case ')': case ']': case '}': + input = InputParen; + if (state == StateStandard + || state == StateNumber + || state == StatePreProcessor + || state == StateCCommentEnd2 + || state == StateCCommentEnd1 + || state == StateString2End + || state == StateStringEnd + ) { + //blockData->parentheses << Parenthesis(Parenthesis::Closed, c, i); + } + break; + case '#': + input = InputHash; + break; + case '"': + input = InputQuotation; + break; + case '\'': + input = InputApostrophe; + break; + case ' ': + input = InputSpace; + break; + case '1': case '2': case '3': case '4': case '5': + case '6': case '7': case '8': case '9': case '0': + if (alphabeth.contains(lastChar) + && (!mathChars.contains(lastChar) || !numbers.contains(text.at(i - 1)))) { + input = InputAlpha; + } else { + if (input == InputAlpha && numbers.contains(lastChar)) + input = InputAlpha; + else + input = InputNumber; + } + break; + case ':': { + input = InputAlpha; + QChar nextChar = QLatin1Char(' '); + if (i < text.length() - 1) + nextChar = text.at(i + 1); + if (state == StateStandard && !questionMark && + lastChar != QLatin1Char(':') && nextChar != QLatin1Char(':')) { + for (int j = 0; j < i; ++j) { + if (format(j) == emptyFormat) + setFormat(j, 1, m_labelFormat); + } + } + break; + } + default: { + if (!questionMark && c == QLatin1Char('?')) + questionMark = true; + if (c.isLetter() || c == QLatin1Char('_')) + input = InputAlpha; + else + input = InputSep; + } break; + } + } + + lastWasBackSlash = !lastWasBackSlash && c == QLatin1Char('\\'); + + if (input == InputAlpha) + buffer += c; + + state = table[state][input]; + + switch (state) { + case StateStandard: { + setFormat(i, 1, emptyFormat); + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + if (!buffer.isEmpty() && input != InputAlpha ) { + highlightKeyword(i, buffer); + buffer.clear(); + } + } break; + case StateCommentStart1: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = true; + buffer.resize(0); + break; + case StateCCommentStart2: + setFormat(i - 1, 2, m_commentFormat); + makeLastStandard = false; + buffer.resize(0); + break; + case StateCppCommentStart2: + setFormat(i - 1, 2, m_commentFormat); + makeLastStandard = false; + buffer.resize(0); + break; + case StateCComment: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCppComment: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCCommentEnd1: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateCCommentEnd2: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_commentFormat); + buffer.resize(0); + break; + case StateStringStart: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_stringFormat); + buffer.resize(0); + break; + case StateStringEnd: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString2Start: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateString2: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_stringFormat); + buffer.resize(0); + break; + case StateString2End: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, emptyFormat); + buffer.resize(0); + break; + case StateNumber: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat( i, 1, m_numberFormat); + buffer.resize(0); + break; + case StatePreProcessor: + if (makeLastStandard) + setFormat(i - 1, 1, emptyFormat); + makeLastStandard = false; + setFormat(i, 1, m_preProcessorFormat); + buffer.resize(0); + break; + } + + lastChar = c; + i++; + if (i >= text.length()) + break; + } + + highlightKeyword(text.length(), buffer); + + if (state == StateCComment + || state == StateCCommentEnd1 + || state == StateCCommentStart2 + ) { + state = StateCComment; + } else if (state == StateString) { + state = StateString; + } else if (state == StateString2) { + state = StateString2; + } else { + state = StateStandard; + } + + setCurrentBlockState(state); +} + +void QScriptHighlighter::highlightKeyword(int currentPos, const QString &buffer) +{ + if (buffer.isEmpty()) + return; + + if (buffer.at(0) == QLatin1Char('Q')) { + setFormat(currentPos - buffer.length(), buffer.length(), m_typeFormat); + } else { + if (qscriptKeywords().contains(buffer)) { + setFormat(currentPos - buffer.length(), buffer.length(), m_keywordFormat); + } + } +} +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qscripthighlighter_p.h b/tools/designer/src/lib/shared/qscripthighlighter_p.h new file mode 100644 index 0000000..9530ba2 --- /dev/null +++ b/tools/designer/src/lib/shared/qscripthighlighter_p.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSCRIPTSYNTAXHIGHLIGHTER_H +#define QSCRIPTSYNTAXHIGHLIGHTER_H + +#include <QtGui/QSyntaxHighlighter> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +class QScriptHighlighter : public QSyntaxHighlighter +{ +public: + explicit QScriptHighlighter(QTextDocument *parent); + virtual void highlightBlock(const QString &text); + +private: + void highlightKeyword(int currentPos, const QString &buffer); + + QTextCharFormat m_numberFormat; + QTextCharFormat m_stringFormat; + QTextCharFormat m_typeFormat; + QTextCharFormat m_keywordFormat; + QTextCharFormat m_labelFormat; + QTextCharFormat m_commentFormat; + QTextCharFormat m_preProcessorFormat; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/qsimpleresource.cpp b/tools/designer/src/lib/shared/qsimpleresource.cpp new file mode 100644 index 0000000..8d29002 --- /dev/null +++ b/tools/designer/src/lib/shared/qsimpleresource.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsimpleresource_p.h" +#include "widgetfactory_p.h" + +#include <formscriptrunner_p.h> +#include <properties_p.h> +#include <ui4_p.h> + +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerLanguageExtension> +#include <script_p.h> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerCustomWidgetInterface> +#include <QtDesigner/extrainfo.h> + +#include <QtGui/QIcon> +#include <QtGui/QWidget> +#include <QtGui/QAction> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace { + typedef QList<DomWidgetData*> DomWidgetDataList; + typedef QList<DomProperty*> DomPropertyList; + typedef QList<QDesignerCustomWidgetInterface *> CustomWidgetInterfaces; +} + +namespace qdesigner_internal { + +bool QSimpleResource::m_warningsEnabled = true; + +QSimpleResource::QSimpleResource(QDesignerFormEditorInterface *core) : + QAbstractFormBuilder(), + m_core(core) +{ + QString workingDirectory = QDir::homePath(); + workingDirectory += QDir::separator(); + workingDirectory += QLatin1String(".designer"); + setWorkingDirectory(QDir(workingDirectory)); + // Disable scripting in the editors. + formScriptRunner()-> setOptions(QFormScriptRunner::DisableScripts); +} + +QSimpleResource::~QSimpleResource() +{ + +} + +QBrush QSimpleResource::setupBrush(DomBrush *brush) +{ + return QAbstractFormBuilder::setupBrush(brush); +} + +DomBrush *QSimpleResource::saveBrush(const QBrush &brush) +{ + return QAbstractFormBuilder::saveBrush(brush); +} + +QIcon QSimpleResource::nameToIcon(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QSimpleResource::nameToIcon() is obsoleted"; + return QIcon(); +} + +QString QSimpleResource::iconToFilePath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::iconToFilePath() is obsoleted"; + return QString(); +} + +QString QSimpleResource::iconToQrcPath(const QIcon &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::iconToQrcPath() is obsoleted"; + return QString(); +} + +QPixmap QSimpleResource::nameToPixmap(const QString &filePath, const QString &qrcPath) +{ + Q_UNUSED(filePath) + Q_UNUSED(qrcPath) + qWarning() << "QSimpleResource::nameToPixmap() is obsoleted"; + return QPixmap(); +} + +QString QSimpleResource::pixmapToFilePath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::pixmapToFilePath() is obsoleted"; + return QString(); +} + +QString QSimpleResource::pixmapToQrcPath(const QPixmap &pm) const +{ + Q_UNUSED(pm) + qWarning() << "QSimpleResource::pixmapToQrcPath() is obsoleted"; + return QString(); +} + +DomScript *QSimpleResource::createScript(const QString &script, ScriptSource source) +{ + if (script.isEmpty()) + return 0; + DomScript *domScript = new DomScript(); + switch (source) { + case ScriptExtension: + domScript->setAttributeSource(QLatin1String("extension")); + break; + case ScriptDesigner: + domScript->setAttributeSource(QLatin1String("designer")); + break; + case ScriptCustomWidgetPlugin: + domScript->setAttributeSource(QLatin1String("customwidgetplugin")); + break; + } + domScript->setAttributeLanguage(QLatin1String("Qt Script")); + domScript->setText(script); + return domScript; +} + +// Add a script to a list of DomScripts unless empty +void QSimpleResource::addScript(const QString &script, ScriptSource source, DomScripts &domScripts) +{ + if (DomScript *domScript = createScript(script, source)) { + domScripts += domScript; + } +} + +void QSimpleResource::addExtensionDataToDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget) +{ + QExtensionManager *emgr = core->extensionManager(); + if (QDesignerExtraInfoExtension *extra = qt_extension<QDesignerExtraInfoExtension*>(emgr, widget)) { + extra->saveWidgetExtraInfo(ui_widget); + } + if (QDesignerScriptExtension *scriptExt = qt_extension<QDesignerScriptExtension*>(emgr, widget)) { + // Add internal state + const QVariantMap data = scriptExt->data(); + if (!data.empty()) { + // Convert the map to a DomState. + // We pass on the widget for property introspection. Thus, non-designable properties + // that have to be converted using QMetaObject (enums and the like) will work. + DomPropertyList properties; + const QVariantMap::const_iterator vcend = data.constEnd(); + for (QVariantMap::const_iterator it = data.constBegin(); it != vcend; ++it) { + if (DomProperty *prop = variantToDomProperty(afb, widget->metaObject(), it.key(), it.value())) + properties += prop; + } + if (!properties.empty()) { + DomWidgetData *domData = new DomWidgetData; + domData->setElementProperty(properties); + DomWidgetDataList domDataList; + domDataList += domData; + ui_widget->setElementWidgetData(domDataList); + } + + } + // Add script + const QString script = scriptExt->script(); + if (!script.isEmpty()) { + DomScripts domScripts = ui_widget->elementScript(); + addScript(script, ScriptExtension, domScripts); + ui_widget->setElementScript(domScripts); + } + } +} + +void QSimpleResource::applyExtensionDataFromDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget, bool applyState) +{ + QExtensionManager *emgr = core->extensionManager(); + if (QDesignerExtraInfoExtension *extra = qt_extension<QDesignerExtraInfoExtension*>(emgr, widget)) { + extra->loadWidgetExtraInfo(ui_widget); + } + if (applyState) { + if (QDesignerScriptExtension *scriptExt = qt_extension<QDesignerScriptExtension*>(emgr, widget)) { + // Apply the state. + // We pass on the widget for property introspection. Thus, non-designable properties + // that have to be converted using QMetaObject (enums and the like) will work. + QVariantMap data; + DomWidgetDataList domDataList = ui_widget->elementWidgetData(); + if (!domDataList.empty()) { + foreach (const DomWidgetData *domData, domDataList) { + const DomPropertyList properties = domData->elementProperty(); + foreach(const DomProperty *prop, properties) { + const QVariant vprop = domPropertyToVariant(afb, widget->metaObject(), prop); + if (vprop.type() != QVariant::Invalid) + data.insert(prop->attributeName(), vprop); + } + } + } + scriptExt->setData(data); + } + } +} + +QString QSimpleResource::customWidgetScript(QDesignerFormEditorInterface *core, QObject *object) +{ + return customWidgetScript(core, qdesigner_internal::WidgetFactory::classNameOf(core, object)); +} + +bool QSimpleResource::hasCustomWidgetScript(QDesignerFormEditorInterface *, QObject *) +{ + return false; +} + +QString QSimpleResource::customWidgetScript(QDesignerFormEditorInterface *, const QString &) +{ + return QString(); +} + +bool QSimpleResource::setWarningsEnabled(bool warningsEnabled) +{ + const bool rc = m_warningsEnabled; + m_warningsEnabled = warningsEnabled; + return rc; +} + +bool QSimpleResource::warningsEnabled() +{ + return m_warningsEnabled; +} + +// ------------ FormBuilderClipboard + +FormBuilderClipboard::FormBuilderClipboard(QWidget *w) +{ + m_widgets += w; +} + +bool FormBuilderClipboard::empty() const +{ + return m_widgets.empty() && m_actions.empty(); +} +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/qsimpleresource_p.h b/tools/designer/src/lib/shared/qsimpleresource_p.h new file mode 100644 index 0000000..8d45c3c --- /dev/null +++ b/tools/designer/src/lib/shared/qsimpleresource_p.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QSIMPLERESOURCE_H +#define QSIMPLERESOURCE_H + +#include "shared_global_p.h" +#include "abstractformbuilder.h" + +QT_BEGIN_NAMESPACE + +class DomScript; + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QSimpleResource : public QAbstractFormBuilder +{ +public: + explicit QSimpleResource(QDesignerFormEditorInterface *core); + virtual ~QSimpleResource(); + + QBrush setupBrush(DomBrush *brush); + DomBrush *saveBrush(const QBrush &brush); + + inline QDesignerFormEditorInterface *core() const + { return m_core; } + + // Query extensions for additional data + static void addExtensionDataToDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget); + static void applyExtensionDataFromDOM(QAbstractFormBuilder *afb, + QDesignerFormEditorInterface *core, + DomWidget *ui_widget, QWidget *widget, + bool applyState); + // Enable warnings while saving. Turn off for backups. + static bool setWarningsEnabled(bool warningsEnabled); + static bool warningsEnabled(); + // Return the script returned by the CustomWidget codeTemplate API + static QString customWidgetScript(QDesignerFormEditorInterface *core, QObject *object); + static QString customWidgetScript(QDesignerFormEditorInterface *core, const QString &className); + static bool hasCustomWidgetScript(QDesignerFormEditorInterface *core, QObject *object); + +protected: + virtual QIcon nameToIcon(const QString &filePath, const QString &qrcPath); + virtual QString iconToFilePath(const QIcon &pm) const; + virtual QString iconToQrcPath(const QIcon &pm) const; + virtual QPixmap nameToPixmap(const QString &filePath, const QString &qrcPath); + virtual QString pixmapToFilePath(const QPixmap &pm) const; + virtual QString pixmapToQrcPath(const QPixmap &pm) const; + + enum ScriptSource { ScriptDesigner, ScriptExtension, ScriptCustomWidgetPlugin }; + static DomScript*createScript(const QString &script, ScriptSource source); + typedef QList<DomScript*> DomScripts; + static void addScript(const QString &script, ScriptSource source, DomScripts &domScripts); + +private: + static bool m_warningsEnabled; + QDesignerFormEditorInterface *m_core; +}; + +// Contents of clipboard for formbuilder copy and paste operations +// (Actions and widgets) +struct QDESIGNER_SHARED_EXPORT FormBuilderClipboard { + typedef QList<QAction*> ActionList; + + FormBuilderClipboard() {} + FormBuilderClipboard(QWidget *w); + + bool empty() const; + + QWidgetList m_widgets; + ActionList m_actions; +}; + +// Base class for a form builder used in the editor that +// provides copy and paste.(move into base interface) +class QDESIGNER_SHARED_EXPORT QEditorFormBuilder : public QSimpleResource +{ +public: + explicit QEditorFormBuilder(QDesignerFormEditorInterface *core) : QSimpleResource(core) {} + + virtual bool copy(QIODevice *dev, const FormBuilderClipboard &selection) = 0; + virtual DomUI *copy(const FormBuilderClipboard &selection) = 0; + + // A widget parent needs to be specified, otherwise, the widget factory cannot locate the form window via parent + // and thus is not able to construct special widgets (QLayoutWidget). + virtual FormBuilderClipboard paste(DomUI *ui, QWidget *widgetParent, QObject *actionParent = 0) = 0; + virtual FormBuilderClipboard paste(QIODevice *dev, QWidget *widgetParent, QObject *actionParent = 0) = 0; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/qtresourceeditordialog.cpp b/tools/designer/src/lib/shared/qtresourceeditordialog.cpp new file mode 100644 index 0000000..adddf72 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourceeditordialog.cpp @@ -0,0 +1,2226 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "abstractformeditor.h" +#include "qtresourceeditordialog_p.h" +#include "ui_qtresourceeditordialog.h" +#include "qtresourcemodel_p.h" +#include "iconloader_p.h" + +#include <abstractdialoggui_p.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QCoreApplication> +#include <QtXml/QDomDocument> +#include <QtGui/QMenu> +#include <QtGui/QHeaderView> +#include <QtGui/QInputDialog> +#include <QtGui/QMessageBox> +#include <QtGui/QPushButton> +#include <QtGui/QStandardItemModel> + +QT_BEGIN_NAMESPACE + +static const char *rccRootTag = "RCC"; +static const char *rccTag = "qresource"; +static const char *rccFileTag = "file"; +static const char *rccAliasAttribute = "alias"; +static const char *rccPrefixAttribute = "prefix"; +static const char *rccLangAttribute = "lang"; +static const char *SplitterPosition = "SplitterPosition"; +static const char *Geometry = "Geometry"; +static const char *QrcDialogC = "QrcDialog"; + +static QString msgOverwrite(const QString &fname) +{ + return QApplication::translate("QtResourceEditorDialog", "%1 already exists.\nDo you want to replace it?", 0, QApplication::UnicodeUTF8).arg(fname); +} + +static QString msgTagMismatch(const QString &got, const QString &expected) +{ + return QApplication::translate("QtResourceEditorDialog", "The file does not appear to be a resource file; element '%1' was found where '%2' was expected.").arg(got).arg(expected); +} + +namespace { + +// below 3 data classes should be derived from QSharedData and made implicit shared class +struct QtResourceFileData { + QString path; + QString alias; + bool operator==(const QtResourceFileData &other) const { + if (path == other.path && alias == other.alias) + return true; + return false; + } +}; + +struct QtResourcePrefixData { + QString prefix; + QString language; + QList<QtResourceFileData> resourceFileList; + bool operator==(const QtResourcePrefixData &other) const { + if (prefix == other.prefix && language == other.language && resourceFileList == other.resourceFileList) + return true; + return false; + } +}; + +struct QtQrcFileData { + QString qrcPath; + QList<QtResourcePrefixData> resourceList; + bool operator==(const QtQrcFileData &other) const { + if (qrcPath == other.qrcPath && resourceList == other.resourceList) + return true; + return false; + } +}; + +bool loadResourceFileData(const QDomElement &fileElem, QtResourceFileData *fileData, QString *errorMessage) +{ + if (!fileData) + return false; + + if (fileElem.tagName() != QLatin1String(rccFileTag)) { + *errorMessage = msgTagMismatch(fileElem.tagName(), QLatin1String(rccFileTag)); + return false; + } + + QtResourceFileData &data = *fileData; + + data.path = fileElem.text(); + data.alias = fileElem.attribute(QLatin1String(rccAliasAttribute)); + + return true; +} + +static bool loadResourcePrefixData(const QDomElement &prefixElem, QtResourcePrefixData *prefixData, QString *errorMessage) +{ + if (!prefixData) + return false; + + if (prefixElem.tagName() != QLatin1String(rccTag)) { + *errorMessage = msgTagMismatch(prefixElem.tagName(), QLatin1String(rccTag)); + return false; + } + + QtResourcePrefixData &data = *prefixData; + + data.prefix = prefixElem.attribute(QLatin1String(rccPrefixAttribute)); + data.language = prefixElem.attribute(QLatin1String(rccLangAttribute)); + QDomElement fileElem = prefixElem.firstChildElement(); + while (!fileElem.isNull()) { + QtResourceFileData fileData; + if (!loadResourceFileData(fileElem, &fileData, errorMessage)) + return false; + data.resourceFileList.append(fileData); + fileElem = fileElem.nextSiblingElement(); + } + return true; +} + +static bool loadQrcFileData(const QDomDocument &doc, const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage) +{ + if (!qrcFileData) + return false; + + QtQrcFileData &data = *qrcFileData; + + QDomElement docElem = doc.documentElement(); + if (docElem.tagName() != QLatin1String(rccRootTag)) { + *errorMessage = msgTagMismatch(docElem.tagName(), QLatin1String(rccRootTag)); + return false; + } + + QDomElement prefixElem = docElem.firstChildElement(); + while (!prefixElem.isNull()) { + QtResourcePrefixData prefixData; + if (!loadResourcePrefixData(prefixElem, &prefixData, errorMessage)) + return false; + data.resourceList.append(prefixData); + prefixElem = prefixElem.nextSiblingElement(); + } + + data.qrcPath = path; + + return true; +} + +QDomElement saveResourceFileData(QDomDocument &doc, const QtResourceFileData &fileData) +{ + QDomElement fileElem = doc.createElement(QLatin1String(rccFileTag)); + if (!fileData.alias.isEmpty()) + fileElem.setAttribute(QLatin1String(rccAliasAttribute), fileData.alias); + + QDomText textElem = doc.createTextNode(fileData.path); + fileElem.appendChild(textElem); + + return fileElem; +} + +QDomElement saveResourcePrefixData(QDomDocument &doc, const QtResourcePrefixData &prefixData) +{ + QDomElement prefixElem = doc.createElement(QLatin1String(rccTag)); + if (!prefixData.prefix.isEmpty()) + prefixElem.setAttribute(QLatin1String(rccPrefixAttribute), prefixData.prefix); + if (!prefixData.language.isEmpty()) + prefixElem.setAttribute(QLatin1String(rccLangAttribute), prefixData.language); + + QListIterator<QtResourceFileData> itFile(prefixData.resourceFileList); + while (itFile.hasNext()) { + QDomElement fileElem = saveResourceFileData(doc, itFile.next()); + prefixElem.appendChild(fileElem); + } + + return prefixElem; +} + +QDomDocument saveQrcFileData(const QtQrcFileData &qrcFileData) +{ + QDomDocument doc; + QDomElement docElem = doc.createElement(QLatin1String(rccRootTag)); + QListIterator<QtResourcePrefixData> itPrefix(qrcFileData.resourceList); + while (itPrefix.hasNext()) { + QDomElement prefixElem = saveResourcePrefixData(doc, itPrefix.next()); + + docElem.appendChild(prefixElem); + } + doc.appendChild(docElem); + + return doc; +} +// --------------- QtResourceFile +class QtResourceFile { +public: + friend class QtQrcManager; + + QString path() const { return m_path; } + QString alias() const { return m_alias; } + QString fullPath() const { return m_fullPath; } +private: + QtResourceFile() {} + + QString m_path; + QString m_alias; + QString m_fullPath; +}; + +class QtResourcePrefix { +public: + friend class QtQrcManager; + + QString prefix() const { return m_prefix; } + QString language() const { return m_language; } + QList<QtResourceFile *> resourceFiles() const { return m_resourceFiles; } +private: + QtResourcePrefix() {} + + QString m_prefix; + QString m_language; + QList<QtResourceFile *> m_resourceFiles; + +}; +// ------------------- QtQrcFile +class QtQrcFile { +public: + friend class QtQrcManager; + + QString path() const { return m_path; } + QString fileName() const { return m_fileName; } + QList<QtResourcePrefix *> resourcePrefixList() const { return m_resourcePrefixes; } + QtQrcFileData initialState() const { return m_initialState; } + +private: + QtQrcFile() { } + + void setPath(const QString &path) { + m_path = path; + QFileInfo fi(path); + m_fileName = fi.fileName(); + } + + QString m_path; + QString m_fileName; + QList<QtResourcePrefix *> m_resourcePrefixes; + QtQrcFileData m_initialState; +}; + +// ------------------ QtQrcManager +class QtQrcManager : public QObject +{ + Q_OBJECT +public: + QtQrcManager(QObject *parent = 0); + ~QtQrcManager(); + + QList<QtQrcFile *> qrcFiles() const; + + // helpers + QtQrcFile *qrcFileOf(const QString &path) const; + QtQrcFile *qrcFileOf(QtResourcePrefix *resourcePrefix) const; + QtQrcFile *qrcFileOf(QtResourceFile *resourceFile) const; + QtResourcePrefix *resourcePrefixOf(QtResourceFile *resourceFile) const; + + QtQrcFile *importQrcFile(const QtQrcFileData &qrcFileData, QtQrcFile *beforeQrcFile = 0); + void exportQrcFile(QtQrcFile *qrcFile, QtQrcFileData *qrcFileData) const; + + QList<QtResourceFile *> resourceFilesOf(const QString &resourceFullPath) const; + QIcon icon(const QString &resourceFullPath) const; + bool exists(const QString &resourceFullPath) const; + bool exists(QtQrcFile *qrcFile) const; + + QtQrcFile *prevQrcFile(QtQrcFile *qrcFile) const; + QtQrcFile *nextQrcFile(QtQrcFile *qrcFile) const; + QtResourcePrefix *prevResourcePrefix(QtResourcePrefix *resourcePrefix) const; + QtResourcePrefix *nextResourcePrefix(QtResourcePrefix *resourcePrefix) const; + QtResourceFile *prevResourceFile(QtResourceFile *resourceFile) const; + QtResourceFile *nextResourceFile(QtResourceFile *resourceFile) const; + + void clear(); + +public slots: + + QtQrcFile *insertQrcFile(const QString &path, QtQrcFile *beforeQrcFile = 0, bool newFile = false); + void moveQrcFile(QtQrcFile *qrcFile, QtQrcFile *beforeQrcFile); + void setInitialState(QtQrcFile *qrcFile, const QtQrcFileData &initialState); + void removeQrcFile(QtQrcFile *qrcFile); + + QtResourcePrefix *insertResourcePrefix(QtQrcFile *qrcFile, const QString &prefix, + const QString &language, QtResourcePrefix *beforeResourcePrefix = 0); + void moveResourcePrefix(QtResourcePrefix *resourcePrefix, QtResourcePrefix *beforeResourcePrefix); // the same qrc file??? + void changeResourcePrefix(QtResourcePrefix *resourcePrefix, const QString &newPrefix); + void changeResourceLanguage(QtResourcePrefix *resourcePrefix, const QString &newLanguage); + void removeResourcePrefix(QtResourcePrefix *resourcePrefix); + + QtResourceFile *insertResourceFile(QtResourcePrefix *resourcePrefix, const QString &path, + const QString &alias, QtResourceFile *beforeResourceFile = 0); + void moveResourceFile(QtResourceFile *resourceFile, QtResourceFile *beforeResourceFile); // the same prefix??? + void changeResourceAlias(QtResourceFile *resourceFile, const QString &newAlias); + void removeResourceFile(QtResourceFile *resourceFile); + +signals: + void qrcFileInserted(QtQrcFile *qrcFile); + void qrcFileMoved(QtQrcFile *qrcFile, QtQrcFile *oldBeforeQrcFile); + void qrcFileRemoved(QtQrcFile *qrcFile); + + void resourcePrefixInserted(QtResourcePrefix *resourcePrefix); + void resourcePrefixMoved(QtResourcePrefix *resourcePrefix, QtResourcePrefix *oldBeforeResourcePrefix); + void resourcePrefixChanged(QtResourcePrefix *resourcePrefix, const QString &oldPrefix); + void resourceLanguageChanged(QtResourcePrefix *resourcePrefix, const QString &oldLanguage); + void resourcePrefixRemoved(QtResourcePrefix *resourcePrefix); + + void resourceFileInserted(QtResourceFile *resourceFile); + void resourceFileMoved(QtResourceFile *resourceFile, QtResourceFile *oldBeforeResourceFile); + void resourceAliasChanged(QtResourceFile *resourceFile, const QString &oldAlias); + void resourceFileRemoved(QtResourceFile *resourceFile); +private: + + QList<QtQrcFile *> m_qrcFiles; + QMap<QString, QtQrcFile *> m_pathToQrc; + QMap<QtQrcFile *, bool> m_qrcFileToExists; + QMap<QtResourcePrefix *, QtQrcFile *> m_prefixToQrc; + QMap<QtResourceFile *, QtResourcePrefix *> m_fileToPrefix; + QMap<QString, QList<QtResourceFile *> > m_fullPathToResourceFiles; + QMap<QString, QIcon> m_fullPathToIcon; + QMap<QString, bool> m_fullPathToExists; +}; + +QtQrcManager::QtQrcManager(QObject *parent) + : QObject(parent) +{ + +} + +QtQrcManager::~QtQrcManager() +{ + clear(); +} + +QList<QtQrcFile *> QtQrcManager::qrcFiles() const +{ + return m_qrcFiles; +} + +QtQrcFile *QtQrcManager::qrcFileOf(const QString &path) const +{ + return m_pathToQrc.value(path); +} + +QtQrcFile *QtQrcManager::qrcFileOf(QtResourcePrefix *resourcePrefix) const +{ + return m_prefixToQrc.value(resourcePrefix); +} + +QtQrcFile *QtQrcManager::qrcFileOf(QtResourceFile *resourceFile) const +{ + return qrcFileOf(resourcePrefixOf(resourceFile)); +} + +QtResourcePrefix *QtQrcManager::resourcePrefixOf(QtResourceFile *resourceFile) const +{ + return m_fileToPrefix.value(resourceFile); +} + +QtQrcFile *QtQrcManager::importQrcFile(const QtQrcFileData &qrcFileData, QtQrcFile *beforeQrcFile) +{ + QtQrcFile *qrcFile = insertQrcFile(qrcFileData.qrcPath, beforeQrcFile); + if (!qrcFile) + return 0; + QListIterator<QtResourcePrefixData> itPrefix(qrcFileData.resourceList); + while (itPrefix.hasNext()) { + const QtResourcePrefixData &prefixData = itPrefix.next(); + QtResourcePrefix *resourcePrefix = insertResourcePrefix(qrcFile, prefixData.prefix, prefixData.language, 0); + QListIterator<QtResourceFileData> itFile(prefixData.resourceFileList); + while (itFile.hasNext()) { + const QtResourceFileData &fileData = itFile.next(); + insertResourceFile(resourcePrefix, fileData.path, fileData.alias, 0); + } + } + setInitialState(qrcFile, qrcFileData); + return qrcFile; +} + +void QtQrcManager::exportQrcFile(QtQrcFile *qrcFile, QtQrcFileData *qrcFileData) const +{ + if (!qrcFileData) + return; + + if (!qrcFile) + return; + + QtQrcFileData &data = *qrcFileData; + + QList<QtResourcePrefixData> resourceList; + + QList<QtResourcePrefix *> resourcePrefixes = qrcFile->resourcePrefixList(); + QListIterator<QtResourcePrefix *> itPrefix(resourcePrefixes); + while (itPrefix.hasNext()) { + QList<QtResourceFileData> resourceFileList; + + QtResourcePrefix *prefix = itPrefix.next(); + + QList<QtResourceFile *> resourceFiles = prefix->resourceFiles(); + QListIterator<QtResourceFile *> itFile(resourceFiles); + while (itFile.hasNext()) { + QtResourceFile *file = itFile.next(); + QtResourceFileData fileData; + fileData.path = file->path(); + fileData.alias = file->alias(); + resourceFileList << fileData; + } + QtResourcePrefixData prefixData; + prefixData.prefix = prefix->prefix(); + prefixData.language = prefix->language(); + prefixData.resourceFileList = resourceFileList; + + resourceList << prefixData; + } + data = QtQrcFileData(); + data.qrcPath = qrcFile->path(); + data.resourceList = resourceList; +} + +QList<QtResourceFile *> QtQrcManager::resourceFilesOf(const QString &resourcePath) const +{ + return m_fullPathToResourceFiles.value(resourcePath); +} + +QIcon QtQrcManager::icon(const QString &resourceFullPath) const +{ + return m_fullPathToIcon.value(resourceFullPath); +} + +bool QtQrcManager::exists(const QString &resourceFullPath) const +{ + return m_fullPathToExists.value(resourceFullPath, false); +} + +bool QtQrcManager::exists(QtQrcFile *qrcFile) const +{ + return m_qrcFileToExists.value(qrcFile, false); +} + +QtQrcFile *QtQrcManager::prevQrcFile(QtQrcFile *qrcFile) const +{ + if (!qrcFile) + return 0; + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx <= 0) + return 0; + return m_qrcFiles.at(idx - 1); +} + +QtQrcFile *QtQrcManager::nextQrcFile(QtQrcFile *qrcFile) const +{ + if (!qrcFile) + return 0; + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0 || idx == m_qrcFiles.size() - 1) + return 0; + return m_qrcFiles.at(idx + 1); +} + +QtResourcePrefix *QtQrcManager::prevResourcePrefix(QtResourcePrefix *resourcePrefix) const +{ + if (!resourcePrefix) + return 0; + QList<QtResourcePrefix *> prefixes = qrcFileOf(resourcePrefix)->resourcePrefixList(); + const int idx = prefixes.indexOf(resourcePrefix); + if (idx <= 0) + return 0; + return prefixes.at(idx - 1); +} + +QtResourcePrefix *QtQrcManager::nextResourcePrefix(QtResourcePrefix *resourcePrefix) const +{ + if (!resourcePrefix) + return 0; + QList<QtResourcePrefix *> prefixes = qrcFileOf(resourcePrefix)->resourcePrefixList(); + const int idx = prefixes.indexOf(resourcePrefix); + if (idx < 0 || idx == prefixes.size() - 1) + return 0; + return prefixes.at(idx + 1); +} + +QtResourceFile *QtQrcManager::prevResourceFile(QtResourceFile *resourceFile) const +{ + if (!resourceFile) + return 0; + QList<QtResourceFile *> files = resourcePrefixOf(resourceFile)->resourceFiles(); + const int idx = files.indexOf(resourceFile); + if (idx <= 0) + return 0; + return files.at(idx - 1); +} + +QtResourceFile *QtQrcManager::nextResourceFile(QtResourceFile *resourceFile) const +{ + if (!resourceFile) + return 0; + QList<QtResourceFile *> files = resourcePrefixOf(resourceFile)->resourceFiles(); + const int idx = files.indexOf(resourceFile); + if (idx < 0 || idx == files.size() - 1) + return 0; + return files.at(idx + 1); +} + +void QtQrcManager::clear() +{ + QList<QtQrcFile *> oldQrcFiles = qrcFiles(); + QListIterator<QtQrcFile *> it(oldQrcFiles); + while (it.hasNext()) + removeQrcFile(it.next()); +} + +QtQrcFile *QtQrcManager::insertQrcFile(const QString &path, QtQrcFile *beforeQrcFile, bool newFile) +{ + if (m_pathToQrc.contains(path)) + return 0; + + int idx = m_qrcFiles.indexOf(beforeQrcFile); + if (idx < 0) + idx = m_qrcFiles.size(); + + QtQrcFile *qrcFile = new QtQrcFile(); + qrcFile->setPath(path); + + m_qrcFiles.insert(idx, qrcFile); + m_pathToQrc[path] = qrcFile; + + const QFileInfo fi(path); + m_qrcFileToExists[qrcFile] = fi.exists() || newFile; + + emit qrcFileInserted(qrcFile); + return qrcFile; +} + +void QtQrcManager::moveQrcFile(QtQrcFile *qrcFile, QtQrcFile *beforeQrcFile) +{ + if (qrcFile == beforeQrcFile) + return; + + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0) + return; + + int beforeIdx = m_qrcFiles.indexOf(beforeQrcFile); + if (beforeIdx < 0) + beforeIdx = m_qrcFiles.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtQrcFile *oldBefore = 0; + if (idx < m_qrcFiles.size() - 1) + oldBefore = m_qrcFiles.at(idx + 1); + + m_qrcFiles.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + m_qrcFiles.insert(beforeIdx, qrcFile); + + emit qrcFileMoved(qrcFile, oldBefore); +} + +void QtQrcManager::setInitialState(QtQrcFile *qrcFile, const QtQrcFileData &initialState) +{ + qrcFile->m_initialState = initialState; +} + +void QtQrcManager::removeQrcFile(QtQrcFile *qrcFile) +{ + const int idx = m_qrcFiles.indexOf(qrcFile); + if (idx < 0) + return; + + QList<QtResourcePrefix *> resourcePrefixes = qrcFile->resourcePrefixList(); + QListIterator<QtResourcePrefix *> it(resourcePrefixes); + while (it.hasNext()) + removeResourcePrefix(it.next()); + + emit qrcFileRemoved(qrcFile); + + m_qrcFiles.removeAt(idx); + m_pathToQrc.remove(qrcFile->path()); + m_qrcFileToExists.remove(qrcFile); + delete qrcFile; +} + +QtResourcePrefix *QtQrcManager::insertResourcePrefix(QtQrcFile *qrcFile, const QString &prefix, + const QString &language, QtResourcePrefix *beforeResourcePrefix) +{ + if (!qrcFile) + return 0; + + int idx = qrcFile->m_resourcePrefixes.indexOf(beforeResourcePrefix); + if (idx < 0) + idx = qrcFile->m_resourcePrefixes.size(); + + QtResourcePrefix *resourcePrefix = new QtResourcePrefix(); + resourcePrefix->m_prefix = prefix; + resourcePrefix->m_language = language; + + qrcFile->m_resourcePrefixes.insert(idx, resourcePrefix); + m_prefixToQrc[resourcePrefix] = qrcFile; + + emit resourcePrefixInserted(resourcePrefix); + return resourcePrefix; +} + +void QtQrcManager::moveResourcePrefix(QtResourcePrefix *resourcePrefix, QtResourcePrefix *beforeResourcePrefix) +{ + if (resourcePrefix == beforeResourcePrefix) + return; + + QtQrcFile *qrcFile = qrcFileOf(resourcePrefix); + if (!qrcFile) + return; + + if (beforeResourcePrefix && qrcFileOf(beforeResourcePrefix) != qrcFile) + return; + + const int idx = qrcFile->m_resourcePrefixes.indexOf(resourcePrefix); + + int beforeIdx = qrcFile->m_resourcePrefixes.indexOf(beforeResourcePrefix); + if (beforeIdx < 0) + beforeIdx = qrcFile->m_resourcePrefixes.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtResourcePrefix *oldBefore = 0; + if (idx < qrcFile->m_resourcePrefixes.size() - 1) + oldBefore = qrcFile->m_resourcePrefixes.at(idx + 1); + + qrcFile->m_resourcePrefixes.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + qrcFile->m_resourcePrefixes.insert(beforeIdx, resourcePrefix); + + emit resourcePrefixMoved(resourcePrefix, oldBefore); +} + +void QtQrcManager::changeResourcePrefix(QtResourcePrefix *resourcePrefix, const QString &newPrefix) +{ + if (!resourcePrefix) + return; + + const QString oldPrefix = resourcePrefix->m_prefix; + if (oldPrefix == newPrefix) + return; + + resourcePrefix->m_prefix = newPrefix; + + emit resourcePrefixChanged(resourcePrefix, oldPrefix); +} + +void QtQrcManager::changeResourceLanguage(QtResourcePrefix *resourcePrefix, const QString &newLanguage) +{ + if (!resourcePrefix) + return; + + const QString oldLanguage = resourcePrefix->m_language; + if (oldLanguage == newLanguage) + return; + + resourcePrefix->m_language = newLanguage; + + emit resourceLanguageChanged(resourcePrefix, oldLanguage); +} + +void QtQrcManager::removeResourcePrefix(QtResourcePrefix *resourcePrefix) +{ + QtQrcFile *qrcFile = qrcFileOf(resourcePrefix); + if (!qrcFile) + return; + + const int idx = qrcFile->m_resourcePrefixes.indexOf(resourcePrefix); + + QList<QtResourceFile *> resourceFiles = resourcePrefix->resourceFiles(); + QListIterator<QtResourceFile *> it(resourceFiles); + while (it.hasNext()) + removeResourceFile(it.next()); + + emit resourcePrefixRemoved(resourcePrefix); + + qrcFile->m_resourcePrefixes.removeAt(idx); + m_prefixToQrc.remove(resourcePrefix); + delete resourcePrefix; +} + +QtResourceFile *QtQrcManager::insertResourceFile(QtResourcePrefix *resourcePrefix, const QString &path, + const QString &alias, QtResourceFile *beforeResourceFile) +{ + if (!resourcePrefix) + return 0; + + int idx = resourcePrefix->m_resourceFiles.indexOf(beforeResourceFile); + if (idx < 0) + idx = resourcePrefix->m_resourceFiles.size(); + + QtResourceFile *resourceFile = new QtResourceFile(); + resourceFile->m_path = path; + resourceFile->m_alias = alias; + const QFileInfo fi(qrcFileOf(resourcePrefix)->path()); + const QDir dir(fi.absolutePath()); + const QString fullPath = dir.absoluteFilePath(path); + resourceFile->m_fullPath = fullPath; + + resourcePrefix->m_resourceFiles.insert(idx, resourceFile); + m_fileToPrefix[resourceFile] = resourcePrefix; + m_fullPathToResourceFiles[fullPath].append(resourceFile); + if (!m_fullPathToIcon.contains(fullPath)) { + m_fullPathToIcon[fullPath] = QIcon(fullPath); + const QFileInfo fullInfo(fullPath); + m_fullPathToExists[fullPath] = fullInfo.exists(); + } + + emit resourceFileInserted(resourceFile); + return resourceFile; +} + +void QtQrcManager::moveResourceFile(QtResourceFile *resourceFile, QtResourceFile *beforeResourceFile) +{ + if (resourceFile == beforeResourceFile) + return; + + QtResourcePrefix *resourcePrefix = resourcePrefixOf(resourceFile); + if (!resourcePrefix) + return; + + if (beforeResourceFile && resourcePrefixOf(beforeResourceFile) != resourcePrefix) + return; + + const int idx = resourcePrefix->m_resourceFiles.indexOf(resourceFile); + + int beforeIdx = resourcePrefix->m_resourceFiles.indexOf(beforeResourceFile); + if (beforeIdx < 0) + beforeIdx = resourcePrefix->m_resourceFiles.size(); + + if (idx == beforeIdx - 1) // the same position, nothing changes + return; + + QtResourceFile *oldBefore = 0; + if (idx < resourcePrefix->m_resourceFiles.size() - 1) + oldBefore = resourcePrefix->m_resourceFiles.at(idx + 1); + + resourcePrefix->m_resourceFiles.removeAt(idx); + if (idx < beforeIdx) + beforeIdx -= 1; + + resourcePrefix->m_resourceFiles.insert(beforeIdx, resourceFile); + + emit resourceFileMoved(resourceFile, oldBefore); +} + +void QtQrcManager::changeResourceAlias(QtResourceFile *resourceFile, const QString &newAlias) +{ + if (!resourceFile) + return; + + const QString oldAlias = resourceFile->m_alias; + if (oldAlias == newAlias) + return; + + resourceFile->m_alias = newAlias; + + emit resourceAliasChanged(resourceFile, oldAlias); +} + +void QtQrcManager::removeResourceFile(QtResourceFile *resourceFile) +{ + QtResourcePrefix *resourcePrefix = resourcePrefixOf(resourceFile); + if (!resourcePrefix) + return; + + const int idx = resourcePrefix->m_resourceFiles.indexOf(resourceFile); + + emit resourceFileRemoved(resourceFile); + + resourcePrefix->m_resourceFiles.removeAt(idx); + m_fileToPrefix.remove(resourceFile); + const QString fullPath = resourceFile->fullPath(); + m_fullPathToResourceFiles[fullPath].removeAll(resourceFile); // optimize me + if (m_fullPathToResourceFiles[fullPath].isEmpty()) { + m_fullPathToResourceFiles.remove(fullPath); + m_fullPathToIcon.remove(fullPath); + m_fullPathToExists.remove(fullPath); + } + delete resourceFile; +} + + + +} + +// ----------------- QtResourceEditorDialogPrivate +class QtResourceEditorDialogPrivate +{ + QtResourceEditorDialog *q_ptr; + Q_DECLARE_PUBLIC(QtResourceEditorDialog) +public: + QtResourceEditorDialogPrivate(); + + void slotQrcFileInserted(QtQrcFile *qrcFile); + void slotQrcFileMoved(QtQrcFile *qrcFile); + void slotQrcFileRemoved(QtQrcFile *qrcFile); + + QStandardItem *insertResourcePrefix(QtResourcePrefix *resourcePrefix); + + void slotResourcePrefixInserted(QtResourcePrefix *resourcePrefix) { insertResourcePrefix(resourcePrefix); } + void slotResourcePrefixMoved(QtResourcePrefix *resourcePrefix); + void slotResourcePrefixChanged(QtResourcePrefix *resourcePrefix); + void slotResourceLanguageChanged(QtResourcePrefix *resourcePrefix); + void slotResourcePrefixRemoved(QtResourcePrefix *resourcePrefix); + void slotResourceFileInserted(QtResourceFile *resourceFile); + void slotResourceFileMoved(QtResourceFile *resourceFile); + void slotResourceAliasChanged(QtResourceFile *resourceFile); + void slotResourceFileRemoved(QtResourceFile *resourceFile); + + void slotCurrentQrcFileChanged(QListWidgetItem *item); + void slotCurrentTreeViewItemChanged(const QModelIndex &index); + void slotListWidgetContextMenuRequested(const QPoint &pos); + void slotTreeViewContextMenuRequested(const QPoint &pos); + void slotTreeViewItemChanged(QStandardItem *item); + + void slotNewQrcFile(); + void slotImportQrcFile(); + void slotRemoveQrcFile(); + void slotMoveUpQrcFile(); + void slotMoveDownQrcFile(); + + void slotNewPrefix(); + void slotAddFiles(); + void slotChangePrefix(); + void slotChangeLanguage(); + void slotChangeAlias(); + void slotClonePrefix(); + void slotRemove(); + void slotMoveUp(); + void slotMoveDown(); + + bool loadQrcFile(const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage); + bool loadQrcFile(const QString &path, QtQrcFileData *qrcFileData); + bool saveQrcFile(const QtQrcFileData &qrcFileData); + + QString qrcFileText(QtQrcFile *qrcFile) const; + + QMessageBox::StandardButton warning(const QString &title, const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok, + QMessageBox::StandardButton defaultButton = QMessageBox::NoButton) const; + + QString browseForNewLocation(const QString &resourceFile, const QDir &rootDir) const; + QString copyResourceFile(const QString &resourceFile, const QString &destPath) const; + QtResourceFile *getCurrentResourceFile() const; + QtResourcePrefix *getCurrentResourcePrefix() const; + void selectTreeRow(QStandardItem *item); + QString getSaveFileNameWithExtension(QWidget *parent, + const QString &title, QString dir, const QString &filter, const QString &extension) const; + QString qrcStartDirectory() const; + + Ui::QtResourceEditorDialog m_ui; + QDesignerFormEditorInterface *m_core; + QtResourceModel *m_resourceModel; + QDesignerDialogGuiInterface *m_dlgGui; + QtQrcManager *m_qrcManager; + QList<QtQrcFileData> m_initialState; + + QMap<QtQrcFile *, QListWidgetItem *> m_qrcFileToItem; + QMap<QListWidgetItem *, QtQrcFile *> m_itemToQrcFile; + QMap<QtResourcePrefix *, QStandardItem *> m_resourcePrefixToPrefixItem; + QMap<QtResourcePrefix *, QStandardItem *> m_resourcePrefixToLanguageItem; + QMap<QStandardItem *, QtResourcePrefix *> m_prefixItemToResourcePrefix; + QMap<QStandardItem *, QtResourcePrefix *> m_languageItemToResourcePrefix; + QMap<QtResourceFile *, QStandardItem *> m_resourceFileToPathItem; + QMap<QtResourceFile *, QStandardItem *> m_resourceFileToAliasItem; + QMap<QStandardItem *, QtResourceFile *> m_pathItemToResourceFile; + QMap<QStandardItem *, QtResourceFile *> m_aliasItemToResourceFile; + + bool m_ignoreCurrentChanged; + bool m_firstQrcFileDialog; + QtQrcFile *m_currentQrcFile; + + QAction *m_newQrcFileAction; + QAction *m_importQrcFileAction; + QAction *m_removeQrcFileAction; + QAction *m_moveUpQrcFileAction; + QAction *m_moveDownQrcFileAction; + + QAction *m_newPrefixAction; + QAction *m_addResourceFileAction; + QAction *m_changePrefixAction; + QAction *m_changeLanguageAction; + QAction *m_changeAliasAction; + QAction *m_clonePrefixAction; + QAction *m_moveUpAction; + QAction *m_moveDownAction; + QAction *m_removeAction; + + QStandardItemModel *m_treeModel; + QItemSelectionModel *m_treeSelection; +}; + +QtResourceEditorDialogPrivate::QtResourceEditorDialogPrivate() : + q_ptr(0), + m_core(0), + m_resourceModel(0), + m_dlgGui(0), + m_qrcManager(0), + m_ignoreCurrentChanged(false), + m_firstQrcFileDialog(true), + m_currentQrcFile(0), + m_newQrcFileAction(0), + m_importQrcFileAction(0), + m_removeQrcFileAction(0), + m_moveUpQrcFileAction(0), + m_moveDownQrcFileAction(0), + m_newPrefixAction(0), + m_addResourceFileAction(0), + m_changePrefixAction(0), + m_changeLanguageAction(0), + m_changeAliasAction(0), + m_clonePrefixAction(0), + m_moveUpAction(0), + m_moveDownAction(0), + m_removeAction(0), + m_treeModel(0), + m_treeSelection(0) +{ +} + +QMessageBox::StandardButton QtResourceEditorDialogPrivate::warning(const QString &title, const QString &text, QMessageBox::StandardButtons buttons, + QMessageBox::StandardButton defaultButton) const +{ + return m_dlgGui->message(q_ptr, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, title, text, buttons, defaultButton); +} + +QString QtResourceEditorDialogPrivate::qrcFileText(QtQrcFile *qrcFile) const +{ + const QString path = qrcFile->path(); + const QString fileName = qrcFile->fileName(); + const QFileInfo fi(path); + if (fi.exists() && !fi.isWritable()) + return QApplication::translate("QtResourceEditorDialog", "%1 [read-only]").arg(fileName); + if (!m_qrcManager->exists(qrcFile)) + return QApplication::translate("QtResourceEditorDialog", "%1 [missing]").arg(fileName); + return fileName; +} + +void QtResourceEditorDialogPrivate::slotQrcFileInserted(QtQrcFile *qrcFile) +{ + QListWidgetItem *currentItem = m_ui.qrcFileList->currentItem(); + int idx = m_ui.qrcFileList->count(); + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(qrcFile); + QListWidgetItem *nextItem = m_qrcFileToItem.value(nextQrcFile); + if (nextItem) { + const int row = m_ui.qrcFileList->row(nextItem); + if (row >= 0) + idx = row; + } + const QString path = qrcFile->path(); + QListWidgetItem *item = new QListWidgetItem(qrcFileText(qrcFile)); + item->setToolTip(path); + m_ignoreCurrentChanged = true; + m_ui.qrcFileList->insertItem(idx, item); + m_ui.qrcFileList->setCurrentItem(currentItem); + m_ignoreCurrentChanged = false; + m_qrcFileToItem[qrcFile] = item; + m_itemToQrcFile[item] = qrcFile; + if (!m_qrcManager->exists(qrcFile)) + item->setForeground(QBrush(Qt::red)); +} + +void QtResourceEditorDialogPrivate::slotQrcFileMoved(QtQrcFile *qrcFile) +{ + QListWidgetItem *currentItem = m_ui.qrcFileList->currentItem(); + QListWidgetItem *item = m_qrcFileToItem.value(qrcFile); + m_ignoreCurrentChanged = true; + m_ui.qrcFileList->takeItem(m_ui.qrcFileList->row(item)); + + int idx = m_ui.qrcFileList->count(); + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(qrcFile); + QListWidgetItem *nextItem = m_qrcFileToItem.value(nextQrcFile); + if (nextItem) { + int row = m_ui.qrcFileList->row(nextItem); + if (row >= 0) + idx = row; + } + m_ui.qrcFileList->insertItem(idx, item); + if (currentItem == item) + m_ui.qrcFileList->setCurrentItem(item); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotQrcFileRemoved(QtQrcFile *qrcFile) +{ + QListWidgetItem *item = m_qrcFileToItem.value(qrcFile); + if (item == m_ui.qrcFileList->currentItem()) + m_ui.qrcFileList->setCurrentItem(0); // this should trigger list view signal currentItemChanged(0), and slot should set m_currentQrcFile to 0 + m_ignoreCurrentChanged = true; + delete item; + m_ignoreCurrentChanged = false; + m_itemToQrcFile.remove(item); + m_qrcFileToItem.remove(qrcFile); +} + +QStandardItem *QtResourceEditorDialogPrivate::insertResourcePrefix(QtResourcePrefix *resourcePrefix) +{ + if (m_qrcManager->qrcFileOf(resourcePrefix) != m_currentQrcFile) + return 0; + + QtResourcePrefix *prevResourcePrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + QStandardItem *prevItem = m_resourcePrefixToPrefixItem.value(prevResourcePrefix); + + int row = 0; + if (prevItem) + row = m_treeModel->indexFromItem(prevItem).row() + 1; + + QStandardItem *prefixItem = new QStandardItem(); + QStandardItem *languageItem = new QStandardItem(); + QList<QStandardItem *> items; + items << prefixItem; + items << languageItem; + m_treeModel->insertRow(row, items); + const QModelIndex newIndex = m_treeModel->indexFromItem(prefixItem); + m_ui.resourceTreeView->setExpanded(newIndex, true); + prefixItem->setFlags(prefixItem->flags() | Qt::ItemIsEditable); + languageItem->setFlags(languageItem->flags() | Qt::ItemIsEditable); + m_resourcePrefixToPrefixItem[resourcePrefix] = prefixItem; + m_resourcePrefixToLanguageItem[resourcePrefix] = languageItem; + m_prefixItemToResourcePrefix[prefixItem] = resourcePrefix; + m_languageItemToResourcePrefix[languageItem] = resourcePrefix; + slotResourcePrefixChanged(resourcePrefix); + slotResourceLanguageChanged(resourcePrefix); + return prefixItem; +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixMoved(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *prefixItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!prefixItem) + return; + + QStandardItem *languageItem = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!languageItem) + return; + + const QModelIndex index = m_treeModel->indexFromItem(prefixItem); + const bool expanded = m_ui.resourceTreeView->isExpanded(index); + m_ignoreCurrentChanged = true; + const QList<QStandardItem *> items = m_treeModel->takeRow(index.row()); + + int row = m_treeModel->rowCount(); + QtResourcePrefix *nextResourcePrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + QStandardItem *nextItem = m_resourcePrefixToPrefixItem.value(nextResourcePrefix); + if (nextItem) + row = m_treeModel->indexFromItem(nextItem).row(); + m_treeModel->insertRow(row, items); + m_ignoreCurrentChanged = false; + m_ui.resourceTreeView->setExpanded(m_treeModel->indexFromItem(items.at(0)), expanded); +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixChanged(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *item = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!item) + return; + + m_ignoreCurrentChanged = true; + QString prefix = resourcePrefix->prefix(); + if (prefix.isEmpty()) + prefix = QApplication::translate("QtResourceEditorDialog", "<no prefix>", 0, QApplication::UnicodeUTF8); + item->setText(prefix); + item->setToolTip(prefix); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceLanguageChanged(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *item = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!item) + return; + + m_ignoreCurrentChanged = true; + const QString language = resourcePrefix->language(); + item->setText(language); + item->setToolTip(language); + + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourcePrefixRemoved(QtResourcePrefix *resourcePrefix) +{ + QStandardItem *prefixItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + if (!prefixItem) + return; + + QStandardItem *languageItem = m_resourcePrefixToLanguageItem.value(resourcePrefix); + if (!languageItem) + return; + + m_ignoreCurrentChanged = true; + m_treeModel->takeRow(m_treeModel->indexFromItem(prefixItem).row()); + delete prefixItem; + delete languageItem; + m_ignoreCurrentChanged = false; + m_prefixItemToResourcePrefix.remove(prefixItem); + m_languageItemToResourcePrefix.remove(languageItem); + m_resourcePrefixToPrefixItem.remove(resourcePrefix); + m_resourcePrefixToLanguageItem.remove(resourcePrefix); +} + +void QtResourceEditorDialogPrivate::slotResourceFileInserted(QtResourceFile *resourceFile) +{ + QtResourcePrefix *resourcePrefix = m_qrcManager->resourcePrefixOf(resourceFile); + if (m_qrcManager->qrcFileOf(resourcePrefix) != m_currentQrcFile) + return; + + QtResourceFile *prevResourceFile = m_qrcManager->prevResourceFile(resourceFile); + QStandardItem *prevItem = m_resourceFileToPathItem.value(prevResourceFile); + + QStandardItem *pathItem = new QStandardItem(resourceFile->path()); + QStandardItem *aliasItem = new QStandardItem(); + QStandardItem *parentItem = m_resourcePrefixToPrefixItem.value(resourcePrefix); + QList<QStandardItem *> items; + items << pathItem; + items << aliasItem; + + int row = 0; + if (prevItem) + row = m_treeModel->indexFromItem(prevItem).row() + 1; + + parentItem->insertRow(row, items); + + pathItem->setFlags(pathItem->flags() & ~Qt::ItemIsEditable); + aliasItem->setFlags(aliasItem->flags() | Qt::ItemIsEditable); + m_resourceFileToPathItem[resourceFile] = pathItem; + m_resourceFileToAliasItem[resourceFile] = aliasItem; + m_pathItemToResourceFile[pathItem] = resourceFile; + m_aliasItemToResourceFile[aliasItem] = resourceFile; + pathItem->setToolTip(resourceFile->path()); + pathItem->setIcon(m_qrcManager->icon(resourceFile->fullPath())); + if (!m_qrcManager->exists(resourceFile->fullPath())) { + pathItem->setText(QApplication::translate("QtResourceEditorDialog", "%1 [missing]").arg(resourceFile->path())); + QBrush redBrush(Qt::red); + pathItem->setForeground(redBrush); + aliasItem->setForeground(redBrush); + } + slotResourceAliasChanged(resourceFile); +} + +void QtResourceEditorDialogPrivate::slotResourceFileMoved(QtResourceFile *resourceFile) +{ + QStandardItem *pathItem = m_resourceFileToPathItem.value(resourceFile); + if (!pathItem) + return; + + QStandardItem *aliasItem = m_resourceFileToAliasItem.value(resourceFile); + if (!aliasItem) + return; + + QStandardItem *parentItem = pathItem->parent(); + m_ignoreCurrentChanged = true; + const QList<QStandardItem *> items = parentItem->takeRow(m_treeModel->indexFromItem(pathItem).row()); + + int row = parentItem->rowCount(); + QtResourceFile *nextResourceFile = m_qrcManager->nextResourceFile(resourceFile); + QStandardItem *nextItem = m_resourceFileToPathItem.value(nextResourceFile); + if (nextItem) + row = m_treeModel->indexFromItem(nextItem).row(); + parentItem->insertRow(row, items); + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceAliasChanged(QtResourceFile *resourceFile) +{ + QStandardItem *item = m_resourceFileToAliasItem.value(resourceFile); + if (!item) + return; + + m_ignoreCurrentChanged = true; + const QString alias = resourceFile->alias(); + item->setText(alias); + item->setToolTip(alias); + + m_ignoreCurrentChanged = false; +} + +void QtResourceEditorDialogPrivate::slotResourceFileRemoved(QtResourceFile *resourceFile) +{ + QStandardItem *pathItem = m_resourceFileToPathItem.value(resourceFile); + if (!pathItem) + return; + + QStandardItem *aliasItem = m_resourceFileToAliasItem.value(resourceFile); + if (!aliasItem) + return; + + QStandardItem *parentItem = pathItem->parent(); + + m_ignoreCurrentChanged = true; + parentItem->takeRow(m_treeModel->indexFromItem(pathItem).row()); + delete pathItem; + delete aliasItem; + m_ignoreCurrentChanged = false; + m_pathItemToResourceFile.remove(pathItem); + m_aliasItemToResourceFile.remove(aliasItem); + m_resourceFileToPathItem.remove(resourceFile); + m_resourceFileToAliasItem.remove(resourceFile); +} + + +void QtResourceEditorDialogPrivate::slotCurrentQrcFileChanged(QListWidgetItem *item) +{ + if (m_ignoreCurrentChanged) + return; + + QtQrcFile *newCurrentQrcFile = m_itemToQrcFile.value(item); + + if (newCurrentQrcFile == m_currentQrcFile) + return; + + if (m_currentQrcFile) { + QMap<QtResourcePrefix *, QStandardItem *> currentPrefixList = m_resourcePrefixToPrefixItem; + QMapIterator<QtResourcePrefix *, QStandardItem *> itPrefix(currentPrefixList); + while (itPrefix.hasNext()) { + QtResourcePrefix *resourcePrefix = itPrefix.next().key(); + QList<QtResourceFile *> currentResourceFiles = resourcePrefix->resourceFiles(); + QListIterator<QtResourceFile *> itFile(currentResourceFiles); + while (itFile.hasNext()) + slotResourceFileRemoved(itFile.next()); + slotResourcePrefixRemoved(resourcePrefix); + } + } + + m_currentQrcFile = newCurrentQrcFile; + slotCurrentTreeViewItemChanged(QModelIndex()); + QStandardItem *firstPrefix = 0; // select first prefix + if (m_currentQrcFile) { + QList<QtResourcePrefix *> newPrefixList = m_currentQrcFile->resourcePrefixList(); + QListIterator<QtResourcePrefix *> itPrefix(newPrefixList); + while (itPrefix.hasNext()) { + QtResourcePrefix *resourcePrefix = itPrefix.next(); + if (QStandardItem *newPrefixItem = insertResourcePrefix(resourcePrefix)) + if (!firstPrefix) + firstPrefix = newPrefixItem; + QList<QtResourceFile *> newResourceFiles = resourcePrefix->resourceFiles(); + QListIterator<QtResourceFile *> itFile(newResourceFiles); + while (itFile.hasNext()) + slotResourceFileInserted(itFile.next()); + } + } + m_ui.resourceTreeView->setCurrentIndex(firstPrefix ? m_treeModel->indexFromItem(firstPrefix) : QModelIndex()); + + m_removeQrcFileAction->setEnabled(m_currentQrcFile); + m_moveUpQrcFileAction->setEnabled(m_currentQrcFile && m_qrcManager->prevQrcFile(m_currentQrcFile)); + m_moveDownQrcFileAction->setEnabled(m_currentQrcFile && m_qrcManager->nextQrcFile(m_currentQrcFile)); +} + +void QtResourceEditorDialogPrivate::slotCurrentTreeViewItemChanged(const QModelIndex &index) +{ + QStandardItem *item = m_treeModel->itemFromIndex(index); + QtResourceFile *resourceFile = m_pathItemToResourceFile.value(item); + if (!resourceFile) + resourceFile = m_aliasItemToResourceFile.value(item); + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (!resourcePrefix) + resourcePrefix = m_languageItemToResourcePrefix.value(item); + + bool moveUpEnabled = false; + bool moveDownEnabled = false; + bool currentItem = resourceFile || resourcePrefix; + + if (resourceFile) { + if (m_qrcManager->prevResourceFile(resourceFile)) + moveUpEnabled = true; + if (m_qrcManager->nextResourceFile(resourceFile)) + moveDownEnabled = true; + } else if (resourcePrefix) { + if (m_qrcManager->prevResourcePrefix(resourcePrefix)) + moveUpEnabled = true; + if (m_qrcManager->nextResourcePrefix(resourcePrefix)) + moveDownEnabled = true; + } + + m_newPrefixAction->setEnabled(m_currentQrcFile); + m_addResourceFileAction->setEnabled(currentItem); + m_changePrefixAction->setEnabled(currentItem); + m_changeLanguageAction->setEnabled(currentItem); + m_changeAliasAction->setEnabled(resourceFile); + m_removeAction->setEnabled(currentItem); + m_moveUpAction->setEnabled(moveUpEnabled); + m_moveDownAction->setEnabled(moveDownEnabled); + m_clonePrefixAction->setEnabled(currentItem); +} + +void QtResourceEditorDialogPrivate::slotListWidgetContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_newQrcFileAction); + menu.addAction(m_importQrcFileAction); + menu.addAction(m_removeQrcFileAction); + menu.addSeparator(); + menu.addAction(m_moveUpQrcFileAction); + menu.addAction(m_moveDownQrcFileAction); + menu.exec(m_ui.qrcFileList->mapToGlobal(pos)); +} + +void QtResourceEditorDialogPrivate::slotTreeViewContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_newPrefixAction); + menu.addAction(m_addResourceFileAction); + menu.addAction(m_removeAction); + menu.addSeparator(); + menu.addAction(m_changePrefixAction); + menu.addAction(m_changeLanguageAction); + menu.addAction(m_changeAliasAction); + menu.addSeparator(); + menu.addAction(m_clonePrefixAction); + menu.addSeparator(); + menu.addAction(m_moveUpAction); + menu.addAction(m_moveDownAction); + menu.exec(m_ui.resourceTreeView->mapToGlobal(pos)); +} + +void QtResourceEditorDialogPrivate::slotTreeViewItemChanged(QStandardItem *item) +{ + if (m_ignoreCurrentChanged) + return; + + const QString newValue = item->text(); + QtResourceFile *resourceFile = m_aliasItemToResourceFile.value(item); + if (resourceFile) { + m_qrcManager->changeResourceAlias(resourceFile, newValue); + return; + } + + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (resourcePrefix) { + m_qrcManager->changeResourcePrefix(resourcePrefix, newValue); + return; + } + + resourcePrefix = m_languageItemToResourcePrefix.value(item); + if (resourcePrefix) { + m_qrcManager->changeResourceLanguage(resourcePrefix, newValue); + return; + } +} + +QString QtResourceEditorDialogPrivate::getSaveFileNameWithExtension(QWidget *parent, + const QString &title, QString dir, const QString &filter, const QString &extension) const +{ + const QChar dot = QLatin1Char('.'); + + QString saveFile; + while (true) { + saveFile = m_dlgGui->getSaveFileName(parent, title, dir, filter, 0, QFileDialog::DontConfirmOverwrite); + if (saveFile.isEmpty()) + return saveFile; + + const QFileInfo fInfo(saveFile); + if (fInfo.suffix().isEmpty() && !fInfo.fileName().endsWith(dot)) { + saveFile += dot; + saveFile += extension; + } + + const QFileInfo fi(saveFile); + if (!fi.exists()) + break; + + if (warning(title, msgOverwrite(fi.fileName()), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) + break; + + dir = saveFile; + } + return saveFile; +} + +QString QtResourceEditorDialogPrivate::qrcStartDirectory() const +{ + if (!m_currentQrcFile) + return QString(); + const QDir dir = QFileInfo(m_currentQrcFile->path()).dir(); + return dir.exists() ? dir.absolutePath() : QString(); +} + +void QtResourceEditorDialogPrivate::slotNewQrcFile() +{ + const QString qrcPath = getSaveFileNameWithExtension(q_ptr, + QApplication::translate("QtResourceEditorDialog", "New Resource File", 0, QApplication::UnicodeUTF8), + m_firstQrcFileDialog ? qrcStartDirectory() : QString(), + QApplication::translate("QtResourceEditorDialog", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8), + QLatin1String("qrc")); + if (qrcPath.isEmpty()) + return; + + m_firstQrcFileDialog = false; + if (QtQrcFile *sameQrcFile = m_qrcManager->qrcFileOf(qrcPath)) { + // QMessageBox ??? + QListWidgetItem *item = m_qrcFileToItem.value(sameQrcFile); + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + return; + } + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + + QtQrcFile *qrcFile = m_qrcManager->insertQrcFile(qrcPath, nextQrcFile, true); + m_ui.qrcFileList->setCurrentItem(m_qrcFileToItem.value(qrcFile)); +} + +void QtResourceEditorDialogPrivate::slotImportQrcFile() +{ + const QString qrcPath = m_dlgGui->getOpenFileName(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Import Resource File", 0, QApplication::UnicodeUTF8), + m_firstQrcFileDialog ? qrcStartDirectory() : QString(), + QApplication::translate("QtResourceEditorDialog", "Resource files (*.qrc)", 0, QApplication::UnicodeUTF8)); + if (qrcPath.isEmpty()) + return; + m_firstQrcFileDialog = false; + if (QtQrcFile *sameQrcFile = m_qrcManager->qrcFileOf(qrcPath)) { + // QMessageBox ??? + QListWidgetItem *item = m_qrcFileToItem.value(sameQrcFile); + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + return; + } + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + + QtQrcFileData qrcFileData; + loadQrcFile(qrcPath, &qrcFileData); + QtQrcFile *qrcFile = m_qrcManager->importQrcFile(qrcFileData, nextQrcFile); + m_ui.qrcFileList->setCurrentItem(m_qrcFileToItem.value(qrcFile)); +} + +void QtResourceEditorDialogPrivate::slotRemoveQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *currentQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + if (!currentQrcFile) + currentQrcFile = m_qrcManager->prevQrcFile(m_currentQrcFile); + + m_qrcManager->removeQrcFile(m_currentQrcFile); + QListWidgetItem *item = m_qrcFileToItem.value(currentQrcFile); + if (item) { + m_ui.qrcFileList->setCurrentItem(item); + item->setSelected(true); + } +} + +void QtResourceEditorDialogPrivate::slotMoveUpQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *prevQrcFile = m_qrcManager->prevQrcFile(m_currentQrcFile); + if (!prevQrcFile) + return; + + m_qrcManager->moveQrcFile(m_currentQrcFile, prevQrcFile); +} + +void QtResourceEditorDialogPrivate::slotMoveDownQrcFile() +{ + if (!m_currentQrcFile) + return; + + QtQrcFile *nextQrcFile = m_qrcManager->nextQrcFile(m_currentQrcFile); + if (!nextQrcFile) + return; + nextQrcFile = m_qrcManager->nextQrcFile(nextQrcFile); + + m_qrcManager->moveQrcFile(m_currentQrcFile, nextQrcFile); +} + +QtResourceFile *QtResourceEditorDialogPrivate::getCurrentResourceFile() const +{ + QStandardItem *currentItem = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + + + QtResourceFile *currentResourceFile = 0; + if (currentItem) { + currentResourceFile = m_pathItemToResourceFile.value(currentItem); + if (!currentResourceFile) + currentResourceFile = m_aliasItemToResourceFile.value(currentItem); + } + return currentResourceFile; +} + +QtResourcePrefix *QtResourceEditorDialogPrivate::getCurrentResourcePrefix() const +{ + QStandardItem *currentItem = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + + QtResourcePrefix *currentResourcePrefix = 0; + if (currentItem) { + currentResourcePrefix = m_prefixItemToResourcePrefix.value(currentItem); + if (!currentResourcePrefix) { + currentResourcePrefix = m_languageItemToResourcePrefix.value(currentItem); + if (!currentResourcePrefix) { + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (currentResourceFile) + currentResourcePrefix = m_qrcManager->resourcePrefixOf(currentResourceFile); + } + } + } + return currentResourcePrefix; +} + +void QtResourceEditorDialogPrivate::selectTreeRow(QStandardItem *item) +{ + const QModelIndex index = m_treeModel->indexFromItem(item); + m_treeSelection->select(index, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); + m_treeSelection->setCurrentIndex(index, QItemSelectionModel::Select); +} + +void QtResourceEditorDialogPrivate::slotNewPrefix() +{ + if (!m_currentQrcFile) + return; + + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + QtResourcePrefix *nextResourcePrefix = m_qrcManager->nextResourcePrefix(currentResourcePrefix); + QtResourcePrefix *newResourcePrefix = m_qrcManager->insertResourcePrefix(m_currentQrcFile, + QApplication::translate("QtResourceEditorDialog", "newPrefix", 0, QApplication::UnicodeUTF8), + QString(), nextResourcePrefix); + if (!newResourcePrefix) + return; + + QStandardItem *newItem = m_resourcePrefixToPrefixItem.value(newResourcePrefix); + if (!newItem) + return; + + const QModelIndex index = m_treeModel->indexFromItem(newItem); + selectTreeRow(newItem); + m_ui.resourceTreeView->edit(index); +} + +static inline QString outOfPathWarning(const QString &fname) +{ + return QApplication::translate("QtResourceEditorDialog", + "<p><b>Warning:</b> The file</p>" + "<p>%1</p>" + "<p>is outside of the current resource file's parent directory.</p>").arg(fname); +} + +static inline QString outOfPathWarningInfo() +{ + return QApplication::translate("QtResourceEditorDialog", + "<p>To resolve the issue, press:</p>" + "<table>" + "<tr><th align=\"left\">Copy</th><td>to copy the file to the resource file's parent directory.</td></tr>" + "<tr><th align=\"left\">Copy As...</th><td>to copy the file into a subdirectory of the resource file's parent directory.</td></tr>" + "<tr><th align=\"left\">Keep</th><td>to use its current location.</td></tr></table>"); +} + +void QtResourceEditorDialogPrivate::slotAddFiles() +{ + if (!m_currentQrcFile) + return; + + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (!currentResourcePrefix) + return; + + QString initialPath = m_currentQrcFile->path(); + if (currentResourceFile) { + QFileInfo fi(currentResourceFile->fullPath()); + initialPath = fi.absolutePath(); + } + + const QStringList resourcePaths = m_dlgGui->getOpenImageFileNames(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Add Files", 0, QApplication::UnicodeUTF8), + initialPath); + if (resourcePaths.isEmpty()) + return; + + QtResourceFile *nextResourceFile = m_qrcManager->nextResourceFile(currentResourceFile); + if (!currentResourceFile) { + QList<QtResourceFile *> resourceFiles = currentResourcePrefix->resourceFiles(); + if (resourceFiles.count() > 0) + nextResourceFile = resourceFiles.first(); + } + + const QFileInfo fi(m_currentQrcFile->path()); + const QString destDir = fi.absolutePath(); + const QDir dir(fi.absolutePath()); + QStringListIterator itResourcePath(resourcePaths); + while (itResourcePath.hasNext()) { + QString resourcePath = itResourcePath.next(); + QString relativePath = dir.relativeFilePath(resourcePath); + if (relativePath.startsWith(QLatin1String(".."))) { + QMessageBox msgBox(QMessageBox::Warning, + QApplication::translate("QtResourceEditorDialog", "Incorrect Path", 0, QApplication::UnicodeUTF8), + outOfPathWarning(relativePath), QMessageBox::Cancel); + msgBox.setInformativeText(outOfPathWarningInfo()); + QPushButton *copyButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Copy", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *copyAsButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Copy As...", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *keepButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Keep", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + QPushButton *skipButton = msgBox.addButton(QApplication::translate("QtResourceEditorDialog", + "Skip", 0, QApplication::UnicodeUTF8), QMessageBox::ActionRole); + msgBox.setEscapeButton(QMessageBox::Cancel); + msgBox.setDefaultButton(copyButton); + msgBox.exec(); + QString destPath; + if (msgBox.clickedButton() == keepButton) { + // nothing + } else if (msgBox.clickedButton() == copyButton) { + QFileInfo resInfo(resourcePath); + QDir dd(destDir); + destPath = dd.absoluteFilePath(resInfo.fileName()); + if (dd.exists(resInfo.fileName())) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + msgOverwrite(resInfo.fileName()), + QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Yes) + continue; + } + resourcePath = copyResourceFile(resourcePath, destPath); // returns empty string in case copy failed or was canceled + } else if (msgBox.clickedButton() == copyAsButton) { + destPath = browseForNewLocation(resourcePath, dir); // returns empty string in case browsing was canceled + if (destPath.isEmpty()) + continue; + resourcePath = copyResourceFile(resourcePath, destPath); // returns empty string in case copy failed or was canceled + } else if (msgBox.clickedButton() == skipButton) { // skipped + continue; + } else { // canceled + return; + } + if (resourcePath.isEmpty()) + continue; + } + relativePath = dir.relativeFilePath(resourcePath); + QtResourceFile *newResourceFile = m_qrcManager->insertResourceFile(currentResourcePrefix, relativePath, QString(), nextResourceFile); + + QStandardItem *newItem = m_resourceFileToPathItem.value(newResourceFile); + if (newItem) + selectTreeRow(newItem); + } +} + +void QtResourceEditorDialogPrivate::slotChangePrefix() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + QStandardItem *item = m_resourcePrefixToPrefixItem.value(currentResourcePrefix); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotChangeLanguage() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + QStandardItem *item = m_resourcePrefixToLanguageItem.value(currentResourcePrefix); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotChangeAlias() +{ + QtResourceFile *currentResourceFile = getCurrentResourceFile(); + if (!currentResourceFile) + return; + + QStandardItem *item = m_resourceFileToAliasItem.value(currentResourceFile); + QModelIndex index = m_treeModel->indexFromItem(item); + selectTreeRow(item); + m_ui.resourceTreeView->scrollTo(index); + m_ui.resourceTreeView->edit(index); +} + +void QtResourceEditorDialogPrivate::slotClonePrefix() +{ + QtResourcePrefix *currentResourcePrefix = getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return; + + bool ok; + QString suffix = QInputDialog::getText(q_ptr, QApplication::translate("QtResourceEditorDialog", "Clone Prefix", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Enter the suffix which you want to add to the names of the cloned files.\n" + "This could for example be a language extension like \"_de\".", 0, QApplication::UnicodeUTF8), + QLineEdit::Normal, QString(), &ok); + if (!ok) + return; + + QtResourcePrefix *newResourcePrefix = m_qrcManager->insertResourcePrefix(m_currentQrcFile, currentResourcePrefix->prefix(), + currentResourcePrefix->language(), m_qrcManager->nextResourcePrefix(currentResourcePrefix)); + if (newResourcePrefix) { + QList<QtResourceFile *> files = currentResourcePrefix->resourceFiles(); + QListIterator<QtResourceFile *> itFile(files); + while (itFile.hasNext()) { + QtResourceFile *resourceFile = itFile.next(); + QString path = resourceFile->path(); + QFileInfo fi(path); + QDir dir(fi.dir()); + QString oldSuffix = fi.completeSuffix(); + if (!oldSuffix.isEmpty()) + oldSuffix = QLatin1Char('.') + oldSuffix; + const QString newBaseName = fi.baseName() + suffix + oldSuffix; + const QString newPath = QDir::cleanPath(dir.filePath(newBaseName)); + m_qrcManager->insertResourceFile(newResourcePrefix, newPath, + resourceFile->alias()); + } + } +} + +void QtResourceEditorDialogPrivate::slotRemove() +{ + QStandardItem *item = m_treeModel->itemFromIndex(m_treeSelection->currentIndex()); + if (!item) + return; + + QtResourceFile *resourceFile = m_pathItemToResourceFile.value(item); + if (!resourceFile) + resourceFile = m_aliasItemToResourceFile.value(item); + QtResourcePrefix *resourcePrefix = m_prefixItemToResourcePrefix.value(item); + if (!resourcePrefix) + resourcePrefix = m_languageItemToResourcePrefix.value(item); + + QStandardItem *newCurrentItem = 0; + + if (resourceFile) { + QtResourceFile *nextFile = m_qrcManager->nextResourceFile(resourceFile); + if (!nextFile) + nextFile = m_qrcManager->prevResourceFile(resourceFile); + newCurrentItem = m_resourceFileToPathItem.value(nextFile); + if (!newCurrentItem) + newCurrentItem = m_resourcePrefixToPrefixItem.value(m_qrcManager->resourcePrefixOf(resourceFile)); + } + if (!newCurrentItem) { + QtResourcePrefix *nextPrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + if (!nextPrefix) + nextPrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + newCurrentItem = m_resourcePrefixToPrefixItem.value(nextPrefix); + } + + selectTreeRow(newCurrentItem); + + if (resourcePrefix) + m_qrcManager->removeResourcePrefix(resourcePrefix); + else if (resourceFile) + m_qrcManager->removeResourceFile(resourceFile); +} + +void QtResourceEditorDialogPrivate::slotMoveUp() +{ + if (QtResourceFile *resourceFile = getCurrentResourceFile()) { + QtResourceFile *prevFile = m_qrcManager->prevResourceFile(resourceFile); + + if (!prevFile) + return; + + m_qrcManager->moveResourceFile(resourceFile, prevFile); + selectTreeRow(m_resourceFileToPathItem.value(resourceFile)); + } else if (QtResourcePrefix *resourcePrefix = getCurrentResourcePrefix()) { + QtResourcePrefix *prevPrefix = m_qrcManager->prevResourcePrefix(resourcePrefix); + + if (!prevPrefix) + return; + + m_qrcManager->moveResourcePrefix(resourcePrefix, prevPrefix); + selectTreeRow(m_resourcePrefixToPrefixItem.value(resourcePrefix)); + } +} + +void QtResourceEditorDialogPrivate::slotMoveDown() +{ + if (QtResourceFile *resourceFile = getCurrentResourceFile()) { + QtResourceFile *nextFile = m_qrcManager->nextResourceFile(resourceFile); + + if (!nextFile) + return; + + m_qrcManager->moveResourceFile(resourceFile, m_qrcManager->nextResourceFile(nextFile)); + selectTreeRow(m_resourceFileToPathItem.value(resourceFile)); + } else if (QtResourcePrefix *resourcePrefix = getCurrentResourcePrefix()) { + QtResourcePrefix *nextPrefix = m_qrcManager->nextResourcePrefix(resourcePrefix); + + if (!nextPrefix) + return; + + m_qrcManager->moveResourcePrefix(resourcePrefix, m_qrcManager->nextResourcePrefix(nextPrefix)); + selectTreeRow(m_resourcePrefixToPrefixItem.value(resourcePrefix)); + } +} + +QString QtResourceEditorDialogPrivate::browseForNewLocation(const QString &resourceFile, const QDir &rootDir) const +{ + QFileInfo fi(resourceFile); + const QString initialPath = rootDir.absoluteFilePath(fi.fileName()); + while (1) { + QString newPath = m_dlgGui->getSaveFileName(q_ptr, + QApplication::translate("QtResourceEditorDialog", "Copy As", 0, QApplication::UnicodeUTF8), + initialPath); + QString relativePath = rootDir.relativeFilePath(newPath); + if (relativePath.startsWith(QLatin1String(".."))) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy As", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "<p>The selected file:</p>" + "<p>%1</p><p>is outside of the current resource file's directory:</p><p>%2</p>" + "<p>Please select another path within this directory.<p>", 0, + QApplication::UnicodeUTF8).arg(relativePath).arg(rootDir.absolutePath()), + QMessageBox::Ok | QMessageBox::Cancel, QMessageBox::Ok) != QMessageBox::Ok) + return QString(); + } else { + return newPath; + } + } + + return QString(); +} + +QString QtResourceEditorDialogPrivate::copyResourceFile(const QString &resourceFile, const QString &destPath) const +{ + QFileInfo fi(destPath); + if (fi.exists()) { + while (fi.exists() && !QFile::remove(destPath)) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not overwrite %1.", 0, QApplication::UnicodeUTF8).arg(fi.fileName()), + QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Retry) + return QString(); + } + } + while (!QFile::copy(resourceFile, destPath)) { + if (warning(QApplication::translate("QtResourceEditorDialog", "Copy", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not copy\n%1\nto\n%2", + 0, QApplication::UnicodeUTF8).arg(resourceFile).arg(destPath), + QMessageBox::Retry | QMessageBox::Cancel, QMessageBox::Cancel) != QMessageBox::Retry) + return QString(); + } + return destPath; +} +bool QtResourceEditorDialogPrivate::loadQrcFile(const QString &path, QtQrcFileData *qrcFileData) +{ + QString errorMessage; + const bool rc = loadQrcFile(path, qrcFileData, &errorMessage); +// if (!rc) +// warning(QApplication::translate("QtResourceEditorDialog", "Resource File Load Error"), errorMessage); + return rc; +} +bool QtResourceEditorDialogPrivate::loadQrcFile(const QString &path, QtQrcFileData *qrcFileData, QString *errorMessage) +{ + if (!qrcFileData) + return false; + + qrcFileData->qrcPath = path; + + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + // there is sufficient hint while loading a form and after opening the editor (qrc marked marked with red and with [missing] text) + //*errorMessage = QApplication::translate("QtResourceEditorDialog", "Unable to open %1 for reading: %2").arg(path).arg(file.errorString()); + return false; + } + + QByteArray dataArray = file.readAll(); + file.close(); + + QDomDocument doc; + int errLine, errCol; + if (!doc.setContent(dataArray, errorMessage, &errLine, &errCol)) { + *errorMessage = QCoreApplication::translate("QtResourceEditorDialog", "A parse error occurred at line %1, column %2 of %3:\n%4").arg(errLine).arg(errCol).arg(path).arg(*errorMessage); + return false; + } + + return loadQrcFileData(doc, path, qrcFileData, errorMessage); +} + +bool QtResourceEditorDialogPrivate::saveQrcFile(const QtQrcFileData &qrcFileData) +{ + QFile file(qrcFileData.qrcPath); + while (!file.open(QIODevice::WriteOnly)) { + QMessageBox msgBox(QMessageBox::Warning, + QApplication::translate("QtResourceEditorDialog", "Save Resource File", 0, QApplication::UnicodeUTF8), + QApplication::translate("QtResourceEditorDialog", "Could not write %1: %2", 0, QApplication::UnicodeUTF8).arg(qrcFileData.qrcPath).arg(file.errorString()), + QMessageBox::Cancel|QMessageBox::Ignore|QMessageBox::Retry); + msgBox.setEscapeButton(QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Ignore); + switch (msgBox.exec()) { + case QMessageBox::Retry: + break; // nothing + case QMessageBox::Ignore: + return true; + default: + return false; + } + } + + QDomDocument doc = saveQrcFileData(qrcFileData); + + QByteArray dataArray = doc.toByteArray(2); + file.write(dataArray); + + file.close(); + return true; +} + +QtResourceEditorDialog::QtResourceEditorDialog(QDesignerFormEditorInterface *core, QDesignerDialogGuiInterface *dlgGui, QWidget *parent) + : QDialog(parent) +{ + d_ptr = new QtResourceEditorDialogPrivate(); + d_ptr->q_ptr = this; + d_ptr->m_ui.setupUi(this); + d_ptr->m_qrcManager = new QtQrcManager(this); + d_ptr->m_dlgGui = dlgGui; + d_ptr->m_core = core; + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setWindowTitle(tr("Edit Resources")); + + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileInserted(QtQrcFile *)), + this, SLOT(slotQrcFileInserted(QtQrcFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileMoved(QtQrcFile *, QtQrcFile *)), + this, SLOT(slotQrcFileMoved(QtQrcFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(qrcFileRemoved(QtQrcFile *)), + this, SLOT(slotQrcFileRemoved(QtQrcFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixInserted(QtResourcePrefix *)), + this, SLOT(slotResourcePrefixInserted(QtResourcePrefix *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixMoved(QtResourcePrefix *, QtResourcePrefix *)), + this, SLOT(slotResourcePrefixMoved(QtResourcePrefix *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixChanged(QtResourcePrefix *, const QString &)), + this, SLOT(slotResourcePrefixChanged(QtResourcePrefix *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceLanguageChanged(QtResourcePrefix *, const QString &)), + this, SLOT(slotResourceLanguageChanged(QtResourcePrefix *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourcePrefixRemoved(QtResourcePrefix *)), + this, SLOT(slotResourcePrefixRemoved(QtResourcePrefix *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileInserted(QtResourceFile *)), + this, SLOT(slotResourceFileInserted(QtResourceFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileMoved(QtResourceFile *, QtResourceFile *)), + this, SLOT(slotResourceFileMoved(QtResourceFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceAliasChanged(QtResourceFile *, const QString &)), + this, SLOT(slotResourceAliasChanged(QtResourceFile *))); + connect(d_ptr->m_qrcManager, SIGNAL(resourceFileRemoved(QtResourceFile *)), + this, SLOT(slotResourceFileRemoved(QtResourceFile *))); + + QIcon upIcon = qdesigner_internal::createIconSet(QString::fromUtf8("up.png")); + QIcon downIcon = qdesigner_internal::createIconSet(QString::fromUtf8("down.png")); + QIcon minusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("minus-16.png")); + QIcon newIcon = qdesigner_internal::createIconSet(QString::fromUtf8("filenew-16.png")); + QIcon openIcon = qdesigner_internal::createIconSet(QString::fromUtf8("fileopen-16.png")); + QIcon removeIcon = qdesigner_internal::createIconSet(QString::fromUtf8("editdelete-16.png")); + QIcon addPrefixIcon = qdesigner_internal::createIconSet(QString::fromUtf8("prefix-add.png")); + + d_ptr->m_newQrcFileAction = new QAction(newIcon, tr("New..."), this); + d_ptr->m_newQrcFileAction->setToolTip(tr("New Resource File")); + d_ptr->m_importQrcFileAction = new QAction(openIcon, tr("Open..."), this); + d_ptr->m_importQrcFileAction->setToolTip(tr("Open Resource File")); + d_ptr->m_removeQrcFileAction = new QAction(removeIcon, tr("Remove"), this); + d_ptr->m_moveUpQrcFileAction = new QAction(upIcon, tr("Move Up"), this); + d_ptr->m_moveDownQrcFileAction = new QAction(downIcon, tr("Move Down"), this); + + d_ptr->m_newPrefixAction = new QAction(addPrefixIcon, tr("Add Prefix"), this); + d_ptr->m_newPrefixAction->setToolTip(tr("Add Prefix")); + d_ptr->m_addResourceFileAction = new QAction(openIcon, tr("Add Files..."), this); + d_ptr->m_changePrefixAction = new QAction(tr("Change Prefix"), this); + d_ptr->m_changeLanguageAction = new QAction(tr("Change Language"), this); + d_ptr->m_changeAliasAction = new QAction(tr("Change Alias"), this); + d_ptr->m_clonePrefixAction = new QAction(tr("Clone Prefix..."), this); + d_ptr->m_removeAction = new QAction(minusIcon, tr("Remove"), this); + d_ptr->m_moveUpAction = new QAction(upIcon, tr("Move Up"), this); + d_ptr->m_moveDownAction = new QAction(downIcon, tr("Move Down"), this); + + d_ptr->m_ui.newQrcButton->setDefaultAction(d_ptr->m_newQrcFileAction); + d_ptr->m_ui.importQrcButton->setDefaultAction(d_ptr->m_importQrcFileAction); + d_ptr->m_ui.removeQrcButton->setDefaultAction(d_ptr->m_removeQrcFileAction); + + d_ptr->m_ui.newResourceButton->setDefaultAction(d_ptr->m_newPrefixAction); + d_ptr->m_ui.addResourceButton->setDefaultAction(d_ptr->m_addResourceFileAction); + d_ptr->m_ui.removeResourceButton->setDefaultAction(d_ptr->m_removeAction); + + connect(d_ptr->m_newQrcFileAction, SIGNAL(triggered()), this, SLOT(slotNewQrcFile())); + connect(d_ptr->m_importQrcFileAction, SIGNAL(triggered()), this, SLOT(slotImportQrcFile())); + connect(d_ptr->m_removeQrcFileAction, SIGNAL(triggered()), this, SLOT(slotRemoveQrcFile())); + connect(d_ptr->m_moveUpQrcFileAction, SIGNAL(triggered()), this, SLOT(slotMoveUpQrcFile())); + connect(d_ptr->m_moveDownQrcFileAction, SIGNAL(triggered()), this, SLOT(slotMoveDownQrcFile())); + + connect(d_ptr->m_newPrefixAction, SIGNAL(triggered()), this, SLOT(slotNewPrefix())); + connect(d_ptr->m_addResourceFileAction, SIGNAL(triggered()), this, SLOT(slotAddFiles())); + connect(d_ptr->m_changePrefixAction, SIGNAL(triggered()), this, SLOT(slotChangePrefix())); + connect(d_ptr->m_changeLanguageAction, SIGNAL(triggered()), this, SLOT(slotChangeLanguage())); + connect(d_ptr->m_changeAliasAction, SIGNAL(triggered()), this, SLOT(slotChangeAlias())); + connect(d_ptr->m_clonePrefixAction, SIGNAL(triggered()), this, SLOT(slotClonePrefix())); + connect(d_ptr->m_removeAction, SIGNAL(triggered()), this, SLOT(slotRemove())); + connect(d_ptr->m_moveUpAction, SIGNAL(triggered()), this, SLOT(slotMoveUp())); + connect(d_ptr->m_moveDownAction, SIGNAL(triggered()), this, SLOT(slotMoveDown())); + + d_ptr->m_ui.qrcFileList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(d_ptr->m_ui.qrcFileList, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotListWidgetContextMenuRequested(const QPoint &))); + connect(d_ptr->m_ui.qrcFileList, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + this, SLOT(slotCurrentQrcFileChanged(QListWidgetItem *))); + + d_ptr->m_treeModel = new QStandardItemModel(this); + d_ptr->m_treeModel->setColumnCount(2); + d_ptr->m_treeModel->setHorizontalHeaderItem(0, new QStandardItem(tr("Prefix / Path"))); + d_ptr->m_treeModel->setHorizontalHeaderItem(1, new QStandardItem(tr("Language / Alias"))); + d_ptr->m_ui.resourceTreeView->setModel(d_ptr->m_treeModel); + d_ptr->m_ui.resourceTreeView->setContextMenuPolicy(Qt::CustomContextMenu); + d_ptr->m_treeSelection = d_ptr->m_ui.resourceTreeView->selectionModel(); + connect(d_ptr->m_ui.resourceTreeView->header(), SIGNAL(sectionDoubleClicked(int)), d_ptr->m_ui.resourceTreeView, SLOT(resizeColumnToContents(int))); + d_ptr->m_ui.resourceTreeView->setTextElideMode(Qt::ElideLeft); + + connect(d_ptr->m_ui.resourceTreeView, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotTreeViewContextMenuRequested(const QPoint &))); + connect(d_ptr->m_treeModel, SIGNAL(itemChanged(QStandardItem *)), + this, SLOT(slotTreeViewItemChanged(QStandardItem *))); + connect(d_ptr->m_treeSelection, SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)), + this, SLOT(slotCurrentTreeViewItemChanged(const QModelIndex &))); + + d_ptr->m_ui.resourceTreeView->setColumnWidth(0, 200); + + d_ptr->slotCurrentTreeViewItemChanged(QModelIndex()); + d_ptr->m_removeQrcFileAction->setEnabled(false); + d_ptr->m_moveUpQrcFileAction->setEnabled(false); + d_ptr->m_moveDownQrcFileAction->setEnabled(false); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(QrcDialogC)); + + d_ptr->m_ui.splitter->restoreState(settings->value(QLatin1String(SplitterPosition)).toByteArray()); + if (settings->contains(QLatin1String(Geometry))) + setGeometry(settings->value(QLatin1String(Geometry)).toRect()); + + settings->endGroup(); +} + +QtResourceEditorDialog::~QtResourceEditorDialog() +{ + QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager(); + settings->beginGroup(QLatin1String(QrcDialogC)); + + settings->setValue(QLatin1String(SplitterPosition), d_ptr->m_ui.splitter->saveState()); + settings->setValue(QLatin1String(Geometry), geometry()); + settings->endGroup(); + + delete d_ptr; +} + +QtResourceModel *QtResourceEditorDialog::model() const +{ + return d_ptr->m_resourceModel; +} + +void QtResourceEditorDialog::setResourceModel(QtResourceModel *model) +{ + d_ptr->m_resourceModel = model; + + QtResourceSet *resourceSet = d_ptr->m_resourceModel->currentResourceSet(); + if (!resourceSet) { + // disable everything but cancel button + return; + } + + d_ptr->m_initialState.clear(); + + // enable qrcBox + + QStringList paths = resourceSet->activeQrcPaths(); + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + QtQrcFileData qrcFileData; + d_ptr->loadQrcFile(path, &qrcFileData); + d_ptr->m_initialState << qrcFileData; + d_ptr->m_qrcManager->importQrcFile(qrcFileData); + } + if (d_ptr->m_ui.qrcFileList->count() > 0) { + d_ptr->m_ui.qrcFileList->item(0)->setSelected(true); + } +} + +QString QtResourceEditorDialog::selectedResource() const +{ + //QtResourcePrefix *currentResourcePrefix = d_ptr->m_qrcManager->resourcePrefixOf(currentResourceFile); + QtResourcePrefix *currentResourcePrefix = d_ptr->getCurrentResourcePrefix(); + if (!currentResourcePrefix) + return QString(); + + const QChar slash(QLatin1Char('/')); + QString resource = currentResourcePrefix->prefix(); + if (!resource.startsWith(slash)) + resource.prepend(slash); + if (!resource.endsWith(slash)) + resource.append(slash); + resource.prepend(QLatin1Char(':')); + + QtResourceFile *currentResourceFile = d_ptr->getCurrentResourceFile(); + if (!currentResourceFile) + return resource; + + QString resourceEnding = currentResourceFile->path(); + if (!currentResourceFile->alias().isEmpty()) + resourceEnding = currentResourceFile->alias(); + + const QString dotSlash(QLatin1String("./")); + const QString dotDotSlash(QLatin1String("../")); + while (1) { + if (resourceEnding.startsWith(slash)) + resourceEnding = resourceEnding.mid(1); + else if (resourceEnding.startsWith(dotSlash)) + resourceEnding = resourceEnding.mid(dotSlash.count()); + else if (resourceEnding.startsWith(dotDotSlash)) + resourceEnding = resourceEnding.mid(dotDotSlash.count()); + else + break; + } + + resource.append(resourceEnding); + + return resource; +} + +void QtResourceEditorDialog::displayResourceFailures(const QString &logOutput, QDesignerDialogGuiInterface *dlgGui, QWidget *parent) +{ + const QString msg = tr("<html><p><b>Warning:</b> There have been problems while reloading the resources:</p><pre>%1</pre></html>").arg(logOutput); + dlgGui->message(parent, QDesignerDialogGuiInterface::ResourceEditorMessage, QMessageBox::Warning, + tr("Resource Warning"), msg); +} + +void QtResourceEditorDialog::accept() +{ + QStringList newQrcPaths; + QList<QtQrcFileData> currentState; + + QList<QtQrcFile *> qrcFiles = d_ptr->m_qrcManager->qrcFiles(); + QListIterator<QtQrcFile *> itQrc(qrcFiles); + while (itQrc.hasNext()) { + QtQrcFile *qrcFile = itQrc.next(); + QtQrcFileData qrcFileData; + d_ptr->m_qrcManager->exportQrcFile(qrcFile, &qrcFileData); + currentState << qrcFileData; + if (qrcFileData == qrcFile->initialState()) { + // nothing + } else { + d_ptr->m_resourceModel->setWatcherEnabled(qrcFileData.qrcPath, false); + bool ok = d_ptr->saveQrcFile(qrcFileData); + d_ptr->m_resourceModel->setWatcherEnabled(qrcFileData.qrcPath, true); + if (!ok) + return; + + d_ptr->m_resourceModel->setModified(qrcFileData.qrcPath); + } + newQrcPaths << qrcFileData.qrcPath; + } + + if (currentState == d_ptr->m_initialState) { + // nothing + } else { + int errorCount; + QString errorMessages; + d_ptr->m_resourceModel->currentResourceSet()->activateQrcPaths(newQrcPaths, &errorCount, &errorMessages); + if (errorCount) + displayResourceFailures(errorMessages, d_ptr->m_dlgGui, this); + } + QDialog::accept(); +} + +QString QtResourceEditorDialog::editResources(QDesignerFormEditorInterface *core, + QtResourceModel *model, + QDesignerDialogGuiInterface *dlgGui, + QWidget *parent) +{ + QtResourceEditorDialog dialog(core, dlgGui, parent); + dialog.setResourceModel(model); + if (dialog.exec() == QDialog::Accepted) + return dialog.selectedResource(); + return QString(); +} + +QT_END_NAMESPACE + +#include "moc_qtresourceeditordialog_p.cpp" +#include "qtresourceeditordialog.moc" diff --git a/tools/designer/src/lib/shared/qtresourceeditordialog.ui b/tools/designer/src/lib/shared/qtresourceeditordialog.ui new file mode 100644 index 0000000..fa760d9 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourceeditordialog.ui @@ -0,0 +1,177 @@ +<ui version="4.0" > + <class>QtResourceEditorDialog</class> + <widget class="QDialog" name="QtResourceEditorDialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>469</width> + <height>317</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout" > + <item> + <widget class="QSplitter" name="splitter" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="childrenCollapsible" > + <bool>false</bool> + </property> + <widget class="QWidget" name="qrcLayoutWidget" > + <layout class="QGridLayout" name="qrcLayout" > + <item row="0" column="0" colspan="4" > + <widget class="QListWidget" name="qrcFileList" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Expanding" hsizetype="Ignored" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="1" column="0" > + <widget class="QToolButton" name="newQrcButton" > + <property name="toolTip" > + <string>New File</string> + </property> + <property name="text" > + <string>N</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QToolButton" name="removeQrcButton" > + <property name="toolTip" > + <string>Remove File</string> + </property> + <property name="text" > + <string>R</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType" > + <enum>QSizePolicy::Ignored</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>21</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" > + <widget class="QToolButton" name="importQrcButton" > + <property name="text" > + <string>I</string> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="resourceLayoutWidget" > + <layout class="QGridLayout" name="resourceLayout" > + <item row="0" column="0" colspan="4" > + <widget class="QTreeView" name="resourceTreeView" /> + </item> + <item row="1" column="0" > + <widget class="QToolButton" name="newResourceButton" > + <property name="toolTip" > + <string>New Resource</string> + </property> + <property name="text" > + <string>N</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QToolButton" name="addResourceButton" > + <property name="text" > + <string>A</string> + </property> + </widget> + </item> + <item row="1" column="2" > + <widget class="QToolButton" name="removeResourceButton" > + <property name="toolTip" > + <string>Remove Resource or File</string> + </property> + <property name="text" > + <string>R</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <spacer name="horizontalSpacer" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>QtResourceEditorDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>QtResourceEditorDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/qtresourceeditordialog_p.h b/tools/designer/src/lib/shared/qtresourceeditordialog_p.h new file mode 100644 index 0000000..f2531d9 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourceeditordialog_p.h @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEEDITOR_H +#define QTRESOURCEEDITOR_H + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QDesignerDialogGuiInterface; +class QDesignerFormEditorInterface; + +class QtResourceEditorDialog : public QDialog +{ + Q_OBJECT +public: + QtResourceModel *model() const; + void setResourceModel(QtResourceModel *model); + + QString selectedResource() const; + + static QString editResources(QDesignerFormEditorInterface *core, QtResourceModel *model, + QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + + // Helper to display a message box with rcc logs in case of errors. + static void displayResourceFailures(const QString &logOutput, QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + +public slots: + virtual void accept(); + +private: + QtResourceEditorDialog(QDesignerFormEditorInterface *core, QDesignerDialogGuiInterface *dlgGui, QWidget *parent = 0); + ~QtResourceEditorDialog(); + + class QtResourceEditorDialogPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtResourceEditorDialog) + Q_DISABLE_COPY(QtResourceEditorDialog) + + Q_PRIVATE_SLOT(d_func(), void slotQrcFileInserted(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotQrcFileMoved(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotQrcFileRemoved(QtQrcFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixInserted(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixMoved(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixChanged(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceLanguageChanged(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourcePrefixRemoved(QtResourcePrefix *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileInserted(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileMoved(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceAliasChanged(QtResourceFile *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceFileRemoved(QtResourceFile *)) + + Q_PRIVATE_SLOT(d_func(), void slotCurrentQrcFileChanged(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeViewItemChanged(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotTreeViewContextMenuRequested(const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotTreeViewItemChanged(QStandardItem *)) + + Q_PRIVATE_SLOT(d_func(), void slotNewQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotImportQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotRemoveQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotMoveUpQrcFile()) + Q_PRIVATE_SLOT(d_func(), void slotMoveDownQrcFile()) + + Q_PRIVATE_SLOT(d_func(), void slotNewPrefix()) + Q_PRIVATE_SLOT(d_func(), void slotAddFiles()) + Q_PRIVATE_SLOT(d_func(), void slotChangePrefix()) + Q_PRIVATE_SLOT(d_func(), void slotChangeLanguage()) + Q_PRIVATE_SLOT(d_func(), void slotChangeAlias()) + Q_PRIVATE_SLOT(d_func(), void slotClonePrefix()) + Q_PRIVATE_SLOT(d_func(), void slotRemove()) + Q_PRIVATE_SLOT(d_func(), void slotMoveUp()) + Q_PRIVATE_SLOT(d_func(), void slotMoveDown()) +}; + +QT_END_NAMESPACE + +#endif + diff --git a/tools/designer/src/lib/shared/qtresourcemodel.cpp b/tools/designer/src/lib/shared/qtresourcemodel.cpp new file mode 100644 index 0000000..e6152be --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourcemodel.cpp @@ -0,0 +1,648 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtresourcemodel_p.h" +#include <rcc.h> + +#include <QtCore/QStringList> +#include <QtCore/QMap> +#include <QtCore/QResource> +#include <QtCore/QFileInfo> +#include <QtCore/QIODevice> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtCore/QBuffer> +#include <QtCore/QFileSystemWatcher> + +QT_BEGIN_NAMESPACE + +enum { debugResourceModel = 0 }; + +// ------------------- QtResourceSetPrivate +class QtResourceSetPrivate +{ + QtResourceSet *q_ptr; + Q_DECLARE_PUBLIC(QtResourceSet) +public: + QtResourceSetPrivate(QtResourceModel *model = 0); + + QtResourceModel *m_resourceModel; +}; + +QtResourceSetPrivate::QtResourceSetPrivate(QtResourceModel *model) : + q_ptr(0), + m_resourceModel(model) +{ +} + +// -------------------- QtResourceModelPrivate +class QtResourceModelPrivate +{ + QtResourceModel *q_ptr; + Q_DECLARE_PUBLIC(QtResourceModel) + Q_DISABLE_COPY(QtResourceModelPrivate) +public: + QtResourceModelPrivate(); + void activate(QtResourceSet *resourceSet, const QStringList &newPaths, int *errorCount = 0, QString *errorMessages = 0); + void removeOldPaths(QtResourceSet *resourceSet, const QStringList &newPaths); + + QMap<QString, bool> m_pathToModified; + QMap<QtResourceSet *, QStringList> m_resourceSetToPaths; + QMap<QtResourceSet *, bool> m_resourceSetToReload; // while path is recreated it needs to be reregistered + // (it is - in the new current resource set, but when the path was used in + // other resource set + // then later when that resource set is activated it needs to be reregistered) + QMap<QtResourceSet *, bool> m_newlyCreated; // all created but not activated yet + // (if was active at some point and it's not now it will not be on that map) + QMap<QString, QList<QtResourceSet *> > m_pathToResourceSet; + QtResourceSet *m_currentResourceSet; + + typedef QMap<QString, const QByteArray *> PathDataMap; + PathDataMap m_pathToData; + + QMap<QString, QStringList> m_pathToContents; // qrc path to its contents. + QMap<QString, QString> m_fileToQrc; // this map contains the content of active resource set only. + // Activating different resource set changes the contents. + + QFileSystemWatcher *m_fileWatcher; + bool m_fileWatcherEnabled; + QMap<QString, bool> m_fileWatchedMap; +private: + void registerResourceSet(QtResourceSet *resourceSet); + void unregisterResourceSet(QtResourceSet *resourceSet); + void setWatcherEnabled(const QString &path, bool enable); + void addWatcher(const QString &path); + void removeWatcher(const QString &path); + + void slotFileChanged(const QString &); + + const QByteArray *createResource(const QString &path, QStringList *contents, int *errorCount, QIODevice &errorDevice) const; + void deleteResource(const QByteArray *data) const; +}; + +QtResourceModelPrivate::QtResourceModelPrivate() : + q_ptr(0), + m_currentResourceSet(0), + m_fileWatcher(0), + m_fileWatcherEnabled(true) +{ +} + +// --------------------- QtResourceSet +QtResourceSet::QtResourceSet() : + d_ptr(new QtResourceSetPrivate) +{ + d_ptr->q_ptr = this; +} + +QtResourceSet::QtResourceSet(QtResourceModel *model) : + d_ptr(new QtResourceSetPrivate(model)) +{ + d_ptr->q_ptr = this; +} + +QtResourceSet::~QtResourceSet() +{ + delete d_ptr; +} + +QStringList QtResourceSet::activeQrcPaths() const +{ + QtResourceSet *that = const_cast<QtResourceSet *>(this); + return d_ptr->m_resourceModel->d_ptr->m_resourceSetToPaths.value(that); +} + +void QtResourceSet::activateQrcPaths(const QStringList &paths, int *errorCount, QString *errorMessages) +{ + d_ptr->m_resourceModel->d_ptr->activate(this, paths, errorCount, errorMessages); +} + +bool QtResourceSet::isModified(const QString &path) const +{ + return d_ptr->m_resourceModel->isModified(path); +} + +void QtResourceSet::setModified(const QString &path) +{ + d_ptr->m_resourceModel->setModified(path); +} + +// ------------------- QtResourceModelPrivate +const QByteArray *QtResourceModelPrivate::createResource(const QString &path, QStringList *contents, int *errorCount, QIODevice &errorDevice) const +{ + typedef RCCResourceLibrary::ResourceDataFileMap ResourceDataFileMap; + const QByteArray *rc = 0; + *errorCount = -1; + contents->clear(); + do { + // run RCC + RCCResourceLibrary library; + library.setVerbose(true); + library.setInputFiles(QStringList(path)); + library.setFormat(RCCResourceLibrary::Binary); + + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + if (!library.readFiles(/* ignore errors*/ true, errorDevice)) + break; + // return code cannot be fully trusted, might still be empty + const ResourceDataFileMap resMap = library.resourceDataFileMap(); + if (resMap.empty()) + break; + + if (!library.output(buffer, errorDevice)) + break; + + *errorCount = library.failedResources().size(); + *contents = resMap.keys(); + + buffer.close(); + rc = new QByteArray(buffer.data()); + } while (false); + + if (debugResourceModel) + qDebug() << "createResource" << path << "returns data=" << rc << " hasWarnings=" << *errorCount; + return rc; +} + +void QtResourceModelPrivate::deleteResource(const QByteArray *data) const +{ + if (data) { + if (debugResourceModel) + qDebug() << "deleteResource"; + delete data; + } +} + +void QtResourceModelPrivate::registerResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + + // unregister old paths (all because the order of registration is important), later it can be optimized a bit + QStringList toRegister = resourceSet->activeQrcPaths(); + QStringListIterator itRegister(toRegister); + while (itRegister.hasNext()) { + const QString path = itRegister.next(); + if (debugResourceModel) + qDebug() << "registerResourceSet " << path; + const PathDataMap::const_iterator itRcc = m_pathToData.constFind(path); + if (itRcc != m_pathToData.constEnd()) { // otherwise data was not created yet + if (!QResource::registerResource(reinterpret_cast<const uchar *>(itRcc.value()->constData()))) { + qDebug() << "** WARNING: Failed to register " << path << " (QResource failure)."; + } else { + QStringList contents = m_pathToContents.value(path); + QStringListIterator itContents(contents); + while (itContents.hasNext()) { + const QString filePath = itContents.next(); + if (!m_fileToQrc.contains(filePath)) // the first loaded resource has higher priority in qt resource system + m_fileToQrc.insert(filePath, path); + } + } + } + } +} + +void QtResourceModelPrivate::unregisterResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + + // unregister old paths (all because the order of registration is importans), later it can be optimized a bit + QStringList toUnregister = resourceSet->activeQrcPaths(); + QStringListIterator itUnregister(toUnregister); + while (itUnregister.hasNext()) { + const QString path = itUnregister.next(); + if (debugResourceModel) + qDebug() << "unregisterResourceSet " << path; + const PathDataMap::const_iterator itRcc = m_pathToData.constFind(path); + if (itRcc != m_pathToData.constEnd()) { // otherwise data was not created yet + if (!QResource::unregisterResource(reinterpret_cast<const uchar *>(itRcc.value()->constData()))) + qDebug() << "** WARNING: Failed to unregister " << path << " (QResource failure)."; + } + } + m_fileToQrc.clear(); +} + +void QtResourceModelPrivate::activate(QtResourceSet *resourceSet, const QStringList &newPaths, int *errorCountPtr, QString *errorMessages) +{ + if (debugResourceModel) + qDebug() << "activate " << resourceSet; + if (errorCountPtr) + *errorCountPtr = 0; + if (errorMessages) + errorMessages->clear(); + + QBuffer errorStream; + errorStream.open(QIODevice::WriteOnly); + + int errorCount = 0; + int generatedCount = 0; + bool newResourceSetChanged = false; + + if (resourceSet && resourceSet->activeQrcPaths() != newPaths && !m_newlyCreated.contains(resourceSet)) + newResourceSetChanged = true; + + PathDataMap newPathToData = m_pathToData; + + QStringListIterator itPath(newPaths); + while (itPath.hasNext()) { + const QString path = itPath.next(); + if (resourceSet && !m_pathToResourceSet[path].contains(resourceSet)) + m_pathToResourceSet[path].append(resourceSet); + const QMap<QString, bool>::iterator itMod = m_pathToModified.find(path); + if (itMod == m_pathToModified.end() || itMod.value()) { // new path or path is already created, but needs to be recreated + QStringList contents; + int qrcErrorCount; + generatedCount++; + if (const QByteArray *data = createResource(path, &contents, &qrcErrorCount, errorStream)) { + newPathToData.insert(path, data); + if (qrcErrorCount) // Count single failed files as sort of 1/2 error + errorCount++; + addWatcher(path); + } else { + newPathToData.remove(path); + errorCount++; + } + m_pathToModified.insert(path, false); + m_pathToContents.insert(path, contents); + newResourceSetChanged = true; + const QMap<QString, QList<QtResourceSet *> >::iterator itReload = m_pathToResourceSet.find(path); + if (itReload != m_pathToResourceSet.end()) { + QList<QtResourceSet *> resources = itReload.value(); + QListIterator<QtResourceSet *> itRes(resources); + while (itRes.hasNext()) { + QtResourceSet *res = itRes.next(); + if (res != resourceSet) { + m_resourceSetToReload[res] = true; + } + } + } + } else { // path is already created, don't need to recreate + } + } + + QList<const QByteArray *> oldData = m_pathToData.values(); + QList<const QByteArray *> newData = newPathToData.values(); + + QList<const QByteArray *> toDelete; + QListIterator<const QByteArray *> itOld(oldData); + if (itOld.hasNext()) { + const QByteArray *array = itOld.next(); + if (!newData.contains(array)) + toDelete.append(array); + } + + // Nothing can fail below here? + if (generatedCount) { + if (errorCountPtr) + *errorCountPtr = errorCount; + errorStream.close(); + const QString stderrOutput = QString::fromUtf8(errorStream.data()); + if (debugResourceModel) + qDebug() << "Output: (" << errorCount << ")\n" << stderrOutput; + if (errorMessages) + *errorMessages = stderrOutput; + } + // register + const QMap<QtResourceSet *, bool>::iterator itReload = m_resourceSetToReload.find(resourceSet); + if (itReload != m_resourceSetToReload.end()) { + if (itReload.value()) { + newResourceSetChanged = true; + m_resourceSetToReload.insert(resourceSet, false); + } + } + + QStringList oldActivePaths; + if (m_currentResourceSet) + oldActivePaths = m_currentResourceSet->activeQrcPaths(); + + const bool needReregister = (oldActivePaths != newPaths) || newResourceSetChanged; + + QMap<QtResourceSet *, bool>::iterator itNew = m_newlyCreated.find(resourceSet); + if (itNew != m_newlyCreated.end()) { + m_newlyCreated.remove(resourceSet); + if (needReregister) + newResourceSetChanged = true; + } + + if (!newResourceSetChanged && !needReregister && (m_currentResourceSet == resourceSet)) { + foreach (const QByteArray *data, toDelete) + deleteResource(data); + + return; // nothing changed + } + + if (needReregister) + unregisterResourceSet(m_currentResourceSet); + + foreach (const QByteArray *data, toDelete) + deleteResource(data); + + m_pathToData = newPathToData; + m_currentResourceSet = resourceSet; + + if (resourceSet) + removeOldPaths(resourceSet, newPaths); + + if (needReregister) + registerResourceSet(m_currentResourceSet); + + emit q_ptr->resourceSetActivated(m_currentResourceSet, newResourceSetChanged); + + // deactivates the paths from old current resource set + // add new paths to the new current resource set + // reloads all paths which are marked as modified from the current resource set; + // activates the paths from current resource set + // emits resourceSetActivated() (don't emit only in case when old resource set is the same as new one + // AND no path was reloaded AND the list of paths is exactly the same) +} + +void QtResourceModelPrivate::removeOldPaths(QtResourceSet *resourceSet, const QStringList &newPaths) +{ + QStringList oldPaths = m_resourceSetToPaths.value(resourceSet); + if (oldPaths != newPaths) { + // remove old + QStringListIterator itOldPaths(oldPaths); + while (itOldPaths.hasNext()) { + QString oldPath = itOldPaths.next(); + if (!newPaths.contains(oldPath)) { + const QMap<QString, QList<QtResourceSet *> >::iterator itRemove = m_pathToResourceSet.find(oldPath); + if (itRemove != m_pathToResourceSet.end()) { + const int idx = itRemove.value().indexOf(resourceSet); + if (idx >= 0) + itRemove.value().removeAt(idx); + if (itRemove.value().count() == 0) { + PathDataMap::iterator it = m_pathToData.find(oldPath); + if (it != m_pathToData.end()) + deleteResource(it.value()); + m_pathToResourceSet.erase(itRemove); + m_pathToModified.remove(oldPath); + m_pathToContents.remove(oldPath); + m_pathToData.remove(oldPath); + removeWatcher(oldPath); + } + } + } + } + m_resourceSetToPaths[resourceSet] = newPaths; + } +} + +void QtResourceModelPrivate::setWatcherEnabled(const QString &path, bool enable) +{ + m_fileWatcher->removePath(path); + + if (!enable) + return; + + QFileInfo fi(path); + if (fi.exists()) + m_fileWatcher->addPath(path); +} + +void QtResourceModelPrivate::addWatcher(const QString &path) +{ + QMap<QString, bool>::ConstIterator it = m_fileWatchedMap.constFind(path); + if (it != m_fileWatchedMap.constEnd() && it.value() == false) + return; + + m_fileWatchedMap.insert(path, true); + if (!m_fileWatcherEnabled) + return; + setWatcherEnabled(path, true); +} + +void QtResourceModelPrivate::removeWatcher(const QString &path) +{ + if (!m_fileWatchedMap.contains(path)) + return; + + m_fileWatchedMap.remove(path); + if (!m_fileWatcherEnabled) + return; + setWatcherEnabled(path, false); +} + +void QtResourceModelPrivate::slotFileChanged(const QString &path) +{ + setWatcherEnabled(path, false); + emit q_ptr->qrcFileModifiedExternally(path); + setWatcherEnabled(path, true); //readd +} + +// ----------------------- QtResourceModel +QtResourceModel::QtResourceModel(QObject *parent) : + QObject(parent), + d_ptr(new QtResourceModelPrivate) +{ + d_ptr->q_ptr = this; + + d_ptr->m_fileWatcher = new QFileSystemWatcher(this); + connect(d_ptr->m_fileWatcher, SIGNAL(fileChanged(const QString &)), + this, SLOT(slotFileChanged(const QString &))); +} + +QtResourceModel::~QtResourceModel() +{ + blockSignals(true); + QList<QtResourceSet *> resourceList = resourceSets(); + QListIterator<QtResourceSet *> it(resourceList); + while (it.hasNext()) + removeResourceSet(it.next()); + blockSignals(false); + delete d_ptr; +} + +QStringList QtResourceModel::loadedQrcFiles() const +{ + return d_ptr->m_pathToModified.keys(); +} + +bool QtResourceModel::isModified(const QString &path) const +{ + QMap<QString, bool>::const_iterator it = d_ptr->m_pathToModified.find(path); + if (it != d_ptr->m_pathToModified.constEnd()) + return it.value(); + return true; +} + +void QtResourceModel::setModified(const QString &path) +{ + QMap<QString, bool>::const_iterator itMod = d_ptr->m_pathToModified.find(path); + if (itMod == d_ptr->m_pathToModified.constEnd()) + return; + + d_ptr->m_pathToModified[path] = true; + QMap<QString, QList<QtResourceSet *> >::const_iterator it = d_ptr->m_pathToResourceSet.constFind(path); + if (it == d_ptr->m_pathToResourceSet.constEnd()) + return; + + QList<QtResourceSet *> resourceList = it.value(); + QListIterator<QtResourceSet *> itReload(resourceList); + while (itReload.hasNext()) + d_ptr->m_resourceSetToReload.insert(itReload.next(), true); +} + +QList<QtResourceSet *> QtResourceModel::resourceSets() const +{ + return d_ptr->m_resourceSetToPaths.keys(); +} + +QtResourceSet *QtResourceModel::currentResourceSet() const +{ + return d_ptr->m_currentResourceSet; +} + +void QtResourceModel::setCurrentResourceSet(QtResourceSet *resourceSet, int *errorCount, QString *errorMessages) +{ + d_ptr->activate(resourceSet, d_ptr->m_resourceSetToPaths.value(resourceSet), errorCount, errorMessages); +} + +QtResourceSet *QtResourceModel::addResourceSet(const QStringList &paths) +{ + QtResourceSet *newResource = new QtResourceSet(this); + d_ptr->m_resourceSetToPaths.insert(newResource, paths); + d_ptr->m_resourceSetToReload.insert(newResource, false); + d_ptr->m_newlyCreated.insert(newResource, true); + QStringListIterator it(paths); + while (it.hasNext()) { + const QString path = it.next(); + d_ptr->m_pathToResourceSet[path].append(newResource); + } + return newResource; +} + +// TODO +void QtResourceModel::removeResourceSet(QtResourceSet *resourceSet) +{ + if (!resourceSet) + return; + if (currentResourceSet() == resourceSet) + setCurrentResourceSet(0); + + // remove rcc files for those paths which are not used in any other resource set + d_ptr->removeOldPaths(resourceSet, QStringList()); + + d_ptr->m_resourceSetToPaths.remove(resourceSet); + d_ptr->m_resourceSetToReload.remove(resourceSet); + d_ptr->m_newlyCreated.remove(resourceSet); + delete resourceSet; +} + +void QtResourceModel::reload(const QString &path, int *errorCount, QString *errorMessages) +{ + setModified(path); + + d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages); +} + +void QtResourceModel::reload(int *errorCount, QString *errorMessages) +{ + QMap<QString, bool>::iterator it = d_ptr->m_pathToModified.begin(); + QMap<QString, bool>::iterator itEnd = d_ptr->m_pathToModified.end(); // will it be valid when I iterate the map and change it??? + while (it != itEnd) { + it = d_ptr->m_pathToModified.insert(it.key(), true); + ++it; + } + + QMap<QtResourceSet *, bool>::iterator itReload = d_ptr->m_resourceSetToReload.begin(); + QMap<QtResourceSet *, bool>::iterator itReloadEnd = d_ptr->m_resourceSetToReload.end(); + while (itReload != itReloadEnd) { + itReload = d_ptr->m_resourceSetToReload.insert(itReload.key(), true); // empty resourceSets could be omitted here + ++itReload; + } + + d_ptr->activate(d_ptr->m_currentResourceSet, d_ptr->m_resourceSetToPaths.value(d_ptr->m_currentResourceSet), errorCount, errorMessages); +} + +QMap<QString, QString> QtResourceModel::contents() const +{ + return d_ptr->m_fileToQrc; +} + +QString QtResourceModel::qrcPath(const QString &file) const +{ + return d_ptr->m_fileToQrc.value(file); +} + +void QtResourceModel::setWatcherEnabled(bool enable) +{ + if (d_ptr->m_fileWatcherEnabled == enable) + return; + + d_ptr->m_fileWatcherEnabled = enable; + + QMapIterator<QString, bool> it(d_ptr->m_fileWatchedMap); + if (it.hasNext()) + d_ptr->setWatcherEnabled(it.next().key(), d_ptr->m_fileWatcherEnabled); +} + +bool QtResourceModel::isWatcherEnabled() const +{ + return d_ptr->m_fileWatcherEnabled; +} + +void QtResourceModel::setWatcherEnabled(const QString &path, bool enable) +{ + QMap<QString, bool>::Iterator it = d_ptr->m_fileWatchedMap.find(path); + if (it == d_ptr->m_fileWatchedMap.end()) + return; + + if (it.value() == enable) + return; + + it.value() = enable; + + if (!d_ptr->m_fileWatcherEnabled) + return; + + d_ptr->setWatcherEnabled(it.key(), enable); +} + +bool QtResourceModel::isWatcherEnabled(const QString &path) +{ + return d_ptr->m_fileWatchedMap.value(path, false); +} + +QT_END_NAMESPACE + +#include "moc_qtresourcemodel_p.cpp" diff --git a/tools/designer/src/lib/shared/qtresourcemodel_p.h b/tools/designer/src/lib/shared/qtresourcemodel_p.h new file mode 100644 index 0000000..726db29 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourcemodel_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEMODEL_H +#define QTRESOURCEMODEL_H + +#include "shared_global_p.h" +#include <QtCore/QMap> +#include <QtCore/QObject> + +QT_BEGIN_NAMESPACE + +class QtResourceModel; + +class QDESIGNER_SHARED_EXPORT QtResourceSet // one instance per one form +{ +public: + QStringList activeQrcPaths() const; + + // activateQrcPaths(): if this QtResourceSet is active it emits resourceSetActivated(); + // otherwise only in case if active QtResource set contains one of + // paths which was marked as modified by this resource set, the signal + // is emitted (with reload = true); + // If new path appears on the list it is automatically added to + // loaded list of QtResourceModel. In addition it is marked as modified in case + // QtResourceModel didn't contain the path. + // If some path is removed from that list (and is not used in any other + // resource set) it is automatically unloaded. The removed file can also be + // marked as modified (later when another resource set which contains + // removed path is activated will be reloaded) + void activateQrcPaths(const QStringList &paths, int *errorCount = 0, QString *errorMessages = 0); + + bool isModified(const QString &path) const; // for all paths in resource model (redundant here, maybe it should be removed from here) + void setModified(const QString &path); // for all paths in resource model (redundant here, maybe it should be removed from here) +private: + QtResourceSet(); + QtResourceSet(QtResourceModel *model); + ~QtResourceSet(); + friend class QtResourceModel; + + class QtResourceSetPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtResourceSet) + Q_DISABLE_COPY(QtResourceSet) +}; + +class QDESIGNER_SHARED_EXPORT QtResourceModel : public QObject // one instance per whole designer +{ + Q_OBJECT +public: + QtResourceModel(QObject *parent = 0); + ~QtResourceModel(); + + QStringList loadedQrcFiles() const; + bool isModified(const QString &path) const; // only for paths which are on loadedQrcFiles() list + void setModified(const QString &path); // only for paths which are on loadedQrcPaths() list + + QList<QtResourceSet *> resourceSets() const; + + QtResourceSet *currentResourceSet() const; + void setCurrentResourceSet(QtResourceSet *resourceSet, int *errorCount = 0, QString *errorMessages = 0); + + QtResourceSet *addResourceSet(const QStringList &paths); + void removeResourceSet(QtResourceSet *resourceSet); + + void reload(const QString &path, int *errorCount = 0, QString *errorMessages = 0); + void reload(int *errorCount = 0, QString *errorMessages = 0); + + // Contents of the current resource set (content file to qrc path) + QMap<QString, QString> contents() const; + // Find the qrc file belonging to the contained file (from current resource set) + QString qrcPath(const QString &file) const; + + void setWatcherEnabled(bool enable); + bool isWatcherEnabled() const; + + void setWatcherEnabled(const QString &path, bool enable); + bool isWatcherEnabled(const QString &path); + +signals: + void resourceSetActivated(QtResourceSet *resourceSet, bool resourceSetChanged); // resourceSetChanged since last time it was activated! + void qrcFileModifiedExternally(const QString &path); + +private: + friend class QtResourceSet; + + class QtResourceModelPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtResourceModel) + Q_DISABLE_COPY(QtResourceModel) + + Q_PRIVATE_SLOT(d_func(), void slotFileChanged(const QString &)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/qtresourceview.cpp b/tools/designer/src/lib/shared/qtresourceview.cpp new file mode 100644 index 0000000..d956491 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourceview.cpp @@ -0,0 +1,766 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "abstractsettings_p.h" +#include "qtresourceview_p.h" +#include "qtresourcemodel_p.h" +#include "qtresourceeditordialog_p.h" +#include "iconloader_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtGui/QToolBar> +#include <QtGui/QAction> +#include <QtGui/QSplitter> +#include <QtGui/QTreeWidget> +#include <QtGui/QListWidget> +#include <QtGui/QHeaderView> +#include <QtGui/QVBoxLayout> +#include <QtGui/QPainter> +#include <QtCore/QFileInfo> +#include <QtCore/QDir> +#include <QtCore/QQueue> +#include <QtGui/QPainter> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QPushButton> +#include <QtGui/QMessageBox> +#include <QtGui/QApplication> +#include <QtGui/QClipboard> +#include <QtGui/QMenu> +#include <QtGui/QDrag> +#include <QtCore/QMimeData> +#include <QtXml/QDomDocument> + +QT_BEGIN_NAMESPACE + +static const char *elementResourceData = "resource"; +static const char *typeAttribute = "type"; +static const char *typeImage = "image"; +static const char *typeStyleSheet = "stylesheet"; +static const char *typeOther = "other"; +static const char *fileAttribute = "file"; +static const char *SplitterPosition = "SplitterPosition"; +static const char *Geometry = "Geometry"; +static const char *ResourceViewDialogC = "ResourceDialog"; + +// ---------------- ResourceListWidget: A list widget that has drag enabled +class ResourceListWidget : public QListWidget { +public: + ResourceListWidget(QWidget *parent = 0); + +protected: + virtual void startDrag(Qt::DropActions supportedActions); +}; + +ResourceListWidget::ResourceListWidget(QWidget *parent) : + QListWidget(parent) +{ + setDragEnabled(true); +} + +void ResourceListWidget::startDrag(Qt::DropActions supportedActions) +{ + if (supportedActions == Qt::MoveAction) + return; + + QListWidgetItem *item = currentItem(); + if (!item) + return; + + const QString filePath = item->data(Qt::UserRole).toString(); + const QIcon icon = item->icon(); + + QMimeData *mimeData = new QMimeData; + const QtResourceView::ResourceType type = icon.isNull() ? QtResourceView::ResourceOther : QtResourceView::ResourceImage; + mimeData->setText(QtResourceView::encodeMimeData(type , filePath)); + + QDrag *drag = new QDrag(this); + if (!icon.isNull()) { + const QSize size = icon.actualSize(iconSize()); + drag->setPixmap(icon.pixmap(size)); + drag->setHotSpot(QPoint(size.width() / 2, size.height() / 2)); + } + + drag->setMimeData(mimeData); + drag->exec(Qt::CopyAction); +} + +/* TODO + + 1) load the icons in separate thread...Hmm..if Qt is configured with threads.... +*/ + +// ---------------------------- QtResourceViewPrivate +class QtResourceViewPrivate +{ + QtResourceView *q_ptr; + Q_DECLARE_PUBLIC(QtResourceView) +public: + QtResourceViewPrivate(QDesignerFormEditorInterface *core); + + void slotResourceSetActivated(QtResourceSet *resourceSet); + void slotCurrentPathChanged(QTreeWidgetItem *); + void slotCurrentResourceChanged(QListWidgetItem *); + void slotResourceActivated(QListWidgetItem *); + void slotEditResources(); + void slotReloadResources(); + void slotCopyResourcePath(); + void slotListWidgetContextMenuRequested(const QPoint &pos); + void createPaths(); + QTreeWidgetItem *createPath(const QString &path, QTreeWidgetItem *parent); + void createResources(const QString &path); + void storeExpansionState(); + void applyExpansionState(); + void restoreSettings(); + void saveSettings(); + void updateActions(); + + QPixmap makeThumbnail(const QPixmap &pix) const; + + QDesignerFormEditorInterface *m_core; + QtResourceModel *m_resourceModel; + QToolBar *m_toolBar; + QTreeWidget *m_treeWidget; + QListWidget *m_listWidget; + QSplitter *m_splitter; + QMap<QString, QStringList> m_pathToContents; // full path to contents file names + QMap<QString, QTreeWidgetItem *> m_pathToItem; + QMap<QTreeWidgetItem *, QString> m_itemToPath; + QMap<QString, QListWidgetItem *> m_resourceToItem; + QMap<QListWidgetItem *, QString> m_itemToResource; + QAction *m_editResourcesAction; + QAction *m_reloadResourcesAction; + QAction *m_copyResourcePathAction; + + QMap<QString, bool> m_expansionState; + + bool m_ignoreGuiSignals; + QString m_settingsKey; + bool m_resourceEditingEnabled; +}; + +QtResourceViewPrivate::QtResourceViewPrivate(QDesignerFormEditorInterface *core) : + q_ptr(0), + m_core(core), + m_resourceModel(0), + m_toolBar(new QToolBar), + m_treeWidget(new QTreeWidget), + m_listWidget(new ResourceListWidget), + m_splitter(0), + m_editResourcesAction(0), + m_reloadResourcesAction(0), + m_copyResourcePathAction(0), + m_ignoreGuiSignals(false), + m_resourceEditingEnabled(true) +{ +} + +void QtResourceViewPrivate::restoreSettings() +{ + if (m_settingsKey.isEmpty()) + return; + + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(m_settingsKey); + + m_splitter->restoreState(settings->value(QLatin1String(SplitterPosition)).toByteArray()); + settings->endGroup(); +} + +void QtResourceViewPrivate::saveSettings() +{ + if (m_settingsKey.isEmpty()) + return; + + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(m_settingsKey); + + settings->setValue(QLatin1String(SplitterPosition), m_splitter->saveState()); + settings->endGroup(); +} + +void QtResourceViewPrivate::slotEditResources() +{ + const QString selectedResource + = QtResourceEditorDialog::editResources(m_core, m_resourceModel, + m_core->dialogGui(), q_ptr); + if (!selectedResource.isEmpty()) + q_ptr->selectResource(selectedResource); +} + +void QtResourceViewPrivate::slotReloadResources() +{ + if (m_resourceModel) { + int errorCount; + QString errorMessages; + m_resourceModel->reload(&errorCount, &errorMessages); + if (errorCount) + QtResourceEditorDialog::displayResourceFailures(errorMessages, m_core->dialogGui(), q_ptr); + } +} + +void QtResourceViewPrivate::slotCopyResourcePath() +{ + const QString path = q_ptr->selectedResource(); + QClipboard *clipboard = QApplication::clipboard(); + clipboard->setText(path); +} + +void QtResourceViewPrivate::slotListWidgetContextMenuRequested(const QPoint &pos) +{ + QMenu menu(q_ptr); + menu.addAction(m_copyResourcePathAction); + menu.exec(m_listWidget->mapToGlobal(pos)); +} + +void QtResourceViewPrivate::storeExpansionState() +{ + QMapIterator<QString, QTreeWidgetItem *> it(m_pathToItem); + while (it.hasNext()) { + it.next(); + m_expansionState[it.key()] = it.value()->isExpanded(); + } +} + +void QtResourceViewPrivate::applyExpansionState() +{ + QMapIterator<QString, QTreeWidgetItem *> it(m_pathToItem); + while (it.hasNext()) { + it.next(); + it.value()->setExpanded(m_expansionState.value(it.key(), true)); + } +} + +QPixmap QtResourceViewPrivate::makeThumbnail(const QPixmap &pix) const +{ + int w = qMax(48, pix.width()); + int h = qMax(48, pix.height()); + QRect imgRect(0, 0, w, h); + QImage img(w, h, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + if (!pix.isNull()) { + QRect r(0, 0, pix.width(), pix.height()); + r.moveCenter(imgRect.center()); + QPainter p(&img); + p.drawPixmap(r.topLeft(), pix); + } + return QPixmap::fromImage(img); +} + +void QtResourceViewPrivate::updateActions() +{ + bool resourceActive = false; + if (m_resourceModel) + resourceActive = m_resourceModel->currentResourceSet(); + + m_editResourcesAction->setVisible(m_resourceEditingEnabled); + m_editResourcesAction->setEnabled(resourceActive); + m_reloadResourcesAction->setEnabled(resourceActive); +} + +void QtResourceViewPrivate::slotResourceSetActivated(QtResourceSet *resourceSet) +{ + Q_UNUSED(resourceSet) + + updateActions(); + + storeExpansionState(); + const QString currentPath = m_itemToPath.value(m_treeWidget->currentItem()); + const QString currentResource = m_itemToResource.value(m_listWidget->currentItem()); + m_treeWidget->clear(); + m_pathToContents.clear(); + m_pathToItem.clear(); + m_itemToPath.clear(); + m_listWidget->clear(); + m_resourceToItem.clear(); + m_itemToResource.clear(); + + createPaths(); + applyExpansionState(); + + if (!currentResource.isEmpty()) + q_ptr->selectResource(currentResource); + else if (!currentPath.isEmpty()) + q_ptr->selectResource(currentPath); +} + +void QtResourceViewPrivate::slotCurrentPathChanged(QTreeWidgetItem *item) +{ + if (m_ignoreGuiSignals) + return; + + m_listWidget->clear(); + m_resourceToItem.clear(); + m_itemToResource.clear(); + + if (!item) + return; + + const QString currentPath = m_itemToPath.value(item); + createResources(currentPath); +} + +void QtResourceViewPrivate::slotCurrentResourceChanged(QListWidgetItem *item) +{ + m_copyResourcePathAction->setEnabled(item); + if (m_ignoreGuiSignals) + return; + + emit q_ptr->resourceSelected(m_itemToResource.value(item)); +} + +void QtResourceViewPrivate::slotResourceActivated(QListWidgetItem *item) +{ + if (m_ignoreGuiSignals) + return; + + emit q_ptr->resourceActivated(m_itemToResource.value(item)); +} + +void QtResourceViewPrivate::createPaths() +{ + if (!m_resourceModel) + return; + + const QString root(QLatin1Char(':')); + + QMap<QString, QString> pathToParentPath; // full path to full parent path + QMap<QString, QStringList> pathToSubPaths; // full path to full sub paths + + QMap<QString, QString> contents = m_resourceModel->contents(); + QMapIterator<QString, QString> itContents(contents); + while (itContents.hasNext()) { + const QString filePath = itContents.next().key(); + const QFileInfo fi(filePath); + QString dirPath = fi.absolutePath(); + m_pathToContents[dirPath].append(fi.fileName()); + while (!pathToParentPath.contains(dirPath) && dirPath != root) { + const QFileInfo fd(dirPath); + const QString parentDirPath = fd.absolutePath(); + pathToParentPath[dirPath] = parentDirPath; + pathToSubPaths[parentDirPath].append(dirPath); + dirPath = parentDirPath; + } + } + + QQueue<QPair<QString, QTreeWidgetItem *> > pathToParentItemQueue; + pathToParentItemQueue.enqueue(qMakePair(root, static_cast<QTreeWidgetItem *>(0))); + while (!pathToParentItemQueue.isEmpty()) { + QPair<QString, QTreeWidgetItem *> pathToParentItem = pathToParentItemQueue.dequeue(); + const QString path = pathToParentItem.first; + QTreeWidgetItem *item = createPath(path, pathToParentItem.second); + QStringList subPaths = pathToSubPaths.value(path); + QStringListIterator itSubPaths(subPaths); + while (itSubPaths.hasNext()) + pathToParentItemQueue.enqueue(qMakePair(itSubPaths.next(), item)); + } +} + +QTreeWidgetItem *QtResourceViewPrivate::createPath(const QString &path, QTreeWidgetItem *parent) +{ + QTreeWidgetItem *item = 0; + if (parent) + item = new QTreeWidgetItem(parent); + else + item = new QTreeWidgetItem(m_treeWidget); + m_pathToItem[path] = item; + m_itemToPath[item] = path; + QString substPath; + if (parent) { + QFileInfo di(path); + substPath = di.fileName(); + } else { + substPath = QLatin1String("<resource root>"); + } + item->setText(0, substPath); + item->setToolTip(0, path); + return item; +} + +void QtResourceViewPrivate::createResources(const QString &path) +{ + QDir dir(path); + QStringList files = m_pathToContents.value(path); + QStringListIterator it(files); + while (it.hasNext()) { + QString file = it.next(); + QString filePath = dir.absoluteFilePath(file); + QFileInfo fi(filePath); + if (fi.isFile()) { + QListWidgetItem *item = new QListWidgetItem(fi.fileName(), m_listWidget); + const QPixmap pix = QPixmap(filePath); + if (pix.isNull()) { + item->setToolTip(filePath); + } else { + item->setIcon(QIcon(makeThumbnail(pix))); + const QSize size = pix.size(); + item->setToolTip(QtResourceView::tr("Size: %1 x %2\n%3").arg(size.width()).arg(size.height()).arg(filePath)); + } + item->setFlags(item->flags() | Qt::ItemIsDragEnabled); + item->setData(Qt::UserRole, filePath); + m_itemToResource[item] = filePath; + m_resourceToItem[filePath] = item; + } + } +} + +// -------------- QtResourceView + +QtResourceView::QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent) : + QWidget(parent), + d_ptr(new QtResourceViewPrivate(core)) +{ + d_ptr->q_ptr = this; + + d_ptr->m_editResourcesAction = new QAction(qdesigner_internal::createIconSet(QLatin1String("edit.png")), tr("Edit Resources..."), this); + d_ptr->m_toolBar->addAction(d_ptr->m_editResourcesAction); + connect(d_ptr->m_editResourcesAction, SIGNAL(triggered()), this, SLOT(slotEditResources())); + d_ptr->m_editResourcesAction->setEnabled(false); + + d_ptr->m_reloadResourcesAction = new QAction(qdesigner_internal::createIconSet(QLatin1String("reload.png")), tr("Reload"), this); + d_ptr->m_toolBar->addAction(d_ptr->m_reloadResourcesAction); + connect(d_ptr->m_reloadResourcesAction, SIGNAL(triggered()), this, SLOT(slotReloadResources())); + d_ptr->m_reloadResourcesAction->setEnabled(false); + + d_ptr->m_copyResourcePathAction = new QAction(qdesigner_internal::createIconSet(QLatin1String("editcopy.png")), tr("Copy Path"), this); + connect(d_ptr->m_copyResourcePathAction, SIGNAL(triggered()), this, SLOT(slotCopyResourcePath())); + d_ptr->m_copyResourcePathAction->setEnabled(false); + + d_ptr->m_splitter = new QSplitter; + d_ptr->m_splitter->setChildrenCollapsible(false); + d_ptr->m_splitter->addWidget(d_ptr->m_treeWidget); + d_ptr->m_splitter->addWidget(d_ptr->m_listWidget); + + QLayout *layout = new QVBoxLayout(this); + layout->setMargin(0); + layout->setSpacing(0); + layout->addWidget(d_ptr->m_toolBar); + layout->addWidget(d_ptr->m_splitter); + + d_ptr->m_treeWidget->setColumnCount(1); + d_ptr->m_treeWidget->header()->hide(); + d_ptr->m_treeWidget->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding)); + + d_ptr->m_listWidget->setViewMode(QListView::IconMode); + d_ptr->m_listWidget->setResizeMode(QListView::Adjust); + d_ptr->m_listWidget->setIconSize(QSize(48, 48)); + d_ptr->m_listWidget->setGridSize(QSize(64, 64)); + + connect(d_ptr->m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + this, SLOT(slotCurrentPathChanged(QTreeWidgetItem *))); + connect(d_ptr->m_listWidget, SIGNAL(currentItemChanged(QListWidgetItem *, QListWidgetItem *)), + this, SLOT(slotCurrentResourceChanged(QListWidgetItem *))); + connect(d_ptr->m_listWidget, SIGNAL(itemActivated(QListWidgetItem *)), + this, SLOT(slotResourceActivated(QListWidgetItem *))); + d_ptr->m_listWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(d_ptr->m_listWidget, SIGNAL(customContextMenuRequested(QPoint)), + this, SLOT(slotListWidgetContextMenuRequested(QPoint))); +} + +QtResourceView::~QtResourceView() +{ + if (!d_ptr->m_settingsKey.isEmpty()) + d_ptr->saveSettings(); + + delete d_ptr; +} + +bool QtResourceView::event(QEvent *event) +{ + if (event->type() == QEvent::Show) { + d_ptr->m_listWidget->scrollToItem(d_ptr->m_listWidget->currentItem()); + d_ptr->m_treeWidget->scrollToItem(d_ptr->m_treeWidget->currentItem()); + } + return QWidget::event(event); +} + +QtResourceModel *QtResourceView::model() const +{ + return d_ptr->m_resourceModel; +} + +QString QtResourceView::selectedResource() const +{ + QListWidgetItem *item = d_ptr->m_listWidget->currentItem(); + return d_ptr->m_itemToResource.value(item); +} + +void QtResourceView::selectResource(const QString &resource) +{ + QFileInfo fi(resource); + QDir dir = fi.absoluteDir(); + if (fi.isDir()) + dir = QDir(resource); + QString dirPath = dir.absolutePath(); + QMap<QString, QTreeWidgetItem *>::const_iterator it; + while ((it = d_ptr->m_pathToItem.find(dirPath)) == d_ptr->m_pathToItem.constEnd()) { + if (!dir.cdUp()) + break; + dirPath = dir.absolutePath(); + } + if (it != d_ptr->m_pathToItem.constEnd()) { + QTreeWidgetItem *treeItem = it.value(); + d_ptr->m_treeWidget->setCurrentItem(treeItem); + d_ptr->m_treeWidget->scrollToItem(treeItem); + // expand all up to current one is done by qt + // list widget is already propagated (currrent changed was sent by qt) + QListWidgetItem *item = d_ptr->m_resourceToItem.value(resource); + if (item) { + d_ptr->m_listWidget->setCurrentItem(item); + d_ptr->m_listWidget->scrollToItem(item); + } + } +} + +QString QtResourceView::settingsKey() const +{ + return d_ptr->m_settingsKey; +} + +void QtResourceView::setSettingsKey(const QString &key) +{ + if (d_ptr->m_settingsKey == key) + return; + + d_ptr->m_settingsKey = key; + + if (key.isEmpty()) + return; + + d_ptr->restoreSettings(); +} + +void QtResourceView::setResourceModel(QtResourceModel *model) +{ + if (d_ptr->m_resourceModel) { + disconnect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet *, bool)), + this, SLOT(slotResourceSetActivated(QtResourceSet *))); + } + + // clear here + d_ptr->m_treeWidget->clear(); + d_ptr->m_listWidget->clear(); + + d_ptr->m_resourceModel = model; + + if (!d_ptr->m_resourceModel) + return; + + connect(d_ptr->m_resourceModel, SIGNAL(resourceSetActivated(QtResourceSet *, bool)), + this, SLOT(slotResourceSetActivated(QtResourceSet *))); + + // fill new here + d_ptr->slotResourceSetActivated(d_ptr->m_resourceModel->currentResourceSet()); +} + +bool QtResourceView::isResourceEditingEnabled() const +{ + return d_ptr->m_resourceEditingEnabled; +} + +void QtResourceView::setResourceEditingEnabled(bool enable) +{ + d_ptr->m_resourceEditingEnabled = enable; + d_ptr->updateActions(); +} + +void QtResourceView::setDragEnabled(bool dragEnabled) +{ + d_ptr->m_listWidget->setDragEnabled(dragEnabled); +} + +bool QtResourceView::dragEnabled() const +{ + return d_ptr->m_listWidget->dragEnabled(); +} + +QString QtResourceView::encodeMimeData(ResourceType resourceType, const QString &path) +{ + QDomDocument doc; + QDomElement elem = doc.createElement(QLatin1String(elementResourceData)); + switch (resourceType) { + case ResourceImage: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeImage)); + break; + case ResourceStyleSheet: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeStyleSheet)); + break; + case ResourceOther: + elem.setAttribute(QLatin1String(typeAttribute), QLatin1String(typeOther)); + break; + } + elem.setAttribute(QLatin1String(fileAttribute), path); + doc.appendChild(elem); + return doc.toString(); +} + +bool QtResourceView::decodeMimeData(const QMimeData *md, ResourceType *t, QString *file) +{ + return md->hasText() ? decodeMimeData(md->text(), t, file) : false; +} + +bool QtResourceView::decodeMimeData(const QString &text, ResourceType *t, QString *file) +{ + + const QString docElementName = QLatin1String(elementResourceData); + static const QString docElementString = QLatin1Char('<') + docElementName; + + if (text.isEmpty() || text.indexOf(docElementString) == -1) + return false; + + QDomDocument doc; + if (!doc.setContent(text)) + return false; + + const QDomElement domElement = doc.documentElement(); + if (domElement.tagName() != docElementName) + return false; + + if (t) { + const QString typeAttr = QLatin1String(typeAttribute); + if (domElement.hasAttribute (typeAttr)) { + const QString typeValue = domElement.attribute(typeAttr, QLatin1String(typeOther)); + if (typeValue == QLatin1String(typeImage)) { + *t = ResourceImage; + } else { + *t = typeValue == QLatin1String(typeStyleSheet) ? ResourceStyleSheet : ResourceOther; + } + } + } + if (file) { + const QString fileAttr = QLatin1String(fileAttribute); + if (domElement.hasAttribute(fileAttr)) { + *file = domElement.attribute(fileAttr, QString()); + } else { + file->clear(); + } + } + return true; +} + +// ---------------------------- QtResourceViewDialogPrivate + +class QtResourceViewDialogPrivate +{ + QtResourceViewDialog *q_ptr; + Q_DECLARE_PUBLIC(QtResourceViewDialog) +public: + QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core); + + void slotResourceSelected(const QString &resource) { setOkButtonEnabled(!resource.isEmpty()); } + void setOkButtonEnabled(bool v) { m_box->button(QDialogButtonBox::Ok)->setEnabled(v); } + + QDesignerFormEditorInterface *m_core; + QtResourceView *m_view; + QDialogButtonBox *m_box; +}; + +QtResourceViewDialogPrivate::QtResourceViewDialogPrivate(QDesignerFormEditorInterface *core) : + q_ptr(0), + m_core(core), + m_view(new QtResourceView(core)), + m_box(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)) +{ + m_view->setSettingsKey(QLatin1String(ResourceViewDialogC)); +} + +// ------------ QtResourceViewDialog +QtResourceViewDialog::QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + d_ptr(new QtResourceViewDialogPrivate(core)) +{ + setWindowTitle(tr("Select Resource")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + d_ptr->q_ptr = this; + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(d_ptr->m_view); + layout->addWidget(d_ptr->m_box); + connect(d_ptr->m_box, SIGNAL(accepted()), this, SLOT(accept())); + connect(d_ptr->m_box, SIGNAL(rejected()), this, SLOT(reject())); + connect(d_ptr->m_view, SIGNAL(resourceActivated(QString)), this, SLOT(accept())); + connect(d_ptr->m_view, SIGNAL(resourceSelected(QString)), this, SLOT(slotResourceSelected(QString))); + d_ptr->setOkButtonEnabled(false); + d_ptr->m_view->setResourceModel(core->resourceModel()); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(ResourceViewDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + setGeometry(settings->value(QLatin1String(Geometry)).toRect()); + + settings->endGroup(); +} + +QtResourceViewDialog::~QtResourceViewDialog() +{ + QDesignerSettingsInterface *settings = d_ptr->m_core->settingsManager(); + settings->beginGroup(QLatin1String(ResourceViewDialogC)); + + settings->setValue(QLatin1String(Geometry), geometry()); + + settings->endGroup(); + + delete d_ptr; +} + +QString QtResourceViewDialog::selectedResource() const +{ + return d_ptr->m_view->selectedResource(); +} + +void QtResourceViewDialog::selectResource(const QString &path) +{ + d_ptr->m_view->selectResource(path); +} + +bool QtResourceViewDialog::isResourceEditingEnabled() const +{ + return d_ptr->m_view->isResourceEditingEnabled(); +} + +void QtResourceViewDialog::setResourceEditingEnabled(bool enable) +{ + d_ptr->m_view->setResourceEditingEnabled(enable); +} + +QT_END_NAMESPACE + +#include "moc_qtresourceview_p.cpp" diff --git a/tools/designer/src/lib/shared/qtresourceview_p.h b/tools/designer/src/lib/shared/qtresourceview_p.h new file mode 100644 index 0000000..33162c5 --- /dev/null +++ b/tools/designer/src/lib/shared/qtresourceview_p.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTRESOURCEVIEW_H +#define QTRESOURCEVIEW_H + +#include "shared_global_p.h" +#include <QtGui/QWidget> +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QtResourceModel; +class QtResourceSet; +class QDesignerFormEditorInterface; +class QMimeData; + +class QDESIGNER_SHARED_EXPORT QtResourceView : public QWidget +{ + Q_OBJECT +public: + QtResourceView(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~QtResourceView(); + + void setDragEnabled(bool dragEnabled); + bool dragEnabled() const; + + QtResourceModel *model() const; + void setResourceModel(QtResourceModel *model); + + QString selectedResource() const; + void selectResource(const QString &resource); + + QString settingsKey() const; + void setSettingsKey(const QString &key); + + bool isResourceEditingEnabled() const; + void setResourceEditingEnabled(bool enable); + + // Helpers for handling the drag originating in QtResourceView (Xml/text) + enum ResourceType { ResourceImage, ResourceStyleSheet, ResourceOther }; + static QString encodeMimeData(ResourceType resourceType, const QString &path); + + static bool decodeMimeData(const QMimeData *md, ResourceType *t = 0, QString *file = 0); + static bool decodeMimeData(const QString &text, ResourceType *t = 0, QString *file = 0); + +signals: + void resourceSelected(const QString &resource); + void resourceActivated(const QString &resource); + +protected: + bool event(QEvent *event); + +private: + + class QtResourceViewPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtResourceView) + Q_DISABLE_COPY(QtResourceView) + Q_PRIVATE_SLOT(d_func(), void slotResourceSetActivated(QtResourceSet *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentPathChanged(QTreeWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentResourceChanged(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotResourceActivated(QListWidgetItem *)) + Q_PRIVATE_SLOT(d_func(), void slotEditResources()) + Q_PRIVATE_SLOT(d_func(), void slotReloadResources()) + Q_PRIVATE_SLOT(d_func(), void slotCopyResourcePath()) + Q_PRIVATE_SLOT(d_func(), void slotListWidgetContextMenuRequested(const QPoint &pos)) +}; + +class QDESIGNER_SHARED_EXPORT QtResourceViewDialog : public QDialog +{ + Q_OBJECT +public: + QtResourceViewDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + virtual ~QtResourceViewDialog(); + + QString selectedResource() const; + void selectResource(const QString &path); + + bool isResourceEditingEnabled() const; + void setResourceEditingEnabled(bool enable); + +private: + class QtResourceViewDialogPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtResourceViewDialog) + Q_DISABLE_COPY(QtResourceViewDialog) + Q_PRIVATE_SLOT(d_func(), void slotResourceSelected(const QString &)) +}; + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/richtexteditor.cpp b/tools/designer/src/lib/shared/richtexteditor.cpp new file mode 100644 index 0000000..8aa036b --- /dev/null +++ b/tools/designer/src/lib/shared/richtexteditor.cpp @@ -0,0 +1,762 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::RichTextEditorDialog +*/ + +#include "richtexteditor_p.h" +#include "htmlhighlighter_p.h" +#include "iconselector_p.h" +#include "ui_addlinkdialog.h" +#include "abstractsettings_p.h" + +#include "iconloader_p.h" + +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtCore/QList> +#include <QtCore/QMap> +#include <QtCore/QPointer> + +#include <QtGui/QAction> +#include <QtGui/QColorDialog> +#include <QtGui/QComboBox> +#include <QtGui/QFontDatabase> +#include <QtGui/QTextCursor> +#include <QtGui/QPainter> +#include <QtGui/QIcon> +#include <QtGui/QMenu> +#include <QtGui/QMoveEvent> +#include <QtGui/QTabWidget> +#include <QtGui/QTextDocument> +#include <QtGui/QTextBlock> +#include <QtGui/QToolBar> +#include <QtGui/QToolButton> +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QPushButton> +#include <QtGui/QDialogButtonBox> + +QT_BEGIN_NAMESPACE + +static const char *RichTextDialogC = "RichTextDialog"; +static const char *Geometry = "Geometry"; + +namespace qdesigner_internal { + +class RichTextEditor : public QTextEdit +{ + Q_OBJECT +public: + RichTextEditor(QWidget *parent = 0); + void setDefaultFont(const QFont &font); + + QToolBar *createToolBar(QDesignerFormEditorInterface *core, QWidget *parent = 0); + +public slots: + void setFontBold(bool b); + void setFontPointSize(double); + void setText(const QString &text); + QString text(Qt::TextFormat format) const; + +signals: + void stateChanged(); +}; + +class AddLinkDialog : public QDialog +{ + Q_OBJECT + +public: + AddLinkDialog(RichTextEditor *editor, QWidget *parent = 0); + ~AddLinkDialog(); + + int showDialog(); + +public slots: + void accept(); + +private: + RichTextEditor *m_editor; + Ui::AddLinkDialog *m_ui; +}; + +AddLinkDialog::AddLinkDialog(RichTextEditor *editor, QWidget *parent) : + QDialog(parent), + m_ui(new Ui::AddLinkDialog) +{ + m_ui->setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_editor = editor; +} + +AddLinkDialog::~AddLinkDialog() +{ + delete m_ui; +} + +int AddLinkDialog::showDialog() +{ + // Set initial focus + const QTextCursor cursor = m_editor->textCursor(); + if (cursor.hasSelection()) { + m_ui->titleInput->setText(cursor.selectedText()); + m_ui->urlInput->setFocus(); + } else { + m_ui->titleInput->setFocus(); + } + + return exec(); +} + +void AddLinkDialog::accept() +{ + const QString title = m_ui->titleInput->text(); + const QString url = m_ui->urlInput->text(); + + if (!title.isEmpty()) { + QString html = QLatin1String("<a href=\""); + html += url; + html += QLatin1String("\">"); + html += title; + html += QLatin1String("</a>"); + + m_editor->insertHtml(html); + } + + m_ui->titleInput->clear(); + m_ui->urlInput->clear(); + + QDialog::accept(); +} + +class HtmlTextEdit : public QTextEdit +{ + Q_OBJECT + +public: + HtmlTextEdit(QWidget *parent = 0) + : QTextEdit(parent) + {} + + void contextMenuEvent(QContextMenuEvent *event); + +private slots: + void actionTriggered(QAction *action); +}; + +void HtmlTextEdit::contextMenuEvent(QContextMenuEvent *event) +{ + QMenu *menu = createStandardContextMenu(); + QMenu *htmlMenu = new QMenu(tr("Insert HTML entity"), menu); + + typedef struct { + const char *text; + const char *entity; + } Entry; + + const Entry entries[] = { + { "&& (&&)", "&" }, + { "& ", " " }, + { "&< (<)", "<" }, + { "&> (>)", ">" }, + { "&© (Copyright)", "©" }, + { "&® (Trade Mark)", "®" }, + }; + + for (int i = 0; i < 6; ++i) { + QAction *entityAction = new QAction(QLatin1String(entries[i].text), + htmlMenu); + entityAction->setData(QLatin1String(entries[i].entity)); + htmlMenu->addAction(entityAction); + } + + menu->addMenu(htmlMenu); + connect(htmlMenu, SIGNAL(triggered(QAction*)), + SLOT(actionTriggered(QAction*))); + menu->exec(event->globalPos()); + delete menu; +} + +void HtmlTextEdit::actionTriggered(QAction *action) +{ + insertPlainText(action->data().toString()); +} + +class ColorAction : public QAction +{ + Q_OBJECT + +public: + ColorAction(QObject *parent); + + const QColor& color() const { return m_color; } + void setColor(const QColor &color); + +signals: + void colorChanged(const QColor &color); + +private slots: + void chooseColor(); + +private: + QColor m_color; +}; + +ColorAction::ColorAction(QObject *parent): + QAction(parent) +{ + setText(tr("Text Color")); + setColor(Qt::black); + connect(this, SIGNAL(triggered()), this, SLOT(chooseColor())); +} + +void ColorAction::setColor(const QColor &color) +{ + if (color == m_color) + return; + m_color = color; + QPixmap pix(24, 24); + QPainter painter(&pix); + painter.setRenderHint(QPainter::Antialiasing, false); + painter.fillRect(pix.rect(), m_color); + painter.setPen(m_color.darker()); + painter.drawRect(pix.rect().adjusted(0, 0, -1, -1)); + setIcon(pix); +} + +void ColorAction::chooseColor() +{ + const QColor col = QColorDialog::getColor(m_color, 0); + if (col.isValid() && col != m_color) { + setColor(col); + emit colorChanged(m_color); + } +} + +class RichTextEditorToolBar : public QToolBar +{ + Q_OBJECT +public: + RichTextEditorToolBar(QDesignerFormEditorInterface *core, + RichTextEditor *editor, + QWidget *parent = 0); + +public slots: + void updateActions(); + +private slots: + void alignmentActionTriggered(QAction *action); + void sizeInputActivated(const QString &size); + void colorChanged(const QColor &color); + void setVAlignSuper(bool super); + void setVAlignSub(bool sub); + void insertLink(); + void insertImage(); + +private: + QAction *m_bold_action; + QAction *m_italic_action; + QAction *m_underline_action; + QAction *m_valign_sup_action; + QAction *m_valign_sub_action; + QAction *m_align_left_action; + QAction *m_align_center_action; + QAction *m_align_right_action; + QAction *m_align_justify_action; + QAction *m_link_action; + QAction *m_image_action; + ColorAction *m_color_action; + QComboBox *m_font_size_input; + + QDesignerFormEditorInterface *m_core; + QPointer<RichTextEditor> m_editor; +}; + +static QAction *createCheckableAction(const QIcon &icon, const QString &text, + QObject *receiver, const char *slot, + QObject *parent = 0) +{ + QAction *result = new QAction(parent); + result->setIcon(icon); + result->setText(text); + result->setCheckable(true); + result->setChecked(false); + if (slot) + QObject::connect(result, SIGNAL(triggered(bool)), receiver, slot); + return result; +} + +RichTextEditorToolBar::RichTextEditorToolBar(QDesignerFormEditorInterface *core, + RichTextEditor *editor, + QWidget *parent) : + QToolBar(parent), + m_link_action(new QAction(this)), + m_image_action(new QAction(this)), + m_color_action(new ColorAction(this)), + m_font_size_input(new QComboBox), + m_core(core), + m_editor(editor) +{ + // Font size combo box + m_font_size_input->setEditable(false); + const QList<int> font_sizes = QFontDatabase::standardSizes(); + foreach (int font_size, font_sizes) + m_font_size_input->addItem(QString::number(font_size)); + + connect(m_font_size_input, SIGNAL(activated(QString)), + this, SLOT(sizeInputActivated(QString))); + addWidget(m_font_size_input); + + addSeparator(); + + // Bold, italic and underline buttons + + m_bold_action = createCheckableAction( + createIconSet(QLatin1String("textbold.png")), + tr("Bold"), editor, SLOT(setFontBold(bool)), this); + m_bold_action->setShortcut(tr("CTRL+B")); + addAction(m_bold_action); + + m_italic_action = createCheckableAction( + createIconSet(QLatin1String("textitalic.png")), + tr("Italic"), editor, SLOT(setFontItalic(bool)), this); + m_italic_action->setShortcut(tr("CTRL+I")); + addAction(m_italic_action); + + m_underline_action = createCheckableAction( + createIconSet(QLatin1String("textunder.png")), + tr("Underline"), editor, SLOT(setFontUnderline(bool)), this); + m_underline_action->setShortcut(tr("CTRL+U")); + addAction(m_underline_action); + + addSeparator(); + + // Left, center, right and justified alignment buttons + + QActionGroup *alignment_group = new QActionGroup(this); + connect(alignment_group, SIGNAL(triggered(QAction*)), + SLOT(alignmentActionTriggered(QAction*))); + + m_align_left_action = createCheckableAction( + createIconSet(QLatin1String("textleft.png")), + tr("Left Align"), editor, 0, alignment_group); + addAction(m_align_left_action); + + m_align_center_action = createCheckableAction( + createIconSet(QLatin1String("textcenter.png")), + tr("Center"), editor, 0, alignment_group); + addAction(m_align_center_action); + + m_align_right_action = createCheckableAction( + createIconSet(QLatin1String("textright.png")), + tr("Right Align"), editor, 0, alignment_group); + addAction(m_align_right_action); + + m_align_justify_action = createCheckableAction( + createIconSet(QLatin1String("textjustify.png")), + tr("Justify"), editor, 0, alignment_group); + addAction(m_align_justify_action); + + addSeparator(); + + // Superscript and subscript buttons + + m_valign_sup_action = createCheckableAction( + createIconSet(QLatin1String("textsuperscript.png")), + tr("Superscript"), + this, SLOT(setVAlignSuper(bool)), this); + addAction(m_valign_sup_action); + + m_valign_sub_action = createCheckableAction( + createIconSet(QLatin1String("textsubscript.png")), + tr("Subscript"), + this, SLOT(setVAlignSub(bool)), this); + addAction(m_valign_sub_action); + + addSeparator(); + + // Insert hyperlink and image buttons + + m_link_action->setIcon(createIconSet(QLatin1String("textanchor.png"))); + m_link_action->setText(tr("Insert &Link")); + connect(m_link_action, SIGNAL(triggered()), SLOT(insertLink())); + addAction(m_link_action); + + m_image_action->setIcon(createIconSet(QLatin1String("insertimage.png"))); + m_image_action->setText(tr("Insert &Image")); + connect(m_image_action, SIGNAL(triggered()), SLOT(insertImage())); + addAction(m_image_action); + + addSeparator(); + + // Text color button + connect(m_color_action, SIGNAL(colorChanged(QColor)), + this, SLOT(colorChanged(QColor))); + addAction(m_color_action); + + connect(editor, SIGNAL(textChanged()), this, SLOT(updateActions())); + connect(editor, SIGNAL(stateChanged()), this, SLOT(updateActions())); + + updateActions(); +} + +void RichTextEditorToolBar::alignmentActionTriggered(QAction *action) +{ + Qt::Alignment new_alignment; + + if (action == m_align_left_action) { + new_alignment = Qt::AlignLeft; + } else if (action == m_align_center_action) { + new_alignment = Qt::AlignCenter; + } else if (action == m_align_right_action) { + new_alignment = Qt::AlignRight; + } else { + new_alignment = Qt::AlignJustify; + } + + m_editor->setAlignment(new_alignment); +} + +void RichTextEditorToolBar::colorChanged(const QColor &color) +{ + m_editor->setTextColor(color); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::sizeInputActivated(const QString &size) +{ + bool ok; + int i = size.toInt(&ok); + if (!ok) + return; + + m_editor->setFontPointSize(i); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::setVAlignSuper(bool super) +{ + const QTextCharFormat::VerticalAlignment align = super ? + QTextCharFormat::AlignSuperScript : QTextCharFormat::AlignNormal; + + QTextCharFormat charFormat = m_editor->currentCharFormat(); + charFormat.setVerticalAlignment(align); + m_editor->setCurrentCharFormat(charFormat); + + m_valign_sub_action->setChecked(false); +} + +void RichTextEditorToolBar::setVAlignSub(bool sub) +{ + const QTextCharFormat::VerticalAlignment align = sub ? + QTextCharFormat::AlignSubScript : QTextCharFormat::AlignNormal; + + QTextCharFormat charFormat = m_editor->currentCharFormat(); + charFormat.setVerticalAlignment(align); + m_editor->setCurrentCharFormat(charFormat); + + m_valign_sup_action->setChecked(false); +} + +void RichTextEditorToolBar::insertLink() +{ + AddLinkDialog linkDialog(m_editor, this); + linkDialog.showDialog(); + m_editor->setFocus(); +} + +void RichTextEditorToolBar::insertImage() +{ + const QString path = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), QString(), this); + if (!path.isEmpty()) + m_editor->insertHtml(QLatin1String("<img src=\"") + path + QLatin1String("\"/>")); +} + +void RichTextEditorToolBar::updateActions() +{ + if (m_editor == 0) { + setEnabled(false); + return; + } + + const Qt::Alignment alignment = m_editor->alignment(); + const QTextCursor cursor = m_editor->textCursor(); + const QTextCharFormat charFormat = cursor.charFormat(); + const QFont font = charFormat.font(); + const QTextCharFormat::VerticalAlignment valign = + charFormat.verticalAlignment(); + const bool superScript = valign == QTextCharFormat::AlignSuperScript; + const bool subScript = valign == QTextCharFormat::AlignSubScript; + + if (alignment & Qt::AlignLeft) { + m_align_left_action->setChecked(true); + } else if (alignment & Qt::AlignRight) { + m_align_right_action->setChecked(true); + } else if (alignment & Qt::AlignHCenter) { + m_align_center_action->setChecked(true); + } else { + m_align_justify_action->setChecked(true); + } + + m_bold_action->setChecked(font.bold()); + m_italic_action->setChecked(font.italic()); + m_underline_action->setChecked(font.underline()); + m_valign_sup_action->setChecked(superScript); + m_valign_sub_action->setChecked(subScript); + + const int size = font.pointSize(); + const int idx = m_font_size_input->findText(QString::number(size)); + if (idx != -1) + m_font_size_input->setCurrentIndex(idx); + + m_color_action->setColor(m_editor->textColor()); +} + +RichTextEditor::RichTextEditor(QWidget *parent) + : QTextEdit(parent) +{ + connect(this, SIGNAL(currentCharFormatChanged(QTextCharFormat)), + this, SIGNAL(stateChanged())); + connect(this, SIGNAL(cursorPositionChanged()), + this, SIGNAL(stateChanged())); +} + +QToolBar *RichTextEditor::createToolBar(QDesignerFormEditorInterface *core, QWidget *parent) +{ + return new RichTextEditorToolBar(core, this, parent); +} + +void RichTextEditor::setFontBold(bool b) +{ + if (b) + setFontWeight(QFont::Bold); + else + setFontWeight(QFont::Normal); +} + +void RichTextEditor::setFontPointSize(double d) +{ + QTextEdit::setFontPointSize(qreal(d)); +} + +void RichTextEditor::setText(const QString &text) +{ + if (Qt::mightBeRichText(text)) + setHtml(text); + else + setPlainText(text); +} + +void RichTextEditor::setDefaultFont(const QFont &font) +{ + document()->setDefaultFont(font); + if (font.pointSize() > 0) + setFontPointSize(font.pointSize()); + else + setFontPointSize(QFontInfo(font).pointSize()); + emit textChanged(); +} + +QString RichTextEditor::text(Qt::TextFormat format) const +{ + switch (format) { + case Qt::LogText: + case Qt::PlainText: + return toPlainText(); + case Qt::RichText: + return toHtml(); + case Qt::AutoText: + break; + } + const QString html = toHtml(); + const QString plain = toPlainText(); + QTextEdit tester; + tester.setPlainText(plain); + return tester.toHtml() == html ? plain : html; +} + +RichTextEditorDialog::RichTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent) : + QDialog(parent), + m_editor(new RichTextEditor()), + m_text_edit(new HtmlTextEdit), + m_tab_widget(new QTabWidget), + m_state(Clean), + m_core(core) +{ + setWindowTitle(tr("Edit text")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + m_text_edit->setAcceptRichText(false); + new HtmlHighlighter(m_text_edit); + + connect(m_editor, SIGNAL(textChanged()), this, SLOT(richTextChanged())); + connect(m_text_edit, SIGNAL(textChanged()), this, SLOT(sourceChanged())); + + // The toolbar needs to be created after the RichTextEditor + QToolBar *tool_bar = m_editor->createToolBar(core); + tool_bar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + + QWidget *rich_edit = new QWidget; + QVBoxLayout *rich_edit_layout = new QVBoxLayout(rich_edit); + rich_edit_layout->addWidget(tool_bar); + rich_edit_layout->addWidget(m_editor); + + QWidget *plain_edit = new QWidget; + QVBoxLayout *plain_edit_layout = new QVBoxLayout(plain_edit); + plain_edit_layout->addWidget(m_text_edit); + + m_tab_widget->setTabPosition(QTabWidget::South); + m_tab_widget->addTab(rich_edit, tr("Rich Text")); + m_tab_widget->addTab(plain_edit, tr("Source")); + connect(m_tab_widget, SIGNAL(currentChanged(int)), + SLOT(tabIndexChanged(int))); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); + QPushButton *ok_button = buttonBox->button(QDialogButtonBox::Ok); + ok_button->setText(tr("&OK")); + ok_button->setDefault(true); + buttonBox->button(QDialogButtonBox::Cancel)->setText(tr("&Cancel")); + connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(m_tab_widget); + layout->addWidget(buttonBox); + + m_editor->setFocus(); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(RichTextDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +RichTextEditorDialog::~RichTextEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(RichTextDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +int RichTextEditorDialog::showDialog() +{ + m_tab_widget->setCurrentIndex(0); + m_editor->selectAll(); + m_editor->setFocus(); + + return exec(); +} + +void RichTextEditorDialog::setDefaultFont(const QFont &font) +{ + m_editor->setDefaultFont(font); +} + +void RichTextEditorDialog::setText(const QString &text) +{ + m_editor->setText(text); + m_text_edit->setPlainText(text); + m_state = Clean; +} + +QString RichTextEditorDialog::text(Qt::TextFormat format) const +{ + // In autotext mode, if the user has changed the source, use that + if (format == Qt::AutoText && (m_state == Clean || m_state == SourceChanged)) + return m_text_edit->toPlainText(); + // If the plain text HTML editor is selected, first copy its contents over + // to the rich text editor so that it is converted to Qt-HTML or actual + // plain text. + if (m_tab_widget->currentIndex() == SourceIndex && m_state == SourceChanged) + m_editor->setHtml(m_text_edit->toPlainText()); + return m_editor->text(format); +} + +void RichTextEditorDialog::tabIndexChanged(int newIndex) +{ + // Anything changed, is there a need for a conversion? + if (newIndex == SourceIndex && m_state != RichTextChanged) + return; + if (newIndex == RichTextIndex && m_state != SourceChanged) + return; + const State oldState = m_state; + // Remember the cursor position, since it is invalidated by setPlainText + QTextEdit *new_edit = (newIndex == SourceIndex) ? m_text_edit : m_editor; + const int position = new_edit->textCursor().position(); + + if (newIndex == SourceIndex) + m_text_edit->setPlainText(m_editor->text(Qt::RichText)); + else + m_editor->setHtml(m_text_edit->toPlainText()); + + QTextCursor cursor = new_edit->textCursor(); + cursor.movePosition(QTextCursor::End); + if (cursor.position() > position) { + cursor.setPosition(position); + } + new_edit->setTextCursor(cursor); + m_state = oldState; // Changed is triggered by setting the text +} + +void RichTextEditorDialog::richTextChanged() +{ + m_state = RichTextChanged; +} + +void RichTextEditorDialog::sourceChanged() +{ + m_state = SourceChanged; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include "richtexteditor.moc" diff --git a/tools/designer/src/lib/shared/richtexteditor_p.h b/tools/designer/src/lib/shared/richtexteditor_p.h new file mode 100644 index 0000000..5341fbb --- /dev/null +++ b/tools/designer/src/lib/shared/richtexteditor_p.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef RICHTEXTEDITOR_H +#define RICHTEXTEDITOR_H + +#include <QtGui/QTextEdit> +#include <QtGui/QDialog> +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QTabWidget; +class QToolBar; + +class QDesignerFormEditorInterface; + +namespace qdesigner_internal { + +class RichTextEditor; + +class QDESIGNER_SHARED_EXPORT RichTextEditorDialog : public QDialog +{ + Q_OBJECT +public: + RichTextEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent = 0); + ~RichTextEditorDialog(); + + int showDialog(); + void setDefaultFont(const QFont &font); + void setText(const QString &text); + QString text(Qt::TextFormat format = Qt::AutoText) const; + +private slots: + void tabIndexChanged(int newIndex); + void richTextChanged(); + void sourceChanged(); + +private: + enum TabIndex { RichTextIndex, SourceIndex }; + enum State { Clean, RichTextChanged, SourceChanged }; + RichTextEditor *m_editor; + QTextEdit *m_text_edit; + QTabWidget *m_tab_widget; + State m_state; + QDesignerFormEditorInterface *m_core; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // RITCHTEXTEDITOR_H diff --git a/tools/designer/src/lib/shared/scriptcommand.cpp b/tools/designer/src/lib/shared/scriptcommand.cpp new file mode 100644 index 0000000..5261562 --- /dev/null +++ b/tools/designer/src/lib/shared/scriptcommand.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "scriptcommand_p.h" +#include "metadatabase_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +ScriptCommand::ScriptCommand(QDesignerFormWindowInterface *formWindow) : + QDesignerFormWindowCommand(QCoreApplication::translate("Command", "Change script"), formWindow) +{ +} + +bool ScriptCommand::init(const ObjectList &list, const QString &script) +{ + MetaDataBase *metaDataBase = qobject_cast<MetaDataBase*>(formWindow()->core()->metaDataBase()); + if (!metaDataBase) + return false; + + // Save old values + m_oldValues.clear(); + foreach (QObject *obj, list) { + const MetaDataBaseItem* item = metaDataBase->metaDataBaseItem(obj); + if (!item) + return false; + m_oldValues.push_back(ObjectScriptPair(obj, item->script())); + } + m_script = script; + return true; +} + +void ScriptCommand::redo() +{ + MetaDataBase *metaDataBase = qobject_cast<MetaDataBase*>(formWindow()->core()->metaDataBase()); + Q_ASSERT(metaDataBase); + + ObjectScriptList::const_iterator cend = m_oldValues.constEnd(); + for (ObjectScriptList::const_iterator it = m_oldValues.constBegin();it != cend; ++it ) { + if (it->first) + metaDataBase->metaDataBaseItem(it->first)->setScript(m_script); + } +} + +void ScriptCommand::undo() +{ + MetaDataBase *metaDataBase = qobject_cast<MetaDataBase*>(formWindow()->core()->metaDataBase()); + Q_ASSERT(metaDataBase); + + ObjectScriptList::const_iterator cend = m_oldValues.constEnd(); + for (ObjectScriptList::const_iterator it = m_oldValues.constBegin();it != cend; ++it ) { + if (it->first) + metaDataBase->metaDataBaseItem(it->first)->setScript(it->second); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/scriptcommand_p.h b/tools/designer/src/lib/shared/scriptcommand_p.h new file mode 100644 index 0000000..45cc588 --- /dev/null +++ b/tools/designer/src/lib/shared/scriptcommand_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QDESIGNER_SCRIPTCOMMAND_H +#define QDESIGNER_SCRIPTCOMMAND_H + +#include "qdesigner_formwindowcommand_p.h" + +#include <QtCore/QPair> +#include <QtCore/QList> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT ScriptCommand: public QDesignerFormWindowCommand +{ + ScriptCommand(const ScriptCommand &); + ScriptCommand& operator=(const ScriptCommand &); + +public: + explicit ScriptCommand(QDesignerFormWindowInterface *formWindow); + + typedef QList<QObject *> ObjectList; + bool init(const ObjectList &list, const QString &script); + + virtual void redo(); + virtual void undo(); + +private: + typedef QPair<QPointer<QObject>, QString> ObjectScriptPair; + typedef QList<ObjectScriptPair> ObjectScriptList; + ObjectScriptList m_oldValues; + QString m_script; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // QDESIGNER_SCRIPTCOMMAND_H diff --git a/tools/designer/src/lib/shared/scriptdialog.cpp b/tools/designer/src/lib/shared/scriptdialog.cpp new file mode 100644 index 0000000..616f0c9 --- /dev/null +++ b/tools/designer/src/lib/shared/scriptdialog.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::ScriptDialog +*/ + +#include "scriptdialog_p.h" +#include "qscripthighlighter_p.h" + +#include <abstractdialoggui_p.h> + +#include <QtGui/QTextEdit> +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QMessageBox> +#include <QtScript/QScriptEngine> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // ScriptDialog + ScriptDialog::ScriptDialog(QDesignerDialogGuiInterface *m_dialogGui, QWidget *parent) : + QDialog(parent), + m_dialogGui(m_dialogGui), + m_textEdit(new QTextEdit) + { + setWindowTitle(tr("Edit script")); + setModal(true); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + const QString textHelp = tr("\ +<html>Enter a Qt Script snippet to be executed while loading the form.<br>\ +The widget and its children are accessible via the \ +variables <i>widget</i> and <i>childWidgets</i>, respectively."); + m_textEdit->setToolTip(textHelp); + m_textEdit->setWhatsThis(textHelp); + m_textEdit->setMinimumSize(QSize(600, 400)); + vboxLayout->addWidget(m_textEdit); + new QScriptHighlighter(m_textEdit->document()); + // button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + connect(buttonBox , SIGNAL(accepted()), this, SLOT(slotAccept())); + vboxLayout->addWidget(buttonBox); + } + + bool ScriptDialog::editScript(QString &script) + { + m_textEdit->setText(script); + if (exec() != Accepted) + return false; + + script = trimmedScript(); + return true; + } + + void ScriptDialog::slotAccept() + { + if (checkScript()) + accept(); + } + + QString ScriptDialog::trimmedScript() const + { + // Ensure a single newline + QString rc = m_textEdit->toPlainText().trimmed(); + if (!rc.isEmpty()) + rc += QLatin1Char('\n'); + return rc; + } + + bool ScriptDialog::checkScript() + { + const QString script = trimmedScript(); + if (script.isEmpty()) + return true; + QScriptEngine scriptEngine; + if (scriptEngine.canEvaluate(script)) + return true; + m_dialogGui->message(this, QDesignerDialogGuiInterface::ScriptDialogMessage, QMessageBox::Warning, + windowTitle(), tr("Syntax error"), QMessageBox::Ok); + return false; + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/scriptdialog_p.h b/tools/designer/src/lib/shared/scriptdialog_p.h new file mode 100644 index 0000000..418cc1e --- /dev/null +++ b/tools/designer/src/lib/shared/scriptdialog_p.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SCRIPTDIALOG_H +#define SCRIPTDIALOG_H + +#include "shared_global_p.h" + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QDesignerDialogGuiInterface; + +class QTextEdit; + +namespace qdesigner_internal { + + // Dialog for showing script errors + class QDESIGNER_SHARED_EXPORT ScriptDialog : public QDialog { + Q_OBJECT + + public: + explicit ScriptDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent); + bool editScript(QString &script); + + private slots: + void slotAccept(); + + private: + QString trimmedScript() const; + bool checkScript(); + + QDesignerDialogGuiInterface *m_dialogGui; + QTextEdit *m_textEdit; + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SCRIPTDIALOG_H diff --git a/tools/designer/src/lib/shared/scripterrordialog.cpp b/tools/designer/src/lib/shared/scripterrordialog.cpp new file mode 100644 index 0000000..c4a7e07 --- /dev/null +++ b/tools/designer/src/lib/shared/scripterrordialog.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::ScriptErrorDialog +*/ + +#include "scripterrordialog_p.h" + +#include <QtGui/QTextEdit> +#include <QtGui/QTextCursor> +#include <QtGui/QVBoxLayout> +#include <QtGui/QHBoxLayout> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QPen> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +static void formatError(const QFormScriptRunner::Error &error, + QTextCursor &cursor) +{ + const QTextCharFormat oldFormat = cursor.charFormat(); + // Message + cursor.insertText(QCoreApplication::translate("ScriptErrorDialog", "An error occurred while running the scripts for \"%1\":\n").arg(error.objectName)); + + QTextCharFormat format(oldFormat); + + // verbatim listing + format.setFontFamily(QLatin1String("Courier")); + cursor.insertText(error.script, format); + + const QString newLine(QLatin1Char('\n')); + + cursor.insertText(newLine); + + // red error + format = oldFormat; + format.setTextOutline(QPen(Qt::red)); + cursor.insertText(error.errorMessage, format); + cursor.insertText(newLine); + cursor.setCharFormat (oldFormat); +} + +namespace qdesigner_internal { + + // ScriptErrorDialog + ScriptErrorDialog::ScriptErrorDialog(const Errors& errors, QWidget *parent) : + QDialog(parent), + m_textEdit(new QTextEdit) + { + setWindowTitle(tr("Script errors")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + setModal(true); + + QVBoxLayout *vboxLayout = new QVBoxLayout(this); + + m_textEdit->setReadOnly(true); + m_textEdit->setMinimumSize(QSize(600, 400)); + vboxLayout->addWidget(m_textEdit); + // button box + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); + connect(buttonBox , SIGNAL(rejected()), this, SLOT(reject())); + vboxLayout->addWidget(buttonBox); + + // Generate text + QTextCursor cursor = m_textEdit->textCursor(); + cursor.movePosition (QTextCursor::End); + foreach (const QFormScriptRunner::Error error, errors) + formatError(error, cursor); + } +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/scripterrordialog_p.h b/tools/designer/src/lib/shared/scripterrordialog_p.h new file mode 100644 index 0000000..2a8b89a --- /dev/null +++ b/tools/designer/src/lib/shared/scripterrordialog_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SCRIPTERRORDIALOG_H +#define SCRIPTERRORDIALOG_H + +#include "shared_global_p.h" +#include "formscriptrunner_p.h" + +#include <QtGui/QDialog> + +QT_BEGIN_NAMESPACE + +class QTextEdit; + +namespace qdesigner_internal { + + // Dialog for showing script errors + class QDESIGNER_SHARED_EXPORT ScriptErrorDialog : public QDialog { + Q_OBJECT + + public: + typedef QFormScriptRunner::Errors Errors; + ScriptErrorDialog(const Errors& errors, QWidget *parent); + + private: + QTextEdit *m_textEdit; + + }; +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SCRIPTERRORDIALOG_H diff --git a/tools/designer/src/lib/shared/selectsignaldialog.ui b/tools/designer/src/lib/shared/selectsignaldialog.ui new file mode 100644 index 0000000..99387dd --- /dev/null +++ b/tools/designer/src/lib/shared/selectsignaldialog.ui @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SelectSignalDialog</class> + <widget class="QDialog" name="SelectSignalDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>514</width> + <height>183</height> + </rect> + </property> + <property name="windowTitle"> + <string>Go to slot</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Select signal</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QTreeWidget" name="signalList"> + <property name="sortingEnabled"> + <bool>false</bool> + </property> + <attribute name="headerVisible"> + <bool>false</bool> + </attribute> + <column> + <property name="text"> + <string>signal</string> + </property> + </column> + <column> + <property name="text"> + <string>class</string> + </property> + </column> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>SelectSignalDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>257</x> + <y>335</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>SelectSignalDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>325</x> + <y>335</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/shared.pri b/tools/designer/src/lib/shared/shared.pri new file mode 100644 index 0000000..8ed051a --- /dev/null +++ b/tools/designer/src/lib/shared/shared.pri @@ -0,0 +1,189 @@ + +INCLUDEPATH += $$PWD +QT += script + +include($$QT_SOURCE_TREE/tools/shared/qtpropertybrowser/qtpropertybrowser.pri) +include($$QT_SOURCE_TREE/tools/shared/deviceskin/deviceskin.pri) +include($$QT_SOURCE_TREE/src/tools/rcc/rcc.pri) +include($$QT_SOURCE_TREE/tools/shared/findwidget/findwidget.pri) +include($$QT_SOURCE_TREE/tools/shared/qtgradienteditor/qtgradienteditor.pri) + +# Input +FORMS += $$PWD/addlinkdialog.ui \ + $$PWD/orderdialog.ui \ + $$PWD/newactiondialog.ui \ + $$PWD/gridpanel.ui \ + $$PWD/signalslotdialog.ui \ + $$PWD/previewconfigurationwidget.ui \ + $$PWD/qtresourceeditordialog.ui \ + $$PWD/newformwidget.ui \ + $$PWD/selectsignaldialog.ui \ + $$PWD/formlayoutrowdialog.ui \ + $$PWD/plugindialog.ui + +HEADERS += \ + $$PWD/shared_global_p.h \ + $$PWD/spacer_widget_p.h \ + $$PWD/layoutinfo_p.h \ + $$PWD/layout_p.h \ + $$PWD/connectionedit_p.h \ + $$PWD/pluginmanager_p.h \ + $$PWD/metadatabase_p.h \ + $$PWD/qdesigner_formeditorcommand_p.h \ + $$PWD/qdesigner_formwindowcommand_p.h \ + $$PWD/qdesigner_command_p.h \ + $$PWD/morphmenu_p.h \ + $$PWD/qdesigner_command2_p.h \ + $$PWD/qdesigner_formbuilder_p.h \ + $$PWD/qdesigner_taskmenu_p.h \ + $$PWD/formlayoutmenu_p.h \ + $$PWD/qdesigner_widget_p.h \ + $$PWD/qdesigner_propertysheet_p.h \ + $$PWD/qdesigner_membersheet_p.h \ + $$PWD/qdesigner_propertyeditor_p.h \ + $$PWD/qdesigner_objectinspector_p.h \ + $$PWD/qdesigner_integration_p.h \ + $$PWD/invisible_widget_p.h \ + $$PWD/qlayout_widget_p.h \ + $$PWD/sheet_delegate_p.h \ + $$PWD/qdesigner_stackedbox_p.h \ + $$PWD/qdesigner_tabwidget_p.h \ + $$PWD/qdesigner_dockwidget_p.h \ + $$PWD/qdesigner_toolbox_p.h \ + $$PWD/qdesigner_dnditem_p.h \ + $$PWD/qsimpleresource_p.h \ + $$PWD/widgetfactory_p.h \ + $$PWD/widgetdatabase_p.h \ + $$PWD/qdesigner_promotion_p.h \ + $$PWD/qdesigner_introspection_p.h \ + $$PWD/promotionmodel_p.h \ + $$PWD/qdesigner_promotiondialog_p.h \ + $$PWD/iconloader_p.h \ + $$PWD/richtexteditor_p.h \ + $$PWD/plaintexteditor_p.h \ + $$PWD/actioneditor_p.h \ + $$PWD/actionrepository_p.h \ + $$PWD/qdesigner_toolbar_p.h \ + $$PWD/qdesigner_menubar_p.h \ + $$PWD/qdesigner_menu_p.h \ + $$PWD/actionprovider_p.h \ + $$PWD/orderdialog_p.h \ + $$PWD/newactiondialog_p.h \ + $$PWD/stylesheeteditor_p.h \ + $$PWD/csshighlighter_p.h \ + $$PWD/shared_enums_p.h \ + $$PWD/textpropertyeditor_p.h \ + $$PWD/propertylineedit_p.h \ + $$PWD/promotiontaskmenu_p.h \ + $$PWD/scripterrordialog_p.h \ + $$PWD/scriptcommand_p.h \ + $$PWD/scriptdialog_p.h \ + $$PWD/qscripthighlighter_p.h \ + $$PWD/gridpanel_p.h \ + $$PWD/grid_p.h \ + $$PWD/formwindowbase_p.h \ + $$PWD/qdesigner_utils_p.h \ + $$PWD/qdesigner_widgetbox_p.h \ + $$PWD/signalslotdialog_p.h \ + $$PWD/extensionfactory_p.h \ + $$PWD/dialoggui_p.h \ + $$PWD/deviceprofile_p.h \ + $$PWD/zoomwidget_p.h \ + $$PWD/previewmanager_p.h \ + $$PWD/previewconfigurationwidget_p.h \ + $$PWD/codedialog_p.h \ + $$PWD/qtresourceeditordialog_p.h \ + $$PWD/qtresourcemodel_p.h \ + $$PWD/qtresourceview_p.h \ + $$PWD/iconselector_p.h \ + $$PWD/htmlhighlighter_p.h \ + $$PWD/qdesigner_widgetitem_p.h \ + $$PWD/qdesigner_qsettings_p.h \ + $$PWD/qdesigner_formwindowmanager_p.h \ + $$PWD/shared_settings_p.h \ + $$PWD/newformwidget_p.h \ + $$PWD/filterwidget_p.h \ + $$PWD/plugindialog_p.h + +SOURCES += \ + $$PWD/spacer_widget.cpp \ + $$PWD/layoutinfo.cpp \ + $$PWD/layout.cpp \ + $$PWD/connectionedit.cpp \ + $$PWD/pluginmanager.cpp \ + $$PWD/qdesigner_formwindowcommand.cpp \ + $$PWD/qdesigner_formeditorcommand.cpp \ + $$PWD/qdesigner_command.cpp \ + $$PWD/morphmenu.cpp \ + $$PWD/qdesigner_command2.cpp \ + $$PWD/qdesigner_propertycommand.cpp \ + $$PWD/qdesigner_formbuilder.cpp \ + $$PWD/qdesigner_taskmenu.cpp \ + $$PWD/formlayoutmenu.cpp \ + $$PWD/qdesigner_widget.cpp \ + $$PWD/qdesigner_dockwidget.cpp \ + $$PWD/qdesigner_propertysheet.cpp \ + $$PWD/qdesigner_membersheet.cpp \ + $$PWD/qdesigner_propertyeditor.cpp \ + $$PWD/qdesigner_objectinspector.cpp \ + $$PWD/qdesigner_integration.cpp \ + $$PWD/qdesigner_dnditem.cpp \ + $$PWD/qsimpleresource.cpp \ + $$PWD/invisible_widget.cpp \ + $$PWD/qlayout_widget.cpp \ + $$PWD/sheet_delegate.cpp \ + $$PWD/metadatabase.cpp \ + $$PWD/qdesigner_stackedbox.cpp \ + $$PWD/qdesigner_tabwidget.cpp \ + $$PWD/qdesigner_toolbox.cpp \ + $$PWD/widgetfactory.cpp \ + $$PWD/widgetdatabase.cpp \ + $$PWD/qdesigner_promotion.cpp \ + $$PWD/qdesigner_introspection.cpp \ + $$PWD/promotionmodel.cpp \ + $$PWD/qdesigner_promotiondialog.cpp \ + $$PWD/richtexteditor.cpp \ + $$PWD/plaintexteditor.cpp \ + $$PWD/actioneditor.cpp \ + $$PWD/actionrepository.cpp \ + $$PWD/qdesigner_toolbar.cpp \ + $$PWD/qdesigner_menubar.cpp \ + $$PWD/qdesigner_menu.cpp \ + $$PWD/orderdialog.cpp \ + $$PWD/newactiondialog.cpp \ + $$PWD/stylesheeteditor.cpp \ + $$PWD/csshighlighter.cpp \ + $$PWD/textpropertyeditor.cpp \ + $$PWD/propertylineedit.cpp \ + $$PWD/promotiontaskmenu.cpp \ + $$PWD/scripterrordialog.cpp \ + $$PWD/scriptcommand.cpp \ + $$PWD/scriptdialog.cpp \ + $$PWD/qscripthighlighter.cpp\ + $$PWD/gridpanel.cpp \ + $$PWD/grid.cpp \ + $$PWD/formwindowbase.cpp \ + $$PWD/qdesigner_utils.cpp \ + $$PWD/qdesigner_widgetbox.cpp \ + $$PWD/iconloader.cpp \ + $$PWD/signalslotdialog.cpp \ + $$PWD/dialoggui.cpp \ + $$PWD/deviceprofile.cpp \ + $$PWD/zoomwidget.cpp \ + $$PWD/previewmanager.cpp \ + $$PWD/previewconfigurationwidget.cpp \ + $$PWD/codedialog.cpp \ + $$PWD/qtresourceeditordialog.cpp \ + $$PWD/qtresourcemodel.cpp \ + $$PWD/qtresourceview.cpp \ + $$PWD/iconselector.cpp \ + $$PWD/htmlhighlighter.cpp \ + $$PWD/qdesigner_widgetitem.cpp \ + $$PWD/qdesigner_qsettings.cpp \ + $$PWD/qdesigner_formwindowmanager.cpp \ + $$PWD/shared_settings.cpp \ + $$PWD/newformwidget.cpp \ + $$PWD/filterwidget.cpp \ + $$PWD/plugindialog.cpp + +RESOURCES += $$PWD/shared.qrc diff --git a/tools/designer/src/lib/shared/shared.qrc b/tools/designer/src/lib/shared/shared.qrc new file mode 100644 index 0000000..81be807 --- /dev/null +++ b/tools/designer/src/lib/shared/shared.qrc @@ -0,0 +1,20 @@ +<RCC> + <qresource prefix="/trolltech/designer"> + <file>defaultgradients.xml</file> + <!-- Templates --> + <file>templates/forms/Dialog_with_Buttons_Bottom.ui</file> + <file>templates/forms/240x320/Dialog_with_Buttons_Bottom.ui</file> + <file>templates/forms/320x240/Dialog_with_Buttons_Bottom.ui</file> + <file>templates/forms/480x640/Dialog_with_Buttons_Bottom.ui</file> + <file>templates/forms/640x480/Dialog_with_Buttons_Bottom.ui</file> + <file>templates/forms/Dialog_with_Buttons_Right.ui</file> + <file>templates/forms/240x320/Dialog_with_Buttons_Right.ui</file> + <file>templates/forms/320x240/Dialog_with_Buttons_Right.ui</file> + <file>templates/forms/480x640/Dialog_with_Buttons_Right.ui</file> + <file>templates/forms/640x480/Dialog_with_Buttons_Right.ui</file> + <file>templates/forms/Dialog_without_Buttons.ui</file> + <file>templates/forms/Widget.ui</file> + <file>templates/forms/Main_Window.ui</file> + </qresource> +</RCC> + diff --git a/tools/designer/src/lib/shared/shared_enums_p.h b/tools/designer/src/lib/shared/shared_enums_p.h new file mode 100644 index 0000000..838b9d0 --- /dev/null +++ b/tools/designer/src/lib/shared/shared_enums_p.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHAREDENUMS_H +#define SHAREDENUMS_H + +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + // Validation mode of text property line edits + enum TextPropertyValidationMode { + // Allow for multiline editing using literal "\n". + ValidationMultiLine, + // Allow for HTML rich text including multiline editing using literal "\n". + ValidationRichText, + // Validate a stylesheet + ValidationStyleSheet, + // Single line mode, suppresses newlines + ValidationSingleLine, + // Allow only for identifier characters + ValidationObjectName, + // Allow only for identifier characters and colons + ValidationObjectNameScope, + // URL + ValidationURL + }; + + // Container types + enum ContainerType { + // A container with pages, at least one of which one must always be present (for example, QTabWidget) + PageContainer, + // Mdi type container. All pages may be deleted, no concept of page order + MdiContainer, + // Wizard container + WizardContainer + }; + + enum AuxiliaryItemDataRoles { + // item->flags while being edited + ItemFlagsShadowRole = 0x13370551 + }; + +} + +QT_END_NAMESPACE + +#endif // SHAREDENUMS_H diff --git a/tools/designer/src/lib/shared/shared_global_p.h b/tools/designer/src/lib/shared/shared_global_p.h new file mode 100644 index 0000000..277472c --- /dev/null +++ b/tools/designer/src/lib/shared/shared_global_p.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_GLOBAL_H +#define SHARED_GLOBAL_H + +#include <QtCore/qglobal.h> + +#ifdef QT_DESIGNER_STATIC +#define QDESIGNER_SHARED_EXTERN +#define QDESIGNER_SHARED_IMPORT +#else +#define QDESIGNER_SHARED_EXTERN Q_DECL_EXPORT +#define QDESIGNER_SHARED_IMPORT Q_DECL_IMPORT +#endif + +#ifndef QT_NO_SHARED_EXPORT +# ifdef QDESIGNER_SHARED_LIBRARY +# define QDESIGNER_SHARED_EXPORT QDESIGNER_SHARED_EXTERN +# else +# define QDESIGNER_SHARED_EXPORT QDESIGNER_SHARED_IMPORT +# endif +#else +# define QDESIGNER_SHARED_EXPORT +#endif + +#endif // SHARED_GLOBAL_H diff --git a/tools/designer/src/lib/shared/shared_settings.cpp b/tools/designer/src/lib/shared/shared_settings.cpp new file mode 100644 index 0000000..c97dd3b --- /dev/null +++ b/tools/designer/src/lib/shared/shared_settings.cpp @@ -0,0 +1,321 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "shared_settings_p.h" +#include "grid_p.h" +#include "previewmanager_p.h" +#include "qdesigner_utils_p.h" +#include <QtDesigner/abstractformeditor.h> +#include <QtDesigner/private/abstractsettings_p.h> +#include <QtCore/QStringList> +#include <QtCore/QDir> +#include <QtCore/QVariantMap> +#include <QtCore/QCoreApplication> +#include <QtCore/QSize> + +QT_BEGIN_NAMESPACE + +static const char *designerPath = "/.designer"; +static const char *defaultGridKey = "defaultGrid"; +static const char *previewKey = "Preview"; +static const char *enabledKey = "Enabled"; +static const char *userDeviceSkinsKey= "UserDeviceSkins"; +static const char *zoomKey = "zoom"; +static const char *zoomEnabledKey = "zoomEnabled"; +static const char *deviceProfileIndexKey = "DeviceProfileIndex"; +static const char *deviceProfilesKey = "DeviceProfiles"; +static const char *formTemplatePathsKey = "FormTemplatePaths"; +static const char *formTemplateKey = "FormTemplate"; +static const char *newFormSizeKey = "NewFormSize"; + +using namespace qdesigner_internal; + +static bool checkTemplatePath(const QString &path, bool create) +{ + QDir current(QDir::current()); + if (current.exists(path)) + return true; + + if (!create) + return false; + + if (current.mkpath(path)) + return true; + + qdesigner_internal::designerWarning(QCoreApplication::translate("QDesignerSharedSettings", "The template path %1 could not be created.").arg(path)); + return false; +} + +namespace qdesigner_internal { + +QDesignerSharedSettings::QDesignerSharedSettings(QDesignerFormEditorInterface *core) + : m_settings(core->settingsManager()) +{ +} + +Grid QDesignerSharedSettings::defaultGrid() const +{ + Grid grid; + const QVariantMap defaultGridMap + = m_settings->value(QLatin1String(defaultGridKey), QVariantMap()).toMap(); + if (!defaultGridMap.empty()) + grid.fromVariantMap(defaultGridMap); + return grid; +} + +void QDesignerSharedSettings::setDefaultGrid(const Grid &grid) +{ + m_settings->setValue(QLatin1String(defaultGridKey), grid.toVariantMap()); +} + +const QStringList &QDesignerSharedSettings::defaultFormTemplatePaths() +{ + static QStringList rc; + if (rc.empty()) { + // Ensure default form template paths + const QString templatePath = QLatin1String("/templates"); + // home + QString path = QDir::homePath(); + path += QLatin1String(designerPath); + path += templatePath; + if (checkTemplatePath(path, true)) + rc += path; + + // designer/bin: Might be owned by root in some installations, do not force it. + path = qApp->applicationDirPath(); + path += templatePath; + if (checkTemplatePath(path, false)) + rc += path; + } + return rc; +} + +QStringList QDesignerSharedSettings::formTemplatePaths() const +{ + return m_settings->value(QLatin1String(formTemplatePathsKey), + defaultFormTemplatePaths()).toStringList(); +} + +void QDesignerSharedSettings::setFormTemplatePaths(const QStringList &paths) +{ + m_settings->setValue(QLatin1String(formTemplatePathsKey), paths); +} + +QString QDesignerSharedSettings::formTemplate() const +{ + return m_settings->value(QLatin1String(formTemplateKey)).toString(); +} + +void QDesignerSharedSettings::setFormTemplate(const QString &t) +{ + m_settings->setValue(QLatin1String(formTemplateKey), t); +} + +void QDesignerSharedSettings::setAdditionalFormTemplatePaths(const QStringList &additionalPaths) +{ + // merge template paths + QStringList templatePaths = defaultFormTemplatePaths(); + templatePaths += additionalPaths; + setFormTemplatePaths(templatePaths); +} + +QStringList QDesignerSharedSettings::additionalFormTemplatePaths() const +{ + // get template paths excluding internal ones + QStringList rc = formTemplatePaths(); + foreach (QString internalTemplatePath, defaultFormTemplatePaths()) { + const int index = rc.indexOf(internalTemplatePath); + if (index != -1) + rc.removeAt(index); + } + return rc; +} + +QSize QDesignerSharedSettings::newFormSize() const +{ + return m_settings->value(QLatin1String(newFormSizeKey), QSize(0, 0)).toSize(); +} + +void QDesignerSharedSettings::setNewFormSize(const QSize &s) +{ + if (s.isNull()) { + m_settings->remove(QLatin1String(newFormSizeKey)); + } else { + m_settings->setValue(QLatin1String(newFormSizeKey), s); + } +} + + +PreviewConfiguration QDesignerSharedSettings::customPreviewConfiguration() const +{ + PreviewConfiguration configuration; + configuration.fromSettings(QLatin1String(previewKey), m_settings); + return configuration; +} + +void QDesignerSharedSettings::setCustomPreviewConfiguration(const PreviewConfiguration &configuration) +{ + configuration.toSettings(QLatin1String(previewKey), m_settings); +} + +bool QDesignerSharedSettings::isCustomPreviewConfigurationEnabled() const +{ + m_settings->beginGroup(QLatin1String(previewKey)); + bool isEnabled = m_settings->value(QLatin1String(enabledKey), false).toBool(); + m_settings->endGroup(); + return isEnabled; +} + +void QDesignerSharedSettings::setCustomPreviewConfigurationEnabled(bool enabled) +{ + m_settings->beginGroup(QLatin1String(previewKey)); + m_settings->setValue(QLatin1String(enabledKey), enabled); + m_settings->endGroup(); +} + +QStringList QDesignerSharedSettings::userDeviceSkins() const +{ + m_settings->beginGroup(QLatin1String(previewKey)); + QStringList userDeviceSkins + = m_settings->value(QLatin1String(userDeviceSkinsKey), QStringList()).toStringList(); + m_settings->endGroup(); + return userDeviceSkins; +} + +void QDesignerSharedSettings::setUserDeviceSkins(const QStringList &userDeviceSkins) +{ + m_settings->beginGroup(QLatin1String(previewKey)); + m_settings->setValue(QLatin1String(userDeviceSkinsKey), userDeviceSkins); + m_settings->endGroup(); +} + +int QDesignerSharedSettings::zoom() const +{ + return m_settings->value(QLatin1String(zoomKey), 100).toInt(); +} + +void QDesignerSharedSettings::setZoom(int z) +{ + m_settings->setValue(QLatin1String(zoomKey), QVariant(z)); +} + +bool QDesignerSharedSettings::zoomEnabled() const +{ + return m_settings->value(QLatin1String(zoomEnabledKey), false).toBool(); +} + +void QDesignerSharedSettings::setZoomEnabled(bool v) +{ + m_settings->setValue(QLatin1String(zoomEnabledKey), v); +} + +DeviceProfile QDesignerSharedSettings::currentDeviceProfile() const +{ + return deviceProfileAt(currentDeviceProfileIndex()); +} + +void QDesignerSharedSettings::setCurrentDeviceProfileIndex(int i) +{ + m_settings->setValue(QLatin1String(deviceProfileIndexKey), i); +} + +int QDesignerSharedSettings::currentDeviceProfileIndex() const +{ + return m_settings->value(QLatin1String(deviceProfileIndexKey), -1).toInt(); +} + +static inline QString msgWarnDeviceProfileXml(const QString &msg) +{ + return QCoreApplication::translate("QDesignerSharedSettings", "An error has been encountered while parsing device profile XML: %1").arg(msg); +} + +DeviceProfile QDesignerSharedSettings::deviceProfileAt(int idx) const +{ + DeviceProfile rc; + if (idx < 0) + return rc; + const QStringList xmls = deviceProfileXml(); + if (idx >= xmls.size()) + return rc; + QString errorMessage; + if (!rc.fromXml(xmls.at(idx), &errorMessage)) { + rc.clear(); + designerWarning(msgWarnDeviceProfileXml(errorMessage)); + } + return rc; +} + +QStringList QDesignerSharedSettings::deviceProfileXml() const +{ + return m_settings->value(QLatin1String(deviceProfilesKey), QStringList()).toStringList(); +} + +QDesignerSharedSettings::DeviceProfileList QDesignerSharedSettings::deviceProfiles() const +{ + DeviceProfileList rc; + const QStringList xmls = deviceProfileXml(); + if (xmls.empty()) + return rc; + // De-serialize + QString errorMessage; + DeviceProfile dp; + const QStringList::const_iterator scend = xmls.constEnd(); + for (QStringList::const_iterator it = xmls.constBegin(); it != scend; ++it) { + if (dp.fromXml(*it, &errorMessage)) { + rc.push_back(dp); + } else { + designerWarning(msgWarnDeviceProfileXml(errorMessage)); + } + } + return rc; +} + +void QDesignerSharedSettings::setDeviceProfiles(const DeviceProfileList &dp) +{ + QStringList l; + const DeviceProfileList::const_iterator dcend = dp.constEnd(); + for (DeviceProfileList::const_iterator it = dp.constBegin(); it != dcend; ++it) + l.push_back(it->toXml()); + m_settings->setValue(QLatin1String(deviceProfilesKey), l); +} +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/shared_settings_p.h b/tools/designer/src/lib/shared/shared_settings_p.h new file mode 100644 index 0000000..c021b00 --- /dev/null +++ b/tools/designer/src/lib/shared/shared_settings_p.h @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef SHARED_SETTINGS_H +#define SHARED_SETTINGS_H + +#include "shared_global_p.h" +#include "deviceprofile_p.h" + +#include <QtCore/qglobal.h> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerSettingsInterface; + +class QStringList; +class QSize; + +namespace qdesigner_internal { +class Grid; +class PreviewConfiguration; +} + +/*! + Auxiliary methods to store/retrieve settings + */ +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT QDesignerSharedSettings { +public: + typedef QList<DeviceProfile> DeviceProfileList; + + explicit QDesignerSharedSettings(QDesignerFormEditorInterface *core); + + Grid defaultGrid() const; + void setDefaultGrid(const Grid &grid); + + QStringList formTemplatePaths() const; + void setFormTemplatePaths(const QStringList &paths); + + void setAdditionalFormTemplatePaths(const QStringList &additionalPaths); + QStringList additionalFormTemplatePaths() const; + + QString formTemplate() const; + void setFormTemplate(const QString &t); + + QSize newFormSize() const; + void setNewFormSize(const QSize &s); + + // Check with isCustomPreviewConfigurationEnabled if custom or default + // configuration should be used. + PreviewConfiguration customPreviewConfiguration() const; + void setCustomPreviewConfiguration(const PreviewConfiguration &configuration); + + bool isCustomPreviewConfigurationEnabled() const; + void setCustomPreviewConfigurationEnabled(bool enabled); + + QStringList userDeviceSkins() const; + void setUserDeviceSkins(const QStringList &userDeviceSkins); + + bool zoomEnabled() const; + void setZoomEnabled(bool v); + + // Zoom in percent + int zoom() const; + void setZoom(int z); + + // Embedded Design + DeviceProfile currentDeviceProfile() const; + void setCurrentDeviceProfileIndex(int i); + int currentDeviceProfileIndex() const; + + DeviceProfile deviceProfileAt(int idx) const; + DeviceProfileList deviceProfiles() const; + void setDeviceProfiles(const DeviceProfileList &dp); + + static const QStringList &defaultFormTemplatePaths(); + +protected: + QDesignerSettingsInterface *settings() const { return m_settings; } + +private: + QStringList deviceProfileXml() const; + QDesignerSettingsInterface *m_settings; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHARED_SETTINGS_H diff --git a/tools/designer/src/lib/shared/sheet_delegate.cpp b/tools/designer/src/lib/shared/sheet_delegate.cpp new file mode 100644 index 0000000..c0722bb --- /dev/null +++ b/tools/designer/src/lib/shared/sheet_delegate.cpp @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "sheet_delegate_p.h" + +#include <QtCore/QAbstractItemModel> +#include <QtGui/QTreeView> +#include <QtGui/QStyle> +#include <QtGui/QPainter> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + +SheetDelegate::SheetDelegate(QTreeView *view, QWidget *parent) + : QItemDelegate(parent), + m_view(view) +{ +} + +void SheetDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + const QAbstractItemModel *model = index.model(); + Q_ASSERT(model); + + if (!model->parent(index).isValid()) { + // this is a top-level item. + QStyleOptionButton buttonOption; + + buttonOption.state = option.state; +#ifdef Q_WS_MAC + buttonOption.state |= QStyle::State_Raised; +#endif + buttonOption.state &= ~QStyle::State_HasFocus; + + buttonOption.rect = option.rect; + buttonOption.palette = option.palette; + buttonOption.features = QStyleOptionButton::None; + m_view->style()->drawControl(QStyle::CE_PushButton, &buttonOption, painter, m_view); + + QStyleOption branchOption; + static const int i = 9; // ### hardcoded in qcommonstyle.cpp + QRect r = option.rect; + branchOption.rect = QRect(r.left() + i/2, r.top() + (r.height() - i)/2, i, i); + branchOption.palette = option.palette; + branchOption.state = QStyle::State_Children; + + if (m_view->isExpanded(index)) + branchOption.state |= QStyle::State_Open; + + m_view->style()->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, painter, m_view); + + // draw text + QRect textrect = QRect(r.left() + i*2, r.top(), r.width() - ((5*i)/2), r.height()); + QString text = elidedText(option.fontMetrics, textrect.width(), Qt::ElideMiddle, + model->data(index, Qt::DisplayRole).toString()); + m_view->style()->drawItemText(painter, textrect, Qt::AlignCenter, + option.palette, m_view->isEnabled(), text); + + } else { + QItemDelegate::paint(painter, option, index); + } +} + +QSize SheetDelegate::sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const +{ + QStyleOptionViewItem option = opt; + QSize sz = QItemDelegate::sizeHint(opt, index) + QSize(2, 2); + return sz; +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/sheet_delegate_p.h b/tools/designer/src/lib/shared/sheet_delegate_p.h new file mode 100644 index 0000000..a3d70df --- /dev/null +++ b/tools/designer/src/lib/shared/sheet_delegate_p.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef SHEET_DELEGATE_H +#define SHEET_DELEGATE_H + +#include "shared_global_p.h" + +#include <QtGui/QItemDelegate> +#include <QtGui/QTreeView> + +QT_BEGIN_NAMESPACE + +class QTreeView; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT SheetDelegate: public QItemDelegate +{ + Q_OBJECT +public: + SheetDelegate(QTreeView *view, QWidget *parent); + + virtual void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + virtual QSize sizeHint(const QStyleOptionViewItem &opt, const QModelIndex &index) const; + +private: + QTreeView *m_view; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // SHEET_DELEGATE_H diff --git a/tools/designer/src/lib/shared/signalslotdialog.cpp b/tools/designer/src/lib/shared/signalslotdialog.cpp new file mode 100644 index 0000000..a23d228 --- /dev/null +++ b/tools/designer/src/lib/shared/signalslotdialog.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "signalslotdialog_p.h" +#include "ui_signalslotdialog.h" +#include "metadatabase_p.h" +#include "widgetdatabase_p.h" + +#include "qdesigner_formwindowcommand_p.h" +#include "iconloader_p.h" + +#include <QtDesigner/QDesignerMemberSheetExtension> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerWidgetFactoryInterface> +#include <abstractdialoggui_p.h> + +#include <QtGui/QStandardItemModel> +#include <QtGui/QRegExpValidator> +#include <QtGui/QItemDelegate> +#include <QtGui/QLineEdit> +#include <QtGui/QApplication> +#include <QtGui/QMessageBox> + +#include <QtCore/QRegExp> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +// Regexp to match a function signature, arguments potentially +// with namespace colons. +static const char *signatureRegExp = "^[\\w+_]+\\(([\\w+:]\\*?,?)*\\)$"; +static const char *methodNameRegExp = "^[\\w+_]+$"; + +static QStandardItem *createEditableItem(const QString &text) +{ + QStandardItem *rc = new QStandardItem(text); + rc->setFlags(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable); + return rc; +} + +static QStandardItem *createDisabledItem(const QString &text) +{ + QStandardItem *rc = new QStandardItem(text); + Qt::ItemFlags flags = rc->flags(); + rc->setFlags(flags & ~(Qt::ItemIsEnabled|Qt::ItemIsEditable|Qt::ItemIsSelectable)); + return rc; +} + +static void fakeMethodsFromMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, QStringList &slotList, QStringList &signalList) +{ + slotList.clear(); + signalList.clear(); + if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast<qdesigner_internal::MetaDataBase *>(core->metaDataBase())) + if (const qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o)) { + slotList = item->fakeSlots(); + signalList = item->fakeSignals(); + } +} + +static void fakeMethodsToMetaDataBase(QDesignerFormEditorInterface *core, QObject *o, const QStringList &slotList, const QStringList &signalList) +{ + if (qdesigner_internal::MetaDataBase *metaDataBase = qobject_cast<qdesigner_internal::MetaDataBase *>(core->metaDataBase())) { + qdesigner_internal::MetaDataBaseItem *item = metaDataBase->metaDataBaseItem(o); + Q_ASSERT(item); + item->setFakeSlots(slotList); + item->setFakeSignals(signalList); + } +} + +static void existingMethodsFromMemberSheet(QDesignerFormEditorInterface *core, + QObject *o, + QStringList &slotList, QStringList &signalList) +{ + slotList.clear(); + signalList.clear(); + + QDesignerMemberSheetExtension *msheet = qt_extension<QDesignerMemberSheetExtension*>(core->extensionManager(), o); + if (!msheet) + return; + + for (int i = 0, count = msheet->count(); i < count; ++i) + if (msheet->isVisible(i)) { + if (msheet->isSlot(i)) + slotList += msheet->signature(i); + else + if (msheet->isSignal(i)) + signalList += msheet->signature(i); + } +} + +namespace { + // Internal helper class: A Delegate that validates using RegExps and additionally checks + // on closing (adds missing parentheses). + class SignatureDelegate : public QItemDelegate { + public: + SignatureDelegate(QObject * parent = 0); + virtual QWidget * createEditor (QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const; + virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + + private: + const QRegExp m_signatureRegexp; + const QRegExp m_methodNameRegexp; + }; + + SignatureDelegate::SignatureDelegate(QObject * parent) : + QItemDelegate(parent), + m_signatureRegexp(QLatin1String(signatureRegExp)), + m_methodNameRegexp(QLatin1String(methodNameRegExp)) + { + Q_ASSERT(m_signatureRegexp.isValid()); + Q_ASSERT(m_methodNameRegexp.isValid()); + } + + QWidget * SignatureDelegate::createEditor ( QWidget * parent, const QStyleOptionViewItem &option, const QModelIndex &index ) const + { + QWidget *rc = QItemDelegate::createEditor(parent, option, index); + QLineEdit *le = qobject_cast<QLineEdit *>(rc); + Q_ASSERT(le); + le->setValidator(new QRegExpValidator(m_signatureRegexp, le)); + return rc; + } + + void SignatureDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index ) const + { + QLineEdit *le = qobject_cast<QLineEdit *>(editor); + Q_ASSERT(le); + // Did the user just type a name? .. Add parentheses + QString signature = le->text(); + if (!m_signatureRegexp.exactMatch(signature )) { + if (m_methodNameRegexp.exactMatch(signature )) { + signature += QLatin1String("()"); + le->setText(signature); + } else { + return; + } + } + QItemDelegate::setModelData(editor, model, index); + } + + // ------ FakeMethodMetaDBCommand: Undo Command to change fake methods in the meta DB. + class FakeMethodMetaDBCommand : public qdesigner_internal::QDesignerFormWindowCommand { + + public: + explicit FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow); + + void init(QObject *o, + const QStringList &oldFakeSlots, const QStringList &oldFakeSignals, + const QStringList &newFakeSlots, const QStringList &newFakeSignals); + + virtual void undo() { fakeMethodsToMetaDataBase(core(), m_object, m_oldFakeSlots, m_oldFakeSignals); } + virtual void redo() { fakeMethodsToMetaDataBase(core(), m_object, m_newFakeSlots, m_newFakeSignals); } + + private: + QObject *m_object; + QStringList m_oldFakeSlots; + QStringList m_oldFakeSignals; + QStringList m_newFakeSlots; + QStringList m_newFakeSignals; + }; + + FakeMethodMetaDBCommand::FakeMethodMetaDBCommand(QDesignerFormWindowInterface *formWindow) : + qdesigner_internal::QDesignerFormWindowCommand(QApplication::translate("Command", "Change signals/slots"), formWindow), + m_object(0) + { + } + + void FakeMethodMetaDBCommand::init(QObject *o, + const QStringList &oldFakeSlots, const QStringList &oldFakeSignals, + const QStringList &newFakeSlots, const QStringList &newFakeSignals) + { + m_object = o; + m_oldFakeSlots = oldFakeSlots; + m_oldFakeSignals = oldFakeSignals; + m_newFakeSlots = newFakeSlots; + m_newFakeSignals = newFakeSignals; + } +} + +namespace qdesigner_internal { + +// ------ SignalSlotDialogData +void SignalSlotDialogData::clear() +{ + m_existingMethods.clear(); + m_fakeMethods.clear(); +} + +// ------ SignatureModel +SignatureModel::SignatureModel(QObject *parent) : + QStandardItemModel(parent) +{ +} + +bool SignatureModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (role != Qt::EditRole) + return QStandardItemModel::setData(index, value, role); + // check via signal (unless it is the same), in which case we can't be bothered. + const QStandardItem *item = itemFromIndex(index); + Q_ASSERT(item); + const QString signature = value.toString(); + if (item->text() == signature) + return true; + + bool ok = true; + emit checkSignature(signature, &ok); + if (!ok) + return false; + + return QStandardItemModel::setData(index, value, role); +} + +// ------ SignaturePanel +SignaturePanel::SignaturePanel(QObject *parent, QListView *listView, QToolButton *addButton, QToolButton *removeButton, const QString &newPrefix) : + QObject(parent), + m_newPrefix(newPrefix), + m_model(new SignatureModel(this)), + m_listView(listView), + m_removeButton(removeButton) +{ + m_removeButton->setEnabled(false); + + connect(addButton, SIGNAL(clicked()), this, SLOT(slotAdd())); + connect(m_removeButton, SIGNAL(clicked()), this, SLOT(slotRemove())); + + m_listView->setModel(m_model); + SignatureDelegate *delegate = new SignatureDelegate(this); + m_listView->setItemDelegate(delegate); + connect(m_model, SIGNAL(checkSignature(QString,bool*)), this, SIGNAL(checkSignature(QString,bool*))); + + connect(m_listView->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(slotSelectionChanged(QItemSelection, QItemSelection))); +} + +void SignaturePanel::slotAdd() +{ + m_listView->selectionModel()->clearSelection(); + // find unique name + for (int i = 1; ; i++) { + QString newSlot = m_newPrefix; + newSlot += QString::number(i); // Always add number, Avoid setting 'slot' for first entry + newSlot += QLatin1Char('('); + // check for function name independent of parameters + if (m_model->findItems(newSlot, Qt::MatchStartsWith, 0).empty()) { + newSlot += QLatin1Char(')'); + QStandardItem * item = createEditableItem(newSlot); + m_model->appendRow(item); + const QModelIndex index = m_model->indexFromItem (item); + m_listView->setCurrentIndex (index); + m_listView->edit(index); + return; + } + } +} + +int SignaturePanel::count(const QString &signature) const +{ + return m_model->findItems(signature).size(); +} + +void SignaturePanel::slotRemove() +{ + const QModelIndexList selectedIndexes = m_listView->selectionModel()->selectedIndexes (); + if (selectedIndexes.empty()) + return; + + closeEditor(); + // scroll to previous + if (const int row = selectedIndexes.front().row()) + m_listView->setCurrentIndex (selectedIndexes.front().sibling(row - 1, 0)); + + for (int i = selectedIndexes.size() - 1; i >= 0; i--) + qDeleteAll(m_model->takeRow(selectedIndexes[i].row())); +} + +void SignaturePanel::slotSelectionChanged(const QItemSelection &selected, const QItemSelection &) +{ + m_removeButton->setEnabled(!selected.indexes().empty()); +} + +void SignaturePanel::setData(const SignalSlotDialogData &d) +{ + m_model->clear(); + + QStandardItem *lastExisting = 0; + foreach(const QString &s, d.m_existingMethods) { + lastExisting = createDisabledItem(s); + m_model->appendRow(lastExisting); + } + foreach(const QString &s, d.m_fakeMethods) + m_model->appendRow(createEditableItem(s)); + if (lastExisting) + m_listView->scrollTo(m_model->indexFromItem(lastExisting)); +} + +QStringList SignaturePanel::fakeMethods() const +{ + QStringList rc; + if (const int rowCount = m_model->rowCount()) + for (int i = 0; i < rowCount; i++) { + const QStandardItem *item = m_model->item(i); + if (item->flags() & Qt::ItemIsEditable) + rc += item->text(); + } + return rc; +} + +void SignaturePanel::closeEditor() +{ + const QModelIndex idx = m_listView->currentIndex(); + if (idx.isValid()) + m_listView->closePersistentEditor(idx); +} + +// ------ SignalSlotDialog + +SignalSlotDialog::SignalSlotDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent, FocusMode mode) : + QDialog(parent), + m_focusMode(mode), + m_ui(new Ui::SignalSlotDialogClass), + m_dialogGui(dialogGui) +{ + setModal(true); + m_ui->setupUi(this); + + const QIcon plusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("plus.png")); + const QIcon minusIcon = qdesigner_internal::createIconSet(QString::fromUtf8("minus.png")); + m_ui->addSlotButton->setIcon(plusIcon); + m_ui->removeSlotButton->setIcon(minusIcon); + m_ui->addSignalButton->setIcon(plusIcon); + m_ui->removeSignalButton->setIcon(minusIcon); + + m_slotPanel = new SignaturePanel(this, m_ui->slotListView, m_ui->addSlotButton, m_ui->removeSlotButton, QLatin1String("slot")); + m_signalPanel = new SignaturePanel(this, m_ui->signalListView, m_ui->addSignalButton, m_ui->removeSignalButton, QLatin1String("signal")); + connect(m_slotPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*))); + connect(m_signalPanel, SIGNAL(checkSignature(QString,bool*)), this, SLOT(slotCheckSignature(QString,bool*))); + + connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + switch(m_focusMode) { + case FocusSlots: + m_ui->slotListView->setFocus(Qt::OtherFocusReason); + break; + case FocusSignals: + m_ui->signalListView->setFocus(Qt::OtherFocusReason); + break; + } +} + +SignalSlotDialog::~SignalSlotDialog() +{ + delete m_ui; +} + +void SignalSlotDialog::slotCheckSignature(const QString &signature, bool *ok) +{ + QString errorMessage; + do { + if (m_slotPanel->count(signature)) { + errorMessage = tr("There is already a slot with the signature '%1'.").arg(signature); + *ok = false; + break; + } + if (m_signalPanel->count(signature)) { + errorMessage = tr("There is already a signal with the signature '%1'.").arg(signature); + *ok = false; + break; + } + } while (false); + if (!*ok) + m_dialogGui->message(this, QDesignerDialogGuiInterface::SignalSlotDialogMessage, + QMessageBox::Warning, tr("%1 - Duplicate Signature").arg(windowTitle()), errorMessage, QMessageBox::Close); +} + +QDialog::DialogCode SignalSlotDialog::showDialog(SignalSlotDialogData &slotData, SignalSlotDialogData &signalData) +{ + m_slotPanel->setData(slotData); + m_signalPanel->setData(signalData); + + const DialogCode rc = static_cast<DialogCode>(exec()); + if (rc == Rejected) + return rc; + + slotData.m_fakeMethods = m_slotPanel->fakeMethods(); + signalData.m_fakeMethods = m_signalPanel->fakeMethods(); + return rc; +} + +bool SignalSlotDialog::editMetaDataBase(QDesignerFormWindowInterface *fw, QObject *object, QWidget *parent, FocusMode mode) +{ + QDesignerFormEditorInterface *core = fw->core(); + SignalSlotDialog dlg(core->dialogGui(), parent, mode); + dlg.setWindowTitle(tr("Signals/Slots of %1").arg(object->objectName())); + + SignalSlotDialogData slotData; + SignalSlotDialogData signalData; + + existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods); + fakeMethodsFromMetaDataBase(core, object, slotData.m_fakeMethods, signalData.m_fakeMethods); + + const QStringList oldSlots = slotData.m_fakeMethods; + const QStringList oldSignals = signalData.m_fakeMethods; + + if (dlg.showDialog(slotData, signalData) == QDialog::Rejected) + return false; + + if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods) + return false; + + FakeMethodMetaDBCommand *cmd = new FakeMethodMetaDBCommand(fw); + cmd->init(object, oldSlots, oldSignals, slotData.m_fakeMethods, signalData.m_fakeMethods); + fw->commandHistory()->push(cmd); + return true; +} + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QWidget *parent, FocusMode mode) +{ + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index == -1) + return false; + + const QString baseClassName = core->widgetDataBase()->item(index)->extends(); + if (baseClassName.isEmpty()) + return false; + + QWidget *widget = core->widgetFactory()->createWidget(baseClassName, 0); + if (!widget) + return false; + const bool rc = editPromotedClass(core, promotedClassName, widget, parent, mode); + widget->deleteLater(); + return rc; +} + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, QObject *baseObject, QWidget *parent, FocusMode mode) +{ + if (!baseObject->isWidgetType()) + return false; + + const QString promotedClassName = promotedCustomClassName(core, qobject_cast<QWidget*>(baseObject)); + if (promotedClassName.isEmpty()) + return false; + return editPromotedClass(core, promotedClassName, baseObject, parent, mode); +} + + +bool SignalSlotDialog::editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QObject *object, QWidget *parent, FocusMode mode) +{ + WidgetDataBase *db = qobject_cast<WidgetDataBase *>(core->widgetDataBase()); + if (!db) + return false; + + const int index = core->widgetDataBase()->indexOfClassName(promotedClassName); + if (index == -1) + return false; + + WidgetDataBaseItem* item = static_cast<WidgetDataBaseItem*>(db->item(index)); + + SignalSlotDialogData slotData; + SignalSlotDialogData signalData; + + existingMethodsFromMemberSheet(core, object, slotData.m_existingMethods, signalData.m_existingMethods); + slotData.m_fakeMethods = item->fakeSlots(); + signalData.m_fakeMethods = item->fakeSignals(); + + const QStringList oldSlots = slotData.m_fakeMethods; + const QStringList oldSignals = signalData.m_fakeMethods; + + SignalSlotDialog dlg(core->dialogGui(), parent, mode); + dlg.setWindowTitle(tr("Signals/Slots of %1").arg(promotedClassName)); + + if (dlg.showDialog(slotData, signalData) == QDialog::Rejected) + return false; + + if (oldSlots == slotData.m_fakeMethods && oldSignals == signalData.m_fakeMethods) + return false; + + item->setFakeSlots(slotData.m_fakeMethods); + item->setFakeSignals(signalData.m_fakeMethods); + + return true; +} + +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/signalslotdialog.ui b/tools/designer/src/lib/shared/signalslotdialog.ui new file mode 100644 index 0000000..1a8a8d9 --- /dev/null +++ b/tools/designer/src/lib/shared/signalslotdialog.ui @@ -0,0 +1,129 @@ +<ui version="4.0" > + <class>SignalSlotDialogClass</class> + <widget class="QDialog" name="SignalSlotDialogClass" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>617</width> + <height>535</height> + </rect> + </property> + <property name="windowTitle" > + <string>Signals and slots</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QGroupBox" name="slotGroupBox" > + <property name="title" > + <string>Slots</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QListView" name="slotListView" /> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QToolButton" name="addSlotButton" > + <property name="toolTip" > + <string>Add</string> + </property> + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeSlotButton" > + <property name="toolTip" > + <string>Delete</string> + </property> + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="signalGroupBox" > + <property name="title" > + <string>Signals</string> + </property> + <layout class="QVBoxLayout" > + <item> + <widget class="QListView" name="signalListView" /> + </item> + <item> + <layout class="QHBoxLayout" > + <item> + <widget class="QToolButton" name="addSignalButton" > + <property name="toolTip" > + <string>Add</string> + </property> + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="removeSignalButton" > + <property name="toolTip" > + <string>Delete</string> + </property> + <property name="text" > + <string>...</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="spacerName" stdset="0" > + <string/> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <layoutdefault spacing="6" margin="11" /> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/signalslotdialog_p.h b/tools/designer/src/lib/shared/signalslotdialog_p.h new file mode 100644 index 0000000..434b73f --- /dev/null +++ b/tools/designer/src/lib/shared/signalslotdialog_p.h @@ -0,0 +1,173 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef _SIGNALSLOTDIALOG_H +#define _SIGNALSLOTDIALOG_H + +#include "shared_global_p.h" +#include <QtCore/QStringList> +#include <QtGui/QDialog> +#include <QtGui/QStandardItemModel> + +QT_BEGIN_NAMESPACE + +class QDesignerFormEditorInterface; +class QDesignerFormWindowInterface; +class QDesignerDialogGuiInterface; +class QDesignerMemberSheet; +class QListView; +class QToolButton; +class QItemSelection; + +namespace Ui { + class SignalSlotDialogClass; +} + +namespace qdesigner_internal { + +// Dialog data +struct SignalSlotDialogData { + void clear(); + QStringList m_existingMethods; + QStringList m_fakeMethods; +}; + +// Internal helper class: A model for signatures that allows for verifying duplicates +// (checking signals versus slots and vice versa). +class SignatureModel : public QStandardItemModel { + Q_OBJECT + +public: + SignatureModel(QObject *parent = 0); + virtual bool setData (const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + +signals: + void checkSignature(const QString &signature, bool *ok); +}; + +// Internal helper class: Panel for editing method signatures. List view with validator, +// add and remove button +class SignaturePanel : public QObject { + Q_OBJECT + +public: + SignaturePanel(QObject *parent, QListView *listView, QToolButton *addButton, QToolButton *removeButton, const QString &newPrefix); + + QStringList fakeMethods() const; + void setData(const SignalSlotDialogData &d); + int count(const QString &signature) const; + +signals: + void checkSignature(const QString &signature, bool *ok); + +private slots: + void slotAdd(); + void slotRemove(); + void slotSelectionChanged(const QItemSelection &, const QItemSelection &); + +private: + void closeEditor(); + + const QString m_newPrefix; + SignatureModel *m_model; + QListView *m_listView; + QToolButton *m_removeButton; +}; + +// Dialog for editing signals and slots. +// Provides static convenience function +// to modify fake signals and slots. They are +// handled in 2 ways: +// 1) For the MainContainer: Fake signals and slots are stored +// in the meta database (per-instance) +// 2) For promoted widgets: Fake signals and slots are stored +// in the widget database (per-class) +// Arguably, we could require the MainContainer to be promoted for that, too, +// but that would require entering a header. + +class QDESIGNER_SHARED_EXPORT SignalSlotDialog : public QDialog { + Q_OBJECT + +public: + enum FocusMode { FocusSlots, FocusSignals }; + + explicit SignalSlotDialog(QDesignerDialogGuiInterface *dialogGui, QWidget *parent = 0, FocusMode m = FocusSlots); + virtual ~SignalSlotDialog(); + + DialogCode showDialog(SignalSlotDialogData &slotData, SignalSlotDialogData &signalData); + + // Edit fake methods stored in MetaDataBase (per instance, used for main containers) + static bool editMetaDataBase(QDesignerFormWindowInterface *fw, QObject *object, QWidget *parent = 0, FocusMode m = FocusSlots); + + // Edit fake methods of a promoted class stored in WidgetDataBase (synthesizes a widget to obtain existing members). + static bool editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QWidget *parent = 0, FocusMode m = FocusSlots); + // Edit fake methods of a promoted class stored in WidgetDataBase on a base class instance. + static bool editPromotedClass(QDesignerFormEditorInterface *core, QObject *baseObject, QWidget *parent = 0, FocusMode m = FocusSlots); + +private slots: + void slotCheckSignature(const QString &signature, bool *ok); + +private: + // Edit fake methods of a promoted class stored in WidgetDataBase using an instance of the base class. + static bool editPromotedClass(QDesignerFormEditorInterface *core, const QString &promotedClassName, QObject *baseObject, QWidget *parent, FocusMode m); + + const FocusMode m_focusMode; + Ui::SignalSlotDialogClass *m_ui; + QDesignerDialogGuiInterface *m_dialogGui; + SignaturePanel *m_slotPanel; + SignaturePanel *m_signalPanel; +}; +} + +QT_END_NAMESPACE + +#endif diff --git a/tools/designer/src/lib/shared/spacer_widget.cpp b/tools/designer/src/lib/shared/spacer_widget.cpp new file mode 100644 index 0000000..8256bf7 --- /dev/null +++ b/tools/designer/src/lib/shared/spacer_widget.cpp @@ -0,0 +1,280 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "spacer_widget_p.h" +#include "layoutinfo_p.h" + +#include <QtDesigner/abstractformwindow.h> +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtGui/QLayout> +#include <QtGui/QPainter> +#include <QtGui/qevent.h> +#include <QtCore/qdebug.h> + +QT_BEGIN_NAMESPACE + +// The Spacer widget is Designer representation of QLayoutItem. +// It uses QLayoutItem's sizeHint property as QWidget +// sizeHint and the QLayoutItem's sizeType property as QWidget size policy. +// If it is not within a layout, it adds a margin (m_SizeOffset) around it +// to avoid being shrunk to an invisible state when the sizeHint is reset to 0,0 +// and enables sizeHandle-resizing. In a layout, however, this m_SizeOffset +// should not be applied for pixel-exact design. + +Spacer::Spacer(QWidget *parent) : + QWidget(parent), + m_SizeOffset(3, 3), // A small offset to ensure the spacer is still visible when reset to size 0,0 + m_orientation(Qt::Vertical), + m_interactive(true), + m_layoutState(UnknownLayoutState), + m_sizeHint(0, 0) +{ + setAttribute(Qt::WA_MouseNoMask); + m_formWindow = QDesignerFormWindowInterface::findFormWindow(this); + setSizeType(QSizePolicy::Expanding); +} + +bool Spacer::event(QEvent *e) +{ + switch (e->type()) { + case QEvent::ToolTip: + updateToolTip(); // Tooltip includes size, so, refresh on demand + break; + case QEvent::ParentChange: // Cache information about 'being in layout' which is expensive to calculate. + m_layoutState = UnknownLayoutState; + break; + default: + break; + } + return QWidget::event(e); +} + +bool Spacer::isInLayout() const +{ + if (m_layoutState == UnknownLayoutState) { + m_layoutState = OutsideLayout; + if (m_formWindow) + if (const QWidget *parent = parentWidget()) + if (qdesigner_internal::LayoutInfo::managedLayoutType(m_formWindow->core(), parent) != qdesigner_internal::LayoutInfo::NoLayout) + m_layoutState = InLayout; + } + return m_layoutState == InLayout; +} + +void Spacer::paintEvent(QPaintEvent *) +{ + // Only draw spacers when we're editting widgets + if (m_formWindow != 0 && m_formWindow->currentTool() != 0) + return; + + QPainter p(this); + p.setPen(Qt::blue); + const int w = width(); + const int h = height(); + if (w * h == 0) + return; + + if (w <= m_SizeOffset.width() || h <= m_SizeOffset.height()) { + const int lw = w - 1; + const int lh = h - 1; + switch (m_orientation) { + case Qt::Horizontal: + p.drawLine(0, 0, 0, lh); + p.drawLine(lw, 0, lw, lh); + break; + case Qt::Vertical: + p.drawLine(0, 0, lw, 0); + p.drawLine(0, lh, lw, lh); + break; + } + return; + } + if (m_orientation == Qt::Horizontal) { + const int dist = 3; + const int amplitude = qMin(3, h / 3); + const int base = h / 2; + int i = 0; + p.setPen(Qt::white); + for (i = 0; i < w / 3 +2; ++i) + p.drawLine(i * dist, base - amplitude, i * dist + dist / 2, base + amplitude); + p.setPen(Qt::blue); + for (i = 0; i < w / 3 +2; ++i) + p.drawLine(i * dist + dist / 2, base + amplitude, i * dist + dist, base - amplitude); + const int y = h/2; + p.drawLine(0, y-10, 0, y+10); + p.drawLine(w - 1, y-10, w - 1, y+10); + } else { + const int dist = 3; + const int amplitude = qMin(3, w / 3); + const int base = w / 2; + int i = 0; + p.setPen(Qt::white); + for (i = 0; i < h / 3 +2; ++i) + p.drawLine(base - amplitude, i * dist, base + amplitude,i * dist + dist / 2); + p.setPen(Qt::blue); + for (i = 0; i < h / 3 +2; ++i) + p.drawLine(base + amplitude, i * dist + dist / 2, base - amplitude, i * dist + dist); + const int x = w/2; + p.drawLine(x-10, 0, x+10, 0); + p.drawLine(x-10, h - 1, x+10, h - 1); + } +} + +void Spacer::resizeEvent(QResizeEvent* e) +{ + QWidget::resizeEvent(e); + // When resized by widget handle dragging after a reset (QSize(0, 0)): + // Mark the property as changed (geometry and sizeHint are in sync except for 'changed'). + if (m_formWindow) { + const QSize oldSize = e->oldSize(); + if (oldSize.isNull() || oldSize.width() <= m_SizeOffset.width() || oldSize.height() <= m_SizeOffset.height()) + if (QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_formWindow->core()->extensionManager(), this)) + sheet->setChanged(sheet->indexOf(QLatin1String("sizeHint")), true); + } + + updateMask(); + + if (!m_interactive) + return; + + if (!isInLayout()) { // Allow size-handle resize only if not in layout + const QSize currentSize = size(); + if (currentSize.width() >= m_SizeOffset.width() && currentSize.height() >= m_SizeOffset.height()) + m_sizeHint = currentSize - m_SizeOffset; + } +} + +void Spacer::updateMask() +{ + QRegion r(rect()); + const int w = width(); + const int h = height(); + if (w > 1 && h > 1) { + if (m_orientation == Qt::Horizontal) { + const int amplitude = qMin(3, h / 3); + const int base = h / 2; + r = r.subtract(QRect(1, 0, w - 2, base - amplitude)); + r = r.subtract(QRect(1, base + amplitude, w - 2, h - base - amplitude)); + } else { + const int amplitude = qMin(3, w / 3); + const int base = w / 2; + r = r.subtract(QRect(0, 1, base - amplitude, h - 2)); + r = r.subtract(QRect(base + amplitude, 1, w - base - amplitude, h - 2)); + } + } + setMask(r); +} + +void Spacer::setSizeType(QSizePolicy::Policy t) +{ + const QSizePolicy sizeP = m_orientation == Qt::Vertical ? QSizePolicy(QSizePolicy::Minimum, t) : QSizePolicy(t, QSizePolicy::Minimum); + setSizePolicy(sizeP); +} + + +QSizePolicy::Policy Spacer::sizeType() const +{ + return m_orientation == Qt::Vertical ? sizePolicy().verticalPolicy() : sizePolicy().horizontalPolicy(); +} + +Qt::Alignment Spacer::alignment() const +{ + // For grid layouts + return m_orientation == Qt::Vertical ? Qt::AlignHCenter : Qt::AlignVCenter; +} + +QSize Spacer::sizeHint() const +{ + return isInLayout() ? m_sizeHint : m_sizeHint + m_SizeOffset; +} + +QSize Spacer::sizeHintProperty() const +{ + return m_sizeHint; +} + +void Spacer::setSizeHintProperty(const QSize &s) +{ + m_sizeHint = s; + + if (!isInLayout()) // Visible resize only if not in layout + resize(s + m_SizeOffset); + + updateGeometry(); +} + +Qt::Orientation Spacer::orientation() const +{ + return m_orientation; +} + +void Spacer::setOrientation(Qt::Orientation o) +{ + if (m_orientation == o) + return; + + const QSizePolicy::Policy st = sizeType(); // flip size type + m_orientation = o; + setSizeType(st); + + if (m_interactive) { + m_sizeHint = QSize(m_sizeHint.height(), m_sizeHint.width()); + if (!isInLayout()) + resize(m_sizeHint + m_SizeOffset); + } + + updateMask(); + update(); + updateGeometry(); +} + +void Spacer::updateToolTip() +{ + const QString format = m_orientation == Qt::Horizontal ? tr("Horizontal Spacer '%1', %2 x %3") : tr("Vertical Spacer '%1', %2 x %3"); + QString msg = format.arg(objectName()).arg(m_sizeHint.width()).arg(m_sizeHint.height()); + setToolTip(msg); +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/spacer_widget_p.h b/tools/designer/src/lib/shared/spacer_widget_p.h new file mode 100644 index 0000000..2b90813 --- /dev/null +++ b/tools/designer/src/lib/shared/spacer_widget_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef SPACER_WIDGET_H +#define SPACER_WIDGET_H + +#include "shared_global_p.h" + +#include <QtGui/QWidget> +#include <QtGui/QSizePolicy> + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; + +class QDESIGNER_SHARED_EXPORT Spacer: public QWidget +{ + Q_OBJECT + + Q_ENUMS(SizeType) + // Special hack: Make name appear as "spacer name" + Q_PROPERTY(QString spacerName READ objectName WRITE setObjectName) + Q_PROPERTY(Qt::Orientation orientation READ orientation WRITE setOrientation) + Q_PROPERTY(QSizePolicy::Policy sizeType READ sizeType WRITE setSizeType) + Q_PROPERTY(QSize sizeHint READ sizeHintProperty WRITE setSizeHintProperty DESIGNABLE true STORED true) + +public: + Spacer(QWidget *parent = 0); + + QSize sizeHint() const; + + QSize sizeHintProperty() const; + void setSizeHintProperty(const QSize &s); + + QSizePolicy::Policy sizeType() const; + void setSizeType(QSizePolicy::Policy t); + + Qt::Alignment alignment() const; + Qt::Orientation orientation() const; + + void setOrientation(Qt::Orientation o); + void setInteractiveMode(bool b) { m_interactive = b; }; + + virtual bool event(QEvent *e); + +protected: + void paintEvent(QPaintEvent *e); + void resizeEvent(QResizeEvent* e); + void updateMask(); + +private: + bool isInLayout() const; + void updateToolTip(); + + const QSize m_SizeOffset; + QDesignerFormWindowInterface *m_formWindow; + Qt::Orientation m_orientation; + bool m_interactive; + // Cache information about 'being in layout' which is expensive to calculate. + enum LayoutState { InLayout, OutsideLayout, UnknownLayoutState }; + mutable LayoutState m_layoutState; + QSize m_sizeHint; +}; + +QT_END_NAMESPACE + +#endif // SPACER_WIDGET_H diff --git a/tools/designer/src/lib/shared/stylesheeteditor.cpp b/tools/designer/src/lib/shared/stylesheeteditor.cpp new file mode 100644 index 0000000..2056fcf --- /dev/null +++ b/tools/designer/src/lib/shared/stylesheeteditor.cpp @@ -0,0 +1,415 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::StyleSheetEditorDialog +*/ + +#include "stylesheeteditor_p.h" +#include "csshighlighter_p.h" +#include "iconselector_p.h" +#include "qtgradientmanager.h" +#include "qtgradientviewdialog.h" +#include "qtgradientutils.h" +#include "qdesigner_integration_p.h" +#include "qdesigner_utils_p.h" +#include "abstractsettings_p.h" + +#include <QtDesigner/QDesignerFormWindowInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QExtensionManager> + +#include <QtCore/QSignalMapper> +#include <QtGui/QAction> +#include <QtGui/QColorDialog> +#include <QtGui/QDialogButtonBox> +#include <QtGui/QFontDialog> +#include <QtGui/QMenu> +#include <QtGui/QPushButton> +#include <QtGui/QTextDocument> +#include <QtGui/QToolBar> +#include <QtGui/QVBoxLayout> +#include "private/qcssparser_p.h" + +QT_BEGIN_NAMESPACE + +static const char *styleSheetProperty = "styleSheet"; +static const char *StyleSheetDialogC = "StyleSheetDialog"; +static const char *Geometry = "Geometry"; + +namespace qdesigner_internal { + +StyleSheetEditor::StyleSheetEditor(QWidget *parent) + : QTextEdit(parent) +{ + setTabStopWidth(fontMetrics().width(QLatin1Char(' '))*4); + new CssHighlighter(document()); +} + +// --- StyleSheetEditorDialog +StyleSheetEditorDialog::StyleSheetEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent, Mode mode): + QDialog(parent), + m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel|QDialogButtonBox::Help)), + m_editor(new StyleSheetEditor), + m_validityLabel(new QLabel(tr("Valid Style Sheet"))), + m_core(core), + m_addResourceAction(new QAction(tr("Add Resource..."), this)), + m_addGradientAction(new QAction(tr("Add Gradient..."), this)), + m_addColorAction(new QAction(tr("Add Color..."), this)), + m_addFontAction(new QAction(tr("Add Font..."), this)) +{ + setWindowTitle(tr("Edit Style Sheet")); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + connect(m_buttonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect(m_buttonBox, SIGNAL(helpRequested()), this, SLOT(slotRequestHelp())); + m_buttonBox->button(QDialogButtonBox::Help)->setShortcut(QKeySequence::HelpContents); + + connect(m_editor, SIGNAL(textChanged()), this, SLOT(validateStyleSheet())); + + QToolBar *toolBar = new QToolBar; + + QGridLayout *layout = new QGridLayout; + layout->addWidget(toolBar, 0, 0, 1, 2); + layout->addWidget(m_editor, 1, 0, 1, 2); + layout->addWidget(m_validityLabel, 2, 0, 1, 1); + layout->addWidget(m_buttonBox, 2, 1, 1, 1); + setLayout(layout); + + m_editor->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_editor, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenuRequested(const QPoint &))); + + QSignalMapper *resourceActionMapper = new QSignalMapper(this); + QSignalMapper *gradientActionMapper = new QSignalMapper(this); + QSignalMapper *colorActionMapper = new QSignalMapper(this); + + resourceActionMapper->setMapping(m_addResourceAction, QString()); + gradientActionMapper->setMapping(m_addGradientAction, QString()); + colorActionMapper->setMapping(m_addColorAction, QString()); + + connect(m_addResourceAction, SIGNAL(triggered()), resourceActionMapper, SLOT(map())); + connect(m_addGradientAction, SIGNAL(triggered()), gradientActionMapper, SLOT(map())); + connect(m_addColorAction, SIGNAL(triggered()), colorActionMapper, SLOT(map())); + connect(m_addFontAction, SIGNAL(triggered()), this, SLOT(slotAddFont())); + + m_addResourceAction->setEnabled(mode == ModePerForm); + + const char * const resourceProperties[] = { + "background-image", + "border-image", + "image", + 0 + }; + + const char * const colorProperties[] = { + "color", + "background-color", + "alternate-background-color", + "border-color", + "border-top-color", + "border-right-color", + "border-bottom-color", + "border-left-color", + "gridline-color", + "selection-color", + "selection-background-color", + 0 + }; + + QMenu *resourceActionMenu = new QMenu(this); + QMenu *gradientActionMenu = new QMenu(this); + QMenu *colorActionMenu = new QMenu(this); + + for (int resourceProperty = 0; resourceProperties[resourceProperty]; ++resourceProperty) { + QAction *action = resourceActionMenu->addAction(QLatin1String(resourceProperties[resourceProperty])); + connect(action, SIGNAL(triggered()), resourceActionMapper, SLOT(map())); + resourceActionMapper->setMapping(action, QLatin1String(resourceProperties[resourceProperty])); + } + + for (int colorProperty = 0; colorProperties[colorProperty]; ++colorProperty) { + QAction *gradientAction = gradientActionMenu->addAction(QLatin1String(colorProperties[colorProperty])); + QAction *colorAction = colorActionMenu->addAction(QLatin1String(colorProperties[colorProperty])); + connect(gradientAction, SIGNAL(triggered()), gradientActionMapper, SLOT(map())); + connect(colorAction, SIGNAL(triggered()), colorActionMapper, SLOT(map())); + gradientActionMapper->setMapping(gradientAction, QLatin1String(colorProperties[colorProperty])); + colorActionMapper->setMapping(colorAction, QLatin1String(colorProperties[colorProperty])); + } + + connect(resourceActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddResource(QString))); + connect(gradientActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddGradient(QString))); + connect(colorActionMapper, SIGNAL(mapped(QString)), this, SLOT(slotAddColor(QString))); + + m_addResourceAction->setMenu(resourceActionMenu); + m_addGradientAction->setMenu(gradientActionMenu); + m_addColorAction->setMenu(colorActionMenu); + + toolBar->addAction(m_addResourceAction); + toolBar->addAction(m_addGradientAction); + toolBar->addAction(m_addColorAction); + toolBar->addAction(m_addFontAction); + + m_editor->setFocus(); + + QDesignerSettingsInterface *settings = core->settingsManager(); + settings->beginGroup(QLatin1String(StyleSheetDialogC)); + + if (settings->contains(QLatin1String(Geometry))) + restoreGeometry(settings->value(QLatin1String(Geometry)).toByteArray()); + + settings->endGroup(); +} + +StyleSheetEditorDialog::~StyleSheetEditorDialog() +{ + QDesignerSettingsInterface *settings = m_core->settingsManager(); + settings->beginGroup(QLatin1String(StyleSheetDialogC)); + + settings->setValue(QLatin1String(Geometry), saveGeometry()); + settings->endGroup(); +} + +void StyleSheetEditorDialog::setOkButtonEnabled(bool v) +{ + m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(v); + if (QPushButton *applyButton = m_buttonBox->button(QDialogButtonBox::Apply)) + applyButton->setEnabled(v); +} + +void StyleSheetEditorDialog::slotContextMenuRequested(const QPoint &pos) +{ + QMenu *menu = m_editor->createStandardContextMenu(); + menu->addSeparator(); + menu->addAction(m_addResourceAction); + menu->addAction(m_addGradientAction); + menu->exec(mapToGlobal(pos)); + delete menu; +} + +void StyleSheetEditorDialog::slotAddResource(const QString &property) +{ + const QString path = IconSelector::choosePixmapResource(m_core, m_core->resourceModel(), QString(), this); + if (!path.isEmpty()) + insertCssProperty(property, QString(QLatin1String("url(%1)")).arg(path)); +} + +void StyleSheetEditorDialog::slotAddGradient(const QString &property) +{ + bool ok; + const QGradient grad = QtGradientViewDialog::getGradient(&ok, m_core->gradientManager(), this); + if (ok) + insertCssProperty(property, QtGradientUtils::styleSheetCode(grad)); +} + +void StyleSheetEditorDialog::slotAddColor(const QString &property) +{ + bool ok; + QRgb rgba = QColorDialog::getRgba(0xffffffff, &ok, this); + if (!ok) + return; + + QColor color; + color.setRgba(rgba); + QString colorStr; + + if (color.alpha() == 255) { + colorStr = QString(QLatin1String("rgb(%1, %2, %3)")).arg( + color.red()).arg(color.green()).arg(color.blue()); + } else { + colorStr = QString(QLatin1String("rgba(%1, %2, %3, %4)")).arg( + color.red()).arg(color.green()).arg(color.blue()).arg(color.alpha()); + } + + insertCssProperty(property, colorStr); +} + +void StyleSheetEditorDialog::slotAddFont() +{ + bool ok; + QFont font = QFontDialog::getFont(&ok, this); + if (ok) { + QString fontStr; + if (font.weight() != QFont::Normal) { + fontStr += QString::number(font.weight()); + fontStr += QLatin1Char(' '); + } + + switch (font.style()) { + case QFont::StyleItalic: + fontStr += QLatin1String("italic "); + break; + case QFont::StyleOblique: + fontStr += QLatin1String("oblique "); + break; + default: + break; + } + fontStr += QString::number(font.pointSize()); + fontStr += QLatin1String("pt \""); + fontStr += font.family(); + fontStr += QLatin1Char('"'); + + insertCssProperty(QLatin1String("font"), fontStr); + QString decoration; + if (font.underline()) + decoration += QLatin1String("underline"); + if (font.strikeOut()) { + if (!decoration.isEmpty()) + decoration += QLatin1Char(' '); + decoration += QLatin1String("line-through"); + } + insertCssProperty(QLatin1String("text-decoration"), decoration); + } +} + +void StyleSheetEditorDialog::insertCssProperty(const QString &name, const QString &value) +{ + if (!value.isEmpty()) { + QTextCursor cursor = m_editor->textCursor(); + if (!name.isEmpty()) { + cursor.beginEditBlock(); + cursor.removeSelectedText(); + cursor.movePosition(QTextCursor::EndOfLine); + + // Simple check to see if we're in a selector scope + const QTextDocument *doc = m_editor->document(); + const QTextCursor closing = doc->find(QLatin1String("}"), cursor, QTextDocument::FindBackward); + const QTextCursor opening = doc->find(QLatin1String("{"), cursor, QTextDocument::FindBackward); + const bool inSelector = !opening.isNull() && (closing.isNull() || + closing.position() < opening.position()); + QString insertion; + if (m_editor->textCursor().block().length() != 1) + insertion += QLatin1Char('\n'); + if (inSelector) + insertion += QLatin1Char('\t'); + insertion += name; + insertion += QLatin1String(": "); + insertion += value; + insertion += QLatin1Char(';'); + cursor.insertText(insertion); + cursor.endEditBlock(); + } else { + cursor.insertText(value); + } + } +} + +void StyleSheetEditorDialog::slotRequestHelp() +{ + QDesignerIntegration::requestHelp(m_core, QLatin1String("qt"), + QLatin1String("stylesheet-reference.html")); +} + +QDialogButtonBox * StyleSheetEditorDialog::buttonBox() const +{ + return m_buttonBox; +} + +QString StyleSheetEditorDialog::text() const +{ + return m_editor->toPlainText(); +} + +void StyleSheetEditorDialog::setText(const QString &t) +{ + m_editor->setText(t); +} + +bool StyleSheetEditorDialog::isStyleSheetValid(const QString &styleSheet) +{ + QCss::Parser parser(styleSheet); + QCss::StyleSheet sheet; + if (parser.parse(&sheet)) + return true; + QString fullSheet = QLatin1String("* { "); + fullSheet += styleSheet; + fullSheet += QLatin1Char('}'); + QCss::Parser parser2(fullSheet); + return parser2.parse(&sheet); +} + +void StyleSheetEditorDialog::validateStyleSheet() +{ + const bool valid = isStyleSheetValid(m_editor->toPlainText()); + setOkButtonEnabled(valid); + if (valid) { + m_validityLabel->setText(tr("Valid Style Sheet")); + m_validityLabel->setStyleSheet(QLatin1String("color: green")); + } else { + m_validityLabel->setText(tr("Invalid Style Sheet")); + m_validityLabel->setStyleSheet(QLatin1String("color: red")); + } +} + +// --- StyleSheetPropertyEditorDialog +StyleSheetPropertyEditorDialog::StyleSheetPropertyEditorDialog(QWidget *parent, + QDesignerFormWindowInterface *fw, + QWidget *widget): + StyleSheetEditorDialog(fw->core(), parent), + m_fw(fw), + m_widget(widget) +{ + Q_ASSERT(m_fw != 0); + + QPushButton *apply = buttonBox()->addButton(QDialogButtonBox::Apply); + QObject::connect(apply, SIGNAL(clicked()), this, SLOT(applyStyleSheet())); + QObject::connect(buttonBox(), SIGNAL(accepted()), this, SLOT(applyStyleSheet())); + + QDesignerPropertySheetExtension *sheet = + qt_extension<QDesignerPropertySheetExtension*>(m_fw->core()->extensionManager(), m_widget); + Q_ASSERT(sheet != 0); + const int index = sheet->indexOf(QLatin1String(styleSheetProperty)); + const PropertySheetStringValue value = qVariantValue<PropertySheetStringValue>(sheet->property(index)); + setText(value.value()); +} + +void StyleSheetPropertyEditorDialog::applyStyleSheet() +{ + const PropertySheetStringValue value(text(), false); + m_fw->cursor()->setWidgetProperty(m_widget, QLatin1String(styleSheetProperty), qVariantFromValue(value)); +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/stylesheeteditor_p.h b/tools/designer/src/lib/shared/stylesheeteditor_p.h new file mode 100644 index 0000000..c9fd682 --- /dev/null +++ b/tools/designer/src/lib/shared/stylesheeteditor_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef STYLESHEETEDITOR_H +#define STYLESHEETEDITOR_H + +#include <QtGui/QTextEdit> +#include <QtGui/QDialog> +#include <QtGui/QLabel> +#include "shared_global_p.h" + +QT_BEGIN_NAMESPACE + +class QDesignerFormWindowInterface; +class QDesignerFormEditorInterface; + +class QDialogButtonBox; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT StyleSheetEditor : public QTextEdit +{ + Q_OBJECT +public: + StyleSheetEditor(QWidget *parent = 0); +}; + +// Edit a style sheet. +class QDESIGNER_SHARED_EXPORT StyleSheetEditorDialog : public QDialog +{ + Q_OBJECT +public: + enum Mode { + ModeGlobal, // resources are disabled (we don't have current resource set loaded), used e.g. in configuration dialog context + ModePerForm // resources are available + }; + + StyleSheetEditorDialog(QDesignerFormEditorInterface *core, QWidget *parent, Mode mode = ModePerForm); + ~StyleSheetEditorDialog(); + QString text() const; + void setText(const QString &t); + + static bool isStyleSheetValid(const QString &styleSheet); + + +private slots: + void validateStyleSheet(); + void slotContextMenuRequested(const QPoint &pos); + void slotAddResource(const QString &property); + void slotAddGradient(const QString &property); + void slotAddColor(const QString &property); + void slotAddFont(); + void slotRequestHelp(); + +protected: + QDialogButtonBox *buttonBox() const; + void setOkButtonEnabled(bool v); + +private: + void insertCssProperty(const QString &name, const QString &value); + + QDialogButtonBox *m_buttonBox; + StyleSheetEditor *m_editor; + QLabel *m_validityLabel; + QDesignerFormEditorInterface *m_core; + QAction *m_addResourceAction; + QAction *m_addGradientAction; + QAction *m_addColorAction; + QAction *m_addFontAction; +}; + +// Edit the style sheet property of the designer selection. +// Provides an "Apply" button. + +class QDESIGNER_SHARED_EXPORT StyleSheetPropertyEditorDialog : public StyleSheetEditorDialog +{ + Q_OBJECT +public: + StyleSheetPropertyEditorDialog(QWidget *parent, QDesignerFormWindowInterface *fw, QWidget *widget); + + static bool isStyleSheetValid(const QString &styleSheet); + +private slots: + void applyStyleSheet(); + +private: + QDesignerFormWindowInterface *m_fw; + QWidget *m_widget; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // STYLESHEETEDITOR_H diff --git a/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui b/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..7ff64b0 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>240</width> + <height>320</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>270</y> + <width>221</width> + <height>41</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui b/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..203af78 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/240x320/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>240</width> + <height>320</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>150</x> + <y>10</y> + <width>81</width> + <height>301</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui b/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..0ac856e --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>240</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>200</y> + <width>301</width> + <height>32</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui b/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..52f0f66 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/320x240/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>320</width> + <height>240</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>230</x> + <y>10</y> + <width>81</width> + <height>221</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui b/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..0d219c9 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>640</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>600</y> + <width>461</width> + <height>32</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui b/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..c82a78e --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/480x640/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>480</width> + <height>640</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>390</x> + <y>10</y> + <width>81</width> + <height>621</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui b/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..adc5d48 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>10</x> + <y>440</y> + <width>621</width> + <height>32</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui b/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..defb42a --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/640x480/Dialog_with_Buttons_Right.ui @@ -0,0 +1,67 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>640</width> + <height>480</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>550</x> + <y>10</y> + <width>81</width> + <height>461</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui b/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui new file mode 100644 index 0000000..18d31ab --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Bottom.ui @@ -0,0 +1,71 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>30</x> + <y>240</y> + <width>341</width> + <height>32</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui b/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui new file mode 100644 index 0000000..703d594 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/Dialog_with_Buttons_Right.ui @@ -0,0 +1,71 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + <widget class="QDialogButtonBox" name="buttonBox" > + <property name="geometry" > + <rect> + <x>290</x> + <y>20</y> + <width>81</width> + <height>241</height> + </rect> + </property> + <property name="orientation" > + <enum>Qt::Vertical</enum> + </property> + <property name="standardButtons" > + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </widget> + <pixmapfunction></pixmapfunction> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>Dialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel" > + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel" > + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>Dialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel" > + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel" > + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/Dialog_without_Buttons.ui b/tools/designer/src/lib/shared/templates/forms/Dialog_without_Buttons.ui new file mode 100644 index 0000000..1be6298 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/Dialog_without_Buttons.ui @@ -0,0 +1,18 @@ +<ui version="4.0" > + <class>Dialog</class> + <widget class="QDialog" name="Dialog" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Dialog</string> + </property> + </widget> + <resources/> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/Main_Window.ui b/tools/designer/src/lib/shared/templates/forms/Main_Window.ui new file mode 100644 index 0000000..9ae3b50 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/Main_Window.ui @@ -0,0 +1,24 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>MainWindow</class> + <widget class="QMainWindow" name="MainWindow" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>800</width> + <height>600</height> + </rect> + </property> + <property name="windowTitle" > + <string>MainWindow</string> + </property> + <widget class="QMenuBar" name="menubar" /> + <widget class="QWidget" name="centralwidget" /> + <widget class="QStatusBar" name="statusbar" /> + </widget> + <pixmapfunction></pixmapfunction> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/templates/forms/Widget.ui b/tools/designer/src/lib/shared/templates/forms/Widget.ui new file mode 100644 index 0000000..4b7d6a4 --- /dev/null +++ b/tools/designer/src/lib/shared/templates/forms/Widget.ui @@ -0,0 +1,21 @@ +<ui version="4.0" > + <author></author> + <comment></comment> + <exportmacro></exportmacro> + <class>Form</class> + <widget class="QWidget" name="Form" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>400</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + </widget> + <pixmapfunction></pixmapfunction> + <connections/> +</ui> diff --git a/tools/designer/src/lib/shared/textpropertyeditor.cpp b/tools/designer/src/lib/shared/textpropertyeditor.cpp new file mode 100644 index 0000000..cf12842 --- /dev/null +++ b/tools/designer/src/lib/shared/textpropertyeditor.cpp @@ -0,0 +1,429 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "textpropertyeditor_p.h" +#include "propertylineedit_p.h" +#include "stylesheeteditor_p.h" + +#include <QtGui/QLineEdit> +#include <QtGui/QRegExpValidator> +#include <QtGui/QResizeEvent> +#include <QtGui/QCompleter> +#include <QtGui/QAbstractItemView> +#include <QtCore/QRegExp> +#include <QtCore/QUrl> +#include <QtCore/QFile> +#include <QtCore/QDebug> + +QT_BEGIN_NAMESPACE + +namespace { + const QChar NewLineChar(QLatin1Char('\n')); + const QLatin1String EscapedNewLine("\\n"); + + // A validator that replaces offending strings + class ReplacementValidator : public QValidator { + public: + ReplacementValidator (QObject * parent, + const QString &offending, + const QString &replacement); + virtual void fixup ( QString & input ) const; + virtual State validate ( QString & input, int &pos) const; + private: + const QString m_offending; + const QString m_replacement; + }; + + ReplacementValidator::ReplacementValidator (QObject * parent, + const QString &offending, + const QString &replacement) : + QValidator(parent ), + m_offending(offending), + m_replacement(replacement) + { + } + + void ReplacementValidator::fixup ( QString & input ) const { + input.replace(m_offending, m_replacement); + } + + QValidator::State ReplacementValidator::validate ( QString & input, int &/* pos */) const { + fixup (input); + return Acceptable; + } + + // A validator for style sheets. Does newline handling and validates sheets. + class StyleSheetValidator : public ReplacementValidator { + public: + StyleSheetValidator (QObject * parent); + virtual State validate(QString & input, int &pos) const; + }; + + StyleSheetValidator::StyleSheetValidator (QObject * parent) : + ReplacementValidator(parent, NewLineChar, EscapedNewLine) + { + } + + QValidator::State StyleSheetValidator::validate ( QString & input, int &pos) const + { + // base class + const State state = ReplacementValidator:: validate(input, pos); + if (state != Acceptable) + return state; + // now check style sheet, create string with newlines + const QString styleSheet = qdesigner_internal::TextPropertyEditor::editorStringToString(input, qdesigner_internal::ValidationStyleSheet); + const bool valid = qdesigner_internal::StyleSheetEditorDialog::isStyleSheetValid(styleSheet); + return valid ? Acceptable : Intermediate; + } + + // A validator for URLs based on QUrl. Enforces complete protocol + // specification with a completer (adds a trailing slash) + class UrlValidator : public QValidator { + public: + UrlValidator(QCompleter *completer, QObject *parent); + + virtual State validate(QString &input, int &pos) const; + virtual void fixup(QString &input) const; + private: + QUrl guessUrlFromString(const QString &string) const; + QCompleter *m_completer; + }; + + UrlValidator::UrlValidator(QCompleter *completer, QObject *parent) : + QValidator(parent), + m_completer(completer) + { + } + + QValidator::State UrlValidator::validate(QString &input, int &pos) const + { + Q_UNUSED(pos); + + if (input.isEmpty()) + return Acceptable; + + const QUrl url(input, QUrl::StrictMode); + + if (!url.isValid() || url.isEmpty()) + return Intermediate; + + if (url.scheme().isEmpty()) + return Intermediate; + + if (url.host().isEmpty() && url.path().isEmpty()) + return Intermediate; + + return Acceptable; + } + + void UrlValidator::fixup(QString &input) const + { + // Don't try to fixup if the user is busy selecting a completion proposal + if (const QAbstractItemView *iv = m_completer->popup()) { + if (iv->isVisible()) + return; + } + + input = guessUrlFromString(input).toString(); + } + + QUrl UrlValidator::guessUrlFromString(const QString &string) const + { + const QString urlStr = string.trimmed(); + const QRegExp qualifiedUrl(QLatin1String("^[a-zA-Z]+\\:.*")); + + // Check if it looks like a qualified URL. Try parsing it and see. + const bool hasSchema = qualifiedUrl.exactMatch(urlStr); + if (hasSchema) { + const QUrl url(urlStr, QUrl::TolerantMode); + if (url.isValid()) + return url; + } + + // Might be a Qt resource + if (string.startsWith(QLatin1String(":/"))) + return QUrl(QLatin1String("qrc") + string); + + // Might be a file. + if (QFile::exists(urlStr)) + return QUrl::fromLocalFile(urlStr); + + // Might be a short url - try to detect the schema. + if (!hasSchema) { + const int dotIndex = urlStr.indexOf(QLatin1Char('.')); + if (dotIndex != -1) { + const QString prefix = urlStr.left(dotIndex).toLower(); + QString urlString; + if (prefix == QLatin1String("ftp")) + urlString += prefix; + else + urlString += QLatin1String("http"); + urlString += QLatin1String("://"); + urlString += urlStr; + const QUrl url(urlString, QUrl::TolerantMode); + if (url.isValid()) + return url; + } + } + + // Fall back to QUrl's own tolerant parser. + return QUrl(string, QUrl::TolerantMode); + } +} + +namespace qdesigner_internal { + // TextPropertyEditor + TextPropertyEditor::TextPropertyEditor(QWidget *parent, + EmbeddingMode embeddingMode, + TextPropertyValidationMode validationMode) : + QWidget(parent), + m_validationMode(ValidationSingleLine), + m_updateMode(UpdateAsYouType), + m_lineEdit(new PropertyLineEdit(this)), + m_textEdited(false) + { + switch (embeddingMode) { + case EmbeddingNone: + break; + case EmbeddingTreeView: + m_lineEdit->setFrame(false); + break; + case EmbeddingInPlace: + m_lineEdit->setFrame(false); + Q_ASSERT(parent); + m_lineEdit->setBackgroundRole(parent->backgroundRole()); + break; + } + + setFocusProxy(m_lineEdit); + + connect(m_lineEdit,SIGNAL(editingFinished()), this, SIGNAL(editingFinished())); + connect(m_lineEdit,SIGNAL(returnPressed()), this, SLOT(slotEditingFinished())); + connect(m_lineEdit,SIGNAL(textChanged(QString)), this, SLOT(slotTextChanged(QString))); + connect(m_lineEdit,SIGNAL(textEdited(QString)), this, SLOT(slotTextEdited())); + + setTextPropertyValidationMode(validationMode); + } + + void TextPropertyEditor::setTextPropertyValidationMode(TextPropertyValidationMode vm) { + m_validationMode = vm; + m_lineEdit->setWantNewLine(multiLine(m_validationMode)); + switch (m_validationMode) { + case ValidationStyleSheet: + m_lineEdit->setValidator(new StyleSheetValidator(m_lineEdit)); + m_lineEdit->setCompleter(0); + break; + case ValidationMultiLine: + case ValidationRichText: + // Set a validator that replaces newline characters by literal "\\n". + // While it is not possible to actually type a newline characters, + // it can be pasted into the line edit. + m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, EscapedNewLine)); + m_lineEdit->setCompleter(0); + break; + case ValidationSingleLine: + // Set a validator that replaces newline characters by a blank. + m_lineEdit->setValidator(new ReplacementValidator(m_lineEdit, NewLineChar, QString(QLatin1Char(' ')))); + m_lineEdit->setCompleter(0); + break; + case ValidationObjectName: + setRegExpValidator(QLatin1String("[_a-zA-Z][_a-zA-Z0-9]{,1023}")); + m_lineEdit->setCompleter(0); + break; + case ValidationObjectNameScope: + setRegExpValidator(QLatin1String("[_a-zA-Z:][_a-zA-Z0-9:]{,1023}")); + m_lineEdit->setCompleter(0); + break; + case ValidationURL: { + static QStringList urlCompletions; + if (urlCompletions.empty()) { + urlCompletions.push_back(QLatin1String("about:blank")); + urlCompletions.push_back(QLatin1String("http://")); + urlCompletions.push_back(QLatin1String("http://www.")); + urlCompletions.push_back(QLatin1String("http://qtsoftware.com/")); + urlCompletions.push_back(QLatin1String("file://")); + urlCompletions.push_back(QLatin1String("ftp://")); + urlCompletions.push_back(QLatin1String("data:")); + urlCompletions.push_back(QLatin1String("data:text/html,")); + urlCompletions.push_back(QLatin1String("qrc:/")); + } + QCompleter *completer = new QCompleter(urlCompletions, m_lineEdit); + m_lineEdit->setCompleter(completer); + m_lineEdit->setValidator(new UrlValidator(completer, m_lineEdit)); + } + break; + } + + setFocusProxy(m_lineEdit); + markIntermediateState(); + } + + void TextPropertyEditor::setRegExpValidator(const QString &pattern) + { + const QRegExp regExp(pattern); + Q_ASSERT(regExp.isValid()); + m_lineEdit->setValidator(new QRegExpValidator(regExp,m_lineEdit)); + } + + QString TextPropertyEditor::text() const + { + return m_cachedText; + } + + void TextPropertyEditor::markIntermediateState() + { + if (m_lineEdit->hasAcceptableInput()) { + m_lineEdit->setPalette(QPalette()); + } else { + QPalette palette = m_lineEdit->palette(); + palette.setColor(QPalette::Active, QPalette::Text, Qt::red); + m_lineEdit->setPalette(palette); + } + + } + + void TextPropertyEditor::setText(const QString &text) + { + m_cachedText = text; + m_lineEdit->setText(stringToEditorString(text, m_validationMode)); + markIntermediateState(); + m_textEdited = false; + } + + void TextPropertyEditor::slotTextEdited() + { + m_textEdited = true; + } + + void TextPropertyEditor::slotTextChanged(const QString &text) { + m_cachedText = editorStringToString(text, m_validationMode); + markIntermediateState(); + if (m_updateMode == UpdateAsYouType) + emit textChanged(m_cachedText); + } + + void TextPropertyEditor::slotEditingFinished() + { + if (m_updateMode == UpdateOnFinished && m_textEdited) { + emit textChanged(m_cachedText); + m_textEdited = false; + } + } + + void TextPropertyEditor::selectAll() { + m_lineEdit->selectAll(); + } + + void TextPropertyEditor::clear() { + m_lineEdit->clear(); + } + + void TextPropertyEditor::setAlignment(Qt::Alignment alignment) { + m_lineEdit->setAlignment(alignment); + } + + void TextPropertyEditor::installEventFilter(QObject *filterObject) + { + if (m_lineEdit) + m_lineEdit->installEventFilter(filterObject); + } + + void TextPropertyEditor::resizeEvent ( QResizeEvent * event ) { + m_lineEdit->resize( event->size()); + } + + QSize TextPropertyEditor::sizeHint () const { + return m_lineEdit->sizeHint (); + } + + QSize TextPropertyEditor::minimumSizeHint () const { + return m_lineEdit->minimumSizeHint (); + } + + // Returns whether newline characters are valid in validationMode. + bool TextPropertyEditor::multiLine(TextPropertyValidationMode validationMode) { + return validationMode == ValidationMultiLine || validationMode == ValidationStyleSheet || validationMode == ValidationRichText; + } + + // Replace newline characters literal "\n" for inline editing in mode ValidationMultiLine + QString TextPropertyEditor::stringToEditorString(const QString &s, TextPropertyValidationMode validationMode) { + if (s.isEmpty() || !multiLine(validationMode)) + return s; + + QString rc(s); + // protect backslashes + rc.replace(QLatin1Char('\\'), QLatin1String("\\\\")); + // escape newlines + rc.replace(NewLineChar, QString(EscapedNewLine)); + return rc; + + } + + // Replace literal "\n" by actual new lines for inline editing in mode ValidationMultiLine + // Note: As the properties are updated while the user types, it is important + // that trailing slashes ('bla\') are not deleted nor ignored, else this will + // cause jumping of the cursor + QString TextPropertyEditor::editorStringToString(const QString &s, TextPropertyValidationMode validationMode) { + if (s.isEmpty() || !multiLine(validationMode)) + return s; + + QString rc(s); + for (int pos = 0; (pos = rc.indexOf(QLatin1Char('\\'),pos)) >= 0 ; ) { + // found an escaped character. If not a newline or at end of string, leave as is, else insert '\n' + const int nextpos = pos + 1; + if (nextpos >= rc.length()) // trailing '\\' + break; + // Escaped NewLine + if (rc.at(nextpos) == QChar(QLatin1Char('n'))) + rc[nextpos] = NewLineChar; + // Remove escape, go past escaped + rc.remove(pos,1); + pos++; + } + return rc; + } + + bool TextPropertyEditor::hasAcceptableInput() const { + return m_lineEdit->hasAcceptableInput(); + } +} + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/textpropertyeditor_p.h b/tools/designer/src/lib/shared/textpropertyeditor_p.h new file mode 100644 index 0000000..d921135 --- /dev/null +++ b/tools/designer/src/lib/shared/textpropertyeditor_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef TEXTPROPERTYEDITOR_H +#define TEXTPROPERTYEDITOR_H + +#include "shared_global_p.h" +#include "shared_enums_p.h" + +#include <QtGui/QWidget> + +QT_BEGIN_NAMESPACE + +namespace qdesigner_internal { + + class PropertyLineEdit; + + // Inline-Editor for text properties. Does escaping of newline characters + // to '\n' and back and provides validation modes. The interface + // corresponds to that of QLineEdit. + class QDESIGNER_SHARED_EXPORT TextPropertyEditor : public QWidget + { + TextPropertyEditor(const TextPropertyEditor &); + TextPropertyEditor& operator=(const TextPropertyEditor &); + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText USER true) + public: + enum EmbeddingMode { + // Stand-alone widget + EmbeddingNone, + // Disable frame + EmbeddingTreeView, + // For editing in forms + EmbeddingInPlace + }; + + enum UpdateMode { + // Emit textChanged() as the user types + UpdateAsYouType, + // Emit textChanged() only when the user finishes (for QUrl, etc.) + UpdateOnFinished + }; + + TextPropertyEditor(QWidget *parent = 0, EmbeddingMode embeddingMode = EmbeddingNone, TextPropertyValidationMode validationMode = ValidationMultiLine); + + TextPropertyValidationMode textPropertyValidationMode() const { return m_validationMode; } + void setTextPropertyValidationMode(TextPropertyValidationMode vm); + + UpdateMode updateMode() const { return m_updateMode; } + void setUpdateMode(UpdateMode um) { m_updateMode = um; } + + QString text() const; + + virtual QSize sizeHint () const; + virtual QSize minimumSizeHint () const; + + void setAlignment(Qt::Alignment alignment); + + bool hasAcceptableInput() const; + + // installs an event filter object on the private QLineEdit + void installEventFilter(QObject *filterObject); + + // Replace newline characters by literal "\n" for inline editing + // in mode ValidationMultiLine + static QString stringToEditorString(const QString &s, TextPropertyValidationMode validationMode = ValidationMultiLine); + + // Replace literal "\n" by actual new lines in mode ValidationMultiLine + static QString editorStringToString(const QString &s, TextPropertyValidationMode validationMode = ValidationMultiLine); + + // Returns whether newline characters are valid in validationMode. + static bool multiLine(TextPropertyValidationMode validationMode); + + signals: + void textChanged(const QString &text); + void editingFinished(); + + public slots: + void setText(const QString &text); + void selectAll(); + void clear(); + + protected: + void resizeEvent(QResizeEvent * event ); + + private slots: + void slotTextChanged(const QString &text); + void slotTextEdited(); + void slotEditingFinished(); + + private: + void setRegExpValidator(const QString &pattern); + void markIntermediateState(); + + TextPropertyValidationMode m_validationMode; + UpdateMode m_updateMode; + PropertyLineEdit* m_lineEdit; + + // Cached text containing real newline characters. + QString m_cachedText; + bool m_textEdited; + }; +} + +QT_END_NAMESPACE + +#endif // TEXTPROPERTYEDITOR_H diff --git a/tools/designer/src/lib/shared/widgetdatabase.cpp b/tools/designer/src/lib/shared/widgetdatabase.cpp new file mode 100644 index 0000000..e452eab --- /dev/null +++ b/tools/designer/src/lib/shared/widgetdatabase.cpp @@ -0,0 +1,865 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "widgetdatabase_p.h" +#include "widgetfactory_p.h" +#include "spacer_widget_p.h" +#include "abstractlanguage.h" +#include "pluginmanager_p.h" +#include "qdesigner_widgetbox_p.h" +#include "qdesigner_utils_p.h" +#include <ui4_p.h> + +#include <QtDesigner/customwidget.h> +#include <QtDesigner/propertysheet.h> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerFormEditorInterface> + +#include <QtXml/QXmlStreamWriter> +#include <QtCore/QtAlgorithms> +#include <QtCore/qdebug.h> +#include <QtCore/QMetaProperty> +#include <QtCore/QTextStream> +#include <QtCore/QRegExp> +#include <QtCore/QCoreApplication> + +QT_BEGIN_NAMESPACE + +namespace { + enum { debugWidgetDataBase = 0 }; +} + +namespace qdesigner_internal { + +// ---------------------------------------------------------- +WidgetDataBaseItem::WidgetDataBaseItem(const QString &name, const QString &group) + : m_name(name), + m_group(group), + m_compat(0), + m_container(0), + m_form(0), + m_custom(0), + m_promoted(0) +{ +} + +QString WidgetDataBaseItem::name() const +{ + return m_name; +} + +void WidgetDataBaseItem::setName(const QString &name) +{ + m_name = name; +} + +QString WidgetDataBaseItem::group() const +{ + return m_group; +} + +void WidgetDataBaseItem::setGroup(const QString &group) +{ + m_group = group; +} + +QString WidgetDataBaseItem::toolTip() const +{ + return m_toolTip; +} + +void WidgetDataBaseItem::setToolTip(const QString &toolTip) +{ + m_toolTip = toolTip; +} + +QString WidgetDataBaseItem::whatsThis() const +{ + return m_whatsThis; +} + +void WidgetDataBaseItem::setWhatsThis(const QString &whatsThis) +{ + m_whatsThis = whatsThis; +} + +QString WidgetDataBaseItem::includeFile() const +{ + return m_includeFile; +} + +void WidgetDataBaseItem::setIncludeFile(const QString &includeFile) +{ + m_includeFile = includeFile; +} + +QIcon WidgetDataBaseItem::icon() const +{ + return m_icon; +} + +void WidgetDataBaseItem::setIcon(const QIcon &icon) +{ + m_icon = icon; +} + +bool WidgetDataBaseItem::isCompat() const +{ + return m_compat; +} + +void WidgetDataBaseItem::setCompat(bool b) +{ + m_compat = b; +} + +bool WidgetDataBaseItem::isContainer() const +{ + return m_container; +} + +void WidgetDataBaseItem::setContainer(bool b) +{ + m_container = b; +} + +bool WidgetDataBaseItem::isCustom() const +{ + return m_custom; +} + +void WidgetDataBaseItem::setCustom(bool b) +{ + m_custom = b; +} + +QString WidgetDataBaseItem::pluginPath() const +{ + return m_pluginPath; +} + +void WidgetDataBaseItem::setPluginPath(const QString &path) +{ + m_pluginPath = path; +} + +bool WidgetDataBaseItem::isPromoted() const +{ + return m_promoted; +} + +void WidgetDataBaseItem::setPromoted(bool b) +{ + m_promoted = b; +} + +QString WidgetDataBaseItem::extends() const +{ + return m_extends; +} + +void WidgetDataBaseItem::setExtends(const QString &s) +{ + m_extends = s; +} + +void WidgetDataBaseItem::setDefaultPropertyValues(const QList<QVariant> &list) +{ + m_defaultPropertyValues = list; +} + +QList<QVariant> WidgetDataBaseItem::defaultPropertyValues() const +{ + return m_defaultPropertyValues; +} + +QStringList WidgetDataBaseItem::fakeSlots() const +{ + return m_fakeSlots; +} + +void WidgetDataBaseItem::setFakeSlots(const QStringList &fs) +{ + m_fakeSlots = fs; +} + +QStringList WidgetDataBaseItem::fakeSignals() const +{ + return m_fakeSignals; +} + +void WidgetDataBaseItem::setFakeSignals(const QStringList &fs) +{ + m_fakeSignals = fs; +} + +QString WidgetDataBaseItem::addPageMethod() const +{ + return m_addPageMethod; +} + +void WidgetDataBaseItem::setAddPageMethod(const QString &m) +{ + m_addPageMethod = m; +} + +WidgetDataBaseItem *WidgetDataBaseItem::clone(const QDesignerWidgetDataBaseItemInterface *item) +{ + WidgetDataBaseItem *rc = new WidgetDataBaseItem(item->name(), item->group()); + + rc->setToolTip(item->toolTip()); + rc->setWhatsThis(item->whatsThis()); + rc->setIncludeFile(item->includeFile()); + rc->setIcon(item->icon()); + rc->setCompat(item->isCompat()); + rc->setContainer(item->isContainer()); + rc->setCustom(item->isCustom() ); + rc->setPluginPath(item->pluginPath()); + rc->setPromoted(item->isPromoted()); + rc->setExtends(item->extends()); + rc->setDefaultPropertyValues(item->defaultPropertyValues()); + // container page method, fake slots and signals ignored here.y + return rc; +} + +// ---------------------------------------------------------- +WidgetDataBase::WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerWidgetDataBaseInterface(parent), + m_core(core) +{ +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) DECLARE_WIDGET(W, C) +#define DECLARE_WIDGET(W, C) append(new WidgetDataBaseItem(QString::fromUtf8(#W))); + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + append(new WidgetDataBaseItem(QString::fromUtf8("Line"))); + append(new WidgetDataBaseItem(QString::fromUtf8("Spacer"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QSplitter"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QLayoutWidget"))); + // QDesignerWidget is used as central widget and as container for tab widgets, etc. + WidgetDataBaseItem *designerWidgetItem = new WidgetDataBaseItem(QString::fromUtf8("QDesignerWidget")); + designerWidgetItem->setContainer(true); + append(designerWidgetItem); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDialog"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenu"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerMenuBar"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerDockWidget"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QDesignerQ3WidgetStack"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QAction"))); + append(new WidgetDataBaseItem(QString::fromUtf8("QButtonGroup"))); + + // ### remove me + // ### check the casts + +#if 0 // ### enable me after 4.1 + item(indexOfClassName(QLatin1String("QToolBar")))->setContainer(true); +#endif + + item(indexOfClassName(QLatin1String("QTabWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QGroupBox")))->setContainer(true); + item(indexOfClassName(QLatin1String("QScrollArea")))->setContainer(true); + item(indexOfClassName(QLatin1String("QStackedWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QToolBox")))->setContainer(true); + item(indexOfClassName(QLatin1String("QFrame")))->setContainer(true); + item(indexOfClassName(QLatin1String("QLayoutWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerDialog")))->setContainer(true); + item(indexOfClassName(QLatin1String("QSplitter")))->setContainer(true); + item(indexOfClassName(QLatin1String("QMainWindow")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDockWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerDockWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDesignerQ3WidgetStack")))->setContainer(true); + item(indexOfClassName(QLatin1String("QMdiArea")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWorkspace")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWizard")))->setContainer(true); + item(indexOfClassName(QLatin1String("QWizardPage")))->setContainer(true); + + item(indexOfClassName(QLatin1String("QWidget")))->setContainer(true); + item(indexOfClassName(QLatin1String("QDialog")))->setContainer(true); +} + +WidgetDataBase::~WidgetDataBase() +{ +} + +QDesignerFormEditorInterface *WidgetDataBase::core() const +{ + return m_core; +} + +int WidgetDataBase::indexOfObject(QObject *object, bool /*resolveName*/) const +{ + QExtensionManager *mgr = m_core->extensionManager(); + QDesignerLanguageExtension *lang = qt_extension<QDesignerLanguageExtension*> (mgr, m_core); + + QString id; + + if (lang) + id = lang->classNameOf(object); + + if (id.isEmpty()) + id = WidgetFactory::classNameOf(m_core,object); + + return QDesignerWidgetDataBaseInterface::indexOfClassName(id); +} + +static WidgetDataBaseItem *createCustomWidgetItem(const QDesignerCustomWidgetInterface *c, + const QDesignerCustomWidgetData &data) +{ + WidgetDataBaseItem *item = new WidgetDataBaseItem(c->name(), c->group()); + item->setContainer(c->isContainer()); + item->setCustom(true); + item->setIcon(c->icon()); + item->setIncludeFile(c->includeFile()); + item->setToolTip(c->toolTip()); + item->setWhatsThis(c->whatsThis()); + item->setPluginPath(data.pluginPath()); + item->setAddPageMethod(data.xmlAddPageMethod()); + item->setExtends(data.xmlExtends()); + return item; +} + +void WidgetDataBase::loadPlugins() +{ + typedef QMap<QString, int> NameIndexMap; + typedef QList<QDesignerWidgetDataBaseItemInterface*> ItemList; + typedef QMap<QString, QDesignerWidgetDataBaseItemInterface*> NameItemMap; + typedef QSet<QString> NameSet; + // 1) create a map of existing custom classes + NameIndexMap existingCustomClasses; + NameSet nonCustomClasses; + const int count = m_items.size(); + for (int i = 0; i < count; i++) { + const QDesignerWidgetDataBaseItemInterface* item = m_items[i]; + if (item->isCustom() && !item->isPromoted()) + existingCustomClasses.insert(item->name(), i); + else + nonCustomClasses.insert(item->name()); + } + // 2) create a list plugins + ItemList pluginList; + const QDesignerPluginManager *pm = m_core->pluginManager(); + foreach(QDesignerCustomWidgetInterface* c, pm->registeredCustomWidgets()) + pluginList += createCustomWidgetItem(c, pm->customWidgetData(c)); + + // 3) replace custom classes or add new ones, remove them from existingCustomClasses, + // leaving behind deleted items + unsigned replacedPlugins = 0; + unsigned addedPlugins = 0; + unsigned removedPlugins = 0; + if (!pluginList.empty()) { + ItemList::const_iterator cend = pluginList.constEnd(); + for (ItemList::const_iterator it = pluginList.constBegin();it != cend; ++it ) { + QDesignerWidgetDataBaseItemInterface* pluginItem = *it; + const QString pluginName = pluginItem->name(); + NameIndexMap::iterator existingIt = existingCustomClasses.find(pluginName); + if (existingIt == existingCustomClasses.end()) { + // Add new class. + if (nonCustomClasses.contains(pluginName)) { + designerWarning(tr("A custom widget plugin whose class name (%1) matches that of an existing class has been found.").arg(pluginName)); + } else { + append(pluginItem); + addedPlugins++; + } + } else { + // replace existing info + const int existingIndex = existingIt.value(); + delete m_items[existingIndex]; + m_items[existingIndex] = pluginItem; + existingCustomClasses.erase(existingIt); + replacedPlugins++; + + } + } + } + // 4) remove classes that have not been matched. The stored indexes become invalid while deleting. + if (!existingCustomClasses.empty()) { + NameIndexMap::const_iterator cend = existingCustomClasses.constEnd(); + for (NameIndexMap::const_iterator it = existingCustomClasses.constBegin();it != cend; ++it ) { + const int index = indexOfClassName(it.key()); + if (index != -1) { + remove(index); + removedPlugins++; + } + } + } + if (debugWidgetDataBase) + qDebug() << "WidgetDataBase::loadPlugins(): " << addedPlugins << " added, " << replacedPlugins << " replaced, " << removedPlugins << "deleted."; +} + +void WidgetDataBase::remove(int index) +{ + Q_ASSERT(index < m_items.size()); + delete m_items.takeAt(index); +} + +QList<QVariant> WidgetDataBase::defaultPropertyValues(const QString &name) +{ + WidgetFactory *factory = qobject_cast<WidgetFactory *>(m_core->widgetFactory()); + Q_ASSERT(factory); + // Create non-widgets, widgets in order + QObject* object = factory->createObject(name, 0); + if (!object) + object = factory->createWidget(name, 0); + if (!object) { + qDebug() << "** WARNING Factory failed to create " << name; + return QList<QVariant>(); + } + // Get properties from sheet. + QList<QVariant> result; + if (const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), object)) { + const int propertyCount = sheet->count(); + for (int i = 0; i < propertyCount; ++i) { + result.append(sheet->property(i)); + } + } + delete object; + return result; +} + +void WidgetDataBase::grabDefaultPropertyValues() +{ + const int itemCount = count(); + for (int i = 0; i < itemCount; ++i) { + QDesignerWidgetDataBaseItemInterface *dbItem = item(i); + const QList<QVariant> default_prop_values = defaultPropertyValues(dbItem->name()); + dbItem->setDefaultPropertyValues(default_prop_values); + } +} + +void WidgetDataBase::grabStandardWidgetBoxIcons() +{ + // At this point, grab the default icons for the non-custom widgets from + // the widget box. They will show up in the object inspector. + if (const QDesignerWidgetBox *wb = qobject_cast<const QDesignerWidgetBox *>(m_core->widgetBox())) { + const QString qWidgetClass = QLatin1String("QWidget"); + const int itemCount = count(); + for (int i = 0; i < itemCount; ++i) { + QDesignerWidgetDataBaseItemInterface *dbItem = item(i); + if (!dbItem->isCustom() && dbItem->icon().isNull()) { + // Careful not to catch the layout icons when looking for + // QWidget + const QString name = dbItem->name(); + if (name == qWidgetClass) { + dbItem->setIcon(wb->iconForWidget(name, QLatin1String("Containers"))); + } else { + dbItem->setIcon(wb->iconForWidget(name)); + } + } + } + } +} + +// --------------------- Functions relevant generation of new forms based on widgets (apart from the standard templates) + +enum { NewFormWidth = 400, NewFormHeight = 300 }; + +// Check if class is suitable to generate a form from +static inline bool isExistingTemplate(const QString &className) +{ + return className == QLatin1String("QWidget") || className == QLatin1String("QDialog") || className == QLatin1String("QMainWindow"); +} + +// Check if class is suitable to generate a form from +static inline bool suitableForNewForm(const QString &className) +{ + if (className.isEmpty()) // Missing custom widget information + return false; + if (className == QLatin1String("QWorkspace")) + return false; + if (className == QLatin1String("QSplitter")) + return false; + if (className.startsWith(QLatin1String("QDesigner")) || className.startsWith(QLatin1String("Q3")) || className.startsWith(QLatin1String("QLayout"))) + return false; + return true; +} + +// Return a list of widget classes from which new forms can be generated. +// Suitable for 'New form' wizards in integrations. +QStringList WidgetDataBase::formWidgetClasses(const QDesignerFormEditorInterface *core) +{ + static QStringList rc; + if (rc.empty()) { + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widgetCount = wdb->count(); + for (int i = 0; i < widgetCount; i++) { + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); + if (item->isContainer() && !item->isCustom() && !item->isPromoted()) { + const QString name = item->name(); // Standard Widgets: no existing templates + if (!isExistingTemplate(name) && suitableForNewForm(name)) + rc += name; + } + } + } + return rc; +} + +// Return a list of custom widget classes from which new forms can be generated. +// Suitable for 'New form' wizards in integrations. +QStringList WidgetDataBase::customFormWidgetClasses(const QDesignerFormEditorInterface *core) +{ + QStringList rc; + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + const int widgetCount = wdb->count(); + for (int i = 0; i < widgetCount; i++) { // Custom widgets: check name and base class. + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(i); + if (item->isContainer() && item->isCustom() && !item->isPromoted()) { + if (suitableForNewForm(item->name()) && suitableForNewForm(item->extends())) + rc += item->name(); + } + } + return rc; +} + +// Get XML for a new form from the widget box. Change objectName/geometry +// properties to be suitable for new forms +static QString xmlFromWidgetBox(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) +{ + typedef QList<DomProperty*> PropertyList; + + QDesignerWidgetBoxInterface::Widget widget; + const bool found = QDesignerWidgetBox::findWidget(core->widgetBox(), className, QString(), &widget); + if (!found) + return QString(); + DomUI *domUI = QDesignerWidgetBox::xmlToUi(className, widget.domXml(), false); + domUI->setAttributeVersion(QLatin1String("4.0")); + if (!domUI) + return QString(); + DomWidget *domWidget = domUI->elementWidget(); + if (!domWidget) + return QString(); + // Properties: Remove the "objectName" property in favour of the name attribute and check geometry. + domWidget->setAttributeName(objectName); + const QString geometryProperty = QLatin1String("geometry"); + const QString objectNameProperty = QLatin1String("objectName"); + PropertyList properties = domWidget->elementProperty(); + for (PropertyList::iterator it = properties.begin(); it != properties.end(); ) { + DomProperty *property = *it; + if (property->attributeName() == objectNameProperty) { // remove "objectName" + it = properties.erase(it); + delete property; + } else { + if (property->attributeName() == geometryProperty) { // Make sure form is at least 400, 300 + if (DomRect *geom = property->elementRect()) { + if (geom->elementWidth() < NewFormWidth) + geom->setElementWidth(NewFormWidth); + if (geom->elementHeight() < NewFormHeight) + geom->setElementHeight(NewFormHeight); + } + } + ++it; + } + } + // Add a window title property + DomString *windowTitleString = new DomString; + windowTitleString->setText(objectName); + DomProperty *windowTitleProperty = new DomProperty; + windowTitleProperty->setAttributeName(QLatin1String("windowTitle")); + windowTitleProperty->setElementString(windowTitleString); + properties.push_back(windowTitleProperty); + // ------ + domWidget->setElementProperty(properties); + // Embed in in DomUI and get string. Omit the version number. + domUI->setElementClass(objectName); + + QString rc; + { // Serialize domUI + QXmlStreamWriter writer(&rc); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + domUI->write(writer); + writer.writeEndDocument(); + } + delete domUI; + return rc; +} + +// Generate default standard ui new form xml based on the class passed on as similarClassName. +static QString generateNewFormXML(const QString &className, const QString &similarClassName, const QString &name) +{ + QString rc; { + QTextStream str(&rc); + str << QLatin1String("<ui version=\"4.0\" >\n<class>") << name << QLatin1String("</class>\n") + << QLatin1String("<widget class=\"") << className << QLatin1String("\" name=\"") << name << QLatin1String("\" >\n") + << QLatin1String("<property name=\"geometry\" >\n<rect><x>0</x><y>0</y><width>") + << NewFormWidth << QLatin1String("</width><height>") << NewFormHeight << QLatin1String("</height></rect>\n</property>\n"); + str << QLatin1String("<property name=\"windowTitle\" >\n<string>") << name << QLatin1String("</string>\n</property>\n"); + + if (similarClassName == QLatin1String("QMainWindow")) { + str << QLatin1String("<widget class=\"QWidget\" name=\"centralwidget\" />\n"); + } else { + if (similarClassName == QLatin1String("QWizard")) + str << QLatin1String("<widget class=\"QWizardPage\" name=\"wizardPage1\" /><widget class=\"QWizardPage\" name=\"wizardPage2\" />\n"); + } + str << QLatin1String("</widget>\n</ui>\n"); + } + return rc; +} + +// Generate a form template using a class name obtained from formWidgetClasses(), customFormWidgetClasses(). +QString WidgetDataBase::formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName) +{ + // How to find suitable XML for a class: + // 1) Look in widget box (as all the required centralwidgets, tab widget pages, etc. should be there). + const QString widgetBoxXml = xmlFromWidgetBox(core, className, objectName); + if (!widgetBoxXml.isEmpty()) + return widgetBoxXml; + // 2) If that fails, only custom main windows, custom dialogs and unsupported Qt Widgets should + // be left over. Generate something that is similar to the default templates. Find a similar class. + const QDesignerWidgetDataBaseInterface *wdb = core->widgetDataBase(); + QString similarClass = QLatin1String("QWidget"); + const int index = wdb->indexOfClassName(className); + if (index != -1) { + const QDesignerWidgetDataBaseItemInterface *item = wdb->item(index); + similarClass = item->isCustom() ? item->extends() : item->name(); + } + // Generate standard ui based on the class passed on as baseClassName. + const QString rc = generateNewFormXML(className, similarClass, objectName); + return rc; +} + +// Set a fixed size on a XML template +QString WidgetDataBase::scaleFormTemplate(const QString &xml, const QSize &size, bool fixed) +{ + typedef QList<DomProperty*> PropertyList; + DomUI *domUI = QDesignerWidgetBox::xmlToUi(QLatin1String("Form"), xml, false); + if (!domUI) + return QString(); + DomWidget *domWidget = domUI->elementWidget(); + if (!domWidget) + return QString(); + // Properties: Find/Ensure the geometry, minimum and maximum sizes properties + const QString geometryPropertyName = QLatin1String("geometry"); + const QString minimumSizePropertyName = QLatin1String("minimumSize"); + const QString maximumSizePropertyName = QLatin1String("maximumSize"); + DomProperty *geomProperty = 0; + DomProperty *minimumSizeProperty = 0; + DomProperty *maximumSizeProperty = 0; + + PropertyList properties = domWidget->elementProperty(); + const PropertyList::const_iterator cend = properties.constEnd(); + for (PropertyList::const_iterator it = properties.constBegin(); it != cend; ++it) { + const QString name = (*it)->attributeName(); + if (name == geometryPropertyName) { + geomProperty = *it; + } else { + if (name == minimumSizePropertyName) { + minimumSizeProperty = *it; + } else { + if (name == maximumSizePropertyName) + maximumSizeProperty = *it; + } + } + } + if (!geomProperty) { + geomProperty = new DomProperty; + geomProperty->setAttributeName(geometryPropertyName); + geomProperty->setElementRect(new DomRect); + properties.push_front(geomProperty); + } + if (fixed) { + if (!minimumSizeProperty) { + minimumSizeProperty = new DomProperty; + minimumSizeProperty->setAttributeName(minimumSizePropertyName); + minimumSizeProperty->setElementSize(new DomSize); + properties.push_back(minimumSizeProperty); + } + if (!maximumSizeProperty) { + maximumSizeProperty = new DomProperty; + maximumSizeProperty->setAttributeName(maximumSizePropertyName); + maximumSizeProperty->setElementSize(new DomSize); + properties.push_back(maximumSizeProperty); + } + } + // Set values of geometry, minimum and maximum sizes properties + const int width = size.width(); + const int height = size.height(); + if (DomRect *geom = geomProperty->elementRect()) { + geom->setElementWidth(width); + geom->setElementHeight(height); + } + if (fixed) { + if (DomSize *s = minimumSizeProperty->elementSize()) { + s->setElementWidth(width); + s->setElementHeight(height); + } + if (DomSize *s = maximumSizeProperty->elementSize()) { + s->setElementWidth(width); + s->setElementHeight(height); + } + } + // write back + domWidget->setElementProperty(properties); + + QString rc; + { // serialize domUI + QXmlStreamWriter writer(&rc); + writer.setAutoFormatting(true); + writer.setAutoFormattingIndent(1); + writer.writeStartDocument(); + domUI->write(writer); + writer.writeEndDocument(); + } + + delete domUI; + return rc; +} + +// ---- free functions +QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile) +{ + const bool global = !includeFile.isEmpty() && + includeFile[0] == QLatin1Char('<') && + includeFile[includeFile.size() - 1] == QLatin1Char('>'); + if (global) { + includeFile.remove(includeFile.size() - 1, 1); + includeFile.remove(0, 1); + } + return IncludeSpecification(includeFile, global ? IncludeGlobal : IncludeLocal); +} + +QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType) { + if (includeType == IncludeGlobal && !includeFile.isEmpty()) { + includeFile.append(QLatin1Char('>')); + includeFile.insert(0, QLatin1Char('<')); + } + return includeFile; +} + + +/* Appends a derived class to the database inheriting the data of the base class. Used + for custom and promoted widgets. + + Depending on whether an entry exists, the existing or a newly created entry is + returned. A return value of 0 indicates that the base class could not be found. */ + +QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface * + appendDerived(QDesignerWidgetDataBaseInterface *db, + const QString &className, const QString &group, + const QString &baseClassName, + const QString &includeFile, + bool promoted, bool custom) +{ + if (debugWidgetDataBase) + qDebug() << "appendDerived " << className << " derived from " << baseClassName; + // Check. + if (className.isEmpty() || baseClassName.isEmpty()) { + qWarning("** WARNING %s called with an empty class names: '%s' extends '%s'.", + Q_FUNC_INFO, className.toUtf8().constData(), baseClassName.toUtf8().constData()); + return 0; + } + // Check whether item already exists. + QDesignerWidgetDataBaseItemInterface *derivedItem = 0; + const int existingIndex = db->indexOfClassName(className); + if ( existingIndex != -1) + derivedItem = db->item(existingIndex); + if (derivedItem) { + // Check the existing item for base class mismatch. This will likely + // happen when loading a file written by an instance with missing plugins. + // In that case, just warn and ignore the file properties. + // + // An empty base class indicates that it is not known (for example, for custom plugins). + // In this case, the widget DB is later updated once the widget is created + // by DOM (by querying the metaobject). Suppress the warning. + const QString existingBaseClass = derivedItem->extends(); + if (existingBaseClass.isEmpty() || baseClassName == existingBaseClass) + return derivedItem; + + // Warn about mismatches + designerWarning(QCoreApplication::translate("WidgetDataBase", + "The file contains a custom widget '%1' whose base class (%2)" + " differs from the current entry in the widget database (%3)." + " The widget database is left unchanged."). + arg(className, baseClassName, existingBaseClass)); + return derivedItem; + } + // Create this item, inheriting its base properties + const int baseIndex = db->indexOfClassName(baseClassName); + if (baseIndex == -1) { + if (debugWidgetDataBase) + qDebug() << "appendDerived failed due to missing base class"; + return 0; + } + const QDesignerWidgetDataBaseItemInterface *baseItem = db->item(baseIndex); + derivedItem = WidgetDataBaseItem::clone(baseItem); + // Sort of hack: If base class is QWidget, we most likely + // do not want to inherit the container attribute. + static const QString qWidgetName = QLatin1String("QWidget"); + if (baseItem->name() == qWidgetName) + derivedItem->setContainer(false); + // set new props + derivedItem->setName(className); + derivedItem->setGroup(group); + derivedItem->setCustom(custom); + derivedItem->setPromoted(promoted); + derivedItem->setExtends(baseClassName); + derivedItem->setIncludeFile(includeFile); + db->append(derivedItem); + return derivedItem; +} + +/* Return a list of database items to which a class can be promoted to. */ + +QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList + promotionCandidates(const QDesignerWidgetDataBaseInterface *db, + const QString &baseClassName) +{ + WidgetDataBaseItemList rc; + // find existing promoted widgets deriving from base. + const int count = db->count(); + for (int i = 0; i < count; ++i) { + QDesignerWidgetDataBaseItemInterface *item = db->item(i); + if (item->isPromoted() && item->extends() == baseClassName) { + rc.push_back(item); + } + } + return rc; +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/widgetdatabase_p.h b/tools/designer/src/lib/shared/widgetdatabase_p.h new file mode 100644 index 0000000..4414bbe --- /dev/null +++ b/tools/designer/src/lib/shared/widgetdatabase_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef WIDGETDATABASE_H +#define WIDGETDATABASE_H + +#include "shared_global_p.h" + +#include <QtDesigner/QDesignerWidgetDataBaseInterface> + +#include <QtGui/QIcon> +#include <QtCore/QString> +#include <QtCore/QVariant> +#include <QtCore/QPair> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE + +class QObject; +class QDesignerCustomWidgetInterface; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT WidgetDataBaseItem: public QDesignerWidgetDataBaseItemInterface +{ +public: + WidgetDataBaseItem(const QString &name = QString(), + const QString &group = QString()); + + QString name() const; + void setName(const QString &name); + + QString group() const; + void setGroup(const QString &group); + + QString toolTip() const; + void setToolTip(const QString &toolTip); + + QString whatsThis() const; + void setWhatsThis(const QString &whatsThis); + + QString includeFile() const; + void setIncludeFile(const QString &includeFile); + + + QIcon icon() const; + void setIcon(const QIcon &icon); + + bool isCompat() const; + void setCompat(bool compat); + + bool isContainer() const; + void setContainer(bool b); + + bool isCustom() const; + void setCustom(bool b); + + QString pluginPath() const; + void setPluginPath(const QString &path); + + bool isPromoted() const; + void setPromoted(bool b); + + QString extends() const; + void setExtends(const QString &s); + + void setDefaultPropertyValues(const QList<QVariant> &list); + QList<QVariant> defaultPropertyValues() const; + + static WidgetDataBaseItem *clone(const QDesignerWidgetDataBaseItemInterface *item); + + QStringList fakeSlots() const; + void setFakeSlots(const QStringList &); + + QStringList fakeSignals() const; + void setFakeSignals(const QStringList &); + + QString addPageMethod() const; + void setAddPageMethod(const QString &m); + +private: + QString m_name; + QString m_group; + QString m_toolTip; + QString m_whatsThis; + QString m_includeFile; + QString m_pluginPath; + QString m_extends; + QString m_addPageMethod; + QIcon m_icon; + uint m_compat: 1; + uint m_container: 1; + uint m_form: 1; + uint m_custom: 1; + uint m_promoted: 1; + QList<QVariant> m_defaultPropertyValues; + QStringList m_fakeSlots; + QStringList m_fakeSignals; +}; + +enum IncludeType { IncludeLocal, IncludeGlobal }; + +typedef QPair<QString, IncludeType> IncludeSpecification; + +QDESIGNER_SHARED_EXPORT IncludeSpecification includeSpecification(QString includeFile); +QDESIGNER_SHARED_EXPORT QString buildIncludeFile(QString includeFile, IncludeType includeType); + +class QDESIGNER_SHARED_EXPORT WidgetDataBase: public QDesignerWidgetDataBaseInterface +{ + Q_OBJECT +public: + WidgetDataBase(QDesignerFormEditorInterface *core, QObject *parent = 0); + virtual ~WidgetDataBase(); + + virtual QDesignerFormEditorInterface *core() const; + + virtual int indexOfObject(QObject *o, bool resolveName = true) const; + + void remove(int index); + + + void grabDefaultPropertyValues(); + void grabStandardWidgetBoxIcons(); + + // Helpers for 'New Form' wizards in integrations. Obtain a list of suitable classes and generate XML for them. + static QStringList formWidgetClasses(const QDesignerFormEditorInterface *core); + static QStringList customFormWidgetClasses(const QDesignerFormEditorInterface *core); + static QString formTemplate(const QDesignerFormEditorInterface *core, const QString &className, const QString &objectName); + + // Helpers for 'New Form' wizards: Set a fixed size on a XML form template + static QString scaleFormTemplate(const QString &xml, const QSize &size, bool fixed); + +public slots: + void loadPlugins(); + +private: + QList<QVariant> defaultPropertyValues(const QString &name); + + QDesignerFormEditorInterface *m_core; +}; + +QDESIGNER_SHARED_EXPORT QDesignerWidgetDataBaseItemInterface + *appendDerived(QDesignerWidgetDataBaseInterface *db, + const QString &className, + const QString &group, + const QString &baseClassName, + const QString &includeFile, + bool promoted, + bool custom); + +typedef QList<QDesignerWidgetDataBaseItemInterface*> WidgetDataBaseItemList; + +QDESIGNER_SHARED_EXPORT WidgetDataBaseItemList + promotionCandidates(const QDesignerWidgetDataBaseInterface *db, + const QString &baseClassName); +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETDATABASE_H diff --git a/tools/designer/src/lib/shared/widgetfactory.cpp b/tools/designer/src/lib/shared/widgetfactory.cpp new file mode 100644 index 0000000..3822fec --- /dev/null +++ b/tools/designer/src/lib/shared/widgetfactory.cpp @@ -0,0 +1,897 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/* +TRANSLATOR qdesigner_internal::WidgetFactory +*/ + +#include "widgetfactory_p.h" +#include "widgetdatabase_p.h" +#include "metadatabase_p.h" +#include "qlayout_widget_p.h" +#include "qdesigner_widget_p.h" +#include "qdesigner_tabwidget_p.h" +#include "qdesigner_toolbox_p.h" +#include "qdesigner_stackedbox_p.h" +#include "qdesigner_toolbar_p.h" +#include "qdesigner_menubar_p.h" +#include "qdesigner_menu_p.h" +#include "qdesigner_dockwidget_p.h" +#include "qdesigner_utils_p.h" +#include "formwindowbase_p.h" + +// shared +#include "layoutinfo_p.h" +#include "spacer_widget_p.h" +#include "layout_p.h" +#include "abstractintrospection_p.h" + +// sdk +#include <QtDesigner/QDesignerFormEditorInterface> +#include <QtDesigner/QDesignerContainerExtension> +#include <QtDesigner/QDesignerCustomWidgetInterface> +#include <QtDesigner/QExtensionManager> +#include <QtDesigner/QDesignerPropertySheetExtension> +#include <QtDesigner/QDesignerLanguageExtension> +#include <QtDesigner/QDesignerFormWindowManagerInterface> +#include <QtDesigner/QDesignerFormWindowCursorInterface> + +#include <QtGui/QtGui> +#include <QtGui/QScrollBar> +#include <QtGui/QFontComboBox> +#include <QtGui/QAbstractSpinBox> +#include <QtGui/QLineEdit> +#include <QtGui/QButtonGroup> +#include <QtGui/QStyle> +#include <QtGui/QStyleFactory> +#include <QtGui/QWizard> +#include <QtCore/qdebug.h> +#include <QtCore/QMetaObject> + +QT_BEGIN_NAMESPACE + +#ifdef Q_OS_WIN +static inline bool isAxWidget(const QObject *o) +{ + // Is it one of QDesignerAxWidget/QDesignerAxPluginWidget? + static const char *axWidgetName = "QDesignerAx"; + static const unsigned axWidgetNameLen = qstrlen(axWidgetName); + return qstrncmp(o->metaObject()->className(), axWidgetName, axWidgetNameLen) == 0; +} +#endif + +/* Dynamic boolean property indicating object was created by the factory + * for the form editor. */ + +static const char *formEditorDynamicProperty = "_q_formEditorObject"; + +namespace qdesigner_internal { + +// A friendly SpinBox that grants access to its QLineEdit +class FriendlySpinBox : public QAbstractSpinBox { +public: + friend class WidgetFactory; +}; + +// An event filter for form-combo boxes that prevents the embedded line edit +// from getting edit focus (and drawing blue artifacts/lines). It catches the +// ChildPolished event when the "editable" property flips to true and the +// QLineEdit is created and turns off the LineEdit's focus policy. + +class ComboEventFilter : public QObject { +public: + explicit ComboEventFilter(QComboBox *parent) : QObject(parent) {} + virtual bool eventFilter(QObject *watched, QEvent *event); +}; + +bool ComboEventFilter::eventFilter(QObject *watched, QEvent *event) +{ + if (event->type() == QEvent::ChildPolished) { + QComboBox *cb = static_cast<QComboBox*>(watched); + if (QLineEdit *le = cb->lineEdit()) + le->setFocusPolicy(Qt::NoFocus); + } + return QObject::eventFilter(watched, event); +} + +/* Watch out for QWizards changing their pages and make sure that not some + * selected widget becomes invisible on a hidden page (causing the selection + * handles to shine through). Select the wizard in that case in analogy to + * the QTabWidget event filters, etc. */ + +class WizardPageChangeWatcher : public QObject { + Q_OBJECT +public: + explicit WizardPageChangeWatcher(QWizard *parent); + +public slots: + void pageChanged(); +}; + +WizardPageChangeWatcher::WizardPageChangeWatcher(QWizard *parent) : + QObject(parent) +{ + connect(parent, SIGNAL(currentIdChanged(int)), this, SLOT(pageChanged())); +} + +void WizardPageChangeWatcher::pageChanged() +{ + /* Use a bit more conservative approach than that for the QTabWidget, + * change the selection only if a selected child becomes invisible by + * changing the page. */ + QWizard *wizard = static_cast<QWizard *>(parent()); + QDesignerFormWindowInterface *fw = QDesignerFormWindowInterface::findFormWindow(wizard); + if (!fw) + return; + QDesignerFormWindowCursorInterface *cursor = fw->cursor(); + const int selCount = cursor->selectedWidgetCount(); + for (int i = 0; i < selCount; i++) { + if (!cursor->selectedWidget(i)->isVisible()) { + fw->clearSelection(false); + fw->selectWidget(wizard, true); + break; + } + } +} + +// ---------------- WidgetFactory::Strings +WidgetFactory::Strings::Strings() : + m_alignment(QLatin1String("alignment")), + m_bottomMargin(QLatin1String("bottomMargin")), + m_geometry(QLatin1String("geometry")), + m_leftMargin(QLatin1String("leftMargin")), + m_line(QLatin1String("Line")), + m_objectName(QLatin1String("objectName")), + m_spacerName(QLatin1String("spacerName")), + m_orientation(QLatin1String("orientation")), + m_q3WidgetStack(QLatin1String("Q3WidgetStack")), + m_qAction(QLatin1String("QAction")), + m_qButtonGroup(QLatin1String("QButtonGroup")), + m_qAxWidget(QLatin1String("QAxWidget")), + m_qDialog(QLatin1String("QDialog")), + m_qDockWidget(QLatin1String("QDockWidget")), + m_qLayoutWidget(QLatin1String("QLayoutWidget")), + m_qMenu(QLatin1String("QMenu")), + m_qMenuBar(QLatin1String("QMenuBar")), + m_qWidget(QLatin1String("QWidget")), + m_rightMargin(QLatin1String("rightMargin")), + m_sizeHint(QLatin1String("sizeHint")), + m_spacer(QLatin1String("Spacer")), + m_text(QLatin1String("text")), + m_title(QLatin1String("title")), + m_topMargin(QLatin1String("topMargin")), + m_windowIcon(QLatin1String("windowIcon")), + m_windowTitle(QLatin1String("windowTitle")) +{ +} +// ---------------- WidgetFactory +QPointer<QWidget> *WidgetFactory::m_lastPassiveInteractor = new QPointer<QWidget>(); +bool WidgetFactory::m_lastWasAPassiveInteractor = false; +const char *WidgetFactory::disableStyleCustomPaintingPropertyC = "_q_custom_style_disabled"; + +WidgetFactory::WidgetFactory(QDesignerFormEditorInterface *core, QObject *parent) + : QDesignerWidgetFactoryInterface(parent), + m_core(core), + m_formWindow(0), + m_currentStyle(0) +{ +} + +WidgetFactory::~WidgetFactory() +{ +} + +QDesignerFormWindowInterface *WidgetFactory::currentFormWindow(QDesignerFormWindowInterface *fw) +{ + QDesignerFormWindowInterface *was = m_formWindow; + m_formWindow = fw; + return was; +} + +void WidgetFactory::loadPlugins() +{ + m_customFactory.clear(); + + QDesignerPluginManager *pluginManager = m_core->pluginManager(); + + QList<QDesignerCustomWidgetInterface*> lst = pluginManager->registeredCustomWidgets(); + foreach (QDesignerCustomWidgetInterface *c, lst) { + m_customFactory.insert(c->name(), c); + } +} + +// Convencience to create non-widget objects. Returns 0 if unknown +QObject* WidgetFactory::createObject(const QString &className, QObject* parent) const +{ + if (className.isEmpty()) { + qWarning("** WARNING %s called with an empty class name", Q_FUNC_INFO); + return 0; + } + if (className == m_strings.m_qAction) + return new QAction(parent); + if (className == m_strings.m_qButtonGroup) + return new QButtonGroup(parent); + return 0; +} + +QWidget* WidgetFactory::createCustomWidget(const QString &className, QWidget *parentWidget, bool *creationError) const +{ + *creationError = false; + CustomWidgetFactoryMap::const_iterator it = m_customFactory.constFind(className); + if (it == m_customFactory.constEnd()) + return 0; + + QDesignerCustomWidgetInterface *factory = it.value(); + QWidget *rc = factory->createWidget(parentWidget); + // shouldn't happen + if (!rc) { + *creationError = true; + designerWarning(tr("The custom widget factory registered for widgets of class %1 returned 0.").arg(className)); + return 0; + } + // Figure out the base class unless it is known + static QSet<QString> knownCustomClasses; + if (!knownCustomClasses.contains(className)) { + QDesignerWidgetDataBaseInterface *wdb = m_core->widgetDataBase(); + const int widgetInfoIndex = wdb->indexOfObject(rc, false); + if (widgetInfoIndex != -1) { + if (wdb->item(widgetInfoIndex)->extends().isEmpty()) { + const QDesignerMetaObjectInterface *mo = core()->introspection()->metaObject(rc)->superClass(); + // If we hit on a 'Q3DesignerXXWidget' that claims to be a 'Q3XXWidget', step + // over. + if (mo && mo->className() == className) + mo = mo->superClass(); + while (mo != 0) { + if (core()->widgetDataBase()->indexOfClassName(mo->className()) != -1) { + wdb->item(widgetInfoIndex)->setExtends(mo->className()); + break; + } + mo = mo->superClass(); + } + } + knownCustomClasses.insert(className); + } + } + // Since a language plugin may lie about its names, like Qt Jambi + // does, return immediately here... + QDesignerLanguageExtension *lang = + qt_extension<QDesignerLanguageExtension *>(m_core->extensionManager(), m_core); + if (lang) + return rc; + +#ifdef Q_OS_WIN + if (isAxWidget(rc)) + return rc; +#endif + // Check for mismatched class names which is hard to track. + // Perform literal comparison first for QAxWidget, for which a meta object hack is in effect. + const char *createdClassNameC = rc->metaObject()->className(); + const QByteArray classNameB = className.toUtf8(); + const char *classNameC = classNameB.constData(); + + if (qstrcmp(createdClassNameC, classNameC) && !rc->inherits(classNameC)) + designerWarning(tr("A class name mismatch occurred when creating a widget using the custom widget factory registered for widgets of class %1." + " It returned a widget of class %2.").arg(className).arg(QString::fromUtf8(createdClassNameC))); + return rc; +} + + +QWidget *WidgetFactory::createWidget(const QString &widgetName, QWidget *parentWidget) const +{ + if (widgetName.isEmpty()) { + qWarning("** WARNING %s called with an empty class name", Q_FUNC_INFO); + return 0; + } + // Preview or for form window? + QDesignerFormWindowInterface *fw = m_formWindow; + if (! fw) + fw = QDesignerFormWindowInterface::findFormWindow(parentWidget); + + QWidget *w = 0; + do { + // 1) custom. If there is an explicit failure(factory wants to indicate something is wrong), + // return 0, do not try to find fallback, which might be worse in the case of Q3 widget. + bool customWidgetCreationError; + w = createCustomWidget(widgetName, parentWidget, &customWidgetCreationError); + if (w) { + break; + } else { + if (customWidgetCreationError) + return 0; + } + + // 2) Special widgets + if (widgetName == m_strings.m_line) { + w = new Line(parentWidget); + } else if (widgetName == m_strings.m_qDockWidget) { + w = new QDesignerDockWidget(parentWidget); + } else if (widgetName == m_strings.m_qMenuBar) { + w = new QDesignerMenuBar(parentWidget); + } else if (widgetName == m_strings.m_qMenu) { + w = new QDesignerMenu(parentWidget); + } else if (widgetName == m_strings.m_spacer) { + w = new Spacer(parentWidget); + } else if (widgetName == m_strings.m_qDockWidget) { + w = new QDesignerDockWidget(parentWidget); + } else if (widgetName == m_strings.m_qLayoutWidget) { + w = fw ? new QLayoutWidget(fw, parentWidget) : new QWidget(parentWidget); + } else if (widgetName == m_strings.m_qDialog) { + if (fw) { + w = new QDesignerDialog(fw, parentWidget); + } else { + w = new QDialog(parentWidget); + } + } else if (widgetName == m_strings.m_qWidget) { + /* We want a 'QDesignerWidget' that draws a grid only for widget + * forms and container extension pages (not for preview and not + * for normal QWidget children on forms (legacy) */ + if (fw && parentWidget) { + if (qt_extension<QDesignerContainerExtension*>(m_core->extensionManager(), parentWidget)) { + w = new QDesignerWidget(fw, parentWidget); + } else { + if (const FormWindowBase *fwb = qobject_cast<FormWindowBase *>(fw)) + if (parentWidget == fwb->formContainer()) + w = new QDesignerWidget(fw, parentWidget); + } + } + if (!w) + w = new QWidget(parentWidget); + } + if (w) + break; + + // 3) table + const QByteArray widgetNameBA = widgetName.toUtf8(); + const char *widgetNameC = widgetNameBA.constData(); + + if (w) { // symmetry for macro + } + +#define DECLARE_LAYOUT(L, C) +#define DECLARE_COMPAT_WIDGET(W, C) /*DECLARE_WIDGET(W, C)*/ +#define DECLARE_WIDGET(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(parentWidget); } +#define DECLARE_WIDGET_1(W, C) else if (!qstrcmp(widgetNameC, #W)) { Q_ASSERT(w == 0); w = new W(0, parentWidget); } + +#include "widgets.table" + +#undef DECLARE_COMPAT_WIDGET +#undef DECLARE_LAYOUT +#undef DECLARE_WIDGET +#undef DECLARE_WIDGET_1 + + if (w) + break; + // 4) fallBack + const QString fallBackBaseClass = m_strings.m_qWidget; + QDesignerWidgetDataBaseInterface *db = core()->widgetDataBase(); + QDesignerWidgetDataBaseItemInterface *item = db->item(db->indexOfClassName(widgetName)); + if (item == 0) { + // Emergency: Create, derived from QWidget + QString includeFile = widgetName.toLower(); + includeFile += QLatin1String(".h"); + item = appendDerived(db,widgetName, tr("%1 Widget").arg(widgetName),fallBackBaseClass, + includeFile, true, true); + Q_ASSERT(item); + } + QString baseClass = item->extends(); + if (baseClass.isEmpty()) { + // Currently happens in the case of Q3-Support widgets + baseClass =fallBackBaseClass; + } + w = createWidget(baseClass, parentWidget); + promoteWidget(core(),w,widgetName); + } while (false); + + Q_ASSERT(w != 0); + if (m_currentStyle) + w->setStyle(m_currentStyle); + initializeCommon(w); + if (fw) { // form editor initialization + initialize(w); + } else { // preview-only initialization + initializePreview(w); + } + return w; +} + +QString WidgetFactory::classNameOf(QDesignerFormEditorInterface *c, const QObject* o) +{ + if (o == 0) + return QString(); + + const char *className = o->metaObject()->className(); + if (!o->isWidgetType()) + return QLatin1String(className); + const QWidget *w = static_cast<const QWidget*>(o); + // check promoted before designer special + const QString customClassName = promotedCustomClassName(c, const_cast<QWidget*>(w)); + if (!customClassName.isEmpty()) + return customClassName; + if (qobject_cast<const QDesignerMenuBar*>(w)) + return QLatin1String("QMenuBar"); + else if (qobject_cast<const QDesignerMenu*>(w)) + return QLatin1String("QMenu"); + else if (qobject_cast<const QDesignerDockWidget*>(w)) + return QLatin1String("QDockWidget"); + else if (qobject_cast<const QDesignerDialog*>(w)) + return QLatin1String("QDialog"); + else if (qobject_cast<const QDesignerWidget*>(w)) + return QLatin1String("QWidget"); +#ifdef Q_OS_WIN + else if (isAxWidget(w)) + return QLatin1String("QAxWidget"); +#endif + else if (qstrcmp(className, "QDesignerQ3WidgetStack") == 0) + return QLatin1String("Q3WidgetStack"); + + return QLatin1String(className); +} + +QLayout *WidgetFactory::createUnmanagedLayout(QWidget *parentWidget, int type) +{ + switch (type) { + case LayoutInfo::HBox: + return new QHBoxLayout(parentWidget); + case LayoutInfo::VBox: + return new QVBoxLayout(parentWidget); + case LayoutInfo::Grid: + return new QGridLayout(parentWidget); + case LayoutInfo::Form: + return new QFormLayout(parentWidget); + default: + Q_ASSERT(0); + break; + } + return 0; +} + + +/*! Creates a layout on the widget \a widget of the type \a type + which can be \c HBox, \c VBox or \c Grid. +*/ + +QLayout *WidgetFactory::createLayout(QWidget *widget, QLayout *parentLayout, int type) const // ### (sizepolicy) +{ + QDesignerMetaDataBaseInterface *metaDataBase = core()->metaDataBase(); + + if (parentLayout == 0) { + QWidget *page = containerOfWidget(widget); + if (page) { + widget = page; + } else { + const QString msg = tr("The current page of the container '%1' (%2) could not be determined while creating a layout." +"This indicates an inconsistency in the ui-file, probably a layout being constructed on a container widget.").arg(widget->objectName()).arg(classNameOf(core(), widget)); + designerWarning(msg); + } + } + + Q_ASSERT(metaDataBase->item(widget) != 0); // ensure the widget is managed + + if (parentLayout == 0 && metaDataBase->item(widget->layout()) == 0) { + parentLayout = widget->layout(); + } + + QWidget *parentWidget = parentLayout != 0 ? 0 : widget; + + QLayout *layout = createUnmanagedLayout(parentWidget, type); + metaDataBase->add(layout); // add the layout in the MetaDataBase + + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core()->extensionManager(), layout); + + sheet->setChanged(sheet->indexOf(m_strings.m_objectName), true); + if (widget->inherits("Q3GroupBox")) { + layout->setContentsMargins(widget->style()->pixelMetric(QStyle::PM_LayoutLeftMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutTopMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutRightMargin), + widget->style()->pixelMetric(QStyle::PM_LayoutBottomMargin)); + QGridLayout *grid = qobject_cast<QGridLayout *>(layout); + if (grid) { + grid->setHorizontalSpacing(-1); + grid->setVerticalSpacing(-1); + } else { + layout->setSpacing(-1); + } + layout->setAlignment(Qt::AlignTop); + // Just to ensure; before 4.3 orientation property was always set (now only for QSplitter class). + // Calling Q3GroupBox::setOrientation() invoked in turn setSpacing(0). Below fixes that + widget->layout()->setSpacing(-1); + } else if (widget->inherits("QLayoutWidget")) { + sheet->setProperty(sheet->indexOf(m_strings.m_leftMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_topMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_rightMargin), 0); + sheet->setProperty(sheet->indexOf(m_strings.m_bottomMargin), 0); + } + + if (sheet) { + const int index = sheet->indexOf(m_strings.m_alignment); + if (index != -1) + sheet->setChanged(index, true); + } + + if (metaDataBase->item(widget->layout()) == 0) { + Q_ASSERT(layout->parent() == 0); + QBoxLayout *box = qobject_cast<QBoxLayout*>(widget->layout()); + if (!box) { // we support only unmanaged box layouts + const QString msg = tr("Attempt to add a layout to a widget '%1' (%2) which already has an unmanaged layout of type %3.\n" + "This indicates an inconsistency in the ui-file."). + arg(widget->objectName()).arg(classNameOf(core(), widget)).arg(classNameOf(core(), widget->layout())); + designerWarning(msg); + return 0; + } + box->addLayout(layout); + } + + return layout; +} + +/*! Returns the widget into which children should be inserted when \a + w is a container known to designer. + + Usually, it is \a w itself, but there are exceptions (for example, a + tabwidget is known to designer as a container, but the child + widgets should be inserted into the current page of the + tabwidget. In this case, the current page of + the tabwidget would be returned.) + */ +QWidget* WidgetFactory::containerOfWidget(QWidget *w) const +{ + if (QDesignerContainerExtension *container = qt_extension<QDesignerContainerExtension*>(core()->extensionManager(), w)) + return container->widget(container->currentIndex()); + + return w; +} + +/*! Returns the actual designer widget of the container \a w. This is + normally \a w itself, but it might be a parent or grand parent of \a w + (for example, when working with a tabwidget and \a w is the container which + contains and layouts children, but the actual widget known to + designer is the tabwidget which is the parent of \a w. In this case, + the tabwidget would be returned.) +*/ + +QWidget* WidgetFactory::widgetOfContainer(QWidget *w) const +{ + // ### cleanup + if (!w) + return 0; + if (w->parentWidget() && w->parentWidget()->parentWidget() && + w->parentWidget()->parentWidget()->parentWidget() && + qobject_cast<QToolBox*>(w->parentWidget()->parentWidget()->parentWidget())) + return w->parentWidget()->parentWidget()->parentWidget(); + + while (w != 0) { + if (core()->widgetDataBase()->isContainer(w) || + (w && qobject_cast<QDesignerFormWindowInterface*>(w->parentWidget()))) + return w; + + w = w->parentWidget(); + } + + return w; +} + +QDesignerFormEditorInterface *WidgetFactory::core() const +{ + return m_core; +} + +// Necessary initializations for form editor/preview objects +void WidgetFactory::initializeCommon(QWidget *widget) const +{ + // Apply style + if (m_currentStyle) + widget->setStyle(m_currentStyle); + // Prevent the wizard from emulating the Windows Vista Theme. + // This theme (in both Aero and Basic mode) is tricky to + // emulate properly in designer due to 1) the manipulation of the non-client area of + // the top-level window, and 2) the upper-right location of the Back button. + // The wizard falls back to QWizard::ModernStyle whenever the Vista theme + // would normally apply. + if (QWizard *wizard = qobject_cast<QWizard *>(widget)) { + wizard->setProperty("_q_wizard_vista_off", QVariant(true)); + return; + } +} + +// Necessary initializations for preview objects +void WidgetFactory::initializePreview(QWidget *widget) const +{ + + if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(widget)) { + QStackedWidgetPreviewEventFilter::install(stackedWidget); // Add browse button only. + return; + } +} + +// Necessary initializations for form editor objects +void WidgetFactory::initialize(QObject *object) const +{ + // Indicate that this is a form object (for QDesignerFormWindowInterface::findFormWindow) + object->setProperty(formEditorDynamicProperty, QVariant(true)); + QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(m_core->extensionManager(), object); + if (!sheet) + return; + + sheet->setChanged(sheet->indexOf(m_strings.m_objectName), true); + + if (!object->isWidgetType()) { + if (qobject_cast<QAction*>(object)) + sheet->setChanged(sheet->indexOf(m_strings.m_text), true); + return; + } + + QWidget *widget = static_cast<QWidget*>(object); + const bool isMenu = qobject_cast<QMenu*>(widget); + const bool isMenuBar = !isMenu && qobject_cast<QMenuBar*>(widget); + + widget->setAttribute(Qt::WA_TransparentForMouseEvents, false); + widget->setFocusPolicy((isMenu || isMenuBar) ? Qt::StrongFocus : Qt::NoFocus); + + if (!isMenu) + sheet->setChanged(sheet->indexOf(m_strings.m_geometry), true); + + if (qobject_cast<Spacer*>(widget)) { + sheet->setChanged(sheet->indexOf(m_strings.m_spacerName), true); + return; + } + + const int o = sheet->indexOf(m_strings.m_orientation); + if (o != -1 && widget->inherits("QSplitter")) + sheet->setChanged(o, true); + + if (QToolBar *toolBar = qobject_cast<QToolBar*>(widget)) { + ToolBarEventFilter::install(toolBar); + sheet->setVisible(sheet->indexOf(m_strings.m_windowTitle), true); + toolBar->setFloatable(false); // prevent toolbars from being dragged off + return; + } + + if (qobject_cast<QDockWidget*>(widget)) { + sheet->setVisible(sheet->indexOf(m_strings.m_windowTitle), true); + sheet->setVisible(sheet->indexOf(m_strings.m_windowIcon), true); + return; + } + + if (isMenu) { + sheet->setChanged(sheet->indexOf(m_strings.m_title), true); + return; + } + // helpers + if (QToolBox *toolBox = qobject_cast<QToolBox*>(widget)) { + QToolBoxHelper::install(toolBox); + return; + } + if (QStackedWidget *stackedWidget = qobject_cast<QStackedWidget*>(widget)) { + QStackedWidgetEventFilter::install(stackedWidget); + return; + } + if (QTabWidget *tabWidget = qobject_cast<QTabWidget*>(widget)) { + QTabWidgetEventFilter::install(tabWidget); + return; + } + // Prevent embedded line edits from getting focus + if (QAbstractSpinBox *asb = qobject_cast<QAbstractSpinBox *>(widget)) { + if (QLineEdit *lineEdit = static_cast<FriendlySpinBox*>(asb)->lineEdit()) + lineEdit->setFocusPolicy(Qt::NoFocus); + return; + } + if (QComboBox *cb = qobject_cast<QComboBox *>(widget)) { + if (QFontComboBox *fcb = qobject_cast<QFontComboBox *>(widget)) { + fcb->lineEdit()->setFocusPolicy(Qt::NoFocus); // Always present + return; + } + cb->installEventFilter(new ComboEventFilter(cb)); + return; + } + if (QWizard *wz = qobject_cast<QWizard *>(widget)) { + WizardPageChangeWatcher *pw = new WizardPageChangeWatcher(wz); + Q_UNUSED(pw); + } +} + +static inline QString classNameOfStyle(const QStyle *s) +{ + return QLatin1String(s->metaObject()->className()); +} + +QString WidgetFactory::styleName() const +{ + return classNameOfStyle(style()); +} + +static inline bool isApplicationStyle(const QString &styleName) +{ + return styleName.isEmpty() || styleName == classNameOfStyle(qApp->style()); +} + +void WidgetFactory::setStyleName(const QString &styleName) +{ + m_currentStyle = isApplicationStyle(styleName) ? static_cast<QStyle*>(0) : getStyle(styleName); +} + +QStyle *WidgetFactory::style() const +{ + return m_currentStyle ? m_currentStyle : qApp->style(); +} + +QStyle *WidgetFactory::getStyle(const QString &styleName) +{ + if (isApplicationStyle(styleName)) + return qApp->style(); + + StyleCache::iterator it = m_styleCache.find(styleName); + if (it == m_styleCache.end()) { + QStyle *style = QStyleFactory::create(styleName); + if (!style) { + const QString msg = tr("Cannot create style '%1'.").arg(styleName); + designerWarning(msg); + return 0; + } + it = m_styleCache.insert(styleName, style); + } + return it.value(); +} + +void WidgetFactory::applyStyleTopLevel(const QString &styleName, QWidget *w) +{ + if (QStyle *style = getStyle(styleName)) + applyStyleToTopLevel(style, w); +} + +void WidgetFactory::applyStyleToTopLevel(QStyle *style, QWidget *widget) +{ + const QPalette standardPalette = style->standardPalette(); + if (widget->style() == style && widget->palette() == standardPalette) + return; + + widget->setStyle(style); + widget->setPalette(standardPalette); + const QWidgetList lst = qFindChildren<QWidget*>(widget); + const QWidgetList::const_iterator cend = lst.constEnd(); + for (QWidgetList::const_iterator it = lst.constBegin(); it != cend; ++it) + (*it)->setStyle(style); +} + +// Check for 'interactor' click on a tab bar, +// which can appear within a QTabWidget or as a standalone widget. + +static bool isTabBarInteractor(const QTabBar *tabBar) +{ + // Tabbar embedded in Q(Designer)TabWidget, ie, normal tab widget case + if (qobject_cast<const QTabWidget*>(tabBar->parentWidget())) + return true; + + // Standalone tab bar on the form. Return true for tab rect areas + // only to allow the user to select the tab bar by clicking outside the actual tabs. + const int count = tabBar->count(); + if (count == 0) + return false; + + // click into current tab: No Interaction + const int currentIndex = tabBar->currentIndex(); + const QPoint pos = tabBar->mapFromGlobal(QCursor::pos()); + if (tabBar->tabRect(currentIndex).contains(pos)) + return false; + + // click outside: No Interaction + const QRect geometry = QRect(QPoint(0, 0), tabBar->size()); + if (!geometry.contains(pos)) + return false; + // click into another tab: Let's interact, switch tabs. + for (int i = 0; i < count; i++) + if (tabBar->tabRect(i).contains(pos)) + return true; + return false; +} + +bool WidgetFactory::isPassiveInteractor(QWidget *widget) +{ + static const QString qtPassive = QLatin1String("__qt__passive_"); + if (m_lastPassiveInteractor != 0 && (QWidget*)(*m_lastPassiveInteractor) == widget) + return m_lastWasAPassiveInteractor; + + if (QApplication::activePopupWidget() || widget == 0) // if a popup is open, we have to make sure that this one is closed, else X might do funny things + return true; + + m_lastWasAPassiveInteractor = false; + (*m_lastPassiveInteractor) = widget; + + if (const QTabBar *tabBar = qobject_cast<const QTabBar*>(widget)) { + if (isTabBarInteractor(tabBar)) + m_lastWasAPassiveInteractor = true; + return m_lastWasAPassiveInteractor; + } else if (qobject_cast<QSizeGrip*>(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast<QMdiSubWindow*>(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast<QAbstractButton*>(widget) && (qobject_cast<QTabBar*>(widget->parent()) || qobject_cast<QToolBox*>(widget->parent()))) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast<QMenuBar*>(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast<QToolBar*>(widget)) + return (m_lastWasAPassiveInteractor = true); + else if (qobject_cast<QScrollBar*>(widget)) { + // A scroll bar is an interactor on a QAbstractScrollArea only. + if (const QWidget *parent = widget->parentWidget()) { + const QString objectName = parent->objectName(); + static const QString scrollAreaVContainer = QLatin1String("qt_scrollarea_vcontainer"); + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + void formWindowAdded(QDesignerFormWindowInterface *formWindow); + static const QString scrollAreaHContainer = QLatin1String("qt_scrollarea_hcontainer"); + if (objectName == scrollAreaVContainer || objectName == scrollAreaHContainer) { + m_lastWasAPassiveInteractor = true; + return m_lastWasAPassiveInteractor; + } + } + } else if (qstrcmp(widget->metaObject()->className(), "QDockWidgetTitle") == 0) + return (m_lastWasAPassiveInteractor = true); + else if (qstrcmp(widget->metaObject()->className(), "QWorkspaceTitleBar") == 0) + return (m_lastWasAPassiveInteractor = true); + else if (widget->objectName().startsWith(qtPassive)) + return (m_lastWasAPassiveInteractor = true); + return m_lastWasAPassiveInteractor; +} + +void WidgetFactory::formWindowAdded(QDesignerFormWindowInterface *formWindow) +{ + setFormWindowStyle(formWindow); +} + +void WidgetFactory::activeFormWindowChanged(QDesignerFormWindowInterface *formWindow) +{ + setFormWindowStyle(formWindow); +} + +void WidgetFactory::setFormWindowStyle(QDesignerFormWindowInterface *formWindow) +{ + if (FormWindowBase *fwb = qobject_cast<FormWindowBase *>(formWindow)) + setStyleName(fwb->styleName()); +} + +bool WidgetFactory::isFormEditorObject(const QObject *object) +{ + return object->property(formEditorDynamicProperty).isValid(); +} +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#include "widgetfactory.moc" diff --git a/tools/designer/src/lib/shared/widgetfactory_p.h b/tools/designer/src/lib/shared/widgetfactory_p.h new file mode 100644 index 0000000..1a053ed --- /dev/null +++ b/tools/designer/src/lib/shared/widgetfactory_p.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef WIDGETFACTORY_H +#define WIDGETFACTORY_H + +#include "shared_global_p.h" +#include "pluginmanager_p.h" + +#include <QtDesigner/QDesignerWidgetFactoryInterface> + +#include <QtCore/QMap> +#include <QtCore/QHash> +#include <QtCore/QVariant> +#include <QtCore/QPointer> + +QT_BEGIN_NAMESPACE + +class QObject; +class QWidget; +class QLayout; +class QDesignerFormEditorInterface; +class QDesignerCustomWidgetInterface; +class QDesignerFormWindowInterface; +class QStyle; + +namespace qdesigner_internal { + +class QDESIGNER_SHARED_EXPORT WidgetFactory: public QDesignerWidgetFactoryInterface +{ + Q_OBJECT +public: + explicit WidgetFactory(QDesignerFormEditorInterface *core, QObject *parent = 0); + ~WidgetFactory(); + + virtual QWidget* containerOfWidget(QWidget *widget) const; + virtual QWidget* widgetOfContainer(QWidget *widget) const; + + QObject* createObject(const QString &className, QObject* parent) const; + + virtual QWidget *createWidget(const QString &className, QWidget *parentWidget) const; + virtual QLayout *createLayout(QWidget *widget, QLayout *layout, int type) const; + + virtual bool isPassiveInteractor(QWidget *widget); + virtual void initialize(QObject *object) const; + void initializeCommon(QWidget *object) const; + void initializePreview(QWidget *object) const; + + + virtual QDesignerFormEditorInterface *core() const; + + static QString classNameOf(QDesignerFormEditorInterface *core, const QObject* o); + + QDesignerFormWindowInterface *currentFormWindow(QDesignerFormWindowInterface *fw); + + static QLayout *createUnmanagedLayout(QWidget *parentWidget, int type); + + // The widget factory maintains a cache of styles which it owns. + QString styleName() const; + void setStyleName(const QString &styleName); + + /* Return a cached style matching the name or QApplication's style if + * it is the default. */ + QStyle *getStyle(const QString &styleName); + // Return the current style used by the factory. This either a cached one + // or QApplication's style */ + QStyle *style() const; + + // Apply one of the cached styles or QApplication's style to a toplevel widget. + void applyStyleTopLevel(const QString &styleName, QWidget *w); + static void applyStyleToTopLevel(QStyle *style, QWidget *widget); + + // Return whether object was created by the factory for the form editor. + static bool isFormEditorObject(const QObject *o); + + // Boolean dynamic property to set on widgets to prevent custom + // styles from interfering + static const char *disableStyleCustomPaintingPropertyC; + +public slots: + void loadPlugins(); + +private slots: + void activeFormWindowChanged(QDesignerFormWindowInterface *formWindow); + void formWindowAdded(QDesignerFormWindowInterface *formWindow); + +private: + struct Strings { // Reduce string allocations by storing predefined strings + Strings(); + const QString m_alignment; + const QString m_bottomMargin; + const QString m_geometry; + const QString m_leftMargin; + const QString m_line; + const QString m_objectName; + const QString m_spacerName; + const QString m_orientation; + const QString m_q3WidgetStack; + const QString m_qAction; + const QString m_qButtonGroup; + const QString m_qAxWidget; + const QString m_qDialog; + const QString m_qDockWidget; + const QString m_qLayoutWidget; + const QString m_qMenu; + const QString m_qMenuBar; + const QString m_qWidget; + const QString m_rightMargin; + const QString m_sizeHint; + const QString m_spacer; + const QString m_text; + const QString m_title; + const QString m_topMargin; + const QString m_windowIcon; + const QString m_windowTitle; + }; + + QWidget* createCustomWidget(const QString &className, QWidget *parentWidget, bool *creationError) const; + QDesignerFormWindowInterface *findFormWindow(QWidget *parentWidget) const; + void setFormWindowStyle(QDesignerFormWindowInterface *formWindow); + + const Strings m_strings; + QDesignerFormEditorInterface *m_core; + typedef QMap<QString, QDesignerCustomWidgetInterface*> CustomWidgetFactoryMap; + CustomWidgetFactoryMap m_customFactory; + QDesignerFormWindowInterface *m_formWindow; + + // Points to the cached style or 0 if the default (qApp) is active + QStyle *m_currentStyle; + typedef QHash<QString, QStyle *> StyleCache; + StyleCache m_styleCache; + + static QPointer<QWidget> *m_lastPassiveInteractor; + static bool m_lastWasAPassiveInteractor; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif // WIDGETFACTORY_H diff --git a/tools/designer/src/lib/shared/zoomwidget.cpp b/tools/designer/src/lib/shared/zoomwidget.cpp new file mode 100644 index 0000000..f4ab48e --- /dev/null +++ b/tools/designer/src/lib/shared/zoomwidget.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "zoomwidget_p.h" + +#include <QtGui/QGraphicsScene> +#include <QtGui/QGraphicsProxyWidget> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QActionGroup> +#include <QtGui/QContextMenuEvent> +#include <QtGui/QScrollBar> + +#include <QtCore/QTextStream> +#include <QtCore/qmath.h> +#include <QtCore/QDebug> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +typedef QList<QAction*> ActionList; +typedef QList<QGraphicsItem *> GraphicsItemList; + +enum { debugZoomWidget = 0 }; + +static const int menuZoomList[] = { 100, 25, 50, 75, 125, 150 , 175, 200 }; + +static inline QSize qCeiling(const QSizeF &s) +{ + return QSize(qCeil(s.width()), qCeil(s.height())); +} + +namespace qdesigner_internal { + +// ---------- ZoomMenu + +ZoomMenu::ZoomMenu(QObject *parent) : + QObject(parent), + m_menuActions(new QActionGroup(this)) +{ + connect(m_menuActions, SIGNAL(triggered(QAction*)), this, SLOT(slotZoomMenu(QAction*))); + const int nz = sizeof(menuZoomList)/sizeof(int); + for (int i = 0; i < nz; i++) { + const int zoom = menuZoomList[i]; + //: Zoom factor + QAction *a = m_menuActions->addAction(tr("%1 %").arg(zoom)); + a->setCheckable(true); + a->setData(QVariant(zoom)); + if (zoom == 100) + a->setChecked(true); + m_menuActions->addAction(a); + } +} + +int ZoomMenu::zoomOf(const QAction *a) +{ + return a->data().toInt(); +} + +void ZoomMenu::addActions(QMenu *m) +{ + const ActionList za = m_menuActions->actions(); + const ActionList::const_iterator cend = za.constEnd(); + for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) { + m->addAction(*it); + if (zoomOf(*it) == 100) + m->addSeparator(); + } +} + +int ZoomMenu::zoom() const +{ + return m_menuActions->checkedAction()->data().toInt(); +} + +void ZoomMenu::setZoom(int percent) +{ + const ActionList za = m_menuActions->actions(); + const ActionList::const_iterator cend = za.constEnd(); + for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) + if (zoomOf(*it) == percent) { + (*it)->setChecked(true); + return; + } +} + +void ZoomMenu::slotZoomMenu(QAction *a) +{ + emit zoomChanged(zoomOf(a)); +} + +QList<int> ZoomMenu::zoomValues() +{ + QList<int> rc; + const int nz = sizeof(menuZoomList)/sizeof(int); + for (int i = 0; i < nz; i++) + rc.push_back(menuZoomList[i]); + return rc; +} + +// --------- ZoomView +ZoomView::ZoomView(QWidget *parent) : + QGraphicsView(parent), + m_scene(new QGraphicsScene(this)), + m_zoom(100), + m_zoomFactor(1.0), + m_zoomContextMenuEnabled(false), + m_autoScrollSuppressed(true), + m_zoomMenu(0) +{ + setFrameShape(QFrame::NoFrame); + setScene(m_scene); + if (debugZoomWidget) + qDebug() << "scene" << m_scene->sceneRect(); + +} + +int ZoomView::zoom() const +{ + return m_zoom; +} + +void ZoomView::scrollToOrigin() +{ + const QPoint origin(0 ,0); + const QPoint current = scrollPosition(); + if (current != origin) { + if (debugZoomWidget) + qDebug() << "ZoomView::scrollToOrigin from " << current; + setScrollPosition(origin); + } +} + +void ZoomView::setZoom(int percent) +{ + if (debugZoomWidget) + qDebug() << "ZoomView::setZoom" << percent; + + if (m_zoom == percent) + return; + + m_zoom = percent; + const qreal hundred = 100.0; + m_zoomFactor = static_cast<qreal>(m_zoom) / hundred; + + applyZoom(); + if (m_zoomMenu) // Do not force them into existence + m_zoomMenu->setZoom(m_zoom); + + resetTransform(); + scale(m_zoomFactor, m_zoomFactor); + if (m_autoScrollSuppressed) + scrollToOrigin(); +} + +void ZoomView::applyZoom() +{ +} + +qreal ZoomView::zoomFactor() const +{ + return m_zoomFactor; +} + +bool ZoomView::isZoomContextMenuEnabled() const +{ + return m_zoomContextMenuEnabled; +} + +void ZoomView::setZoomContextMenuEnabled(bool e) +{ + m_zoomContextMenuEnabled = e; +} + +bool ZoomView::isAutoScrollSuppressed() const +{ + return m_autoScrollSuppressed; +} + +void ZoomView::setAutoScrollSuppressed(bool s) +{ + m_autoScrollSuppressed = s; +} + +ZoomMenu *ZoomView::zoomMenu() +{ + if (!m_zoomMenu) { + m_zoomMenu = new ZoomMenu(this); + m_zoomMenu->setZoom(m_zoom); + connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoom(int))); + } + return m_zoomMenu; +} + +void ZoomView::contextMenuEvent(QContextMenuEvent *event) +{ + if (debugZoomWidget > 1) + qDebug() << "ZoomView::contextMenuEvent" << event->pos() << event->globalPos() << zoom() << '%'; + + if (m_zoomContextMenuEnabled) { + showContextMenu(event->globalPos()); + } else { + QGraphicsView::contextMenuEvent(event); + } +} + +void ZoomView::showContextMenu(const QPoint &globalPos) +{ + QMenu menu; + zoomMenu()->addActions(&menu); + if (debugZoomWidget) { + menu.addSeparator(); + QAction *da = menu.addAction(QLatin1String("Dump")); + connect(da, SIGNAL(triggered()), this, SLOT(dump())); + } + menu.exec(globalPos); +} + +QPoint ZoomView::scrollPosition() const +{ + return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value()); +} + +void ZoomView::setScrollPosition(const QPoint& pos) +{ + horizontalScrollBar()->setValue(pos.x()); + verticalScrollBar()->setValue(pos.y()); +} + +// -------------- ZoomProxyWidget +ZoomProxyWidget::ZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) : + QGraphicsProxyWidget(parent, wFlags) +{ +} + +QVariant ZoomProxyWidget::itemChange(GraphicsItemChange change, const QVariant &value) +{ + switch (change) { + case ItemPositionChange: { + const QPointF newPos = value.toPointF(); + const QPointF desiredPos = QPointF(0, 0); + if (newPos != desiredPos && debugZoomWidget) + qDebug() << "ZoomProxyWidget::itemChange: refusing " << newPos; + return desiredPos; + } + default: + break; + } + return QGraphicsProxyWidget::itemChange(change, value); +} + +/* ZoomedEventFilterRedirector: Event filter for the zoomed widget. + * It redirects the events to another handler of ZoomWidget as its + * base class QScrollArea also implements eventFilter() for its viewport. */ + +static const char *zoomedEventFilterRedirectorNameC = "__qt_ZoomedEventFilterRedirector"; + +class ZoomedEventFilterRedirector : public QObject { + Q_DISABLE_COPY(ZoomedEventFilterRedirector) + +public: + explicit ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent); + virtual bool eventFilter(QObject *watched, QEvent *event); + +private: + ZoomWidget *m_zw; +}; + +ZoomedEventFilterRedirector::ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent) : + QObject(parent), + m_zw(zw) +{ + setObjectName(QLatin1String(zoomedEventFilterRedirectorNameC)); +} + +bool ZoomedEventFilterRedirector::eventFilter(QObject *watched, QEvent *event) +{ + return m_zw->zoomedEventFilter(watched, event); +} + + +// --------- ZoomWidget + +ZoomWidget::ZoomWidget(QWidget *parent) : + ZoomView(parent), + m_proxy(0), + m_viewResizeBlocked(false), + m_widgetResizeBlocked(false), + m_widgetZoomContextMenuEnabled(false) +{ + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); +} + +void ZoomWidget::setWidget(QWidget *w, Qt::WindowFlags wFlags) +{ + if (debugZoomWidget) + qDebug() << "ZoomWidget::setWidget" << w << bin << wFlags; + + if (m_proxy) { + scene().removeItem(m_proxy); + if (QWidget *w = m_proxy->widget()) { + // remove the event filter + if (QObject *evf = qFindChild<QObject*>(w, QLatin1String(zoomedEventFilterRedirectorNameC))) + w->removeEventFilter(evf); + } + m_proxy->deleteLater(); + } + // Set window flags on the outer proxy for them to take effect + m_proxy = createProxyWidget(0, Qt::Window); + m_proxy->setWidget(w); + + m_proxy->setWindowFlags(wFlags); + scene().addItem(m_proxy); + w->installEventFilter(new ZoomedEventFilterRedirector(this, w)); + resizeToWidgetSize(); // Do manually for new widget + m_proxy->show(); +} + +bool ZoomWidget::isWidgetZoomContextMenuEnabled() const +{ + return m_widgetZoomContextMenuEnabled; +} +void ZoomWidget::setWidgetZoomContextMenuEnabled(bool e) +{ + m_widgetZoomContextMenuEnabled = e; +} + +QSize ZoomWidget::viewPortMargin() const +{ + return QSize(0, 0); +} + +QSizeF ZoomWidget::widgetDecorationSizeF() const +{ + qreal left, top, right, bottom; + m_proxy->getWindowFrameMargins (&left, &top, &right, &bottom); + const QSizeF rc = QSizeF(left + right, top + bottom); + return rc; +} + +QSize ZoomWidget::widgetSize() const +{ + if (m_proxy) + return m_proxy->widget()->size(); + return QSize(0, 0); +} + +/* Convert widget size to QGraphicsView size. + * Watch out for limits (0, QWIDGETSIZE_MAX); just pass them on */ + +QSize ZoomWidget::widgetSizeToViewSize(const QSize &s, bool *ptrToValid) const +{ + const QSize vpMargin = viewPortMargin(); + const QSizeF deco = widgetDecorationSizeF(); + const int width = s.width(); + + QSize rc = s; + bool valid = false; + if (width != 0 && width != QWIDGETSIZE_MAX) { + valid = true; + rc.setWidth(vpMargin.width() + qCeil(deco.width() + zoomFactor() * static_cast<qreal>(width))); + } + + const int height = s.height(); + if (height != 0 && height != QWIDGETSIZE_MAX) { + valid = true; + rc.setHeight(vpMargin.height() + qCeil(deco.height() + zoomFactor() * static_cast<qreal>(height))); + } + + if (ptrToValid) + *ptrToValid = valid; + + return rc; +} + +// On changing zoom: Make QGraphicsView big enough to hold the widget +void ZoomWidget::resizeToWidgetSize() +{ + if (!m_proxy) + return; + + m_viewResizeBlocked = true; + // Convert size, apply transformed min/max size if applicable + const QSize wsize = widgetSize(); + const QSize viewSize = widgetSizeToViewSize(wsize); + + bool hasMinimumSize = false; + const QSize minimumSize = m_proxy->widget()->minimumSize(); + const QSize viewMinimumSize = widgetSizeToViewSize(minimumSize, &hasMinimumSize); + + bool hasMaximumSize = false; + const QSize maximumSize = m_proxy->widget()->maximumSize(); + const QSize viewMaximumSize = widgetSizeToViewSize(maximumSize, &hasMaximumSize); + + if (debugZoomWidget) { + qDebug() + << "ZoomWidget::resizeToWidgetSize()\n" + << "Widget: " << wsize << "(scaled)" << (wsize * zoomFactor()) << " Min/Max" << minimumSize << maximumSize << '\n' + << " View: " << viewSize << hasMinimumSize << viewMinimumSize << hasMaximumSize << viewMaximumSize; + } + // Apply + if (hasMinimumSize) + setMinimumSize(viewMinimumSize); + if (hasMaximumSize) + setMaximumSize(viewMaximumSize); + // now resize + doResize(viewSize); + if (debugZoomWidget) + qDebug() << "ZoomWidget::resizeToWidgetSize(): resulting view size" << size(); + m_viewResizeBlocked = false; +} + +void ZoomWidget::applyZoom() +{ + resizeToWidgetSize(); +} + +/* virtual */ void ZoomWidget::doResize(const QSize &s) +{ + if (debugZoomWidget > 1) + qDebug() << ">ZoomWidget::doResize() " << s; + resize(s); +} + +void ZoomWidget::resizeEvent(QResizeEvent *event) +{ + /* QGraphicsView Resized from outside: Adapt widget. For some reason, + * the size passed in the event is not to be trusted. This might be due + * to some QScrollArea event fiddling. Have QScrollArea resize first + * and the use the size ZoomView::resizeEvent(event); */ + if (m_proxy && !m_viewResizeBlocked) { + if (debugZoomWidget > 1) + qDebug() << ">ZoomWidget (" << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size(); + const QSizeF newViewPortSize = size() - viewPortMargin(); + const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF(); + m_widgetResizeBlocked = true; + m_proxy->widget()->resize(widgetSizeF.toSize()); + scrollToOrigin(); + m_widgetResizeBlocked = false; + } +} + +QSize ZoomWidget::minimumSizeHint() const +{ + if (!m_proxy) + return QGraphicsView::minimumSizeHint(); + + const QSizeF wmsh = m_proxy->widget()->minimumSizeHint(); + const QSize rc = viewPortMargin() + (wmsh * zoomFactor()).toSize(); + if (debugZoomWidget > 1) + qDebug() << "minimumSizeHint()" << rc; + return rc; +} + +QSize ZoomWidget::sizeHint() const +{ + if (!m_proxy) + return QGraphicsView::sizeHint(); + + const QSizeF wsh = m_proxy->widget()->sizeHint(); + const QSize rc = viewPortMargin() + (wsh * zoomFactor()).toSize(); + if (debugZoomWidget > 1) + qDebug() << "sizeHint()" << rc; + return rc; +} + +bool ZoomWidget::zoomedEventFilter(QObject * /*watched*/, QEvent *event) +{ + switch (event->type()) { + case QEvent::KeyPress: + if (debugZoomWidget) { // Debug helper: Press 'D' on the zoomed widget + const QKeyEvent *kevent = static_cast<QKeyEvent*>(event); + if (kevent->key() == Qt::Key_D) + dump(); + } + break; + case QEvent::Resize: + if (debugZoomWidget > 1) { + const QResizeEvent *re = static_cast<const QResizeEvent *>(event); + qDebug() << "ZoomWidget::zoomedEventFilter" << re->oldSize() << re->size() << " at " << m_proxy->widget()->geometry(); + } + if (!m_widgetResizeBlocked) + resizeToWidgetSize(); + + break; + case QEvent::ContextMenu: + if (m_widgetZoomContextMenuEnabled) { + // Calculate global position from scaled + QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event); + const QPointF origin = mapToGlobal(QPoint(0, 0)) - scrollPosition(); + const QPointF pos = QPointF(origin + (QPointF(ce->pos()) * zoomFactor())); + showContextMenu(pos.toPoint()); + ce->accept(); + return true; + } + break; + default: + break; + } + return false; +} + +void ZoomWidget::setItemAcceptDrops(bool) +{ + if (m_proxy) + m_proxy->setAcceptDrops(true); +} + +bool ZoomWidget::itemAcceptDrops() const +{ + return m_proxy ? m_proxy->acceptDrops() : false; +} + + // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget +QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const +{ + return new ZoomProxyWidget(parent, wFlags); +} + +void ZoomWidget::dump() const +{ + + qDebug() << "ZoomWidget " << geometry() << " Viewport " << viewport()->geometry() + << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect(); + if (m_proxy) { + qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size() + << "\nProxy size hint" + << m_proxy->effectiveSizeHint(Qt::MinimumSize) + << m_proxy->effectiveSizeHint(Qt::PreferredSize) + << m_proxy->effectiveSizeHint(Qt::MaximumSize) + << "\nMatrix: " << m_proxy->matrix() + << "\nWidget: " << m_proxy->widget()->geometry() + << "scaled" << (zoomFactor() * m_proxy->widget()->size()); + } +} + +} // namespace qdesigner_internal + +QT_END_NAMESPACE diff --git a/tools/designer/src/lib/shared/zoomwidget_p.h b/tools/designer/src/lib/shared/zoomwidget_p.h new file mode 100644 index 0000000..fe850f7 --- /dev/null +++ b/tools/designer/src/lib/shared/zoomwidget_p.h @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the Qt Designer of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef ZOOMWIDGET_H +#define ZOOMWIDGET_H + +#include "shared_global_p.h" + +#include <QtGui/QGraphicsView> +#include <QtGui/QGraphicsProxyWidget> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE + +class QGraphicsScene; +class QMenu; +class QAction; +class QActionGroup; + +namespace qdesigner_internal { + +// A checkable zoom menu action group. Operates in percent. + +class QDESIGNER_SHARED_EXPORT ZoomMenu : public QObject { + Q_OBJECT + Q_DISABLE_COPY(ZoomMenu) + +public: + ZoomMenu(QObject *parent = 0); + void addActions(QMenu *m); + + int zoom() const; + + // Return a list of available zoom values. + static QList<int> zoomValues(); + +public slots: + void setZoom(int percent); + +signals: + void zoomChanged(int); + +private slots: + void slotZoomMenu(QAction *); + +private: + static int zoomOf(const QAction *a); + + QActionGroup *m_menuActions; +}; + +/* Zoom view: A QGraphicsView with a zoom menu */ + +class QDESIGNER_SHARED_EXPORT ZoomView : public QGraphicsView +{ + Q_PROPERTY(int zoom READ zoom WRITE setZoom DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(bool zoomContextMenuEnabled READ isZoomContextMenuEnabled WRITE setZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(bool autoScrollSuppressed READ isAutoScrollSuppressed WRITE setAutoScrollSuppressed DESIGNABLE true SCRIPTABLE true) + + Q_OBJECT + Q_DISABLE_COPY(ZoomView) +public: + ZoomView(QWidget *parent = 0); + + /* Zoom in percent (for easily implementing menus) and qreal zoomFactor + * in sync */ + int zoom() const; // in percent + qreal zoomFactor() const; + + // Zoom Menu on QGraphicsView. + bool isZoomContextMenuEnabled() const; + void setZoomContextMenuEnabled(bool e); + + // Suppress scrolling when changing zoom. Default: on + bool isAutoScrollSuppressed() const; + void setAutoScrollSuppressed(bool s); + + QGraphicsScene &scene() { return *m_scene; } + const QGraphicsScene &scene() const { return *m_scene; } + + // Helpers for menu + ZoomMenu *zoomMenu(); + + QPoint scrollPosition() const; + void setScrollPosition(const QPoint& pos); + void scrollToOrigin(); + +public slots: + void setZoom(int percent); + void showContextMenu(const QPoint &globalPos); + +protected: + void contextMenuEvent(QContextMenuEvent *event); + + // Overwrite for implementing additional behaviour when doing setZoom(); + virtual void applyZoom(); + +private: + QGraphicsScene *m_scene; + int m_zoom; + qreal m_zoomFactor; + + bool m_zoomContextMenuEnabled; + bool m_autoScrollSuppressed; + bool m_resizeBlocked; + ZoomMenu *m_zoomMenu; +}; + +/* The proxy widget used in ZoomWidget. It refuses to move away from 0,0, + * This behaviour is required for Windows only. */ + +class QDESIGNER_SHARED_EXPORT ZoomProxyWidget : public QGraphicsProxyWidget { + Q_DISABLE_COPY(ZoomProxyWidget) +public: + explicit ZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0); + +protected: + virtual QVariant itemChange(GraphicsItemChange change, const QVariant &value); +}; + +/* Zoom widget: A QGraphicsView-based container for a widget that allows for + * zooming it. Communicates changes in size in the following ways: + * 1) Embedded widget wants to resize: Listen for its resize in event filter + * and resize + * 2) Zoom is changed: resize to fully display the embedded widget + * 3) Outer widget changes (by manually resizing the window: + * Pass the scaled change on to the embedded widget. + * Provides helper functions for a zoom context menu on the widget. */ + +class QDESIGNER_SHARED_EXPORT ZoomWidget : public ZoomView +{ + Q_PROPERTY(bool widgetZoomContextMenuEnabled READ isWidgetZoomContextMenuEnabled WRITE setWidgetZoomContextMenuEnabled DESIGNABLE true SCRIPTABLE true) + Q_PROPERTY(bool itemAcceptDrops READ itemAcceptDrops WRITE setItemAcceptDrops DESIGNABLE true SCRIPTABLE true) + Q_OBJECT + Q_DISABLE_COPY(ZoomWidget) + +public: + ZoomWidget(QWidget *parent = 0); + void setWidget(QWidget *w, Qt::WindowFlags wFlags = 0); + + const QGraphicsProxyWidget *proxy() const { return m_proxy; } + QGraphicsProxyWidget *proxy() { return m_proxy; } + + /* Enable the zoom Menu on the Widget (as opposed ZoomView's menu which + * is on the canvas). */ + bool isWidgetZoomContextMenuEnabled() const; + void setWidgetZoomContextMenuEnabled(bool e); + + void setItemAcceptDrops(bool); + bool itemAcceptDrops() const; + + virtual QSize minimumSizeHint() const; + virtual QSize sizeHint() const; + + bool zoomedEventFilter(QObject *watched, QEvent *event); + +public slots: + // debug state + void dump() const; + +protected: + void resizeEvent(QResizeEvent * event); + + // Overwritten from ZoomView + virtual void applyZoom(); + // Overwrite to actually perform a resize. This is required if we are in a layout. Default does resize(). + virtual void doResize(const QSize &s); + +private: + // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget + virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const; + QSize widgetSizeToViewSize(const QSize &s, bool *ptrToValid = 0) const; + + void resizeToWidgetSize(); + QSize viewPortMargin() const; + QSize widgetSize() const; + QSizeF widgetDecorationSizeF() const; + + QGraphicsProxyWidget *m_proxy; + bool m_viewResizeBlocked; + bool m_widgetResizeBlocked; + bool m_widgetZoomContextMenuEnabled; +}; + +} // namespace qdesigner_internal + +QT_END_NAMESPACE + +#endif |