src/hbwidgets/dataform/hbdataform.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 14 May 2010 16:09:54 +0300
changeset 2 06ff229162e9
parent 1 f7ac710697a9
child 3 11d3954df52a
permissions -rw-r--r--
Revision: 201017 Kit: 201019

/****************************************************************************
**
** 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 HbWidgets 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 <hbdataform.h>
#include <hbdataformmodelitem.h>
#include <hbdataformmodel.h>
#include "hbdataform_p.h"
#include "hbdataformviewitem_p.h"
#include "hbdataitemcontainer_p.h"
#include "hbdatagroup_p.h"
#include "hbdatagroup_p_p.h"
#include <hbcombobox.h>
#include "hbdataformheadingwidget_p.h"
#include "hbdataformmodelitem_p.h"
#include "hbtreemodeliterator_p.h"

#include <QGraphicsSceneMouseEvent>
#include <QCoreApplication>

/*!
    @beta
    @hbwidgets
    \class HbDataForm
    \brief HbDataForm represents hierarchical dataitems in the form of groups,pages and 
    items.
    The HbDataForm class provides a default view implementation of dataform.
    A HbDataForm implements a hierarchical representation of data items from a model.

    HbDataForm implements the interfaces defined by the HbAbstractItemView class to allow 
    it to display data provided  by models derived from the QAbstractItemModel class.

    It is simple to construct a dataform displaying data from a model. The user has to create
    HbDataFormModel and create the hierarchy of HbDataFormModelItems .The hierarchy is 
    similar to the following.
    
    - HbDataForm
       - HbDataFormPage1
         - HbDataGroup1
           - HbDataGroupPage1
             - HbDataItem
             - HbDataItem
             - HbDataItem
             - HbDataItem
       - HbDataFormPage2
         - HbDataGroup2
           - HbDataGroupPage2
             - HbDataItem
             - HbDataItem
             - HbDataItem
             - HbDataItem
 
    HbDataItem can be the child of HbDataForm, HbDataFormPage,HbDataGroup and 
    HbDataGroupPage. An instance of HbDataForm has to be created and model should be set 
    to the form using setModel(HbDataFormModel) API.
    The properties of each DataItem node can be set using HbDataFormModelItem convenient
    API's. These data are parsed while the visualization instance of each item is created and 
    set on each item.

    The model/view architecture ensures that the contents of the data view are updated as the 
    model changes.

    Items that have children can be in expanded (children are visible) or collapsed 
    (children are hidden) state. DataItems of type HbDataFormPage, HbDataGroup and 
    HbDataGroupPage can be expanded and collapsed. HbDataItem of type FormPageItem, 
    GroupItem, GroupPageItem can only have children. Each item in model is represented by an 
    instance of HbDataFormViewItem. HbDataForm uses HbDataFormViewItem prototype to instantiate 
    the HbDataForm items. HbDataFormViewItem can be subclassed for customization purposes.
    
    The Model hierarchy can be created using the convenient API's provided on model class like
    appendDataFormPage , appendDataFormGroup ,appendDataFormGroupPage and 
    appendDataFormItem. All of these will return HbDataFormModelItem instance correspoding 
    to each type on which user can set item specific data. Otherwise each HbDataFormModelItem can 
    be created individually by passing the corresponding type of item (GroupItem, GroupPageItem, 
    FormPageItem) and create the tree of HbDataFormModelItem using setParent API 
    or by passing the parent HbDataFormModelItem in constructor. Later the top level 
    HbDataFormModelItem can be added inside the model.

    After doing the setModel, the visualization gets created . Only the items inside the expanded 
    group or group page instances are created. When an item's visualization is created , 
    DataForm emits activated(constQModelIndex&) signal. The application can get 
    HbDataFormViewItem and content widget from DataForm using QModelIndex.    
    
    The signal emitted by HbDataForm
    \li activated(const QModelIndex &index) Emitted when the HbDataFormViewItem corresponding to
    \a index is shown. User can connect to this signal and can fetch the instance of 
    HbDataFormViewItem from HbDataForm using the API dataFormViewItem(const QModelIndex &index).
    \li dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) emitted when the 
    HbDataFormModel is updated \atopLeft and \abottomRight will be same since every node has only one column.
    User can connect to this signal and can fetch the instance of HbDataFormViewItem from HbDataForm 
    using the API dataFormViewItem(const QModelIndex &index) or user can fetch HbDataFormModelItem using API 
    itemFromIndex(const QModelIndex &index) in HbDataFormModel .When user updates model using 
    setContentWidgetData API provided in HbDataFormModelItem class, then DataForm takes care of updating the 
    corresponding item's visualization.

    The user can also provide connection information to correspoding content widget of each HbDataFormModelItem
    using API addConnection(HbDataFormModelItem* item, const char* signal, QObject* receiver, consta char* slot)
    provided in HbDataForm class.The connection will be established when the item visualization is created .
    similar way removeConnection(HbDataFormModelItem *item, const char* signal, QObject *receiver, const char* slot)
    and removeAllConnection() API can be used. Connection can be established or removed even at runtime also.
    
    An example of how to create HbDataForm:
    \snippet{ultimatecodesnippet/ultimatecodesnippet.cpp,31}

    The output generated by the above code looks like:

    \image html hbsettingform.png

    This is how HbDataForm will look like in landscape mode:

    \image html hbsettingform_landscape.png

    \sa HbDataFormViewItem, HbDataFormModel, HbDataFormModelItem

    Creating Custom Item:
    Application developer can create custom DataItem by deriving from HbDataFormViewItem and setting this as 
    prototype(setItemProtoType() API).
    Application has to override virtual API's createCustomWidget() , restore()and save(). 
    createCustomWidget() API should return the corresponding custom HbWidget which can also be a compound widget.
    Signal connection for child widgets inside the compound widget should taken care by the application. 
    restore() API will be called by the framework when the model data is changed.
    So restore() should take care of updating the visual items with correspoding data from model. 
    save() API should update the model.App developer should  connect respective widgets SIGNALs to SLOT save() 
    and update the data to model .

*/

/*!
    \fn void HbAbstractItemView::activated(const QModelIndex &index)

    This signal is emitted when HbDataFormViewItem corresponding to \a index is shown.

*/

/*!
    Constructs DataForm with given \a parent.
    \param parent parent .
 */
HbDataForm::HbDataForm(QGraphicsItem *parent)
    : HbAbstractItemView(*new HbDataFormPrivate(), new HbDataItemContainer(),
                         new HbTreeModelIterator(0, QModelIndex(), false), parent)
{
    Q_D( HbDataForm );
    d->q_ptr = this;
    d->init();
    setVerticalScrollBarPolicy(ScrollBarAlwaysOff);
    //d->mHeadingWidget->createPrimitives();
    //static_cast<HbDataItemContainer*>(container())->setFormHeading(d->mHeadingWidget);
}

/*!
    Constructs a data form with a private class object \a dd, 
    \a container and \a parent.
*/
HbDataForm::HbDataForm(HbDataFormPrivate &dd, HbAbstractItemContainer *container,
                       QGraphicsItem * parent)
        : HbAbstractItemView(dd, container, new HbTreeModelIterator(0, QModelIndex(), false), parent)
{
    Q_D( HbDataForm );
    d->q_ptr = this;
    d->init();
}

/*!
    Destructs the data form.
*/
HbDataForm::~HbDataForm()
{
}

/*!
    \reimp

    Scrolls the view so that the item represented by \a index comes at the middle of 
    screen. By default HbDataForm does not scrolls. Application developer is supposed to 
    call this API if he wants this behaviour. User can connect to activated signal and then
    can call this API.
*/
void HbDataForm::scrollTo(const QModelIndex &index, ScrollHint hint)
{
    //Q_D(HbDataForm);
    //d->revealItem(d->mContainer->itemByIndex(index), PositionAtCenter);
    HbAbstractItemView::scrollTo(index, hint);
}


/*!
    @beta

    Sets the item referred to by \a index to either collapse or expanded, 
    depending on the value of \a expanded. If \a expanded is true then child item are 
    supposed to be visible.

    \sa isExpanded
*/
void HbDataForm::setExpanded(const QModelIndex &index, bool expanded)
{
    Q_D(HbDataForm);

    if (isExpanded(index) != expanded) {
        d->treeModelIterator()->itemExpansionChanged(index);

        HbDataFormViewItem *item =
            static_cast<HbDataFormViewItem *>(d->mContainer->itemByIndex(index));
        if (item) {
            item->setExpanded(expanded);
        }

        d->mContainer->setItemTransientStateValue(index, "expanded", expanded);
        d->mContainer->setModelIndexes();
    }
}

/*!
    @beta

    Returns true if the model item \a index is expanded otherwise returns false.

    \sa setExpanded
*/
bool HbDataForm::isExpanded(const QModelIndex &index) const
{
    Q_D(const HbDataForm);
    QVariant flags = d->mContainer->itemTransientState(index).value("expanded");
    if (flags.isValid() && flags.toBool() == true) {
        return true;
    } else {
        return false;
    }
}

/*!
    @beta

    Sets the heading of HbDataForm with the \a heading provided. Heading is displayed on 
    top of the HbDataForm. Heading is non-focusable.

    \sa heading
*/
void HbDataForm::setHeading(const QString &heading)
{
    Q_D(HbDataForm);

    if(heading.isEmpty() && d->mHeadingWidget) {
        if(!d->mHeadingWidget->mPageCombo && d->mHeadingWidget->mDescription.isEmpty()) {
            // delete the FormheadingWidget
            delete d->mHeadingWidget;
            d->mHeadingWidget = 0;
            // Remove FormheadingWidget from container layout
            HbStyle::setItemName(d->mHeadingWidget,"NULL");
            return;
        }
    }

    if(!d->mHeadingWidget) {
        d->mHeadingWidget = new HbDataFormHeadingWidget();
        HbStyle::setItemName(d->mHeadingWidget,"this");
    }
    d->mHeadingWidget->mHeading = heading;
    d->mHeadingWidget->createPrimitives();

    if(d->mHeadingWidget->mPageCombo || !d->mHeadingWidget->mDescription.isEmpty() || 
        !d->mHeadingWidget->mHeading.isEmpty()) {
        static_cast<HbDataItemContainer*>(d->mContainer)->setFormHeading(d->mHeadingWidget);
    }
    d->mHeadingWidget->callPolish();
}

/*!
    @beta

    Returns heading of HbDataForm.

    \sa setHeading    
*/
QString HbDataForm::heading() const
{
    Q_D(const HbDataForm);
    if(d->mHeadingWidget) {
        return d->mHeadingWidget->mHeading;
    }

    return QString();
}

/*!
    @beta

    Sets the description of HbDataForm with the \a description. Description is displayed 
    below heading. Description is non-focusable.

    \sa description
*/
void HbDataForm::setDescription(const QString &description)
{
    Q_D(HbDataForm);

    if(description.isEmpty() && d->mHeadingWidget) {
        if(!d->mHeadingWidget->mPageCombo && d->mHeadingWidget->mHeading.isEmpty()) {
            // delete the FormheadingWidget
            delete d->mHeadingWidget;
            d->mHeadingWidget = 0;
            // Remove FormheadingWidget from container layout
            HbStyle::setItemName(d->mHeadingWidget,"NULL");
            return;
        }
    }

    if(!d->mHeadingWidget) {
        d->mHeadingWidget = new HbDataFormHeadingWidget();
        HbStyle::setItemName(d->mHeadingWidget,"this");
    }
    d->mHeadingWidget->mDescription = description;
    d->mHeadingWidget->createPrimitives();

    if(d->mHeadingWidget->mPageCombo || !d->mHeadingWidget->mDescription.isEmpty() || 
        !d->mHeadingWidget->mHeading.isEmpty()) {
            static_cast<HbDataItemContainer*>(d->mContainer)->setFormHeading(d->mHeadingWidget);
    }
    d->mHeadingWidget->callPolish();
}

/*!
    @beta

    Returns description of HbDataForm.

    \sa setDescription
*/
QString HbDataForm::description() const
{
    Q_D(const HbDataForm);
    if(d->mHeadingWidget) {
        return d->mHeadingWidget->mDescription;
    }
    return QString();
}

/*!

    \deprecated HbDataForm::primitive(HbStyle::Primitive)
         is deprecated.

    \reimp

    Returns the style primitive of HbDataForm depending upon the type \a primitive.
    \sa primitive
*/
QGraphicsItem* HbDataForm::primitive(HbStyle::Primitive primitive) const
{
    Q_D(const HbDataForm);

    switch (primitive) {
        case HbStyle::P_DataForm_heading_background:
            return d->mHeadingWidget->mBackgroundItem;
        case HbStyle::P_DataForm_heading:
            return d->mHeadingWidget->mHeadingItem;
        case HbStyle::P_DataForm_description:
            return d->mHeadingWidget->mDescriptionItem;
        default:
            return 0;
    }
}

/*!
    \reimp

    If \a model passed is NULL then all values of data form are reset. Calls the
    setModel of base class. This API does not clears the heading and description set
    for HbDataForm. If with new \a model user does not wants heading and description
    then he should call setHeading and setDescription with empty string.

    \sa setHeading, setDescription
*/
void HbDataForm::setModel(QAbstractItemModel *model, HbAbstractViewItem *prototype)
{
    Q_D(HbDataForm);
    if(d->mHeadingWidget) {
        if(model) {
            d->mHeadingWidget->mActivePage = -1;
        }
        if(d->mHeadingWidget->mPageCombo) {
            delete d->mHeadingWidget->mPageCombo;
            d->mHeadingWidget->mPageCombo = 0;
        }
    }
    HbAbstractItemView::setModel(model, prototype);
}


/*!
    \reimp
*/

void HbDataForm::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)
{
    Q_UNUSED(bottomRight);
    if(topLeft.isValid()) {

            HbDataFormViewItem* item = static_cast<HbDataFormViewItem*>(itemByIndex(topLeft));
            HbDataFormModelItem *modelItem = 
                        static_cast<HbDataFormModel *>(model())->itemFromIndex(topLeft);           
            HbDataFormModelItemPrivate *modelItem_priv = HbDataFormModelItemPrivate::d_ptr(modelItem);

            if(item){
                HbDataFormViewItemPrivate::d_ptr(item)->setEnabled( modelItem->isEnabled() );
                if( modelItem_priv->dirtyProperty() == "LabelRole"      ||
                    modelItem_priv->dirtyProperty() == "DecorationRole" || 
                    modelItem_priv->dirtyProperty() == "DescriptionRole" ) {

                     HbDataFormViewItemPrivate::d_ptr(item)->updateData();
                     return;
                }
                item->restore();
            }
    }
}
/*!
    \reimp

    Initializes \a option with the values from HbDataForm.
*/
void HbDataForm::initStyleOption(HbStyleOptionDataForm *option)
{
    Q_D(HbDataForm);
    d->mHeadingWidget->initStyleOption(option);
}


/*!
    \reimp
*/
void HbDataForm::rowsInserted(const QModelIndex &parent, int start, int end)
{
    HbAbstractItemView::rowsInserted(parent, start, end);
}

/*!
    \reimp
*/
void HbDataForm::rowsAboutToBeRemoved(const QModelIndex &index, int start, int end)
{
   Q_D(HbDataForm);

     for(int i = start; i <= end; i++) {

        QModelIndex childIndex = model()->index(i,0,index);
        // HbDataFormViewItem* view_Item =  static_cast<HbDataFormViewItem*>(itemByIndex(childIndex));
        QModelIndex siblingIndex = model()->index(i+1,0,index);
        HbDataFormModelItem::DataItemType itemType = static_cast<HbDataFormModelItem::DataItemType>(
        childIndex.data(HbDataFormModelItem::ItemTypeRole).toInt());
        QString label = childIndex.data(HbDataFormModelItem::LabelRole).toString();        

        if(itemType == HbDataFormModelItem::FormPageItem) {
            d->removeFormPage(label);            
        }

        if(itemType == HbDataFormModelItem::GroupPageItem) {
            QModelIndex group = childIndex.parent();
            HbDataGroup* groupItem = static_cast<HbDataGroup*>(itemByIndex(group));
            if(groupItem) {
                HbDataGroupPrivate::d_ptr(groupItem)->removeGroupPage(label);                
            }           
        }        
     }
}

/*!
    @alpha 

    This API can be used to connect with the signal of HbDataFormViewItem's content widget.
    For example: If HbDataFormModelItem is of type DataItemType::SliderItem then user
    can connect to the signals of slider using this API.
    Example Usage:
    \code
    HbDataForm *form = new HbDataForm();
    HbDataFormModel *model = new HbDataFormModel();
    HbDataFormModelItem *sliderItem = model->appendDataFormItem(HbDataFormModelItem::SliderItem);
    form->addConnection(sliderItem, SIGNAL(sliderReleased()), 
        this, SLOT(volumeChanged()));
    \endcode

    \param item Instance of model item 
    \param signal Signal of content widget. 
    \param receiver Instance of object whose slot will be called 
    \param slot Slot of \a receiver which will get called when signal is emitted
 
    \sa removeConnection 
*/
void HbDataForm::addConnection(HbDataFormModelItem * item, 
                               const char* signal, 
                               QObject *receiver, 
                               const char* slot)
{   
    Q_D(HbDataForm);
    ItemSignal itemSignal;
    itemSignal.reciever = receiver;
    itemSignal.signal = signal;
    itemSignal.slot = slot;
    d->mConnectionList.insertMulti(item, itemSignal);
    d->connectNow(item, signal, receiver, slot);
}

/*!
    @alpha

    This API can be used to remove the signal connection which was established using the
    addConnection API.

    \sa addConnection 
*/
void HbDataForm::removeConnection(HbDataFormModelItem * item, 
                                  const char* signal, 
                                  QObject *receiver, 
                                  const char* slot)
{   
    Q_D(HbDataForm);
    d->removeConnection(item, signal, receiver, slot);
}

/*!
    @alpha

    Removes the connection of all the contentwidget of all the items which has been established .
    The connection information stored inside DataForm also cleared.

    \sa removeAllConnection 
*/
void HbDataForm::removeAllConnection()
{   
    Q_D(HbDataForm);
    d->removeAllConnection();
}

/*!
    @alpha

    Removes the all connections of the contentwidget of HbDataFormModelItem's corresponding 
    visual Item ( HbdataFormViewItem).The connection information of correspoding 
    HbDataFormModelItem stored inside DataForm also cleared.

    \sa removeAllConnection 
*/
void HbDataForm::removeAllConnection(HbDataFormModelItem *item)
{   
    Q_D(HbDataForm);
    d->removeAllConnection(item);
}

#include "moc_hbdataform.cpp"