homescreenapp/hsdomainmodel/src/hspage.cpp
branchRCL_3
changeset 82 5f0182e07bfb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/homescreenapp/hsdomainmodel/src/hspage.cpp	Tue Aug 31 15:06:34 2010 +0300
@@ -0,0 +1,799 @@
+/*
+* 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 <QHash>
+
+#include <HbInstance>
+
+#include "hsdomainmodeldatastructures.h"
+#include "hspage.h"
+#include "hspagevisual.h"
+#include "hspagetoucharea.h"
+#include "hspagenewwidgetlayout.h"
+#include "hsscene.h"
+#include "hsdatabase.h"
+#include "hswidgethost.h"
+#include "hswidgethostvisual.h"
+#include "hswallpaper.h"
+#include "hswidgetpositioningonwidgetadd.h"
+#include "hswidgetpositioningonorientationchange.h"
+#include "hsconfiguration.h"
+#include "hsgui.h"
+
+
+/*!
+    \class HsPage
+    \ingroup group_hsdomainmodel
+    \brief Represents a page in the framework.
+    HsPage contains group of widgets. HsPage can have a wallpaper.
+*/
+
+/*!
+    Constructor.
+
+    \a parent Owner.
+    \a aFlags Window flags.
+*/
+HsPage::HsPage(QObject* parent)
+    : QObject(parent),
+      mDatabaseId(-1),
+      mPageVisual(new HsPageVisual), 
+      mWallpaper(0),
+      mRemovable(true),
+      mPageMargin(0.0)
+{
+ 
+    //Page margin
+    mPageMargin = HSCONFIGURATION_GET(pageMargin);
+    connect(HsConfiguration::instance(), SIGNAL(propertyChanged(QString)), SLOT(onPageMarginChanged(QString)));
+}
+
+/*!
+    Destructor.
+*/
+HsPage::~HsPage()
+{
+    // visuals are owned by widget host, detach those from page visual
+    HsWidgetHostVisual *visual(0);
+    foreach (HsWidgetHost *widget, mWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+    }
+   
+    foreach (HsWidgetHost *widget, mNewWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+    }
+   
+    foreach (HsWidgetHost *widget, mUnavailableWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+    }
+    qDeleteAll(mWidgets);
+    qDeleteAll(mNewWidgets);
+    qDeleteAll(mUnavailableWidgets);
+
+    delete mPageVisual;
+    delete mWallpaper;
+}
+
+/*!
+    Returns the database id.
+*/
+int HsPage::databaseId() const
+{
+    return mDatabaseId;
+}
+
+/*!
+    Sets the database id to \a id.
+*/
+void HsPage::setDatabaseId(int id)
+{
+    mDatabaseId = id;
+}
+/*
+void HsPage::setGeometry(const QRectF &rect)
+{
+    if (mTouchArea) {
+        mTouchArea->resize(rect.size());
+    }
+
+    HbWidget::setGeometry(rect);
+}
+*/
+/*!
+    Loads widgets.
+*/
+bool HsPage::load()
+{
+    if (mDatabaseId < 0) {
+        return false;
+    }
+
+    HsDatabase *db = HsDatabase::instance();
+
+    QList<HsWidgetData> datas;
+    if (!db->widgets(mDatabaseId, datas)) {
+        return false;
+    }
+
+    if (HSCONFIGURATION_GET(sceneType) == HsConfiguration::PageWallpapers) {
+        mWallpaper = new HsPageWallpaper(this);
+    }
+
+    foreach (HsWidgetData data, datas) {
+        HsWidgetHost *widget = new HsWidgetHost(data.id);
+        if (!widget->loadPresentation(Qt::Horizontal) &&
+            !widget->loadPresentation(Qt::Vertical)) {
+            widget->setPage(this);
+            widget->visual()->hide();
+            connectWidget(widget);
+            mNewWidgets << widget;            
+        }
+        else {
+            mWidgets.append(widget);
+            connectWidget(widget);
+            widget->setPage(this);
+            widget->visual()->setParentItem(this->visual());
+            widget->startWidget(isDefaultPage());
+        }
+    }
+
+    connect(HsGui::instance(),
+        SIGNAL(orientationChanged(Qt::Orientation)),
+        SLOT(onOrientationChanged(Qt::Orientation)));
+
+    return true;
+}
+
+/*!
+    Return wallpaper.
+*/
+HsWallpaper *HsPage::wallpaper() const
+{
+    return mWallpaper;
+}
+/*!
+    Add given existing \a widgetHost to a page. Returns true if successful
+*/
+bool HsPage::addExistingWidget(HsWidgetHost *widgetHost)
+{
+    if (!widgetHost) {
+        return false;
+    }
+
+    if (mWidgets.contains(widgetHost)) {
+        return true;
+    }
+
+    if (!widgetHost->setPage(this)) {
+        return false;
+    }
+
+    connectWidget(widgetHost);
+    mWidgets << widgetHost;
+    widgetHost->visual()->setParentItem(visual());
+
+    return true;
+ }
+
+/*!
+    Remove given \a widgetHost from a page. Widget is not deleted. 
+    Returns true if successful
+*/
+bool HsPage::removeWidget(HsWidgetHost *widgetHost)
+{
+    if (!widgetHost || !widgetHost->setPage(0)) {
+        return false;
+    }
+
+    disconnectWidget(widgetHost);
+    mWidgets.removeOne(widgetHost);
+    widgetHost->visual()->setParentItem(0);
+
+    return true;
+}
+
+/*!
+    Returns list of new widgets belonging to a page. Widgets which are
+    not yet layouted are considered as new widgets. 
+*/
+QList<HsWidgetHost *> HsPage::newWidgets()
+{
+    return mNewWidgets;
+}
+
+/*!
+    Adds new widget into a page. Returns true if successfull.
+*/
+bool HsPage::addNewWidget(HsWidgetHost* widgetHost, const QPointF &touchPoint)
+{
+    mTouchPoint = touchPoint;
+
+    if (!widgetHost || mWidgets.contains(widgetHost)) {
+        return false;
+    }
+
+    if (mNewWidgets.contains(widgetHost)) {
+        return true;
+    }
+    
+    widgetHost->setPage(this);    
+
+    widgetHost->visual()->hide();
+
+    connectWidget(widgetHost);
+    mNewWidgets << widgetHost;
+
+    return true;
+}
+
+/*!
+    Layouts all the new widgets
+*/
+void HsPage::layoutNewWidgets()
+{
+    if (mNewWidgets.isEmpty()) {
+        return;
+    }
+
+    HsPageNewWidgetLayout *newWidgetLayout = static_cast<HsPageNewWidgetLayout *>(visual()->layout());
+    if (!newWidgetLayout) {
+        newWidgetLayout = new HsPageNewWidgetLayout(mTouchPoint);
+        visual()->setLayout(newWidgetLayout);
+    }    
+    updateZValues();
+    HsWidgetHost *widget = 0;
+    for (int i = 0; i < mNewWidgets.count(); ++i) {
+        widget = mNewWidgets.at(i);
+        newWidgetLayout->addItem(widget);
+        widget->setPage(this);
+        widget->visual()->setParentItem(visual());
+        widget->showWidget();
+        widget->visual()->show();
+    }
+    mWidgets << mNewWidgets;
+    mNewWidgets.clear();
+}
+
+/*!
+    Clears new widgets list and resets layout.
+*/
+void HsPage::resetNewWidgets()
+{
+    mNewWidgets.clear();
+    visual()->setLayout(0);
+}
+
+/*!
+    Remove page and all it's contained widgets from database 
+*/
+bool HsPage::deleteFromDatabase()
+{
+    
+    HsWidgetHostVisual *visual(0);
+    foreach (HsWidgetHost *widget, mWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+        widget->remove();
+    }
+    mWidgets.clear();
+    
+
+    foreach (HsWidgetHost *widget, mNewWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+        widget->remove();
+    }
+    mNewWidgets.clear();
+
+    foreach (HsWidgetHost *widget, mUnavailableWidgets) {
+        visual = widget->visual();
+        visual->setParent(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+        widget->remove();
+    }
+    mUnavailableWidgets.clear();
+    
+    if (mWallpaper) {
+        mWallpaper->remove();
+    }
+    return HsDatabase::instance()->deletePage(mDatabaseId);
+}
+
+/*!
+    Return list of widgets belonging to a page 
+*/
+QList<HsWidgetHost *> HsPage::widgets() const
+{
+    return mWidgets;
+}
+
+/*!
+    Returns true if the page can be removed. Otherwise,
+    returns false.
+*/
+bool HsPage::isRemovable() const
+{
+    return mRemovable;
+}
+
+/*!
+    Sets removable flag to \a removable.
+*/
+void HsPage::setRemovable(bool removable)
+{
+    mRemovable = removable;
+}
+
+/*!
+    Return true if page is default page.
+*/
+bool HsPage::isDefaultPage() const
+{
+    return mDatabaseId == HSCONFIGURATION_GET(defaultPageId);
+}
+
+/*!
+    Return true if page is active page.
+*/
+bool HsPage::isActivePage() const
+{
+    return this == HsScene::instance()->activePage();
+}
+
+/*!
+    Create page into database and return instance of a new page.
+*/
+HsPage *HsPage::createInstance(const HsPageData &pageData)
+{
+    HsDatabase *db = HsDatabase::instance();
+    Q_ASSERT(db);
+
+    HsPageData data(pageData);
+    if (db->insertPage(data)) {
+        HsPage *page = new HsPage;
+        page->setDatabaseId(data.id);
+        return page;
+    }
+
+    return 0;
+}
+
+/*!
+    The widget is bounded in the rectangle which is smaller by pageMargin on all sides of page.
+*/
+QPointF HsPage::adjustedWidgetPosition(const QRectF &origWidgetRect)
+{
+    QRectF widgetAreaRect = contentGeometry();
+    qreal widgetX = qBound(widgetAreaRect.left(), origWidgetRect.x(), widgetAreaRect.right() - origWidgetRect.width());
+    qreal widgetY = qBound(widgetAreaRect.top(), origWidgetRect.y(), widgetAreaRect.bottom() - origWidgetRect.height());
+
+    return QPointF(widgetX, widgetY);
+}
+
+/*!
+    Returns rect of rectangular where widgets are allowed to be placed in the page.
+*/
+QRectF HsPage::contentGeometry()
+{
+    return contentGeometry(HsGui::instance()->orientation());
+}
+
+/*!
+    Returns rect of rectangular where widgets are allowed to be placed in the page.
+*/
+QRectF HsPage::contentGeometry(Qt::Orientation orientation)
+{
+    QRectF pageRect = HsGui::instance()->layoutRect();
+
+    if (orientation != HsGui::instance()->orientation()) {
+        qreal width = pageRect.width();
+        qreal height = pageRect.height();
+        pageRect.setWidth(height);
+        pageRect.setHeight(width);
+    }
+
+    //Take care of chrome in both orientation
+    pageRect.setTop(64);
+
+    //Shrink by page margins at each side
+    return pageRect.adjusted(mPageMargin, mPageMargin, -mPageMargin, -mPageMargin);
+}
+
+/*!
+    Returns rect of rectangular where widgets are allowed to be placed in the page.
+*/
+QRectF HsPage::contentRect()
+{
+    return contentRect(HsGui::instance()->orientation());
+}
+
+/*!
+    Returns rect of rectangular where widgets are allowed to be placed in the page.
+*/
+QRectF HsPage::contentRect(Qt::Orientation orientation)
+{
+    QRectF rect = contentGeometry(orientation);
+    rect.moveTopLeft(QPointF(0,0));
+    return rect;
+}
+
+HsPageVisual *HsPage::visual() const
+{
+    return mPageVisual;
+}
+/*!
+    Calls onShow() for contained widgets.
+*/
+void HsPage::showWidgets()
+{
+    foreach (HsWidgetHost *widget, mWidgets) {
+        if (widget->visual()->parentItem() == visual()) {
+            widget->showWidget();
+        }
+    }
+}
+
+/*!
+    Calls onHide() for contained widgets.
+*/
+void HsPage::hideWidgets()
+{
+    foreach (HsWidgetHost *widget, mWidgets) {
+        if (widget->visual()->parentItem() == visual()) {
+            widget->hideWidget();
+        }
+    }
+}
+
+/*!
+    Propagate online state to widgets.
+*/
+void HsPage::setOnline(bool online)
+{
+    foreach (HsWidgetHost *widget, mNewWidgets) {
+        widget->setOnline(online);
+    }
+    foreach (HsWidgetHost *widget, mWidgets) {
+        widget->setOnline(online);
+    }
+}
+
+/*!
+    Update widgets z-values and persist those. Active widget has top most 
+    z-value.
+*/
+void HsPage::updateZValues()
+{
+    int z = 0;
+
+    QHash<int, qreal> widgetZValues;
+
+    if (!mWidgets.isEmpty()) {
+        QMultiMap<qreal, HsWidgetHost *> map;
+        foreach (HsWidgetHost *widget, mWidgets) {
+            map.insert(widget->visual()->zValue(), widget);
+        }
+
+        QList<HsWidgetHost *> sortedWidgets = map.values();
+
+        HsWidgetHost *activeWidget = HsScene::instance()->activeWidget();
+        if (sortedWidgets.contains(activeWidget)) {
+            sortedWidgets.removeOne(activeWidget);
+            sortedWidgets.append(activeWidget);
+        }
+
+        foreach (HsWidgetHost *widget, sortedWidgets) {
+            widgetZValues.insert(widget->databaseId(), z);
+            widget->visual()->setZValue(z++);
+        }
+    }
+
+    if (!mNewWidgets.isEmpty()) {
+        foreach (HsWidgetHost *widget, mNewWidgets) {
+            widgetZValues.insert(widget->databaseId(), z);
+            widget->visual()->setZValue(z++);
+        }
+    }
+
+    if (!widgetZValues.isEmpty()) {
+        HsDatabase::instance()->updateWidgetZValues(
+            widgetZValues, HsGui::instance()->orientation());
+    }
+}
+/*!
+    Return this page's index.
+*/
+int HsPage::pageIndex()
+{
+    return HsScene::instance()->pages().indexOf(this);
+}
+/*!
+    Create touch area for page.
+*/
+/*void HsPage::setupTouchArea()
+{
+    mTouchArea = new HsPageTouchArea(this);
+    mTouchArea->setZValue(-1);
+}*/
+/*!
+    Utility to connect widget signals to page.
+*/
+void HsPage::connectWidget(HsWidgetHost *widget)
+{
+    connect(widget, SIGNAL(finished()), SLOT(onWidgetFinished()));
+    connect(widget, SIGNAL(faulted()), SLOT(onWidgetFaulted()));
+    connect(widget->visual(), SIGNAL(resized()), SLOT(onWidgetResized()));
+    connect(widget, SIGNAL(available()), SLOT(onWidgetAvailable()));
+    connect(widget, SIGNAL(unavailable()), SLOT(onWidgetUnavailable()));
+}
+/*!
+    Disconnect widget signals from page
+*/
+void HsPage::disconnectWidget(HsWidgetHost *widget)
+{
+    widget->visual()->disconnect(this);
+    widget->disconnect(this);
+}
+
+/*!
+    Disconnect and remove widget
+*/
+void HsPage::onWidgetFinished()
+{
+    HsWidgetHost *widget = qobject_cast<HsWidgetHost *>(sender());
+
+    // It can be in new widget list if we haven't layouted it yet
+    // or layouted new widget and widget list
+    if (!mNewWidgets.removeOne(widget)) {
+        mWidgets.removeOne(widget);
+    }
+    if (widget){     
+        HsWidgetHostVisual *visual = widget->visual();
+        visual->setParentItem(0);
+        if (visual->scene()) {
+            visual->scene()->removeItem(visual);
+        }
+        disconnectWidget(widget);
+        widget->remove();
+    }
+}
+
+/*!
+    Remove widget if it faulted
+*/
+void HsPage::onWidgetFaulted()
+{
+    onWidgetFinished();
+}
+
+/*!
+    Calculates new widget position on page when widget size changes. If page has layout then there are new widgets
+    and we use layout to calculate new widget positions.
+*/
+void HsPage::onWidgetResized()
+{
+    if (!visual()->layout()) {
+        HsWidgetHostVisual *widgetVisual = qobject_cast<HsWidgetHostVisual *>(sender());
+        widgetVisual->setPos(adjustedWidgetPosition(widgetVisual->geometry()));
+    } else {
+        visual()->layout()->invalidate();
+    }
+}
+/*!
+    Show widget if it came available
+*/
+void HsPage::onWidgetAvailable()
+{
+    HsWidgetHost *widget = qobject_cast<HsWidgetHost *>(sender());
+    HsWidgetHostVisual *widgetVisual(widget->visual());
+
+    mUnavailableWidgets.removeOne(widget);
+    mWidgets.append(widget);
+
+    widgetVisual->setParentItem(visual());
+    widget->startWidget(isActivePage());
+    widgetVisual->show();
+}
+/*!
+    Update internal bookkeeping and hide widget
+*/
+void HsPage::onWidgetUnavailable()
+{
+    HsWidgetHost *widget = qobject_cast<HsWidgetHost *>(sender());
+    HsWidgetHostVisual *widgetVisual(widget->visual());
+
+    if (mWidgets.contains(widget)) {
+        mWidgets.removeOne(widget);
+    } else if (mNewWidgets.contains(widget)) {
+        mNewWidgets.removeOne(widget);
+    } else {
+        return;
+    }
+
+    mUnavailableWidgets.append(widget);
+
+    widgetVisual->hide();
+    widgetVisual->setParentItem(0);
+}
+
+/*!
+    Run positioning algorithm for widgets which don't have position on 
+    target orientation. Otherwise set orientation positions for widgets.
+*/
+void HsPage::onOrientationChanged(Qt::Orientation orientation)
+{
+    HsWidgetPositioningOnOrientationChange *converter =
+        HsWidgetPositioningOnOrientationChange::instance();
+
+    Qt::Orientation orientationFrom = orientation == Qt::Vertical ? Qt::Horizontal : Qt::Vertical;
+    QRectF from = contentGeometry(orientationFrom);
+    QRectF to = contentGeometry(orientation);
+
+    HsWidgetPresentationData presentation;
+    presentation.orientation = orientation;
+    
+    HsWidgetHostVisual *visual(0);
+#ifdef HSWIDGETORGANIZER_ALGORITHM
+    QList<HsWidgetHost*> newWidgets;
+#endif //HSWIDGETORGANIZER_ALGORITHM    
+    foreach (HsWidgetHost *widget, mWidgets) {
+        visual = widget->visual();
+        if (!widget->getPresentation(presentation)) {
+#ifndef HSWIDGETORGANIZER_ALGORITHM
+            QList<QRectF> geometries = converter->convert(
+                from, QList<QRectF>() << visual->geometry(), to);
+            visual->setGeometry(geometries.first());
+            widget->savePresentation();
+#else //HSWIDGETORGANIZER_ALGORITHM
+            newWidgets << widget;
+#endif //HSWIDGETORGANIZER_ALGORITHM
+        } else {
+            QRectF adjustWidgetPosition;
+            adjustWidgetPosition = visual->geometry();
+            adjustWidgetPosition.moveTopLeft(presentation.pos());
+            visual->setPos(adjustedWidgetPosition(adjustWidgetPosition));
+            visual->setZValue(presentation.zValue);
+            widget->savePresentation(); //Needed to follow pageMargin dynamic change
+        }
+    }
+    
+#ifdef HSWIDGETORGANIZER_ALGORITHM
+    // sort new widgets in order
+    if (newWidgets.count()) {
+        // TODO: read from configuration? or just use height for portrait and width for landscape (currently only height is used)
+        sortOrder order(height);
+        if(orientation == Qt::Horizontal) {
+            order = width;
+        } 
+        sortWidgets(order, newWidgets);
+        // get rects for new widgets
+        QList<QRectF> newRects;
+        foreach (HsWidgetHost *newWidget, newWidgets) {
+            newRects << QRectF(QPointF(), newWidget->visual()->preferredSize());
+        }
+        
+        // get page rect
+        QRectF pageRect = contentGeometry();
+
+        // scan existing widgets rects
+        QList<QRectF> existingRects;
+        foreach (HsWidgetHost *widget, mWidgets) {
+            if (!newWidgets.contains(widget)) {
+                existingRects << QRectF(widget->visual()->pos(), widget->visual()->preferredSize());
+            }
+        }
+         
+        // calculate new widget positions with "stuck 'em all"-algorithm
+        HsWidgetPositioningOnWidgetAdd *algorithm =
+            HsWidgetPositioningOnWidgetAdd::instance();
+        QList<QRectF> calculatedRects =
+            algorithm->convert(pageRect, existingRects, newRects, QPointF());
+
+        for ( int i=0; i<newWidgets.count(); i++) {            
+            int j = mWidgets.indexOf(newWidgets.at(i));
+            mWidgets.at(j)->visual()->setGeometry(calculatedRects.at(i));
+            mWidgets.at(j)->savePresentation();            
+        }               
+    }    
+#endif //HSWIDGETORGANIZER_ALGORITHM
+    
+}
+
+void HsPage::onPageMarginChanged(const QString &value)
+{
+    if (value == "pageMargin") {
+        mPageMargin = HSCONFIGURATION_GET(pageMargin);
+        HsWidgetHostVisual *visual(0);
+        if (!mWidgets.isEmpty()) {
+            foreach (HsWidgetHost *widget, mWidgets) {
+                visual = widget->visual();
+                visual->setPos(adjustedWidgetPosition(visual->geometry()));
+                widget->savePresentation();
+            }
+        }
+
+        if (!mNewWidgets.isEmpty()) {
+            foreach (HsWidgetHost *widget, mNewWidgets) {
+                visual = widget->visual();
+                visual->setPos(adjustedWidgetPosition(visual->geometry()));
+                widget->savePresentation();
+            }
+        }
+    }
+}
+#ifdef HSWIDGETORGANIZER_ALGORITHM
+// TODO: sorting should be done in algorithm class, make widget<->rect mapping here and move sortWidgets function to algorithm side
+/*!
+    Sorts widgets in height/width order
+*/
+void HsPage::sortWidgets(sortOrder order, QList<HsWidgetHost*> &widgets)
+{
+    QList<HsWidgetHost*> tmpWidgets;
+
+    for ( int i = 0; i < widgets.count(); i++) {
+        int index = 0;
+        // add first widget to sorted list
+        if (i == 0) {
+            tmpWidgets << widgets.at(i);
+        } else {
+            // go through existing widgets in the sorted list
+            for ( int j = 0; j < tmpWidgets.count(); j++) {
+                // sort widgets in height order
+                if (order == height) {
+                    /* if widgets heigth is smaller on already
+                       existing ones in the list -> increment index
+                    */
+                    if (widgets.at(i)->visual()->preferredHeight() <= tmpWidgets.at(j)->visual()->preferredHeight()) {
+                        index++;
+                    }
+                // sort widgets in width order
+                } else {
+                    /* if widgets width is smaller on already
+                       existing ones in the sorted list -> increment index
+                    */
+                    if (widgets.at(i)->visual()->preferredWidth() <= tmpWidgets.at(j)->visual()->preferredWidth()) {
+                        index++;
+                    }
+                }
+            }
+            // add widget to its correct index in sorted list
+            tmpWidgets.insert(index, widgets.at(i));
+        }
+    }
+    widgets = tmpWidgets;    
+}
+#endif //HSWIDGETORGANIZER_ALGORITHM