emailuis/nmailui/src/nmmessagesearchlistview.cpp
branchRCL_3
changeset 63 d189ee25cf9d
--- /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<HbLineEdit *>(
+            mDocumentLoader->findWidget(NMUI_MESSAGE_SEARCH_LIST_LINE_EDIT));
+
+        if (mLineEdit) {
+            connect(mLineEdit, SIGNAL(textChanged(QString)), 
+                    this, SLOT(criteriaChanged(QString)));
+        }
+    
+        mPushButton = qobject_cast<HbPushButton *>(
+        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<HbGroupBox *>(
+            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<HbTreeView *>(
+            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<HbLabel *>(
+            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<NmMessageListModelItem*>();
+
+    if (mItemContextMenu && mLongPressedItem &&
+        mLongPressedItem->itemType() == NmMessageListModelItem::NmMessageItemMessage) {
+        // Clear the previous menu actions.
+        mItemContextMenu->clearActions();
+        NmUiExtensionManager &extMngr = mApplication.extManager();
+        QList<NmAction*> 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<NmMessageListModelItem*>();
+
+    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<HbAbstractViewItem*> 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<QStandardItemModel*>(&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.