examples/keepintouch/addressfinder.cpp
changeset 0 876b1a06bc25
child 5 603d3f8b6302
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/keepintouch/addressfinder.cpp	Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,606 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+**   * Redistributions of source code must retain the above copyright
+**     notice, this list of conditions and the following disclaimer.
+**   * Redistributions in binary form must reproduce the above copyright
+**     notice, this list of conditions and the following disclaimer in
+**     the documentation and/or other materials provided with the
+**     distribution.
+**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
+**     the names of its contributors may be used to endorse or promote
+**     products derived from this software without specific prior written
+**     permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "addressfinder.h"
+
+#include <qcontactdetailfilter.h>
+#include <qcontactemailaddress.h>
+#include <qcontactmanager.h>
+#include <qcontactphonenumber.h>
+#include <qmessage.h>
+#include <qmessageservice.h>
+
+#include <QCheckBox>
+#include <QComboBox>
+#include <QDateTime>
+#include <QGroupBox>
+#include <QLabel>
+#include <QLayout>
+#include <QListWidget>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QTimer>
+#include <QDebug>
+#include <QApplication>
+#include <QMenuBar>
+#include <QTabWidget>
+#include <QScrollArea>
+
+namespace {
+
+QString simpleAddress(const QString &recipient)
+{
+    QString name;
+    QString addressOnly;
+
+    QMessageAddress::parseEmailAddress(recipient, &name, &addressOnly);
+
+    return addressOnly;
+}
+
+QString simpleAddress(const QMessageAddress &address)
+{
+    return simpleAddress(address.addressee());
+}
+
+//! [contact-lookup]
+QString contactDisplayName(const QMessageAddress &address)
+{
+    QString addressOnly(simpleAddress(address));
+
+    // See if we can match this address to a contact
+    QContactDetailFilter filter;
+    if (address.type() == QMessageAddress::Email) {
+        // Match contacts on email address data
+        filter.setDetailDefinitionName(QContactEmailAddress::DefinitionName);
+        filter.setValue(addressOnly);
+        filter.setMatchFlags(QContactFilter::MatchContains);
+    } else if (address.type() == QMessageAddress::Phone) {
+        // Match contacts on phone number data
+        filter.setDetailDefinitionName(QContactPhoneNumber::DefinitionName);
+        filter.setValue(addressOnly);
+        filter.setMatchFlags(QContactFilter::MatchPhoneNumber);
+    }
+
+    QContactManager manager;
+    foreach (const QContactLocalId &contactId, manager.contactIds(filter)) {
+        // Any match is acceptable
+        const QContact &contact(manager.contact(contactId));
+        return contact.displayLabel();
+    }
+
+    // We couldn't match anything, so return the original address
+    return address.addressee();
+}
+//! [contact-lookup]
+
+}
+
+AddressFinder::AddressFinder(QWidget *parent, Qt::WindowFlags flags)
+    : QMainWindow(parent, flags),
+      tabWidget(0),
+      includePeriod(0),
+      excludePeriod(0),
+      excludeCheckBox(0),
+      searchAction(0),
+      searchButton(0),
+      contactList(0),
+      messageCombo(0)
+ {
+    setupUi();
+
+    connect(&service, SIGNAL(stateChanged(QMessageService::State)), this, SLOT(stateChanged(QMessageService::State)));
+    connect(&service, SIGNAL(messagesFound(QMessageIdList)), this, SLOT(messagesFound(QMessageIdList)));
+}
+
+AddressFinder::~AddressFinder()
+{
+}
+
+void AddressFinder::includePeriodChanged(int selected)
+{
+    // Only allow smaller periods to be excluded
+    excludePeriod->clear();
+
+    switch (selected) {
+        case 0: excludePeriod->insertItem(0, "9 Months"); // fall through:
+        case 1: excludePeriod->insertItem(0, "6 Months");
+        case 2: excludePeriod->insertItem(0, "3 Months");
+        case 3: excludePeriod->insertItem(0, "Month");
+        case 4: excludePeriod->insertItem(0, "Week");
+        default: break;
+    }
+
+    excludePeriod->setCurrentIndex(0);
+}
+
+void AddressFinder::excludePeriodEnabled(int state)
+{
+    excludePeriod->setEnabled(state == Qt::Checked);
+}
+
+//! [address-selected]
+void AddressFinder::addressSelected(const QString &address)
+{
+    messageCombo->clear();
+
+    QString addressOnly(simpleAddress(address));
+
+    // Add the subject of each message to this address to the message pane
+    typedef QPair<QString, QMessageId> MessageDetails;
+    foreach (const MessageDetails &message, addressMessages[addressOnly]) {
+        messageCombo->addItem(message.first);
+    }
+}
+//! [address-selected]
+
+void AddressFinder::searchMessages()
+{
+    setSearchActionEnabled(false);
+
+    contactList->clear();
+    messageCombo->clear();
+    excludedAddresses.clear();
+    addressList.clear();
+    addressMessages.clear();
+    inclusionMessages.clear();
+    exclusionMessages.clear();
+
+//! [create-date-range]
+    QDateTime now(QDateTime::currentDateTime());
+    bool useExclusionPeriod(excludeCheckBox->isChecked());
+
+    // Determine the dates that demarcate the selected range
+    QDateTime minimumDate(now);
+    switch (includePeriod->currentIndex()) {
+        case 0: minimumDate = minimumDate.addMonths(-12); break;
+        case 1: minimumDate = minimumDate.addMonths(-9); break;
+        case 2: minimumDate = minimumDate.addMonths(-6); break;
+        case 3: minimumDate = minimumDate.addMonths(-3); break;
+        case 4: minimumDate = minimumDate.addMonths(-1); break;
+        case 5: minimumDate = minimumDate.addDays(-7); break;
+        default: break;
+    }
+
+    QDateTime maximumDate(now);
+    if (useExclusionPeriod) {
+        // We have an exclusion period to apply
+        switch (excludePeriod->currentIndex()) {
+            case 0: maximumDate = maximumDate.addDays(-7); break;
+            case 1: maximumDate = maximumDate.addMonths(-1); break;
+            case 2: maximumDate = maximumDate.addMonths(-3); break;
+            case 3: maximumDate = maximumDate.addMonths(-6); break;
+            case 4: maximumDate = maximumDate.addMonths(-9); break;
+            default: break;
+        }
+    }
+//! [create-date-range]
+
+//! [create-simple-filters]
+    // We will include addresses contacted following the minimum date
+    QMessageFilter includeFilter(QMessageFilter::byTimeStamp(minimumDate, QMessageDataComparator::GreaterThanEqual));
+    // Windows mobile only sets a receptionTimeStamp for sent messsages
+    includeFilter |= QMessageFilter::byReceptionTimeStamp(minimumDate, QMessageDataComparator::GreaterThanEqual);
+
+    QMessageFilter excludeFilter;
+    if (useExclusionPeriod) {
+        // We will exclude addresses contacted following the maximum date
+        excludeFilter = QMessageFilter::byTimeStamp(maximumDate, QMessageDataComparator::GreaterThanEqual);
+        excludeFilter |= QMessageFilter::byReceptionTimeStamp(maximumDate, QMessageDataComparator::GreaterThanEqual);
+    }
+//! [create-simple-filters]
+
+//! [create-composite-filters]
+    // We only want to match messages that we sent
+    QMessageFilter sentFilter(QMessageFilter::byStandardFolder(QMessage::SentFolder));
+
+    // Create the filter needed to locate messages to search for addresses to include
+    if (useExclusionPeriod) {
+        inclusionFilter = (sentFilter & includeFilter & ~excludeFilter);
+    } else {
+        inclusionFilter = (sentFilter & includeFilter);
+    }
+//! [create-composite-filters]
+
+//! [begin-search]
+    if (useExclusionPeriod) {
+        // Create the filter needed to locate messages whose address we will exclude
+        QMessageFilter exclusionFilter;
+        exclusionFilter = (sentFilter & excludeFilter);
+        
+        // Start the search for messages containing addresses to exclude
+        service.queryMessages(exclusionFilter);
+    } else {
+        // Only search for messages containing addresses to include
+        service.queryMessages(inclusionFilter);
+
+        // Clear the inclusion filter to indicate that we have searched for it
+        inclusionFilter = QMessageFilter();
+    }
+//! [begin-search]
+}
+
+//! [handle-search-result]
+void AddressFinder::stateChanged(QMessageService::State newState)
+{
+    if (newState == QMessageService::FinishedState) {
+        if (service.error() == QMessageManager::NoError) {
+            if (!inclusionFilter.isEmpty()) {
+                // Now find the included messages
+                service.queryMessages(inclusionFilter);
+
+                // Clear the inclusion filter to indicate that we have searched for it
+                inclusionFilter = QMessageFilter();
+            } else {
+                // We have found the exclusion and inclusion message sets
+                if (!inclusionMessages.isEmpty()) {
+                    // Begin processing the message sets
+                    QTimer::singleShot(0, this, SLOT(continueSearch()));
+//! [handle-search-result]
+                } else {
+                    QMessageBox::information(0, tr("Empty"), tr("No messages found"));
+                    searchAction->setEnabled(true);
+#ifdef USE_SEARCH_BUTTON
+                    searchButton->setEnabled(true);
+#endif
+                }
+            }
+        } else {
+            QMessageBox::warning(0, tr("Failed"), tr("Unable to perform search"));
+            setSearchActionEnabled(true);
+        }
+    }
+}
+
+//! [accumulate-matches]
+void AddressFinder::messagesFound(const QMessageIdList &ids)
+{
+    // Add these IDs to the relevant set
+    if (!inclusionFilter.isEmpty()) {
+        exclusionMessages << ids;
+    } else {
+        inclusionMessages << ids;
+    }
+}
+//! [accumulate-matches]
+
+//! [continue-search]
+void AddressFinder::continueSearch()
+{
+    if (!exclusionMessages.isEmpty()) {
+        // Take the first message whose addreses we should exclude
+        QMessageId id(exclusionMessages.takeFirst());
+        const QMessage message(id);
+
+        // All recipient addresses are to be excluded
+        foreach (const QMessageAddress &address, message.to() + message.cc() + message.bcc()) {
+            excludedAddresses.insert(simpleAddress(address));
+        }
+    } else if (!inclusionMessages.isEmpty()) {
+        // Take the first message to inspect for suitable addresses
+        QMessageId id(inclusionMessages.takeFirst());
+        const QMessage message(id);
+
+        QString details;
+
+        // For each recipient of this message
+        foreach (const QMessageAddress &address, message.to() + message.cc() + message.bcc()) {
+            QString addressOnly(simpleAddress(address));
+
+            // Ignore recipients whose addresses we have added to the exclusion set
+            if (!excludedAddresses.contains(addressOnly)) {
+                // Link this message to this address
+                QList<QPair<QString, QMessageId> > &messageList(addressMessages[addressOnly]);
+                if (messageList.isEmpty()) {
+                    addressList.append(addressOnly);
+
+                    // Add the recipient to our visible list of contacts to keep in touch with
+                    contactList->addItem(contactDisplayName(address));
+                }
+
+                if (details.isEmpty()) {
+                    // Determine the properties of the message
+                    details = QString("[%1] %2").arg(message.date().toString("MMM d")).arg(message.subject());
+                }
+                messageList.append(qMakePair(details, id));
+            }
+        }
+    }
+
+    if (!exclusionMessages.isEmpty() || !inclusionMessages.isEmpty()) {
+        // There are more messages to process
+        QTimer::singleShot(0, this, SLOT(continueSearch()));
+    } else {
+        // We're finished our search
+        setSearchActionEnabled(true);
+#ifndef USE_SEARCH_BUTTON
+        tabChanged(1);
+#endif
+
+        if (
+#ifdef USE_CONTACTS_COMBOBOX
+                contactList->currentIndex() != -1
+#else
+                contactList->currentItem()
+#endif
+                ) {
+            // Select the first address automatically
+            addressSelected(
+#ifdef USE_CONTACTS_COMBOBOX
+                    contactList->currentText()
+#else
+                    contactList->currentItem()->text()
+#endif
+                    );
+        }
+    }
+}
+//! [continue-search]
+
+#ifndef USE_SEARCH_BUTTON
+void AddressFinder::tabChanged(int index)
+{
+    QWidget* currentTab = tabWidget->currentWidget();
+    QAction* action = 0;
+    if(currentTab && !currentTab->actions().isEmpty())
+        action = currentTab->actions().first();
+    menuBar()->setDefaultAction(action);
+
+    Q_UNUSED(index)
+}
+#endif
+
+void AddressFinder::setupUi()
+{
+    setWindowTitle(tr("Keep In Touch"));
+
+#ifndef USE_SEARCH_BUTTON
+    tabWidget = new QTabWidget(this);
+    setCentralWidget(tabWidget);
+    connect(tabWidget,SIGNAL(currentChanged(int)),this,SLOT(tabChanged(int)));
+#else
+    QWidget* centralWidget = new QWidget(this);
+    QScrollArea* scrollArea = new QScrollArea(this);
+    scrollArea->setWidget(centralWidget);
+    scrollArea->setWidgetResizable(true);
+    setCentralWidget(scrollArea);
+    QVBoxLayout* centralLayout = new QVBoxLayout(centralWidget);
+#endif
+
+    QGroupBox *inputGroup = new QGroupBox(tr("Find addresses"));
+    inputGroup->setAlignment(Qt::AlignLeft);
+#ifndef USE_SEARCH_BUTTON
+    tabWidget->addTab(inputGroup,"Search");
+#else
+    centralLayout->addWidget(inputGroup);
+#endif
+
+    QGridLayout *filterLayout = new QGridLayout(inputGroup);
+#ifdef Q_WS_MAEMO_5
+    // Maemo 5 style doesn't take group box titles into account.
+    int spacingHack = QFontMetrics(QFont()).height();
+    filterLayout->setContentsMargins(0, spacingHack, 0, 0);
+#endif
+
+    QLabel *includeLabel = new QLabel(tr("Contacted in the last"));
+    filterLayout->addWidget(includeLabel, 0, 0);
+    filterLayout->setAlignment(includeLabel, Qt::AlignRight);
+
+    excludeCheckBox = new QCheckBox(tr("But not in the last"));
+#ifdef Q_WS_MAEMO_5
+    // Maemo 5 style cuts off check box text.
+    excludeCheckBox->setText(excludeCheckBox->text() + "  ");
+#endif
+    connect(excludeCheckBox, SIGNAL(stateChanged(int)), this, SLOT(excludePeriodEnabled(int)));
+    filterLayout->addWidget(excludeCheckBox, 1, 0);
+    filterLayout->setAlignment(excludeCheckBox, Qt::AlignRight);
+
+    includePeriod = new QComboBox;
+    includePeriod->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    includePeriod->addItem(tr("Year"));
+    includePeriod->addItem(tr("9 Months"));
+    includePeriod->addItem(tr("6 Months"));
+    includePeriod->addItem(tr("3 Months"));
+    includePeriod->addItem(tr("Month"));
+    includePeriod->addItem(tr("Week"));
+    connect(includePeriod, SIGNAL(currentIndexChanged(int)), this, SLOT(includePeriodChanged(int)));
+    filterLayout->addWidget(includePeriod, 0, 1);
+
+    excludePeriod = new QComboBox;
+    excludePeriod->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    filterLayout->addWidget(excludePeriod, 1, 1);
+    excludePeriod->setEnabled(false);
+
+#ifdef USE_SEARCH_BUTTON
+    searchButton = new QPushButton(tr("Search"));
+    searchButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    connect(searchButton, SIGNAL(clicked()), this, SLOT(searchMessages()), Qt::QueuedConnection);
+    filterLayout->addWidget(searchButton, 2, 1);
+#endif
+
+#ifdef USE_CONTACTS_COMBOBOX
+    contactList = new QComboBox(this);
+    connect(contactList, SIGNAL(currentIndexChanged(QString)), this, SLOT(addressSelected(QString)));
+#else
+    contactList = new QListWidget(this);
+    connect(contactList, SIGNAL(currentTextChanged(QString)), this, SLOT(addressSelected(QString)));
+#endif
+
+#ifndef USE_SEARCH_BUTTON
+    QWidget* resultsWidget = new QWidget(this);
+    QVBoxLayout* resultsLayout = new QVBoxLayout(resultsWidget);
+    tabWidget->addTab(resultsWidget,"Results");
+#else
+    QVBoxLayout* resultsLayout = centralLayout;
+#endif
+
+    QGroupBox *addressGroup = new QGroupBox(tr("Contacts"));
+    addressGroup->setAlignment(Qt::AlignLeft);
+    addressGroup->setLayout(new QVBoxLayout);
+#ifdef Q_WS_MAEMO_5
+    addressGroup->layout()->setContentsMargins(0, spacingHack, 0, 0);
+#endif
+    addressGroup->layout()->addWidget(contactList);
+    resultsLayout->addWidget(addressGroup);
+
+    QGroupBox *messageGroup = new QGroupBox(tr("Messages"));
+    messageGroup->setAlignment(Qt::AlignLeft);
+
+    QVBoxLayout *groupLayout = new QVBoxLayout;
+#ifdef Q_WS_MAEMO_5
+    groupLayout->setContentsMargins(0, spacingHack, 0, 0);
+#endif
+
+    messageCombo = new QComboBox;
+    connect(messageCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(messageIndexChanged(int)));
+
+    groupLayout->addWidget(messageCombo);
+
+    showButton = new QPushButton(tr("Show..."));
+    showButton->setEnabled(false);
+    connect(showButton, SIGNAL(clicked()), this, SLOT(showMessage()));
+
+    forwardButton = new QPushButton(tr("Forward..."));
+    forwardButton->setEnabled(false);
+    connect(forwardButton, SIGNAL(clicked()), this, SLOT(forwardMessage()));
+
+    QHBoxLayout *buttonLayout = new QHBoxLayout;
+    buttonLayout->addWidget(showButton);
+    buttonLayout->addWidget(forwardButton);
+
+    groupLayout->addLayout(buttonLayout);
+
+    messageGroup->setLayout(groupLayout);
+    resultsLayout->addWidget(messageGroup);
+
+    searchAction = new QAction("Search",this);
+    inputGroup->addAction(searchAction);
+    connect(searchAction,SIGNAL(triggered()),this,SLOT(searchMessages()),Qt::QueuedConnection);
+    QAction* quitAction = menuBar()->addAction("Quit");
+    connect(quitAction,SIGNAL(triggered()),qApp,SLOT(quit()));
+
+    includePeriodChanged(0);
+#ifndef USE_SEARCH_BUTTON
+    tabChanged(0);
+#endif
+
+    QWidgetList focusableWidgets;
+    focusableWidgets << excludeCheckBox
+                     << includePeriod
+                     << excludePeriod
+                     << contactList
+                     << messageCombo
+                     << showButton
+#ifndef USE_SEARCH_BUTTON
+                     << tabWidget
+#else
+                     << searchButton
+                     << scrollArea
+#endif
+                     << forwardButton;
+
+  foreach(QWidget* w, focusableWidgets)
+       w->setContextMenuPolicy(Qt::NoContextMenu);
+
+    excludePeriod->setFocus();
+}
+
+void AddressFinder::setSearchActionEnabled(bool val)
+{
+    searchAction->setEnabled(val);
+#ifdef USE_SEARCH_BUTTON
+    searchButton->setEnabled(val);
+#endif
+}
+
+void AddressFinder::messageIndexChanged(int index)
+{
+    bool messageSelected(index != -1);
+    showButton->setEnabled(messageSelected);
+    forwardButton->setEnabled(messageSelected);
+}
+
+//! [show-message]
+void AddressFinder::showMessage()
+{
+    int index = messageCombo->currentIndex();
+    if (index != -1) {
+        // Find the address currently selected
+        const QString &selectedAddress(addressList[
+#ifdef USE_CONTACTS_COMBOBOX
+                contactList->currentIndex()
+#else
+                contactList->currentRow()
+#endif
+                ]);
+
+        // Show the message selected
+        QMessageId &messageId((addressMessages[selectedAddress])[index].second);
+        service.show(messageId);
+    }
+}
+//! [show-message]
+
+//! [compose-message]
+void AddressFinder::forwardMessage()
+{
+    int index = messageCombo->currentIndex();
+    if (index != -1) {
+        // Find the address currently selected
+        const QString &selectedAddress(addressList[
+#ifdef USE_CONTACTS_COMBOBOX
+                contactList->currentIndex()
+#else
+                contactList->currentRow()
+#endif
+                ]);
+
+        // Find the selected message
+        QMessageId &messageId((addressMessages[selectedAddress])[index].second);
+        QMessage original(messageId);
+
+        // Create a message which forwards the selected message to the same recipient
+        QMessage fwd(original.createResponseMessage(QMessage::Forward));
+        fwd.setTo(original.to());
+        service.compose(fwd);
+    }
+}
+//! [compose-message]
+