diff options
Diffstat (limited to 'tools/designer/src/lib/shared/zoomwidget.cpp')
-rw-r--r-- | tools/designer/src/lib/shared/zoomwidget.cpp | 578 |
1 files changed, 578 insertions, 0 deletions
diff --git a/tools/designer/src/lib/shared/zoomwidget.cpp b/tools/designer/src/lib/shared/zoomwidget.cpp new file mode 100644 index 0000000..f4ab48e --- /dev/null +++ b/tools/designer/src/lib/shared/zoomwidget.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (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 either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** 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.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@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_autoScrollSuppressed(true), + m_zoomMenu(0) +{ + 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); + if (m_autoScrollSuppressed) + scrollToOrigin(); +} + +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; +} + +bool ZoomView::isAutoScrollSuppressed() const +{ + return m_autoScrollSuppressed; +} + +void ZoomView::setAutoScrollSuppressed(bool s) +{ + m_autoScrollSuppressed = s; +} + +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() << ">ZoomWidget (" << 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()); + scrollToOrigin(); + m_widgetResizeBlocked = false; + } +} + +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 " << 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 |