qt_demoe/third/designer/lib/shared/previewmanager.cpp

944 lines
32 KiB
C++

/****************************************************************************
**
** Copyright (C) 2010 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$
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** 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.
**
** 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 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"