source: trunk/tools/designer/src/lib/shared/previewmanager.cpp

Last change on this file was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 31.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
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** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at qt-info@nokia.com.
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
76QT_BEGIN_NAMESPACE
77
78static 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
89namespace {
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
108namespace 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
114class DesignerZoomProxyWidget : public ZoomProxyWidget {
115 Q_DISABLE_COPY(DesignerZoomProxyWidget)
116public:
117 DesignerZoomProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0);
118protected:
119 virtual QSizeF sizeHint(Qt::SizeHint which, const QSizeF & constraint = QSizeF() ) const;
120};
121
122DesignerZoomProxyWidget::DesignerZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) :
123 ZoomProxyWidget(parent, wFlags)
124{
125}
126
127QSizeF 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
135class DesignerZoomWidget : public ZoomWidget {
136 Q_DISABLE_COPY(DesignerZoomWidget)
137public:
138 DesignerZoomWidget(QWidget *parent = 0);
139private:
140 virtual QGraphicsProxyWidget *createProxyWidget(QGraphicsItem *parent = 0, Qt::WindowFlags wFlags = 0) const;
141};
142
143DesignerZoomWidget::DesignerZoomWidget(QWidget *parent) :
144 ZoomWidget(parent)
145{
146}
147
148QGraphicsProxyWidget *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
157class PreviewDeviceSkin : public DeviceSkin
158{
159 Q_OBJECT
160public:
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
167private 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
172protected:
173 virtual void populateContextMenu(QMenu *) {}
174
175private slots:
176 void slotDirection(QAction *);
177
178protected:
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
185private:
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
195PreviewDeviceSkin::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
211void 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
219void 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
227void 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.
237static 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
251void 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
276void 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
295void PreviewDeviceSkin::fitWidget(const QSize &size)
296{
297 view()->setFixedSize(size);
298}
299
300QMatrix 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
317class PreviewConfigurationData : public QSharedData {
318public:
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
328PreviewConfigurationData::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
339class ZoomablePreviewDeviceSkin : public PreviewDeviceSkin
340{
341 Q_OBJECT
342public:
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
348public slots:
349 void setZoomPercent(int);
350
351signals:
352 void zoomPercentChanged(int);
353
354protected:
355 virtual void populateContextMenu(QMenu *m);
356 virtual QMatrix skinTransform() const;
357 virtual void fitWidget(const QSize &size);
358
359private:
360 ZoomMenu *m_zoomMenu;
361 QAction *m_zoomSubMenuAction;
362 ZoomWidget *m_zoomWidget;
363};
364
365ZoomablePreviewDeviceSkin::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
381static inline qreal zoomFactor(int percent)
382{
383 return qreal(percent) / 100.0;
384}
385
386static inline QSize scaleSize(int zoomPercent, const QSize &size)
387{
388 return zoomPercent == 100 ? size : (QSizeF(size) * zoomFactor(zoomPercent)).toSize();
389}
390
391void ZoomablePreviewDeviceSkin::setPreview(QWidget *formWidget)
392{
393 m_zoomWidget->setWidget(formWidget);
394 m_zoomWidget->resize(scaleSize(zoomPercent(), screenSize()));
395}
396
397int ZoomablePreviewDeviceSkin::zoomPercent() const
398{
399 return m_zoomWidget->zoom();
400}
401
402void 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
417void 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
429QMatrix 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
441void ZoomablePreviewDeviceSkin::fitWidget(const QSize &size)
442{
443 m_zoomWidget->resize(scaleSize(zoomPercent(), size));
444}
445
446// ------------- PreviewConfiguration
447
448static const char *styleKey = "Style";
449static const char *appStyleSheetKey = "AppStyleSheet";
450static const char *skinKey = "Skin";
451
452PreviewConfiguration::PreviewConfiguration() :
453 m_d(new PreviewConfigurationData)
454{
455}
456
457PreviewConfiguration::PreviewConfiguration(const QString &sty, const QString &applicationSheet, const QString &skin) :
458 m_d(new PreviewConfigurationData(sty, applicationSheet, skin))
459{
460}
461
462PreviewConfiguration::PreviewConfiguration(const PreviewConfiguration &o) :
463 m_d(o.m_d)
464{
465}
466
467PreviewConfiguration &PreviewConfiguration::operator=(const PreviewConfiguration &o)
468{
469 m_d.operator=(o.m_d);
470 return *this;
471}
472
473PreviewConfiguration::~PreviewConfiguration()
474{
475}
476
477void 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
485QString PreviewConfiguration::style() const
486{
487 return m_d->m_style;
488}
489
490void 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()).
496QString PreviewConfiguration::applicationStyleSheet() const
497{
498 return m_d->m_applicationStyleSheet;
499}
500
501void PreviewConfiguration::setApplicationStyleSheet(const QString &as)
502{
503 m_d->m_applicationStyleSheet = as;
504}
505
506QString PreviewConfiguration::deviceSkin() const
507{
508 return m_d->m_deviceSkin;
509}
510
511void PreviewConfiguration::setDeviceSkin(const QString &s)
512{
513 m_d->m_deviceSkin = s;
514}
515
516void 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
526void 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
548QDESIGNER_SHARED_EXPORT bool operator<(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
549{
550 return compare(pc1, pc2) < 0;
551}
552
553QDESIGNER_SHARED_EXPORT bool operator==(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
554{
555 return compare(pc1, pc2) == 0;
556}
557
558QDESIGNER_SHARED_EXPORT bool operator!=(const PreviewConfiguration &pc1, const PreviewConfiguration &pc2)
559{
560 return compare(pc1, pc2) != 0;
561}
562
563// ------------- PreviewManagerPrivate
564class PreviewManagerPrivate {
565public:
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
583PreviewManagerPrivate::PreviewManagerPrivate(PreviewManager::PreviewMode mode) :
584 m_mode(mode),
585 m_core(0),
586 m_updateBlocked(false)
587{
588}
589
590// ------------- PreviewManager
591
592PreviewManager::PreviewManager(PreviewMode mode, QObject *parent) :
593 QObject(parent),
594 d(new PreviewManagerPrivate(mode))
595{
596}
597
598PreviewManager:: ~PreviewManager()
599{
600 delete d;
601}
602
603
604Qt::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
618QWidget *PreviewManager::createDeviceSkinContainer(const QDesignerFormWindowInterface *fw) const
619{
620 return new QDialog(fw->window());
621}
622
623// Some widgets might require fake containers
624
625static 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
646static 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
657QWidget *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
662QWidget *PreviewManager::showPreview(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
663{
664 return showPreview(fw, style, -1, errorMessage);
665}
666
667QWidget *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
748QWidget *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
808QWidget *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
827void 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
844void 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
862bool 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
904int PreviewManager::previewCount() const
905{
906 return d->m_previews.size();
907}
908
909QPixmap 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
914QPixmap PreviewManager::createPreviewPixmap(const QDesignerFormWindowInterface *fw, const QString &style, QString *errorMessage)
915{
916 return createPreviewPixmap(fw, style, -1, errorMessage);
917}
918
919QPixmap 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
932void 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
941QT_END_NAMESPACE
942
943#include "previewmanager.moc"
Note: See TracBrowser for help on using the repository browser.