src/hbcore/gui/hbpopupmanager.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /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);
+}
+