/*
* 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 <QGraphicsLinearLayout>
#include <hbcolorscheme.h>
#include <hbdocumentloader.h>
#include <QTranslator>
#include <hbframedrawer.h>
#include <hbframeitem.h>
#include <hblabel.h>
#include "nmcommon.h"
#include "nmhswidget.h"
#include "nmhswidgetemailengine.h"
#include "nmmessageenvelope.h"
#include "nmhswidgettitlerow.h"
#include "nmhswidgetemailrow.h"
#include "nmhswidgetconsts.h"
#include "nmhswidgetdatetimeobserver.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)
{
NM_FUNCTION;
}
/*!
Destructor
*/
NmHsWidget::~NmHsWidget()
{
NM_FUNCTION;
delete mTranslator;
mTranslator = NULL;
delete mEngine;
mEngine = NULL;
delete mBackgroundFrameDrawer;
mBackgroundFrameDrawer = NULL;
delete mDateObserver;
mDateObserver = 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));
if (!mMainContainer || !mWidgetContainer || !mContentContainer
|| !mEmptySpaceContainer || !mNoMailsLabel ) {
//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();
mTranslator->load(KNmHsWidgetLocFileName + lang, 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;
mWidgetContainer->setBackgroundItem(backgroundLayoutItem);
}
/*!
Initializes the widget.
called by home screen fw when widget is added to home screen
*/
void NmHsWidget::onInitialize()
{
NM_FUNCTION;
QT_TRY {
// 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();
//Engine construction is 2 phased.
mEngine = new NmHsWidgetEmailEngine(mAccountId);
//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;
}
mTitleRow->updateAccountName(mEngine->accountName());
//create observer for date/time change events
mDateObserver = new NmHsWidgetDateTimeObserver();
//Crete MailRows and associated connections
createMailRowsList();
updateMailData();
mTitleRow->updateUnreadCount(mEngine->unreadCount());
mTitleRow->setAccountIcon(mAccountIconName);
mTitleRow->setExpandCollapseIcon(mIsExpanded);
//Get signals about changes in mail data
connect(mEngine, SIGNAL( mailDataChanged() ), this, SLOT( updateMailData() ));
//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
connect(mTitleRow, SIGNAL( mailboxLaunchTriggered() )
,mEngine, SLOT( launchMailAppInboxView() ) );
connect(mTitleRow, SIGNAL( expandCollapseButtonPressed() )
,this, SLOT( handleExpandCollapseEvent() ) );
setMinimumSize(mTitleRow->minimumWidth(),
mEmptySpaceContainer->minimumHeight() + mTitleRow->minimumHeight());
}
QT_CATCH(...) {
NM_ERROR(1,"NmHsWidget::onInitialize fail @ catch");
emit error();
}
}
/*!
updateMailData slot
*/
void NmHsWidget::updateMailData()
{
NM_FUNCTION;
QList<NmMessageEnvelope> envelopes;
int count = 0;
if (mIsExpanded) {
count = mEngine->getEnvelopes(envelopes, KMaxNumberOfMailsShown);
}
updateLayout(count);
//count is safe for envelopes and mMailRows
for (int i = 0; i < count; i++) {
mMailRows.at(i)->updateMailData(envelopes.at(i));
}
}
/*!
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;
toggleExpansionState();
}
/*!
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
updateMailData();
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 mMailRows list to include KMaxNumberOfMailsShown mail row widgets
/post mMailRows contains KMaxNumberOfMailsShown mailRows
*/
void NmHsWidget::createMailRowsList()
{
NM_FUNCTION;
//make sure that there are as many email rows as needed
while (mMailRows.count() < KMaxNumberOfMailsShown) {
NmHsWidgetEmailRow *row = new NmHsWidgetEmailRow(this);
if (!row->setupUI()) {
NM_ERROR(1, "NmHsWidget::createMailRowsList row->setUpUI() fails");
//if setupUI fails no point to proceed
emit error();
return;
}
connect(row, SIGNAL(mailViewerLaunchTriggered(const NmId&)), mEngine,
SLOT(launchMailAppMailViewer(const NmId&)));
connect(mDateObserver, SIGNAL(dateTimeChanged()), row, SLOT(updateDateTime()));
mMailRows.append(row);
}
}
/*!
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(const int mailCount)
{
NM_FUNCTION;
if (mIsExpanded) {
//set container height to content height
qreal contentHeight = KMaxNumberOfMailsShown
* mMailRows.first()->maximumHeight();
mContentContainer->setMaximumHeight(contentHeight);
mContentContainer->setVisible(true);
if (mailCount == 0) {
addNoMailsLabelToLayout();
removeEmailRowsFromLayout();
}
else {
removeNoMailsLabelFromLayout();
addEmailRowsToLayout();
}
}
else {
removeNoMailsLabelFromLayout();
removeEmailRowsFromLayout();
mContentContainer->setVisible(false);
mContentContainer->setMaximumHeight(0);
}
//resize the widget to new layout size
qreal totalHeight = mEmptySpaceContainer->preferredHeight() + mTitleRow->containerHeight() + mContentContainer->maximumHeight();
//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
resize(mTitleRow->maximumWidth(), totalHeight);
mMainContainer->resize(mTitleRow->maximumWidth(), totalHeight);
mWidgetContainer->resize(mTitleRow->maximumWidth(), totalHeight - mEmptySpaceContainer->preferredHeight());
updateMailRowsVisibility(mailCount);
}
/*!
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() || mMailRows.isEmpty()) {
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 every emailrow to the layout
/post all elements in mMailRows are added to mContentLayout
*/
void NmHsWidget::addEmailRowsToLayout()
{
NM_FUNCTION;
foreach(NmHsWidgetEmailRow *row, mMailRows)
{
mContentLayout->addItem(row);
}
}
/*!
removeEmailRowsFromLayout removes every emailrow from the layout
/post none of the elements in mMailRows are in mContentLayout
*/
void NmHsWidget::removeEmailRowsFromLayout()
{
NM_FUNCTION;
foreach(NmHsWidgetEmailRow *row, mMailRows)
{
mContentLayout->removeItem(row);
}
}
/*!
Updates mail row visibilities in static widget
/param visibleCount defines how many items do have mail data
/post all row items having mail data are visible, other rows are hidden
*/
void NmHsWidget::updateMailRowsVisibility(const int visibleCount)
{
NM_FUNCTION;
// set visible as many rows as requested by visibleCount param
bool isVisible;
for (int i = 0; i < mMailRows.count(); i++) {
isVisible = false;
if ((mIsExpanded) && (i < visibleCount)) {
isVisible = true;
}
mMailRows.at(i)->setVisible(isVisible);
}
}
/*!
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;
}
}