diff -r dcf0eedfc1a3 -r d189ee25cf9d emailuis/nmailui/src/nmmessagesearchlistview.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/emailuis/nmailui/src/nmmessagesearchlistview.cpp Tue Aug 31 15:04:17 2010 +0300 @@ -0,0 +1,736 @@ +/* +* 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: +* +*/ + +static const char *NMUI_MESSAGE_SEARCH_LIST_VIEW_XML = ":/docml/nmmessagesearchlistview.docml"; +static const char *NMUI_MESSAGE_SEARCH_LIST_VIEW = "NmMessageSearchListView"; +static const char *NMUI_MESSAGE_SEARCH_LIST_TREE_LIST = "MessageTreeList"; +static const char *NMUI_MESSAGE_SEARCH_LIST_NO_MESSAGES = "MessageListNoMessages"; +static const char *NMUI_MESSAGE_SEARCH_LIST_INFO_LABEL = "LabelGroupBox"; +static const char *NMUI_MESSAGE_SEARCH_LIST_LINE_EDIT = "LineEdit"; +static const char *NMUI_MESSAGE_SEARCH_LIST_PUSH_BUTTON = "PushButton"; + + +#include "nmuiheaders.h" + + +/*! + \class NmMessageSearchListView + \brief The view for searching messages. +*/ + + +/*! + Class constructor. +*/ +NmMessageSearchListView::NmMessageSearchListView( + NmApplication &application, + NmUiStartParam* startParam, + NmUiEngine &uiEngine, + NmMessageListModel &msgListModel, + HbDocumentLoader *documentLoader, + QGraphicsItem *parent) +: NmBaseView(startParam, application, parent), + mApplication(application), + mUiEngine(uiEngine), + mMsgListModel(msgListModel), + mDocumentLoader(documentLoader), + mItemContextMenu(NULL), + mMessageListWidget(NULL), + mInfoLabel(NULL), + mNoMessagesLabel(NULL), + mLineEdit(NULL), + mPushButton(NULL), + mLongPressedItem(NULL), + mViewReady(false), + mSearchInProgress(false) +{ + NM_FUNCTION; + + loadViewLayout(); + initTreeView(); +} + + +/*! + Class destructor. +*/ +NmMessageSearchListView::~NmMessageSearchListView() +{ + NM_FUNCTION; + + delete mDocumentLoader; + + mWidgetList.clear(); + + if (mItemContextMenu){ + mItemContextMenu->clearActions(); + } + + delete mItemContextMenu; +} + + +/*! + From NmBaseView. + + Returns the view ID. + + \return The view ID. +*/ +NmUiViewId NmMessageSearchListView::nmailViewId() const +{ + NM_FUNCTION; + + return NmUiViewMessageSearchList; +} + + +/*! + From NmBaseView. + + Does the lazy loading after the view layout has been loaded. +*/ +void NmMessageSearchListView::viewReady() +{ + NM_FUNCTION; + + if (!mViewReady){ + // Set the mailbox name to the title pane. + setViewTitle(); + + // Refresh the list. + QMetaObject::invokeMethod(this, "refreshList", Qt::QueuedConnection); + + // Highlight the search input. + setSearchInputMode(NmHighlightedMode); + + mViewReady = true; + } +} + + +/*! + From NmMenuObserver. + + Handles action commands which are usually given by the user by selecting + menu items. + + \param actionResponse The command details. +*/ +void NmMessageSearchListView::handleActionCommand(NmActionResponse &actionResponse) +{ + NM_FUNCTION; + + // Handle options menu commands here. + if (actionResponse.menuType() == NmActionOptionsMenu) { + switch (actionResponse.responseCommand()) { + case NmActionResponseCommandUpdateMailboxName: { + setViewTitle(); + break; + } + case NmActionResponseCommandMailboxDeleted: { + mApplication.prepareForPopView(); + break; + } + default: { + break; + } + } + } + // Handle context menu commands here. + if (actionResponse.menuType() == NmActionContextMenu) { + if (mLongPressedItem){ + NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageViewer, + mStartParam->mailboxId(), mLongPressedItem->envelope().folderId(), + mLongPressedItem->envelope().messageId()); + + mApplication.enterNmUiView(startParam); + mLongPressedItem = NULL; + } + } +} + + +/*! + From NmBaseView. + + Loads the view layout from the XML file. +*/ +void NmMessageSearchListView::loadViewLayout() +{ + NM_FUNCTION; + + // Use the document loader to load the view layout. + bool ok(false); + setObjectName(QString(NMUI_MESSAGE_SEARCH_LIST_VIEW)); + QObjectList objectList; + objectList.append(this); + + // Pass the view to the document loader. Instead of creating a new view, the + // document loader uses this view when the docml file is parsed. + if (mDocumentLoader) { + mDocumentLoader->setObjectTree(objectList); + mWidgetList = mDocumentLoader->load(NMUI_MESSAGE_SEARCH_LIST_VIEW_XML, &ok); + } + + if (ok) { + // Load the search panel (contains the line edit and the push button + // widgets. + mLineEdit = qobject_cast( + mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_LINE_EDIT)); + + if (mLineEdit) { + connect(mLineEdit, SIGNAL(textChanged(QString)), + this, SLOT(criteriaChanged(QString))); + } + + mPushButton = qobject_cast( + mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_PUSH_BUTTON)); + + if (mPushButton) { + // button is disabled when line edit is empty + mPushButton->setEnabled(false); + + // The push button both starts and stops the search. + connect(mPushButton, SIGNAL(clicked()), this, SLOT(toggleSearch())); + mPushButton->setIcon(HbIcon("qtg_mono_search")); + } + + // Load the info label. + mInfoLabel = qobject_cast( + mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_INFO_LABEL)); + + if (mInfoLabel) { + NM_COMMENT("NmMessageSearchListView: info label loaded"); + + // If the heading is empty, the widget will not be shown which in + // turn would ruin the layout. + mInfoLabel->setHeading(" "); + } + + // Get the message list widget. + mMessageListWidget = qobject_cast( + mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_TREE_LIST)); + + if (mMessageListWidget) { + NM_COMMENT("NmMessageSearchListView: message list widget loaded"); + + // Set the item prototype. + mMessageListWidget->setItemPrototype(new NmMessageListViewItem()); + + // Set the list widget properties. + mMessageListWidget->setItemRecycling(true); + mMessageListWidget->contentWidget()->setProperty("indentation", 0); + mMessageListWidget->setScrollDirections(Qt::Vertical); + mMessageListWidget->setClampingStyle(HbScrollArea::BounceBackClamping); + mMessageListWidget->setFrictionEnabled(true); + mMessageListWidget->setItemPixmapCacheEnabled(true); + + // We want the search results to appear one by one. + mMessageListWidget->setEnabledAnimations(HbAbstractItemView::Appear & + HbAbstractItemView::Expand); + } + + // Load the no messages label. + mNoMessagesLabel = qobject_cast( + mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_NO_MESSAGES)); + + if (mNoMessagesLabel) { + NMLOG("NmMessageSearchListView: No messages label loaded."); + mNoMessagesLabel->hide(); + } + } + else { + NM_ERROR(1, "NmMessageSearchListView: failed to load widgets from XML"); + } +} + + +/*! + Initializes the tree view. +*/ +void NmMessageSearchListView::initTreeView() +{ + NM_FUNCTION; + + // Get the mailbox widget pointer and set the parameters. + if (mMessageListWidget) { + connect(mMessageListWidget, SIGNAL(activated(const QModelIndex &)), + this, SLOT(itemActivated(const QModelIndex &))); + + connect(mMessageListWidget, SIGNAL(longPressed(HbAbstractViewItem*, QPointF)), + this, SLOT(showItemContextMenu(HbAbstractViewItem*, QPointF))); + + mMessageListWidget->setFocus(); + mItemContextMenu = new HbMenu(); + } + + // Clear the previous content if any. + mMsgListModel.clear(); +} + + +/*! + Sets the title according to the name of the current mailbox. +*/ +void NmMessageSearchListView::setViewTitle() +{ + NM_FUNCTION; + + if (mStartParam){ + NmMailboxMetaData *meta = mUiEngine.mailboxById(mStartParam->mailboxId()); + + if (meta){ + setTitle(meta->name()); + } + } +} + + +/*! + Toggles the visiblity between the message list widget and the "no messages" + label. + + \param visible If true, will display the "no messages" label. If false, + will display the message list widget. +*/ +void NmMessageSearchListView::noMessagesLabelVisibility(bool visible) +{ + NM_FUNCTION; + + if (visible) { + // Hide the message list widget and display the "no messages" label. + if (mMessageListWidget) { + mMessageListWidget->hide(); + } + + if (mNoMessagesLabel && !mNoMessagesLabel->isVisible()) { + mNoMessagesLabel->show(); + } + } + else { + // Hide the "no messages" label and display the message list widget. + if (mNoMessagesLabel && mNoMessagesLabel->isVisible()) { + mNoMessagesLabel->hide(); + } + + if (mMessageListWidget) { + mMessageListWidget->show(); + } + } +} + + +/*! + Updates the search result count information. If the message list does not + contain any items, a "no messages" label is displayed. Otherwise the result + count in the information label is updated according to the number of + messages in the list. +*/ +void NmMessageSearchListView::updateSearchResultCountInfo() +{ + NM_FUNCTION; + + const int resultCount = mMsgListModel.rowCount(); + + if (resultCount) { + if (mInfoLabel) { + // Display the result count on the info label. + QString resultsString(hbTrId("txt_mail_list_search_results",resultCount)); + mInfoLabel->setHeading(resultsString); + + if (!mInfoLabel->isVisible()) { + mInfoLabel->show(); + } + } + } + else { + // No search results! + if (mInfoLabel && mInfoLabel->isVisible()) { + mInfoLabel->hide(); + } + + // Display the "no messages" label and highlight the search term. + noMessagesLabelVisibility(true); + } + +} + + +/*! + Sets the mode for the search input. + + \param mode The mode to set. +*/ +void NmMessageSearchListView::setSearchInputMode(NmSearchInputMode mode) +{ + NM_FUNCTION; + + if (!mLineEdit) { + // No line edit widget! + return; + } + + switch (mode) { + case NmNormalMode: { + mLineEdit->setEnabled(true); + break; + } + case NmHighlightedMode: { + mLineEdit->setEnabled(true); + mLineEdit->setFocus(); + break; + } + case NmDimmedMode: { + mLineEdit->setEnabled(false); + break; + } + } +} + + +/*! + From NmBaseView. + + Reloads the view contents with new start parameters. This method is + typically used when the view is already open and an external view activation + occurs for this same view. +*/ +void NmMessageSearchListView::reloadViewContents(NmUiStartParam *startParam) +{ + NM_FUNCTION; + + if (startParam && startParam->viewId() == NmUiViewMessageSearchList) { + // Delete the existing start parameter data. + delete mStartParam; + mStartParam = NULL; + + // Store the new start parameter data. + mStartParam = startParam; + + // Update the model with new parameters. + mUiEngine.messageListModelForSearch(startParam->mailboxId()); + refreshList(); + + // Refresh the mailbox name. + setViewTitle(); + } + else { + // Invalid start parameter data! Unused start parameter needs to be + // deleted. + NM_ERROR(1, "NmMessageSearchListView: invalid message list start parameter"); + delete startParam; + } +} + + +/*! + Called when text is changed in the edit field. If there is no search term + in the edit field, the search button is dimmed and disabled. If the field + contains text, the button can be clicked. + + \param text The text in the field after the modification. +*/ +void NmMessageSearchListView::criteriaChanged(QString text) +{ + NM_FUNCTION; + NM_COMMENT(QString("NmMessageSearchListView::criteriaChanged %1").arg(text)); + + // Check if the button should be disabled/enabled. + bool enabled = mPushButton->isEnabled(); + + if (!enabled && !text.isEmpty()) { + mPushButton->setEnabled(true); + } + else if (enabled && text.isEmpty()) { + mPushButton->setEnabled(false); + } +} + + +/*! + Displays the item context menu. This method gets called if an item on the + list is long pressed. +*/ +void NmMessageSearchListView::showItemContextMenu( + HbAbstractViewItem *listViewItem, const QPointF &coords) +{ + NM_FUNCTION; + + // Store long press item for later use with response. + mLongPressedItem = + mMsgListModel.data(listViewItem->modelIndex(), + Qt::DisplayRole).value(); + + if (mItemContextMenu && mLongPressedItem && + mLongPressedItem->itemType() == NmMessageListModelItem::NmMessageItemMessage) { + // Clear the previous menu actions. + mItemContextMenu->clearActions(); + NmUiExtensionManager &extMngr = mApplication.extManager(); + QList list; + + // Fetch the menu actions based on the selected item. + NmMessageEnvelope *envelope = mLongPressedItem->envelopePtr(); + + if (envelope){ + NmActionRequest request(this, NmActionContextMenu, + NmActionContextViewMessageSearchList, NmActionContextDataMessage, + mStartParam->mailboxId(), envelope->folderId(), + envelope->messageId(), QVariant::fromValue(envelope)); + + extMngr.getActions(request, list); + } + + for (int i(0); i < list.count(); ++i) { + mItemContextMenu->addAction(list[i]); + } + + mItemContextMenu->setPreferredPos(coords); + mItemContextMenu->open(); + } +} + + +/*! + Stores the given index and forwards the call to handleSelection(). This + method gets called when an item on the list is selected. + + \param index The index of the activated item. +*/ +void NmMessageSearchListView::itemActivated(const QModelIndex &index) +{ + NM_FUNCTION; + + mActivatedIndex = index; + QMetaObject::invokeMethod(this, "handleSelection", Qt::QueuedConnection); +} + + +/*! + If the selected item is a message, will open the message. +*/ +void NmMessageSearchListView::handleSelection() +{ + NM_FUNCTION; + + // Do expand/collapse for title divider items + NmMessageListModelItem* modelItem = mMsgListModel.data( + mActivatedIndex, Qt::DisplayRole).value(); + + if (modelItem && + modelItem->itemType() == NmMessageListModelItem::NmMessageItemMessage) + { + if (mSearchInProgress) { + // Stop the search. + toggleSearch(); + } + + // Open the message. + NmUiStartParam *startParam = new NmUiStartParam(NmUiViewMessageViewer, + mStartParam->mailboxId(), modelItem->envelope().folderId(), + modelItem->envelope().messageId()); + + mApplication.enterNmUiView(startParam); + } +} + + +/*! + Displays the message list widtet if not visible and scrolls to the + appropriate point on the list. +*/ +void NmMessageSearchListView::itemsAdded(const QModelIndex &parent, int start, int end) +{ + NM_FUNCTION; + + Q_UNUSED(parent); + Q_UNUSED(end); + + // The search is an asynchronous operation. If a user stops the search, it + // might take a short while before the search is actually stopped and during + // this time it is possible that messages matching the search are added. + // Therefore, update the result count info if items are added after the + // search has been stopped by the user. + if (!mSearchInProgress) { + updateSearchResultCountInfo(); + } + + if (!start && mMessageListWidget) { + QList items = mMessageListWidget->visibleItems(); + + if (items.count()) { + QModelIndex index = items.at(0)->modelIndex(); + + while (index.row() > 0) { + QModelIndex previous = + mMessageListWidget->modelIterator()->previousIndex(index); + + if (previous.isValid()) { + mMessageListWidget->scrollTo(previous); + } + + index = previous; + } + } + } +} + + +/*! + This method gets called when an item is removed from the list. If the + search has completed (or stopped), the search result count information is + updated according to the number of messages in the list. +*/ +void NmMessageSearchListView::itemsRemoved() +{ + NM_FUNCTION; + + if (!mSearchInProgress) { + updateSearchResultCountInfo(); + } +} + + +/*! + Refreshes the search list. +*/ +void NmMessageSearchListView::refreshList() +{ + NM_FUNCTION; + + if (mMessageListWidget) { + // Set the model. + mMessageListWidget->setModel( + static_cast(&mMsgListModel)); + + // Connect the signals. + connect(&mMsgListModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), + this, SLOT(itemsAdded(const QModelIndex&, int, int)), Qt::UniqueConnection); + + connect(&mMsgListModel, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), + this, SLOT(itemsRemoved()), Qt::UniqueConnection); + + connect(&mMsgListModel, SIGNAL(setNewParam(NmUiStartParam*)), + this, SLOT(reloadViewContents(NmUiStartParam*)), Qt::UniqueConnection); + } + + // The info label cannot be hidden when constructed because doing so would + // ruin the layout (for example the line edit widget's width would be too + // short in lanscape). + if (mInfoLabel) { + mInfoLabel->hide(); + } +} + + +/*! + Starts and stops the search according to the current status. + + Starts search: Uses the input given by the user as a search string and + starts an asynchronous search. Any previous search results are removed from + the search list. + + Stops search: Sets the number of search results into the info label. +*/ +void NmMessageSearchListView::toggleSearch() +{ + NM_FUNCTION; + + if (mSearchInProgress) { + // Search is in progress - do cancel. + mUiEngine.cancelSearch(mStartParam->mailboxId()); + handleSearchComplete(); + } + else { + // Do start the search. + mSearchInProgress = true; + + // Clear previous results if any. + mMsgListModel.clear(); + + connect(&mUiEngine, SIGNAL(searchComplete()), + this, SLOT(handleSearchComplete()), Qt::UniqueConnection); + + // Get the search input and start the search. + QStringList searchStrings; + + if (mLineEdit) { + searchStrings.append(mLineEdit->text()); + } + + mUiEngine.search(mStartParam->mailboxId(), searchStrings); + + // Hide the virtual keyboard + QInputContext *ic = qApp->inputContext(); + if (ic) { + QEvent *closeEvent = new QEvent(QEvent::CloseSoftwareInputPanel); + ic->filterEvent(closeEvent); + delete closeEvent; + } + + // Hide the "no messages" label if visible and dim the search input. + noMessagesLabelVisibility(false); + setSearchInputMode(NmDimmedMode); + + // Display the info label. + if (mInfoLabel) { + mInfoLabel->setHeading(hbTrId("txt_mail_list_searching")); + mInfoLabel->show(); + } + + // Change the push button text. + if (mPushButton) { + mPushButton->setIcon(HbIcon("qtg_mono_search_stop")); + } + } +} + + +/*! + If the search matched any messages, displays the search result count in the + info label. If no messages were found, the method displays the "no messages" + label. In either case, the search panel is updated. +*/ +void NmMessageSearchListView::handleSearchComplete() +{ + NM_FUNCTION; + + mSearchInProgress = false; + + // Change the push button text. + if (mPushButton) { + mPushButton->setIcon(HbIcon("qtg_mono_search")); + } + + // Display the search result count. + updateSearchResultCountInfo(); + + const int resultCount = mMsgListModel.rowCount(); + + if (resultCount) { + // Undim the search input. + setSearchInputMode(NmNormalMode); + } + else { + // Highlight the search field. + noMessagesLabelVisibility(true); + setSearchInputMode(NmHighlightedMode); + } +} + + +// End of file.