Fix compiler warnings in Qt Designer.
[qt:qt.git] / tools / designer / src / lib / shared / previewmanager.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 #ifndef QT_NO_CURSOR
285     QApplication::setOverrideCursor(Qt::WaitCursor);
286 #endif
287     if (oldOrientation != newOrientation) {
288         QSize size = screenSize();
289         if (newOrientation == Qt::Horizontal)
290             size.transpose();
291         fitWidget(size);
292     }
293     setTransform(skinTransform());
294 #ifndef QT_NO_CURSOR
295     QApplication::restoreOverrideCursor();
296 #endif
297 }
298
299 void PreviewDeviceSkin::fitWidget(const QSize &size)
300 {
301     view()->setFixedSize(size);
302 }
303
304 QMatrix PreviewDeviceSkin::skinTransform() const
305 {
306     QMatrix newTransform;    
307     switch (m_direction)  {
308         case DirectionUp:
309             break;
310         case DirectionLeft:
311             newTransform.rotate(270.0);
312             break;
313         case DirectionRight:
314             newTransform.rotate(90.0);
315             break;
316     }
317     return newTransform;
318 }
319
320 // ------------ PreviewConfigurationPrivate
321 class PreviewConfigurationData : public QSharedData {
322 public:
323     PreviewConfigurationData() {}
324     explicit PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin);
325
326     QString m_style;
327     // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()).
328     QString m_applicationStyleSheet;
329     QString m_deviceSkin;
330 };
331
332 PreviewConfigurationData::PreviewConfigurationData(const QString &style, const QString &applicationStyleSheet, const QString &deviceSkin) :
333     m_style(style),
334     m_applicationStyleSheet(applicationStyleSheet),
335     m_deviceSkin(deviceSkin)
336 {
337 }
338
339 /* ZoomablePreviewDeviceSkin: A Zoomable Widget Preview skin. Embeds preview
340  *  into a ZoomWidget and this in turn into the DeviceSkin view and keeps
341  * Device skin zoom + ZoomWidget zoom in sync. */
342
343 class ZoomablePreviewDeviceSkin : public PreviewDeviceSkin
344 {
345     Q_OBJECT
346 public:
347     explicit ZoomablePreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent);
348     virtual void setPreview(QWidget *w);
349
350     int zoomPercent() const; // Device Skins have a double 'zoom' property
351
352 public slots:
353     void setZoomPercent(int);
354
355 signals:
356     void zoomPercentChanged(int);
357
358 protected:
359     virtual void populateContextMenu(QMenu *m);    
360     virtual QMatrix skinTransform() const;
361     virtual void fitWidget(const QSize &size);
362
363 private:
364     ZoomMenu *m_zoomMenu;
365     QAction *m_zoomSubMenuAction;
366     ZoomWidget *m_zoomWidget;
367 };
368
369 ZoomablePreviewDeviceSkin::ZoomablePreviewDeviceSkin(const DeviceSkinParameters &parameters, QWidget *parent) :
370     PreviewDeviceSkin(parameters, parent),
371     m_zoomMenu(new ZoomMenu(this)),
372     m_zoomSubMenuAction(0),
373     m_zoomWidget(new DesignerZoomWidget)
374 {
375     connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoomPercent(int)));
376     connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SIGNAL(zoomPercentChanged(int)));
377     m_zoomWidget->setZoomContextMenuEnabled(false);
378     m_zoomWidget->setWidgetZoomContextMenuEnabled(false);
379     m_zoomWidget->resize(screenSize());
380     m_zoomWidget->setParent(this, Qt::SubWindow);
381     m_zoomWidget->setAutoFillBackground(true);
382     setView(m_zoomWidget);
383 }
384
385 static inline qreal zoomFactor(int percent)
386 {
387     return qreal(percent) / 100.0;
388 }
389
390 static inline QSize scaleSize(int zoomPercent, const QSize &size)
391 {
392     return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize();
393 }
394
395 void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget)
396 {    
397     m_zoomWidget->setWidget(formWidget);
398     m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize()));
399 }
400
401 int ZoomablePreviewDeviceSkin::zoomPercent() const
402 {
403     return m_zoomWidget->zoom();
404 }
405
406 void ZoomablePreviewDeviceSkin::setZoomPercent(int zp)
407 {
408     if (zp == zoomPercent())
409         return;
410
411     // If not triggered by the menu itself: Update it
412     if (m_zoomMenu->zoom() != zp)
413         m_zoomMenu->setZoom(zp);
414
415 #ifndef QT_NO_CURSOR
416     QApplication::setOverrideCursor(Qt::WaitCursor);    
417 #endif
418     m_zoomWidget->setZoom(zp);
419     setTransform(skinTransform());
420 #ifndef QT_NO_CURSOR
421     QApplication::restoreOverrideCursor();
422 #endif
423 }
424
425 void ZoomablePreviewDeviceSkin::populateContextMenu(QMenu *menu)
426 {
427     if (!m_zoomSubMenuAction) {
428         m_zoomSubMenuAction = new QAction(tr("&Zoom"), this);
429         QMenu *zoomSubMenu = new QMenu;
430         m_zoomSubMenuAction->setMenu(zoomSubMenu);
431         m_zoomMenu->addActions(zoomSubMenu);
432     }
433     menu->addAction(m_zoomSubMenuAction);
434     menu->addSeparator();
435 }
436
437 QMatrix ZoomablePreviewDeviceSkin::skinTransform() const
438 {
439     // Complete transformation consisting of base class rotation and zoom.
440     QMatrix rc = PreviewDeviceSkin::skinTransform();
441     const int zp = zoomPercent();
442     if (zp != 100) {
443         const qreal factor = zoomFactor(zp);
444         rc.scale(factor, factor);
445     }
446     return rc;
447 }
448
449 void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size)
450 {
451     m_zoomWidget->resize(scaleSize(zoomPercent(), size));
452 }
453
454 // ------------- PreviewConfiguration
455
456 static const char *styleKey = "Style";
457 static const char *appStyleSheetKey = "AppStyleSheet";
458 static const char *skinKey = "Skin";
459
460 PreviewConfiguration::PreviewConfiguration() :
461     m_d(new PreviewConfigurationData)
462 {
463 }
464
465 PreviewConfiguration::PreviewConfiguration(const QString &sty, const QString &applicationSheet, const QString &skin) :
466     m_d(new PreviewConfigurationData(sty, applicationSheet, skin))
467 {
468 }
469
470 PreviewConfiguration::PreviewConfiguration(const PreviewConfiguration &o) :
471     m_d(o.m_d)
472 {
473 }
474
475 PreviewConfiguration &PreviewConfiguration::operator=(const PreviewConfiguration &o)
476 {
477     m_d.operator=(o.m_d);
478     return *this;
479 }
480
481 PreviewConfiguration::~PreviewConfiguration()
482 {
483 }
484
485 void PreviewConfiguration::clear()
486 {
487     PreviewConfigurationData &d = *m_d;
488     d.m_style.clear();
489     d.m_applicationStyleSheet.clear();
490     d.m_deviceSkin.clear();
491 }
492
493 QString PreviewConfiguration::style() const
494 {
495     return m_d->m_style;
496 }
497
498 void PreviewConfiguration::setStyle(const QString &s)
499 {
500     m_d->m_style = s;
501 }
502
503 // Style sheet to prepend (to simulate the effect od QApplication::setSyleSheet()).
504 QString PreviewConfiguration::applicationStyleSheet() const
505 {
506     return m_d->m_applicationStyleSheet;
507 }
508
509 void PreviewConfiguration::setApplicationStyleSheet(const QString &as)
510 {
511     m_d->m_applicationStyleSheet = as;
512 }
513
514 QString PreviewConfiguration::deviceSkin() const
515 {
516     return m_d->m_deviceSkin;
517 }
518
519 void PreviewConfiguration::setDeviceSkin(const QString &s)
520 {
521      m_d->m_deviceSkin = s;
522 }
523
524 void PreviewConfiguration::toSettings(const QString &prefix, QDesignerSettingsInterface *settings) const
525 {
526     const PreviewConfigurationData &d = *m_d;
527     settings->beginGroup(prefix);
528     settings->setValue(QLatin1String(styleKey),  d.m_style);
529     settings->setValue(QLatin1String(appStyleSheetKey), d.m_applicationStyleSheet);
530     settings->setValue(QLatin1String(skinKey), d.m_deviceSkin);
531     settings->endGroup();
532 }
533
534 void PreviewConfiguration::fromSettings(const QString &prefix, const QDesignerSettingsInterface *settings)
535 {
536     clear();
537     QString key = prefix;
538     key += QLatin1Char('/');
539     const int prefixSize = key.size();
540
541     PreviewConfigurationData &d = *m_d;
542
543     const QVariant emptyString = QVariant(QString());
544
545     key += QLatin1String(styleKey);
546     d.m_style = settings->value(key, emptyString).toString();
547
548     key.replace(prefixSize, key.size() - prefixSize, QLatin1String(appStyleSheetKey));
549     d.m_applicationStyleSheet = settings->value(key, emptyString).toString();
550
551     key.replace(prefixSize, key.size() - prefixSize, QLatin1String(skinKey));
552     d.m_deviceSkin = settings->value(key, emptyString).toString();
553 }
554
555
556 QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
557 {
558     return compare(pc1, pc2) < 0;
559 }
560
561 QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
562 {
563     return compare(pc1, pc2) == 0;
564 }
565
566 QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
567 {
568     return compare(pc1, pc2) != 0;
569 }
570
571 // ------------- PreviewManagerPrivate
572 class PreviewManagerPrivate {
573 public:
574     PreviewManagerPrivate(PreviewManager::PreviewMode mode);
575
576     const PreviewManager::PreviewMode m_mode;
577
578     QPointer<QWidget> m_activePreview;
579
580     typedef QList<PreviewData> PreviewDataList;
581
582     PreviewDataList m_previews;
583
584     typedef QMap<QString, DeviceSkinParameters> DeviceSkinConfigCache;
585     DeviceSkinConfigCache m_deviceSkinConfigCache;
586
587     QDesignerFormEditorInterface *m_core;
588     bool m_updateBlocked;
589 };
590
591 PreviewManagerPrivate::PreviewManagerPrivate(PreviewManager::PreviewMode mode) :
592     m_mode(mode),
593     m_core(0),
594     m_updateBlocked(false)
595 {
596 }
597
598 // ------------- PreviewManager
599
600 PreviewManager::PreviewManager(PreviewMode mode, QObject *parent) :
601    QObject(parent),
602    d(new PreviewManagerPrivate(mode))
603 {
604 }
605
606 PreviewManager:: ~PreviewManager()
607 {
608     delete d;
609 }
610
611
612 Qt::WindowFlags PreviewManager::previewWindowFlags(const QWidget *widget) const
613 {
614 #ifdef Q_WS_WIN
615     Qt::WindowFlags windowFlags = (widget->windowType() == Qt::Window) ? Qt::Window | Qt::WindowMaximizeButtonHint : Qt::WindowFlags(Qt::Dialog);
616 #else
617     Q_UNUSED(widget)
618     // Only Dialogs have close buttons on Mac.
619     // On Linux, we don't want an additional task bar item and we don't want a minimize button;
620     // we want the preview to be on top.
621     Qt::WindowFlags windowFlags = Qt::Dialog;
622 #endif
623     return windowFlags;
624 }
625
626 QWidget *PreviewManager::createDeviceSkinContainer(const QDesignerFormWindowInterface *fw) const
627 {
628     return new QDialog(fw->window());
629 }
630
631 // Some widgets might require fake containers
632
633 static QWidget *fakeContainer(QWidget *w)
634 {
635     // Prevent a dock widget from trying to dock to Designer's main window
636     // (which can be found in the parent hierarchy in MDI mode) by
637     // providing a fake mainwindow
638     if (QDockWidget *dock = qobject_cast<QDockWidget *>(w)) {
639         // Reparent: Clear modality, propagate title and resize outer container
640         const QSize size = w->size();
641         w->setWindowModality(Qt::NonModal);
642         dock->setFeatures(dock->features() & ~(QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable|QDockWidget::DockWidgetClosable));
643         dock->setAllowedAreas(Qt::LeftDockWidgetArea);
644         QMainWindow *mw = new QMainWindow;
645         int leftMargin, topMargin, rightMargin, bottomMargin;
646         mw->getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin);
647         mw->addDockWidget(Qt::LeftDockWidgetArea, dock);
648         mw->resize(size + QSize(leftMargin + rightMargin, topMargin + bottomMargin));
649         return mw;
650     }
651     return w;
652 }
653
654 static PreviewConfiguration configurationFromSettings(QDesignerFormEditorInterface *core, const QString &style)
655 {
656     qdesigner_internal::PreviewConfiguration pc;
657     const QDesignerSharedSettings settings(core);
658     if (settings.isCustomPreviewConfigurationEnabled())
659         pc = settings.customPreviewConfiguration();
660     if (!style.isEmpty())
661         pc.setStyle(style);
662     return pc;
663 }
664
665 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage)
666 {
667     return showPreview(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage);
668 }
669
670 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
671 {
672     return showPreview(fw, style, -1, errorMessage);
673 }
674
675 QWidget *PreviewManager::createPreview(const QDesignerFormWindowInterface *fw,
676                                        const PreviewConfiguration &pc,
677                                        int deviceProfileIndex,
678                                        QString *errorMessage,
679                                        int initialZoom)
680 {
681     if (!d->m_core)
682         d->m_core = fw->core();
683
684     const bool zoomable = initialZoom > 0;
685     // Figure out which profile to apply
686     DeviceProfile deviceProfile;
687     if (deviceProfileIndex >= 0) {
688         deviceProfile = QDesignerSharedSettings(fw->core()).deviceProfileAt(deviceProfileIndex);
689     } else {
690         if (const FormWindowBase *fwb = qobject_cast<const FormWindowBase *>(fw))
691             deviceProfile = fwb->deviceProfile();
692     }
693     // Create
694     QWidget *formWidget = QDesignerFormBuilder::createPreview(fw, pc.style(), pc.applicationStyleSheet(), deviceProfile, errorMessage);
695     if (!formWidget)
696         return 0;
697
698     const QString title = tr("%1 - [Preview]").arg(formWidget->windowTitle());
699     formWidget = fakeContainer(formWidget);
700     formWidget->setWindowTitle(title);
701
702     // Clear any modality settings, child widget modalities must not be higher than parent's
703     formWidget->setWindowModality(Qt::NonModal);
704     // No skin
705     const QString deviceSkin = pc.deviceSkin();
706     if (deviceSkin.isEmpty()) {
707         if (zoomable) { // Embed into ZoomWidget
708             ZoomWidget *zw = new DesignerZoomWidget;
709             connect(zw->zoomMenu(), SIGNAL(zoomChanged(int)), this, SLOT(slotZoomChanged(int)));
710             zw->setWindowTitle(title);
711             zw->setWidget(formWidget);
712             // Keep any widgets' context menus working, do not use global menu
713             zw->setWidgetZoomContextMenuEnabled(true);
714             zw->setParent(fw->window(), previewWindowFlags(formWidget));
715             // Make preview close when Widget closes (Dialog/accept, etc)
716             formWidget->setAttribute(Qt::WA_DeleteOnClose, true);
717             connect(formWidget, SIGNAL(destroyed()), zw, SLOT(close()));
718             zw->setZoom(initialZoom);
719             zw->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
720             return zw;
721         }
722         formWidget->setParent(fw->window(), previewWindowFlags(formWidget));
723         formWidget->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
724         return formWidget;
725     }
726     // Embed into skin. find config in cache
727     PreviewManagerPrivate::DeviceSkinConfigCache::iterator it = d->m_deviceSkinConfigCache.find(deviceSkin);
728     if (it == d->m_deviceSkinConfigCache.end()) {
729         DeviceSkinParameters parameters;
730         if (!parameters.read(deviceSkin, DeviceSkinParameters::ReadAll, errorMessage)) {
731             formWidget->deleteLater();
732             return 0;
733           }
734         it = d->m_deviceSkinConfigCache.insert(deviceSkin, parameters);
735     }
736
737     QWidget *skinContainer = createDeviceSkinContainer(fw);
738     PreviewDeviceSkin *skin = 0;
739     if (zoomable) {
740         ZoomablePreviewDeviceSkin *zds = new ZoomablePreviewDeviceSkin(it.value(), skinContainer);
741         zds->setZoomPercent(initialZoom);
742         connect(zds, SIGNAL(zoomPercentChanged(int)), this, SLOT(slotZoomChanged(int)));
743         skin = zds;
744     }  else {
745         skin = new PreviewDeviceSkin(it.value(), skinContainer);
746     }
747     skin->setPreview(formWidget);
748     // Make preview close when Widget closes (Dialog/accept, etc)
749     formWidget->setAttribute(Qt::WA_DeleteOnClose, true);
750     connect(formWidget, SIGNAL(destroyed()), skinContainer, SLOT(close()));
751     skinContainer->setWindowTitle(title);
752     skinContainer->setProperty(WidgetFactory::disableStyleCustomPaintingPropertyC, QVariant(true));
753     return skinContainer;
754 }
755
756 QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw,
757                                      const PreviewConfiguration &pc,
758                                      int deviceProfileIndex,
759                                      QString *errorMessage)
760 {
761     enum { Spacing = 10 };
762     if (QWidget *existingPreviewWidget = raise(fw, pc))
763         return existingPreviewWidget;
764
765     const QDesignerSharedSettings settings(fw->core());
766     const int initialZoom = settings.zoomEnabled() ? settings.zoom() : -1;
767
768     QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage, initialZoom);
769     if (!widget)
770         return 0;
771     // Install filter for Escape key
772     widget->setAttribute(Qt::WA_DeleteOnClose, true);
773     widget->installEventFilter(this);
774
775     switch (d->m_mode) {
776     case ApplicationModalPreview:
777         // Cannot do this on the Mac as the dialog would have no close button
778         widget->setWindowModality(Qt::ApplicationModal);
779         break;
780     case SingleFormNonModalPreview:
781     case MultipleFormNonModalPreview:
782         widget->setWindowModality(Qt::NonModal);
783         connect(fw, SIGNAL(changed()), widget, SLOT(close()));
784         connect(fw, SIGNAL(destroyed()), widget, SLOT(close()));
785         if (d->m_mode == SingleFormNonModalPreview)
786             connect(fw->core()->formWindowManager(), SIGNAL(activeFormWindowChanged(QDesignerFormWindowInterface*)), widget, SLOT(close()));
787         break;
788     }
789     // Semi-smart algorithm to position previews:
790     // If its the first one, position relative to form.
791     // 2nd, attempt to tile right (for comparing styles) or cascade
792     const QSize size = widget->size();
793     const bool firstPreview = d->m_previews.empty();
794     if (firstPreview) {
795         widget->move(fw->mapToGlobal(QPoint(Spacing, Spacing)));
796     } else {
797         if (QWidget *lastPreview = d->m_previews.back().m_widget) {
798             QDesktopWidget *desktop = qApp->desktop();
799             const QRect lastPreviewGeometry = lastPreview->frameGeometry();
800             const QRect availGeometry = desktop->availableGeometry(desktop->screenNumber(lastPreview));
801             const QPoint newPos = lastPreviewGeometry.topRight() + QPoint(Spacing, 0);
802             if (newPos.x() +  size.width() < availGeometry.right())
803                 widget->move(newPos);
804             else
805                 widget->move(lastPreviewGeometry.topLeft() + QPoint(Spacing, Spacing));
806         }
807
808     }
809     d->m_previews.push_back(PreviewData(widget, fw, pc));
810     widget->show();
811     if (firstPreview)
812         emit firstPreviewOpened();
813     return widget;
814 }
815
816 QWidget *PreviewManager::raise(const QDesignerFormWindowInterface *fw, const PreviewConfiguration &pc)
817 {
818     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
819     if (d->m_previews.empty())
820         return 0;
821
822     // find matching window
823     const PreviewDataList::const_iterator cend =  d->m_previews.constEnd();
824     for (PreviewDataList::const_iterator it = d->m_previews.constBegin(); it !=  cend ;++it) {
825         QWidget * w = it->m_widget;
826         if (w && it->m_formWindow == fw && it->m_configuration == pc) {
827             w->raise();
828             w->activateWindow();
829             return w;
830         }
831     }
832     return 0;
833 }
834
835 void PreviewManager::closeAllPreviews()
836 {
837     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
838     if (!d->m_previews.empty()) {
839         d->m_updateBlocked = true;
840         d->m_activePreview = 0;
841         const PreviewDataList::iterator cend =  d->m_previews.end();
842         for (PreviewDataList::iterator it = d->m_previews.begin(); it !=  cend ;++it) {
843             if (it->m_widget)
844                 it->m_widget->close();
845         }
846         d->m_previews.clear();
847         d->m_updateBlocked = false;
848         emit lastPreviewClosed();
849     }
850 }
851
852 void PreviewManager::updatePreviewClosed(QWidget *w)
853 {
854     typedef PreviewManagerPrivate::PreviewDataList PreviewDataList;
855     if (d->m_updateBlocked)
856         return;
857     // Purge out all 0 or widgets to be deleted
858     for (PreviewDataList::iterator it = d->m_previews.begin(); it != d->m_previews.end() ; ) {
859         QWidget *iw = it->m_widget; // Might be 0 when catching QEvent::Destroyed
860         if (iw == 0 || iw == w) {
861             it = d->m_previews.erase(it);
862         } else {
863             ++it;
864         }
865     }
866     if (d->m_previews.empty())
867         emit lastPreviewClosed();
868 }
869
870 bool PreviewManager::eventFilter(QObject *watched, QEvent *event)
871 {
872     // Courtesy of designer
873     do {
874         if (!watched->isWidgetType())
875             break;
876         QWidget *previewWindow = qobject_cast<QWidget *>(watched);
877         if (!previewWindow || !previewWindow->isWindow())
878             break;
879
880         switch (event->type()) {
881         case QEvent::KeyPress:
882         case QEvent::ShortcutOverride:        {
883             const  QKeyEvent *keyEvent = static_cast<const QKeyEvent *>(event);
884             const int key = keyEvent->key();
885             if ((key == Qt::Key_Escape
886 #ifdef Q_WS_MAC
887                  || (keyEvent->modifiers() == Qt::ControlModifier && key == Qt::Key_Period)
888 #endif
889                  )) {
890                 previewWindow->close();
891                 return true;
892             }
893         }
894             break;
895         case QEvent::WindowActivate:
896             d->m_activePreview = previewWindow;
897             break;
898         case  QEvent::Destroy: // We don't get QEvent::Close if someone accepts a QDialog.
899             updatePreviewClosed(previewWindow);
900             break;
901         case  QEvent::Close:
902             updatePreviewClosed(previewWindow);
903             previewWindow->removeEventFilter (this);
904             break;
905         default:
906             break;
907         }
908     } while(false);
909     return QObject::eventFilter(watched, event);
910 }
911
912 int PreviewManager::previewCount() const
913 {
914     return  d->m_previews.size();
915 }
916
917 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, int deviceProfileIndex, QString *errorMessage)
918 {
919     return createPreviewPixmap(fw, configurationFromSettings(fw->core(), style), deviceProfileIndex, errorMessage);
920 }
921
922 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
923 {
924     return createPreviewPixmap(fw, style, -1, errorMessage);
925 }
926
927 QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw,
928                                             const PreviewConfiguration &pc,
929                                             int deviceProfileIndex,
930                                             QString *errorMessage)
931 {
932     QWidget *widget = createPreview(fw, pc, deviceProfileIndex, errorMessage);
933     if (!widget)
934         return QPixmap();
935     const QPixmap rc = QPixmap::grabWidget(widget);
936     widget->deleteLater();
937     return rc;
938 }
939
940 void PreviewManager::slotZoomChanged(int z)
941 {
942     if (d->m_core) { // Save the last zoom chosen by the user.
943         QDesignerSharedSettings settings(d->m_core);
944         settings.setZoom(z);
945     }
946 }
947 }
948
949 QT_END_NAMESPACE
950
951 #include "previewmanager.moc"