MeeGo 1.2 Harmattan Developer Documentation Develop for the Nokia N9

mainwindow.cpp Example File

serviceactions/mainwindow.cpp
 /****************************************************************************
 **
 ** 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 "mainwindow.h"
 #include "attachmentlistwidget.h"
 #include "qmessageservice.h"
 #include <qmessagemanager.h>
 #include <QComboBox>
 #include <QListWidget>
 #include <QVBoxLayout>
 #include <QLabel>
 #include <QTabWidget>
 #include <QPointer>
 #include <QPushButton>
 #include <QDebug>
 #include <QLineEdit>
 #include <QTextEdit>
 #include <QTextBrowser>
 #include <QFileDialog>
 #include <QTimer>
 #include <QMessageBox>
 #include <QThread>
 #include <QStackedLayout>
 #include <QPair>
 #include <QScrollArea>
 #include <QMenuBar>
 #include <QApplication>
 #include <QStackedWidget>
 #include <QMutex>
 #include <QKeyEvent>
 #include <QDesktopServices>
 #include <QInputDialog>

 static const QSize WindowGeometry(400,300);
 static const QString WindowTitle("Service-actions Example");
 static unsigned int RecentMessagesCount = 50;

 class AccountsWidget : public QWidget
 {
     Q_OBJECT

 private:
     class Loader : public QThread
     {
     public:
         Loader(AccountsWidget* parent);
         void run();

     private:
         AccountsWidget* m_parent;
     };

 public:
     AccountsWidget(QWidget* parent = 0);
     QMessageAccountId currentAccount() const;
     QString currentAccountName() const;
     bool isEmpty() const;

 signals:
     void accountChanged();

 protected:
     void showEvent(QShowEvent* e);
     void hideEvent(QHideEvent* e);

 private slots:
     void load();
     void loadStarted();
     void loadFinished();

 private:
     void setupUi();
     void setIds(const QMessageAccountIdList& ids);
     QMessageAccountIdList ids() const;

 private:
     QStackedLayout* m_stackedLayout;
     QComboBox* m_accountsCombo;
     QLabel* m_busyLabel;

     Loader m_loader;
     mutable QMutex m_loadMutex;
     QMessageAccountIdList m_ids;
 };

 AccountsWidget::Loader::Loader(AccountsWidget* parent)
 :
 QThread(parent),
 m_parent(parent)
 {
 }

 void AccountsWidget::Loader::run()
 {
     QMessageManager manager;
     m_parent->setIds(manager.queryAccounts());
 }

 AccountsWidget::AccountsWidget(QWidget* parent)
 :
 QWidget(parent),
 m_stackedLayout(0),
 m_accountsCombo(0),
 m_busyLabel(0),
 m_loader(this)
 {
     setupUi();

     connect(&m_loader,SIGNAL(started()),this,SLOT(loadStarted()));
     connect(&m_loader,SIGNAL(finished()),this,SLOT(loadFinished()));
 }

 QMessageAccountId AccountsWidget::currentAccount() const
 {
     QMessageAccountId result;
     if(m_loader.isFinished() && m_accountsCombo->count())
     {
         int index = m_accountsCombo->currentIndex();
         return ids().at(index);
     }

     return result;
 }

 QString AccountsWidget::currentAccountName() const
 {
     if(m_loader.isFinished() && m_accountsCombo->count())
         return m_accountsCombo->itemData(m_accountsCombo->currentIndex()).toString();
     return QString();
 }

 bool AccountsWidget::isEmpty() const
 {
     return m_accountsCombo->count() == 0;
 }

 void AccountsWidget::showEvent(QShowEvent* e)
 {
     load();
     QWidget::showEvent(e);
 }

 void AccountsWidget::hideEvent(QHideEvent* e)
 {
     if(m_loader.isRunning())
         m_loader.exit();
     QWidget::hideEvent(e);
 }

 void AccountsWidget::load()
 {
     static bool runonce = false;
     //#define NOTHREAD
 #ifdef NOTHREAD
     QMessageManager manager;
     if(!runonce)
      setIds(manager.queryAccounts());
     //        m_loader.start();

 #else
     //    if(!runonce)
          m_loader.start();
 #endif
     runonce = true;
 }

 void AccountsWidget::loadStarted()
 {
 #ifndef _WIN32_WCE
     setCursor(Qt::BusyCursor);
 #endif
     m_stackedLayout->setCurrentWidget(m_busyLabel);
 }

 void AccountsWidget::loadFinished()
 {
     m_accountsCombo->clear();

     QMessageAccountIdList accountIds = ids();

     if(!accountIds.isEmpty())
     {
         for(int i = 0; i < accountIds.count(); ++i)
         {
             QMessageAccount account(accountIds[i]);
             m_accountsCombo->addItem(QString("%1 - %2").arg(i+1).arg(account.name()),account.name());
         }

         m_stackedLayout->setCurrentWidget(m_accountsCombo);
     }
     else
         m_busyLabel->setText("No accounts!");

 #ifndef _WIN32_WCE
     setCursor(Qt::ArrowCursor);
 #endif
 }

 void AccountsWidget::setupUi()
 {
     m_stackedLayout = new QStackedLayout(this);

     m_accountsCombo = new QComboBox(this);
     m_stackedLayout->addWidget(m_accountsCombo);
     connect(m_accountsCombo,SIGNAL(currentIndexChanged(int)),this,SIGNAL(accountChanged()));

     m_busyLabel = new QLabel("Loading...");
     m_stackedLayout->addWidget(m_busyLabel);

     setSizePolicy(m_accountsCombo->sizePolicy());

 }

 void AccountsWidget::setIds(const QMessageAccountIdList& ids)
 {
     QMutexLocker mutex(&m_loadMutex);

     m_ids = ids;
 }

 QMessageAccountIdList AccountsWidget::ids() const
 {
     QMutexLocker mutex(&m_loadMutex);
     return m_ids;
 }

 class RecentMessagesWidget : public QWidget
 {
     Q_OBJECT

 public:
     RecentMessagesWidget(QWidget* parent = 0, unsigned int maxRecent = 10);
     ~RecentMessagesWidget();
     QMessageId currentMessage() const;

 signals:
     void selected(const QMessageId& messageId);

 protected:
     void showEvent(QShowEvent* e);
     void hideEvent(QHideEvent* e);

 private slots:
     void currentItemChanged(QListWidgetItem* current, QListWidgetItem* previous);
     void messagesFound(const QMessageIdList& result);
     void stateChanged(QMessageService::State s);
     void messageUpdated(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filter);
     void messageRemoved(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filter);
     void processResults();

 private:
     void setupUi();
     void updateState();
     void load();

 private:
     enum State { Unloaded, Loading, LoadFinished, Processing, LoadFailed, Done };
     static const int MessageIdRole = Qt::UserRole + 1;

 private:
     QListWidget* m_messageListWidget;
     QLabel* m_statusLabel;
     QStackedLayout* m_layout;
     QMessageIdList m_ids;
     QMap<QMessageId,QListWidgetItem*> m_indexMap;
     unsigned int m_maxRecent;
     QMessageService* m_service;
     State m_state;
     QMessageManager::NotificationFilterId m_storeFilterId;
     QMessageManager m_manager;
 };

 RecentMessagesWidget::RecentMessagesWidget(QWidget* parent, unsigned int maxRecent)
 :
 QWidget(parent),
 m_messageListWidget(0),
 m_statusLabel(0),
 m_layout(0),
 m_maxRecent(maxRecent),
 m_service(new QMessageService(this)),
 m_state(Unloaded)
 {
     setupUi();
     connect(m_service,SIGNAL(messagesFound(const QMessageIdList&)),this,SLOT(messagesFound(const QMessageIdList&)));
     connect(m_service,SIGNAL(stateChanged(QMessageService::State)),this,SLOT(stateChanged(QMessageService::State)));

     //register for message update notifications

     connect(&m_manager, SIGNAL(messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)),
         this, SLOT(messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)));
     connect(&m_manager, SIGNAL(messageRemoved(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)),
         this, SLOT(messageRemoved(const QMessageId&, const QMessageManager::NotificationFilterIdSet&)));

     m_storeFilterId = m_manager.registerNotificationFilter(QMessageFilter());
 }

 RecentMessagesWidget::~RecentMessagesWidget()
 {
      m_manager.unregisterNotificationFilter(m_storeFilterId);
 }

 QMessageId RecentMessagesWidget::currentMessage() const
 {
     QMessageId result;

     if(QListWidgetItem* currentItem = m_messageListWidget->currentItem())
         result = QMessageId(currentItem->data(MessageIdRole).toString());

     return result;
 }

 void RecentMessagesWidget::showEvent(QShowEvent* e)
 {
     if(m_state == Unloaded)
         load();

     updateState();

     QWidget::showEvent(e);
 }

 void RecentMessagesWidget::hideEvent(QHideEvent* e)
 {
     if(m_state == Loading || m_state == Processing)
     {
         m_service->cancel();
         m_state = Unloaded;
         m_ids.clear();
     }

     QWidget::hideEvent(e);
 }

 void RecentMessagesWidget::currentItemChanged(QListWidgetItem*, QListWidgetItem*)
 {
     if(m_state != Processing || m_state != Loading)
         emit selected(currentMessage());
 }

 void RecentMessagesWidget::messagesFound(const QMessageIdList& ids)
 {
     m_ids.append(ids);
 }

 void RecentMessagesWidget::stateChanged(QMessageService::State newState)
 {
     if (newState == QMessageService::FinishedState) {
         if ((m_state != LoadFailed) && (m_service->error() == QMessageManager::NoError)) {
             m_state = LoadFinished;
         } else {
             m_state = LoadFailed;
         }
     } else if (newState == QMessageService::CanceledState) {
         m_state = LoadFinished;
     }

     updateState();
 }

 void RecentMessagesWidget::messageUpdated(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filter)
 {
     if(!filter.contains(m_storeFilterId) || m_state == Loading || !id.isValid() || !m_indexMap.contains(id))
         return;

     //update the pertinent entry to reflect completeness

     QListWidgetItem* item = m_indexMap.value(id);
     if(item)
     {

         QMessage message(id);
         bool partialMessage = !message.find(message.bodyId()).isContentAvailable();
         QFont itemFont = item->font();
         itemFont.setItalic(partialMessage);
         item->setFont(itemFont);
     }
 }

 void RecentMessagesWidget::messageRemoved(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filter)
 {
     if(!filter.contains(m_storeFilterId) || m_state == Loading || !id.isValid() || !m_indexMap.contains(id))
         return;

     QListWidgetItem* item = m_indexMap.value(id);
     if(item)
     {
         int row = m_messageListWidget->row(item);
         QListWidgetItem* item = m_messageListWidget->takeItem(row);
         m_indexMap.remove(id);
         delete item;
     }
     m_ids.removeAll(id);
 }

 void RecentMessagesWidget::setupUi()
 {
     m_layout = new QStackedLayout(this);

     m_messageListWidget = new QListWidget(this);
     m_layout->addWidget(m_messageListWidget);
     connect(m_messageListWidget,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)),
         this,SLOT(currentItemChanged(QListWidgetItem*,QListWidgetItem*)));

     m_statusLabel = new QLabel(this);
     m_statusLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
     m_statusLabel->setFrameStyle(QFrame::Box);
     m_layout->addWidget(m_statusLabel);
 }

 void RecentMessagesWidget::updateState()
 {
     switch(m_state)
     {
         case Unloaded:
         {
             m_statusLabel->setText(QString());
             m_layout->setCurrentWidget(m_statusLabel);
         }
         break;
         case Loading:
         {
             m_statusLabel->setText("Loading...");
             m_layout->setCurrentWidget(m_statusLabel);
         }
         break;
         case LoadFinished:
         {
             if(m_ids.isEmpty())
             {
                 m_statusLabel->setText("Finished. No messages.");
                 m_layout->setCurrentWidget(m_statusLabel);
             }
             else
             {
                 m_state = Processing;
                 updateState();
                 processResults();
             }
         }
         break;
         case Processing:
             m_layout->setCurrentWidget(m_messageListWidget);
         break;
         case LoadFailed:
         {
             m_statusLabel->setText("Load failed!");
             m_layout->setCurrentWidget(m_statusLabel);
         }
         break;
     }

 #ifndef _WIN32_WCE
     if(m_state == Loading || m_state == Processing)
         setCursor(Qt::BusyCursor);
     else
         setCursor(Qt::ArrowCursor);
 #endif

 }

 void RecentMessagesWidget::load()
 {
     m_ids.clear();
     m_state = Loading;
     bool b;

     b=m_service->queryMessages(QMessageFilter(),QMessageSortOrder::byReceptionTimeStamp(Qt::DescendingOrder),m_maxRecent);
 };

 void RecentMessagesWidget::processResults()
 {
     if(!m_ids.isEmpty())
     {
         QMessageId id = m_ids.takeFirst();
         QMessage message(id);

         QListWidgetItem* newItem = new QListWidgetItem(message.from().addressee()+QString(":")+message.subject());
         newItem->setData(MessageIdRole,id.toString());
         QFont itemFont = newItem->font();
         bool isPartialMessage = !message.find(message.bodyId()).isContentAvailable();
         itemFont.setItalic(isPartialMessage);
         newItem->setFont(itemFont);
         m_messageListWidget->addItem(newItem);
         m_indexMap.insert(id,newItem);
         m_messageListWidget->update();
         QTimer::singleShot(100,this,SLOT(processResults()));
     }
     else
     {
         m_state = Done;
         updateState();
     }
 }

 class ComposeSendWidget : public QWidget
 {
     Q_OBJECT

 public:
     ComposeSendWidget(QMessageService* service, QWidget* parent = 0);

 signals:
     void actionsChanged();

 private slots:
     void composeButtonClicked();
     void sendButtonClicked();
     void addAttachmentButtonClicked();
     void accountChanged();

 private:
     void setupUi();
     QMessage constructQMessage(bool asHtml = false) const;

 private:
     QStackedLayout* m_layoutStack;
     QMessageService* m_service;
     AccountsWidget* m_accountsWidget;
     QLineEdit* m_toEdit;
     QLineEdit* m_ccEdit;
     QLabel* m_ccLabel;
     QLineEdit* m_bccEdit;
     QLabel* m_bccLabel;
     QLineEdit* m_subjectEdit;
     QLabel* m_subjectLabel;
     QTextEdit* m_bodyEdit;
     AttachmentListWidget* m_attachmentList;
     QAction* m_attachmentsAction;
     QAction* m_sendAsHTMLAction;
 };

 ComposeSendWidget::ComposeSendWidget(QMessageService* service, QWidget* parent)
 :
 QWidget(parent),
 m_layoutStack(0),
 m_service(service),
 m_accountsWidget(0),
 m_toEdit(0),
 m_ccEdit(0),
 m_ccLabel(0),
 m_bccEdit(0),
 m_bccLabel(0),
 m_subjectEdit(0),
 m_subjectLabel(0),
 m_bodyEdit(0),
 m_attachmentList(0),
 m_attachmentsAction(0),
 m_sendAsHTMLAction(0)
 {
     setupUi();
 }

 static void notifyResult(bool result, const QString& description)
 {
 #ifndef _WIN32_WCE
     if(result) QMessageBox::information(0,description,"Succeeded!");
     else QMessageBox::critical(0,description,"Failed.");
 #else
     Q_UNUSED(result);
     Q_UNUSED(description);
 #endif
 }

 void ComposeSendWidget::composeButtonClicked()
 {
     QMessage message(constructQMessage());
     m_service->compose(message);
 }

 void ComposeSendWidget::sendButtonClicked()
 {
     bool asHtml = (sender() == m_sendAsHTMLAction);
     QMessage message(constructQMessage(asHtml));
     notifyResult(m_service->send(message),"Send message");
 }

 void ComposeSendWidget::addAttachmentButtonClicked()
 {
     QStringList filenames = QFileDialog::getOpenFileNames(this,tr("Select attachments"));
     m_attachmentList->addAttachments(filenames);
 }

 void ComposeSendWidget::accountChanged()
 {
     QMessageAccount currentAccount(m_accountsWidget->currentAccount());

     bool isSmsAccount = (currentAccount.messageTypes() & QMessage::Sms) > 0;

     foreach(QWidget* emailSpecificWidget , QList<QWidget*>() << m_bccEdit << m_bccLabel <<
                                                                 m_ccEdit <<  m_ccLabel <<
                                                                 m_subjectEdit << m_subjectLabel) {
         emailSpecificWidget->setVisible(!isSmsAccount);
     }

     m_attachmentsAction->setEnabled(!isSmsAccount);
     m_sendAsHTMLAction->setEnabled(!isSmsAccount);
 }

 void ComposeSendWidget::setupUi()
 {
     QGridLayout* gl = new QGridLayout(this);

     QLabel* accountLabel = new QLabel("Account:",this);
     gl->addWidget(accountLabel,0,0);

     m_accountsWidget = new AccountsWidget(this);
     gl->addWidget(m_accountsWidget,0,1);

     connect(m_accountsWidget,SIGNAL(accountChanged()),this,SLOT(accountChanged()));

     QLabel* toLabel = new QLabel("To:",this);
     gl->addWidget(toLabel,1,0);

     m_toEdit = new QLineEdit(this);
     gl->addWidget(m_toEdit,1,1);

     m_ccLabel = new QLabel("Cc:",this);
     gl->addWidget(m_ccLabel,2,0);

     m_ccEdit = new QLineEdit(this);
     gl->addWidget(m_ccEdit,2,1);

     m_bccLabel = new QLabel("Bcc",this);
     gl->addWidget(m_bccLabel,3,0);

     m_bccEdit = new QLineEdit(this);
     gl->addWidget(m_bccEdit,3,1);

     m_subjectLabel = new QLabel("Subject:",this);
     gl->addWidget(m_subjectLabel,4,0);

     m_subjectEdit = new QLineEdit(this);
     gl->addWidget(m_subjectEdit,4,1);

     m_bodyEdit = new QTextEdit(this);
     gl->addWidget(m_bodyEdit,5,0,1,2);

     m_attachmentList = new AttachmentListWidget(this);
     gl->addWidget(m_attachmentList,6,0,1,2);
     m_attachmentList->hide();

     QAction* composeAction = new QAction("Compose",this);
     connect(composeAction,SIGNAL(triggered()),this,SLOT(composeButtonClicked()));
     addAction(composeAction);

     QAction* sendAction = new QAction("Send",this);
     connect(sendAction,SIGNAL(triggered()),this,SLOT(sendButtonClicked()));
     addAction(sendAction);

     m_sendAsHTMLAction = new QAction("Send as HTML",this);
     connect(m_sendAsHTMLAction,SIGNAL(triggered()),this,SLOT(sendButtonClicked()));
     addAction(m_sendAsHTMLAction);

     QAction* separator = new QAction(this);
     separator->setSeparator(true);
     addAction(separator);

     m_attachmentsAction = new QAction("Add attachment",this);
     connect(m_attachmentsAction,SIGNAL(triggered()),this,SLOT(addAttachmentButtonClicked()));
     addAction(m_attachmentsAction);
 }

 QMessage ComposeSendWidget::constructQMessage(bool asHtml) const
 {
     QMessage message;

     if(m_accountsWidget->isEmpty())
     {
         QMessageBox::critical(const_cast<ComposeSendWidget*>(this),"No Accounts","Cannot send a message without any available accounts");
         return message;
     }

     QMessageAccountId selectedAccountId = m_accountsWidget->currentAccount();
     QMessageAccount selectedAccount(selectedAccountId);

     bool composingSms = (selectedAccount.messageTypes() & QMessage::Sms) > 0;

     QMessageAddressList toList;
     QMessageAddressList ccList;
     QMessageAddressList bccList;

     QMessageAddress::Type addressType = QMessageAddress::Email;
     if(composingSms)
     {
         addressType = QMessageAddress::Phone;
         message.setType(QMessage::Sms);
     }

     foreach(QString s, m_toEdit->text().split(QRegExp("\\s"),QString::SkipEmptyParts))
         toList.append(QMessageAddress(addressType, s));
     message.setTo(toList);

     if(!composingSms)
     {
         foreach(QString s, m_ccEdit->text().split(QRegExp("\\s"),QString::SkipEmptyParts))
             ccList.append(QMessageAddress(QMessageAddress::Email, s));
         message.setCc(ccList);

         foreach(QString s, m_bccEdit->text().split(QRegExp("\\s"),QString::SkipEmptyParts))
             bccList.append(QMessageAddress(QMessageAddress::Email, s));
         message.setBcc(bccList);
         message.setSubject(m_subjectEdit->text());

         message.setType(QMessage::Email);

         message.appendAttachments(m_attachmentList->attachments());
     }

     message.setParentAccountId(selectedAccountId);

     if(!composingSms && asHtml) {
         //create html body
         QString htmlBody("<html><head><title></title></head><body><h2 align=center>%1</h2><hr>%2</body></html>");
         message.setBody(htmlBody.arg(message.subject()).arg(m_bodyEdit->toPlainText()),"text/html");
     }
     else
         message.setBody(m_bodyEdit->toPlainText());

     return message;
 }

 class MessageViewWidget : public QWidget
 {
     Q_OBJECT

     static const unsigned int LoadTimeLimit = 20; //seconds

     static QString changeBetweenBodyAndAttachmentsLinkURL()
     {
         static const QString changeViewUrl("MessageViewWidget://changeview");
         return changeViewUrl;
     };

     static QString downloadAttachmentLinkURL()
     {
         static const QString downloadAttachmentUrl("MessageViewWidget://downloadattachment");
         return downloadAttachmentUrl;
     };

     static QString openAttachmentLinkURL()
     {
         static const QString downloadAttachmentUrl("MessageViewWidget://openattachment");
         return downloadAttachmentUrl;
     };

     static QString downloadLinkURL()
     {
         static const QString url("MessageViewWidget://download");
         return url;
     };

 public:
     MessageViewWidget(QWidget* parent = 0);
     ~MessageViewWidget();

     QMessageId viewing() const;

 signals:
     void bodyShown();
     void attachmentsShown();
     void downloadStarted();
     void downloadFinished();
     void downloadFailed();

 public slots:
     void view(const QMessageId& messageId);
     void showBodyOrAttachments();
     bool retrieveBody();
     bool retrieveAttachment(QMessageContentContainerId attachmentId);
     bool cancelRetrieve();
     bool openAttachment(QMessageContentContainerId attachmentId);

 protected:
     void showEvent(QShowEvent* e);
     void hideEvent(QHideEvent* e);

 private slots:
     void stateChanged(QMessageService::State s);
     void loadTimeout();
     void linkClicked(const QUrl&);
     void messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet& filterSet);
     void messageRemoved(const QMessageId&, const QMessageManager::NotificationFilterIdSet& filterSet);

 private:
     enum State { Unloaded , Loaded, Loading, LoadFailed, LoadCanceled };
     void setupUi();
     void updateState();
     void loadMessage();
     void resetService();

 public:
     bool m_showAttachmentsActivated;

 private:
     QStackedLayout* m_layoutStack;
     QLabel* m_statusLabel;
     QMessageService* m_service;
     QLineEdit* m_fromLabel;
     QLineEdit* m_subjectLabel;
     QTextBrowser* m_messageBrowser;
     QMessageId m_messageId;
     State m_state;
     QTimer m_loadTimer;
     QMessageManager::NotificationFilterId m_storeFilterId;
     QMessageManager m_manager;
 };

 MessageViewWidget::MessageViewWidget(QWidget* parent)
 :
 QWidget(parent),
 m_layoutStack(0),
 m_statusLabel(0),
 m_service(new QMessageService(this)),
 m_messageBrowser(0),
 m_state(Unloaded),
 m_showAttachmentsActivated(false)
 {
     setupUi();
     resetService();
     connect(&m_loadTimer,SIGNAL(timeout()),this,SLOT(loadTimeout()));
     connect(&m_manager, SIGNAL(messageUpdated(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)),
         this,SLOT(messageUpdated(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)));
     connect(&m_manager, SIGNAL(messageRemoved(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)),
         this,SLOT(messageRemoved(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)));
     m_storeFilterId = m_manager.registerNotificationFilter(QMessageFilter());
 }

 MessageViewWidget::~MessageViewWidget()
 {
     m_manager.unregisterNotificationFilter(m_storeFilterId);
 }

 void MessageViewWidget::view(const QMessageId& messageId)
 {
     m_messageId = messageId;
     m_state = m_messageId.isValid() ? Loaded : Unloaded;

     updateState();
 }

 void MessageViewWidget::showBodyOrAttachments()
 {
     m_showAttachmentsActivated = !m_showAttachmentsActivated;
     if (m_showAttachmentsActivated) {
         emit attachmentsShown();
     } else {
         emit bodyShown();
     }
     loadMessage();
 }

 bool MessageViewWidget::retrieveBody()
 {
     if(m_state != Loading && !m_loadTimer.isActive())
     {
         m_loadTimer.setSingleShot(true);
         m_loadTimer.start(LoadTimeLimit * 1000);
         m_state = Unloaded;

         return m_service->retrieveBody(m_messageId);
     }

     return false;
 }


 bool MessageViewWidget::retrieveAttachment(QMessageContentContainerId attachmentId)
 {
     if (m_state != Loading && !m_loadTimer.isActive())
     {
         m_loadTimer.setSingleShot(true);
         m_loadTimer.start(LoadTimeLimit * 1000);
         m_state = Unloaded;

         return m_service->retrieve(m_messageId, attachmentId);
     }

     return false;
 }


 bool MessageViewWidget::cancelRetrieve()
 {
     m_service->cancel();
     m_state = LoadCanceled;
     updateState();

     return true;
 }


 bool MessageViewWidget::openAttachment(QMessageContentContainerId attachmentId)
 {
     QMessage message(m_messageId);
     QMessageContentContainer attachment(message.find(attachmentId));
     QString tempFolder = QDesktopServices::storageLocation(QDesktopServices::TempLocation);
     QString filePath = tempFolder+"/"+attachment.suggestedFileName();
     QFile file(filePath);
     if (!file.open(QIODevice::WriteOnly))
         return false;
     QByteArray content = attachment.content();
     file.write(content);
     file.close();

     QDesktopServices::openUrl(QUrl(QString("file:///")+filePath));

     return true;
 }


 void MessageViewWidget::showEvent(QShowEvent* e)
 {
     updateState();
     QWidget::showEvent(e);
 }

 void MessageViewWidget::hideEvent(QHideEvent* e)
 {
     if(m_state == Loading)
     {
         m_service->cancel();
         m_state = Unloaded;
     }

     QWidget::hideEvent(e);
 }

 void MessageViewWidget::stateChanged(QMessageService::State newState)
 {
     if (m_state == LoadFailed)
         return;

     if (newState == QMessageService::ActiveState) {
         m_state = Loading;
     } else if (newState == QMessageService::FinishedState) {
         m_state = (m_service->error() == QMessageManager::NoError ? Loaded : LoadFailed);
     }

     updateState();
 }

 void MessageViewWidget::loadTimeout()
 {
     qWarning() << "Load timeout";
     m_service->cancel();
     m_state = LoadFailed;
     updateState();
 }

 void MessageViewWidget::linkClicked(const QUrl& url)
 {
     QString urlString = url.toString();
     if (urlString == changeBetweenBodyAndAttachmentsLinkURL()) {
         showBodyOrAttachments();
     } else if (urlString == downloadLinkURL()) {
         retrieveBody();
     } else if (urlString.startsWith(downloadAttachmentLinkURL())) {
         retrieveAttachment(QMessageContentContainerId(urlString.remove(0,downloadAttachmentLinkURL().length()+1)));
     } else if (urlString.startsWith(openAttachmentLinkURL())) {
         openAttachment(QMessageContentContainerId(urlString.remove(0,openAttachmentLinkURL().length()+1)));
     }
 }

 void MessageViewWidget::messageUpdated(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filterSet)
 {
     if(!filterSet.contains(m_storeFilterId) || m_state == Loading || !id.isValid() || id != m_messageId)
         return;

     view(id);
 }

 void MessageViewWidget::messageRemoved(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filterSet)
 {
     Q_UNUSED(filterSet)

     if(id == m_messageId)
     {
         m_state = Unloaded;
         m_loadTimer.stop();
         m_messageId = QMessageId();
         view(QMessageId());
     }
 }

 QMessageId MessageViewWidget::viewing() const
 {
     return m_messageId;
 }

 void MessageViewWidget::setupUi()
 {
     m_layoutStack = new QStackedLayout(this);

     m_statusLabel = new QLabel(this);
     m_statusLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
     m_layoutStack->addWidget(m_statusLabel);

     m_messageBrowser = new QTextBrowser(this);
     m_messageBrowser->setOpenLinks(false);
     connect(m_messageBrowser,SIGNAL(anchorClicked(const QUrl&)),this,SLOT(linkClicked(const QUrl&)));
     m_layoutStack->addWidget(m_messageBrowser);
 }

 void MessageViewWidget::updateState()
 {
     switch(m_state)
     {
         case Unloaded:
         {
             m_messageBrowser->clear();
             m_layoutStack->setCurrentWidget(m_messageBrowser);
         } break;
         case Loading:
         {
             emit downloadStarted();
             m_statusLabel->setText("Downloading...");
             m_layoutStack->setCurrentWidget(m_statusLabel);
         } break;
         case Loaded:
         {
             emit downloadFinished();
             if(m_loadTimer.isActive())
             {
                 m_loadTimer.stop();
                 if(m_service->state() == QMessageService::ActiveState)
                     m_service->cancel();
             }

             loadMessage();
             m_layoutStack->setCurrentWidget(m_messageBrowser);
         } break;
         case LoadFailed:
         {
             emit downloadFailed();
             m_statusLabel->setText("Download failed!");
             m_layoutStack->setCurrentWidget(m_statusLabel);
         } break;
         case LoadCanceled:
         {
             emit downloadFinished();
             loadMessage();
             m_layoutStack->setCurrentWidget(m_messageBrowser);
         }
     }
 }

 void MessageViewWidget::loadMessage()
 {
     m_messageBrowser->clear();

     static const QString htmlTemplate("\
     <html>\
     <head>\
     </head>\
     <body>\
     <table border=\"0\" cellspacing=\"0\">\
         <tr><td><b>From: </b></td><td>%1</td></tr>\
         <tr><td><b>Subject: </b></td><td>%2</td></tr>\
         <tr><td><b>Date: </b></td><td>%3</td></tr>\
         %4\
     </table>\
     <hr>%5\
     <\body>\
     </html>\
     ");

     if(m_messageId.isValid())
     {
         QMessage message(m_messageId);

         QString changeViewTypeLink;
         if (m_showAttachmentsActivated) {
             QString attachments;
             QString changeViewTypeLink = QString("<tr><td></td><td><b><a href=\"%1\">Body</a></b></td></tr>")\
                                          .arg(changeBetweenBodyAndAttachmentsLinkURL());
             QMessageContentContainerIdList attachmentIds(message.attachmentIds());
             for (int i = 0; i < attachmentIds.count(); ++i) {
                 QMessageContentContainer attachment(message.find(attachmentIds[i]));
                 QString attachmentName = attachment.suggestedFileName();
                 if (attachment.isContentAvailable()) {
                     attachments += QString("<b>%1 </b><a href=\"%2\">Open</a><br>")\
                                    .arg(attachmentName)\
                                    .arg(openAttachmentLinkURL()+"/"+attachmentIds[i].toString());
                 } else {
                     attachments += QString("<b>%1 </b><a href=\"%2\">Download</a><br>")\
                                    .arg(attachmentName)\
                                    .arg(downloadAttachmentLinkURL()+"/"+attachmentIds[i].toString());
                 }
             }
             m_messageBrowser->setHtml(htmlTemplate\
                                      .arg(message.from().addressee())\
                                      .arg(message.subject())\
                                      .arg(message.receivedDate().toString())\
                                      .arg(changeViewTypeLink)\
                                      .arg(attachments));
         } else {
             QString changeViewTypeLink;
             QMessageContentContainerIdList attachmentIds(message.attachmentIds());
             if (attachmentIds.count() > 0) {
                 changeViewTypeLink = QString("<tr><td></td><td><b><a href=\"%1\">Attachments (%2)</a></b></td></tr>")\
                                      .arg(changeBetweenBodyAndAttachmentsLinkURL())\
                                      .arg(attachmentIds.count());
             }
             QMessageContentContainer bodyPart = message.find(message.bodyId());

             QString bodyText;

             //for partial message display a download link instead

             bool bodyAvailable = bodyPart.isContentAvailable();

             if (bodyAvailable)
             {
                 if (bodyPart.contentType() == "text")
                     bodyText = bodyPart.textContent();
                 else bodyText = "<Non-text content>";
             }
             else
                 bodyText = QString("<p align=\"center\"><a href=\"%1\">Download</a></p>").arg(downloadLinkURL());
             m_messageBrowser->setHtml(htmlTemplate\
                                      .arg(message.from().addressee())\
                                      .arg(message.subject())\
                                      .arg(message.receivedDate().toString())\
                                      .arg(changeViewTypeLink)\
                                      .arg(bodyText));
         }
     }
 }

 void MessageViewWidget::resetService()
 {
     if(m_service)
         m_service->deleteLater();
     m_service = new QMessageService(this);
     connect(m_service,SIGNAL(stateChanged(QMessageService::State)),this,SLOT(stateChanged(QMessageService::State)));
 }

 class RetrieveWidget : public QWidget
 {
     Q_OBJECT

 public:
     RetrieveWidget(QWidget* parent = 0);

 private slots:
     void messageSelected(const QMessageId& messageId);
     void showBodyOrAttachments();
     void retrieveAttachment();
     void openAttachment();
     void bodyShown();
     void attachmentsShown();
     void downloadStarted();
     void downloadFinished();
     void downloadFailed();

 private:
     void setupUi();

 private:
     QMessageService* m_service;
     RecentMessagesWidget* m_recentMessagesWidget;
     MessageViewWidget* m_messageViewWidget;
     QAction* m_retrieveAction;
     QAction* m_showBodyOrAttachmentsAction;
     QAction* m_retrieveAttachmentAction;
     QAction* m_openAttachmentAction;
     QAction* m_cancelRetrieveAction;
     QMap<QString, QString> m_availableAttachments;
     QMap<QString, QString> m_notAvailableAttachments;
 };

 RetrieveWidget::RetrieveWidget(QWidget* parent)
 :
 QWidget(parent),
 m_recentMessagesWidget(0),
 m_messageViewWidget(0),
 m_retrieveAction(0),
 m_showBodyOrAttachmentsAction(0),
 m_retrieveAttachmentAction(0),
 m_openAttachmentAction(0)
 {
     setupUi();
     connect(m_messageViewWidget, SIGNAL(bodyShown()), this, SLOT(bodyShown()));
     connect(m_messageViewWidget, SIGNAL(attachmentsShown()), this, SLOT(attachmentsShown()));
     connect(m_messageViewWidget, SIGNAL(downloadStarted()), this, SLOT(downloadStarted()));
     connect(m_messageViewWidget, SIGNAL(downloadFinished()), this, SLOT(downloadFinished()));
     connect(m_messageViewWidget, SIGNAL(downloadFailed()), this, SLOT(downloadFailed()));
 }

 void RetrieveWidget::messageSelected(const QMessageId& messageId)
 {
     QMessage message(messageId);

     m_availableAttachments.clear();
     m_notAvailableAttachments.clear();
     QMessageContentContainerIdList attachmentIds(message.attachmentIds());
     if (attachmentIds.count() > 0) {
         m_showBodyOrAttachmentsAction->setEnabled(true);
         bool retrieveAttachment = false;
         bool openAttachment = false;
         for (int i = 0; i < attachmentIds.count(); ++i) {
             QMessageContentContainer attachment(message.find(attachmentIds[i]));
             QString attachmentName = attachment.suggestedFileName();
             if (attachment.isContentAvailable()) {
                 openAttachment = true;
                 m_availableAttachments.insert(attachmentName, attachmentIds[i].toString());
             } else {
                 retrieveAttachment = true;
                 m_notAvailableAttachments.insert(attachmentName, attachmentIds[i].toString());
             }
         }
         if (openAttachment) {
             m_openAttachmentAction->setEnabled(true);
         } else {
             m_openAttachmentAction->setEnabled(false);
         }
         if (retrieveAttachment) {
             m_retrieveAttachmentAction->setEnabled(true);
         } else {
             m_retrieveAttachmentAction->setEnabled(false);
         }
     } else {
         m_showBodyOrAttachmentsAction->setEnabled(false);
         m_retrieveAttachmentAction->setEnabled(false);
         m_openAttachmentAction->setEnabled(false);
     }

     if (m_messageViewWidget->m_showAttachmentsActivated) {
         m_messageViewWidget->showBodyOrAttachments();
     }

     bool partialMessage = !message.find(message.bodyId()).isContentAvailable();

     m_retrieveAction->setEnabled(partialMessage && messageId.isValid());
 }

 void RetrieveWidget::showBodyOrAttachments()
 {
     m_messageViewWidget->showBodyOrAttachments();
 }

 void RetrieveWidget::bodyShown()
 {
     m_showBodyOrAttachmentsAction->setText("Show Attachments");
 }

 void RetrieveWidget::attachmentsShown()
 {
     m_showBodyOrAttachmentsAction->setText("Show Body");
 }

 void RetrieveWidget::downloadStarted()
 {
     m_showBodyOrAttachmentsAction->setEnabled(false);
     m_retrieveAttachmentAction->setEnabled(false);
     m_openAttachmentAction->setEnabled(false);
     m_cancelRetrieveAction->setEnabled(true);
 }

 void RetrieveWidget::downloadFinished()
 {
     messageSelected(m_recentMessagesWidget->currentMessage());
 }

 void RetrieveWidget::downloadFailed()
 {
     messageSelected(m_recentMessagesWidget->currentMessage());
 }

 void RetrieveWidget::retrieveAttachment()
 {
     if (m_notAvailableAttachments.count() > 1) {
         QInputDialog inputDialog;
         inputDialog.setOptions(QInputDialog::UseListViewForComboBoxItems);
         inputDialog.setComboBoxItems(m_notAvailableAttachments.keys());
         inputDialog.setComboBoxEditable(false);
         inputDialog.setLabelText("Select attachment");
         inputDialog.exec();
         if (inputDialog.result() == QDialog::Accepted) {
             m_messageViewWidget->retrieveAttachment(m_notAvailableAttachments.value(inputDialog.textValue()));
         }
     } else {
         m_messageViewWidget->retrieveAttachment(m_notAvailableAttachments.values().first());
     }
 }

 void RetrieveWidget::openAttachment()
 {
     if (m_availableAttachments.count() > 1) {
         QInputDialog inputDialog;
         inputDialog.setOptions(QInputDialog::UseListViewForComboBoxItems);
         inputDialog.setComboBoxItems(m_availableAttachments.keys());
         inputDialog.setComboBoxEditable(false);
         inputDialog.setLabelText("Select attachment");
         inputDialog.exec();
         if (inputDialog.result() == QDialog::Accepted) {
             m_messageViewWidget->openAttachment(m_availableAttachments.value(inputDialog.textValue()));
         }
     } else {
         m_messageViewWidget->openAttachment(m_availableAttachments.values().first());
     }
 }

 void RetrieveWidget::setupUi()
 {
     QVBoxLayout* l = new QVBoxLayout(this);
     l->addWidget(new QLabel(QString("Last %1 messages:").arg(RecentMessagesCount),this));

     m_recentMessagesWidget = new RecentMessagesWidget(this,RecentMessagesCount);
     l->addWidget(m_recentMessagesWidget);

     m_messageViewWidget = new MessageViewWidget(this);
     l->addWidget(m_messageViewWidget);

     m_showBodyOrAttachmentsAction = new QAction("Show Attachments",this);
     connect(m_showBodyOrAttachmentsAction,SIGNAL(triggered(bool)),this,SLOT(showBodyOrAttachments()));
     addAction(m_showBodyOrAttachmentsAction);
     m_showBodyOrAttachmentsAction->setEnabled(false);

     m_retrieveAction = new QAction("Retrieve",this);
     connect(m_retrieveAction,SIGNAL(triggered(bool)),m_messageViewWidget,SLOT(retrieveBody()));
     addAction(m_retrieveAction);
     m_retrieveAction->setEnabled(false);

     m_retrieveAttachmentAction = new QAction("Retrieve attachment",this);
     connect(m_retrieveAttachmentAction, SIGNAL(triggered(bool)), this, SLOT(retrieveAttachment()));
     addAction(m_retrieveAttachmentAction);
     m_retrieveAttachmentAction->setEnabled(false);

     m_openAttachmentAction = new QAction("Open attachment",this);
     connect(m_openAttachmentAction, SIGNAL(triggered(bool)), this, SLOT(openAttachment()));
     addAction(m_openAttachmentAction);
     m_openAttachmentAction->setEnabled(false);

     m_cancelRetrieveAction = new QAction("Cancel retrieve",this);
     connect(m_cancelRetrieveAction, SIGNAL(triggered(bool)), m_messageViewWidget, SLOT(cancelRetrieve()));
     addAction(m_cancelRetrieveAction);
     m_cancelRetrieveAction->setEnabled(false);

     connect(m_recentMessagesWidget,SIGNAL(selected(const QMessageId&)),m_messageViewWidget,SLOT(view(const QMessageId&)));
     connect(m_recentMessagesWidget,SIGNAL(selected(const QMessageId&)),this,SLOT(messageSelected(const QMessageId&)));
 }

 class ShowWidget : public QWidget
 {
     Q_OBJECT

 public:
     ShowWidget(QMessageService* service, QWidget* parent = 0);

 private slots:
     void showButtonClicked();

 private:
     void setupUi();

 private:
     QMessageService* m_service;
     RecentMessagesWidget* m_recentMessagesWidget;
 };

 ShowWidget::ShowWidget(QMessageService* service, QWidget* parent)
 :
 QWidget(parent),
 m_service(service),
 m_recentMessagesWidget(0)
 {
     setupUi();
 }

 void ShowWidget::showButtonClicked()
 {

     QMessageId id = m_recentMessagesWidget->currentMessage();

     if(id.isValid())
         m_service->show(id);
 }

 void ShowWidget::setupUi()
 {
     QVBoxLayout* vbl = new QVBoxLayout(this);

     QString labelText("Last %1 messages:");
     vbl->addWidget(new QLabel(labelText.arg(RecentMessagesCount),this));

     m_recentMessagesWidget = new RecentMessagesWidget(this,RecentMessagesCount);
     vbl->addWidget(m_recentMessagesWidget);

     QAction* showAction = new QAction("Show",this);
     connect(showAction,SIGNAL(triggered()),this,SLOT(showButtonClicked()));
     addAction(showAction);
 }

 class StoreSignalsWidget : public QWidget
 {
     Q_OBJECT

 public:
     StoreSignalsWidget(QWidget* parent = 0);

 private slots:
     void messageAdded(const QMessageId&, const QMessageManager::NotificationFilterIdSet&);
     void messageUpdated(const QMessageId&, const QMessageManager::NotificationFilterIdSet&);
     void messageRemoved(const QMessageId&, const QMessageManager::NotificationFilterIdSet&);

 private:
     void setupUi();
     void appendString(const QString& message);

 private:
     QListWidget* m_activityListWidget;
     QMessageManager::NotificationFilterId m_notificationFilterId;
     QMessageManager m_manager;
 };

 StoreSignalsWidget::StoreSignalsWidget(QWidget* parent)
 :
 QWidget(parent),
 m_activityListWidget(0)
 {
     setupUi();
 }

 void StoreSignalsWidget::messageAdded(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filterSet)
 {
     if(!filterSet.contains(m_notificationFilterId))
         return;

     QMessage message(id);

     QString msg = QString("Added: %1").arg(message.subject());
     m_activityListWidget->addItem(msg);
 }

 void StoreSignalsWidget::messageUpdated(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filterSet)
 {
     if(!filterSet.contains(m_notificationFilterId))
         return;

     QMessage message(id);

     QString msg = QString("Updated: %1").arg(message.subject());
     m_activityListWidget->addItem(msg);
 }

 void StoreSignalsWidget::messageRemoved(const QMessageId& id, const QMessageManager::NotificationFilterIdSet& filterSet)
 {
     if(!filterSet.contains(m_notificationFilterId))
         return;

     QString idString(id.toString());
     idString.truncate(10);

     QString msg = QString("Removed ID: %1 ...").arg(idString);
     m_activityListWidget->addItem(msg);
 }

 void StoreSignalsWidget::setupUi()
 {
     m_activityListWidget = new QListWidget(this);
     QVBoxLayout* l = new QVBoxLayout(this);
     l->setSpacing(0);
     l->setContentsMargins(0,0,0,0);
     l->addWidget(m_activityListWidget);

     connect(&m_manager,
             SIGNAL(messageAdded(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)),
             this,
             SLOT(messageAdded(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)));

     connect(&m_manager,
             SIGNAL(messageRemoved(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)),
             this,
             SLOT(messageRemoved(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)));

     connect(&m_manager,
             SIGNAL(messageUpdated(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)),
             this,
             SLOT(messageUpdated(const QMessageId&,const QMessageManager::NotificationFilterIdSet&)));

     m_notificationFilterId = m_manager.registerNotificationFilter(QMessageFilter());

     QAction* clearAction = new QAction("Clear",this);
     connect(clearAction,SIGNAL(triggered(bool)),m_activityListWidget,SLOT(clear()));
     addAction(clearAction);
 }

 MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f)
 :
 QMainWindow(parent,f),
 m_tabWidget(0)
 {

     m_service = new QMessageService(this);

     connect(m_service,SIGNAL(stateChanged(QMessageService::State)),
             this,SLOT(serviceStateChanged(QMessageService::State)));

     //example widgets

     m_widgetStack = new QStackedWidget(this);
     setCentralWidget(m_widgetStack);

     foreach(QWidget* exampleWidget, QWidgetList() << new ComposeSendWidget(m_service,this)
                                                   << new ShowWidget(m_service,this)
                                                   << new RetrieveWidget(this)
                                                   << new StoreSignalsWidget(this)) {

         m_widgetStack->addWidget(exampleWidget);
 #ifdef _WIN32_WCE
         exampleWidget->installEventFilter(this);
 #endif
     }

     //main menu
 #ifndef _WIN32_WCE
     QMenu* fileMenu = new QMenu("File",this);
 #endif

     int index = 0;
     foreach(QAction* viewAction, QList<QAction*>() << new QAction("Compose\\Send",this)
                                                    << new QAction("Show",this)
                                                    << new QAction("Retrieve/Query",this)
                                                    << new QAction("Store Signals",this))
     {
         connect(viewAction,SIGNAL(triggered()),this,SLOT(viewSelected()));
 #ifndef _WIN32_WCE
         fileMenu->addAction(viewAction);
 #else
         menuBar()->addAction(viewAction);
 #endif
         viewAction->setData(index);
         index++;
     }
 #ifndef _WIN32_WCE
     fileMenu->addSeparator();
 #else
     menuBar()->addSeparator();
 #endif

     QAction* quitAction = new QAction("Quit",this);
     connect(quitAction,SIGNAL(triggered()),qApp,SLOT(quit()));
 #ifndef _WIN32_WCE
     fileMenu->addAction(quitAction);
     menuBar()->addMenu(fileMenu);
 #else
     menuBar()->addAction(quitAction);
 #endif

     QTimer::singleShot(0,this,SLOT(viewSelected()));

     //window properties

     setWindowTitle(WindowTitle);
     resize(WindowGeometry);

 }

 #ifdef _WIN32_WCE
 bool MainWindow::eventFilter(QObject* source, QEvent* e)
 {
     bool actionChanged = (m_widgetStack->currentWidget() == source) && e->type() == QEvent::ActionChanged;
     if(actionChanged)
         viewSelected(); //update the menu items
     return false;
 }
 #endif

 void MainWindow::serviceStateChanged(QMessageService::State newState)
 {
     if ((newState == QMessageService::FinishedState) && (m_service->error() != QMessageManager::NoError))
         QMessageBox::critical(this,"Error","One or more service actions failed");
 }

 void MainWindow::viewSelected()
 {
     static QMenu* actionMenu = 0;

     if(!actionMenu)
     {
         actionMenu = new QMenu("Action",this);
 #ifndef _WIN32_WCE
         menuBar()->addMenu(actionMenu);
 #endif
     }
     QAction* senderAction = qobject_cast<QAction*>(sender());
     if(senderAction)
         m_widgetStack->setCurrentIndex(senderAction->data().toInt());

     bool currentViewHasActions = m_widgetStack->currentWidget() && !m_widgetStack->currentWidget()->actions().isEmpty();
     actionMenu->clear();
     if(currentViewHasActions)
     {
         foreach(QAction* a, m_widgetStack->currentWidget()->actions())
             actionMenu->addAction(a);
     }
 #ifdef _WIN32_WCE
     static QAction* leftSoftButton = new QAction("Action",this);
     leftSoftButton->setMenu(actionMenu);
     menuBar()->setDefaultAction(leftSoftButton);
 #endif
 }

 #include <mainwindow.moc>