--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/gui/hbpopupmanager.cpp Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** 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 developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include "hbpopupmanager_p.h"
+#include "hbpopupmanager_p_p.h"
+#include "hbpopup.h"
+#include "hbpopup_p.h"
+#include "hbnamespace_p.h"
+#include "hbinstance.h"
+#include "hbmainwindow_p.h"
+#include "hbevent.h"
+#include "hbwidget.h"
+#include "hbgraphicsscene.h"
+#include "hbmenu.h"
+#include "hbmenu_p.h"
+
+#include <QGraphicsWidget>
+#include <QGraphicsSceneMouseEvent>
+#include <QGraphicsLayout>
+#include <QDebug>
+
+
+HbPopupLayoutSpacer::HbPopupLayoutSpacer( QGraphicsItem *parent )
+: HbWidgetBase( parent )
+{
+#if QT_VERSION >= 0x040600
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+#endif
+ setAcceptedMouseButtons(Qt::NoButton);
+}
+
+QSizeF HbPopupLayoutSpacer::sizeHint(Qt::SizeHint which, const QSizeF &constraint ) const
+{
+ Q_UNUSED( constraint );
+
+ if ( which == Qt::MaximumSize ) {
+ return QSizeF( QWIDGETSIZE_MAX, QWIDGETSIZE_MAX );
+ }
+ return QSizeF(0.f,0.f);
+}
+
+/*
+\reimp
+*/
+void HbPopupLayoutSpacer::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget);
+ Q_UNUSED(painter);
+}
+
+
+HbPopupLayoutProxy::HbPopupLayoutProxy( HbPopup *popup, QGraphicsItem *parent )
+: HbWidgetBase(parent), mPopup(popup)
+{
+#if QT_VERSION >= 0x040600
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+#endif
+ setAcceptedMouseButtons(Qt::NoButton);
+ popup->installEventFilter(this);
+}
+
+QSizeF HbPopupLayoutProxy::sizeHint(Qt::SizeHint which, const QSizeF &constraint ) const
+{
+ if (!mPopup.isNull()) {
+ return mPopup->effectiveSizeHint(which, constraint);
+ }
+ return QSizeF(0, 0);
+}
+
+void HbPopupLayoutProxy::setGeometry(const QRectF &rect)
+{
+ if (!mPopup.isNull()) {
+ const QSizeF popupPreferredSize =
+ mPopup->effectiveSizeHint(Qt::PreferredSize);
+ const QSizeF usedSize( qMin(rect.width(), popupPreferredSize.width() ),
+ qMin( rect.height(), popupPreferredSize.height() ) );
+ QPointF usedPos(rect.left(), rect.top());
+ mPreferredPos = ((HbPopupPrivate*)HbPopupPrivate::d_ptr(mPopup))->preferredPos;
+ mPlacement = ((HbPopupPrivate*)HbPopupPrivate::d_ptr(mPopup))->placement;
+ mPreferredPosSet = ((HbPopupPrivate*)HbPopupPrivate::d_ptr(mPopup))->preferredPosSet;
+ if ( mPreferredPosSet ) { // user set preferred pos, ignore css
+ const qreal pw(parentItem()->boundingRect().width());
+ const qreal ph(parentItem()->boundingRect().height());
+ const qreal uw(usedSize.width());
+ const qreal uh(usedSize.height());
+ const qreal x(mPreferredPos.x());
+ const qreal y(mPreferredPos.y());
+ qreal usedx = 0;
+ qreal usedy = 0;
+ switch( mPlacement ) {
+ case HbPopup::TopLeftCorner:
+ usedx = qMin(x, pw-uw);
+ usedy = qMin(y, ph-uh);
+ break;
+ case HbPopup::TopRightCorner:
+ usedx = qMin(x-uw, pw-uw);
+ usedy = qMin(y, ph-uh);
+ break;
+ case HbPopup::BottomLeftCorner:
+ usedx = qMin(x, pw-uw);
+ usedy = qMin(y-uh, ph-uh);
+ break;
+ case HbPopup::BottomRightCorner:
+ usedx = qMin(x-uw, pw-uw);
+ usedy = qMin(y-uh, ph-uh);
+ break;
+ case HbPopup::TopEdgeCenter:
+ usedx = qMin(x-uw/2, pw-uw);
+ usedy = qMin(y, ph-uh);
+ break;
+ case HbPopup::RightEdgeCenter:
+ usedx = qMin(x-uw, pw-uw);
+ usedy = qMin(y-uh/2, ph-uh);
+ break;
+ case HbPopup::BottomEdgeCenter:
+ usedx = qMin(x-uw/2, pw-uw);
+ usedy = qMin(y-uh, ph-uh);
+ break;
+ case HbPopup::LeftEdgeCenter:
+ usedx = qMin(x, pw-uw);
+ usedy = qMin(y-uh/2, ph-uh);
+ break;
+ case HbPopup::Center:
+ usedx = qMin(x-uw/2, pw-uw);
+ usedy = qMin(y-uh/2, ph-uh);
+ break;
+ default:
+ //should not happen
+ break;
+ }
+ if ( usedx < 0 ) usedx = 0;
+ if ( usedy < 0 ) usedy = 0;
+
+ usedPos = QPointF(usedx, usedy);
+ }
+ mPopup->setGeometry(QRectF(usedPos, usedSize));
+ }
+}
+
+bool HbPopupLayoutProxy::event( QEvent *e )
+{
+ switch ( e->type() )
+ {
+ case QEvent::Polish:
+ polishEvent();
+ return QObject::event(e);
+ default:
+ return HbWidgetBase::event( e );
+ }
+}
+
+bool HbPopupLayoutProxy::eventFilter(QObject *obj, QEvent *event)
+{
+ Q_UNUSED( obj );
+ switch( event->type() ) {
+ case QEvent::LayoutRequest: {
+ updateGeometry();
+ break;
+ }
+ case QEvent::ContextMenu: {
+ updateGeometry();
+ break;
+ }
+ default:
+ break;
+ }
+ return false;
+}
+
+
+/*
+\reimp
+*/
+void HbPopupLayoutProxy::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
+{
+ Q_UNUSED(option);
+ Q_UNUSED(widget);
+ Q_UNUSED(painter);
+}
+
+HbPopupLayoutManager::HbPopupLayoutManager( HbPopup *popup, QGraphicsScene *scene )
+: HbWidget()
+{
+#if QT_VERSION >= 0x040600
+ setFlag(QGraphicsItem::ItemHasNoContents, true);
+#endif
+ setAcceptedMouseButtons(Qt::NoButton);
+ scene->addItem( this );
+
+ // create proxy
+ HbPopupLayoutProxy *childItem = new HbPopupLayoutProxy( popup, this );
+
+ // create spacers
+ HbStyle::setItemName( new HbPopupLayoutSpacer(this), "spacerTop");
+ HbStyle::setItemName( new HbPopupLayoutSpacer(this), "spacerBottom");
+ HbStyle::setItemName( new HbPopupLayoutSpacer(this), "spacerLeft");
+ HbStyle::setItemName( new HbPopupLayoutSpacer(this), "spacerRight");
+
+ const QMetaObject *metaObject = popup->metaObject();
+ if ( metaObject->className() == QLatin1String("HbMenu") ) {
+ HbMenu *menu = qobject_cast<HbMenu*>(popup);
+ if (menu && menu->menuType() == HbMenu::OptionsMenu) {
+ HbStyle::setItemName( childItem, "options-menu" );
+ } else {
+ HbStyle::setItemName( childItem, "menu" );
+ }
+ } else if ( metaObject->className() == QLatin1String("HbToolTipLabel") ) {
+ HbStyle::setItemName( childItem, "" );
+ } else if ( metaObject->className() == QLatin1String("HbToolBarExtension" ) ) {
+ HbStyle::setItemName( childItem, "toolbar-extension" );
+ } else {
+ HbStyle::setItemName( childItem, "popup" );
+ }
+ if ( metaObject->className() == QLatin1String("HbVolumeSliderPopup") ) {
+ HbStyle::setItemName( childItem, "volumesliderpopup" );
+ connect(scene,SIGNAL(sceneRectChanged(QRectF)),this,SLOT(orientationChanged(QRectF)));
+ }
+ if ( metaObject->className() == QLatin1String("HbZoomSliderPopup") ) {
+ HbStyle::setItemName( childItem, "zoomsliderpopup" );
+ connect(scene,SIGNAL(sceneRectChanged(QRectF)),this,SLOT(orientationChanged(QRectF)));
+ }
+}
+
+void HbPopupLayoutManager::orientationChanged(const QRectF& rect)
+{
+ Q_UNUSED(rect);
+ repolish();
+}
+
+
+/*!
+ \reimp
+*/
+QSizeF HbPopupLayoutManager::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
+{
+ Q_UNUSED(which);
+ Q_UNUSED(constraint);
+ return HbDeviceProfile::profile(this).logicalSize();
+}
+
+/*
+\reimp
+*/
+void HbPopupLayoutManager::changeEvent(QEvent *event)
+{
+ HbWidget::changeEvent(event);
+ if (event && event->type() == QEvent::LayoutDirectionChange) {
+ repolish();
+ }
+}
+
+/*
+\reimp
+*/
+void HbPopupLayoutManager::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget )
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget);
+ Q_UNUSED(painter);
+}
+
+
+/*
+ \class HbPopupManagerPrivate
+ \brief HbPopupManagerPrivate private data class for HbPopupManager.
+*/
+HbPopupManagerPrivate::HbPopupManagerPrivate(HbGraphicsScene *scene, QObject * parent):
+ QObject(parent),
+ topLevelFocusablePopup(0),
+ initialFocusedItem(0),
+ scene(scene)
+{
+ connect(&deviceFadeControl, SIGNAL(fadeChange(bool)), this,
+ SLOT(deviceFadeChange(bool)));
+}
+
+HbPopupManagerPrivate::~HbPopupManagerPrivate()
+{
+}
+
+/*
+ Deliver mouseEvent to all HbPopupBackGround object for non-modal popups until event blocking
+ item is found.
+*/
+void HbPopupManagerPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent *mouseEvent)
+{
+ foreach (QGraphicsItem *item, scene->items(mouseEvent->scenePos())) {
+ if (eventBlockItem(item)) {
+ break;
+ }
+ if (item->type() == HbPrivate::ItemType_PopupBackGround) {
+ static_cast<GraphicsItem *>(item)->sceneEvent(mouseEvent);
+ // Finish loop if popup of background is modal
+ if (static_cast<HbPopupBackGround *>(item)->isModal()) {
+ break;
+ }
+ }
+ }
+}
+
+/*
+ Returns true if item is an event blocking item.
+*/
+bool HbPopupManagerPrivate::eventBlockItem(QGraphicsItem *item)
+{
+ if (item->type() == HbPrivate::ItemType_FadeItem) {
+ return true;
+ }
+
+ return false;
+}
+
+qreal HbPopupManagerPrivate::popupZValueForPriority( int priority, int popupCountWaterMark )
+{
+ qreal ret = (qreal)(HbPrivate::PopupZValueDefaultOffset)
+ + (qreal)(priority * HbPrivate::PopupWithDifferentPriorityZValueUnit)
+ + (qreal)(popupCountWaterMark * HbPrivate::PopupWithSamePriorityZValueUnit);
+
+ Q_ASSERT (HbPrivate::PopupZValueRangeStart <= ret && ret <= HbPrivate::PopupZValueRangeEnd);
+
+ return ret;
+}
+
+qreal HbPopupManagerPrivate::nextPopupZValueForPriority( int priority )
+{
+ // The priorities are converted to Z values so that the resulting Z value is
+ // smaller than HbPrivate::PopupZValue + 1 this ensures that all the popup
+ // Z values fit in [HbPrivate::PopupZValue, HbPrivate::PopupZValue + 1] range
+ PopupCountRegister popupCountRegister = popupCountRegisters.value(priority, PopupCountRegister());
+ popupCountRegister.count++;
+ popupCountRegister.waterMark++;
+ popupCountRegisters[priority] = popupCountRegister;
+
+ return popupZValueForPriority( priority, popupCountRegister.waterMark );
+}
+
+void HbPopupManagerPrivate::updateHash( int priority, qreal zValueOfRemovedPopup )
+{
+ PopupCountRegister popupCountRegister = popupCountRegisters.value(priority);
+
+ popupCountRegister.count--;
+ // If there are no more popups for priority then reset water mark to 0
+ if (!popupCountRegister.count) {
+ popupCountRegister.waterMark = 0;
+ }
+ // If the popup that removed had the highest Z value for priority decrease the water mark
+ else if (qFuzzyCompare(popupZValueForPriority(priority, popupCountRegister.waterMark), zValueOfRemovedPopup)) {
+ popupCountRegister.waterMark--;
+ }
+ popupCountRegisters[priority] = popupCountRegister;
+}
+
+void HbPopupManagerPrivate::eventHook(QEvent *event)
+{
+ if (event && event->type() == QEvent::GraphicsSceneMouseRelease && popupList.count() && scene) {
+ mouseReleaseEvent(static_cast<QGraphicsSceneMouseEvent *>(event));
+ }
+}
+
+void HbPopupManagerPrivate::showPopup(HbPopup *popup)
+{
+ if (popup) {
+ addPopup(popup);
+ } else {
+ qWarning() << "HbPopupManager::showPopup(): null pointer passed!";
+ }
+}
+
+void HbPopupManagerPrivate::hidePopup(HbPopup *popup)
+{
+ if (popup) {
+ removePopup(popup);
+ }
+}
+
+void HbPopupManagerPrivate::addPopup(HbPopup *popup)
+{
+ if (!popupList.contains(popup)) {
+ popupList.append(popup);
+ qreal popupZvalue = nextPopupZValueForPriority(HbPopupPrivate::d_ptr(popup)->priority());
+
+ // Set popup and its background item to correct Z value
+ popup->setZValue(popupZvalue);
+ HbPopupBackGround *backgroundItem = HbPopupPrivate::d_ptr(popup)->backgroundItem;
+ if (backgroundItem) {
+ backgroundItem->setZValue(popupZvalue - HbPrivate::PopupBackgroundItemZValueUnit);
+ }
+
+ if ( !popup->parentItem() ) {
+ if ( popup->metaObject()->className() != QLatin1String("HbToolTipLabel") &&
+ popup->metaObject()->className() != QLatin1String("HbInputCustomButtonList") &&
+ popup->metaObject()->className() != QLatin1String("HbCharPreviewPane") &&
+ popup->metaObject()->className() != QLatin1String("HbCandidateList") &&
+ popup->metaObject()->className() != QLatin1String("HbExactWordPopup") &&
+ popup->metaObject()->className() != QLatin1String("HbInputSmileyPicker") &&
+ popup->metaObject()->className() != QLatin1String("Hb12KeyCustomKeypad") &&
+ !popup->inherits("HbInputVkbWidget")) {
+ setGeometryForPopup( popup );
+ }
+ }
+
+ // Check if popup is top level popup
+ if((!topLevelFocusablePopup || topLevelFocusablePopup->zValue() < popup->zValue()) &&
+ popup->flags().testFlag(QGraphicsItem::ItemIsFocusable) ) {
+
+ topLevelFocusablePopup = popup;
+
+ // If this is the first popup in the stack being launched
+ if(!initialFocusedItem && scene) {
+ initialFocusedItem = scene->focusItem();
+ }
+
+ topLevelFocusablePopup->setFocus();
+ }
+
+ updateFading();
+ }
+}
+
+void HbPopupManagerPrivate::removePopup(HbPopup *popup)
+{
+ //delete the layoutproxy and remove the popup from parentItems list
+ //only if its in its destruction
+ bool popupInDestruction = (HbPopupPrivate::d_ptr(popup))->inDestruction;
+ if ( parentItems.contains( popup ) && (popupInDestruction)) {
+ //HbPopupLayoutManager *parentItem = parentItems[popup];
+ parentItems.remove( popup );
+ //delete parentItem;
+ }
+
+ // Only execute if close if popup was in popupList before
+ if (popupList.removeOne(popup)) {
+ // Update popup count registers
+ updateHash(HbPopupPrivate::d_ptr(popup)->priority(), popup->zValue());
+
+ // Update fading
+ updateFading(true);
+
+ // Find new top level popup
+ if (topLevelFocusablePopup && topLevelFocusablePopup == popup) {
+ topLevelFocusablePopup = 0;
+ foreach (HbPopup *popup, popupList) {
+ if ((!topLevelFocusablePopup || topLevelFocusablePopup->zValue() < popup->zValue()) &&
+ popup->flags().testFlag(QGraphicsItem::ItemIsFocusable)){
+ topLevelFocusablePopup = popup;
+ }
+ }
+ }
+
+ bool wasActivePopup = true;
+ // If the previous popup was an inactive panel, then it would not have
+ // gained focused. So no need to set the focus to the next topmost popup.
+ if (popup->isPanel() && !popup->isActive()) {
+ wasActivePopup = false;
+ }
+ if (topLevelFocusablePopup && wasActivePopup ) {
+ topLevelFocusablePopup->setFocus();
+ }
+ //TODO: this is not very efficient way of deciding if has already been deleted or not
+ else if (initialFocusedItem && scene &&
+ scene->items().contains(initialFocusedItem)) {
+
+ // Move the focus to the initial focus item if there is no current focus item or
+ // the ancestor of the current fucus item is popup
+ if( !scene->focusItem() || popup->isAncestorOf(scene->focusItem())) {
+ initialFocusedItem->setFocus();
+ initialFocusedItem = 0;
+ }
+ }
+ }
+}
+
+void HbPopupManagerPrivate::updateFading(bool unfadeFirst /* false */)
+{
+ HbPopup *topFadingPopup = 0;
+ // Find the top fading popup
+ foreach (HbPopup *popup, popupList) {
+ if (popup->isBackgroundFaded()) {
+ if (!topFadingPopup || topFadingPopup->zValue() < popup->zValue()) {
+ topFadingPopup = popup;
+ }
+ }
+ }
+
+ // To avoid double fading, device dialog server may ask applications to
+ // set background fading off
+ if (deviceFadeControl.fadeOff()) {
+ topFadingPopup = 0;
+ unfadeFirst = true;
+ }
+ // Device dialog server commands other applications to set background fading off.
+ deviceFadeControl.setFadeOff(topFadingPopup != 0);
+
+ HbMainWindow *mainWindow = 0;
+ if (!scene->views().isEmpty()) {
+ // in the Hb architecture, there is only one view per scene
+ mainWindow = qobject_cast<HbMainWindow *>(scene->views().at(0));
+ }
+ if (mainWindow) {
+ if (unfadeFirst) {
+ HbMainWindowPrivate::d_ptr(mainWindow)->unfadeScreen();
+ }
+
+ if (topFadingPopup) {
+ HbMainWindowPrivate::d_ptr(mainWindow)->fadeScreen(
+ HbPopupPrivate::d_ptr(topFadingPopup)->backgroundItem->zValue() - HbPrivate::FadingItemZValueUnit );
+ }
+ }
+}
+
+// Device wide fading change is requested
+void HbPopupManagerPrivate::deviceFadeChange(bool fadeOff)
+{
+ updateFading(fadeOff);
+}
+
+void HbPopupManagerPrivate::setGeometryForPopup(HbPopup *popup)
+{
+ // clear the old one if deleted
+ HbPopupLayoutManager *parentItem = 0;
+ HbPopupLayoutProxy *childItem = 0;
+ if ( parentItems.contains( popup ) ) {
+ parentItem = parentItems[popup];
+ childItem = static_cast<HbPopupLayoutProxy*>(parentItem->childItems().at(0));
+ if ( childItem->mPopup.isNull() ) {
+ parentItems.remove( popup );
+ delete parentItem;
+ }
+ }
+ parentItem = 0;
+ childItem = 0;
+ if ( parentItems.contains( popup ) ) {
+ parentItem = parentItems[popup];
+ } else {
+ parentItem = new HbPopupLayoutManager( popup, scene );
+ parentItems.insert( popup, parentItem );
+ }
+ childItem = static_cast<HbPopupLayoutProxy*>(parentItem->childItems().at(0));
+ childItem->setSizePolicy( popup->sizePolicy() );
+}
+
+
+/*
+ \class HbPopupManager
+ \brief HbPopupManager is managing the priority order of HbPopup objects and fading of
+ the screen.
+*/
+
+HbPopupManager::HbPopupManager(HbGraphicsScene *scene, QObject *parent): QObject(parent)
+{
+ d = new HbPopupManagerPrivate(scene, this);
+}
+
+/*
+ Destructor.
+*/
+HbPopupManager::~HbPopupManager()
+{
+// no need to delete d because it is child of this object
+}
+
+void HbPopupManager::eventHook(QEvent *event)
+{
+ d->eventHook(event);
+}
+
+void HbPopupManager::showPopup(HbPopup *popup)
+{
+ d->showPopup(popup);
+}
+
+void HbPopupManager::hidePopup(HbPopup *popup)
+{
+ d->hidePopup(popup);
+}
+