emailuis/nmhswidget/src/nmhswidget.cpp
author hgs
Thu, 14 Oct 2010 17:33:43 +0300
changeset 76 38bf5461e270
parent 74 6c59112cfd31
permissions -rw-r--r--
201041

/*
 * Copyright (c) 2010 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 <QtGui>
#include <QTranslator>
#include <QGraphicsLinearLayout>
#include <QTimer>
#include <hbcolorscheme.h>
#include <hbdocumentloader.h>
#include <hbframedrawer.h>
#include <hbframeitem.h>
#include <hblabel.h>
#include <hbstyleloader.h>
#include <hblistview.h>
#include <hbdeviceprofile.h>
#include <hbevent.h>

#include "nmcommon.h"
#include "nmmessageenvelope.h"
#include "nmhswidget.h"
#include "nmhswidgetemailengine.h"
#include "nmhswidgettitlerow.h"
#include "nmhswidgetconsts.h"
#include "nmhswidgetdatetimeobserver.h"
#include "nmhswidgetlistviewitem.h"
#include "nmhswidgetlistmodel.h"
#include "emailtrace.h"


NmHsWidget::NmHsWidget(QGraphicsItem *parent, Qt::WindowFlags flags)
    : HbWidget(parent, flags), 
      mMainContainer(0),
      mEmptySpaceContainer(0),
      mWidgetContainer(0),
      mTitleRow(0),
      mContentContainer(0),
      mNoMailsLabel(0),
      mContentLayout(0),              
      mBackgroundFrameDrawer(0),
      mTranslator(0),
      mEngine(0),      
      mAccountId(0),
      mAccountIconName(),
      mDateObserver(0),
      mIsExpanded(false),
      mListView(0),
	  mListModel(0),
	  mListActivityTimer(0)
{
    NM_FUNCTION;
}

/*!
 Destructor
 */
NmHsWidget::~NmHsWidget()
{
    NM_FUNCTION;

    delete mTranslator;
    mTranslator = NULL;

    delete mEngine;
    mEngine = NULL;

    delete mBackgroundFrameDrawer;
    mBackgroundFrameDrawer = NULL;

    delete mDateObserver;
    mDateObserver = NULL;
     
    if (mListActivityTimer){
        mListActivityTimer->stop();
        delete mListActivityTimer;
        mListActivityTimer = NULL;
    }
}

/*!
 \fn QPainterPath NmHsWidget::shape()

 Called by home screen fw to check widget boundaries, needed to draw
 outside widget boundingRect.
 /return QPainterPath path describing actual boundaries of widget 
  including child items
 */
QPainterPath NmHsWidget::shape() const
{
    NM_FUNCTION;
    
    QPainterPath path;
    path.setFillRule(Qt::WindingFill);
    if (mWidgetContainer){
        //add mWidgetContainer using geometry to get
        //correct point for top-left-corner
        QRectF widgetRect = mWidgetContainer->geometry();
        path.addRect(widgetRect); 
        
        //then fetch shape from title row 
        QPainterPath titlepath;
        titlepath.addPath(mTitleRow->shape());
        //translate it's location to be inside mWidgetContainer
        titlepath.translate(widgetRect.topLeft());
        //and finally add it to path
        path.addPath(titlepath);    
    }
    //simplified path, i.e. only outlines
    return path.simplified();
}

/*!
 \fn void NmHsWidget::onShow()

 called by home screen fw when widget gets visible
 */
void NmHsWidget::onShow()
{
    NM_FUNCTION;
    if (mEngine) {
        mEngine->activate();
    }
}

/*!
 \fn void NmHsWidget::onHide()

 called by home screen fw when widget gets hidden
 */
void NmHsWidget::onHide()
{
    NM_FUNCTION;
    if (mEngine) {
        mEngine->suspend();
    }
}

/*!
 \fn bool NmHsWidget::loadDocML(HbDocumentLoader &loader)
 
 loads layout data and child items from docml file. Must be called after constructor.
 /return true if loading succeeded, otherwise false. False indicates that object is unusable
 */
bool NmHsWidget::loadDocML(HbDocumentLoader &loader)
{
    NM_FUNCTION;
    
    bool ok(false);
    loader.load(KNmHsWidgetDocML, &ok);
    
    if(ok) {
        mMainContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetMainContainer));  
        mWidgetContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetContainer));
        mContentContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetContentContainer));
        mEmptySpaceContainer = static_cast<HbWidget*> (loader.findWidget(KNmHsWidgetEmptySpaceContainer));
        mNoMailsLabel = static_cast<HbLabel*> (loader.findWidget(KNmHsWidgetNoMailsLabel));
        mListView = static_cast<HbListView*> (loader.findWidget(KNmHsWidgetMailListView));
        if (!mMainContainer || !mWidgetContainer || !mContentContainer 
                || !mEmptySpaceContainer || !mNoMailsLabel || !mListView) {
            //something failed in documentloader, no point to continue
            NM_ERROR(1,"NmHsWidget::loadDocML fail @ containers or label");
            ok = false;
        }
    }
    return ok;
}

/*!
 Initializes Localization.
 /post mTranslator constructed & localization file loaded
 */
void NmHsWidget::setupLocalization()
{
    NM_FUNCTION;

    //Use correct localisation
    mTranslator = new QTranslator();
    QString lang = QLocale::system().name();

    //try to load translation from c drive. If not found then try z.
    bool loadingSucceed = mTranslator->load(KNmHsWidgetLocFileName + lang, "c:" + KNmHsWidgetLocLocation);
    if(!loadingSucceed){
        mTranslator->load(KNmHsWidgetLocFileName + lang, "z:" + KNmHsWidgetLocLocation);
    }
    
    QCoreApplication::installTranslator(mTranslator);
}


/*!
 Initializes UI. Everything that is not done in docml files should be here.
 return true if ok, in error false.
 */
void NmHsWidget::setupUi()
{
    NM_FUNCTION;

    //main level layout needed to control docml objects
    QGraphicsLinearLayout *widgetLayout = new QGraphicsLinearLayout(Qt::Vertical);
    widgetLayout->setContentsMargins(KNmHsWidgetContentsMargin, KNmHsWidgetContentsMargin,
            KNmHsWidgetContentsMargin, KNmHsWidgetContentsMargin);
    widgetLayout->setSpacing(KNmHsWidgetContentsMargin);
    widgetLayout->addItem(mMainContainer);
    this->setLayout(widgetLayout);

    //fetch pointer to content container layout
    //to be able to add/remove email rows and no mails label
    mContentLayout = (QGraphicsLinearLayout*) mContentContainer->layout();
    
    //set noMailsLabel properties not supported by doc loader 
    QColor newFontColor;
    newFontColor = HbColorScheme::color("qtc_hs_list_item_content_normal");
    mNoMailsLabel->setTextColor(newFontColor);
    mNoMailsLabel->setVisible(true);   
    
    mContentLayout->removeItem(mNoMailsLabel);
    
    //widget background
    mBackgroundFrameDrawer = new HbFrameDrawer(KNmHsWidgetBackgroundImage,
        HbFrameDrawer::NinePieces);
    HbFrameItem* backgroundLayoutItem = new HbFrameItem(mBackgroundFrameDrawer);
    //set to NULL to indicate that ownership transferred
    mBackgroundFrameDrawer = NULL;
    mContentContainer->setBackgroundItem(backgroundLayoutItem);
	
	HbEffect::add(mContentContainer, "combo_disappear_up", "collapse");
    HbEffect::add(mContentContainer, "combo_appear_down", "expand");
    
}

/*!
 Sets correct properties and mail item proto type for mail list view 
 */
void NmHsWidget::createMailRowsList()
{
	  NM_FUNCTION;

    //construct list model
    mListModel = new NmHsWidgetListModel(); 
    
	//Connect item activation (click) to message view launching
	connect(mListView, SIGNAL(activated(const QModelIndex&)), this,
        SLOT(openMessage(const QModelIndex&)));
        
    //When new mails arrive, scroll list to the start
    connect(mListModel, SIGNAL( messagesAddedToModel() ), 
        this, SLOT( handleMessagesAddedToModel() ));
    
    //connect item removal signal to update layout, so that nomailslabel & listview visibility
    //can be determined
    connect(mListModel, SIGNAL( modelIsEmpty(bool) ), 
            this, SLOT( updateLayout() ));
    
    // Set the list widget properties.
    mListView->setItemRecycling(false);  //This should be changed in future
    NmHsWidgetListViewItem *prototype = new NmHsWidgetListViewItem(mListView);
    mListView->setItemPrototype(prototype);
    mListView->setModel(mListModel);  
}

/*!
 Initializes the widget.
 
 called by home screen fw when widget is added to home screen
 */
void NmHsWidget::onInitialize()
{
    NM_FUNCTION;
    
    QT_TRY {
	    HbStyleLoader::registerFilePath(":/layout/nmhswidgetlistviewitem.widgetml");
	    HbStyleLoader::registerFilePath(":/layout/nmhswidgetlistviewitem.css");
	    
	    // Use document loader to load the contents
	    HbDocumentLoader loader;
		//setup localization before docml loading
	    setupLocalization();
		
	    //load containers and mNoMailsLabel
        if (!loadDocML(loader)) {
            NM_ERROR(1,"NmHsWidget::onInitialize Fail @ loader");
            emit error(); //failure, no point to continue
            return;
        }

        //construct title row
        mTitleRow = new NmHsWidgetTitleRow(this);
        if (!mTitleRow->setupUI(loader)) {
            //title row creation failed
            NM_ERROR(1,"NmHsWidget::onInitialize fail @ titlerow");
            emit error(); //failure, no point to continue
            return;            
        }
				
        setupUi();

        //Crete list for mail items
        createMailRowsList();        

        //Engine construction is 2 phased. 
        mEngine = new NmHsWidgetEmailEngine(mAccountId);
        connect(mEngine,    SIGNAL( mailDataRefreshed(const QList<NmMessageEnvelope*>&) ), 
                mListModel, SLOT  ( refresh          (const QList<NmMessageEnvelope*>&) ));
        connect(mEngine,    SIGNAL( mailDataCleared() ), 
                mListModel, SLOT  ( removeAllMessages() ));
        connect(mEngine,    SIGNAL( mailsReceived (const QList<NmMessageEnvelope*>&) ), 
                mListModel, SLOT  ( addMessages   (const QList<NmMessageEnvelope*>&) ));
        connect(mEngine,    SIGNAL( mailsUpdated  (const QList<NmMessageEnvelope*>&) ), 
                mListModel, SLOT  ( updateMessages(const QList<NmMessageEnvelope*>&) ));
        connect(mEngine,    SIGNAL( mailsDeleted  (const QList<NmId>&)), 
                mListModel, SLOT  ( removeMessages(const QList<NmId>&) ));
        //Client must connect to exception signals before calling the initialize function
        //because we don't want to miss any signals.
        connect(mEngine, SIGNAL( exceptionOccured(const int&) ), this,
            SLOT( onEngineException(const int&) ));
        if (!mEngine->initialize()) {
            //engine construction failed. Give up.
            NM_ERROR(1,"NmHsWidget::onInitialize fail @ engine");
            emit error();
            return;
        }

		//set account name to title row
        mTitleRow->updateAccountName(mEngine->accountName());

        //create observer for date/time change events
        mDateObserver = new NmHsWidgetDateTimeObserver();

        //Create timer for delaying events after list scrolling is ended
        mListActivityTimer = new QTimer(this);
        mListActivityTimer->setInterval(KNmHsWidgetDelayAfterScrollingEnded);
        connect(mListActivityTimer, SIGNAL(timeout()), this, SLOT(scrollListToStart()) );
        //Connect scrolling ended to scrolling timer activation
        //so that after timeout the list will be scrolled to first item
        connect(mListView, SIGNAL( scrollingEnded() ), 
                mListActivityTimer, SLOT( start() ));
        
        //if timer is running, stop it right away when new activity happens
        connect(mListView, SIGNAL( scrollingStarted() ), 
                mListActivityTimer, SLOT( stop() ));
        
        mTitleRow->updateUnreadCount(mEngine->unreadCount());
        mTitleRow->setAccountIcon(mAccountIconName);
        mTitleRow->setExpandCollapseIcon(mIsExpanded);
       
        //Get Signals about changes in unread count
        connect(mEngine, SIGNAL( unreadCountChanged(const int&) )
                ,mTitleRow, SLOT( updateUnreadCount(const int&) ) );
        
        //Get signals about account name changes
        connect(mEngine, SIGNAL( accountNameChanged(const QString&) )
                ,mTitleRow, SLOT( updateAccountName(const QString&) ) );

	    //Get signals about user actions
        //Qt::QueuedConnection used to improve ui responsiveness 
	    connect(mTitleRow, SIGNAL( mailboxLaunchTriggered() )
	            ,mEngine, SLOT( launchMailAppInboxView() ), Qt::QueuedConnection);
	    
	    connect(mTitleRow, SIGNAL( expandCollapseButtonPressed() )
	            ,this, SLOT( handleExpandCollapseEvent() ) );
	    
	    //Get date/time events from date observer
	    connect(mDateObserver, SIGNAL(dateTimeChanged())
	            , mEngine, SLOT(forceUpdate()));
	    
	    setMinimumSize(mTitleRow->minimumWidth(), 
	            mEmptySpaceContainer->minimumHeight() + mTitleRow->minimumHeight());
    }
    QT_CATCH(...) {
        NM_ERROR(1,"NmHsWidget::onInitialize fail @ catch");
        emit error();
    }
}

/*!
 Uninitializes the widget.
 
 called by home screen fw when widget is removed from home screen
 */
void NmHsWidget::onUninitialize()
{
    NM_FUNCTION;
    HbStyleLoader::unregisterFilePath(":/layout/nmhswidgetlistviewitem.widgetml");
    HbStyleLoader::unregisterFilePath(":/layout/nmhswidgetlistviewitem.css");
}


/*!
 Sets monitored account id from given string
 Needed for home screen framework which supports only QString type properties
 */
void NmHsWidget::setAccountId(const QString &text)
{
    NM_FUNCTION;

    bool ok;
    quint64 id = text.toULongLong(&ok);
    if (!ok) {
        NM_ERROR(1, "NmHsWidget::setAccountId: invalid account ID data, signal finished()!!!");
        //No valid account id so give up
        emit finished();
        return;
    }

    mAccountId.setId(id);
}

/*!
 Returns monitored account id as a string
 Needed for home screen framework which supports only QString type properties
 */
QString NmHsWidget::accountId() const
{
    NM_FUNCTION;
    return QString::number(mAccountId.id());
}

/*!
 Sets monitored account icon name from given string
 */
void NmHsWidget::setAccountIconName(const QString &text)
{
    NM_FUNCTION;
    mAccountIconName = text;
}

/*!
 Returns monitored account icon name
 */
QString NmHsWidget::accountIconName() const
{
    NM_FUNCTION;
    return mAccountIconName;
}

/*!
 Slot to handle expand/collapse trigger event
 */
void NmHsWidget::handleExpandCollapseEvent()
{
    NM_FUNCTION;
    if(mIsExpanded){
        HbEffect::start( mContentContainer, "collapse", this, "toggleExpansion");
    }
    else{
        toggleExpansionState();
        HbEffect::start( mContentContainer, "expand");
    }
}

/*!
 Sets widget expand/collapse state
 /post widget expansion state is changed
 */
void NmHsWidget::toggleExpansionState()
{
    NM_FUNCTION;

    mIsExpanded = !mIsExpanded;

    //save new state to home screen
    QStringList propertiesList;
    propertiesList.append("widgetState");
    emit setPreferences(propertiesList);

    //handle state change drawing
    updateLayout();

    mTitleRow->setExpandCollapseIcon(mIsExpanded);
}

/*!
 Sets expand/collapse state from given string (needed by homescreen)
 */
void NmHsWidget::setWidgetStateProperty(QString value)
{
    NM_FUNCTION;
    if (value == KNmHsWidgetStateCollapsed) {
        mIsExpanded = false;
    }
    else {
        mIsExpanded = true;
    }
}

/*!
 Returns widget expand/collapse state as string (needed by homescreen) 
 */
QString NmHsWidget::widgetStateProperty()
{
    NM_FUNCTION;
    if (mIsExpanded) {
        return KNmHsWidgetStateExpanded;
    }
    else {
        return KNmHsWidgetStateCollapsed;
    }
}


/*!
 Updates the Layout to contain the right items
 /param mailCount defines how many emails is to be shown
 /post If widget is collapsed, the layout contains only titleRow widget.
 If widget is expanded and mailCount is 0 layout will contain
 titlerow & noMailsLabel. 
 If widget is expanded and mailCount is greter
 than zero, layout will contain titlerow and KMaxNumberOfMailsShown times
 emailrow(s)
 */
void NmHsWidget::updateLayout()
{
    NM_FUNCTION;
    int mailCount = mListModel->rowCount();
	//collapsed size
    qreal totalHeight = mEmptySpaceContainer->preferredHeight() + mTitleRow->containerHeight();
    
    if (mIsExpanded) {
        //when expanded, grow as big as possible
        totalHeight = KNmHsWidgetHSMaxWidgetHeightInUnits * HbDeviceProfile::current().unitValue();
        mContentContainer->setVisible(true);
        if (mailCount == 0) {
            addNoMailsLabelToLayout();
            removeEmailRowsFromLayout();
        }
        else {
            removeNoMailsLabelFromLayout();
            addEmailRowsToLayout();
        }
    }
    else {
        removeNoMailsLabelFromLayout();
        removeEmailRowsFromLayout();
        mContentContainer->setVisible(false);        
    }

    //set maximum sizes, otherwise widget will stay huge also when collapsed
    setMaximumHeight(totalHeight);
    mMainContainer->setMaximumHeight(totalHeight);
    mWidgetContainer->setMaximumHeight(totalHeight - mEmptySpaceContainer->preferredHeight());
    //resize here or widget cannot draw mail rows when expanding
    //TODO: check if this is still needed as list used
    resize(mTitleRow->maximumWidth(), totalHeight);
    mMainContainer->resize(mTitleRow->maximumWidth(), totalHeight);
    mWidgetContainer->resize(mTitleRow->maximumWidth(), totalHeight - mEmptySpaceContainer->preferredHeight());
}

/*!
 Updates mNoMailsLabel visibility based on widget state
 /param mailCount defines how many mail rows is needed
 /post if mail count is 0 and mIsExpanded equals true, then
 the mNoMailLabel is added to the mContentLayout. 
 */
void NmHsWidget::addNoMailsLabelToLayout()
{
    NM_FUNCTION;

    if ( mNoMailsLabel->isVisible() ) {
        return;
    }
    //Add mNoMailsLabel to layout if not yet there and show it
    mContentLayout->addItem(mNoMailsLabel);
    //resize the widget to new layout size
    mNoMailsLabel->show();
}

/*!
 removeNoMailsLabelFromLayout removes mNoMailsLabel from the layout
 /post mNoMailsLabel is not in mContentLayout
 */
void NmHsWidget::removeNoMailsLabelFromLayout()
{
    NM_FUNCTION;
    //remove mNoMailsLabel from Layout and hide it
    mContentLayout->removeItem(mNoMailsLabel);
    mNoMailsLabel->hide();
}

/*!
 addEmailRowsToLayout adds list widget to content layout
 */
void NmHsWidget::addEmailRowsToLayout()
{
    NM_FUNCTION;
    
    mContentLayout->addItem(mListView);
    
    mListView->setVisible(true);
    
}

/*!
 removeEmailRowsFromLayout removes and hides list widget 
 from content layout
 */
void NmHsWidget::removeEmailRowsFromLayout()
{
    NM_FUNCTION;
    mContentLayout->removeItem(mListView);
    mListView->setVisible(false);
}

/*!
 openMessage slot
 */
void NmHsWidget::openMessage(const QModelIndex& index)
{
    NM_FUNCTION;
    QVariant var = mListModel->data(index,Qt::DisplayRole);
    if(!var.isNull()){
        NmMessageEnvelope *envelope = var.value<NmMessageEnvelope*>();
        mEngine->launchMailAppMailViewer(envelope->messageId());
    }
}

/*!
 handleMessagesAddedToModel slo
 Slot is called when new messages is added to model. This function should scroll 
 the list right away to the first item, unless there is user activity ongoing or
 the scrolling timer is active
 */
void NmHsWidget::handleMessagesAddedToModel()
{
    NM_FUNCTION;
    if(mListActivityTimer->isActive() || mListView->isScrolling()){
        //No need to scroll right away as user is active and we don't want to interupt it
        //or the scrolling event will occur within KNmHsWidgetDelayAfterScrollingEnded msecs.
        return; 
    }else{
        //if the list is idle, scroll to the first item
        scrollListToStart();
    }
}

/*!
 scrollListToStart slot - Scrolls the mListView to the beginning of the list with
 KListScrollUpTime as a time to do it.
 */
void NmHsWidget::scrollListToStart()
{
    NM_FUNCTION;
    QModelIndex index = (mListModel->item(0,0))->index();
    //next line will fail if item recycling is enabled!!
    QPointF pos = (mListView->itemByIndex(index))->pos();
    if(mListView->contentWidget()->pos() != pos){
        //dont move if already there. Otherwise new scrolling events are emitted
        mListView->scrollContentsTo(pos, KListScrollUpTime);
    }
    mListActivityTimer->stop(); //stop the timer
}

/*!
 onEngineException (NmHsWidgetEmailEngineExceptionCode exc)
 signals widget to be finalized
 /param exc exception type
 */
void NmHsWidget::onEngineException(const int& exc)
{
    NM_FUNCTION;
    switch (exc) {
        case (NmEngineExcAccountDeleted):
            emit finished(); //succesful ending
            break;
        case (NmEngineExcFailure):
            emit error(); //failure
            break;
        default:
            break;
    }
}

/*!
    toggleExpansion
*/
void NmHsWidget::toggleExpansion(const HbEffect::EffectStatus &status)
    {
    NM_FUNCTION;

    Q_UNUSED(status);
    
    toggleExpansionState();
    }


/*
 * NmHsWidget::event()
 */
bool NmHsWidget::event( QEvent *event )
{
    NM_FUNCTION;
    QEvent::Type eventType = event->type();
    if( eventType == HbEvent::ThemeChanged ){
        QColor newFontColor;
        newFontColor = HbColorScheme::color("qtc_hs_list_item_content_normal");
        mNoMailsLabel->setTextColor(newFontColor);
        return true;
    }
    return HbWidget::event(event);
}