/****************************************************************************
**
** 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 "hbvolumeslider_p.h"
#include "hbslider_p.h"
#include "hbnamespace_p.h"
#include "hbslidercontrol_p_p.h"
#include "hbinstance.h"
#include "hbabstractbutton.h"
#include "hbslidercontrol_p.h"
#include "hbstyleoptionslider_p.h"
#include <QGraphicsSceneEvent>
#include <QGraphicsScene>
#include <hbwidgetfeedback.h>
#ifdef HB_GESTURE_FW 
#include <hbtapgesture.h>
#endif
#ifdef HB_EFFECTS
#include "hbeffect.h"
#include "hbeffectinternal_p.h"
#define HB_SLIDER_TYPE "HB_SLIDER"
#endif

/*!
    Example usage:
    \code
    HbSlider *slider = new HbSlider(Qt::Horizontal);
    QList<HbSlider::SliderElement> sliderElements;
    sliderElements.append(HbSlider::DecreaseElement);
    sliderElements.append(HbSlider::TrackElement);
    sliderElements.append(HbSlider::IncreaseElement);
    sliderElements.append(HbSlider::IconElement);
    sliderElements.append(HbSlider::TextElement);
    slider->setElements(sliderElements);
    slider->setRange(0, 100);
    slider->setMajorTickInterval(50);
    slider->setTickPosition(Hb::SliderTicksLeft);
    // decreaseIcon is valid HbIcon
    slider->setIcon(HbSlider::DecreaseElement, decreaseIcon);
    // increaseIcon is valid HbIcon
    slider->setIcon(HbSlider:: IncreaseElement, increaseIcon);
    // icon is valid HbIcon
    slider->setIcon(HbSlider:: IncreaseElement,icon);
    slider->setText( "1%");
    \endcode
    @hbwidgets
    \class HbVolumeSlider
    \brief HbVolumeSlider widget provides a vertical volume slider.

    HbVolumeSlider is provided for convenience. HbVolumeSlider sets appropriate
    icons and provides volume slider specific functionality like a
    checkable mute button and appropriate elements out of the box. HbVolumeSlider
    consists of following slider elements by default:
    \li HbSlider::IncreaseElement
    \li HbSlider::TrackElement
    \li HbSlider::DecreaseElement
    \li HbSlider::IconElement

    \image html hbvolumeslider.png A volume slider.

    The elements can be changed by calling HbSlider::setElements( ) later at any time.

    \note order of element can not be changed

    By default HbVolumeSlider will have ticks on right side ( Hb::SliderTicksRight) .

    Orientation of HbVolumeSlider can not be changed. If orientation change is needed, 
    then first create HbSlider and set needed elements.

    Example usage:
    \code
    HbVolumeSlider *slider = new HbVolumeSlider( this );
    connect( slider, SIGNAL( valueChanged( int ) ), receiver, SLOT( volumeSliderChanged( int ) ) );
    connect( slider, SIGNAL( iconToggled( bool ) ), receiver, SLOT( volumeMuted( bool ) ) );
    \endcode

    Note:: if setElement is called on this slider , application is reponsible for
    inconsitent UI.
*/


class HbVolumeSliderPrivate : public HbSliderPrivate
{
    Q_DECLARE_PUBLIC( HbVolumeSlider )

public:
    HbVolumeSliderPrivate( );
    virtual ~HbVolumeSliderPrivate( );
    void init( );
    void _q_valueChanged( int value );
    void _q_muteToggled( bool state );
    bool currentValueVisible;
    // store previous value for un mute
    int previousValue;
};


HbVolumeSliderPrivate::HbVolumeSliderPrivate( )
{

}


HbVolumeSliderPrivate::~HbVolumeSliderPrivate( )
{
}


void HbVolumeSliderPrivate::init( )
{
    Q_Q( HbVolumeSlider );
    sliderControl->setToolTipVisible(false);
    q->connect( q, SIGNAL( valueChanged( int ) ), q, SLOT( _q_valueChanged( int ) ) );
    q->connect( q, SIGNAL( iconToggled( bool ) ), q, SLOT( _q_muteToggled( bool ) ) );
    QList<HbSlider::SliderElement> elements;
    elements << HbSlider::IncreaseElement
        << HbSlider::TrackElement
        << HbSlider::DecreaseElement
        << HbSlider::IconElement;
    if( currentValueVisible )
        elements<<HbSlider::TextElement;
    setElements( elements );
#ifdef HB_GESTURE_FW    
    q->grabGesture(Qt::TapGesture);
#endif 
    // create element because HbSlider will not create element for NULL Icon
    // and volume slider Icon is set in style
    if ( !elementItemMap.contains(HbSlider::IncreaseElement) ) {
        elementItemMap [HbSlider::IncreaseElement].item =
            HbStylePrivate::createPrimitive( HbStylePrivate::P_SliderElement_increase, q);
        HbStyle::setItemName( elementItemMap [HbSlider::IncreaseElement].item, "increment-icon" );
        elementItemMap[HbSlider::IncreaseElement].type = HbStylePrivate::P_SliderElement_increase;
        elementItemMap [HbSlider::IncreaseElement].touchItem =  HbStylePrivate::createPrimitive( HbStylePrivate::P_SliderElement_touchincrease, q );
            // create touch area for increment
        HbStyle::setItemName( elementItemMap [HbSlider::IncreaseElement].touchItem, "increment-icon-toucharea" );
    }

    if ( !elementItemMap.contains(HbSlider::DecreaseElement) ) {
        elementItemMap[HbSlider::DecreaseElement].item =  
            HbStylePrivate::createPrimitive( HbStylePrivate::P_SliderElement_decrease, q );
        elementItemMap[HbSlider::DecreaseElement].type = HbStylePrivate::P_SliderElement_decrease;
        HbStyle::setItemName( elementItemMap[HbSlider::DecreaseElement].item, "decrement-icon" );

        // create touch area for decrement
        elementItemMap[HbSlider::DecreaseElement].touchItem =  HbStylePrivate::createPrimitive( HbStylePrivate::P_SliderElement_touchdecrease, q ) ;
            HbStyle::setItemName( elementItemMap[HbSlider::DecreaseElement].touchItem, "decrement-icon-toucharea" );
    }
    if( !elementItemMap.contains(HbSlider::IconElement) ) {
        elementItemMap[HbSlider::IconElement].item = HbStylePrivate::createPrimitive( HbStylePrivate::P_SliderElement_icon,q );
        elementItemMap[HbSlider::IconElement].type = HbStylePrivate::P_SliderElement_icon;        
        HbStyle::setItemName( elementItemMap[HbSlider::IconElement].item, "icon-icon" );
    // creating a dummy button , because slider icon 
    // need to emit all signal which is emitted by button
    // there is no need for touch item for slider icon because
    // button can act as touch area
       HbAbstractButton *sliderIcon = new HbAbstractButton( q );
       elementItemMap [HbSlider::IconElement].touchItem=sliderIcon;
       HbStyle::setItemName( sliderIcon, "icon" );
       q->connect( sliderIcon, SIGNAL( pressed( ) ), q, SIGNAL( iconPressed( ) ) );
       q->connect( sliderIcon, SIGNAL( released( ) ), q, SIGNAL( iconReleased( ) ) );
       q->connect( sliderIcon, SIGNAL( clicked( ) ), q, SIGNAL( iconClicked( ) ) );
       q->connect( sliderIcon, SIGNAL( toggled( bool ) ), q, SIGNAL( iconToggled( bool ) ) );

#ifdef HB_EFFECTS
       q->connect( sliderIcon, SIGNAL( pressed( ) ), q, SLOT( _q_startIconPressedEffect( ) ) );
       q->connect( sliderIcon, SIGNAL( released( ) ), q,SLOT( _q_startIconReleasedEffect( ) ) );
#endif
    }

    HbStyleOptionSlider option;
    q->initStyleOption( &option );
    option.sliderElementIcon = icons.value( HbSlider::IncreaseElement ); 
    HbStylePrivate::updatePrimitive( elementItemMap [HbSlider::IncreaseElement].item,HbStylePrivate::P_SliderElement_increase,&option );
    option.sliderElementIcon = icons.value( HbSlider::DecreaseElement ); 
    HbStylePrivate::updatePrimitive( elementItemMap[HbSlider::DecreaseElement].item,HbStylePrivate::P_SliderElement_decrease,&option );
    option.sliderElementIcon = icons.value( HbSlider::IconElement ); 
    HbStylePrivate::updatePrimitive( elementItemMap[HbSlider::IconElement].item,elementItemMap[HbSlider::IconElement].type,&option );
    q->setIconCheckable( true );
    q->setFlags( QGraphicsItem::ItemIsFocusable );
    q->setProperty("changeincrementState" ,false);
    q->setProperty("changedecrementState" ,false);
    q->setProperty("state", "normal");
}

/*!
    this slot will be called when ever slider value changes
    This wil change icon of mute /unmute button based on slider value
 */
void HbVolumeSliderPrivate::_q_valueChanged( int value )
{
    Q_Q( HbVolumeSlider );
    if( q->minimum( )==value ) {
        // volume is mute
        q->setIconChecked( true );
    } else {
        // unmute volume
        q->setIconEnabled( true );
        q->setIconChecked( false );
    }
}

/*!
   This slot will be called when slider value reaches to non zero to zero
   or vice versa
 */
void HbVolumeSliderPrivate::_q_muteToggled( bool state )
{
    Q_Q( HbVolumeSlider );
    HbStyleOptionSlider option;
    q->initStyleOption( &option );
    sliderControl->setPreviousValue( false , 0 );
    if( state ) {
        previousValue=q->value( );
        if ( previousValue == q->minimum( ) ) {
            // mute via drag
            sliderControl->setPreviousValue( true , previousValue + q->singleStep( ) );
        }
        else {
            //mute by clicking on mute icon
            sliderControl->setPreviousValue( true , previousValue );
        }
        q->setValue( q->minimum( ) );
        option.state |= QStyle::State_Sunken;
        if( previousValue==q->minimum( ) ){
            q->setIconEnabled( false );
        }
    } else {
        // If Icon is toggled by pressing on it
        sliderControl->setPreviousValue( false , 0 );
        if( q->value( )==q->minimum( ) ) {
            q->setValue( previousValue );
        }
        option.state&= ~( QStyle::State_Sunken );
    }
    option.sliderElementIcon = icons.value( HbSlider::IconElement ); 
    HbStylePrivate::updatePrimitive( elementItemMap[HbSlider::IconElement].item,HbStylePrivate::P_SliderElement_icon,&option );
    q->setProperty("changeincrementState" ,false);
    q->setProperty("changedecrementState" ,false);
    q->setProperty("state", "normal");
    if ( q->value( ) == q->minimum( ) && !q->isIconChecked( ) ) {
        q->setIconChecked ( true);
    }
}


/*!
    The orientation is \b Qt::Vertical and the slider contains increase, track,

    decrease and icon elements by default.

    \sa setOrientation( ), setElements( )

    show current value if \a currentValueVisible is true
*/
HbVolumeSlider::HbVolumeSlider( bool currentValueVisible,QGraphicsItem *parent )
    : HbSlider( *new HbVolumeSliderPrivate, parent )
{
    Q_D( HbVolumeSlider );
    d->q_ptr = this;
    d->currentValueVisible = currentValueVisible;
    d->init( );
    // The default values for volume slider are specified by UX
    setRange( 0,100 );
    setSingleStep( 10 );
    setMajorTickInterval( 10 );
    setSliderPosition( 80 );
    setTickPosition( Hb::SliderTicksRight );
    updatePrimitives( );
}

/*!
    Destroys the volume slider.
*/
HbVolumeSlider::~HbVolumeSlider( )
{
}


/*!
    Sets whether the text element is visible.

    \sa isCurrentValueVisible( )
*/
void HbVolumeSlider::setCurrentValueVisible( bool visible )
{
    Q_D( HbVolumeSlider );
    if( visible != d->currentValueVisible ) {
        // add/delete text element
        d->currentValueVisible  = visible;
        QList<HbSlider::SliderElement> elements;
        elements << HbSlider::IncreaseElement
            << HbSlider::TrackElement
            << HbSlider::DecreaseElement
            << HbSlider::IconElement;
        if( visible ) {
            elements<<HbSlider::TextElement;
        }
        //reset all value specified by UX
        d->setElements( elements );
        setRange( 0,100 ); 
        setSingleStep( 10 );
        setMajorTickInterval( 10 );
        setSliderPosition( 80 );
        setTickPosition( Hb::SliderTicksRight );
        updatePrimitives( );
    }
}


/*!
    Returns \c true whether the text element is visible.

    The default value is \c false.

    \sa setCurrentValueVisible( )
*/
bool HbVolumeSlider::isCurrentValueVisible( ) const
{
    Q_D( const HbVolumeSlider );
    return d->currentValueVisible ;
}

/*!
    \reimp

    This is not supported in HbVolumeSlider
 */
void HbVolumeSlider::setToolTipVisible( bool /*b*/ )
{
    Q_ASSERT_X( false, "HbZoomSlider::setToolTipVisible( )", 
        "Current value display as tooltip is not supported in volume slider" );
}

/*!
    \reimp
    unmute the volume to previous store value in case unmute happens
    by clicking increment/decrement icon
 */
void HbVolumeSlider::mousePressEvent( QGraphicsSceneMouseEvent *event )
{

#ifndef HB_GESTURE_FW    
    Q_D( HbVolumeSlider );
    if( d->elementItemMap.contains(HbSlider::IncreaseElement) ) {
        if ( d->elementItemMap[HbSlider::IncreaseElement].touchItem->isUnderMouse( ) ) {
            d->pressOnIncrement = true;
            HbWidgetFeedback::triggered( d->sliderControl, Hb::InstantPressed, Hb::ModifierSliderElement );
            event->accept( );
            // if volume was mute
            if( isIconChecked( ) ){
                // take to previous value and increase one step
                setValue( d->previousValue+singleStep( ) );
                // start timer for repeat action
                d->sliderControl->setRepeatAction( 
                    HbAbstractSliderControl::SliderSingleStepAdd, d->sliderControl->maximum( ) );
            } else {// simply increase by one step
                d->startIncrementing( );
            }
            setProperty("changeincrementState" ,true);
            setProperty("changedecrementState" ,false);
            setProperty("state", "pressed");
            return;
        }
    }
    if( d->elementItemMap.contains(HbSlider::DecreaseElement) ) {
        if ( d->elementItemMap[HbSlider::DecreaseElement].touchItem->isUnderMouse( ) ) {
            d->pressOnIncrement = false;
            HbWidgetFeedback::triggered( d->sliderControl, Hb::InstantPressed, Hb::ModifierSliderElement );
            event->accept( );
            if( isIconChecked( ) ){
                //set value to previous value and decrement by one step
                setValue( d->previousValue-singleStep( ) );
                if( value( )==minimum( ) ){
                    d->previousValue = value( );
                    setIconEnabled( false );
                }
                //start timer for repeat action
                d->sliderControl->setRepeatAction( 
                HbAbstractSliderControl::SliderSingleStepSub, d->sliderControl->minimum( ) );
            } else {
                d->startDecrementing( );
            }
            setProperty("changeincrementState" ,false);
            setProperty("changedecrementState" ,true);
            setProperty("state", "pressed"); 
            return;
        }
    }
   HbSlider::mousePressEvent( event );
#else 
    Q_UNUSED(event)
#endif 
 }

/*!
    \reimp
    stop the timer and change image for increment element /decrement element
 */
void HbVolumeSlider::mouseReleaseEvent( QGraphicsSceneMouseEvent *event )
{
#ifndef HB_GESTURE_FW 
    Q_D( HbVolumeSlider );
    HbSlider::mouseReleaseEvent( event );
    d->stopRepeatAction( );
    setProperty("changeincrementState" ,false);
    setProperty("changedecrementState" ,false);
    setProperty("state", "normal"); 
#else
    Q_UNUSED(event)
#endif 
}

/*!
    \reimp
 */
QVariant HbVolumeSlider::itemChange( GraphicsItemChange change, const QVariant &value )
{
    Q_D( HbVolumeSlider );
    switch ( static_cast<HbPrivate::HbItemChangeValues>( change ) ) {
        case ItemVisibleHasChanged:
            if( !value.toBool( ) ){
                d->stopRepeatAction( );
                if( d->sliderControl ){
                    d->sliderControl->setSliderDown( false );
                }
                setProperty("changeincrementState" ,false);
                setProperty("changedecrementState" ,false);
                setProperty("state", "normal");   
            }
            break;
        default:
            break;
    }
    return HbSlider::itemChange( change, value );
}

/*!
    \reimp
 */
void HbVolumeSlider::initStyleOption( HbStyleOptionSlider *option ) const
{
    option->sliderType=HbStyleOptionSlider::VolumeType;
    HbSlider::initStyleOption( option );

}

/*!
    \reimp
 */
void HbVolumeSlider::focusInEvent( QFocusEvent* /*event*/ )
{
    Q_D( HbVolumeSlider );
    d->sliderControl->setFocus( );
}

/*!
    \reimp
 */
void HbVolumeSlider::gestureEvent(QGestureEvent *event)
{ 
    Q_D(HbVolumeSlider);
    //consume the event if gesture is on increment or decrement,
    //if the slide ris muted and then on pressing on icrement/decrement value consider previous value as well
    if (HbTapGesture *tap = qobject_cast<HbTapGesture *>(event->gesture(Qt::TapGesture))) {
        QPointF pos = event->mapToGraphicsScene(tap->position());
        bool consumeEvent = false;
        switch( tap->state( ) ) {
        case Qt::GestureStarted: {
            if ( d->elementItemMap.contains (HbSlider::IncreaseElement ) ) {
                if (d->elementItemMap[HbSlider::IncreaseElement].touchItem ) {
                    if (d->elementItemMap[HbSlider::IncreaseElement].touchItem->sceneBoundingRect().contains(pos)) {
                        consumeEvent = true;
                        d->pressOnIncrement = true;
                        HbWidgetFeedback::triggered( d->sliderControl, Hb::InstantPressed, Hb::ModifierSliderElement );
                        event->accept( );
                        // if volume was mute
                        if( isIconChecked( ) ){
                            // take to previous value and increase one step
                            setValue( d->previousValue+singleStep( ) );
                            // start timer for repeat action
                            d->sliderControl->setRepeatAction( 
                                HbAbstractSliderControl::SliderSingleStepAdd, d->sliderControl->maximum( ) );
                        } else {// simply increase by one step
                            d->startIncrementing( );
                        }
                        setProperty("changeincrementState" ,true);
                        setProperty("changedecrementState" ,false);
                        setProperty("state", "pressed");
                    }
                }
            }
            if ( d->elementItemMap.contains (HbSlider::DecreaseElement ) ) {
                if (d->elementItemMap[HbSlider::DecreaseElement].touchItem ) {
                    if (d->elementItemMap[HbSlider::DecreaseElement].touchItem->sceneBoundingRect().contains(pos))  {
                        consumeEvent = true;
                        d->pressOnIncrement = false;
                        HbWidgetFeedback::triggered( d->sliderControl, Hb::InstantPressed, Hb::ModifierSliderElement );
                        event->accept( );
                        if( isIconChecked( ) ){
                            //set value to previous value and decrement by one step
                            setValue( d->previousValue-singleStep( ) );
                            if( value( )==minimum( ) ){
                                d->previousValue = value( );
                                setIconEnabled( false );
                            }
                            //start timer for repeat action
                            d->sliderControl->setRepeatAction( 
                            HbAbstractSliderControl::SliderSingleStepSub, d->sliderControl->minimum( ) );
                        } else {
                            d->startDecrementing( );
                        }
                        setProperty("changeincrementState" ,false);
                        setProperty("changedecrementState" ,true);
                        setProperty("state", "pressed"); 

                    }
                }
            }
        }
        break;
        case Qt::GestureCanceled:
        case Qt::GestureFinished:            
            setProperty("changeincrementState" ,false);
            setProperty("changedecrementState" ,false);
            setProperty("state", "normal"); 
            consumeEvent = true;
            HbSlider::gestureEvent(event);
            break;
        default:
            break;
        }    
        if(!consumeEvent) {
           event->ignore();
           HbSlider::gestureEvent(event);
       }
       

    }
        
}

#include "moc_hbvolumeslider_p.cpp"



