summaryrefslogtreecommitdiffstats
path: root/src/designer/src/lib/shared/previewmanager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/designer/src/lib/shared/previewmanager.cpp')
-rw-r--r--src/designer/src/lib/shared/previewmanager.cpp943
1 files changed, 943 insertions, 0 deletions
diff --git a/src/designer/src/lib/shared/previewmanager.cpp b/src/designer/src/lib/shared/previewmanager.cpp
new file mode 100644
index 000000000..3857b7864
--- /dev/null
+++ b/src/designer/src/lib/shared/previewmanager.cpp
@@ -0,0 +1,943 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (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 Technology Preview License Agreement accompanying
+** this package.
+**
+** 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.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@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 <QtGui/QMatrix>
+
+#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);
+}
+
+// PreviewDeviceSkin: Forwards the key events to the window and
+// provides context menu with rotation options. Derived class
+// can apply additional transformations to the skin.
+
+class PreviewDeviceSkin : public DeviceSkin
+{
+ Q_OBJECT
+public:
+ enum Direction { DirectionUp, DirectionLeft, DirectionRight };
+
+ explicit PreviewDeviceSkin(const DeviceSkinParameters &parameters, 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 *) {}
+
+private slots:
+ void slotDirection(QAction *);
+
+protected:
+ // Fit the widget in case the orientation changes (transposing screensize)
+ virtual void fitWidget(const QSize &size);
+ // Calculate the complete transformation for the skin
+ // (base class implementation provides rotation).
+ virtual QMatrix skinTransform() const;
+
+private:
+ const QSize m_screenSize;
+ Direction m_direction;
+
+ QAction *m_directionUpAction;
+ QAction *m_directionLeftAction;
+ QAction *m_directionRightAction;
+ QAction *m_closeAction;
+};
+
+PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent) :
+ DeviceSkin(parameters, parent),
+ m_screenSize(parameters.screenSize()),
+ m_direction(DirectionUp),
+ m_directionUpAction(0),
+ m_directionLeftAction(0),
+ m_directionRightAction(0),
+ m_closeAction(0)
+{
+ 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);
+ }
+}
+
+// Create a checkable action with integer data and
+// set it checked if it matches the currentState.
+static inline QAction
+ *createCheckableActionIntData(const QString &label,
+ int actionValue, int currentState,
+ QActionGroup *ag, QObject *parent)
+{
+ QAction *a = new QAction(label, parent);
+ a->setData(actionValue);
+ a->setCheckable(true);
+ if (actionValue == currentState)
+ a->setChecked(true);
+ ag->addAction(a);
+ return a;
+}
+
+void PreviewDeviceSkin::slotPopupMenu()
+{
+ QMenu menu(this);
+ // Create actions
+ if (!m_directionUpAction) {
+ QActionGroup *directionGroup = new QActionGroup(this);
+ connect(directionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotDirection(QAction*)));
+ directionGroup->setExclusive(true);
+ m_directionUpAction = createCheckableActionIntData(tr("&Portrait"), DirectionUp, m_direction, directionGroup, this);
+ //: Rotate form preview counter-clockwise
+ m_directionLeftAction = createCheckableActionIntData(tr("Landscape (&CCW)"), DirectionLeft, m_direction, directionGroup, this);
+ //: Rotate form preview clockwise
+ m_directionRightAction = createCheckableActionIntData(tr("&Landscape (CW)"), DirectionRight, m_direction, directionGroup, this);
+ m_closeAction = new QAction(tr("&Close"), this);
+ connect(m_closeAction, SIGNAL(triggered()), parentWidget(), SLOT(close()));
+ }
+ menu.addAction(m_directionUpAction);
+ menu.addAction(m_directionLeftAction);
+ menu.addAction(m_directionRightAction);
+ menu.addSeparator();
+ populateContextMenu(&menu);
+ menu.addAction(m_closeAction);
+ menu.exec(QCursor::pos());
+}
+
+void PreviewDeviceSkin::slotDirection(QAction *a)
+{
+ const Direction newDirection = static_cast<Direction>(a->data().toInt());
+ if (m_direction == newDirection)
+ return;
+ const Qt::Orientation newOrientation = newDirection == DirectionUp ? Qt::Vertical : Qt::Horizontal;
+ const Qt::Orientation oldOrientation = m_direction == DirectionUp ? Qt::Vertical : Qt::Horizontal;
+ m_direction = newDirection;
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ if (oldOrientation != newOrientation) {
+ QSize size = screenSize();
+ if (newOrientation == Qt::Horizontal)
+ size.transpose();
+ fitWidget(size);
+ }
+ setTransform(skinTransform());
+ QApplication::restoreOverrideCursor();
+}
+
+void PreviewDeviceSkin::fitWidget(const QSize &size)
+{
+ view()->setFixedSize(size);
+}
+
+QMatrix PreviewDeviceSkin::skinTransform() const
+{
+ QMatrix newTransform;
+ switch (m_direction) {
+ case DirectionUp:
+ break;
+ case DirectionLeft:
+ newTransform.rotate(270.0);
+ break;
+ case DirectionRight:
+ newTransform.rotate(90.0);
+ break;
+ }
+ return newTransform;
+}
+
+// ------------ 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 &parameters, 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);
+ virtual QMatrix skinTransform() const;
+ virtual void fitWidget(const QSize &size);
+
+private:
+ ZoomMenu *m_zoomMenu;
+ QAction *m_zoomSubMenuAction;
+ ZoomWidget *m_zoomWidget;
+};
+
+ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent) :
+ PreviewDeviceSkin(parameters, parent),
+ m_zoomMenu(new ZoomMenu(this)),
+ m_zoomSubMenuAction(0),
+ 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);
+}
+
+static inline qreal zoomFactor(int percent)
+{
+ return qreal(percent) / 100.0;
+}
+
+static inline QSize scaleSize(int zoomPercent, const QSize &size)
+{
+ return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize();
+}
+
+void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget)
+{
+ m_zoomWidget->setWidget(formWidget);
+ m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize()));
+}
+
+int ZoomablePreviewDeviceSkin::zoomPercent() const
+{
+ return m_zoomWidget->zoom();
+}
+
+void ZoomablePreviewDeviceSkin::setZoomPercent(int zp)
+{
+ if (zp == zoomPercent())
+ return;
+
+ // If not triggered by the menu itself: Update it
+ if (m_zoomMenu->zoom() != zp)
+ m_zoomMenu->setZoom(zp);
+
+ QApplication::setOverrideCursor(Qt::WaitCursor);
+ m_zoomWidget->setZoom(zp);
+ setTransform(skinTransform());
+ QApplication::restoreOverrideCursor();
+}
+
+void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu)
+{
+ if (!m_zoomSubMenuAction) {
+ m_zoomSubMenuAction = new QAction(tr("&Zoom"), this);
+ QMenu *zoomSubMenu = new QMenu;
+ m_zoomSubMenuAction->setMenu(zoomSubMenu);
+ m_zoomMenu->addActions(zoomSubMenu);
+ }
+ menu->addAction(m_zoomSubMenuAction);
+ menu->addSeparator();
+}
+
+QMatrix ZoomablePreviewDeviceSkin::skinTransform() const
+{
+ // Complete transformation consisting of base class rotation and zoom.
+ QMatrix rc = PreviewDeviceSkin::skinTransform();
+ const int zp = zoomPercent();
+ if (zp != 100) {
+ const qreal factor = zoomFactor(zp);
+ rc.scale(factor, factor);
+ }
+ return rc;
+}
+
+void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size)
+{
+ m_zoomWidget->resize(scaleSize(zoomPercent(), size));
+}
+
+// ------------- 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);
+ formWidget->setWindowTitle(title);
+
+ // 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"