src/hbwidgets/devicedialogs/hbdeviceprogressdialog.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 21 4633027730f5
child 34 ed14f46c0e55
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 <hbdevicedialog.h>
#include <hbdevicedialogtrace_p.h>
#include "hbdeviceprogressdialog.h"
#include "hbdeviceprogressdialog_p.h"

#include <QTimer>
#include <QAction>

HbDeviceProgressDialogPrivate::HbDeviceProgressDialogPrivate() : QObject(),
    mVisible(false),
    mUpdateTimerId(0)
{
    TRACE_ENTRY
    for(int i = 0; i < NumActions; i++) {
        mDefaultActions[i] = 0;
    }
    TRACE_EXIT
}

HbDeviceProgressDialogPrivate::~HbDeviceProgressDialogPrivate()
{
    TRACE_ENTRY
    cancel();
    killTimer(mUpdateTimerId);
    for(int i = 0; i < NumActions; i++) {
        delete mDefaultActions[i];
    }
    TRACE_EXIT
}

void HbDeviceProgressDialogPrivate::initProperties(HbProgressDialog::ProgressDialogType type)
{
    for(int i = 0; i < NumProperties; i++) {
        mProperties[i].mFlags = NoFlags;
    }
    clearActions();
    if (!mDefaultActions[CancelButton]) {
        mDefaultActions[CancelButton] = new QAction(0);
    }
    mActions[CancelButton].mAction = mDefaultActions[CancelButton];

    QString text;
    mProperties[ProgressType].mValue.setValue(static_cast<int>(type));
    mProperties[ProgressType].mFlags = Modified;
    if (type == HbProgressDialog::WaitDialog){
        q->setRange(0,0);
        q->setAutoClose(false);
    } else {
        q->setRange(0,100);
        q->setAutoClose(true);
    }
    q->setProgressValue(0);
    q->setText(text);
    q->setIconName(text);
    q->setAnimationDefinition(text);
}

// Send properties to server
void HbDeviceProgressDialogPrivate::sendToServer(bool show)
{
    killTimer(mUpdateTimerId);
    mUpdateTimerId = 0;

    // If this is update but show has not been called, return.
    if (!show && !mVisible) {
      return;
    }

    // If this is update but no properties have been mofified, return
    if (!show && !propertiesModified()) {
        return;
    }

    static const char * const propertyNames[] = {
        "progressDialogType",
        "maximum",
        "minimum",
        "value",
        "autoClose",
        "text",
        "iconName",
        "animationDefinition"
    };

    QVariantMap parameters;
    for(int i = 0; i < NumProperties; i++) {
        if (mProperties[i].mFlags & Modified) {
            if (show || !(mProperties[i].mFlags & SentToServer)) {
                parameters.insert(propertyNames[i], mProperties[i].mValue);
                mProperties[i].mFlags |= SentToServer;
            }
        }
    }

    static const char * const actionNames[] = {
        "cancelAction"
    };

    for(int i = 0; i < NumActions; i++) {
        if (mActions[i].mFlags & Modified) {
            if (show || !(mActions[i].mFlags & SentToServer)) {
                QString actionData; // empty removes action at the plugin
                if (mActions[i].mAction) {
                    actionData.append("t:").append(mActions[i].mAction->text());
                }
                parameters.insert(actionNames[i], actionData);
                mActions[i].mFlags |= SentToServer;
            }
        }
    }

    if (show) {
        for(int i = 0; i < NumActions; i++) {
            mActions[i].mTriggered = false;
        }
        if (mDeviceDialog.show("com.nokia.hb.deviceprogressdialog/1.0", parameters)) {
            mVisible = true;
        } else {
            // Failed to show the device dialog. Start a one shot to emit aboutToClose() signal.
            QTimer::singleShot(0, this, SLOT(aboutToClose()));
        }
    } else {
        mDeviceDialog.update(parameters);
    }
}

// Check if any properties have been modified
bool HbDeviceProgressDialogPrivate::propertiesModified() const
{
    for(int i = 0; i < NumProperties; i++) {
        if ((mProperties[i].mFlags & Modified) && !(mProperties[i].mFlags & SentToServer)) {
            return true;
        }
    }
    for(int i = 0; i < NumActions; i++) {
        if ((mActions[i].mFlags & Modified) && !(mActions[i].mFlags & SentToServer)) {
            return true;
        }
    }
    return false;
}

// Clear actions
void HbDeviceProgressDialogPrivate::clearActions()
{
    for(int i = 0; i < NumActions; i++) {
        mActions[i].mAction = 0;
        mActions[i].mFlags = NoFlags;
        mActions[i].mTriggered = false;
    }
}

// Set int property
void HbDeviceProgressDialogPrivate::setProperty(PropertySelector propertySelector, int value)
{
    Property &property = mProperties[propertySelector];
    property.mValue.setValue(value);
    property.mFlags = Modified;
    scheduleUpdateEvent();
}

// Set string property
void HbDeviceProgressDialogPrivate::setProperty(PropertySelector propertySelector,
    const QString &value)
{
    Property &property = mProperties[propertySelector];
    property.mValue.setValue(value);
    property.mFlags = Modified;
    scheduleUpdateEvent();
}

void HbDeviceProgressDialogPrivate::init(HbProgressDialog::ProgressDialogType type)
{
    TRACE_ENTRY

    initProperties(type);
    connect(&mDeviceDialog, SIGNAL(deviceDialogClosed()), this, SLOT(aboutToClose()));
    connect(&mDeviceDialog, SIGNAL(dataReceived(QVariantMap)), this, SLOT(dataReceived(QVariantMap)));

    TRACE_EXIT
}

void HbDeviceProgressDialogPrivate::cancel()
{
    TRACE_ENTRY
    mDeviceDialog.cancel();
    TRACE_EXIT
    return;
}

// Schedule event to update changed properties to device dialog server. update() is not
// called after each time a property is set. Instead an event is scheduled in order to
// update all changed properties in one shot.
void HbDeviceProgressDialogPrivate::scheduleUpdateEvent()
{
    if (mVisible && mUpdateTimerId == 0) {
        mUpdateTimerId = startTimer(0);
    }
}

void HbDeviceProgressDialogPrivate::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == mUpdateTimerId) {
        sendToServer(false);
    }
}

HbDeviceProgressDialogPrivate::ActionSelector HbDeviceProgressDialogPrivate::actionSelector(
    HbDeviceProgressDialog::ActionRole role)
{
    static const ActionSelector selectors[] = {
        CancelButton
    };
    const unsigned numSelectors = sizeof(selectors) / sizeof(selectors[0]);
    unsigned index = role;
    if (index >= numSelectors) {
        Q_ASSERT(false);
        return InvalidSelector;
    }
    else {
        return selectors[index];
    }
}

void HbDeviceProgressDialogPrivate::aboutToClose()
{
    TRACE_ENTRY

    mVisible = false;
    emit q->aboutToClose();

    TRACE_EXIT
    return;
}

void HbDeviceProgressDialogPrivate::dataReceived(QVariantMap data)
{
    const char *key = "act";
    QVariantMap::const_iterator i = data.find(key);
    if (i != data.constEnd()) {
        if (i.value().toString() == "c") {
            // Client has pressed button. Signal action if one is set. Otherwise emit
            // cancelled() signal.
            mActions[CancelButton].mTriggered = true;
            QAction *action = mActions[CancelButton].mAction;
            if (action) {
                action->trigger();
                emit q->cancelled();
            }
        }
    }
}

/*!
    \class HbDeviceProgressDialog
    \brief HbDeviceProgressDialog is a device dialog version of HbProgressDialog.

    It displays a dialog with a wait animation or progress bar, text, icon or animation and a
    cancel button.

    Device dialogs are shown on top of any running applications and are always modal by nature.

    Two different dialogs are supported: wait and progress. The wait dialog displays an animated
    bar and progress dialog a progress bar indicating progress of the operation.

    Wait dialog is used when length of an operation cannot be determined beforehand. It displays
    an animated bar to indicate an action. The dialog is closed by the user pressing the cancel
    button, the application closing the dialog after the operation is finished.

    Progress dialog is used when the length of operation can be determined. For example
    when deleting a number of files, the progress of the operation could be shown as a
    percentage of the files deleted. Application updates the progress bar during the
    operation. The dialog closes by user pressing the cancel button, the application closing the
    dialog after the operation is finished or automatically when progress value reaches a
    maximum.

    HbDeviceProgressDialog provides a similar kind of interface as HbProgressDialog, excluding
    functions which handle concrete UI-component related information. Progress dialogs are
    always asynchronous by as the client needs to perform the operation and update the dialog
    while the dialog is showing.

    Device progress dialog is launched when show() is called. Launched dialog can be updated by setters.
    Because updating a dialog requires interprocess communication, it's advisable to fully construct the
    progress dialog before calling show().

    Supported icon animation formats are following:
    - GIF (.gif)
    - MNG (.mng)
        - Frame animations

    Sample code:

    An example showing the wait dialog:

    \code
    mDialog = new HbDeviceProgressDialog(HbProgressDialog::WaitDialog);
    mDialog->setText("Connecting...");
    mDialog->show();
    \endcode

    An example showing the progress dialog:

    \include deviceprogressdialog/main.cpp

    Creating a frame animation.

    Create an animation definition file:
    \code
    <animations>
        <icon name="frame_anim_looping" playmode="loop">
            <frame duration="100">c:\icon1.svg</frame>
            <frame duration="200">c:\icon2.svg</frame>
            <frame duration="300">c:\icon3.svg</frame>
            </icon>
    </animations>
    \endcode

    Create HbDeviceMessageBox in a way described before and
    set definition file and animation's logical name.

    \code
    QString animationDefinitionXML("c:\animation.axml");
    QString logicalIconName("frame_anim_looping");

    msg->setAnimationDefinition(animationDefinitionXML);
    msg->setIconName(logicalIconName);
    msg->show();
    \endcode

    \sa HbProgressDialog, HbDialog, HbDeviceDialog
    \stable
    \hbwidgets
*/

/*!
    \fn void HbDeviceProgressDialog::aboutToClose();

    This signal is emitted when a device progress dialog has closed. The closing may
    be a result of close() being called, a dialog with autoClose property has reached
    its maximum value or user pressing cancel button. It is not emitted if close() is
    called before show().
 */

/*!
    \fn void HbDeviceProgressDialog::cancelled();

    This signal is emitted when the device progress dialog is closed by user pressing the
    "cancel" button. It is not emitted if dialog is closed for any other reason.
 */

/*!
    \enum HbDeviceProgressDialog::ActionRole
    Defines roles for actions set into a progress dialog.
*/
/*!
    \var HbDeviceProgressDialog::ActionRole HbDeviceProgressDialog::InvalidRole
    No action.
*/
/*!
    \var HbDeviceProgressDialog::ActionRole HbDeviceProgressDialog::CancelButtonRole
    Cancel button action.
*/

/*!
    Constructor.
    \param type Must be one of the defined HbProgressDialog::ProgressDialogType enumerations.
    \param parent An optional parameter.
*/
HbDeviceProgressDialog::HbDeviceProgressDialog(HbProgressDialog::ProgressDialogType type, QObject *parent) :
    QObject(parent), d(new HbDeviceProgressDialogPrivate)
{
    TRACE_ENTRY
    d->q = this;
    d->init(type);
    TRACE_EXIT
}

/*!
    Constructor.
    \param parent An optional parameter.
*/
HbDeviceProgressDialog::HbDeviceProgressDialog(QObject *parent) :
    QObject(parent), d(new HbDeviceProgressDialogPrivate)
{
    TRACE_ENTRY
    d->q = this;
    d->init(HbProgressDialog::ProgressDialog);
    TRACE_EXIT
}

/*!
    Destructs the class.
*/
HbDeviceProgressDialog::~HbDeviceProgressDialog()
{
    TRACE_ENTRY
    delete d;
    TRACE_EXIT
}

/*!
    Executes the dialog asynchronously.
*/
void HbDeviceProgressDialog::show()
{
    TRACE_ENTRY
    d->sendToServer(true);
    TRACE_EXIT
 }

/*!
    Updates changed properties of a launched progress dialog to device dialog service using
    interprocess communication. Has no effect if show() has not been called or dialog has
    closed already. Calling show() is optional as updating any property schedules an event
    and the dialog is updated next time Qt event loop executes.

    \sa show()
*/
void HbDeviceProgressDialog::update()
{
    TRACE_ENTRY
    d->sendToServer(false);
    TRACE_EXIT
}

/*!
    Closes the dialog.
*/
void HbDeviceProgressDialog::close()
{
    TRACE_ENTRY
    return d->cancel();
    TRACE_EXIT
}

/*!
    Returns an action user triggered causing the dialog to close. Returns 0 if none of the actions were
    triggered and dialog was closed for other reason.

    \sa show(), action()
*/
const QAction *HbDeviceProgressDialog::triggeredAction() const
{
    for(int i = 0; i < HbDeviceProgressDialogPrivate::NumActions; i++) {
        if (d->mActions[i].mTriggered) {
            return d->mActions[i].mAction;
        }
    }
    return 0;
}

/*!
    Sets the maximum value of the progress bar within the dialog.

    \sa maximum()
*/
void HbDeviceProgressDialog::setMaximum(int max)
{
    TRACE_ENTRY
    // Don't allow wait dialog to set max/min other than zero as wait
    // animation bar doesn't work in that case.
    if (progressType() == HbProgressDialog::WaitDialog) {
        max = 0;
    }
    d->setProperty(HbDeviceProgressDialogPrivate::Maximum, max);
    TRACE_EXIT
}

/*!
    Returns the maximum value of the progress bar within the dialog. Default value is 100.

    \sa setMaximum()
*/
int HbDeviceProgressDialog::maximum() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::Maximum].mValue.toInt();
}

/*!
    Sets the minimum value of the progress bar within the dialog.

    \sa minimum()
*/
void HbDeviceProgressDialog::setMinimum(int min)
{
    TRACE_ENTRY
    // Don't allow wait dialog to set max/min other than zero as wait
    // animation bar doesn't work in that case.
    if (progressType() == HbProgressDialog::WaitDialog) {
        min = 0;
    }
    d->setProperty(HbDeviceProgressDialogPrivate::Minimum, min);
    TRACE_EXIT
}

/*!
    Returns the minimum value of the progress bar within the dialog. Default value is 0.

    \sa setMinimum()
*/
int HbDeviceProgressDialog::minimum() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::Minimum].mValue.toInt();
}

/*!
    Sets the minimum and maximum value of the progress bar within the dialog.

    \sa minimum(), maximum()
*/
void HbDeviceProgressDialog::setRange(int min, int max)
{
    setMinimum(min);
    setMaximum(max);
}

/*!
    Sets the value of the progress bar within the dialog.

    \sa progressValue()
*/
void HbDeviceProgressDialog::setProgressValue(int progressValue)
{
    TRACE_ENTRY
    d->setProperty(HbDeviceProgressDialogPrivate::Value, progressValue);
    TRACE_EXIT
}

/*!
    Returns the value of the progress bar within the dialog.

    \sa setProgressValue()
 */
int HbDeviceProgressDialog::progressValue() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::Value].mValue.toInt();
}

/*!
    Sets the autoClose property value of the dialog.

    \param autoClose When set, the dialog is closed when value of the progress bar reaches
    the maximum value of the progress bar.

    \sa autoClose()
*/
void HbDeviceProgressDialog::setAutoClose(bool autoClose)
{
    TRACE_ENTRY
    d->setProperty(HbDeviceProgressDialogPrivate::AutoClose, autoClose);
    TRACE_EXIT
}

/*!
    Returns the value of the autoClose property of the dialog.

    The default value is true for HbProgressDialog::ProgressDialog and false
    for HbProgressDialog::WaitDialog.

    \sa setAutoClose()
 */
bool HbDeviceProgressDialog::autoClose() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::AutoClose].mValue.toInt();
}

/*!
    Sets dialog's progress type. After setProgressType(), a new dialog is launched by a show().

    \sa progressType()
*/
void HbDeviceProgressDialog::setProgressType(HbProgressDialog::ProgressDialogType type)
{
    TRACE_ENTRY
    // After setProgressType(), a new dialog is launched by a show()
    d->mVisible = false;
    // All properties initialized to default
    d->initProperties(type);
    TRACE_EXIT
}

/*!
    Returns dialog's progress type.

    \sa setProgressType()
*/
HbProgressDialog::ProgressDialogType HbDeviceProgressDialog::progressType() const
{
    return static_cast<HbProgressDialog::ProgressDialogType>
        (d->mProperties[HbDeviceProgressDialogPrivate::ProgressType].mValue.toInt());
}

/*!
    Sets text of the dialog.

    \sa text()
*/
void HbDeviceProgressDialog::setText(const QString &text)
{
    TRACE_ENTRY
    d->setProperty(HbDeviceProgressDialogPrivate::Text, text);
    TRACE_EXIT
}

/*!
    Returns text of the dialog.
    \sa setText()
*/
QString HbDeviceProgressDialog::text() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::Text].mValue.toString();
}

/*!
    Sets message box icon name or animation logical name.

    \param aIconName Icon name. Icon can be from Hb resources or themes. Or can be a file in
    a file system.

    \sa IconName()
*/
void HbDeviceProgressDialog::setIconName(const QString &iconName)
{
    TRACE_ENTRY
    d->setProperty(HbDeviceProgressDialogPrivate::IconName, iconName);
    TRACE_EXIT
}

/*!
    Returns name and path of the icon or animation's logical name.

    \sa setIconName()
*/
QString HbDeviceProgressDialog::iconName() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::IconName].mValue.toString();
}

/*!
    Sets animation definition to a dialog. Animation's logical name has to be set
    using setIcon(). Animation definition files must be stored to a place where they
    can be accessed by device dialog service.

    Supported animation formats are following:
    - GIF (.gif)
    - MNG (.mng)
        - Frame animations

    \param animationDefinition Path and name of the animation definition file.

    \sa setIcon(), animationDefinition(), HbIconAnimationManager::addDefinitionFile()

*/
void HbDeviceProgressDialog::setAnimationDefinition(QString &animationDefinition)
{
    TRACE_ENTRY
    d->setProperty(HbDeviceProgressDialogPrivate::AnimationDefinition, animationDefinition);
    TRACE_EXIT
}

/*!
    Returns animation definition file name.

    \sa setAnimationDefinition()
*/
QString HbDeviceProgressDialog::animationDefinition() const
{
    return d->mProperties[HbDeviceProgressDialogPrivate::AnimationDefinition].mValue.toString();
}

/*!
    Sets a new action into progress dialog. When users presses a button on dialog, triggered()
    signal of the action is emitted. HbDeviceProgressDialog constructor sets a default action
    into a dialog.

    \param action Action or Null. Ownership is not transferred.
    \param role Selects an action to set.

    \sa action()
*/
void HbDeviceProgressDialog::setAction(QAction *action, ActionRole role)
{
    TRACE_ENTRY
    HbDeviceProgressDialogPrivate::ActionSelector  actionSelector =
        HbDeviceProgressDialogPrivate::actionSelector(role);
    if (actionSelector != HbDeviceProgressDialogPrivate::InvalidSelector) {
        HbDeviceProgressDialogPrivate::Action &dialogAction = d->mActions[actionSelector];
        dialogAction.mAction = action;
        dialogAction.mFlags = HbDeviceProgressDialogPrivate::Modified;
        d->scheduleUpdateEvent();
    }
    TRACE_EXIT
}

/*!
    Returns progress dialog action. The action may be a default action owned by the dialog
    or the one set by setAction().

    \param role Selects an action to get.

    \sa setAction()
*/
QAction *HbDeviceProgressDialog::action(ActionRole role) const
{
    HbDeviceProgressDialogPrivate::ActionSelector  actionSelector =
        HbDeviceProgressDialogPrivate::actionSelector(role);
    return actionSelector != HbDeviceProgressDialogPrivate::InvalidSelector ?
        d->mActions[actionSelector].mAction : 0;
}