void MessageView::deleteLabelAction(const QString &tag) { if (!message.isValid()) return; Imap::Mailbox::Model *model = dynamic_cast<Imap::Mailbox::Model *>(const_cast<QAbstractItemModel *>(message.model())); model->setMessageFlags(QModelIndexList() << message, tag, Imap::Mailbox::FLAG_REMOVE); }
void MessageView::markAsRead() { if (!message.isValid()) return; Imap::Mailbox::Model *model = const_cast<Imap::Mailbox::Model *>(dynamic_cast<const Imap::Mailbox::Model *>(message.model())); Q_ASSERT(model); if (!model->isNetworkAvailable()) return; if (!message.data(Imap::Mailbox::RoleMessageIsMarkedRead).toBool()) model->markMessagesRead(QModelIndexList() << message, Imap::Mailbox::FLAG_ADD); }
/** @short Instruct the Model that the data it has cached for a particular message is no longer needed */ void MessageDownloader::slotFreeProcessedMessages() { Q_FOREACH(const QPersistentModelIndex &index, m_messagesToBeFreed) { if (!index.isValid()) continue; // The const_cast should be safe here -- this action is certainly not going to invalidate the index, // and even the releaseMessageData() won't (directly) touch its members anyway... Imap::Mailbox::Model *model = qobject_cast<Imap::Mailbox::Model*>(const_cast<QAbstractItemModel*>(index.model())); Q_ASSERT(model); #ifdef DEBUG_PENDING_MESSAGES qDebug() << "Freeing memory for" << index.parent().parent().data(Imap::Mailbox::RoleMailboxName).toString() << "UID" << index.data(Imap::Mailbox::RoleMessageUid).toUInt(); #endif model->releaseMessageData(index); } m_messagesToBeFreed.clear(); }
void MessageView::setMessage(const QModelIndex &index) { // first, let's get a real model QModelIndex messageIndex; const Imap::Mailbox::Model *constModel = 0; Imap::Mailbox::TreeItem *item = Imap::Mailbox::Model::realTreeItem(index, &constModel, &messageIndex); Q_ASSERT(item); // Make sure it's a message Q_ASSERT(messageIndex.isValid()); Imap::Mailbox::Model *realModel = const_cast<Imap::Mailbox::Model *>(constModel); Q_ASSERT(realModel); // The data might be available from the local cache, so let's try to save a possible roundtrip here item->fetch(realModel); if (!messageIndex.data(Imap::Mailbox::RoleIsFetched).toBool()) { // This happens when the message placeholder is already available in the GUI, but the actual message data haven't been // loaded yet. This is especially common with the threading model. // Note that the data might be already available in the cache, it's just that it isn't in the mailbox tree yet. setEmpty(); connect(realModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(handleDataChanged(QModelIndex,QModelIndex))); message = messageIndex; return; } QModelIndex rootPartIndex = messageIndex.child(0, 0); headerSection->show(); if (message != messageIndex) { emptyView->hide(); layout->removeWidget(viewer); if (viewer != emptyView) { viewer->setParent(0); viewer->deleteLater(); } message = messageIndex; netAccess->setExternalsEnabled(false); externalElements->hide(); netAccess->setModelMessage(message); m_loadingItems.clear(); m_loadingSpinner->stop(); PartWidgetFactory::PartLoadingOptions loadingMode; if (m_settings->value(Common::SettingsNames::guiPreferPlaintextRendering, QVariant(true)).toBool()) loadingMode |= PartWidgetFactory::PART_PREFER_PLAINTEXT_OVER_HTML; viewer = factory->create(rootPartIndex, 0, loadingMode); viewer->setParent(this); layout->addWidget(viewer); viewer->show(); m_envelope->setMessage(message); tags->show(); tags->setTagList(messageIndex.data(Imap::Mailbox::RoleMessageFlags).toStringList()); disconnect(this, SLOT(handleDataChanged(QModelIndex,QModelIndex))); connect(realModel, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(handleDataChanged(QModelIndex,QModelIndex))); emit messageChanged(); // We want to propagate the QWheelEvent to upper layers viewer->installEventFilter(this); } if (realModel->isNetworkAvailable()) markAsReadTimer->start(200); // FIXME: make this configurable }
QWidget *PartWidgetFactory::create(const QModelIndex &partIndex, int recursionDepth, const PartLoadingMode loadingMode) { using namespace Imap::Mailbox; Q_ASSERT(partIndex.isValid()); if (recursionDepth > 1000) { return new QLabel(tr("This message contains too deep nesting of MIME message parts.\n" "To prevent stack exhaustion and your head from exploding, only\n" "the top-most thousand items or so are shown."), 0); } bool userPrefersPlaintext = QSettings().value(Common::SettingsNames::guiPreferPlaintextRendering, QVariant(true)).toBool(); QString mimeType = partIndex.data(Imap::Mailbox::RolePartMimeType).toString(); if (mimeType.startsWith(QLatin1String("multipart/"))) { // it's a compound part if (mimeType == QLatin1String("multipart/alternative")) { return new MultipartAlternativeWidget(0, this, partIndex, recursionDepth, userPrefersPlaintext ? QLatin1String("text/plain") : QLatin1String("text/html")); } else if (mimeType == QLatin1String("multipart/signed")) { return new MultipartSignedWidget(0, this, partIndex, recursionDepth); } else if (mimeType == QLatin1String("multipart/related")) { // The purpose of this section is to find a text/html e-mail, along with its associated body parts, and hide // everything else than the HTML widget. // At this point, it might be interesting to somehow respect the user's preference about using text/plain // instead of text/html. However, things are a bit complicated; the widget used at this point really wants // to either show just a single part or alternatively all of them in a sequence. // Furthermore, if someone sends a text/plain and a text/html together inside a multipart/related, they're // just wrong. // Let's see if we know what the root part is QModelIndex mainPartIndex; QVariant mainPartCID = partIndex.data(RolePartMultipartRelatedMainCid); if (mainPartCID.isValid()) { const Imap::Mailbox::Model *constModel = 0; Imap::Mailbox::TreeItemPart *part = dynamic_cast<Imap::Mailbox::TreeItemPart *>(Imap::Mailbox::Model::realTreeItem(partIndex, &constModel)); Imap::Mailbox::Model *model = const_cast<Imap::Mailbox::Model *>(constModel); Imap::Mailbox::TreeItemPart *mainPartPtr = Imap::Network::MsgPartNetAccessManager::cidToPart(mainPartCID.toByteArray(), model, part); if (mainPartPtr) { mainPartIndex = mainPartPtr->toIndex(model); } } if (!mainPartIndex.isValid()) { // The Content-Type-based start parameter was not terribly useful. Let's find the HTML part manually. QModelIndex candidate = partIndex.child(0, 0); while (candidate.isValid()) { if (candidate.data(RolePartMimeType).toString() == QLatin1String("text/html")) { mainPartIndex = candidate; break; } candidate = candidate.sibling(candidate.row() + 1, 0); } } if (mainPartIndex.isValid()) { if (mainPartIndex.data(RolePartMimeType).toString() == QLatin1String("text/html")) { return PartWidgetFactory::create(mainPartIndex, recursionDepth+1); } else { // Sorry, but anything else than text/html is by definition suspicious here. Better than picking some random // choice, let's just show everything. return new GenericMultipartWidget(0, this, partIndex, recursionDepth); } } else { // The RFC2387's wording is clear that in absence of an explicit START argument, the first part is the starting one. // On the other hand, I've seen real-world messages whose first part is some utter garbage (an image sent as // application/octet-stream, for example) and some *other* part is an HTML text. In that case (and if we somehow // failed to pick the HTML part by a heuristic), it's better to show everything. return new GenericMultipartWidget(0, this, partIndex, recursionDepth); } } else { return new GenericMultipartWidget(0, this, partIndex, recursionDepth); } } else if (mimeType == QLatin1String("message/rfc822")) { return new Message822Widget(0, this, partIndex, recursionDepth); } else { QStringList allowedMimeTypes; allowedMimeTypes << "text/html" << "text/plain" << "image/jpeg" << "image/jpg" << "image/pjpeg" << "image/png" << "image/gif"; // The problem is that some nasty MUAs (hint hint Thunderbird) would // happily attach a .tar.gz and call it "inline" bool showInline = partIndex.data(Imap::Mailbox::RolePartBodyDisposition).toByteArray().toLower() != "attachment" && allowedMimeTypes.contains(mimeType); if (showInline) { const Imap::Mailbox::Model *constModel = 0; Imap::Mailbox::TreeItemPart *part = dynamic_cast<Imap::Mailbox::TreeItemPart *>(Imap::Mailbox::Model::realTreeItem(partIndex, &constModel)); Imap::Mailbox::Model *model = const_cast<Imap::Mailbox::Model *>(constModel); Q_ASSERT(model); Q_ASSERT(part); part->fetchFromCache(model); bool showDirectly = loadingMode == LOAD_IMMEDIATELY; if (!part->fetched()) showDirectly &= model->isNetworkOnline() || part->octets() <= ExpensiveFetchThreshold; QWidget *widget = 0; if (showDirectly) { widget = new SimplePartWidget(0, manager, partIndex, m_messageView); } else if (model->isNetworkAvailable() || part->fetched()) { widget = new LoadablePartWidget(0, manager, partIndex, m_messageView, loadingMode == LOAD_ON_SHOW && part->octets() <= ExpensiveFetchThreshold ? LoadablePartWidget::LOAD_ON_SHOW : LoadablePartWidget::LOAD_ON_CLICK); } else { widget = new QLabel(tr("Offline"), 0); } return widget; } else { return new AttachmentView(0, manager, partIndex); } } QLabel *lbl = new QLabel(mimeType, 0); return lbl; }