src/hbwidgets/itemviews/hblistviewitem.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 03 May 2010 12:48:33 +0300
changeset 1 f7ac710697a9
parent 0 16d8024aca5e
child 2 06ff229162e9
permissions -rw-r--r--
Revision: 201015 Kit: 201018

/****************************************************************************
**
** 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 "hblistviewitem.h"
#include "hblistviewitem_p.h"
#include "hblistview.h"

#include <hbnamespace.h>
#include <hbicon.h>
#include <hbiconitem.h>
#include <hbtextitem.h>
#include <hbrichtextitem.h>
#include <hbstyle.h>
#include <hbstyleoptionlistviewitem.h>

#include <QPersistentModelIndex>
#include <QVariant>
#include <QItemSelectionModel>
#include <QStyleOption>
#include <QSizePolicy>

/*!
    @beta
    @hbwidgets
    \class HbListViewItem
    \brief The HbListViewItem class represents a single row in a list.

    The HbListViewItem class provides an item that is used by the HbListView class to
    visualize content within single model index. By default HbListViewItem supports 
    QStringList that is stored into Qt::DisplayRole role and a list of QIcons or 
    HbIcons that is stored into Qt::DecoratorRole role within the index. 

    This class is provided mainly for customization purposes but it also acts as a default
    item prototype inside HbListView. See HbListView how to set customized class as a item prototype.

    \b Subclassing

    When subclassing HbListViewItem, you must provide implementations of createItem() and updateChildItems() functions.
    
    To support multiple list view items within single list view, you must also provide an implementation of canSetModelIndex().

    If derived list view item has transient state information that is not meaningful to store within model index (child item cursor 
    position selection areas etc.) view item can use list views internal state model to store this information. This feature can 
    be taken into use by implementing state() and setState() functions in derived class.

    \b Layout

    Layout selection in list view item is based on HbListViewItem::GraphicsSize and HbListViewItem::StretchingStyle properties together with list view item content.

    Below is described how different subitems are layouted with default layouts, how content is assigned to different subitems and
    what content is optional and what is mandatory in which layout.

    \note Item sizes and alignment related to each other may differ from real layouts in these pictures. 

    \image html listlayout-1.png Icon layout.

    \image html listlayout-2.png Image layout.

    \image html listlayout-3.png Landscape stretched layout.

    Optional items are indicated with dotted line. In all layouts both text-1 and text-2 item will stretch if required in horizontal direction.

    Image layout is selected when HbListViewItem::GraphicsSize is set as HbListViewItem::Image; otherwise icon layout is selected in portrait. 

    Landscape stretched layout will be used in landscape if HbListViewItem::StretchingStyle is set as HbListViewItem::StretchLandscape; 
    otherwise same layout is used both in portrait and landscape. In stretched layout it is mandatory to have either text-1 or icon-1.

    <table align=center>
         <tr><td> Item name </td>     <td> Description </td>
    </tr><tr><td> selection-icon </td><td> Shown when view selection mode is other than HbAbstractItemView::NoSelection. </td>
    </tr><tr><td> icon-1 </td>        <td> HbIcon from Qt::DecorationRole or if Qt::DecorationRole contains an icon list then first HbIcon from it. </td>
    </tr><tr><td> icon-2 </td>        <td> Second HbIcon from icon list inside Qt::DecorationRole. </td>
    </tr><tr><td> text-1 </td>        <td> QString from Qt::DisplayRole or if Qt::DisplayRole contains a string list then first QString from it. </td>
    </tr><tr><td> text-2 </td>        <td> Second QString from string list inside Qt::DisplayRole. </td>
    </tr><tr><td> text-3 </td>        <td> Third QString from string list inside Qt::DisplayRole. </td></tr>
    </table>        

    Below is an example how the layout properties can be configured.

    \snippet{ultimatecodesnippet/ultimatecodesnippet.cpp,45}
*/

/*!
    \enum HbListViewItem::StretchingStyle

    Stretching styles supported by HbListViewItem.

    This enum describes different stretching styles available for list view items in landscape.
*/

/*!
    \var HbListViewItem::NoStretching

    Stretched layout is not used in portrait or landscape resolutions. This is the default value.
*/


/*!
    \var HbListViewItem::StretchLandscape

    Strecthed layout is used in landscape resolution.
*/


/*!
    \enum HbListViewItem::GraphicsSize

    Graphics sizes supported by HbListViewItem.

    This enum describes different graphics sizes available for list view items.
*/

/*!
    \var HbListViewItem::SmallIcon

    Icon layout is used with small icon-1 size.
*/

/*!
    \var HbListViewItem::MediumIcon

    Icon layout is used with medium icon-1 size. This is the default value.
*/

/*!
    \var HbListViewItem::LargeIcon

    Icon layout is used with large icon-1 size.
*/

/*!
    \var HbListViewItem::Thumbnail

    Thumbnail layout is used.
*/

/*!
    \var HbListViewItem::WideThumbnail

    16:9 Thumbnail layout is used.
*/


HbListViewItemPrivate::HbListViewItemPrivate(HbListViewItem *prototype) :
    HbAbstractViewItemPrivate(prototype, new HbListViewItemShared)
{
}

HbListViewItemPrivate::HbListViewItemPrivate(HbListViewItem *prototype, HbListViewItemShared *shared) :
    HbAbstractViewItemPrivate(prototype, shared)
{
}
 
HbListViewItemPrivate::HbListViewItemPrivate(const HbListViewItemPrivate &source) :
    HbAbstractViewItemPrivate(source)
{
}

HbListViewItemPrivate &HbListViewItemPrivate::operator=(const HbListViewItemPrivate &source)
{
    HbAbstractViewItemPrivate::operator=(source);
    
    mItemsChanged = false;

    return *this;
}

HbListViewItemPrivate::~HbListViewItemPrivate()
{
}

void HbListViewItemPrivate::init()
{
    if (isPrototype()) {
        HbEffect::add("listviewitem-focus", "listviewitem_press", "pressed");
        HbEffect::add("listviewitem-focus", "listviewitem_release", "released");
        mSharedData->mItemType = "listviewitem";

        HbEffect::add("viewitem", "viewitem_appear", "appear");
        HbEffect::add("viewitem", "viewitem_disappear", "disappear");
    }
    mContentChangedSupported = true;
    mItemsChanged = false;
}

void HbListViewItemPrivate::setDecorationRole(const QVariant& value,
                                       const int index)
{
    Q_Q( HbListViewItem );

    // create icon item and set it to layout
    HbStyle::Primitive primitive = decorationPrimitive(value);

    if (primitive != HbStyle::P_None) {
        QGraphicsItem *item = mDecorationRoleItems.value(index);
        if (!item) {
            mItemsChanged = true;
            themingPending = true;
            item = q->style()->createPrimitive(primitive, q);

            if (index < mDecorationRoleItems.count()) {
                mDecorationRoleItems.replace(index, item);
            } else {
                mDecorationRoleItems.append(item);
            }
        }
    } else {
        mItemsChanged = true;
        if (index < mDecorationRoleItems.count()) {
            delete mDecorationRoleItems.at(index);
            mDecorationRoleItems.replace(index, 0);
        } else {
            mDecorationRoleItems.insert(index, 0);
        }
    }
}

void HbListViewItemPrivate::setDisplayRole(const QString& value,
                                           const int index)
{
    Q_Q( HbListViewItem );

    // create text item  and set it to layout
    if (!value.isNull()) {
        QGraphicsItem *textItem = mDisplayRoleTextItems.value(index);

        HbStyle::Primitive primitive = displayPrimitive();
        if (!textItem) {
            mItemsChanged = true;
            themingPending = true;
            textItem = q->style()->createPrimitive(primitive, q);
            if (index < mDisplayRoleTextItems.count()) {
                mDisplayRoleTextItems.replace(index, textItem);
            } else {
                mDisplayRoleTextItems.append(textItem);
            }
        }
    } else {
        if (index < mDisplayRoleTextItems.count()) {
            QGraphicsItem *item = mDisplayRoleTextItems.at(index);
            if (item) {
                mItemsChanged = true;
                delete item;
                mDisplayRoleTextItems.replace(index, 0);
            }
        } else {
            mDisplayRoleTextItems.insert(index, 0);
        }
    }
}

/*!
    Constructs a list view item with \a parent.
*/
HbListViewItem::HbListViewItem(QGraphicsItem *parent) : 
    HbAbstractViewItem(*new HbListViewItemPrivate(this), parent)
{
    Q_D( HbListViewItem );
    d->q_ptr = this;

    d->init();
}

/*!
    Creates a separate graphics widget with same list view item state as \a source.
*/
HbListViewItem::HbListViewItem(const HbListViewItem &source) :
    HbAbstractViewItem(*new HbListViewItemPrivate(*source.d_func()), 0)
{
    Q_D( HbListViewItem );
    d->q_ptr = this;

    d->init();
}

/*!
    Constructs a list view item with private view item \a dd object and \a parent.
*/
HbListViewItem::HbListViewItem(HbListViewItemPrivate &dd, QGraphicsItem * parent) :
    HbAbstractViewItem(dd, parent)
{
    Q_D( HbListViewItem );
    d->q_ptr = this;

    d->init();
}

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

/*!
    Assigns the \a source list view item to this list view item and returns a reference to this item.
*/
HbListViewItem &HbListViewItem::operator=(const HbListViewItem &source)
{
    Q_D( HbListViewItem );
    *d = *source.d_func();
    return *this;
}


/*!
    Creates a new item.
*/
HbAbstractViewItem *HbListViewItem::createItem()
{
    return new HbListViewItem(*this);
}

/*!
    \reimp
*/
int HbListViewItem::type() const
{
    return HbListViewItem::Type;
}

/*!
    Updates child graphics items to represent current state and content.
*/
void HbListViewItem::updateChildItems()
{
    Q_D( HbListViewItem );

    // DisplayRoles
    QVariant displayRole = d->mIndex.data(Qt::DisplayRole);
    QStringList stringList;
    if (displayRole.isValid()) {
        if (displayRole.canConvert<QString>()) {
            stringList.append(displayRole.toString());
        } else if (displayRole.canConvert<QStringList>()) {
            stringList = displayRole.toStringList();
        }
    }

    // if internal list is bigger than the new one, remove extra items
    while (d->mDisplayRoleTextItems.count() > stringList.count()) {
        d->mItemsChanged = true;
        delete d->mDisplayRoleTextItems.at(d->mDisplayRoleTextItems.count() - 1);
        d->mDisplayRoleTextItems.removeLast();
    }

    // create/delete primitives
    for (int i = 0; i < stringList.count(); ++i) {
        d->setDisplayRole(stringList.at(i), i);
    }

    d->mStringList = stringList;

    // decorationroles
    QVariant decorationRole = d->mIndex.data(Qt::DecorationRole);
    QVariantList variantList;
    if (decorationRole.isValid()) {
        if (decorationRole.canConvert<QIcon>()
             || decorationRole.canConvert<HbIcon>()) {
            variantList.append(decorationRole);
        } else if (decorationRole.canConvert< QList<QVariant> >()) {
            variantList = decorationRole.toList();
        }
    }

    // if list is too big, remove extra items
    while (d->mDecorationRoleItems.count() > variantList.count()) {
        d->mItemsChanged = true;
        delete d->mDecorationRoleItems.at(d->mDecorationRoleItems.count() - 1);
        d->mDecorationRoleItems.removeLast();
    }

    // create/delete primitives
    for (int i = 0; i < variantList.count(); ++i) {
        d->setDecorationRole(variantList.at(i), i);
    }
    d->mDecorationList = variantList;

    HbAbstractViewItem::updateChildItems();
}

/*!
    \reimp
*/
void HbListViewItem::updatePrimitives()
{
    HB_SDD(HbListViewItem);
    HbStyleOptionListViewItem styleOption;
    initStyleOption(&styleOption);

    int count = d->mStringList.count();
    for (int i = 0; i < count; ++i) {
        QGraphicsItem *item = d->mDisplayRoleTextItems.value(i);
        if (item) {
            styleOption.index = i;
            styleOption.content = d->mStringList.at(i);

            int minimumLines = 1;
            int maximumLines = 1;
            if (    i == 1
                &&  d->isMultilineSupported()) {
                // criteria of secondary text in middle column is fullfilled
                minimumLines = sd->mMinimumSecondaryTextRowCount;
                maximumLines = sd->mMaximumSecondaryTextRowCount;
            }
            // must always set some value to limit count of lines,
            // because by default HbTextItem gives text as much as space it wants
            styleOption.minimumLines = minimumLines;
            styleOption.maximumLines = maximumLines;

            style()->updatePrimitive(item, d->displayPrimitive(), &styleOption);
        }
    }

    styleOption.role = Qt::DecorationRole;
    count = d->mDecorationList.count();
    for (int i = 0; i < count; ++i) {
        QGraphicsItem *item = d->mDecorationRoleItems.value(i);
        if (item) {
            styleOption.index = i;
            styleOption.content = d->mDecorationList.at(i);
            style()->updatePrimitive(   
                        item, 
                        d->decorationPrimitive(d->mDecorationList.at(i)), 
                        &styleOption);
        }
    }
    HbAbstractViewItem::updatePrimitives();
}

/*!
    Returns the current text format.

    The default text format is Qt::PlainText.

    \sa setTextFormat
*/
Qt::TextFormat HbListViewItem::textFormat() const
{
    HB_SDD(const HbListViewItem);

    return sd->mTextFormat;
}

/*!
    Sets the text format as a \a textFormat.  
    
    This method will change the used text format for all view items.

    \sa textFormat
*/
void HbListViewItem::setTextFormat(Qt::TextFormat textFormat)
{
    HB_SDD(HbListViewItem);

    if (textFormat != sd->mTextFormat) {
        sd->mTextFormat = textFormat;

        int count(sd->mCloneItems.count());
        for (int i = 0; i < count; ++i) {
            HbListViewItem *item = static_cast<HbListViewItem *>(sd->mCloneItems.at(i));
            HbListViewItemPrivate *itemPrivate = item->d_func();

            qDeleteAll(itemPrivate->mDisplayRoleTextItems);
            itemPrivate->mDisplayRoleTextItems.clear();

            item->d_func()->mItemsChanged = true;
            item->updateChildItems();
        }
    }
}

/*!
    Returns the current stretching style.

    The default stretching style is HbListViewItem::NoStretching.

    \sa setStretchingStyle
*/
HbListViewItem::StretchingStyle HbListViewItem::stretchingStyle() const
{
    HB_SDD(const HbListViewItem);
    return sd->mStretchingStyle;
}

/*!
    Sets the stretching style as a \a style.  
    
    This method will change the used stretching style for all view items in landscape mode.
    If view is in portrait mode, behaviour defined by \a style becomes effective only when view is changed to landscape mode.

    \sa stretchingStyle
*/
void HbListViewItem::setStretchingStyle(StretchingStyle style)
{
    HB_SDD(HbListViewItem);
    if (style != sd->mStretchingStyle) {
        sd->mStretchingStyle = style;
        if (d->isLandscape()) {
            // secondary text multiline change!
            d->updateCloneItems(false);
            d->repolishCloneItems();
        }
    }
}

/*!
    Returns the current graphics size.

    The default graphics size is HbListViewItem::MediumIcon.

    \sa setGraphicsSize
*/
HbListViewItem::GraphicsSize HbListViewItem::graphicsSize() const
{
    HB_SDD(const HbListViewItem);
    return sd->mGraphicsSize;
}

/*!
    Sets the graphics size as a \a size.  
    
    This method will change the used graphics size for all view items.
    \note When graphics size HbListViewItem::Thumbnail is  set, multiline secondary texts are not supported.

    \sa graphicsSize
    \sa setSecondaryTextRowCount
*/
void HbListViewItem::setGraphicsSize(GraphicsSize size)
{
    HB_SDD(HbListViewItem);
    if (size != sd->mGraphicsSize) {
        bool thumbnailChange = (size == Thumbnail) | (sd->mGraphicsSize == Thumbnail);
        sd->mGraphicsSize = size;
        if (   thumbnailChange
            && !d->isStretching()) {
            // secondary text multiline change!
            d->updateCloneItems(false);
        }
        d->repolishCloneItems();
    }
}

/*!
    Returns the secondary text item maximum and minimum row counts.

    The default maximum row count is 1 and minimum row count is 1.

    \sa setSecondaryTextRowCount
*/
void HbListViewItem::getSecondaryTextRowCount(int &minimum, int &maximum) const
{
    HB_SDD(const HbListViewItem);
    minimum = sd->mMinimumSecondaryTextRowCount;
    maximum = sd->mMaximumSecondaryTextRowCount;
}

/*!
    Sets the secondary text item \a maximum row count and \a minimum row count.

    This method will change these values for all view items.

    \note If HbListViewItem::GraphicsSize is HbListViewItem::Thumbnail or HbListViewItem::StretchingStyle
    is HbListViewItem::StretchLandscape in landscape mode only one row secondary text is supported. \a minimum and
    \a maximum are applied only when graphics size is not HbListViewItem::Thumbnail and
    stretching style is not HbListViewItem::StretchLandscape in landscape mode. 

    \note If Qt::TextFormat is Qt::RichText, multiline secondary texts are note supported.

    \sa setTextFormat
    \sa secondaryTextRowCount
    \sa setStretchingStyle
    }sa setGraphicsSize
*/
void HbListViewItem::setSecondaryTextRowCount(int minimum, int maximum)
{
    HB_SDD(HbListViewItem);
    
    bool update = false;

    maximum = qMax(1, maximum);
    minimum = qBound(1, minimum, maximum);

    if (minimum != sd->mMinimumSecondaryTextRowCount) {
        sd->mMinimumSecondaryTextRowCount = minimum;
        update = true;
    }

    if (maximum != sd->mMaximumSecondaryTextRowCount) {
        sd->mMaximumSecondaryTextRowCount = maximum;
        update = true;
    }

    if (    update
        &&  d->isMultilineSupported()) {
        d->updateCloneItems(false);
    }
}

/*!
    \reimp
*/
void HbListViewItem::polish(HbStyleParameters& params)
{
    HB_SDD(HbListViewItem);

    setProperty("icon-1", (bool)d->mDecorationRoleItems.value(0));
    setProperty("icon-2", (bool)d->mDecorationRoleItems.value(1));

    setProperty("text-1", (bool)d->mDisplayRoleTextItems.value(0));
    setProperty("text-2", (bool)d->mDisplayRoleTextItems.value(1));
    setProperty("text-3", (bool)d->mDisplayRoleTextItems.value(2));

    setProperty("maximumSecondaryTextRowCount", sd->mMaximumSecondaryTextRowCount);
    if (itemView() && itemView()->selectionMode() != HbListView::NoSelection) {
        setProperty("selectionMode", true);
    } else {
        setProperty("selectionMode", false);
    }
    HbAbstractViewItem::polish(params);
}

#include "moc_hblistviewitem.cpp"