summaryrefslogtreecommitdiffstats
path: root/examples/qtmail/app/messagelistview.cpp
diff options
context:
space:
mode:
authorAleksW <qt-info@nokia.com>2010-04-07 13:20:43 +1000
committerAleksW <qt-info@nokia.com>2010-04-07 13:38:15 +1000
commit54405d2fc04be12680e840bb9c697d37fa6910d8 (patch)
treedd66587ee73149fd578739b5a5cb423f1f751061 /examples/qtmail/app/messagelistview.cpp
parentb7277a3cd70ac143a5df80326a2906215b4291c5 (diff)
Restructure source layout grouping qtmail example files together, separate from QMF.
Diffstat (limited to 'examples/qtmail/app/messagelistview.cpp')
-rw-r--r--examples/qtmail/app/messagelistview.cpp1058
1 files changed, 1058 insertions, 0 deletions
diff --git a/examples/qtmail/app/messagelistview.cpp b/examples/qtmail/app/messagelistview.cpp
new file mode 100644
index 00000000..869e0e95
--- /dev/null
+++ b/examples/qtmail/app/messagelistview.cpp
@@ -0,0 +1,1058 @@
+/****************************************************************************
+**
+** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Messaging Framework.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** No Commercial Usage
+** This file contains pre-release code and may not be distributed.
+** You may use this file in accordance with the terms and conditions
+** contained in the either Technology Preview License Agreement or the
+** Beta Release License Agreement.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain
+** additional rights. These rights are described in the Nokia Qt LGPL
+** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
+** package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+** If you are unsure which license is appropriate for your use, please
+** contact the sales department at qt-sales@nokia.com.
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "messagelistview.h"
+#include <qmaillog.h>
+#include <qmailfolder.h>
+#include <qmailfolderkey.h>
+#include <qmailmessagethreadedmodel.h>
+#include <qmailmessagelistmodel.h>
+#include <QTreeView>
+#include <QKeyEvent>
+#include <QLayout>
+#include <QLabel>
+#include <QMap>
+#include <QModelIndex>
+#include <QPushButton>
+#include <QTabBar>
+#include <QToolButton>
+#include <QVBoxLayout>
+#include <QHeaderView>
+#include <QItemDelegate>
+#include <QPainter>
+#include <QComboBox>
+#include <QLineEdit>
+#include <QApplication>
+#include <qmaildatacomparator.h>
+#include <qtmailnamespace.h>
+
+static QStringList headers(QStringList() << "Subject" << "Sender" << "Date" << "Size");
+static const QColor newMessageColor(Qt::blue);
+
+class QuickSearchWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ QuickSearchWidget(QWidget* parent = 0);
+
+ QMailMessageKey searchKey() const;
+
+public slots:
+ void reset(bool emitReset = true);
+
+signals:
+ void quickSearchRequested(const QMailMessageKey& key);
+ void fullSearchRequested();
+
+private slots:
+ void searchTermsChanged();
+
+private:
+ QMailMessageKey buildSearchKey() const;
+
+private:
+ QLineEdit* m_searchTerms;
+ QComboBox* m_statusCombo;
+ QToolButton* m_fullSearchButton;
+ QToolButton* m_clearButton;
+ QMailMessageKey m_key;
+};
+
+QuickSearchWidget::QuickSearchWidget(QWidget* parent)
+:
+QWidget(parent)
+{
+ QHBoxLayout* layout = new QHBoxLayout(this);
+
+ m_clearButton = new QToolButton(this);
+ m_clearButton->setIcon(Qtmail::icon("clear"));
+ connect(m_clearButton,SIGNAL(clicked()),this,SLOT(reset()));
+ layout->addWidget(m_clearButton);
+
+ layout->addWidget(new QLabel("Search:"));
+ m_searchTerms = new QLineEdit(this);
+ connect(m_searchTerms,SIGNAL(textChanged(QString)),this,SLOT(searchTermsChanged()));
+ layout->addWidget(m_searchTerms);
+
+ layout->addWidget(new QLabel("Status:"));
+
+ m_statusCombo = new QComboBox(this);
+ QMailMessageKey removed(QMailMessageKey::status(QMailMessage::Removed));
+ m_statusCombo->addItem(QIcon(":icon/exec"),tr("Any Status"),~removed);
+ m_statusCombo->addItem(QIcon(":icon/mail_generic"),tr("Unread"),QMailMessageKey::status(QMailMessage::Read,QMailDataComparator::Excludes)&~removed);
+ m_statusCombo->addItem(QIcon(":icon/mail_generic"),tr("Important"),QMailMessageKey::status(QMailMessage::Important)&~removed);
+ m_statusCombo->addItem(QIcon(":icon/new"),tr("New"),QMailMessageKey::status(QMailMessage::New)&~removed);
+ m_statusCombo->addItem(QIcon(":icon/mail_reply"),tr("Replied"),QMailMessageKey::status(QMailMessage::Replied)&~removed);
+ m_statusCombo->addItem(QIcon(":icon/mail_forward"),tr("Forwarded"),QMailMessageKey::status(QMailMessage::Forwarded)&~removed);
+ m_statusCombo->addItem(QIcon(":/icon/attach"),tr("Has Attachment"),QMailMessageKey::status(QMailMessage::HasAttachments)&~removed);
+ m_statusCombo->addItem(QIcon(":icon/exec"),tr("Removed"), removed);
+ connect(m_statusCombo,SIGNAL(currentIndexChanged(int)),this,SLOT(searchTermsChanged()));
+ layout->addWidget(m_statusCombo);
+
+ m_fullSearchButton = new QToolButton(this);
+ m_fullSearchButton->setIcon(Qtmail::icon("search"));
+ connect(m_fullSearchButton,SIGNAL(clicked(bool)),this,SIGNAL(fullSearchRequested()));
+ layout->addWidget(m_fullSearchButton);
+
+ m_key = buildSearchKey();
+}
+
+QMailMessageKey QuickSearchWidget::searchKey() const
+{
+ return m_key;
+}
+
+void QuickSearchWidget::reset(bool emitReset)
+{
+ blockSignals(!emitReset);
+ m_statusCombo->setCurrentIndex(0);
+ m_searchTerms->clear();
+ blockSignals(false);
+}
+
+void QuickSearchWidget::searchTermsChanged()
+{
+ m_key = buildSearchKey();
+ emit quickSearchRequested(m_key);
+}
+
+QMailMessageKey QuickSearchWidget::buildSearchKey() const
+{
+ QMailMessageKey statusKey = qvariant_cast<QMailMessageKey>(m_statusCombo->itemData(m_statusCombo->currentIndex()));
+ if(m_searchTerms->text().isEmpty() && m_statusCombo->currentIndex() == 0)
+ return statusKey;
+
+ QMailMessageKey subjectKey = QMailMessageKey::subject(m_searchTerms->text(),QMailDataComparator::Includes);
+ QMailMessageKey senderKey = QMailMessageKey::sender(m_searchTerms->text(),QMailDataComparator::Includes);
+ return ((subjectKey | senderKey) & statusKey);
+}
+
+
+template <typename BaseModel>
+class MessageListModel : public BaseModel
+{
+ // Doesn't work in template classes...
+ //Q_OBJECT
+
+ typedef BaseModel SuperType;
+
+public:
+ MessageListModel(MessageListView* parent);
+
+ QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
+
+ int columnCount(const QModelIndex & parent = QModelIndex()) const;
+ int rowCount(const QModelIndex& parent = QModelIndex()) const;
+
+ QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
+
+private:
+ MessageListView *m_parent;
+};
+
+template <typename BaseModel>
+MessageListModel<BaseModel>::MessageListModel(MessageListView *parent)
+ : BaseModel(parent),
+ m_parent(parent)
+{
+}
+
+template <typename BaseModel>
+QVariant MessageListModel<BaseModel>::headerData(int section, Qt::Orientation o, int role) const
+{
+ if (role == Qt::DisplayRole) {
+ if (section < headers.count())
+ return headers.at(section);
+ }
+
+ return SuperType::headerData(section, o, role);
+}
+
+template <typename BaseModel>
+int MessageListModel<BaseModel>::columnCount(const QModelIndex & parent) const
+{
+ return headers.count();
+
+ Q_UNUSED(parent);
+}
+
+template <typename BaseModel>
+int MessageListModel<BaseModel>::rowCount(const QModelIndex &parentIndex) const
+{
+ int actualRows = SuperType::rowCount(parentIndex);
+
+ if (!parentIndex.isValid() && (actualRows > 0) && m_parent->moreButtonVisible())
+ actualRows++;
+
+ return actualRows;
+}
+
+static QString dateString(const QDateTime& dt)
+{
+ QDateTime current = QDateTime::currentDateTime();
+ //today
+ if(dt.date() == current.date())
+ return QString("Today %1").arg(dt.toString("h:mm:ss ap"));
+ //yesterday
+ else if(dt.daysTo(current) <= 1)
+ return QString("Yesterday %1").arg(dt.toString("h:mm:ss ap"));
+ //within 7 days
+ else if(dt.daysTo(current) < 7)
+ return dt.toString("dddd h:mm:ss ap");
+ else return dt.toString("dd/MM/yy h:mm:ss ap");
+}
+
+template <typename BaseModel>
+QVariant MessageListModel<BaseModel>::data(const QModelIndex & index, int role) const
+{
+ if (index.isValid()) {
+ if (role == Qt::DisplayRole && index.isValid()) {
+ switch(index.column())
+ {
+ case 0:
+ return SuperType::data(index, QMailMessageModelBase::MessageSubjectTextRole);
+ case 1:
+ return SuperType::data(index, QMailMessageModelBase::MessageAddressTextRole);
+ //case 2:
+ // return SuperType::data(index, QMailMessageModelBase::MessageTimeStampTextRole);
+ case 3:
+ return SuperType::data(index, QMailMessageModelBase::MessageSizeTextRole);
+ default:
+ break;
+ }
+
+ if (index.column() == 2) {
+ // Customize the date display
+ QMailMessageMetaData message(SuperType::idFromIndex(index));
+ if (message.id().isValid()) {
+ return dateString(message.date().toLocalTime());
+ }
+ }
+ } else if ((role == Qt::DecorationRole || role == Qt::CheckStateRole) && (index.column() > 0)) {
+ return QVariant();
+ } else if (role == Qt::CheckStateRole && !m_parent->markingMode()) {
+ Qt::CheckState state = static_cast<Qt::CheckState>(SuperType::data(index,role).toInt());
+ if (state == Qt::Unchecked)
+ return QVariant();
+ } else if (role == Qt::DecorationRole) {
+ return SuperType::data(index, QMailMessageModelBase::MessageStatusIconRole);
+ } else if (role == Qt::ForegroundRole) {
+ QMailMessageMetaData message(SuperType::idFromIndex(index));
+ if (message.id().isValid()) {
+ quint64 status = message.status();
+ bool unread = !(status & QMailMessage::Read);
+ if (status & QMailMessage::PartialContentAvailable && unread)
+ return newMessageColor;
+ }
+ }
+ }
+
+ return SuperType::data(index, role);
+}
+
+
+class MessageList : public QTreeView
+{
+ Q_OBJECT
+
+public:
+ MessageList(MessageListView *parent);
+ virtual ~MessageList();
+
+ QPoint clickPos() const { return pos; }
+
+signals:
+ void backPressed();
+ void scrolled();
+ void moreButtonClicked();
+
+protected:
+ void keyPressEvent(QKeyEvent* e);
+ void mouseReleaseEvent(QMouseEvent* e);
+ void scrollContentsBy(int dx, int dy);
+ void mouseMoveEvent(QMouseEvent* e);
+ void mousePressEvent(QMouseEvent* e);
+ bool mouseOverMoreLink(QMouseEvent* e);
+
+ void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
+
+protected slots:
+ void rowsInserted(const QModelIndex &parent, int start, int end);
+
+private:
+ MessageListView *m_parent;
+ QPoint pos;
+};
+
+MessageList::MessageList(MessageListView *parent)
+ : QTreeView(parent),
+ m_parent(parent)
+{
+ setMouseTracking(true);
+}
+
+MessageList::~MessageList()
+{
+}
+
+void MessageList::keyPressEvent(QKeyEvent* e)
+{
+ switch( e->key() ) {
+ case Qt::Key_Space:
+ case Qt::Key_Return:
+ case Qt::Key_Select:
+ case Qt::Key_Enter:
+ {
+ if(currentIndex().isValid())
+ emit clicked(currentIndex());
+ }
+ break;
+ case Qt::Key_No:
+ case Qt::Key_Back:
+ case Qt::Key_Backspace:
+ emit backPressed();
+ break;
+ case Qt::Key_Plus:
+ case Qt::Key_Minus:
+ {
+ QModelIndex index(currentIndex());
+ if (!index.isValid())
+ break;
+ if (e->key() == Qt::Key_Plus) {
+ index = indexBelow(index);
+ } else if (e->key() == Qt::Key_Minus) {
+ index = indexAbove(index);
+ }
+ while (index.isValid()) {
+ QMailMessageId id(index.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ if (!id.isValid())
+ break;
+ QMailMessageMetaData message(id);
+ quint64 status = message.status();
+ bool unread = !(status & QMailMessage::Read);
+ if (unread) {
+ setCurrentIndex(index);
+ scrollTo(index);
+ QApplication::postEvent(this, new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, 0));
+ QApplication::postEvent(this, new QKeyEvent(QEvent::KeyRelease, Qt::Key_Enter, 0));
+ break;
+ }
+ if (e->key() == Qt::Key_Plus) {
+ index = indexBelow(index);
+ } else if (e->key() == Qt::Key_Minus) {
+ index = indexAbove(index);
+ }
+ }
+ }
+ break;
+ default: QTreeView::keyPressEvent(e);
+ }
+}
+
+void MessageList::mouseReleaseEvent(QMouseEvent* e)
+{
+ pos = e->pos();
+ QTreeView::mouseReleaseEvent(e);
+}
+
+void MessageList::scrollContentsBy(int dx, int dy)
+{
+ QTreeView::scrollContentsBy(dx, dy);
+ emit scrolled();
+}
+
+void MessageList::mouseMoveEvent(QMouseEvent* e)
+{
+ if (mouseOverMoreLink(e)) {
+ QCursor handCursor(Qt::PointingHandCursor);
+ setCursor(handCursor);
+ } else if (cursor().shape() == Qt::PointingHandCursor) {
+ setCursor(QCursor());
+ }
+
+ QTreeView::mouseMoveEvent(e);
+}
+
+void MessageList::mousePressEvent(QMouseEvent* e)
+{
+ // TODO: Should be in release event?
+ if (mouseOverMoreLink(e)) {
+ emit moreButtonClicked();
+ }
+
+ QTreeView::mousePressEvent(e);
+}
+
+bool MessageList::mouseOverMoreLink(QMouseEvent* e)
+{
+ if (!m_parent->moreButtonVisible())
+ return false;
+
+ QModelIndex index = indexAt(e->pos());
+ if (index.isValid() && ((!index.parent().isValid()) && (index.row() == model()->rowCount(QModelIndex()) - 1))) {
+ QFont font;
+ font.setUnderline(true);
+ QFontMetrics fm(font);
+ QItemSelection fakeSelection(index.sibling(index.row(),0), index.sibling(index.row(),3));
+ QRegion r = visualRegionForSelection(fakeSelection);
+ QRect brect = r.boundingRect();
+ QRect textRect = fm.boundingRect(brect, Qt::AlignHCenter | Qt::AlignVCenter, tr("Get more messages"));
+ return textRect.contains(e->pos());
+ }
+
+ return false;
+}
+
+void MessageList::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
+{
+ bool isMoreEntry = (!index.parent().isValid() &&
+ m_parent->moreButtonVisible() &&
+ (index.row() == (model()->rowCount(QModelIndex()) - 1)));
+ if (isMoreEntry) {
+ QLinearGradient lg(option.rect.topLeft(), option.rect.topRight());
+ lg.setColorAt(0,option.palette.color(QPalette::Base));
+ lg.setColorAt(0.5,option.palette.color(QPalette::Button));
+ lg.setColorAt(1,option.palette.color(QPalette::Base));
+
+ QFont font = painter->font();
+ font.setUnderline(true);
+
+ painter->save();
+ painter->setFont(font);
+ painter->fillRect(option.rect, QBrush(lg));
+ painter->drawText(option.rect, Qt::AlignHCenter | Qt::AlignVCenter, tr("Get more messages"));
+ painter->restore();
+ } else {
+ QTreeView::drawRow(painter, option, index);
+ }
+}
+
+void MessageList::rowsInserted(const QModelIndex &parent, int start, int end)
+{
+ QTreeView::rowsInserted(parent, start, end);
+ for (int i = start; i < end; ++i) {
+ setExpanded(model()->index(i, 0, parent), true);
+ }
+}
+
+
+MessageListView::MessageListView(QWidget* parent)
+:
+ QWidget(parent),
+ mMessageList(new MessageList(this)),
+ mModel(0),
+ mMarkingMode(false),
+ mIgnoreWhenHidden(true),
+ mSelectedRowsRemoved(false),
+ mQuickSearchWidget(0),
+ mShowMoreButton(false),
+ mThreaded(true)
+{
+ init();
+ showQuickSearch(true);
+}
+
+MessageListView::~MessageListView()
+{
+}
+
+QMailMessageKey MessageListView::key() const
+{
+ return mModel->key();
+}
+
+void MessageListView::setKey(const QMailMessageKey& newKey)
+{
+ // Save the current index for the old key
+ QByteArray keyArray;
+ QDataStream keyStream(&keyArray, QIODevice::WriteOnly);
+ mKey.serialize<QDataStream>(keyStream);
+ mPreviousCurrentCache.insert(keyArray, new QMailMessageId(current()));
+
+ // Find the previous current index for the new key
+ QByteArray newKeyArray;
+ QDataStream newKeyStream(&newKeyArray, QIODevice::WriteOnly);
+ newKey.serialize<QDataStream>(newKeyStream);
+ if (mPreviousCurrentCache[newKeyArray]) {
+ mPreviousCurrent = *mPreviousCurrentCache[newKeyArray];
+ }
+
+ mModel->setKey(newKey & mQuickSearchWidget->searchKey());
+ mExpandAllTimer.start(0);
+ mScrollTimer.stop();
+ mKey = newKey;
+}
+
+QMailMessageSortKey MessageListView::sortKey() const
+{
+ return mModel->sortKey();
+}
+
+void MessageListView::setSortKey(const QMailMessageSortKey& sortKey)
+{
+ mModel->setSortKey(sortKey);
+}
+
+QMailFolderId MessageListView::folderId() const
+{
+ return mFolderId;
+}
+
+void MessageListView::setFolderId(const QMailFolderId& folderId)
+{
+ mFolderId = folderId;
+ setMoreButtonVisible(mFolderId.isValid());
+}
+
+void MessageListView::init()
+{
+ mPreviousCurrentCache.setMaxCost(10000); // Cache current item for 1,000 folders
+ connect(&mExpandAllTimer, SIGNAL(timeout()),
+ this, SLOT(expandAll()));
+
+ mQuickSearchWidget = new QuickSearchWidget(this);
+ connect(mQuickSearchWidget,SIGNAL(quickSearchRequested(QMailMessageKey)),this,SLOT(quickSearch(QMailMessageKey)));
+ connect(mQuickSearchWidget,SIGNAL(fullSearchRequested()),this,SIGNAL(fullSearchRequested()));
+
+ reset();
+
+ mMessageList->setObjectName("messagelistview");
+ mMessageList->setUniformRowHeights(true);
+ mMessageList->setAlternatingRowColors(true);
+ mMessageList->header()->setDefaultSectionSize(180);
+
+ connect(mMessageList, SIGNAL(clicked(QModelIndex)),
+ this, SLOT(indexClicked(QModelIndex)));
+ connect(mMessageList, SIGNAL(activated(QModelIndex)),
+ this, SLOT(indexActivated(QModelIndex)));
+ connect(mMessageList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)),
+ this, SLOT(currentIndexChanged(QModelIndex,QModelIndex)));
+ connect(mMessageList, SIGNAL(backPressed()),
+ this, SIGNAL(backPressed()));
+
+ connect(mMessageList, SIGNAL(moreButtonClicked()),
+ this, SIGNAL(moreClicked()));
+
+ connect(mMessageList, SIGNAL(scrolled()),
+ this, SLOT(reviewVisibleMessages()));
+
+ connect(mMessageList, SIGNAL(collapsed(QModelIndex)),
+ this, SIGNAL(rowCountChanged()));
+ connect(mMessageList, SIGNAL(expanded(QModelIndex)),
+ this, SIGNAL(rowCountChanged()));
+
+ mScrollTimer.setSingleShot(true);
+ connect(&mScrollTimer, SIGNAL(timeout()),
+ this, SLOT(scrollTimeout()));
+
+ QVBoxLayout* vLayout = new QVBoxLayout(this);
+ vLayout->setContentsMargins(0,0,0,0);
+ vLayout->setSpacing(0);
+ vLayout->addWidget(mQuickSearchWidget);
+ vLayout->addWidget(mMessageList);
+
+ setFocusProxy(mMessageList);
+}
+
+QMailMessageId MessageListView::current() const
+{
+ return mMessageList->currentIndex().data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>();
+}
+
+void MessageListView::setCurrent(const QMailMessageId& id)
+{
+ QModelIndex index = mModel->indexFromId(id);
+ if (index.isValid()) {
+ mMessageList->setCurrentIndex(index);
+ }
+}
+
+void MessageListView::setNextCurrent()
+{
+ QModelIndex index = mMessageList->indexBelow(mMessageList->currentIndex());
+ if (index.isValid()) {
+ mMessageList->setCurrentIndex(index);
+ }
+}
+
+void MessageListView::setPreviousCurrent()
+{
+ QModelIndex index = mMessageList->indexAbove(mMessageList->currentIndex());
+ if (index.isValid()) {
+ mMessageList->setCurrentIndex(index);
+ }
+}
+
+bool MessageListView::hasNext() const
+{
+ return (mMessageList->indexBelow(mMessageList->currentIndex()).isValid());
+}
+
+bool MessageListView::hasPrevious() const
+{
+ return (mMessageList->indexAbove(mMessageList->currentIndex()).isValid());
+}
+
+bool MessageListView::hasParent() const
+{
+ return (mMessageList->currentIndex().parent().isValid());
+}
+
+bool MessageListView::hasChildren() const
+{
+ return (mModel->rowCount(mMessageList->currentIndex()) > 0);
+}
+
+int MessageListView::rowCount(const QModelIndex &parentIndex) const
+{
+ int total = 0;
+
+ int count = mModel->rowCount(parentIndex);
+ total += count;
+
+ for (int i = 0; i < count; ++i) {
+ // Count the children of any expanded node
+ QModelIndex idx(mModel->index(i, 0, parentIndex));
+ if (mMessageList->isExpanded(idx)) {
+ total += rowCount(idx);
+ }
+ }
+
+ return total;
+}
+
+void MessageListView::selectedChildren(const QModelIndex &parentIndex, QMailMessageIdList *selectedIds) const
+{
+ for (int i = 0, count = mModel->rowCount(parentIndex); i < count; ++i) {
+ QModelIndex idx(mModel->index(i, 0, parentIndex));
+ if (static_cast<Qt::CheckState>(idx.data(Qt::CheckStateRole).toInt()) == Qt::Checked) {
+ selectedIds->append(idx.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ }
+
+ selectedChildren(idx, selectedIds);
+ }
+}
+
+QMailMessageIdList MessageListView::selected() const
+{
+ QMailMessageIdList selectedIds;
+
+ if (mMarkingMode) {
+ selectedChildren(mMessageList->rootIndex(), &selectedIds);
+ } else {
+ if (current().isValid())
+ selectedIds.append(current());
+ }
+
+ return selectedIds;
+}
+
+void MessageListView::setSelected(const QMailMessageIdList& ids)
+{
+ if (mMarkingMode) {
+ foreach (const QMailMessageId& id, ids)
+ setSelected(id);
+ }
+}
+
+void MessageListView::setSelected(const QMailMessageId& id)
+{
+ QModelIndex index = mModel->indexFromId(id);
+ if (index.isValid()) {
+ if (mMarkingMode) {
+ mModel->setData(index, static_cast<int>(Qt::Checked), Qt::CheckStateRole);
+ } else {
+ mMessageList->setCurrentIndex(index);
+ }
+ }
+}
+
+void MessageListView::selectChildren(const QModelIndex &parentIndex, bool selected, bool *modified)
+{
+ for (int i = 0, count = mModel->rowCount(parentIndex); i < count; ++i) {
+ QModelIndex idx(mModel->index(i, 0, parentIndex));
+ Qt::CheckState state(static_cast<Qt::CheckState>(idx.data(Qt::CheckStateRole).toInt()));
+
+ if (selected && (state == Qt::Unchecked)) {
+ mModel->setData(idx, static_cast<int>(Qt::Checked), Qt::CheckStateRole);
+ *modified = true;
+ } else if (!selected && (state == Qt::Checked)) {
+ mModel->setData(idx, static_cast<int>(Qt::Unchecked), Qt::CheckStateRole);
+ *modified = true;
+ }
+
+ selectChildren(idx, selected, modified);
+ }
+}
+
+void MessageListView::selectAll()
+{
+ bool modified(false);
+
+ selectChildren(mMessageList->rootIndex(), true, &modified);
+
+ if (modified)
+ emit selectionChanged();
+}
+
+void MessageListView::clearSelection()
+{
+ bool modified(false);
+
+ selectChildren(mMessageList->rootIndex(), false, &modified);
+
+ if (modified)
+ emit selectionChanged();
+}
+
+bool MessageListView::markingMode() const
+{
+ return mMarkingMode;
+}
+
+void MessageListView::setMarkingMode(bool set)
+{
+ if (mMarkingMode != set) {
+ mMarkingMode = set;
+ emit selectionChanged();
+ }
+}
+
+bool MessageListView::moreButtonVisible() const
+{
+ return mShowMoreButton;
+}
+
+void MessageListView::setMoreButtonVisible(bool set)
+{
+ if (mShowMoreButton != set) {
+ mShowMoreButton = set;
+ mMessageList->reset();
+ }
+}
+
+bool MessageListView::threaded() const
+{
+ return mThreaded;
+}
+
+void MessageListView::setThreaded(bool set)
+{
+ if (mThreaded != set) {
+ mThreaded = set;
+ reset();
+ }
+}
+
+bool MessageListView::ignoreUpdatesWhenHidden() const
+{
+ return mIgnoreWhenHidden;
+}
+
+void MessageListView::setIgnoreUpdatesWhenHidden(bool ignore)
+{
+ if (ignore != mIgnoreWhenHidden) {
+ mIgnoreWhenHidden = ignore;
+
+ if (!ignore && mModel->ignoreMailStoreUpdates())
+ mModel->setIgnoreMailStoreUpdates(false);
+ }
+}
+
+void MessageListView::showEvent(QShowEvent* e)
+{
+ mModel->setIgnoreMailStoreUpdates(false);
+
+ QWidget::showEvent(e);
+}
+
+void MessageListView::hideEvent(QHideEvent* e)
+{
+ if (mIgnoreWhenHidden)
+ mModel->setIgnoreMailStoreUpdates(true);
+
+ QWidget::hideEvent(e);
+}
+
+void MessageListView::modelChanged()
+{
+ mMarkingMode = false;
+}
+
+void MessageListView::indexClicked(const QModelIndex& index)
+{
+ if (mMarkingMode) {
+ bool checked(static_cast<Qt::CheckState>(index.data(Qt::CheckStateRole).toInt()) == Qt::Checked);
+ mModel->setData(index, static_cast<int>(checked ? Qt::Unchecked : Qt::Checked), Qt::CheckStateRole);
+ emit selectionChanged();
+ } else {
+ QMailMessageId id(index.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ if (id.isValid()) {
+ emit clicked(id);
+ }
+ }
+}
+
+void MessageListView::indexActivated(const QModelIndex& index)
+{
+ if (mMarkingMode) {
+ return;
+ } else {
+ QMailMessageId id(index.data(QMailMessageListModel::MessageIdRole).value<QMailMessageId>());
+ if (id.isValid()) {
+ emit activated(id);
+ }
+ }
+}
+
+void MessageListView::currentIndexChanged(const QModelIndex& currentIndex,
+ const QModelIndex& previousIndex)
+{
+ QMailMessageId currentId(currentIndex.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ QMailMessageId previousId(previousIndex.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+
+ emit currentChanged(currentId, previousId);
+
+ if (!mMarkingMode)
+ emit selectionChanged();
+}
+
+void MessageListView::rowsAboutToBeRemoved(const QModelIndex &parentIndex, int start, int end)
+{
+ if (mMarkingMode && !mSelectedRowsRemoved) {
+ // See if any of the removed rows are currently selected
+ for (int row = start; row <= end; ++row) {
+ QModelIndex idx(mModel->index(row, 0, parentIndex));
+ if (static_cast<Qt::CheckState>(idx.data(Qt::CheckStateRole).toInt()) == Qt::Checked) {
+ mSelectedRowsRemoved = true;
+ break;
+ }
+ }
+ }
+}
+
+void MessageListView::layoutChanged()
+{
+ if (mSelectedRowsRemoved) {
+ mSelectedRowsRemoved = false;
+ emit selectionChanged();
+ }
+}
+
+void MessageListView::reviewVisibleMessages()
+{
+ const int maximumRefreshRate = 15; //seconds
+
+ if (mScrollTimer.isActive())
+ return;
+
+ mPreviousVisibleItems = visibleMessagesIds();
+ emit visibleMessagesChanged();
+
+ mScrollTimer.start(maximumRefreshRate*1000);
+}
+
+void MessageListView::scrollTimeout()
+{
+ QMailMessageIdList strictlyVisibleIds(visibleMessagesIds(false));
+ bool contained = true;
+ foreach(QMailMessageId id, strictlyVisibleIds) {
+ if (!mPreviousVisibleItems.contains(id)) {
+ contained = false;
+ break;
+ }
+ }
+ if (!contained) {
+ reviewVisibleMessages();
+ } else {
+ mPreviousVisibleItems.clear();
+ }
+}
+
+void MessageListView::quickSearch(const QMailMessageKey& key)
+{
+ if (current().isValid())
+ mPreviousCurrent = current();
+
+ if (key.isEmpty()) {
+ mModel->setKey(mKey);
+ mKey = QMailMessageKey();
+ } else {
+ if (mKey.isEmpty()) {
+ mKey = mModel->key();
+ }
+ mModel->setKey(key & mKey);
+ setMoreButtonVisible(false);
+ }
+ mExpandAllTimer.start(0);
+}
+
+QMailMessageIdList MessageListView::visibleMessagesIds(bool buffer) const
+{
+ QMailMessageIdList visibleIds;
+ const int bufferWidth = 50;
+
+ QRect viewRect(mMessageList->viewport()->rect());
+ QModelIndex start(mMessageList->indexAt(QPoint(0,0)));
+ QModelIndex index = start;
+ while (index.isValid()) { // Add visible items
+ QRect indexRect(mMessageList->visualRect(index));
+ if (!indexRect.isValid() || (!viewRect.intersects(indexRect)))
+ break;
+ visibleIds.append(index.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ index = mMessageList->indexBelow(index);
+ }
+ if (!buffer)
+ return visibleIds;
+
+ int i = bufferWidth;
+ while (index.isValid() && (i > 0)) { // Add succeeding items
+ visibleIds.append(index.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ index = mMessageList->indexBelow(index);
+ --i;
+ }
+ i = bufferWidth;
+ index = start;
+ if (index.isValid())
+ index = mMessageList->indexAbove(index);
+ while (index.isValid() && (i > 0)) { // Add preceding items
+ visibleIds.append(index.data(QMailMessageModelBase::MessageIdRole).value<QMailMessageId>());
+ index = mMessageList->indexAbove(index);
+ --i;
+ }
+ return visibleIds;
+}
+
+bool MessageListView::showingQuickSearch() const
+{
+ return mQuickSearchWidget->isVisible();
+}
+
+void MessageListView::showQuickSearch(bool val)
+{
+ mQuickSearchWidget->setVisible(val);
+}
+
+bool MessageListView::eventFilter(QObject *, QEvent *event)
+{
+ if (event->type() == QEvent::KeyPress) {
+ if (QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event)) {
+ if (keyEvent->key() == Qt::Key_Back) {
+ emit backPressed();
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+void MessageListView::reset()
+{
+ if (mQuickSearchWidget && mQuickSearchWidget->isVisible()) {
+ mQuickSearchWidget->reset();
+ }
+
+ QMailMessageKey key;
+ QMailMessageSortKey sortKey;
+ QMailMessageId currentId;
+
+ if (mModel) {
+ key = this->key();
+ sortKey = this->sortKey();
+ currentId = this->current();
+ delete mModel;
+ }
+
+ if (mThreaded) {
+ mModel = new MessageListModel<QMailMessageThreadedModel>(this);
+ } else {
+ mModel = new MessageListModel<QMailMessageListModel>(this);
+ }
+
+ connect(mModel, SIGNAL(modelChanged()),
+ this, SLOT(modelChanged()));
+ connect(mModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)),
+ this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
+ connect(mModel, SIGNAL(layoutChanged()),
+ this, SLOT(layoutChanged()));
+ connect(mModel, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ this, SLOT(reviewVisibleMessages()));
+ connect(mModel, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ this, SLOT(reviewVisibleMessages()));
+
+ mMessageList->setModel(mModel);
+ mMessageList->setRootIsDecorated(false);
+ mExpandAllTimer.start(0);
+
+ if (!key.isEmpty() || !mQuickSearchWidget->searchKey().isEmpty()) {
+ setKey(key);
+ }
+ if (!sortKey.isEmpty()) {
+ setSortKey(sortKey);
+ }
+ if (currentId.isValid()) {
+ setCurrent(currentId);
+ }
+}
+
+void MessageListView::expandAll()
+{
+ mExpandAllTimer.stop();
+ mMessageList->expandAll();
+ scrollTo(mPreviousCurrent);
+ QTimer::singleShot(0, this, SLOT(reviewVisibleMessages()));
+}
+
+void MessageListView::scrollTo(const QMailMessageId &id)
+{
+ if (!id.isValid())
+ return;
+
+ QModelIndex index(mModel->indexFromId(id));
+ if (index.isValid()) {
+ mMessageList->scrollTo(index);
+ mMessageList->setCurrentIndex(index);
+ }
+}
+
+#include "messagelistview.moc"
+