#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;
#ifdef NOTHREAD
QMessageManager manager;
if(!runonce)
setIds(manager.queryAccounts());
#else
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_accountsCombo->setCurrentIndex(i);
}
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)));
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;
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) {
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;
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;
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)));
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
}
#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()));
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();
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>