/*
 *   This file is part of Dianara
 *   Copyright 2012-2016  JanKusanagi JRR <jancoding@gmx.com>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .
 */

#include "publisher.h"

Publisher::Publisher(PumpController *pumpController,
                     GlobalObject *globalObject,
                     QWidget *parent) : QWidget(parent)
{
    this->pController = pumpController;
    connect(pController, SIGNAL(postPublished()),
            this, SLOT(onPublishingOk()));
    connect(pController, SIGNAL(postPublishingFailed()),
            this, SLOT(onPublishingFailed()));

    // After receiving the list of lists, update the "Lists" submenus
    connect(pController, SIGNAL(listsListReceived(QVariantList)),
            this, SLOT(updateListsMenus(QVariantList)));


    this->globalObj = globalObject;
    connect(globalObj, SIGNAL(messagingModeRequested(QString,QString,QString)),
            this, SLOT(startMessageForContact(QString,QString,QString)));
    connect(globalObj, SIGNAL(postEditRequested(QString,QString,QString,QString)),
            this, SLOT(setEditingMode(QString,QString,QString,QString)));


    this->postType = "note";

    this->editingMode = false; // False unless set from setEditingMode
                               // after clicking "Edit" in a post

    this->defaultPublicPosting = false; // initialize
    this->showCharacterCounter = false;

    this->setFocusPolicy(Qt::StrongFocus); // To keep the publisher from getting focus by accident


    this->audienceSelectorTo = new AudienceSelector(pController, "to", this);
    connect(audienceSelectorTo, SIGNAL(audienceSelected(QString,QStringList,QStringList)),
            this, SLOT(updateToCcFields(QString,QStringList,QStringList)));
    this->audienceSelectorCC = new AudienceSelector(pController, "cc", this);
    connect(audienceSelectorCC, SIGNAL(audienceSelected(QString,QStringList,QStringList)),
            this, SLOT(updateToCcFields(QString,QStringList,QStringList)));


    QString titleTooltip = "<b></b>" + tr("Setting a title helps make the "
                                          "Meanwhile feed more informative");

    QFont titleFont;
    titleFont.setPointSize(titleFont.pointSize() + 1);
    titleFont.setBold(true);
    titleLabel = new QLabel(tr("Title") + ":", this);
    titleLabel->setFont(titleFont);
    titleLabel->setToolTip(titleTooltip);

    titleLineEdit = new QLineEdit(this);
    titleLineEdit->setPlaceholderText(tr("Add a brief title for the post here "
                                         "(recommended)"));
    titleLineEdit->setFont(titleFont);
    titleLineEdit->setToolTip(titleTooltip);


    pictureLabel = new QLabel(this);
    pictureLabel->setAlignment(Qt::AlignCenter);
    pictureLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Raised);
    pictureLabel->hide();


    mediaInfoLabel = new QLabel(this);
    mediaInfoLabel->setWordWrap(true);
    mediaInfoLabel->setAlignment(Qt::AlignTop | Qt::AlignLeft);

    selectMediaButton = new QPushButton(this);
    selectMediaButton->setSizePolicy(QSizePolicy::MinimumExpanding,
                                     QSizePolicy::Maximum);
    connect(selectMediaButton, SIGNAL(clicked()),
            this, SLOT(findMediaFile()));
    selectMediaButton->hide();


    uploadProgressBar = new QProgressBar(this);

    removeMediaButton = new QPushButton(QIcon::fromTheme("edit-delete",
                                                         QIcon(":/images/button-delete.png")),
                                        tr("Remove"),
                                        this);
    removeMediaButton->setToolTip("<b></b>"
                                  + tr("Cancel the attachment, and go "
                                       "back to a regular note"));
    removeMediaButton->setSizePolicy(QSizePolicy::MinimumExpanding,
                                     QSizePolicy::Maximum);
    connect(removeMediaButton, SIGNAL(clicked()),
            this, SLOT(cancelMediaMode()));
    removeMediaButton->hide();

    // Set default pixmap and "media not set" message
    this->setEmptyMediaData();
    lastUsedDirectory = QDir::homePath();


    // Composer
    composerBox = new Composer(this->globalObj,
                               true,  // forPublisher = true
                               this);
    composerBox->setSizePolicy(QSizePolicy::Minimum,
                               QSizePolicy::MinimumExpanding);
    connect(composerBox, SIGNAL(focusReceived()),
            this, SLOT(setFullMode()));
    connect(composerBox, SIGNAL(editingFinished()),
            this, SLOT(sendPost()));
    connect(composerBox, SIGNAL(editingCancelled()),
            this, SLOT(setMinimumMode()));
    connect(composerBox, SIGNAL(nickInserted(QString,QString,QString)),
            this, SLOT(addNickToRecipients(QString,QString,QString)));
    connect(composerBox, SIGNAL(textChanged()),
            this, SLOT(updateCharacterCounter()));

    // Pressing Enter in title goes to message body
    connect(titleLineEdit, SIGNAL(returnPressed()),
            composerBox, SLOT(setFocus()));

    // Likewise, pressing UP at the start of the body goes to title
    connect(composerBox, SIGNAL(focusTitleRequested()),
            titleLineEdit, SLOT(setFocus()));


    // Add formatting button exported from Composer
    this->toolsButton = composerBox->getToolsButton();


    // To... menu
    toPublicAction = new QAction(tr("Public"), this);
    toPublicAction->setCheckable(true);
    connect(toPublicAction, SIGNAL(toggled(bool)),
            this, SLOT(setToPublic(bool)));

    toFollowersAction = new QAction(tr("Followers"), this);
    toFollowersAction->setCheckable(true);
    connect(toFollowersAction, SIGNAL(toggled(bool)),
            this, SLOT(setToFollowers(bool)));

    toSelectorListsMenu = new QMenu(tr("Lists"), this);
    toSelectorListsMenu->setDisabled(true); // Disabled until lists are received, if any
    connect(toSelectorListsMenu, SIGNAL(triggered(QAction*)),
            this, SLOT(updateToListsFields(QAction*)));

    toSelectorMenu = new QMenu("to-menu", this);
    toSelectorMenu->addAction(toPublicAction);
    toSelectorMenu->addAction(toFollowersAction);
    toSelectorMenu->addMenu(toSelectorListsMenu);
    toSelectorMenu->addSeparator();
    toSelectorMenu->addAction(tr("People..."),
                              audienceSelectorTo, SLOT(show()));

    toSelectorButton = new QPushButton(QIcon::fromTheme("system-users",
                                                        QIcon(":/images/no-avatar.png")),
                                       tr("To..."),
                                       this);
    toSelectorButton->setToolTip("<b></b>"
                                 + tr("Select who will see this post"));
    toSelectorButton->setMenu(toSelectorMenu);



    // Cc... menu
    ccPublicAction = new QAction(tr("Public"), this);
    ccPublicAction->setCheckable(true);
    connect(ccPublicAction, SIGNAL(toggled(bool)),
            this, SLOT(setCCPublic(bool)));

    ccFollowersAction = new QAction(tr("Followers"), this);
    ccFollowersAction->setCheckable(true);
    connect(ccFollowersAction, SIGNAL(toggled(bool)),
            this, SLOT(setCCFollowers(bool)));

    ccSelectorListsMenu = new QMenu(tr("Lists"), this);
    ccSelectorListsMenu->setDisabled(true); // Disabled until lists are received, if any
    connect(ccSelectorListsMenu, SIGNAL(triggered(QAction*)),
            this, SLOT(updateCcListsFields(QAction*)));

    ccSelectorMenu = new QMenu("cc-menu", this);
    ccSelectorMenu->addAction(ccPublicAction);
    ccSelectorMenu->addAction(ccFollowersAction);
    ccSelectorMenu->addMenu(ccSelectorListsMenu);
    ccSelectorMenu->addSeparator();
    ccSelectorMenu->addAction(tr("People..."),
                              audienceSelectorCC, SLOT(show()));

    ccSelectorButton = new QPushButton(QIcon::fromTheme("system-users",
                                                        QIcon(":/images/no-avatar.png")),
                                       tr("Cc..."),
                                       this);
    ccSelectorButton->setToolTip("<b></b>"
                                 + tr("Select who will get a copy of this post"));
    ccSelectorButton->setMenu(ccSelectorMenu);


    QFont audienceLabelsFont; // "To" column will be normal, "Cc" will be italic
    audienceLabelsFont.setPointSize(audienceLabelsFont.pointSize() - 1);

    // These will hold the names of the *people* selected for the To or Cc fields, if any
    toAudienceLabel = new QLabel(this);
    toAudienceLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    toAudienceLabel->setFont(audienceLabelsFont);  // Normal
    toAudienceLabel->setWordWrap(true);
    toAudienceLabel->setOpenExternalLinks(true);
    connect(toAudienceLabel, SIGNAL(linkHovered(QString)),
            this, SLOT(showHighlightedUrl(QString)));

    ccAudienceLabel = new QLabel(this);
    ccAudienceLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    audienceLabelsFont.setItalic(true);
    ccAudienceLabel->setFont(audienceLabelsFont);  // Italic
    ccAudienceLabel->setWordWrap(true);
    ccAudienceLabel->setOpenExternalLinks(true);
    connect(ccAudienceLabel, SIGNAL(linkHovered(QString)),
            this, SLOT(showHighlightedUrl(QString)));

    // And these will show "public" or "followers" current selection (in bold)
    toPublicFollowersLabel = new QLabel(this);
    toPublicFollowersLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    audienceLabelsFont.setBold(true);
    audienceLabelsFont.setItalic(false);
    toPublicFollowersLabel->setFont(audienceLabelsFont);  // Bold

    ccPublicFollowersLabel = new QLabel(this);
    ccPublicFollowersLabel->setAlignment(Qt::AlignLeft | Qt::AlignTop);
    audienceLabelsFont.setItalic(true);
    ccPublicFollowersLabel->setFont(audienceLabelsFont);  // Bold + italic


    // Menu to set the picture/audio/video modes, under an "Add..." button
    addMediaImageAction = new QAction(QIcon::fromTheme("camera-photo",
                                                       QIcon(":/images/attached-image.png")),
                                      tr("Picture"), this);
    connect(addMediaImageAction, SIGNAL(triggered()),
            this, SLOT(setPictureMode()));

    addMediaAudioAction = new QAction(QIcon::fromTheme("audio-input-microphone",
                                                       QIcon(":/images/attached-audio.png")),
                                      tr("Audio"), this);
    connect(addMediaAudioAction, SIGNAL(triggered()),
            this, SLOT(setAudioMode()));

    addMediaVideoAction = new QAction(QIcon::fromTheme("camera-web",
                                                       QIcon(":/images/attached-video.png")),
                                      tr("Video"), this);
    connect(addMediaVideoAction, SIGNAL(triggered()),
            this, SLOT(setVideoMode()));

    addMediaFileAction = new QAction(QIcon::fromTheme("application-octet-stream",
                                                      QIcon(":/images/attached-file.png")),
                                     tr("Other", "as in other kinds of files"),
                                     this);
    connect(addMediaFileAction, SIGNAL(triggered()),
            this, SLOT(setFileMode()));


    // The menu itself
    addMediaMenu = new QMenu("add-media-menu", this);
    addMediaMenu->addAction(addMediaImageAction);
    addMediaMenu->addAction(addMediaAudioAction);
    addMediaMenu->addAction(addMediaVideoAction);
    addMediaMenu->addAction(addMediaFileAction);

    // The "add..." button holding the menu
    addMediaButton = new QPushButton(QIcon::fromTheme("mail-attachment",
                                                      QIcon(":/images/list-add.png")),
                                     tr("Ad&d..."),
                                     this);
    addMediaButton->setToolTip("<b></b>"
                               + tr("Upload media, like pictures or videos"));
    addMediaButton->setMenu(addMediaMenu);


    // Character counter (optional)
    charCounterLabel = new QLabel(this);
    charCounterLabel->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
    charCounterLabel->setText("0");


    // Info label for "sending" and other status information
    statusInfoLabel = new QLabel(this);
    statusInfoLabel->setSizePolicy(QSizePolicy::Maximum,
                                   QSizePolicy::Maximum);
    statusInfoLabel->setAlignment(Qt::AlignCenter);
    statusInfoLabel->setWordWrap(true);
    audienceLabelsFont.setBold(false);
    audienceLabelsFont.setItalic(false);
    statusInfoLabel->setFont(audienceLabelsFont);


    // To send the post
    postButton = new QPushButton(QIcon::fromTheme("mail-send",
                                                  QIcon(":/images/button-post.png")),
                                 tr("Post", "verb"),
                                 this);
    postButton->setToolTip("<b></b>"
                           + tr("Hit Control+Enter to post with the keyboard"));
    connect(postButton, SIGNAL(clicked()),
            this, SLOT(sendPost()));

    cancelButton = new QPushButton(QIcon::fromTheme("dialog-cancel",
                                                    QIcon(":/images/button-cancel.png")),
                                   tr("Cancel"),
                                   this);
    cancelButton->setToolTip("<b></b>"
                             + tr("Cancel the post"));
    connect(cancelButton, SIGNAL(clicked()),
            composerBox, SLOT(cancelPost()));


    // Now the layout, starting with the Title field and "media mode" stuff
    titleLayout = new QHBoxLayout();
    titleLabel->setContentsMargins(0, 0, 0, 0);
    titleLayout->addWidget(titleLabel,    0);
    titleLayout->addSpacing(1);
    titleLayout->addWidget(titleLineEdit, 2);
    titleLayout->addSpacing(2);
    titleLayout->addWidget(toolsButton,   0);

    mediaLayout = new QGridLayout();
    mediaLayout->setVerticalSpacing(0);
    mediaLayout->setContentsMargins(0, 0, 0, 0);
    mediaLayout->addWidget(pictureLabel,        1, 0, 2, 4);
    mediaLayout->addWidget(mediaInfoLabel,      1, 4, 1, 4, Qt::AlignTop);
    mediaLayout->addWidget(selectMediaButton,   2, 4, 1, 2, Qt::AlignBottom | Qt::AlignLeft);
    mediaLayout->addWidget(uploadProgressBar,   2, 6, 1, 2, Qt::AlignBottom | Qt::AlignRight);
    mediaLayout->addWidget(removeMediaButton,   2, 6, 1, 2, Qt::AlignBottom | Qt::AlignRight);

    buttonsLayout = new QGridLayout();
    buttonsLayout->setVerticalSpacing(0);
    buttonsLayout->setContentsMargins(0, 0, 0, 0);
    buttonsLayout->addWidget(toSelectorButton,       0, 0, 1, 1, Qt::AlignLeft);
    buttonsLayout->addWidget(ccSelectorButton,       0, 1, 1, 1, Qt::AlignLeft);
    buttonsLayout->addWidget(addMediaButton,         0, 3, 1, 2, Qt::AlignCenter);
    buttonsLayout->addWidget(statusInfoLabel,        0, 5, 3, 2, Qt::AlignCenter);
    buttonsLayout->addWidget(postButton,             0, 7, 1, 1);

    // The 2 labels holding people's names, if any
    buttonsLayout->addWidget(toAudienceLabel,        1, 0, 1, 1);
    buttonsLayout->addWidget(ccAudienceLabel,        1, 1, 1, 1);

    // The 2 labels holding "public/followers", if selected
    buttonsLayout->addWidget(toPublicFollowersLabel, 2, 0, 1, 1);
    buttonsLayout->addWidget(ccPublicFollowersLabel, 2, 1, 1, 1);

    // Character counter
    buttonsLayout->addWidget(charCounterLabel,       2, 3, 1, 2, Qt::AlignCenter);

    // The "Cancel post" button
    buttonsLayout->addWidget(cancelButton,           2, 7, 1, 1);

#ifdef GROUPSUPPORT
    groupIdLabel = new QLabel("TO GROUP (ID):", this);
    buttonsLayout->addWidget(groupIdLabel,        3, 0, 1, 1);
    groupIdLineEdit = new QLineEdit(this);
    buttonsLayout->addWidget(groupIdLineEdit,     3, 1, 1, 7);
#endif


    mainLayout = new QVBoxLayout();
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);
    mainLayout->addLayout(titleLayout,   0);
    mainLayout->addLayout(mediaLayout,   0);
    mainLayout->addWidget(composerBox,   2);
    mainLayout->addLayout(buttonsLayout, 0);
    this->setLayout(mainLayout);


    this->setMinimumMode();

    qDebug() << "Publisher created";
}


Publisher::~Publisher()
{
    qDebug() << "Publisher destroyed";
}


/*
 * Set if "public" option for audience is checked as default
 *
 */
void Publisher::syncFromConfig()
{
    this->defaultPublicPosting = this->globalObj->getPublicPostsByDefault();
    this->showCharacterCounter = this->globalObj->getShowCharacterCounter();

    // Ensure status is sync'ed
    this->toSelectorMenu->actions().first()->setChecked(this->defaultPublicPosting);
}


/*
 * Set default "no photo" pixmap and "picture/audio/video not set" message
 *
 * Clear the filename and content type variables too
 */
void Publisher::setEmptyMediaData()
{
    pictureLabel->setToolTip("");

    mediaInfoLabel->clear();

    this->mediaFilename.clear();
    this->mediaContentType.clear();
}


/*
 * Set title and content from the outside, for automation (dbus interface)
 *
 */
void Publisher::setTitleAndContent(QString title, QString content)
{
    QString statusText;

    if (this->titleLineEdit->text().isEmpty()
     && this->composerBox->toPlainText().isEmpty())
    {
        this->titleLineEdit->setText(title.trimmed());
        this->composerBox->setText(content.trimmed());

        statusText = tr("Note started from another application."); // FIXME?
    }
    else
    {
        statusText = tr("Ignoring new note request from another application."); // TMP message FIXME
    }

    this->statusInfoLabel->setText(statusText);
}


void Publisher::updatePublicFollowersLabels()
{
    QString toString;
    foreach (QString listName, toListsNameStringList)
    {
        toString.append(QString::fromUtf8("\342\236\224 ") // arrow sign in front
                        + listName + "\n");
    }
    if (toPublicAction->isChecked())
    {
        toString.append("+" + tr("Public") + "\n");
    }
    if (toFollowersAction->isChecked())
    {
        toString.append("+" + tr("Followers") + "\n");
    }
    toPublicFollowersLabel->setText(toString);


    QString ccString;
    foreach (QString listName, ccListsNameStringList)
    {
        ccString.append(QString::fromUtf8("\342\236\224 ") // arrow sign
                        + listName + "\n");
    }
    if (ccPublicAction->isChecked())
    {
        ccString.append("+" + tr("Public") + "\n");
    }
    if (ccFollowersAction->isChecked())
    {
        ccString.append("+" + tr("Followers") + "\n");
    }
    ccPublicFollowersLabel->setText(ccString);
}



/*
 * Create a key:value map, listing who will receive a post, like:
 *
 * "collection" : "http://activityschema.org/collection/public"
 * "collection" : "https://pump.example/api/user/followers"
 * "group"      : "https://pump.example/api/user/group/someGroupId123"
 * "person"     : "acct:somecontact@pumpserver.example"
 *
 */
QMap<QString,QString> Publisher::getAudienceMap()
{
    QMap<QString,QString> audienceMap;

    onlyToFollowers = true; // Will be set to false if something else is enabled
    // Used to warn the user when posting only to followers having none

    // To: Public is checked
    if (toPublicAction->isChecked())
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("to|collection",
                                "http://activityschema.org/collection/public");
    }

    // To: List of individual people
    foreach (QString address, this->toAddressStringList)
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("to|person",
                                "acct:" + address);
    }

    // To: Lists
    foreach (QString address, this->toListsIdStringList)
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("to|collection",
                                address);
    }


    // To: Followers is checked
    if (toFollowersAction->isChecked())
    {
        audienceMap.insertMulti("to|collection",
                                this->pController->currentFollowersUrl());
    }


    // Cc: Public is checked
    if (ccPublicAction->isChecked())
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("cc|collection",
                                "http://activityschema.org/collection/public");
    }

    // Cc: List of individual people
    foreach (QString address, this->ccAddressStringList)
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("cc|person",
                                "acct:" + address);
    }

    // Cc: Lists
    foreach (QString address, this->ccListsIdStringList)
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("cc|collection",
                                address);
    }

#ifdef GROUPSUPPORT
    // Cc: Groups
    if (!groupIdLineEdit->text().trimmed().isEmpty())
    {
        onlyToFollowers = false;
        audienceMap.insertMulti("to|group", groupIdLineEdit->text().trimmed());
    }
#endif


    // Last check: if Cc: Followers is checked, or nothing is, add Followers
    if (ccFollowersAction->isChecked() || audienceMap.isEmpty())
    {
        // FIXME: maybe fail and warn the user instead
        audienceMap.insertMulti("cc|collection",
                                this->pController->currentFollowersUrl());
    }


    return audienceMap;
}


void Publisher::setMediaModeWidgets()
{
    this->addMediaButton->setDisabled(true);

    this->pictureLabel->show();

    this->mediaInfoLabel->show();
    this->selectMediaButton->show();
    this->removeMediaButton->show();
}


/*
 * Disable some widgets while sending a post,
 * including during media upload
 *
 */
void Publisher::toggleWidgetsWhileSending(bool widgetsEnabled)
{
    this->titleLineEdit->setEnabled(widgetsEnabled);
    this->composerBox->setEnabled(widgetsEnabled);

    this->toolsButton->setEnabled(widgetsEnabled);

    this->toSelectorButton->setEnabled(widgetsEnabled);
    this->ccSelectorButton->setEnabled(widgetsEnabled);

    // Only re-enable "add" button if this was still a simple note
    if (postType == "note")
    {
        this->addMediaButton->setEnabled(widgetsEnabled);
    }
    this->selectMediaButton->setEnabled(widgetsEnabled);
    this->removeMediaButton->setEnabled(widgetsEnabled);

    this->postButton->setEnabled(widgetsEnabled);
}


bool Publisher::isFullMode()
{
    return this->fullMode;
}



/********************************************************************/
/***************************** SLOTS ********************************/
/********************************************************************/



void Publisher::setMinimumMode()
{
    qDebug() << "setting Publisher to minimum mode";

    this->postButton->setFocus(); // Give focus to button,
                                  // in case user shared with Ctrl+Enter

    // Disable possible scrollbars
    this->composerBox->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    this->composerBox->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);

    // Disable Ctrl+Shift+V for plaintext paste, to avoid conflict with Commenters
    this->composerBox->setPlainPasteEnabled(false);


    // ~1 row
    int composerHeight = composerBox->getMessageLabelHeight();
    this->composerBox->setMinimumHeight(composerHeight);
    this->composerBox->setMaximumHeight(composerHeight);

    this->setMinimumHeight(composerHeight + 2);
    this->setMaximumHeight(composerHeight + 2);


    this->audienceSelectorTo->deletePrevious();
    this->audienceSelectorTo->resetLists();
    this->audienceSelectorCC->deletePrevious();
    this->audienceSelectorCC->resetLists();

    this->toAudienceLabel->setText("...");
    toAudienceLabel->repaint(); // Avoid a flicker-like effect later
    toAudienceLabel->clear();
    toAudienceLabel->hide();

    this->toAddressStringList.clear();
    this->toListsNameStringList.clear();
    this->toListsIdStringList.clear();

    toPublicFollowersLabel->clear();
    toPublicFollowersLabel->hide();


    this->ccAudienceLabel->setText("...");
    ccAudienceLabel->repaint();
    ccAudienceLabel->clear();
    ccAudienceLabel->hide();

    this->ccAddressStringList.clear();
    this->ccListsNameStringList.clear();
    this->ccListsIdStringList.clear();

    ccPublicFollowersLabel->clear();
    ccPublicFollowersLabel->hide();


    this->toSelectorButton->hide();
    this->ccSelectorButton->hide();

    // Check "public" if "public posts" is set in the preferences
    toPublicAction->setChecked(this->defaultPublicPosting);
    toFollowersAction->setChecked(false);

    ccPublicAction->setChecked(false);
    ccFollowersAction->setChecked(true);  // Cc: Followers by default

    // Uncheck the lists, very TMP / FIXME
    // This doesn't clear the To/Cc labels sometimes
    foreach (QAction *action, toSelectorListsMenu->actions())
    {
        action->setChecked(false);
    }
    foreach (QAction *action, ccSelectorListsMenu->actions())
    {
        action->setChecked(false);
    }


    titleLabel->hide();
    this->titleLineEdit->clear();
    titleLineEdit->hide();

    toolsButton->hide();

    this->statusInfoLabel->clear();
    statusInfoLabel->hide();
    this->addMediaButton->hide();

    this->charCounterLabel->hide();

    this->postButton->hide();
    this->cancelButton->hide();



    // Clear formatting options like bold, italic and underline
    this->composerBox->setCurrentCharFormat(QTextCharFormat());

    // Hide "media mode" controls
    this->cancelMediaMode();

    // Clear "editing mode", restore stuff
    if (editingMode)
    {
        this->editingMode = false;
        this->editingPostId.clear();

        this->postButton->setText(tr("Post",
                                     "verb")); // Button text back to "Post" as usual
        this->toSelectorButton->setEnabled(true);
        this->ccSelectorButton->setEnabled(true);
    }

    this->fullMode = false;

    // Re-enable stuff just in case the POST request never finished
    this->toggleWidgetsWhileSending(true);

#ifdef GROUPSUPPORT
    this->groupIdLabel->hide();
    this->groupIdLineEdit->hide();
    this->groupIdLineEdit->clear();
#endif
}




void Publisher::setFullMode()
{
    qDebug() << "setting Publisher to full mode";

    this->titleLabel->show();
    this->titleLineEdit->show();

    this->toolsButton->show();

    this->composerBox->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    this->composerBox->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
    this->composerBox->setMaximumHeight(2048);
    this->composerBox->hideInfoMessage();
    this->setMaximumHeight(2048); // i.e. "unlimited"

    // Re-enable ctrl+shift+V for plaintext paste (disabled to avoid conflicts)
    this->composerBox->setPlainPasteEnabled(true);

    this->toSelectorButton->show();
    this->ccSelectorButton->show();

    this->toAudienceLabel->show();
    this->toPublicFollowersLabel->show();

    this->ccAudienceLabel->show();
    this->ccPublicFollowersLabel->show();

    updatePublicFollowersLabels();

    // Avoid re-enabling the media button when re-focusing publisher, but still in media mode...
    if (addMediaButton->isHidden())
    {
        this->addMediaButton->setEnabled(true); // If it wasn't hidden, don't re-enable
    }
    this->addMediaButton->show();
    this->statusInfoLabel->show();

    this->charCounterLabel->setVisible(globalObj->getShowCharacterCounter());

    this->postButton->show();
    this->cancelButton->show();

    this->composerBox->setFocus(); // In case user used menu or shortcut
                                   // instead of clicking on it

    this->fullMode = true;

#ifdef GROUPSUPPORT
    this->groupIdLabel->show();
    this->groupIdLineEdit->show();
#endif
}




void Publisher::setPictureMode()
{
    postType = "image";

    setMediaModeWidgets();

    pictureLabel->setPixmap(QIcon::fromTheme("image-x-generic",
                                             QIcon(":/images/attached-image.png"))
                            .pixmap(200, 150)
                            .scaled(200, 150,
                                    Qt::IgnoreAspectRatio,
                                    Qt::SmoothTransformation));

    selectMediaButton->setIcon(QIcon::fromTheme("folder-image"));
    selectMediaButton->setText(tr("Select Picture..."));
    selectMediaButton->setToolTip("<b></b>"
                                  + tr("Find the picture in your folders"));

    pictureLabel->setToolTip(tr("Picture not set"));

    this->findMediaFile(); // Show the open file dialog directly
}


void Publisher::setAudioMode()
{
    postType = "audio";
    setMediaModeWidgets();

    pictureLabel->setPixmap(QIcon::fromTheme("audio-x-generic",
                                             QIcon(":/images/attached-audio.png"))
                            .pixmap(200, 150)
                            .scaled(200, 150,
                                    Qt::IgnoreAspectRatio,
                                    Qt::SmoothTransformation));

    selectMediaButton->setIcon(QIcon::fromTheme("folder-sound"));
    selectMediaButton->setText(tr("Select Audio File..."));
    selectMediaButton->setToolTip("<b></b>"
                                  + tr("Find the audio file in your folders"));

    pictureLabel->setToolTip(tr("Audio file not set"));

    this->findMediaFile(); // Show the open file dialog directly
}


void Publisher::setVideoMode()
{
    postType = "video";

    setMediaModeWidgets();

    pictureLabel->setPixmap(QIcon::fromTheme("video-x-generic",
                                             QIcon(":/images/attached-video.png"))
                            .pixmap(200, 150)
                            .scaled(200, 150,
                                    Qt::IgnoreAspectRatio,
                                    Qt::SmoothTransformation));

    selectMediaButton->setIcon(QIcon::fromTheme("folder-video"));
    selectMediaButton->setText(tr("Select Video..."));
    selectMediaButton->setToolTip("<b></b>"
                                  + tr("Find the video in your folders"));

    pictureLabel->setToolTip(tr("Video not set"));

    this->findMediaFile(); // Show the open file dialog directly
}

void Publisher::setFileMode()
{
    postType = "file";

    setMediaModeWidgets();

    pictureLabel->setPixmap(QIcon::fromTheme("application-octet-stream",
                                             QIcon(":/images/attached-file.png"))
                            .pixmap(200, 150)
                            .scaled(200, 150,
                                    Qt::IgnoreAspectRatio,
                                    Qt::SmoothTransformation));

    selectMediaButton->setIcon(QIcon::fromTheme("folder"));
    selectMediaButton->setText(tr("Select File..."));
    selectMediaButton->setToolTip("<b></b>"
                                  + tr("Find the file in your folders"));

    pictureLabel->setToolTip(tr("File not set"));

    this->findMediaFile(); // Show the open file dialog directly
}


/*
 * Remove the attachment from the publisher
 *
 */
void Publisher::cancelMediaMode()
{
    this->addMediaButton->setEnabled(true);

    this->pictureLabel->hide();
    this->mediaInfoLabel->hide();
    this->selectMediaButton->hide();
    this->removeMediaButton->hide();
    this->uploadProgressBar->hide();

    this->setEmptyMediaData();

    this->postType = "note";
}



/*
 * Set Publiser to edit mode, after user clicks on "Edit" in a post
 *
 */
void Publisher::setEditingMode(QString postId, QString postType,
                               QString postTitle, QString postText)
{
    // Prevent the "Edit" option from destroying a post currently being composed!
    if (editingMode || fullMode)
    {
        QMessageBox::warning(this, tr("Error: Already composing"),
                             tr("You can't edit a post at this time, "
                                "because a post is already being composed."));
        return;
    }

    this->editingMode = true;
    this->editingPostId = postId;
    this->postType = postType;
    setFullMode();


    // Fill in the contents of the post
    this->titleLineEdit->setText(postTitle);
    this->composerBox->setText(postText);

    // Change/disable some controls
    this->postButton->setText(tr("Update"));
    this->toSelectorButton->setDisabled(true);
    this->ccSelectorButton->setDisabled(true);
    this->addMediaButton->setDisabled(true);

    // Clear these, so it doesn't look like the audience is different than when originally posted
    toPublicAction->setChecked(false);
    toFollowersAction->setChecked(false);
    ccPublicAction->setChecked(false);
    ccFollowersAction->setChecked(false);
    this->toAudienceLabel->clear();
    this->ccAudienceLabel->clear();

    this->statusInfoLabel->setText(tr("Editing post"));
}


void Publisher::startMessageForContact(QString id, QString name, QString url)
{
    if (name.trimmed().isEmpty()) // Ensure "name" has some text in it
    {
        name = id;
    }

    if (fullMode)
    {
        QMessageBox::warning(this, tr("Error: Already composing"),
                             tr("You can't create a message for %1 at "
                                "this time, because a post is already "
                                "being composed.").arg(name));
        return;
    }

    setFullMode();

    // Unselect Public and Followers from To/Cc
    toPublicAction->setChecked(false);
    toFollowersAction->setChecked(false);
    ccPublicAction->setChecked(false);
    ccFollowersAction->setChecked(false);


    // Add user name/ID to the "To" field...
    this->audienceSelectorTo->copyToSelected(QIcon::fromTheme("user-identity",
                                                              QIcon(":/images/no-avatar.png")),
                                             QString("%1 <%2>").arg(name).arg(id),
                                             url);
    this->audienceSelectorTo->setAudience();


    // Set a default title... FIXME?
    this->titleLineEdit->setText(name + ":");
}

/*
 * Add name + id to selected recipients when auto-completing a nick
 *
 * 'name' can always be expected to have a value
 *
 */
void Publisher::addNickToRecipients(QString id, QString name, QString url)
{
    // If for some reason a name is received without an ID, do nothing
    if (id.trimmed().isEmpty())
    {
        qDebug() << "Trying to add a nick to recipients without an ID!"
                 << name << " - " << url;
        return;
    }

    /* TMP/FIXME: Until audience can be modified when editing, To/Cc buttons
     * are disabled, and so must be the nick autocompletion. Not actually
     * disabled, but nick won't be added to the audience lists, since it
     * wouldn't really do anything.
     *
     */
    if (editingMode)
    {
        qDebug() << "Publisher: Can't modify audience in EDITING mode";
        return;
    }


    this->audienceSelectorTo->copyToSelected(QIcon::fromTheme("user-identity", // FIXME
                                                              QIcon(":/images/no-avatar.png")),
                                             QString("%1 <%2>").arg(name).arg(id),
                                             url);
    this->audienceSelectorTo->setAudience();
}


/*
 * After the post is confirmed to have been received by the server
 * re-enable publisher, clear text, etc.
 *
 */
void Publisher::onPublishingOk()
{
    this->statusInfoLabel->clear();
    this->toggleWidgetsWhileSending(true);

    this->composerBox->erase();

    // Done composing message, hide buttons until we get focus again
    setMinimumMode();
}

/*
 * If there was an HTTP error while posting...
 *
 */
void Publisher::onPublishingFailed()
{
    qDebug() << "Posting failed, re-enabling Publisher";
    this->statusInfoLabel->setText(tr("Posting failed.\n\nTry again."));

    this->toggleWidgetsWhileSending(true);
    this->uploadProgressBar->hide();

    // In media mode, show the "remove" button again (hidden during upload)
    if (this->postType != "note")
    {
        this->removeMediaButton->show();
    }

    this->composerBox->setFocus();
}


/*
 * These are called when selecting Public or Followers in the menus
 *
 * When selecting "Cc: Followers", "To: Followers" gets unselected, etc.
 *
 */
void Publisher::setToPublic(bool activated)
{
    if (activated)
    {
        ccPublicAction->setChecked(false);     // Unselect "Cc: Public"
    }
    updatePublicFollowersLabels();
}

void Publisher::setToFollowers(bool activated)
{
    if (activated)
    {
        ccFollowersAction->setChecked(false);  // Unselect "Cc: Followers"
    }
    updatePublicFollowersLabels();
}

void Publisher::setCCPublic(bool activated)
{
    if (activated)
    {
        toPublicAction->setChecked(false);     // Unselect "To: Public"
    }
    updatePublicFollowersLabels();
}

void Publisher::setCCFollowers(bool activated)
{
    if (activated)
    {
        toFollowersAction->setChecked(false);  // Unselect "To: Followers"
    }
    updatePublicFollowersLabels();
}




/*
 * Receive a list of addresses for the To or Cc fields
 *
 */
void Publisher::updateToCcFields(QString selectorType,
                                 QStringList contactsList,
                                 QStringList urlsList)
{
    qDebug() << "Publisher::updateToCcFields()" << selectorType << contactsList;

    QRegExp contactRE("(.+)\\s+\\<(.+@.+)\\>", Qt::CaseInsensitive);
    contactRE.setMinimal(true);

    QString addressesString;
    QStringList addressesStringList;

    QString contactName;
    QString contactId;
    QString contactUrl;

    int urlCounter = 0;
    int urlsListLast = urlsList.size() - 1;
    foreach (QString contactString, contactsList)
    {
        contactRE.indexIn(contactString);

        contactName = contactRE.cap(1).trimmed();
        contactId = contactRE.cap(2).trimmed();
        if (contactName.isEmpty())
        {
            contactName = contactId;
        }

        contactUrl.clear();
        if (urlCounter <= urlsListLast) // Ensure stringlist position is valid
        {                               // Works even if the list is empty: 0 <= -1
            contactUrl = urlsList.at(urlCounter);
        }

        // Append to the visible names string to be displayed under the composer
        addressesString.append("<a href=\"" + contactUrl + "\">"
                               + contactName + "</a>");

        if (urlCounter < urlsListLast) // Not in the last item yet
        {
            addressesString.append(", ");
            ++urlCounter;
        }

        // Add also to the simple ID list for getAudienceMap()
        addressesStringList.append(contactId);
    }


    if (selectorType == "to")
    {
        this->toAudienceLabel->setText(addressesString);
        this->toAddressStringList = addressesStringList;
    }
    else   // "cc"
    {
        this->ccAudienceLabel->setText(addressesString);
        this->ccAddressStringList = addressesStringList;
    }

    qDebug() << "Names:" << addressesString;
    qDebug() << "Addresses:" << addressesStringList;
}



/*
 * Fill the "Lists" submenus with PumpController's lists info
 *
 *
 */
void Publisher::updateListsMenus(QVariantList listsList)
{
    // First, clear the menus
    toSelectorListsMenu->clear(); // clear() should delete the actions
    ccSelectorListsMenu->clear();

    if (listsList.length() > 0) // if there are some lists, enable the menu
    {
        toSelectorListsMenu->setEnabled(true);
        ccSelectorListsMenu->setEnabled(true);
    }

    QString listName;
    QVariant listId;
    foreach (QVariant list, listsList)
    {
        listName = list.toMap().value("displayName").toString();
        listId = list.toMap().value("id");

        QAction *toListAction = new QAction(listName, this);
        toListAction->setCheckable(true);
        toListAction->setData(listId);
        toSelectorListsMenu->addAction(toListAction);

        QAction *ccListAction = new QAction(listName, this);
        ccListAction->setCheckable(true);
        ccListAction->setData(listId);
        ccSelectorListsMenu->addAction(ccListAction);
    }
}



void Publisher::updateToListsFields(QAction *listAction)
{
    QString listName = listAction->text();
    QString listId = listAction->data().toString();

    if (listAction->isChecked())
    {
        qDebug() << "To list selected" << listName << listId;

        toListsNameStringList.append(listName);
        toListsIdStringList.append(listId);
    }
    else
    {
        qDebug() << "To list unselected" << listName;

        toListsNameStringList.removeAll(listName);
        toListsIdStringList.removeAll(listId);
    }

    this->updatePublicFollowersLabels();

    qDebug() << "Current To Lists:" << toListsNameStringList << toListsIdStringList;
}



void Publisher::updateCcListsFields(QAction *listAction)
{
    QString listName = listAction->text();
    QString listId = listAction->data().toString();

    if (listAction->isChecked())
    {
        qDebug() << "Cc list selected" << listName << listId;

        ccListsNameStringList.append(listName);
        ccListsIdStringList.append(listId);
    }
    else
    {
        qDebug() << "Cc list unselected" << listName;

        ccListsNameStringList.removeAll(listName);
        ccListsIdStringList.removeAll(listId);
    }

    this->updatePublicFollowersLabels();

    qDebug() << "Current Cc Lists:" << ccListsNameStringList << ccListsIdStringList;
}


/*
 * Show the URL of a contact in the recipients list, in the status bar
 *
 * FIXME: merge with Post::showHighlightedUrl()
 *
 */
void Publisher::showHighlightedUrl(QString url)
{
    if (!url.isEmpty())
    {
        this->pController->showTransientMessage(url);
        qDebug() << "Highlighted recipient url in publisher:" << url;
    }
    else
    {
        this->pController->showTransientMessage("");
    }
}




/*
 * Send the post (note, image, audio...) to the server
 *
 */
void Publisher::sendPost()
{
    qDebug() << "Publisher character count:" << composerBox->textCursor()
                                                .document()->characterCount();

    bool textIsEmpty;
    if (composerBox->textCursor().document()->characterCount() > 1) // kinda tmp
    {
        textIsEmpty = false;
    }
    else
    {
        textIsEmpty = true;
    }


    // If there's some text in the post, or attached media, send it
    if ( (postType == "note" && !textIsEmpty)
      || (postType != "note" && !mediaFilename.isEmpty())
      || editingMode ) // Or editing, then the other stuff doesn't matter
    {
        QString postTitle = this->titleLineEdit->text().trimmed();
        postTitle.replace("\n", " "); // Post title could have newlines if copy-pasted
        if (postTitle.length() > 120) // Limit title length to something sane
        {
            postTitle = postTitle.left(115) + " [...]";
        }

        QString cleanHtmlString = MiscHelpers::cleanupHtml(composerBox->toHtml());


        QMap<QString,QString> audienceMap = this->getAudienceMap();

        // Warn user if posting only to Followers, but having none
        if (this->onlyToFollowers // Set in getAudienceMap()
            && this->pController->currentFollowersCount() == 0
            && !editingMode)
        {
            qDebug() << "WARNING: You have no followers yet; Post to public?";

            int action = QMessageBox::warning(this,
                                 tr("Warning: You have no followers yet"),
                                 tr("You're trying to post to your followers "
                                    "only, but you don't have any followers "
                                    "yet.")
                                 + " "
                                 + tr("If you post like this, no one will be "
                                      "able to see your message.")
                                 + "\n\n"
                                 + tr("Do you want to make the post public "
                                      "instead of followers-only?")
                                 + "\n",
                                 tr("&Yes, make it public"),
                                 tr("&No, post to my followers only"),
                                 tr("&Cancel, go back to the post"),
                                 2, 2); // Cancel is default, and also activated with ESC
            if (action == 0)
            {
                this->toPublicAction->setChecked(true);  // Check To:Public
                audienceMap = this->getAudienceMap();    // Process again
                qDebug() << "Checked To:Public";
            }
            else if (action == 2)
            {
                qDebug() << "Posting aborted; back to post composer";
                return;
            }
        }

        // Don't erase just yet!! Just disable until we get "200 OK" from the server.
        this->toggleWidgetsWhileSending(false);

        if (!editingMode)
        {
            this->statusInfoLabel->setText(tr("Posting..."));

            if (postType == "note")
            {
                this->pController->postNote(audienceMap,
                                            cleanHtmlString,
                                            postTitle);
            }
            else
            {
                uploadNetworkReply =  this->pController->postMedia(audienceMap,
                                                                   cleanHtmlString,
                                                                   postTitle,
                                                                   mediaFilename,
                                                                   postType,
                                                                   mediaContentType);
                connect(uploadNetworkReply, SIGNAL(uploadProgress(qint64,qint64)),
                        this, SLOT(updateProgressBar(qint64,qint64)));
                // This will be automatically disconnected when
                // the networkReply is automatically deleted!

                // The "remove" button uses the same space as the progress bar
                removeMediaButton->hide();

                uploadProgressBar->setValue(0);
                uploadProgressBar->show();
            }
        }
        else
        {
            this->statusInfoLabel->setText(tr("Updating..."));
            this->pController->updatePost(this->editingPostId,
                                          this->postType,
                                          cleanHtmlString,
                                          postTitle);
        }
    }
    else
    {
        if (this->postType == "note")
        {
            this->statusInfoLabel->setText(tr("Post is empty."));
        }
        else // image, audio...
        {
            this->statusInfoLabel->setText(tr("File not selected."));
        }
        qDebug() << "Can't send post: text is empty, or no media";
    }
}



/*
 * Let the user find a file in his/her folders, for media upload
 *
 */
void Publisher::findMediaFile()
{
    QString findDialogTitle;
    QString mediaTypes;
    QString errorTitle;
    QString errorMessage;

    if (postType == "image")
    {
        findDialogTitle = tr("Select one image");
        mediaTypes = tr("Image files") + " (*.png *.jpg *.jpeg *.jpe"
                                         " *.gif *.mng *.webp *.svg);;";

        errorTitle =  tr("Invalid image");
        errorMessage = tr("The image format cannot be detected.\n"
                          "The extension might be wrong, like a GIF "
                          "image renamed to image.jpg or similar.");
    }
    else if (postType == "audio")
    {
        findDialogTitle = tr("Select one audio file");
        mediaTypes = tr("Audio files") + " (*.ogg *.oga *.flac *.mka"
                                         " *.mp3 *.mpga *.wav *.wma);;";

        errorTitle = tr("Invalid audio file");
        errorMessage = tr("The audio format cannot be detected.");
    }
    else if (postType == "video")
    {
        findDialogTitle = tr("Select one video file");
        mediaTypes = tr("Video files") + " (*.avi *.mpg *.mpeg *.mpe *.mpv"
                                         " *.ogm *.ogg *.mkv *.mp4 *.webm"
                                         " *.mov *.flv *.3gp *.wmv);;";

        errorTitle = tr("Invalid video file");
        errorMessage = tr("The video format cannot be detected.");
    }
    else // File
    {
        findDialogTitle = tr("Select one file");
        mediaTypes = "";

        errorTitle = tr("Invalid file");
        errorMessage = tr("The file type cannot be detected.");
    }

    QString filename;
    filename = QFileDialog::getOpenFileName(this, findDialogTitle,
                                            this->lastUsedDirectory,
                                            mediaTypes
                                            + tr("All files") + " (*)");

    if (!filename.isEmpty())
    {
        qDebug() << "Selected" << filename << "for upload";
        QFileInfo fileInfo(filename);
        this->mediaContentType = MiscHelpers::getFileMimeType(filename);

        // Temporary protection; https://github.com/e14n/pump.io/issues/1015
        if (fileInfo.size() > 10485760) // 10 MiB; this protects the servers from abuse
        {
            QString bigImageNote;
            if (postType == "image")
            {
                bigImageNote = "\n\n"
                               + tr("Since you're uploading an image, you could "
                                    "scale it down a little or save it in a "
                                    "more compressed format, like JPG.");
            }

            QMessageBox::warning(this, tr("File is too big"),
                                 tr("Dianara currently limits file uploads "
                                    "to 10 MiB per post, to prevent possible "
                                    "storage or network problems in the "
                                    "servers.")
                                 + "\n\n"
                                 + tr("This is a temporary measure, since "
                                      "the servers cannot set their "
                                      "own limits yet.")
                                 + bigImageNote
                                 + "\n\n"
                                 + tr("Sorry for the inconvenience."));
        }
        else if (!mediaContentType.isEmpty())
        {
            this->pictureLabel->setToolTip("<b></b>" // wordwrapped
                                           + filename);
            this->mediaFilename = filename;

            QString metaDataString;
            if (postType == "image")
            {
                QPixmap imagePixmap = QPixmap(filename);
                if (imagePixmap.isNull())
                {
                    QMessageBox::warning(this, errorTitle, errorMessage);
                }
                this->pictureLabel->setPixmap(imagePixmap.scaled(320, 180,  // 16:9
                                                                 Qt::KeepAspectRatio,
                                                                 Qt::SmoothTransformation));
                metaDataString = "<br><br>"
                                 "<b>" + tr("Resolution",
                                            "Image resolution (size)") + ":</b> "
                                 + MiscHelpers::resolutionString(imagePixmap.width(),
                                                                 imagePixmap.height());
            }

            this->lastUsedDirectory = fileInfo.path();
            qDebug() << "last used directory:" << lastUsedDirectory;


            mediaInfoLabel->setText(QString("<b>%1</b>"
                                            "<br>"
                                            "<b>" + tr("Type") + ":</b> %2"
                                            "<br>"
                                            "<b>" + tr("Size") + ":</b> %3")
                                    .arg(fileInfo.fileName(),
                                         mediaContentType,
                                         MiscHelpers::fileSizeString(filename))
                                    + metaDataString);

            // If there's no title, set one from the filename
            if (this->titleLineEdit->text().trimmed().isEmpty())
            {
                // But only if configured to do so
                if (this->globalObj->getUseFilenameAsTitle())
                {
                    QString titleFromFilename = fileInfo.fileName(); // .baseName() ?
                    titleFromFilename.replace("_", " ");
                    titleFromFilename.replace(".", " ");
                    this->titleLineEdit->setText(titleFromFilename);
                }
            }
        }
        else
        {
            qDebug() << "Unknown " << postType << " format; Extension is probably wrong";
            QMessageBox::warning(this, errorTitle, errorMessage);

            this->mediaContentType.clear();
            this->mediaFilename.clear();
            this->pictureLabel->setToolTip("");
        }


        this->uploadProgressBar->hide();
    }
}


void Publisher::updateProgressBar(qint64 sent, qint64 total)
{
    this->uploadProgressBar->setRange(0, total);
    this->uploadProgressBar->setValue(sent);

    this->uploadProgressBar->setToolTip(tr("%1 KiB of %2 KiB uploaded")
                                        .arg(QLocale::system()
                                             .toString(sent / 1024))
                                        .arg(QLocale::system()
                                             .toString(total / 1024)));
}


void Publisher::updateCharacterCounter()
{
    int charCount = composerBox->textCursor().document()->characterCount() - 1;
    this->charCounterLabel->setText(QString("%1")
                                    .arg(QLocale::system().toString(charCount)));
}
