homescreenapp/hsdomainmodel/src/hswidgethost.cpp
changeset 35 f9ce957a272c
child 36 cdae8c6c3876
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homescreenapp/hsdomainmodel/src/hswidgethost.cpp	Fri Mar 19 09:27:44 2010 +0200
@@ -0,0 +1,592 @@
+/*
+* 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 <qservicemanager.h>
+#include <qservicefilter.h>
+#include <qserviceinterfacedescriptor.h>
+
+#include <HbInstance>
+
+#include "hswidgethost.h"
+#include "hsdatabase.h"
+#include "hswidgetdata.h"
+#include "hspage.h"
+#include "hsapp_defs.h"
+#include "hsscene.h"
+#include "cadefs.h"
+#include "canotifier.h"
+#include "canotifierfilter.h"
+#include "caservice.h"
+#include "caquery.h"
+#include "caentry.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(const HsWidgetData &widgetData, 
+                                           const QVariantMap &preferences)
+{
+    HsWidgetHost *host = NULL;
+    
+    HsDatabase* db = HsDatabase::instance();
+    Q_ASSERT(db);
+
+    int databaseId = -1;
+    if (db->insertWidget(widgetData, databaseId)) {
+        db->setWidgetPreferences(databaseId, preferences);
+        host = new HsWidgetHost(databaseId);
+    }
+
+    return host;                
+}
+/*!
+    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),
+      mDatabaseId(databaseId)
+{
+    CaQuery query;
+    query.setEntryTypeNames(QStringList(widgetTypeName()));
+    CaNotifierFilter filter(query);    
+    CaNotifier *notifier = CaService::instance()->createNotifier(filter);
+    notifier->setParent(this);
+    connect(notifier,
+            SIGNAL(entryChanged(const CaEntry&, ChangeType)),
+            SLOT(onEntryChanged(const CaEntry&, ChangeType)), Qt::QueuedConnection);
+
+    /* 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();
+    Q_ASSERT(db);
+
+    // Find the widget data.
+    HsWidgetData widgetData;
+    if (!db->widget(mDatabaseId, widgetData, false)) {
+        return false;
+    }
+
+    mUri = widgetData.uri();
+
+    // Create the hosted widget.
+    QServiceManager manager;
+    QServiceFilter filter("com.nokia.IHomeScreenWidget");
+    filter.setServiceName(widgetData.uri());
+    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);
+    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()));
+    }
+
+    loadWidgetPresentation();
+
+    QGraphicsLinearLayout *layout = new QGraphicsLinearLayout;
+    layout->setContentsMargins(0, 0, 0, 0);
+    layout->addItem(mWidget);
+    setLayout(layout);
+    
+    return true;
+}
+
+bool HsWidgetHost::setPage(HsPage *page)
+{
+    HsDatabase* db = HsDatabase::instance();
+    Q_ASSERT(db);
+
+    HsWidgetData data;
+    if (db->widget(mDatabaseId, data, false)) {
+        if (!page) {
+            data.setPageId(-1);
+        } else {
+            data.setPageId(page->databaseId());
+        }
+        if (!db->updateWidget(data, false)) {
+            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()
+{
+    if (HsDatabase::instance()->deleteWidget(mDatabaseId)) {
+        mDatabaseId = -1;
+        return true;
+    }
+    return false;
+}
+
+/*!
+    Set widget presentation by using current values.
+    Return true if successfull.
+*/
+bool HsWidgetHost::setWidgetPresentation()
+{
+    HsDatabase *db = HsDatabase::instance();
+    Q_ASSERT(db);
+
+    QString key = hbInstance->orientation() == Qt::Vertical ? 
+        "portrait" : "landscape";
+   
+    HsWidgetPresentationData data;
+    data.setWidgetId(databaseId());
+    data.setKey(key);
+    data.setPosition(pos());
+    data.setSize(size());
+    data.setZValue(zValue());
+    
+    HsWidgetPresentationData temp;
+    if (!db->widgetPresentation(databaseId(), key, temp)) {        
+        if (!db->insertWidgetPresentation(data)) {
+            return false;
+        }
+    } else {
+        data.setId(temp.id());
+        if (!db->updateWidgetPresentation(data)) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*!
+    Set widget presentation data. Return true if successfull
+
+*/
+bool HsWidgetHost::setWidgetPresentationData(HsWidgetPresentationData &presentationData)
+{
+    presentationData.setWidgetId(mDatabaseId);
+    return HsDatabase::instance()->insertWidgetPresentation(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)
+{
+    return HsDatabase::instance()->widgetPresentation(mDatabaseId,key,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;
+    if (db->widgetPresentation(databaseId(), key, data)) {
+        return data;
+    } else {
+        return HsWidgetPresentationData();
+    }
+
+}
+
+/*!
+    Load HsWidgetPresentationData for current orientation
+*/
+bool HsWidgetHost::loadWidgetPresentation()
+{
+    HsDatabase *db = HsDatabase::instance();
+    Q_ASSERT(db);
+
+    QString key = hbInstance->orientation() == Qt::Vertical ? 
+        "portrait" : "landscape";
+
+    HsWidgetPresentationData data;
+    if (!db->widgetPresentation(databaseId(), key, data)) {
+        return false;
+    }
+
+    setGeometry(QRectF(data.position(), data.size()));
+    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";
+
+    HsWidgetPresentationData data;
+    if (!db->widgetPresentation(databaseId(), key, data)) {
+        return true;
+    } else {
+        if (!db->deleteWidgetPresentation(data.id())) {
+            return false;
+        }
+    }
+
+    return true;
+}
+
+/*!
+    \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()
+{    
+    setPreferencesToWidget();
+    setOnline(HsScene::instance()->isOnline());
+    mOnInitializeMethod.invoke(mWidget);
+}
+
+/*!
+    Calls the widget's onShow() slot if the
+    widget defines it.
+*/
+void HsWidgetHost::showWidget()
+{
+    mOnShowMethod.invoke(mWidget);
+}
+
+/*!
+    Calls the widget's onHide() slot if the
+    widget defines it.
+*/
+void HsWidgetHost::hideWidget()
+{
+    mOnHideMethod.invoke(mWidget);
+}
+
+/*!
+    Calls the widget's onUninitialize() slot if the
+    widget defines it.
+*/
+void HsWidgetHost::uninitializeWidget()
+{
+    mOnUninitializeMethod.invoke(mWidget);
+}
+
+/*!
+    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);
+}
+
+/*!
+    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);
+
+    QVariantMap 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;
+}
+
+/*!
+    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;
+    }
+
+    QVariantMap 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()
+{
+    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()
+{
+    emit widgetError(this);
+}
+
+/*!
+    This slot reacts to \a entry change that is described with 
+    \a changeType. On remove change type, onFinished() signal is
+    emitted.
+*/
+void HsWidgetHost::onEntryChanged(const CaEntry &entry, 
+                                  ChangeType changeType)
+{
+    if (changeType == RemoveChangeType) {
+        QString uri = entry.attribute(widgetUriAttributeName());
+        if (uri == mUri) {
+            onFinished();
+        }
+    }
+}