QString ImapPartAttachmentItem::tooltip() const
{
    Imap::Mailbox::TreeItemPart *part = partPtr();
    if (!part)
        return QString();
    return MessageComposer::tr("%1, %2").arg(part->mimeType(), Imap::Mailbox::PrettySize::prettySize(part->octets()));
}
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;
}