--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tools/designer/src/lib/shared/zoomwidget.cpp Mon Jan 11 14:00:40 2010 +0000
@@ -0,0 +1,570 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Designer of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the Technology Preview License Agreement accompanying
+** this package.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at qt-info@nokia.com.
+**
+**
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "zoomwidget_p.h"
+
+#include <QtGui/QGraphicsScene>
+#include <QtGui/QGraphicsProxyWidget>
+#include <QtGui/QMenu>
+#include <QtGui/QAction>
+#include <QtGui/QActionGroup>
+#include <QtGui/QContextMenuEvent>
+#include <QtGui/QScrollBar>
+
+#include <QtCore/QTextStream>
+#include <QtCore/qmath.h>
+#include <QtCore/QDebug>
+#include <QtCore/QList>
+
+QT_BEGIN_NAMESPACE
+
+typedef QList<QAction*> ActionList;
+typedef QList<QGraphicsItem *> GraphicsItemList;
+
+enum { debugZoomWidget = 0 };
+
+static const int menuZoomList[] = { 100, 25, 50, 75, 125, 150 , 175, 200 };
+
+static inline QSize qCeiling(const QSizeF &s)
+{
+ return QSize(qCeil(s.width()), qCeil(s.height()));
+}
+
+namespace qdesigner_internal {
+
+// ---------- ZoomMenu
+
+ZoomMenu::ZoomMenu(QObject *parent) :
+ QObject(parent),
+ m_menuActions(new QActionGroup(this))
+{
+ connect(m_menuActions, SIGNAL(triggered(QAction*)), this, SLOT(slotZoomMenu(QAction*)));
+ const int nz = sizeof(menuZoomList)/sizeof(int);
+ for (int i = 0; i < nz; i++) {
+ const int zoom = menuZoomList[i];
+ //: Zoom factor
+ QAction *a = m_menuActions->addAction(tr("%1 %").arg(zoom));
+ a->setCheckable(true);
+ a->setData(QVariant(zoom));
+ if (zoom == 100)
+ a->setChecked(true);
+ m_menuActions->addAction(a);
+ }
+}
+
+int ZoomMenu::zoomOf(const QAction *a)
+{
+ return a->data().toInt();
+}
+
+void ZoomMenu::addActions(QMenu *m)
+{
+ const ActionList za = m_menuActions->actions();
+ const ActionList::const_iterator cend = za.constEnd();
+ for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it) {
+ m->addAction(*it);
+ if (zoomOf(*it) == 100)
+ m->addSeparator();
+ }
+}
+
+int ZoomMenu::zoom() const
+{
+ return m_menuActions->checkedAction()->data().toInt();
+}
+
+void ZoomMenu::setZoom(int percent)
+{
+ const ActionList za = m_menuActions->actions();
+ const ActionList::const_iterator cend = za.constEnd();
+ for (ActionList::const_iterator it = za.constBegin(); it != cend; ++it)
+ if (zoomOf(*it) == percent) {
+ (*it)->setChecked(true);
+ return;
+ }
+}
+
+void ZoomMenu::slotZoomMenu(QAction *a)
+{
+ emit zoomChanged(zoomOf(a));
+}
+
+QList<int> ZoomMenu::zoomValues()
+{
+ QList<int> rc;
+ const int nz = sizeof(menuZoomList)/sizeof(int);
+ for (int i = 0; i < nz; i++)
+ rc.push_back(menuZoomList[i]);
+ return rc;
+}
+
+// --------- ZoomView
+ZoomView::ZoomView(QWidget *parent) :
+ QGraphicsView(parent),
+ m_scene(new QGraphicsScene(this)),
+ m_zoom(100),
+ m_zoomFactor(1.0),
+ m_zoomContextMenuEnabled(false),
+ m_zoomMenu(0)
+{
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setFrameShape(QFrame::NoFrame);
+ setScene(m_scene);
+ if (debugZoomWidget)
+ qDebug() << "scene" << m_scene->sceneRect();
+
+}
+
+int ZoomView::zoom() const
+{
+ return m_zoom;
+}
+
+void ZoomView::scrollToOrigin()
+{
+ const QPoint origin(0 ,0);
+ const QPoint current = scrollPosition();
+ if (current != origin) {
+ if (debugZoomWidget)
+ qDebug() << "ZoomView::scrollToOrigin from " << current;
+ setScrollPosition(origin);
+ }
+}
+
+void ZoomView::setZoom(int percent)
+{
+ if (debugZoomWidget)
+ qDebug() << "ZoomView::setZoom" << percent;
+
+ if (m_zoom == percent)
+ return;
+
+ m_zoom = percent;
+ const qreal hundred = 100.0;
+ m_zoomFactor = static_cast<qreal>(m_zoom) / hundred;
+
+ applyZoom();
+ if (m_zoomMenu) // Do not force them into existence
+ m_zoomMenu->setZoom(m_zoom);
+
+ resetTransform();
+ scale(m_zoomFactor, m_zoomFactor);
+}
+
+void ZoomView::applyZoom()
+{
+}
+
+qreal ZoomView::zoomFactor() const
+{
+ return m_zoomFactor;
+}
+
+bool ZoomView::isZoomContextMenuEnabled() const
+{
+ return m_zoomContextMenuEnabled;
+}
+
+void ZoomView::setZoomContextMenuEnabled(bool e)
+{
+ m_zoomContextMenuEnabled = e;
+}
+
+ZoomMenu *ZoomView::zoomMenu()
+{
+ if (!m_zoomMenu) {
+ m_zoomMenu = new ZoomMenu(this);
+ m_zoomMenu->setZoom(m_zoom);
+ connect(m_zoomMenu, SIGNAL(zoomChanged(int)), this, SLOT(setZoom(int)));
+ }
+ return m_zoomMenu;
+}
+
+void ZoomView::contextMenuEvent(QContextMenuEvent *event)
+{
+ if (debugZoomWidget > 1)
+ qDebug() << "ZoomView::contextMenuEvent" << event->pos() << event->globalPos() << zoom() << '%';
+
+ if (m_zoomContextMenuEnabled) {
+ showContextMenu(event->globalPos());
+ } else {
+ QGraphicsView::contextMenuEvent(event);
+ }
+}
+
+void ZoomView::showContextMenu(const QPoint &globalPos)
+{
+ QMenu menu;
+ zoomMenu()->addActions(&menu);
+ if (debugZoomWidget) {
+ menu.addSeparator();
+ QAction *da = menu.addAction(QLatin1String("Dump"));
+ connect(da, SIGNAL(triggered()), this, SLOT(dump()));
+ }
+ menu.exec(globalPos);
+}
+
+QPoint ZoomView::scrollPosition() const
+{
+ return QPoint(horizontalScrollBar()->value(), verticalScrollBar()->value());
+}
+
+void ZoomView::setScrollPosition(const QPoint& pos)
+{
+ horizontalScrollBar()->setValue(pos.x());
+ verticalScrollBar()->setValue(pos.y());
+}
+
+// -------------- ZoomProxyWidget
+ZoomProxyWidget::ZoomProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) :
+ QGraphicsProxyWidget(parent, wFlags)
+{
+}
+
+QVariant ZoomProxyWidget::itemChange(GraphicsItemChange change, const QVariant &value)
+{
+ switch (change) {
+ case ItemPositionChange: {
+ const QPointF newPos = value.toPointF();
+ const QPointF desiredPos = QPointF(0, 0);
+ if (newPos != desiredPos && debugZoomWidget)
+ qDebug() << "ZoomProxyWidget::itemChange: refusing " << newPos;
+ return desiredPos;
+ }
+ default:
+ break;
+ }
+ return QGraphicsProxyWidget::itemChange(change, value);
+}
+
+/* ZoomedEventFilterRedirector: Event filter for the zoomed widget.
+ * It redirects the events to another handler of ZoomWidget as its
+ * base class QScrollArea also implements eventFilter() for its viewport. */
+
+static const char *zoomedEventFilterRedirectorNameC = "__qt_ZoomedEventFilterRedirector";
+
+class ZoomedEventFilterRedirector : public QObject {
+ Q_DISABLE_COPY(ZoomedEventFilterRedirector)
+
+public:
+ explicit ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent);
+ virtual bool eventFilter(QObject *watched, QEvent *event);
+
+private:
+ ZoomWidget *m_zw;
+};
+
+ZoomedEventFilterRedirector::ZoomedEventFilterRedirector(ZoomWidget *zw, QObject *parent) :
+ QObject(parent),
+ m_zw(zw)
+{
+ setObjectName(QLatin1String(zoomedEventFilterRedirectorNameC));
+}
+
+bool ZoomedEventFilterRedirector::eventFilter(QObject *watched, QEvent *event)
+{
+ return m_zw->zoomedEventFilter(watched, event);
+}
+
+
+// --------- ZoomWidget
+
+ZoomWidget::ZoomWidget(QWidget *parent) :
+ ZoomView(parent),
+ m_proxy(0),
+ m_viewResizeBlocked(false),
+ m_widgetResizeBlocked(false),
+ m_widgetZoomContextMenuEnabled(false)
+{
+ setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+}
+
+void ZoomWidget::setWidget(QWidget *w, Qt::WindowFlags wFlags)
+{
+ if (debugZoomWidget)
+ qDebug() << "ZoomWidget::setWidget" << w << bin << wFlags;
+
+ if (m_proxy) {
+ scene().removeItem(m_proxy);
+ if (QWidget *w = m_proxy->widget()) {
+ // remove the event filter
+ if (QObject *evf = qFindChild<QObject*>(w, QLatin1String(zoomedEventFilterRedirectorNameC)))
+ w->removeEventFilter(evf);
+ }
+ m_proxy->deleteLater();
+ }
+ // Set window flags on the outer proxy for them to take effect
+ m_proxy = createProxyWidget(0, Qt::Window);
+ m_proxy->setWidget(w);
+
+ m_proxy->setWindowFlags(wFlags);
+ scene().addItem(m_proxy);
+ w->installEventFilter(new ZoomedEventFilterRedirector(this, w));
+ resizeToWidgetSize(); // Do manually for new widget
+ m_proxy->show();
+}
+
+bool ZoomWidget::isWidgetZoomContextMenuEnabled() const
+{
+ return m_widgetZoomContextMenuEnabled;
+}
+void ZoomWidget::setWidgetZoomContextMenuEnabled(bool e)
+{
+ m_widgetZoomContextMenuEnabled = e;
+}
+
+QSize ZoomWidget::viewPortMargin() const
+{
+ return QSize(0, 0);
+}
+
+QSizeF ZoomWidget::widgetDecorationSizeF() const
+{
+ qreal left, top, right, bottom;
+ m_proxy->getWindowFrameMargins (&left, &top, &right, &bottom);
+ const QSizeF rc = QSizeF(left + right, top + bottom);
+ return rc;
+}
+
+QSize ZoomWidget::widgetSize() const
+{
+ if (m_proxy)
+ return m_proxy->widget()->size();
+ return QSize(0, 0);
+}
+
+/* Convert widget size to QGraphicsView size.
+ * Watch out for limits (0, QWIDGETSIZE_MAX); just pass them on */
+
+QSize ZoomWidget::widgetSizeToViewSize(const QSize &s, bool *ptrToValid) const
+{
+ const QSize vpMargin = viewPortMargin();
+ const QSizeF deco = widgetDecorationSizeF();
+ const int width = s.width();
+
+ QSize rc = s;
+ bool valid = false;
+ if (width != 0 && width != QWIDGETSIZE_MAX) {
+ valid = true;
+ rc.setWidth(vpMargin.width() + qCeil(deco.width() + zoomFactor() * static_cast<qreal>(width)));
+ }
+
+ const int height = s.height();
+ if (height != 0 && height != QWIDGETSIZE_MAX) {
+ valid = true;
+ rc.setHeight(vpMargin.height() + qCeil(deco.height() + zoomFactor() * static_cast<qreal>(height)));
+ }
+
+ if (ptrToValid)
+ *ptrToValid = valid;
+
+ return rc;
+}
+
+// On changing zoom: Make QGraphicsView big enough to hold the widget
+void ZoomWidget::resizeToWidgetSize()
+{
+ if (!m_proxy)
+ return;
+
+ m_viewResizeBlocked = true;
+ // Convert size, apply transformed min/max size if applicable
+ const QSize wsize = widgetSize();
+ const QSize viewSize = widgetSizeToViewSize(wsize);
+
+ bool hasMinimumSize = false;
+ const QSize minimumSize = m_proxy->widget()->minimumSize();
+ const QSize viewMinimumSize = widgetSizeToViewSize(minimumSize, &hasMinimumSize);
+
+ bool hasMaximumSize = false;
+ const QSize maximumSize = m_proxy->widget()->maximumSize();
+ const QSize viewMaximumSize = widgetSizeToViewSize(maximumSize, &hasMaximumSize);
+
+ if (debugZoomWidget) {
+ qDebug()
+ << "ZoomWidget::resizeToWidgetSize()\n"
+ << "Widget: " << wsize << "(scaled)" << (wsize * zoomFactor()) << " Min/Max" << minimumSize << maximumSize << '\n'
+ << " View: " << viewSize << hasMinimumSize << viewMinimumSize << hasMaximumSize << viewMaximumSize;
+ }
+ // Apply
+ if (hasMinimumSize)
+ setMinimumSize(viewMinimumSize);
+ if (hasMaximumSize)
+ setMaximumSize(viewMaximumSize);
+ // now resize
+ doResize(viewSize);
+ if (debugZoomWidget)
+ qDebug() << "ZoomWidget::resizeToWidgetSize(): resulting view size" << size();
+ m_viewResizeBlocked = false;
+}
+
+void ZoomWidget::applyZoom()
+{
+ resizeToWidgetSize();
+}
+
+/* virtual */ void ZoomWidget::doResize(const QSize &s)
+{
+ if (debugZoomWidget > 1)
+ qDebug() << ">ZoomWidget::doResize() " << s;
+ resize(s);
+}
+
+void ZoomWidget::resizeEvent(QResizeEvent *event)
+{
+ /* QGraphicsView Resized from outside: Adapt widget. For some reason,
+ * the size passed in the event is not to be trusted. This might be due
+ * to some QScrollArea event fiddling. Have QScrollArea resize first
+ * and the use the size ZoomView::resizeEvent(event); */
+ if (m_proxy && !m_viewResizeBlocked) {
+ if (debugZoomWidget > 1)
+ qDebug() << '>' << Q_FUNC_INFO << size() << ")::resizeEvent from " << event->oldSize() << " to " << event->size();
+ const QSizeF newViewPortSize = size() - viewPortMargin();
+ const QSizeF widgetSizeF = newViewPortSize / zoomFactor() - widgetDecorationSizeF();
+ m_widgetResizeBlocked = true;
+ m_proxy->widget()->resize(widgetSizeF.toSize());
+ setSceneRect(QRectF(QPointF(0, 0), widgetSizeF));
+ scrollToOrigin();
+ m_widgetResizeBlocked = false;
+ if (debugZoomWidget > 1)
+ qDebug() << '<' << Q_FUNC_INFO << widgetSizeF << m_proxy->widget()->size() << m_proxy->size();
+ }
+}
+
+QSize ZoomWidget::minimumSizeHint() const
+{
+ if (!m_proxy)
+ return QGraphicsView::minimumSizeHint();
+
+ const QSizeF wmsh = m_proxy->widget()->minimumSizeHint();
+ const QSize rc = viewPortMargin() + (wmsh * zoomFactor()).toSize();
+ if (debugZoomWidget > 1)
+ qDebug() << "minimumSizeHint()" << rc;
+ return rc;
+}
+
+QSize ZoomWidget::sizeHint() const
+{
+ if (!m_proxy)
+ return QGraphicsView::sizeHint();
+
+ const QSizeF wsh = m_proxy->widget()->sizeHint();
+ const QSize rc = viewPortMargin() + (wsh * zoomFactor()).toSize();
+ if (debugZoomWidget > 1)
+ qDebug() << "sizeHint()" << rc;
+ return rc;
+}
+
+bool ZoomWidget::zoomedEventFilter(QObject * /*watched*/, QEvent *event)
+{
+ switch (event->type()) {
+ case QEvent::KeyPress:
+ if (debugZoomWidget) { // Debug helper: Press 'D' on the zoomed widget
+ const QKeyEvent *kevent = static_cast<QKeyEvent*>(event);
+ if (kevent->key() == Qt::Key_D)
+ dump();
+ }
+ break;
+ case QEvent::Resize:
+ if (debugZoomWidget > 1) {
+ const QResizeEvent *re = static_cast<const QResizeEvent *>(event);
+ qDebug() << "ZoomWidget::zoomedEventFilter" << re->oldSize() << re->size() << " at " << m_proxy->widget()->geometry();
+ }
+ if (!m_widgetResizeBlocked)
+ resizeToWidgetSize();
+
+ break;
+ case QEvent::ContextMenu:
+ if (m_widgetZoomContextMenuEnabled) {
+ // Calculate global position from scaled
+ QContextMenuEvent *ce = static_cast<QContextMenuEvent*>(event);
+ const QPointF origin = mapToGlobal(QPoint(0, 0)) - scrollPosition();
+ const QPointF pos = QPointF(origin + (QPointF(ce->pos()) * zoomFactor()));
+ showContextMenu(pos.toPoint());
+ ce->accept();
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+void ZoomWidget::setItemAcceptDrops(bool)
+{
+ if (m_proxy)
+ m_proxy->setAcceptDrops(true);
+}
+
+bool ZoomWidget::itemAcceptDrops() const
+{
+ return m_proxy ? m_proxy->acceptDrops() : false;
+}
+
+ // Factory function for QGraphicsProxyWidgets which can be overwritten. Default creates a ZoomProxyWidget
+QGraphicsProxyWidget *ZoomWidget::createProxyWidget(QGraphicsItem *parent, Qt::WindowFlags wFlags) const
+{
+ return new ZoomProxyWidget(parent, wFlags);
+}
+
+void ZoomWidget::dump() const
+{
+
+ qDebug() << "ZoomWidget::dump " << geometry() << " Viewport " << viewport()->geometry()
+ << "Scroll: " << scrollPosition() << "Matrix: " << matrix() << " SceneRect: " << sceneRect();
+ if (m_proxy) {
+ qDebug() << "Proxy Pos: " << m_proxy->pos() << "Proxy " << m_proxy->size()
+ << "\nProxy size hint"
+ << m_proxy->effectiveSizeHint(Qt::MinimumSize)
+ << m_proxy->effectiveSizeHint(Qt::PreferredSize)
+ << m_proxy->effectiveSizeHint(Qt::MaximumSize)
+ << "\nMatrix: " << m_proxy->matrix()
+ << "\nWidget: " << m_proxy->widget()->geometry()
+ << "scaled" << (zoomFactor() * m_proxy->widget()->size());
+ }
+}
+
+} // namespace qdesigner_internal
+
+QT_END_NAMESPACE