qtinternetradio/ui/src/irviewmanager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 19 Apr 2010 14:01:53 +0300
changeset 0 09774dfdd46b
child 2 2e1adbfc62af
permissions -rw-r--r--
Revision: 201011 Kit: 201015

/*
* 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 <hbaction.h>
#include <QApplication>
#include <QTimer>

#include "irviewmanager.h"
#include "irapplication.h"
#include "irmainview.h"
#include "ircategoryview.h"
#include "irstationsview.h"
#include "irnowplayingview.h"
#include "irsearchchannelsview.h"
#include "irfavoritesview.h"
#include "irhistoryview.h"
#include "irsettingsview.h"
#include "iropenwebaddressview.h"
#include "irsonghistoryview.h"
#include "irplsview.h"

const int KCrossLineWidth = 30; // pixel
const double KCrossLineMinLenth = 180.0; // pixel

const int KCrossLineTimeInterval = 1500; // ms
const int KExitTimeInterval = 800; // ms, used for showing cross Line

const int KCrossLineMinAngle = 15; // degree
const int KCrossLineMaxAngle = 75; // degree

static bool crossLineReady(const QLineF &aLine);
static bool crossLineIntersected(const QLineF &aLineA, const QLineF &aLineB);

enum CrossLineAngleType
{
    EPositiveAngle = 0,  // Line within 1,3 quadrant
    ENegativeAngle       // Line within 2,4 quadrant
};
static CrossLineAngleType crossLineAngleType(const QLineF &aLine);

/*
 * Description : constructor.
 *               add a softkey action to it in order to know that back buttion is touched.
 */
IRViewManager::IRViewManager() : iViewToHide(NULL),
                                 iCrossLineAReady(false),
                                 iCrossLineBReady(false),
                                 iCrossLineEnable(true),
                                 iCrossLineShowing(false),
                                 iCrossLineTimer(NULL),
                                 iExitTimer(NULL),
                                 iExiting(false)
{
    iBackAction = new HbAction(Hb::BackNaviAction, this);
    connect(iBackAction, SIGNAL(triggered()), this, SLOT(backToPreviousView()));
    
    iExitAction = new HbAction(Hb::QuitNaviAction, this);
    connect(iExitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
    
    connect(this, SIGNAL(currentViewChanged(HbView *)), this, SLOT(currentViewChanged(HbView *)));
    
    //effect for item selection
    HbEffect::add("irview", ":/effect/viewchangeeffects_show.fxml", "show");
    HbEffect::add("irview", ":/effect/viewchangeeffects_hide.fxml", "hide");
    
    iCrossLineTimer = new QTimer(this);
    iExitTimer = new QTimer(this);
    iCrossLineTimer->setSingleShot(true);
    iExitTimer->setSingleShot(true);
    connect(iCrossLineTimer,SIGNAL(timeout()),this,SLOT(crossLineReset()));
    connect(iExitTimer,SIGNAL(timeout()),this,SLOT(exitTimeout()));     
}

/*
 * Description : destructor
 */
IRViewManager::~IRViewManager()
{
    HbEffect::remove("irview", ":/effect/viewchangeeffects_show.fxml", "show");
    HbEffect::remove("irview", ":/effect/viewchangeeffects_hide.fxml", "hide");
}

/*
 * Description : from base class IRAbstractViewManager.
 *               get a pointer to a specified view. If the view is not created yet,
 *               view manager can create it and then return pointer to it.
 * Parameters  : aViewId : the view's id
 *               aCreateIfNotExist : whether or not create a view if it doesn't exist
 * Return      : pointer to the specified view.
 */
IRBaseView* IRViewManager::getView(TIRViewId aViewId, bool aCreateIfNotExist)
{
    int viewNumber = views().count();
    for (int i = 0; i < viewNumber; ++i)
    {
        IRBaseView* addedView = static_cast<IRBaseView*>(views().at(i));
        if (addedView && addedView->id() == aViewId)
        {
            return addedView;
        }
    }
    
    if (aCreateIfNotExist)
    {
        IRBaseView* newView = createView(iApplication, aViewId);
        addView(newView);
        return newView;
    }
    
    return NULL;
}

/*
 * Description : from base class IRAbstractViewManager.
 *               Judge if a view is in the view stack.
 * Parameters  : aViewId : the view's id
 * Return      : true  : the view is in view stack
 *               false : the view is not in view stack
 */
bool IRViewManager::isViewInStack(TIRViewId aViewId) const
{
    int numberOfViewsInStack = iViewStack.count();

    for (int i = numberOfViewsInStack-1; i >=0 ; i--)
    {
        IRBaseView* view = iViewStack[i];
        if (view)
        {
            if (view->id() == aViewId)
            {
               return true;
            }
        }
    }
    
    return false;
}

/*
 * Description : from base class IRAbstractViewManager.
 *               Activate a view specified aViewId. The new view will be current view.
 *               Old current view will be deactivated and pushed into view stack if 
 *               aStackCurrent is true.
 * Parameters  : aViewId : the view's id.
 *               aStackCurrent : whether or not push current view into view stack
 * Return      : None
 */
void IRViewManager::activateView(TIRViewId aViewId, bool aPushCurrentView)
{    
    if (isViewInStack(aViewId))
    {
        backToView(aViewId);
        return;
    }
    
    IRBaseView *baseView = static_cast<IRBaseView*>(currentView());
    if (baseView && baseView->id() == aViewId)
    {
        baseView->updateView();
        return;
    }
    
    IRBaseView *view = getView(aViewId, true);
    
    if (view)
    {
        if (view->flag() & EViewFlag_ClearStackWhenActivate)
        {
            clearStack();
        }
        else
        {    if (aPushCurrentView)
             {
                 if (baseView && !(baseView->flag() & EViewFlag_UnStackable))
                 {
                     iViewStack.push(baseView);
                 }
             }
        
             //deactivate current view
             if (baseView)
             {
                 baseView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Hide);
             }
        }      
         
        switchToNextView(view);
    }
}

/*
 * Description : from base class IRAbstractViewManager.
 *               Activate a view specified by aView. The new view will be current view.
 *               Old current view will be deactivated and pushed into view stack if 
 *               aStackCurrent is true.
 * Parameters  : aView : pointer to the view to be activated.
 *               aStackCurrent : whether or not push current view into view stack
 */
void IRViewManager::activateView(IRBaseView *aView, bool aPushCurrentView)
{
    if (aView == NULL)
    {
        return;
    }
    
    activateView(aView->id(), aPushCurrentView);
}

/*
 * Description : back view stack until a view whose id is aViewId.
 *               The view will become current view.
 * Parameters  : aViewId : the view's id
 */
void IRViewManager::backToView(TIRViewId aViewId)
{
    if (aViewId == currentViewId())
    {
        return;
    }
    
    //step 1 : back current view
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
    }
    
    //step 2 : back the views in view stack
    while (!iViewStack.isEmpty())
    {
        topView = iViewStack.top();
        if (topView->id() == aViewId)
        {
            break;
        }
        
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        iViewStack.pop();
    }
    
    //step 3 : we back to the view or the view is not in view stack. Activate it.
    if (!iViewStack.isEmpty())
    {
        topView = iViewStack.pop();
        Q_ASSERT(topView->id() == aViewId);        
        switchToNextView(topView);

    }
    else
    {
        //backToView(id) is called when view is in stack, it's impossible to get here
        Q_ASSERT(false);
    }
}

/*
 * Description : from base class IRAbstractViewManager.
 *               return the current view's id.
 * Return      : current view's id. 
 */
TIRViewId IRViewManager::currentViewId() const
{
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        return topView->id();
    }
    
    return EIRView_InvalidId;
}

/*
 * Description : from base class IRAbstractViewManager.
 *               handle system events reported by system event collector.
 * Parameters  : aEvent : see the definition of TIRSystemEventType
 * Return      : EIR_DoDefault : caller does default handling
 *               EIR_NoDefault : caller doesn't do default handling
 */
TIRHandleResult IRViewManager::handleSystemEvent(TIRSystemEventType aEvent)
{
    TIRHandleResult result = EIR_DoDefault;
    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    
    if (topView)
    {
        result = topView->handleSystemEvent(aEvent);
    }
    
    return result;
}

/*
 * Description : push a view into the view stack by id *               
 * Parameters  : aEvent : see the definition of TIRSystemEventType
 * Return      : void :  there is no return value for the function
 *                
 */
void IRViewManager::pushViewById(TIRViewId aViewId)
{
    if (isViewInStack(aViewId))
    {     
        return;
    }
    
    IRBaseView *curView = getView(aViewId, true);
    Q_ASSERT(curView);
    iViewStack.push(curView);
    
    updateSoftkey();
}
 

//                                     slot functions

/*
 * Description : slot function for softkey action.
 *               Bring user to a previous view. If view stack is empty, quit application.
 */
void IRViewManager::backToPreviousView()
{
    if(iViewStack.isEmpty())
    {
        return;
    }

    IRBaseView *topView = static_cast<IRBaseView*>(currentView());
    IRBaseView *viewToShow = iViewStack.pop();
    
    if(viewToShow)
    {
        if(topView)
        {
            topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        }
        switchToNextView(viewToShow);
    }
}


void IRViewManager::switchToNextView(IRBaseView *aView)
{
    if(NULL == aView)
    {
        return;
    }
    
    // if this is the lauch view
    if(views().count()<=1)
    {
        setCurrentView(aView,false);
        aView->handleCommand(EIR_ViewCommand_ACTIVATED, EIR_ViewCommandReason_Show);  
        aView->handleCommand(EIR_ViewCommand_EffectFinished, EIR_ViewCommandReason_Show);
        return;
    }

    iViewToHide = static_cast<IRBaseView*>(currentView());
    if(aView == iViewToHide)
    {
        aView->handleCommand(EIR_ViewCommand_ACTIVATED, EIR_ViewCommandReason_Show);  
        aView->handleCommand(EIR_ViewCommand_EffectFinished, EIR_ViewCommandReason_Show);
    }
    else
    {
        setCurrentView(aView,false);
        aView->handleCommand(EIR_ViewCommand_ACTIVATED, EIR_ViewCommandReason_Show);
        iViewToHide->setVisible(true);
    
        HbEffect::start(iViewToHide, "irview", "hide", this, "hideEffectFinished");
        HbEffect::start(aView, "irview", "show", this, "showEffectFinished");        
    }
}

void IRViewManager::hideEffectFinished(HbEffect::EffectStatus aStatus)
{
    Q_UNUSED(aStatus); 
    iViewToHide->setVisible(false);
}

void IRViewManager::showEffectFinished(HbEffect::EffectStatus aStatus)
{
    Q_UNUSED(aStatus);
    IRBaseView* viewToShow = static_cast<IRBaseView*>(currentView());
    viewToShow->handleCommand(EIR_ViewCommand_EffectFinished, EIR_ViewCommandReason_Show);
}
    

void IRViewManager::currentViewChanged(HbView *aView)
{
    Q_UNUSED(aView);
    
    updateSoftkey();
}

/*
 * Description : create a view specified by aViewId. A view is created only when it's 
 *               first time requested
 * Parameters  : aApplication : pointer to ir application object
 *               aViewId      : the view's id
 * Return      : pointer to the created view
 */
IRBaseView* IRViewManager::createView(IRApplication* aApplication, TIRViewId aViewId)
{
    switch (aViewId)
    {
        case EIRView_MainView:
            return new IRMainView(aApplication, aViewId);
        
        case EIRView_CategoryView:
            return new IRCategoryView(aApplication, aViewId);
        
        case EIRView_StationsView:
        case EIRView_SearchResultView:
            return new IRStationsView(aApplication, aViewId);
        
        case EIRView_PlayingView:
            return new IRNowPlayingView(aApplication, aViewId);
        
        case EIRView_SearchView:
            return new IRSearchChannelsView(aApplication, aViewId);
        
        case EIRView_FavoritesView:
            return new IRFavoritesView(aApplication, aViewId);
            
        case EIRView_HistoryView:
            return new IRHistoryView(aApplication, aViewId);
            
        case EIRView_SettingsView:
            return new IRSettingsView(aApplication, aViewId);
            
        case EIRView_OpenWebAddressView:
            return new IROpenWebAddressView(aApplication, aViewId);
        
        case EIRView_SongHistoryView:                        
            return new IRSongHistoryView(aApplication, aViewId);
            
        case EIRView_PlsView:
            return new IRPlsView(aApplication, aViewId);
            
        default:
            break;
    }
    
    return NULL;
}

void IRViewManager::clearStack()
{
    IRBaseView *topView = NULL;
    
    //deactivate and back current view if it exists
    topView = static_cast<IRBaseView*>(currentView());
    if (topView)
    {
        topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
    }
    
    while (!iViewStack.isEmpty())
    {
        topView = iViewStack.top();
        if (topView)
        {
            topView->handleCommand(EIR_ViewCommand_DEACTIVATE, EIR_ViewCommandReason_Back);
        }
        iViewStack.pop();
    }
}

void IRViewManager::updateSoftkey()
{
    HbView *topView = currentView();
    if (topView)
    {
        if (iViewStack.isEmpty())
        {
            topView->setNavigationAction(iExitAction);
        }
        else
        {
            topView->setNavigationAction(iBackAction);
        }
    }
}

void IRViewManager::mousePressEvent(QMouseEvent *aEvent)
{
    if(iCrossLineEnable)
    {
        if(iCrossLineAReady)
        {
            iCrossLineB.setP1(aEvent->posF());
        }
        else
        {
            iCrossLineA.setP1(aEvent->posF());
        }
    }

    HbMainWindow::mousePressEvent(aEvent);
}

void IRViewManager::mouseReleaseEvent(QMouseEvent *aEvent)
{
    if(iCrossLineEnable)
    {    
        if(iCrossLineAReady)
        {
            iCrossLineTimer->stop();

            iCrossLineB.setP2(aEvent->posF());
            iCrossLineBReady = crossLineReady(iCrossLineB);
    
            if(iCrossLineBReady && readyToQuit())
            {   
                iCrossLineEnable = false;
                iCrossLineShowing = true;              
                viewport()->repaint(); 
                iExitTimer->start(KExitTimeInterval);       
            }
            else
            {
                crossLineReset();
            }
        }
        else
        {
            iCrossLineA.setP2(aEvent->posF());
            iCrossLineAReady = crossLineReady(iCrossLineA);
    
            if(iCrossLineAReady)
            {
                iCrossLineTimer->stop();
                iCrossLineTimer->start(KCrossLineTimeInterval);
            }
        }
    }
    
    HbMainWindow::mouseReleaseEvent(aEvent);
}

bool IRViewManager::readyToQuit()
{
    if(iCrossLineAReady && iCrossLineBReady)
    {
        return crossLineIntersected(iCrossLineA,iCrossLineB);
    }

    return false;
}

bool crossLineIntersected(const QLineF &aLineA, const QLineF &aLineB)
{
    if(crossLineAngleType(aLineA) != crossLineAngleType(aLineB))
    {
        return QLineF::BoundedIntersection == aLineA.intersect(aLineB,NULL);
    }

    return false;
}

void IRViewManager::paintEvent(QPaintEvent *aEvent)
{
    HbMainWindow::paintEvent(aEvent);

    if(iCrossLineShowing)
    {
        QPainter painter(viewport());
        painter.setPen(QPen(QColor(225,225,225,200),KCrossLineWidth));	
        painter.drawLine(iCrossLineA);
		painter.drawLine(iCrossLineB);
    }
}

void IRViewManager::crossLineReset()
{
    iCrossLineAReady = false;
    iCrossLineBReady = false;
    iCrossLineShowing = false;
}

void IRViewManager::exitTimeout()
{
    crossLineReset();
    viewport()->repaint();
    HbMessageBox exitNote(hbTrId("txt_common_info_exiting"),
            HbMessageBox::MessageTypeInformation);
    exitNote.setPrimaryAction(NULL);
    exitNote.exec();
    qApp->quit();
    iExiting = true;
}

bool IRViewManager::isExiting() const
{
    return iExiting;
}



CrossLineAngleType crossLineAngleType(const QLineF &aLine)
{
    int linePos = aLine.angle() / 90;
    if( 0==linePos || 2==linePos )
    {
        return EPositiveAngle;
    }
    else
    {
        return ENegativeAngle;
    }
}

bool crossLineReady(const QLineF &aLine)
{
    if(aLine.length()> KCrossLineMinLenth)
    {
        int lineDegree = qRound(aLine.angle()) % 90;
        return (lineDegree<=KCrossLineMaxAngle) && (lineDegree>=KCrossLineMinAngle) ? true : false;
    }
    return false;
}