tools/designer/src/lib/shared/previewmanager.cpp
changeset 0 1918ee327afb
child 4 3b1da2848fc7
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the Qt Designer of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "abstractsettings_p.h"
       
    43 #include "previewmanager_p.h"
       
    44 #include "qdesigner_formbuilder_p.h"
       
    45 #include "shared_settings_p.h"
       
    46 #include "shared_settings_p.h"
       
    47 #include "zoomwidget_p.h"
       
    48 #include "formwindowbase_p.h"
       
    49 #include "widgetfactory_p.h"
       
    50 
       
    51 #include <deviceskin.h>
       
    52 
       
    53 #include <QtDesigner/QDesignerFormWindowInterface>
       
    54 #include <QtDesigner/QDesignerFormEditorInterface>
       
    55 #include <QtDesigner/QDesignerFormWindowManagerInterface>
       
    56 
       
    57 #include <QtGui/QWidget>
       
    58 #include <QtGui/qevent.h>
       
    59 #include <QtGui/QDesktopWidget>
       
    60 #include <QtGui/QMainWindow>
       
    61 #include <QtGui/QDockWidget>
       
    62 #include <QtGui/QApplication>
       
    63 #include <QtGui/QPixmap>
       
    64 #include <QtGui/QVBoxLayout>
       
    65 #include <QtGui/QDialog>
       
    66 #include <QtGui/QMenu>
       
    67 #include <QtGui/QAction>
       
    68 #include <QtGui/QActionGroup>
       
    69 #include <QtGui/QCursor>
       
    70 #include <QtGui/QMatrix>
       
    71 
       
    72 #include <QtCore/QMap>
       
    73 #include <QtCore/QDebug>
       
    74 #include <QtCore/QSharedData>
       
    75 
       
    76 QT_BEGIN_NAMESPACE
       
    77 
       
    78 static inline int compare(const qdesigner_internal::PreviewConfiguration &pc1, const qdesigner_internal::PreviewConfiguration &pc2)
       
    79 {
       
    80     int rc = pc1.style().compare(pc2.style());
       
    81     if (rc)
       
    82         return rc;
       
    83     rc = pc1.applicationStyleSheet().compare(pc2.applicationStyleSheet());
       
    84     if (rc)
       
    85         return rc;
       
    86     return pc1.deviceSkin().compare(pc2.deviceSkin());
       
    87 }
       
    88 
       
    89 namespace {
       
    90     // ------ PreviewData (data associated with a preview window)
       
    91     struct PreviewData {
       
    92         PreviewData(const QPointer<QWidget> &widget, const  QDesignerFormWindowInterface *formWindow, const qdesigner_internal::PreviewConfiguration &pc);
       
    93         QPointer<QWidget> m_widget;
       
    94         const QDesignerFormWindowInterface *m_formWindow;
       
    95         qdesigner_internal::PreviewConfiguration m_configuration;
       
    96     };
       
    97 
       
    98     PreviewData::PreviewData(const QPointer<QWidget>& widget,
       
    99                              const QDesignerFormWindowInterface *formWindow,
       
   100                              const qdesigner_internal::PreviewConfiguration &pc) :
       
   101         m_widget(widget),
       
   102         m_formWindow(formWindow),
       
   103         m_configuration(pc)
       
   104     {
       
   105     }
       
   106 }
       
   107 
       
   108 namespace qdesigner_internal {
       
   109 
       
   110 /* In designer, we have the situation that laid-out maincontainers have
       
   111  * a geometry set (which might differ from their sizeHint()). The QGraphicsItem
       
   112  * should return that in its size hint, else such cases won't work */
       
   113 
       
   114 class DesignerZoomProxyWidget : public ZoomProxyWidget  {
       
   115     Q_DISABLE_COPY(DesignerZoomProxyWidget)
       
   116 public:
       
   117     DesignerZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
       
   118 protected:
       
   119     virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
       
   120 };
       
   121 
       
   122 DesignerZoomProxyWidget::DesignerZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) :
       
   123     ZoomProxyWidget(parent, wFlags)
       
   124 {
       
   125 }
       
   126 
       
   127 QSizeF DesignerZoomProxyWidget::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
       
   128 {
       
   129     if (const QWidget *w = widget())
       
   130             return QSizeF(w->size());
       
   131     return ZoomProxyWidget::sizeHint(which, constraint);
       
   132 }
       
   133 
       
   134 // DesignerZoomWidget which returns DesignerZoomProxyWidget in its factory function
       
   135 class DesignerZoomWidget : public ZoomWidget {
       
   136     Q_DISABLE_COPY(DesignerZoomWidget)
       
   137 public:
       
   138     DesignerZoomWidget(QWidget *parent = 0);
       
   139 private:
       
   140     virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const;
       
   141 };
       
   142 
       
   143 DesignerZoomWidget::DesignerZoomWidget(QWidget *parent) :
       
   144     ZoomWidget(parent)
       
   145 {
       
   146 }
       
   147 
       
   148 QGraphicsProxyWidget *DesignerZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const
       
   149 {
       
   150     return new DesignerZoomProxyWidget(parent, wFlags);
       
   151 }
       
   152 
       
   153 // PreviewDeviceSkin: Forwards the key events to the window and
       
   154 // provides context menu with rotation options. Derived class
       
   155 // can apply additional transformations to the skin.
       
   156 
       
   157 class PreviewDeviceSkin : public  DeviceSkin
       
   158 {
       
   159     Q_OBJECT
       
   160 public:
       
   161     enum Direction { DirectionUp, DirectionLeft,  DirectionRight };
       
   162 
       
   163     explicit PreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent);
       
   164     virtual void setPreview(QWidget *w);
       
   165     QSize screenSize() const { return  m_screenSize; }
       
   166 
       
   167 private slots:
       
   168     void slotSkinKeyPressEvent(int code, const QString& text, bool autorep);
       
   169     void slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep);
       
   170     void slotPopupMenu();
       
   171 
       
   172 protected:
       
   173     virtual void populateContextMenu(QMenu *) {}
       
   174 
       
   175 private slots:
       
   176     void slotDirection(QAction *);
       
   177 
       
   178 protected:
       
   179     // Fit the widget in case the orientation changes (transposing screensize)
       
   180     virtual void fitWidget(const QSize &size);    
       
   181     //  Calculate the complete transformation for the skin
       
   182     // (base class implementation provides rotation).
       
   183     virtual QMatrix skinTransform() const;
       
   184 
       
   185 private:
       
   186     const QSize m_screenSize;
       
   187     Direction m_direction;
       
   188 
       
   189     QAction *m_directionUpAction;
       
   190     QAction *m_directionLeftAction;
       
   191     QAction *m_directionRightAction;
       
   192     QAction *m_closeAction;
       
   193 };
       
   194 
       
   195 PreviewDeviceSkin::PreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent) :
       
   196     DeviceSkin(parameters, parent),    
       
   197     m_screenSize(parameters.screenSize()),
       
   198     m_direction(DirectionUp),
       
   199     m_directionUpAction(0),
       
   200     m_directionLeftAction(0),
       
   201     m_directionRightAction(0),
       
   202     m_closeAction(0)
       
   203 {
       
   204     connect(this, SIGNAL(skinKeyPressEvent(int,QString,bool)),
       
   205             this, SLOT(slotSkinKeyPressEvent(int,QString,bool)));
       
   206     connect(this, SIGNAL(skinKeyReleaseEvent(int,QString,bool)),
       
   207             this, SLOT(slotSkinKeyReleaseEvent(int,QString,bool)));
       
   208     connect(this, SIGNAL(popupMenu()), this, SLOT(slotPopupMenu()));
       
   209 }
       
   210 
       
   211 void PreviewDeviceSkin::setPreview(QWidget *formWidget)
       
   212 {
       
   213     formWidget->setFixedSize(m_screenSize);
       
   214     formWidget->setParent(this, Qt::SubWindow);
       
   215     formWidget->setAutoFillBackground(true);
       
   216     setView(formWidget);
       
   217 }
       
   218 
       
   219 void PreviewDeviceSkin::slotSkinKeyPressEvent(int code, const QString& text, bool autorep)
       
   220 {
       
   221     if (QWidget *focusWidget =  QApplication::focusWidget()) {
       
   222         QKeyEvent e(QEvent::KeyPress,code,0,text,autorep);
       
   223         QApplication::sendEvent(focusWidget, &e);
       
   224     }
       
   225 }
       
   226 
       
   227 void PreviewDeviceSkin::slotSkinKeyReleaseEvent(int code, const QString& text, bool autorep)
       
   228 {
       
   229     if (QWidget *focusWidget =  QApplication::focusWidget()) {
       
   230         QKeyEvent e(QEvent::KeyRelease,code,0,text,autorep);
       
   231         QApplication::sendEvent(focusWidget, &e);
       
   232     }
       
   233 }
       
   234 
       
   235 // Create a checkable action with integer data and
       
   236 // set it checked if it matches the currentState.
       
   237 static inline QAction
       
   238         *createCheckableActionIntData(const QString &label,
       
   239                                       int actionValue, int currentState,
       
   240                                       QActionGroup *ag, QObject *parent)
       
   241 {
       
   242     QAction *a = new QAction(label, parent);
       
   243     a->setData(actionValue);
       
   244     a->setCheckable(true);
       
   245     if (actionValue == currentState)
       
   246         a->setChecked(true);
       
   247     ag->addAction(a);
       
   248     return a;
       
   249 }
       
   250 
       
   251 void PreviewDeviceSkin::slotPopupMenu()
       
   252 {
       
   253     QMenu menu(this);
       
   254     // Create actions
       
   255     if (!m_directionUpAction) {
       
   256         QActionGroup *directionGroup = new QActionGroup(this);
       
   257         connect(directionGroup, SIGNAL(triggered(QAction*)), this, SLOT(slotDirection(QAction*)));
       
   258         directionGroup->setExclusive(true);
       
   259         m_directionUpAction = createCheckableActionIntData(tr("&Portrait"), DirectionUp, m_direction, directionGroup, this);
       
   260 	//: Rotate form preview counter-clockwise
       
   261         m_directionLeftAction = createCheckableActionIntData(tr("Landscape (&CCW)"), DirectionLeft, m_direction, directionGroup, this);
       
   262         //: Rotate form preview clockwise
       
   263         m_directionRightAction = createCheckableActionIntData(tr("&Landscape (CW)"), DirectionRight, m_direction, directionGroup, this);
       
   264         m_closeAction = new QAction(tr("&Close"), this);
       
   265         connect(m_closeAction, SIGNAL(triggered()), parentWidget(), SLOT(close()));
       
   266     }
       
   267     menu.addAction(m_directionUpAction);
       
   268     menu.addAction(m_directionLeftAction);
       
   269     menu.addAction(m_directionRightAction);
       
   270     menu.addSeparator();
       
   271     populateContextMenu(&menu);
       
   272     menu.addAction(m_closeAction);
       
   273     menu.exec(QCursor::pos());
       
   274 }
       
   275 
       
   276 void PreviewDeviceSkin::slotDirection(QAction *a)
       
   277 {
       
   278     const Direction newDirection = static_cast<Direction>(a->data().toInt());
       
   279     if (m_direction == newDirection)
       
   280         return;
       
   281     const Qt::Orientation newOrientation = newDirection == DirectionUp ? Qt::Vertical : Qt::Horizontal;
       
   282     const Qt::Orientation oldOrientation = m_direction  == DirectionUp ? Qt::Vertical : Qt::Horizontal;
       
   283     m_direction = newDirection;
       
   284     QApplication::setOverrideCursor(Qt::WaitCursor);
       
   285     if (oldOrientation != newOrientation) {
       
   286         QSize size = screenSize();
       
   287         if (newOrientation == Qt::Horizontal)
       
   288             size.transpose();
       
   289         fitWidget(size);
       
   290     }
       
   291     setTransform(skinTransform());
       
   292     QApplication::restoreOverrideCursor();
       
   293 }
       
   294 
       
   295 void PreviewDeviceSkin::fitWidget(const QSize &size)
       
   296 {
       
   297     view()->setFixedSize(size);
       
   298 }
       
   299 
       
   300 QMatrix PreviewDeviceSkin::skinTransform() const
       
   301 {
       
   302     QMatrix newTransform;    
       
   303     switch (m_direction)  {
       
   304         case DirectionUp:
       
   305             break;
       
   306         case DirectionLeft:
       
   307             newTransform.rotate(270.0);
       
   308             break;
       
   309         case DirectionRight:
       
   310             newTransform.rotate(90.0);
       
   311             break;
       
   312     }
       
   313     return newTransform;
       
   314 }
       
   315 
       
   316 // ------------ PreviewConfigurationPrivate
       
   317 class PreviewConfigurationData : public QSharedData {
       
   318 public:
       
   319     PreviewConfigurationData() {}
       
   320     explicit PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin);
       
   321 
       
   322     QString m_style;
       
   323     // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()).
       
   324     QString m_applicationStyleSheet;
       
   325     QString m_deviceSkin;
       
   326 };
       
   327 
       
   328 PreviewConfigurationData::PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin) :
       
   329     m_style(style),
       
   330     m_applicationStyleSheet(applicationStyleSheet),
       
   331     m_deviceSkin(deviceSkin)
       
   332 {
       
   333 }
       
   334 
       
   335 /* ZoomablePreviewDeviceSkin: A Zoomable Widget Preview skin. Embeds preview
       
   336  *  into a ZoomWidget and this in turn into the DeviceSkin view and keeps
       
   337  * Device skin zoom + ZoomWidget zoom in sync. */
       
   338 
       
   339 class ZoomablePreviewDeviceSkin : public PreviewDeviceSkin
       
   340 {
       
   341     Q_OBJECT
       
   342 public:
       
   343     explicit ZoomablePreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent);
       
   344     virtual void setPreview(QWidget *w);
       
   345 
       
   346     int zoomPercent() const; // Device Skins have a double 'zoom' property
       
   347 
       
   348 public slots:
       
   349     void setZoomPercent(int);
       
   350 
       
   351 signals:
       
   352     void zoomPercentChanged(int);
       
   353 
       
   354 protected:
       
   355     virtual void populateContextMenu(QMenu *m);    
       
   356     virtual QMatrix skinTransform() const;
       
   357     virtual void fitWidget(const QSize &size);
       
   358 
       
   359 private:
       
   360     ZoomMenu *m_zoomMenu;
       
   361     QAction *m_zoomSubMenuAction;
       
   362     ZoomWidget *m_zoomWidget;
       
   363 };
       
   364 
       
   365 ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent) :
       
   366     PreviewDeviceSkin(parameters, parent),
       
   367     m_zoomMenu(new ZoomMenu(this)),
       
   368     m_zoomSubMenuAction(0),
       
   369     m_zoomWidget(new DesignerZoomWidget)
       
   370 {
       
   371     connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int)));
       
   372     connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SIGNAL(zoomPercentChanged(int)));
       
   373     m_zoomWidget->setZoomContextMenuEnabled(false);
       
   374     m_zoomWidget->setWidgetZoomContextMenuEnabled(false);
       
   375     m_zoomWidget->resize(screenSize());
       
   376     m_zoomWidget->setParent(this, Qt::SubWindow);
       
   377     m_zoomWidget->setAutoFillBackground(true);
       
   378     setView(m_zoomWidget);
       
   379 }
       
   380 
       
   381 static inline qreal zoomFactor(int percent)
       
   382 {
       
   383     return qreal(percent) / 100.0;
       
   384 }
       
   385 
       
   386 static inline QSize scaleSize(int zoomPercent, const QSize &size)
       
   387 {
       
   388     return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize();
       
   389 }
       
   390 
       
   391 void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget)
       
   392 {    
       
   393     m_zoomWidget->setWidget(formWidget);
       
   394     m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize()));
       
   395 }
       
   396 
       
   397 int ZoomablePreviewDeviceSkin::zoomPercent() const
       
   398 {
       
   399     return m_zoomWidget->zoom();
       
   400 }
       
   401 
       
   402 void ZoomablePreviewDeviceSkin::setZoomPercent(int zp)
       
   403 {
       
   404     if (zp == zoomPercent())
       
   405         return;
       
   406 
       
   407     // If not triggered by the menu itself: Update it
       
   408     if (m_zoomMenu->zoom() != zp)
       
   409         m_zoomMenu->setZoom(zp);
       
   410 
       
   411     QApplication::setOverrideCursor(Qt::WaitCursor);    
       
   412     m_zoomWidget->setZoom(zp);
       
   413     setTransform(skinTransform());
       
   414     QApplication::restoreOverrideCursor();
       
   415 }
       
   416 
       
   417 void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu)
       
   418 {
       
   419     if (!m_zoomSubMenuAction) {
       
   420         m_zoomSubMenuAction = new QAction(tr("&Zoom"), this);
       
   421         QMenu *zoomSubMenu = new QMenu;
       
   422         m_zoomSubMenuAction->setMenu(zoomSubMenu);
       
   423         m_zoomMenu->addActions(zoomSubMenu);
       
   424     }
       
   425     menu->addAction(m_zoomSubMenuAction);
       
   426     menu->addSeparator();
       
   427 }
       
   428 
       
   429 QMatrix ZoomablePreviewDeviceSkin::skinTransform() const
       
   430 {
       
   431     // Complete transformation consisting of base class rotation and zoom.
       
   432     QMatrix rc = PreviewDeviceSkin::skinTransform();
       
   433     const int zp = zoomPercent();
       
   434     if (zp != 100) {
       
   435         const qreal factor = zoomFactor(zp);
       
   436         rc.scale(factor, factor);
       
   437     }
       
   438     return rc;
       
   439 }
       
   440 
       
   441 void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size)
       
   442 {
       
   443     m_zoomWidget->resize(scaleSize(zoomPercent(), size));
       
   444 }
       
   445 
       
   446 // ------------- PreviewConfiguration
       
   447 
       
   448 static const char *styleKey = "Style";
       
   449 static const char *appStyleSheetKey = "AppStyleSheet";
       
   450 static const char *skinKey = "Skin";
       
   451 
       
   452 PreviewConfiguration::PreviewConfiguration() :
       
   453     m_d(new PreviewConfigurationData)
       
   454 {
       
   455 }
       
   456 
       
   457 PreviewConfiguration::PreviewConfiguration(const QString &sty, const QString &applicationSheet, const QString &skin) :
       
   458     m_d(new PreviewConfigurationData(sty, applicationSheet, skin))
       
   459 {
       
   460 }
       
   461 
       
   462 PreviewConfiguration::PreviewConfiguration(const PreviewConfiguration &o) :
       
   463     m_d(o.m_d)
       
   464 {
       
   465 }
       
   466 
       
   467 PreviewConfiguration &PreviewConfiguration::operator=(const PreviewConfiguration &o)
       
   468 {
       
   469     m_d.operator=(o.m_d);
       
   470     return *this;
       
   471 }
       
   472 
       
   473 PreviewConfiguration::~PreviewConfiguration()
       
   474 {
       
   475 }
       
   476 
       
   477 void PreviewConfiguration::clear()
       
   478 {
       
   479     PreviewConfigurationData &d = *m_d;
       
   480     d.m_style.clear();
       
   481     d.m_applicationStyleSheet.clear();
       
   482     d.m_deviceSkin.clear();
       
   483 }
       
   484 
       
   485 QString PreviewConfiguration::style() const
       
   486 {
       
   487     return m_d->m_style;
       
   488 }
       
   489 
       
   490 void PreviewConfiguration::setStyle(const QString &s)
       
   491 {
       
   492     m_d->m_style = s;
       
   493 }
       
   494 
       
   495 // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()).
       
   496 QString PreviewConfiguration::applicationStyleSheet() const
       
   497 {
       
   498     return m_d->m_applicationStyleSheet;
       
   499 }
       
   500 
       
   501 void PreviewConfiguration::setApplicationStyleSheet(const QString &as)
       
   502 {
       
   503     m_d->m_applicationStyleSheet = as;
       
   504 }
       
   505 
       
   506 QString PreviewConfiguration::deviceSkin() const
       
   507 {
       
   508     return m_d->m_deviceSkin;
       
   509 }
       
   510 
       
   511 void PreviewConfiguration::setDeviceSkin(const QString &s)
       
   512 {
       
   513      m_d->m_deviceSkin = s;
       
   514 }
       
   515 
       
   516 void PreviewConfiguration::toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const
       
   517 {
       
   518     const PreviewConfigurationData &d = *m_d;
       
   519     settings->beginGroup(prefix);
       
   520     settings->setValue(QLatin1String(styleKey),  d.m_style);
       
   521     settings->setValue(QLatin1String(appStyleSheetKey), d.m_applicationStyleSheet);
       
   522     settings->setValue(QLatin1String(skinKey), d.m_deviceSkin);
       
   523     settings->endGroup();
       
   524 }
       
   525 
       
   526 void PreviewConfiguration::fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings)
       
   527 {
       
   528     clear();
       
   529     QString key = prefix;
       
   530     key += QLatin1Char('/');
       
   531     const int prefixSize = key.size();
       
   532 
       
   533     PreviewConfigurationData &d = *m_d;
       
   534 
       
   535     const QVariant emptyString = QVariant(QString());
       
   536 
       
   537     key += QLatin1String(styleKey);
       
   538     d.m_style = settings->value(key, emptyString).toString();
       
   539 
       
   540     key.replace(prefixSize, key.size() - prefixSize, QLatin1String(appStyleSheetKey));
       
   541     d.m_applicationStyleSheet = settings->value(key, emptyString).toString();
       
   542 
       
   543     key.replace(prefixSize, key.size() - prefixSize, QLatin1String(skinKey));
       
   544     d.m_deviceSkin = settings->value(key, emptyString).toString();
       
   545 }
       
   546 
       
   547 
       
   548 QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
       
   549 {
       
   550     return compare(pc1, pc2) < 0;
       
   551 }
       
   552 
       
   553 QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
       
   554 {
       
   555     return compare(pc1, pc2) == 0;
       
   556 }
       
   557 
       
   558 QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
       
   559 {
       
   560     return compare(pc1, pc2) != 0;
       
   561 }
       
   562 
       
   563 // ------------- PreviewManagerPrivate
       
   564 class PreviewManagerPrivate {
       
   565 public:
       
   566     PreviewManagerPrivate(PreviewManager::PreviewMode mode);
       
   567 
       
   568     const PreviewManager::PreviewMode m_mode;
       
   569 
       
   570     QPointer<QWidget> m_activePreview;
       
   571 
       
   572     typedef QList<PreviewData> PreviewDataList;
       
   573 
       
   574     PreviewDataList m_previews;
       
   575 
       
   576     typedef QMap<QString, DeviceSkinParameters> DeviceSkinConfigCache;
       
   577     DeviceSkinConfigCache m_deviceSkinConfigCache;
       
   578 
       
   579     QDesignerFormEditorInterface *m_core;
       
   580     bool m_updateBlocked;
       
   581 };
       
   582 
       
   583 PreviewManagerPrivate::PreviewManagerPrivate(PreviewManager::PreviewMode mode) :
       
   584     m_mode(mode),
       
   585     m_core(0),
       
   586     m_updateBlocked(false)
       
   587 {
       
   588 }
       
   589 
       
   590 // ------------- PreviewManager
       
   591 
       
   592 PreviewManager::PreviewManager(PreviewMode mode, QObject *parent) :
       
   593    QObject(parent),
       
   594    d(new PreviewManagerPrivate(mode))
       
   595 {
       
   596 }
       
   597 
       
   598 PreviewManager:: ~PreviewManager()
       
   599 {
       
   600     delete d;
       
   601 }
       
   602 
       
   603 
       
   604 Qt::WindowFlags PreviewManager::previewWindowFlags(const QWidget *widget) const
       
   605 {
       
   606 #ifdef Q_WS_WIN
       
   607     Qt::WindowFlags windowFlags = (widget->windowType() == Qt::Window) ? Qt::Window | Qt::WindowMaximizeButtonHint : Qt::WindowFlags(Qt::Dialog);
       
   608 #else
       
   609     Q_UNUSED(widget)
       
   610     // Only Dialogs have close buttons on Mac.
       
   611     // On Linux, we don't want an additional task bar item and we don't want a minimize button;
       
   612     // we want the preview to be on top.
       
   613     Qt::WindowFlags windowFlags = Qt::Dialog;
       
   614 #endif
       
   615     return windowFlags;
       
   616 }
       
   617 
       
   618 QWidget *PreviewManager::createDeviceSkinContainer(const QDesignerFormWindowInterface *fw) const
       
   619 {
       
   620     return new QDialog(fw->window());
       
   621 }
       
   622 
       
   623 // Some widgets might require fake containers
       
   624 
       
   625 static QWidget *fakeContainer(QWidget *w)
       
   626 {
       
   627     // Prevent a dock widget from trying to dock to Designer's main window
       
   628     // (which can be found in the parent hierarchy in MDI mode) by
       
   629     // providing a fake mainwindow
       
   630     if (QDockWidget *dock = qobject_cast<QDockWidget *>(w)) {
       
   631         // Reparent: Clear modality, propagate title and resize outer container
       
   632         const QSize size = w->size();
       
   633         w->setWindowModality(Qt::NonModal);
       
   634         dock->setFeatures(dock->features() & ~(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetClosable));
       
   635         dock->setAllowedAreas(Qt::LeftDockWidgetArea);
       
   636         QMainWindow *mw = new QMainWindow;
       
   637         int leftMargin, topMargin, rightMargin, bottomMargin;
       
   638         mw->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
       
   639         mw->addDockWidget(Qt::LeftDockWidgetArea, dock);
       
   640         mw->resize(size + QSize(leftMargin + rightMargin, topMargin + bottomMargin));
       
   641         return mw;
       
   642     }
       
   643     return w;
       
   644 }
       
   645 
       
   646 static PreviewConfiguration configurationFromSettings(QDesignerFormEditorInterface *core, const QString &style)
       
   647 {
       
   648     qdesigner_internal::PreviewConfiguration pc;
       
   649     const QDesignerSharedSettings settings(core);
       
   650     if (settings.isCustomPreviewConfigurationEnabled())
       
   651         pc = settings.customPreviewConfiguration();
       
   652     if (!style.isEmpty())
       
   653         pc.setStyle(style);
       
   654     return pc;
       
   655 }
       
   656 
       
   657 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage)
       
   658 {
       
   659     return showPreview(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage);
       
   660 }
       
   661 
       
   662 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
       
   663 {
       
   664     return showPreview(fw, style, -1, errorMessage);
       
   665 }
       
   666 
       
   667 QWidget *PreviewManager::createPreview(const QDesignerFormWindowInterface *fw,
       
   668                                        const PreviewConfiguration &pc,
       
   669                                        int deviceProfileIndex,
       
   670                                        QString *errorMessage,
       
   671                                        int initialZoom)
       
   672 {
       
   673     if (!d->m_core)
       
   674         d->m_core = fw->core();
       
   675 
       
   676     const bool zoomable = initialZoom > 0;
       
   677     // Figure out which profile to apply
       
   678     DeviceProfile deviceProfile;
       
   679     if (deviceProfileIndex >= 0) {
       
   680         deviceProfile = QDesignerSharedSettings(fw->core()).deviceProfileAt(deviceProfileIndex);
       
   681     } else {
       
   682         if (const FormWindowBase *fwb = qobject_cast<const FormWindowBase *>(fw))
       
   683             deviceProfile = fwb->deviceProfile();
       
   684     }
       
   685     // Create
       
   686     QWidget *formWidget = QDesignerFormBuilder::createPreview(fw, pc.style(), pc.applicationStyleSheet(), deviceProfile, errorMessage);
       
   687     if (!formWidget)
       
   688         return 0;
       
   689 
       
   690     const QString title = tr("%1 - [Preview]").arg(formWidget->windowTitle());
       
   691     formWidget = fakeContainer(formWidget);
       
   692     formWidget->setWindowTitle(title);
       
   693 
       
   694     // Clear any modality settings, child widget modalities must not be higher than parent's
       
   695     formWidget->setWindowModality(Qt::NonModal);
       
   696     // No skin
       
   697     const QString deviceSkin = pc.deviceSkin();
       
   698     if (deviceSkin.isEmpty()) {
       
   699         if (zoomable) { // Embed into ZoomWidget
       
   700             ZoomWidget *zw = new DesignerZoomWidget;
       
   701             connect(zw->zoomMenu(), SIGNAL(zoomChanged(int)), this, SLOT(slotZoomChanged(int)));
       
   702             zw->setWindowTitle(title);
       
   703             zw->setWidget(formWidget);
       
   704             // Keep any widgets' context menus working, do not use global menu
       
   705             zw->setWidgetZoomContextMenuEnabled(true);
       
   706             zw->setParent(fw->window(), previewWindowFlags(formWidget));
       
   707             // Make preview close when Widget closes (Dialog/accept, etc)
       
   708             formWidget->setAttribute(Qt::WA_DeleteOnClose, true);
       
   709             connect(formWidget, SIGNAL(destroyed()), zw, SLOT(close()));
       
   710             zw->setZoom(initialZoom);
       
   711             zw->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
       
   712             return zw;
       
   713         }
       
   714         formWidget->setParent(fw->window(), previewWindowFlags(formWidget));
       
   715         formWidget->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
       
   716         return formWidget;
       
   717     }
       
   718     // Embed into skin. find config in cache
       
   719     PreviewManagerPrivate::DeviceSkinConfigCache::iterator it = d->m_deviceSkinConfigCache.find(deviceSkin);
       
   720     if (it == d->m_deviceSkinConfigCache.end()) {
       
   721         DeviceSkinParameters parameters;
       
   722         if (!parameters.read(deviceSkin, DeviceSkinParameters::ReadAll, errorMessage)) {
       
   723             formWidget->deleteLater();
       
   724             return 0;
       
   725           }
       
   726         it = d->m_deviceSkinConfigCache.insert(deviceSkin, parameters);
       
   727     }
       
   728 
       
   729     QWidget *skinContainer = createDeviceSkinContainer(fw);
       
   730     PreviewDeviceSkin *skin = 0;
       
   731     if (zoomable) {
       
   732         ZoomablePreviewDeviceSkin *zds = new ZoomablePreviewDeviceSkin(it.value(), skinContainer);
       
   733         zds->setZoomPercent(initialZoom);
       
   734         connect(zds, SIGNAL(zoomPercentChanged(int)), this, SLOT(slotZoomChanged(int)));
       
   735         skin = zds;
       
   736     }  else {
       
   737         skin = new PreviewDeviceSkin(it.value(), skinContainer);
       
   738     }
       
   739     skin->setPreview(formWidget);
       
   740     // Make preview close when Widget closes (Dialog/accept, etc)
       
   741     formWidget->setAttribute(Qt::WA_DeleteOnClose, true);
       
   742     connect(formWidget, SIGNAL(destroyed()), skinContainer, SLOT(close()));
       
   743     skinContainer->setWindowTitle(title);
       
   744     skinContainer->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
       
   745     return skinContainer;
       
   746 }
       
   747 
       
   748 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw,
       
   749                                      const PreviewConfiguration &pc,
       
   750                                      int deviceProfileIndex,
       
   751                                      QString *errorMessage)
       
   752 {
       
   753     enum { Spacing = 10 };
       
   754     if (QWidget *existingPreviewWidget = raise(fw, pc))
       
   755         return existingPreviewWidget;
       
   756 
       
   757     const QDesignerSharedSettings settings(fw->core());
       
   758     const int initialZoom = settings.zoomEnabled() ? settings.zoom() : -1;
       
   759 
       
   760     QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage, initialZoom);
       
   761     if (!widget)
       
   762         return 0;
       
   763     // Install filter for Escape key
       
   764     widget->setAttribute(Qt::WA_DeleteOnClose, true);
       
   765     widget->installEventFilter(this);
       
   766 
       
   767     switch (d->m_mode) {
       
   768     case ApplicationModalPreview:
       
   769         // Cannot do this on the Mac as the dialog would have no close button
       
   770         widget->setWindowModality(Qt::ApplicationModal);
       
   771         break;
       
   772     case SingleFormNonModalPreview:
       
   773     case MultipleFormNonModalPreview:
       
   774         widget->setWindowModality(Qt::NonModal);
       
   775         connect(fw, SIGNAL(changed()), widget, SLOT(close()));
       
   776         connect(fw, SIGNAL(destroyed()), widget, SLOT(close()));
       
   777         if (d->m_mode == SingleFormNonModalPreview)
       
   778             connect(fw->core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), widget, SLOT(close()));
       
   779         break;
       
   780     }
       
   781     // Semi-smart algorithm to position previews:
       
   782     // If its the first one, position relative to form.
       
   783     // 2nd, attempt to tile right (for comparing styles) or cascade
       
   784     const QSize size = widget->size();
       
   785     const bool firstPreview = d->m_previews.empty();
       
   786     if (firstPreview) {
       
   787         widget->move(fw->mapToGlobal(QPoint(Spacing, Spacing)));
       
   788     } else {
       
   789         if (QWidget *lastPreview = d->m_previews.back().m_widget) {
       
   790             QDesktopWidget *desktop = qApp->desktop();
       
   791             const QRect lastPreviewGeometry = lastPreview->frameGeometry();
       
   792             const QRect availGeometry = desktop->availableGeometry(desktop->screenNumber(lastPreview));
       
   793             const QPoint newPos = lastPreviewGeometry.topRight() + QPoint(Spacing, 0);
       
   794             if (newPos.x() +  size.width() < availGeometry.right())
       
   795                 widget->move(newPos);
       
   796             else
       
   797                 widget->move(lastPreviewGeometry.topLeft() + QPoint(Spacing, Spacing));
       
   798         }
       
   799 
       
   800     }
       
   801     d->m_previews.push_back(PreviewData(widget, fw, pc));
       
   802     widget->show();
       
   803     if (firstPreview)
       
   804         emit firstPreviewOpened();
       
   805     return widget;
       
   806 }
       
   807 
       
   808 QWidget *PreviewManager::raise(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc)
       
   809 {
       
   810     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
       
   811     if (d->m_previews.empty())
       
   812         return false;
       
   813 
       
   814     // find matching window
       
   815     const PreviewDataList::const_iterator cend =  d->m_previews.constEnd();
       
   816     for (PreviewDataList::const_iterator it = d->m_previews.constBegin(); it !=  cend ;++it) {
       
   817         QWidget * w = it->m_widget;
       
   818         if (w && it->m_formWindow == fw && it->m_configuration == pc) {
       
   819             w->raise();
       
   820             w->activateWindow();
       
   821             return w;
       
   822         }
       
   823     }
       
   824     return 0;
       
   825 }
       
   826 
       
   827 void PreviewManager::closeAllPreviews()
       
   828 {
       
   829     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
       
   830     if (!d->m_previews.empty()) {
       
   831         d->m_updateBlocked = true;
       
   832         d->m_activePreview = 0;
       
   833         const PreviewDataList::iterator cend =  d->m_previews.end();
       
   834         for (PreviewDataList::iterator it = d->m_previews.begin(); it !=  cend ;++it) {
       
   835             if (it->m_widget)
       
   836                 it->m_widget->close();
       
   837         }
       
   838         d->m_previews.clear();
       
   839         d->m_updateBlocked = false;
       
   840         emit lastPreviewClosed();
       
   841     }
       
   842 }
       
   843 
       
   844 void PreviewManager::updatePreviewClosed(QWidget *w)
       
   845 {
       
   846     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
       
   847     if (d->m_updateBlocked)
       
   848         return;
       
   849     // Purge out all 0 or widgets to be deleted
       
   850     for (PreviewDataList::iterator it = d->m_previews.begin(); it != d->m_previews.end() ; ) {
       
   851         QWidget *iw = it->m_widget; // Might be 0 when catching QEvent::Destroyed
       
   852         if (iw == 0 || iw == w) {
       
   853             it = d->m_previews.erase(it);
       
   854         } else {
       
   855             ++it;
       
   856         }
       
   857     }
       
   858     if (d->m_previews.empty())
       
   859         emit lastPreviewClosed();
       
   860 }
       
   861 
       
   862 bool PreviewManager::eventFilter(QObject *watched, QEvent *event)
       
   863 {
       
   864     // Courtesy of designer
       
   865     do {
       
   866         if (!watched->isWidgetType())
       
   867             break;
       
   868         QWidget *previewWindow = qobject_cast<QWidget *>(watched);
       
   869         if (!previewWindow || !previewWindow->isWindow())
       
   870             break;
       
   871 
       
   872         switch (event->type()) {
       
   873         case QEvent::KeyPress:
       
   874         case QEvent::ShortcutOverride:        {
       
   875             const  QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
       
   876             const int key = keyEvent->key();
       
   877             if ((key == Qt::Key_Escape
       
   878 #ifdef Q_WS_MAC
       
   879                  || (keyEvent->modifiers() == Qt::ControlModifier && key == Qt::Key_Period)
       
   880 #endif
       
   881                  )) {
       
   882                 previewWindow->close();
       
   883                 return true;
       
   884             }
       
   885         }
       
   886             break;
       
   887         case QEvent::WindowActivate:
       
   888             d->m_activePreview = previewWindow;
       
   889             break;
       
   890         case  QEvent::Destroy: // We don't get QEvent::Close if someone accepts a QDialog.
       
   891             updatePreviewClosed(previewWindow);
       
   892             break;
       
   893         case  QEvent::Close:
       
   894             updatePreviewClosed(previewWindow);
       
   895             previewWindow->removeEventFilter (this);
       
   896             break;
       
   897         default:
       
   898             break;
       
   899         }
       
   900     } while(false);
       
   901     return QObject::eventFilter(watched, event);
       
   902 }
       
   903 
       
   904 int PreviewManager::previewCount() const
       
   905 {
       
   906     return  d->m_previews.size();
       
   907 }
       
   908 
       
   909 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage)
       
   910 {
       
   911     return createPreviewPixmap(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage);
       
   912 }
       
   913 
       
   914 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
       
   915 {
       
   916     return createPreviewPixmap(fw, style, -1, errorMessage);
       
   917 }
       
   918 
       
   919 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw,
       
   920                                             const PreviewConfiguration &pc,
       
   921                                             int deviceProfileIndex,
       
   922                                             QString *errorMessage)
       
   923 {
       
   924     QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage);
       
   925     if (!widget)
       
   926         return QPixmap();
       
   927     const QPixmap rc = QPixmap::grabWidget(widget);
       
   928     widget->deleteLater();
       
   929     return rc;
       
   930 }
       
   931 
       
   932 void PreviewManager::slotZoomChanged(int z)
       
   933 {
       
   934     if (d->m_core) { // Save the last zoom chosen by the user.
       
   935         QDesignerSharedSettings settings(d->m_core);
       
   936         settings.setZoom(z);
       
   937     }
       
   938 }
       
   939 }
       
   940 
       
   941 QT_END_NAMESPACE
       
   942 
       
   943 #include "previewmanager.moc"