source: trunk/tools/designer/src/lib/shared/zoomwidget.cpp@ 1168

Last change on this file since 1168 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: 16.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
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 [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "zoomwidget_p.h"
43
44#include <QtGui/QGraphicsScene>
45#include <QtGui/QGraphicsProxyWidget>
46#include <QtGui/QMenu>
47#include <QtGui/QAction>
48#include <QtGui/QActionGroup>
49#include <QtGui/QContextMenuEvent>
50#include <QtGui/QScrollBar>
51
52#include <QtCore/QTextStream>
53#include <QtCore/qmath.h>
54#include <QtCore/QDebug>
55#include <QtCore/QList>
56
57QT_BEGIN_NAMESPACE
58
59typedef QList<QAction*> ActionList;
60typedef QList<QGraphicsItem *> GraphicsItemList;
61
62enum { debugZoomWidget = 0 };
63
64static const int menuZoomList[] = { 100, 25, 50, 75, 125, 150 , 175, 200 };
65
66static inline QSize qCeiling(const QSizeF &s)
67{
68 return QSize(qCeil(s.width()), qCeil(s.height()));
69}
70
71namespace qdesigner_internal {
72
73// ---------- ZoomMenu
74
75ZoomMenu::ZoomMenu(QObject *parent) :
76 QObject(parent),
77 m_menuActions(new QActionGroup(this))
78{
79 connect(m_menuActions, SIGNAL(triggered(QAction*)), this, SLOT(slotZoomMenu(QAction*)));
80 const int nz = sizeof(menuZoomList)/sizeof(int);
81 for (int i = 0; i < nz; i++) {
82 const int zoom = menuZoomList[i];
83 //: Zoom factor
84 QAction *a = m_menuActions->addAction(tr("%1 %").arg(zoom));
85 a->setCheckable(true);
86 a->setData(QVariant(zoom));
87 if (zoom == 100)
88 a->setChecked(true);
89 m_menuActions->addAction(a);
90 }
91}
92
93int ZoomMenu::zoomOf(const QAction *a)
94{
95 return a->data().toInt();
96}
97
98void ZoomMenu::addActions(QMenu *m)
99{
100 const ActionList za = m_menuActions->actions();
101 const ActionList::const_iterator cend = za.constEnd();
102 for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) {
103 m->addAction(*it);
104 if (zoomOf(*it) == 100)
105 m->addSeparator();
106 }
107}
108
109int ZoomMenu::zoom() const
110{
111 return m_menuActions->checkedAction()->data().toInt();
112}
113
114void ZoomMenu::setZoom(int percent)
115{
116 const ActionList za = m_menuActions->actions();
117 const ActionList::const_iterator cend = za.constEnd();
118 for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it)
119 if (zoomOf(*it) == percent) {
120 (*it)->setChecked(true);
121 return;
122 }
123}
124
125void ZoomMenu::slotZoomMenu(QAction *a)
126{
127 emit zoomChanged(zoomOf(a));
128}
129
130QList<int> ZoomMenu::zoomValues()
131{
132 QList<int> rc;
133 const int nz = sizeof(menuZoomList)/sizeof(int);
134 for (int i = 0; i < nz; i++)
135 rc.push_back(menuZoomList[i]);
136 return rc;
137}
138
139// --------- ZoomView
140ZoomView::ZoomView(QWidget *parent) :
141 QGraphicsView(parent),
142 m_scene(new QGraphicsScene(this)),
143 m_zoom(100),
144 m_zoomFactor(1.0),
145 m_zoomContextMenuEnabled(false),
146 m_zoomMenu(0)
147{
148 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
149 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
150 setFrameShape(QFrame::NoFrame);
151 setScene(m_scene);
152 if (debugZoomWidget)
153 qDebug() << "scene" << m_scene->sceneRect();
154
155}
156
157int ZoomView::zoom() const
158{
159 return m_zoom;
160}
161
162void ZoomView::scrollToOrigin()
163{
164 const QPoint origin(0 ,0);
165 const QPoint current = scrollPosition();
166 if (current != origin) {
167 if (debugZoomWidget)
168 qDebug() << "ZoomView::scrollToOrigin from " << current;
169 setScrollPosition(origin);
170 }
171}
172
173void ZoomView::setZoom(int percent)
174{
175 if (debugZoomWidget)
176 qDebug() << "ZoomView::setZoom" << percent;
177
178 if (m_zoom == percent)
179 return;
180
181 m_zoom = percent;
182 const qreal hundred = 100.0;
183 m_zoomFactor = static_cast<qreal>(m_zoom) / hundred;
184
185 applyZoom();
186 if (m_zoomMenu) // Do not force them into existence
187 m_zoomMenu->setZoom(m_zoom);
188
189 resetTransform();
190 scale(m_zoomFactor, m_zoomFactor);
191}
192
193void ZoomView::applyZoom()
194{
195}
196
197qreal ZoomView::zoomFactor() const
198{
199 return m_zoomFactor;
200}
201
202bool ZoomView::isZoomContextMenuEnabled() const
203{
204 return m_zoomContextMenuEnabled;
205}
206
207void ZoomView::setZoomContextMenuEnabled(bool e)
208{
209 m_zoomContextMenuEnabled = e;
210}
211
212ZoomMenu *ZoomView::zoomMenu()
213{
214 if (!m_zoomMenu) {
215 m_zoomMenu = new ZoomMenu(this);
216 m_zoomMenu->setZoom(m_zoom);
217 connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoom(int)));
218 }
219 return m_zoomMenu;
220}
221
222void ZoomView::contextMenuEvent(QContextMenuEvent *event)
223{
224 if (debugZoomWidget > 1)
225 qDebug() << "ZoomView::contextMenuEvent" << event->pos() << event->globalPos() << zoom() << '%';
226
227 if (m_zoomContextMenuEnabled) {
228 showContextMenu(event->globalPos());
229 } else {
230 QGraphicsView::contextMenuEvent(event);
231 }
232}
233
234void ZoomView::showContextMenu(const QPoint &globalPos)
235{
236 QMenu menu;
237 zoomMenu()->addActions(&menu);
238 if (debugZoomWidget) {
239 menu.addSeparator();
240 QAction *da = menu.addAction(QLatin1String("Dump"));
241 connect(da, SIGNAL(triggered()), this, SLOT(dump()));
242 }
243 menu.exec(globalPos);
244}
245
246QPoint ZoomView::scrollPosition() const
247{
248 return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
249}
250
251void ZoomView::setScrollPosition(const QPoint& pos)
252{
253 horizontalScrollBar()->setValue(pos.x());
254 verticalScrollBar()->setValue(pos.y());
255}
256
257// -------------- ZoomProxyWidget
258ZoomProxyWidget::ZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) :
259 QGraphicsProxyWidget(parent, wFlags)
260{
261}
262
263QVariant ZoomProxyWidget::itemChange(GraphicsItemChange change, const QVariant &value)
264{
265 switch (change) {
266 case ItemPositionChange: {
267 const QPointF newPos = value.toPointF();
268 const QPointF desiredPos = QPointF(0, 0);
269 if (newPos != desiredPos && debugZoomWidget)
270 qDebug() << "ZoomProxyWidget::itemChange: refusing " << newPos;
271 return desiredPos;
272 }
273 default:
274 break;
275 }
276 return QGraphicsProxyWidget::itemChange(change, value);
277}
278
279/* ZoomedEventFilterRedirector: Event filter for the zoomed widget.
280 * It redirects the events to another handler of ZoomWidget as its
281 * base class QScrollArea also implements eventFilter() for its viewport. */
282
283static const char *zoomedEventFilterRedirectorNameC = "__qt_ZoomedEventFilterRedirector";
284
285class ZoomedEventFilterRedirector : public QObject {
286 Q_DISABLE_COPY(ZoomedEventFilterRedirector)
287
288public:
289 explicit ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent);
290 virtual bool eventFilter(QObject *watched, QEvent *event);
291
292private:
293 ZoomWidget *m_zw;
294};
295
296ZoomedEventFilterRedirector::ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent) :
297 QObject(parent),
298 m_zw(zw)
299{
300 setObjectName(QLatin1String(zoomedEventFilterRedirectorNameC));
301}
302
303bool ZoomedEventFilterRedirector::eventFilter(QObject *watched, QEvent *event)
304{
305 return m_zw->zoomedEventFilter(watched, event);
306}
307
308
309// --------- ZoomWidget
310
311ZoomWidget::ZoomWidget(QWidget *parent) :
312 ZoomView(parent),
313 m_proxy(0),
314 m_viewResizeBlocked(false),
315 m_widgetResizeBlocked(false),
316 m_widgetZoomContextMenuEnabled(false)
317{
318 setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
319 setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
320}
321
322void ZoomWidget::setWidget(QWidget *w, Qt::WindowFlags wFlags)
323{
324 if (debugZoomWidget)
325 qDebug() << "ZoomWidget::setWidget" << w << bin << wFlags;
326
327 if (m_proxy) {
328 scene().removeItem(m_proxy);
329 if (QWidget *w = m_proxy->widget()) {
330 // remove the event filter
331 if (QObject *evf = qFindChild<QObject*>(w, QLatin1String(zoomedEventFilterRedirectorNameC)))
332 w->removeEventFilter(evf);
333 }
334 m_proxy->deleteLater();
335 }
336 // Set window flags on the outer proxy for them to take effect
337 m_proxy = createProxyWidget(0, Qt::Window);
338 m_proxy->setWidget(w);
339
340 m_proxy->setWindowFlags(wFlags);
341 scene().addItem(m_proxy);
342 w->installEventFilter(new ZoomedEventFilterRedirector(this, w));
343 resizeToWidgetSize(); // Do manually for new widget
344 m_proxy->show();
345}
346
347bool ZoomWidget::isWidgetZoomContextMenuEnabled() const
348{
349 return m_widgetZoomContextMenuEnabled;
350}
351void ZoomWidget::setWidgetZoomContextMenuEnabled(bool e)
352{
353 m_widgetZoomContextMenuEnabled = e;
354}
355
356QSize ZoomWidget::viewPortMargin() const
357{
358 return QSize(0, 0);
359}
360
361QSizeF ZoomWidget::widgetDecorationSizeF() const
362{
363 qreal left, top, right, bottom;
364 m_proxy->getWindowFrameMargins (&left, &top, &right, &bottom);
365 const QSizeF rc = QSizeF(left + right, top + bottom);
366 return rc;
367}
368
369QSize ZoomWidget::widgetSize() const
370{
371 if (m_proxy)
372 return m_proxy->widget()->size();
373 return QSize(0, 0);
374}
375
376/* Convert widget size to QGraphicsView size.
377 * Watch out for limits (0, QWIDGETSIZE_MAX); just pass them on */
378
379QSize ZoomWidget::widgetSizeToViewSize(const QSize &s, bool *ptrToValid) const
380{
381 const QSize vpMargin = viewPortMargin();
382 const QSizeF deco = widgetDecorationSizeF();
383 const int width = s.width();
384
385 QSize rc = s;
386 bool valid = false;
387 if (width != 0 && width != QWIDGETSIZE_MAX) {
388 valid = true;
389 rc.setWidth(vpMargin.width() + qCeil(deco.width() + zoomFactor() * static_cast<qreal>(width)));
390 }
391
392 const int height = s.height();
393 if (height != 0 && height != QWIDGETSIZE_MAX) {
394 valid = true;
395 rc.setHeight(vpMargin.height() + qCeil(deco.height() + zoomFactor() * static_cast<qreal>(height)));
396 }
397
398 if (ptrToValid)
399 *ptrToValid = valid;
400
401 return rc;
402}
403
404// On changing zoom: Make QGraphicsView big enough to hold the widget
405void ZoomWidget::resizeToWidgetSize()
406{
407 if (!m_proxy)
408 return;
409
410 m_viewResizeBlocked = true;
411 // Convert size, apply transformed min/max size if applicable
412 const QSize wsize = widgetSize();
413 const QSize viewSize = widgetSizeToViewSize(wsize);
414
415 bool hasMinimumSize = false;
416 const QSize minimumSize = m_proxy->widget()->minimumSize();
417 const QSize viewMinimumSize = widgetSizeToViewSize(minimumSize, &hasMinimumSize);
418
419 bool hasMaximumSize = false;
420 const QSize maximumSize = m_proxy->widget()->maximumSize();
421 const QSize viewMaximumSize = widgetSizeToViewSize(maximumSize, &hasMaximumSize);
422
423 if (debugZoomWidget) {
424 qDebug()
425 << "ZoomWidget::resizeToWidgetSize()\n"
426 << "Widget: " << wsize << "(scaled)" << (wsize * zoomFactor()) << " Min/Max" << minimumSize << maximumSize << '\n'
427 << " View: " << viewSize << hasMinimumSize << viewMinimumSize << hasMaximumSize << viewMaximumSize;
428 }
429 // Apply
430 if (hasMinimumSize)
431 setMinimumSize(viewMinimumSize);
432 if (hasMaximumSize)
433 setMaximumSize(viewMaximumSize);
434 // now resize
435 doResize(viewSize);
436 if (debugZoomWidget)
437 qDebug() << "ZoomWidget::resizeToWidgetSize(): resulting view size" << size();
438 m_viewResizeBlocked = false;
439}
440
441void ZoomWidget::applyZoom()
442{
443 resizeToWidgetSize();
444}
445
446/* virtual */ void ZoomWidget::doResize(const QSize &s)
447{
448 if (debugZoomWidget > 1)
449 qDebug() << ">ZoomWidget::doResize() " << s;
450 resize(s);
451}
452
453void ZoomWidget::resizeEvent(QResizeEvent *event)
454{
455 /* QGraphicsView Resized from outside: Adapt widget. For some reason,
456 * the size passed in the event is not to be trusted. This might be due
457 * to some QScrollArea event fiddling. Have QScrollArea resize first
458 * and the use the size ZoomView::resizeEvent(event); */
459 if (m_proxy && !m_viewResizeBlocked) {
460 if (debugZoomWidget > 1)
461 qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size();
462 const QSizeF newViewPortSize = size() - viewPortMargin();
463 const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF();
464 m_widgetResizeBlocked = true;
465 m_proxy->widget()->resize(widgetSizeF.toSize());
466 setSceneRect(QRectF(QPointF(0, 0), widgetSizeF));
467 scrollToOrigin();
468 m_widgetResizeBlocked = false;
469 if (debugZoomWidget > 1)
470 qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size();
471 }
472}
473
474QSize ZoomWidget::minimumSizeHint() const
475{
476 if (!m_proxy)
477 return QGraphicsView::minimumSizeHint();
478
479 const QSizeF wmsh = m_proxy->widget()->minimumSizeHint();
480 const QSize rc = viewPortMargin() + (wmsh * zoomFactor()).toSize();
481 if (debugZoomWidget > 1)
482 qDebug() << "minimumSizeHint()" << rc;
483 return rc;
484}
485
486QSize ZoomWidget::sizeHint() const
487{
488 if (!m_proxy)
489 return QGraphicsView::sizeHint();
490
491 const QSizeF wsh = m_proxy->widget()->sizeHint();
492 const QSize rc = viewPortMargin() + (wsh * zoomFactor()).toSize();
493 if (debugZoomWidget > 1)
494 qDebug() << "sizeHint()" << rc;
495 return rc;
496}
497
498bool ZoomWidget::zoomedEventFilter(QObject * /*watched*/, QEvent *event)
499{
500 switch (event->type()) {
501 case QEvent::KeyPress:
502 if (debugZoomWidget) { // Debug helper: Press 'D' on the zoomed widget
503 const QKeyEvent *kevent = static_cast<QKeyEvent*>(event);
504 if (kevent->key() == Qt::Key_D)
505 dump();
506 }
507 break;
508 case QEvent::Resize:
509 if (debugZoomWidget > 1) {
510 const QResizeEvent *re = static_cast<const QResizeEvent *>(event);
511 qDebug() << "ZoomWidget::zoomedEventFilter" << re->oldSize() << re->size() << " at " << m_proxy->widget()->geometry();
512 }
513 if (!m_widgetResizeBlocked)
514 resizeToWidgetSize();
515
516 break;
517 case QEvent::ContextMenu:
518 if (m_widgetZoomContextMenuEnabled) {
519 // Calculate global position from scaled
520 QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event);
521 const QPointF origin = mapToGlobal(QPoint(0, 0)) - scrollPosition();
522 const QPointF pos = QPointF(origin + (QPointF(ce->pos()) * zoomFactor()));
523 showContextMenu(pos.toPoint());
524 ce->accept();
525 return true;
526 }
527 break;
528 default:
529 break;
530 }
531 return false;
532}
533
534void ZoomWidget::setItemAcceptDrops(bool)
535{
536 if (m_proxy)
537 m_proxy->setAcceptDrops(true);
538}
539
540bool ZoomWidget::itemAcceptDrops() const
541{
542 return m_proxy ? m_proxy->acceptDrops() : false;
543}
544
545 // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget
546QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const
547{
548 return new ZoomProxyWidget(parent, wFlags);
549}
550
551void ZoomWidget::dump() const
552{
553
554 qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry()
555 << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect();
556 if (m_proxy) {
557 qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size()
558 << "\nProxy size hint"
559 << m_proxy->effectiveSizeHint(Qt::MinimumSize)
560 << m_proxy->effectiveSizeHint(Qt::PreferredSize)
561 << m_proxy->effectiveSizeHint(Qt::MaximumSize)
562 << "\nMatrix: " << m_proxy->matrix()
563 << "\nWidget: " << m_proxy->widget()->geometry()
564 << "scaled" << (zoomFactor() * m_proxy->widget()->size());
565 }
566}
567
568} // namespace qdesigner_internal
569
570QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.