homescreenapp/hsdomainmodel/src/hswidgethost.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:24:59 +0300
changeset 39 4e8ebe173323
parent 36 cdae8c6c3876
child 46 23b5d6a29cce
permissions -rw-r--r--
Revision: 201015 Kit: 201018

/*
* Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:
*
*/

#include <QMetaObject>
#include <QGraphicsLinearLayout>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QGraphicsDropShadowEffect>
#include <QGraphicsSceneResizeEvent>

#include <qservicemanager.h>
#include <qservicefilter.h>
#include <qserviceinterfacedescriptor.h>

#include <HbInstance>

#include "hswidgethost.h"
#include "hsdatabase.h"
#include "hsdomainmodeldatastructures.h"
#include "hspage.h"
#include "hsapp_defs.h"
#include "hsscene.h"
#include "hswidgetcomponentregistry.h"
#include "hswidgetcomponent.h"

QTM_USE_NAMESPACE

/*!
    \class HsWidgetHost
    \ingroup group_hsutils
    \brief Homescreen widget runner.
    Is responsible of running a homescreen widget. Each 
    homescreen widget has its own host.
*/

HsWidgetHost* HsWidgetHost::createInstance(HsWidgetData &widgetData, 
                                           const QVariantHash &preferences)
{
    HsDatabase* db = HsDatabase::instance();
    Q_ASSERT(db);

    if (db->insertWidget(widgetData)) {
        db->setWidgetPreferences(widgetData.id, preferences);
        return new HsWidgetHost(widgetData.id);
    }

    return 0;
}
/*!
    Construct a widget host for the given \a databaseId. 
    \a parent becomes the parent item for the host.
*/
HsWidgetHost::HsWidgetHost(int databaseId, QGraphicsItem *parent)
    : HbWidget(parent),
      mWidget(0),
      mPage(0),
      mState(Constructed),
      mDatabaseId(databaseId),
      mTapAndHoldIcon(0)
{
    setFlags(QGraphicsItem::ItemClipsChildrenToShape);

    /* TODO: Uncomment after the Qt bug has been fixed.
    QGraphicsDropShadowEffect *effect = 
        new QGraphicsDropShadowEffect(this);
    effect->setColor(QColor(0, 0, 0, 150));
    effect->setBlurRadius(5);
    effect->setOffset(3);
    setGraphicsEffect(effect);
    */
}

/*!
    Destructor.
*/
HsWidgetHost::~HsWidgetHost()
{
}

/*!
    Load hosted widget from plugin and validate it.
    Returns true if widget construction is successfull.
*/
bool HsWidgetHost::load()
{
    if (mWidget) {
        return false;
    }

    HsDatabase *db = HsDatabase::instance();
    
    // Find the widget data.
    HsWidgetData data;
    data.id = mDatabaseId;
    if (!db->widget(data)) {
        return false;
    }

	mUri = data.uri;
    // Create the hosted widget.
    QServiceManager manager;
    QServiceFilter filter("com.nokia.symbian.IHomeScreenWidget");
    filter.setServiceName(mUri);
    QList<QServiceInterfaceDescriptor> interfaces = manager.findInterfaces(filter);
    if (interfaces.isEmpty()) {
        return false;
    }

    QObject *widgetObject = manager.loadInterface(interfaces.first());
    mWidget = qobject_cast<QGraphicsWidget *>(widgetObject);

    if (!mWidget || 
        !setMethod("onShow()", mOnShowMethod) ||
        !setMethod("onHide()", mOnHideMethod)) {
        mWidget = 0;
        delete widgetObject;
        return false;
    }
    
    setProperty("isOnline", mIsOnlineProperty);
	setProperty("rootPath", mRootPathProperty);

    setMethod("onInitialize()", mOnInitializeMethod);    
    setMethod("onUninitialize()", mOnUninitializeMethod);
       
    if (hasSignal("setPreferences(const QStringList&)")) {
        connect(mWidget, SIGNAL(setPreferences(QStringList)),
                SLOT(onSetPreferences(QStringList)));
    }
    if (hasSignal("finished()")) {
        connect(mWidget, SIGNAL(finished()),
                SLOT(onFinished()));
    }
    if (hasSignal("error()")) {
        connect(mWidget, SIGNAL(error()),
                SLOT(onError()));
    }
    
    mWidget->installEventFilter(this);    

	loadWidgetPresentation();

    HsScene *scene = HsScene::instance();
    setMaximumSize(scene->maximumWidgetSizeInPixels());
    setMinimumSize(scene->minimumWidgetSizeInPixels());

    mWidget->setParentItem(this);
    
    setNewSize(mWidget->size());
    
    return true;
}

bool HsWidgetHost::setPage(HsPage *page)
{
    HsDatabase* db = HsDatabase::instance();
    Q_ASSERT(db);

    HsWidgetData data;
    data.id = mDatabaseId;
    if (db->widget(data)) {
        if (!page) {
            data.pageId = -1;
        } else {
            data.pageId = page->databaseId();
        }
        if (!db->updateWidget(data)) {
            return false;
        }
    } else {
        return false;
    }

    mPage = page;
    return true;
}
 
HsPage *HsWidgetHost::page() const
{
    return mPage;
}

/*!
    Returns true if this host has a valid widget set.
    Otherwise, return false.
*/
bool HsWidgetHost::isValid() const
{
    return mWidget;
}

/*!
    Returns database id
*/
int HsWidgetHost::databaseId() const
{
    return mDatabaseId; 
}

/*!
    Returns true if this the database operation succeeds,
    false otherwise
*/
bool HsWidgetHost::deleteFromDatabase()
{
    HsDatabase *db = HsDatabase::instance();

    if (!db->deleteWidget(mDatabaseId)) {
        return false;
    }

    mDatabaseId = -1;
    return true;
}

/*!
    Set widget presentation by using current values.
    Return true if successfull.
*/
bool HsWidgetHost::setWidgetPresentation()
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    QString key = HsScene::orientation() == Qt::Vertical ? 
        "portrait" : "landscape";
   
    HsWidgetPresentationData data;    
    data.key      = key;
    data.setPos(pos());
    data.zValue   = zValue();
    data.widgetId = databaseId();
    
    return db->setWidgetPresentation(data);
}

/*!
    Set widget presentation data. Return true if successfull.
*/
bool HsWidgetHost::setWidgetPresentationData(HsWidgetPresentationData &presentationData)
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    presentationData.widgetId = mDatabaseId;
    return db->setWidgetPresentation(presentationData);
}

/*!
    Get widget presentation data matching given \a key. 
    Data is returned on given empty \a presentationData. Return true if successfull 
*/
bool HsWidgetHost::widgetPresentationData(const QString &key,
                                          HsWidgetPresentationData &presentationData)
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    presentationData.key = key;
    presentationData.widgetId = mDatabaseId;
    return db->widgetPresentation(presentationData);
}

/*!
    Return HsWidgetPresentationData for given \a orientation
*/
HsWidgetPresentationData HsWidgetHost::widgetPresentation(Qt::Orientation orientation)
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    QString key = orientation == Qt::Vertical ? 
        "portrait" : "landscape";

    HsWidgetPresentationData data;
    data.key = key;
    data.widgetId = mDatabaseId;
    if (db->widgetPresentation(data)) {
        return data;
    } else {
        return HsWidgetPresentationData();
    }
}

/*!
    Load HsWidgetPresentationData for current orientation
*/
bool HsWidgetHost::loadWidgetPresentation()
{
    HsDatabase *db = HsDatabase::instance();
    
    QString key = HsScene::orientation() == Qt::Vertical ?
        "portrait" : "landscape";

    HsWidgetPresentationData data;
    data.key = key;
    data.widgetId = mDatabaseId;
    if (!db->widgetPresentation(data)) {
        return false;
    }

    setPos(data.x, data.y);
    setZValue(data.zValue);

    return true;
}

/*!
    Delete HsWidgetPresentationData for given \a orientation.
    Return true if successfull.
*/
bool HsWidgetHost::deleteWidgetPresentation(Qt::Orientation orientation)
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    QString key = orientation == Qt::Vertical ? 
        "portrait" : "landscape";
    
    return db->deleteWidgetPresentation(mDatabaseId, key);
}

/*!
    \fn void HsWidgetHost::finished()
    This signal is emitten after the contained widget
    hs reported is completion.
*/

/*!
    \fn void HsWidgetHost::error()
    This signal is emitten after the contained widget
    hs reported an error.
*/

/*!
    Calls the widget's onInitialize() slot if the
    widget defines it.
*/
void HsWidgetHost::initializeWidget()
{   
    if (mState != Constructed) {
        return;
    }
	// bind component to instance
	HsWidgetComponent *component = HsWidgetComponentRegistry::instance()->component(mUri);
	Q_ASSERT(component);
    connect(component, SIGNAL(aboutToUninstall()), SLOT(onFinished()),Qt::QueuedConnection);
	mRootPathProperty.write(mWidget, component->rootPath());
	
    setPreferencesToWidget();
    setOnline(HsScene::instance()->isOnline());
    mOnInitializeMethod.invoke(mWidget);
   
    mState = Initialized;
}

/*!
    Calls the widget's onShow() slot if the
    widget defines it.
*/
void HsWidgetHost::showWidget()
{
    if (mState != Initialized &&
        mState != Hidden) {
        return;
    }

    mOnShowMethod.invoke(mWidget);
    
    mState = Visible;
}

/*!
    Calls the widget's onHide() slot if the
    widget defines it.
*/
void HsWidgetHost::hideWidget()
{
    if (mState != Initialized &&
        mState != Visible) {
        return;
    }

    mOnHideMethod.invoke(mWidget);

    mState = Hidden;
}

/*!
    Calls the widget's onUninitialize() slot if the
    widget defines it.
*/
void HsWidgetHost::uninitializeWidget()
{
    if (mState != Visible &&
        mState != Hidden &&
        mState != Finished) {
        return;
    }

    mOnUninitializeMethod.invoke(mWidget);

    mState = Uninitialized;
}

/*!
    Calls the widget's widgetOnlineState property if the
    widget defines it.
*/
void HsWidgetHost::setOnline(bool online)
{
    mIsOnlineProperty.write(mWidget, online);
}

/*!
    Starts the widget drag animation.
*/
void HsWidgetHost::startDragAnimation()
{
    /* TODO: Uncomment after the Qt bug has been fixed.
    QGraphicsDropShadowEffect *effect = 
        static_cast<QGraphicsDropShadowEffect *>(graphicsEffect());
    */

    setTransformOriginPoint(rect().center());

    QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup();

    QPropertyAnimation *animation = new QPropertyAnimation(this, "scale");
    animation->setDuration(200);
    animation->setEndValue(1.1);
    animationGroup->addAnimation(animation);

    /* TODO: Uncomment after the Qt bug has been fixed.
    animation = new QPropertyAnimation(effect, "offset");
    animation->setDuration(200);
    animation->setEndValue(QPointF(8 ,8));
    animationGroup->addAnimation(animation);
    */

    animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
}
 
/*!
    Starts the widget drop animation.
*/
void HsWidgetHost::startDropAnimation()
{
    /* TODO: Uncomment after the Qt bug has been fixed.
    QGraphicsDropShadowEffect *effect = 
        static_cast<QGraphicsDropShadowEffect *>(graphicsEffect());
    */

    QParallelAnimationGroup *animationGroup = new QParallelAnimationGroup;

    QPropertyAnimation *animation = new QPropertyAnimation(this, "scale");
    animation->setDuration(200);
    animation->setEndValue(1);
    animationGroup->addAnimation(animation);

    /* TODO: Uncomment after the Qt bug has been fixed.
    animation = new QPropertyAnimation(effect, "offset");
    animation->setDuration(200);
    animation->setEndValue(QPointF(3, 3));
    animationGroup->addAnimation(animation);
    */

    animationGroup->start(QAbstractAnimation::DeleteWhenStopped);
}

/*!
    Starts the tap-and-hold animation.
*/
void HsWidgetHost::startTapAndHoldAnimation()
{
    mTapAndHoldIcon = new HbIconItem("tapandhold_animation");
    mTapAndHoldIcon->setZValue(1e6);
    mTapAndHoldIcon->setParentItem(this);
}
 
/*!
    Stops the tap-and-hold animation.
*/
void HsWidgetHost::stopTapAndHoldAnimation()
{
    delete mTapAndHoldIcon;
    mTapAndHoldIcon = 0;
}

/*!
    Filters resize events from widgets and resizes inside max/min size boundaries if needed.
*/
bool HsWidgetHost::eventFilter(QObject *obj, QEvent *event)
{
    if (event->type() == QEvent::GraphicsSceneResize ) {
        QGraphicsSceneResizeEvent *resizeEvent = static_cast<QGraphicsSceneResizeEvent*>(event);
        setNewSize(resizeEvent->newSize());
        return true;
    } else {
         // standard event processing
         return HbWidget::eventFilter(obj, event);
    }
}

/*!
    Checks if a property with the given \a name
    in the contained widget. If the property exists the \a
    metaProperty is made to reference to it. Returns true if
    the property was found. Otherwise, returns false.
*/
bool HsWidgetHost::setProperty(const char *name, QMetaProperty &property)
{
    const QMetaObject *object = mWidget->metaObject();
    int index = object->indexOfProperty(name);
    property = object->property(index);
    return index >= 0;
}

/*!
    Checks if a slot with the given \a signature exists
    in the contained widget. If the slot exists the \a
    method is made to reference to it. Returns true if
    the slot was found. Otherwise, returns false.
*/
bool HsWidgetHost::setMethod(const char *signature, QMetaMethod &method)
{
    const QMetaObject *object = mWidget->metaObject();
    int index = object->indexOfMethod(
        QMetaObject::normalizedSignature(signature));
    method = object->method(index);
    return index >= 0;
}

/*!
    Returns true if a signal with the given \a signature
    exists in the contained widget. Otherwise, returns 
    false.
*/
bool HsWidgetHost::hasSignal(const char *signature)
{
    const QMetaObject *object = mWidget->metaObject();
    int index = object->indexOfSignal(
        QMetaObject::normalizedSignature(signature));
    return index >= 0;
}
/*!
    Returns true if fetching widget preferences from db and setting those
    to widget is successfull
*/
bool HsWidgetHost::setPreferencesToWidget()
{
    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    QVariantHash preferences;
    if (!db->widgetPreferences(mDatabaseId, preferences)) {
        return false;
    }

    QStringList names = preferences.keys();
    foreach(QString name, names) {
        mWidget->setProperty(name.toLatin1(),
                             preferences.value(name));
    }

    return true;
}

/*!
    Resizes and sets preferred size for widget layouts
*/
void HsWidgetHost::setNewSize(const QSizeF &newSize)
{
    resize(newSize);
    setPreferredSize(newSize);
}

/*!
    This slot is connected to the contained widget's
    setPreferences() signal, if it was defined for
    the widget. The widget emits the signal for persisting
    its preferences named with \a names. The given
    preferences are read, validated, and written to
    the database.
*/
void HsWidgetHost::onSetPreferences(const QStringList &names)
{
    if (names.isEmpty()) {
        return;
    }

    QVariantHash preferences;

    foreach (QString name, names) {
        QVariant value = mWidget->property(name.toLatin1());
        preferences.insert(name, value);
    }

    HsDatabase *db = HsDatabase::instance();
    Q_ASSERT(db);

    if (!db->setWidgetPreferences(mDatabaseId, preferences)) {
        onError();
    }
}

/*!
    This slot reacts to the widgets finished() signal, if 
    it was defined for the widget. The widget emits the signal
    when it has finished its execution and is ready for 
    removal from the homescreen.
*/
void HsWidgetHost::onFinished()
{
    mState = Finished;
    emit widgetFinished(this);
}

/*!
    This slot reacts to the widgets error() signal, if it was 
    defined for the widget. The widget emits the signal in 
    failure cases.
*/
void HsWidgetHost::onError()
{
    mState = Faulted;
    emit widgetError(this);
}