/***************************** LICENSE START ***********************************

 Copyright 2015 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QtGlobal>

#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
#include <QGuiApplication>
#else
#include <QApplication>
#endif

#include <QClipboard>
#include <QDebug>
#include <QDrag>
#include <QDropEvent>
#include <QMessageBox>
#include <QObject>
#include <QPainter>
#include <QShortcut>

#include "MvQFolderViewBase.h"

#include "MvQ.h"
#include "MvQBookmarks.h"
#include "MvQContextMenu.h"
#include "MvQDesktopSettings.h"
#include "MvQFileBrowser.h"
#include "MvQFileDialog.h"
#include "FilePropertyDialog.h"
#include "MvQFolderItemProperty.h"
#include "MvQFolderModel.h"
#include "MvQIconMimeData.h"
#include "MvQIconProvider.h"
#include "MvQNewIconWidget.h"
#include "MvQMethods.h"
#include "MvQTheme.h"

#include "Command.h"
#include "Folder.h"
#include "IconClass.h"
#include "IconGroupTools.h"
#include "IconInfo.h"
#include "IconObject.h"

MvQFolderViewBase::MvQFolderViewBase(MvQFolderModel* folderModel, QWidget* view) :
    folderModel_(folderModel),
    view_(view)
{
    filterModel_ = new MvQFolderFilterModel;
    filterModel_->setSourceModel(folderModel_);
    filterModel_->setDynamicSortFilter(true);

    itemProp_ = folderModel_->itemProp();
}

MvQFolderViewBase::~MvQFolderViewBase()
{
    delete filterModel_;
}

QString MvQFolderViewBase::fullName(const QModelIndex& index, IndexType indexType)
{
    return (indexType == FilterModelIndex) ? folderModel_->fullName(filterModel_->mapToSource(index)) : folderModel_->fullName(index);
}

bool MvQFolderViewBase::isFolder(const QModelIndex& index, IndexType indexType)
{
    return (indexType == FilterModelIndex) ? folderModel_->isFolder(filterModel_->mapToSource(index)) : folderModel_->isFolder(index);
}

Folder* MvQFolderViewBase::currentFolder()
{
    return folderModel_->folder();
}

bool MvQFolderViewBase::changeFolderToParent()
{
    bool retVal = folderModel_->setFolderToParent();
    return retVal;
}

bool MvQFolderViewBase::changeFolder(const QModelIndex& index, IndexType indexType)
{
    QModelIndex folderIndex = (indexType == FilterModelIndex) ? filterModel_->mapToSource(index) : index;

    bool retVal = folderModel_->setFolder(folderIndex);
    return retVal;
}

bool MvQFolderViewBase::changeFolder(QString fullName, int iconSize)
{
    bool retVal = folderModel_->setFolder(fullName, iconSize);
    return retVal;
}

bool MvQFolderViewBase::changeFolder(Folder* folder, int iconSize)
{
    bool retVal = folderModel_->setFolder(folder, iconSize);
    return retVal;
}

IconObject* MvQFolderViewBase::itemInfo(const QModelIndex& index, IndexType indexType)
{
    QModelIndex folderIndex = (indexType == FilterModelIndex) ? filterModel_->mapToSource(index) : index;
    return folderModel_->objectFromIndex(folderIndex);
}

void MvQFolderViewBase::handleDoubleClick(const QModelIndex& index)
{
    IconObject* obj = folderModel_->objectFromIndex(filterModel_->mapToSource(index));
    if (obj && !obj->isBrokenLink()) {
        QString method = QString::fromStdString(obj->iconClass().defaultMethod()).toLower();

        if (obj->isFolder()) {
            iconCommand("open", obj);
        }
        else if (!method.isEmpty()) {
            obj->command(method.toStdString());
        }
        else {
            obj->doubleClick();
        }
    }
}

void MvQFolderViewBase::handleContextMenu(const QModelIndex& indexClicked, QModelIndexList indexLst, QPoint globalPos, QPoint widgetPos, QWidget* widget)
{
    if (!folderModel_ || !folderModel_->folder())
        return;

    MvQContextItemSet* cms = cmSet();
    if (!cms)
        return;

    // Icon actions
    if (indexClicked.isValid() && indexClicked.column() == 0)  // indexLst[0].isValid() && indexLst[0].column() == 0)
    {
        std::vector<IconObject*> objLst;
        for (int i = 0; i < indexLst.count(); i++) {
            IconObject* obj = folderModel_->objectFromIndex(filterModel_->mapToSource(indexLst[i]));
            if (obj)
                objLst.push_back(obj);
        }

        if (objLst.size() > 0) {
            QString selection;
            if (objLst.size() == 1)
                // selection=MvQContextMenu::instance()->exec(cmIconItems_,objLst[0],globalPos,widget);
                selection = MvQContextMenu::instance()->exec(cms->icon(), objLst[0], globalPos, widget);
            else
                // selection=MvQContextMenu::instance()->exec(cmMultiIconItems_,objLst,globalPos,widget);
                selection = MvQContextMenu::instance()->exec(cms->multiIcon(), objLst, globalPos, widget);

            if (!selection.isEmpty())
                handleIconCommand(objLst, selection);
        }

        /*IconObject* obj=folderModel_->objectFromIndex(filterModel_->mapToSource(index));
        if(obj)
        {
            QString selection=MvQContextMenu::instance()->exec(cmIconItems_,obj,globalPos,widget);

            if(!selection.isEmpty())
                handleIconCommand(obj,selection);
        }*/
    }

    // Desktop actions
    else {
        // QString selection=MvQContextMenu::instance()->exec(cmDesktopItems_,globalPos,widget);
        QString selection = MvQContextMenu::instance()->exec(cms->desktop(), globalPos, widget);
        if (!selection.isEmpty()) {
            handleDesktopCommand(selection, widgetPos, widget);
        }
    }
}


void MvQFolderViewBase::handleIconShortCut(QShortcut* sc, QModelIndexList indexLst)
{
    if (!sc)
        return;

    std::vector<IconObject*> objLst;
    for (int i = 0; i < indexLst.count(); i++) {
        IconObject* obj = folderModel_->objectFromIndex(filterModel_->mapToSource(indexLst[i]));
        if (obj)
            objLst.push_back(obj);
    }

    if (objLst.size() > 0) {
        QString selection = sc->property("cmd").toString();
        if (!selection.isEmpty()) {
            handleIconCommand(objLst, selection);
        }
    }
}

void MvQFolderViewBase::handleDesktopShortCut(QShortcut* sc, QPoint pos, QWidget* widget)
{
    if (!sc)
        return;

    QString selection = sc->property("cmd").toString();
    if (!selection.isEmpty())
        handleDesktopCommand(selection, pos, widget);
}

void MvQFolderViewBase::handleIconCommand(QModelIndexList indexLst, QString name)
{
    std::vector<IconObject*> objLst;
    for (int i = 0; i < indexLst.count(); i++) {
        IconObject* obj = folderModel_->objectFromIndex(filterModel_->mapToSource(indexLst[i]));
        if (obj)
            objLst.push_back(obj);
    }

    if (objLst.size() > 0) {
        handleIconCommand(objLst, name);
    }
}

void MvQFolderViewBase::handleIconCommand(const std::vector<IconObject*>& objLstIn, QString name)
{
    if (objLstIn.empty())
        return;

    // Selects the iconobjects for that the command is valid
    std::vector<IconObject*> objLst;
    for (auto it : objLstIn) {
        if (it) {
            std::set<std::string> cmdSet = it->can();
            QString canCmd = name;
            if (name.startsWith("compress_"))
                canCmd = "compress";
            else if (name.startsWith("archive_"))
                canCmd = "archive";

            if (cmdSet.find(canCmd.toStdString()) != cmdSet.end()) {
                objLst.push_back(it);
            }
        }
    }

    if (objLst.empty())
        return;

    if (IconGroupTools::can(name.toStdString())) {
        if (objLst.size() > 0) {
            QPoint refPos = itemProp_->referencePosition(objLst[0]);
            refPos += QPoint(32, 32);
            folderModel_->setPositionHint("", refPos.x(), refPos.y());
        }

        IconGroupTools::run(name.toStdString(), objLst);
    }
    else if (name == "destroy") {
        if (confirmDelete(objLst.size())) {
            for (auto it : objLst)
                it->command(name.toStdString());
        }
    }
    else if (name == "copy") {
        if (objLst.size() > 0) {
            QList<IconObject*> lst;
            for (auto it : objLst)
                lst << it;

            copyToClipboard(lst.at(0), lst);
        }
    }
    else if (name == "cut") {
        if (objLst.size() > 0) {
            QList<IconObject*> lst;
            for (auto it : objLst) {
                //(*it)->notifyCutBegin();
                lst << it;
            }

            cutToClipboard(lst.at(0), lst);
        }
    }
    else if (name == "copy_path") {
        if (objLst.size() > 0) {
            QList<IconObject*> lst;
            for (auto it : objLst) {
                lst << it;
            }

            copyPathToClipboard(lst.at(0), lst);
        }
    }
    else if (name == "empty") {
        MvQ::emptyWasteBasket();
    }
    else {
        for (auto it : objLst)
            handleIconCommand(it, name);
    }
}

void MvQFolderViewBase::handleIconCommand(IconObject* obj, QString name)
{
    if (!obj)
        return;

    // Check if the command is valid for the object
    std::set<std::string> cmdSet = obj->can();
    if (cmdSet.find(name.toStdString()) == cmdSet.end())
        return;

    // Delete icon (permanently)
    if (name == "destroy") {
        if (confirmDelete(1)) {
            obj->command(name.toStdString());
        }
    }

    // Other actions
    else if (Command::isValid(name.toStdString())) {
        if (name == "save") {
            QPoint refPos = itemProp_->referencePosition(obj);
            refPos += QPoint(32, 32);
            folderModel_->setPositionHint("", refPos.x(), refPos.y());
        }

        obj->command(name.toStdString());
    }
    else if (name == "openInWin") {
        MvQFileBrowser::openBrowser(QString::fromStdString(obj->fullName()), view_);
    }
    else if (name == "bookmark") {
        MvQBookmarks::addItem(obj);
    }
    else if (name == "copy") {
        QList<IconObject*> lst;
        lst << obj;
        copyToClipboard(obj, lst);
    }
    else if (name == "rename") {
        if (obj->renamable())
            rename(obj);
    }
    else if (name == "recheck_type") {
        if (Folder* f = obj->parent())
            f->recheckKidIconClass(obj);
    }
    else if (name == "property") {
        FilePropertyDialog d;
        d.setItem(obj);
        d.exec();
    }
    else {
        iconCommand(name, obj);
    }
}

void MvQFolderViewBase::handleDesktopCommand(QString name, QPoint pos, QWidget* widget)
{
    Folder* folder = folderModel_->folder();

    // For the detailed view the actual icon position should be ignored for desktop commands,
    // e.g. when creating a new icon. In this case we use an "undefined" position!!!
    // ignoreItemPositionForCm() tells us when we need to apply it.

    if (name == "createFolder") {
        const IconClass& kind = IconClass::find("FOLDER");
        if (ignoreItemPositionForCm())
            kind.createOne(folder);
        else
            kind.createOne(folder, pos.x(), pos.y());
    }
    else if (name == "createMacro") {
        const IconClass& kind = IconClass::find("MACRO");
        if (ignoreItemPositionForCm())
            kind.createOne(folder);
        else
            kind.createOne(folder, pos.x(), pos.y());
    }
    else if (name == "createPython") {
        const IconClass& kind = IconClass::find("PYTHON");
        if (ignoreItemPositionForCm())
            kind.createOne(folder);
        else
            kind.createOne(folder, pos.x(), pos.y());
    }
    else if (name == "createIcon") {
        MvQNewIconDialog dialog(widget);

        if (dialog.exec() == QDialog::Accepted) {
            const IconClass& kind = dialog.selected();
            QPoint refPos = itemProp_->referencePosition(QString::fromStdString(kind.name()), pos);
            if (ignoreItemPositionForCm())
                kind.createOne(folder);
            else
                kind.createOne(folder, refPos.x(), refPos.y());
            // showLastCreated();
        }
    }
    else if (name == "createLinkToFile" || name == "createLinkToFolder") {
        MvQFileDialog dialog(QString::fromStdString(folder->path().str()), QObject::tr("Create symbolic link"), widget);
        dialog.setAcceptMode(QFileDialog::AcceptOpen);

        if (name == "createLinkToFile")
            dialog.setFileMode(QFileDialog::ExistingFile);
        else
            dialog.setFileMode(QFileDialog::Directory);

        dialog.setLabelText(QFileDialog::Accept, "Ok");

        if (dialog.exec() == QDialog::Accepted) {
            QStringList lst = dialog.selectedFiles();
            if (lst.count() > 0) {
                Path result(lst[0].toStdString());
                std::string name = folder->uniqueName(result.name());
                Path path = folder->path().add(name);

                path.symlink(result);

                // The dot file for a link should not be created in the
                // link target's folder. By commenting out the line below we can prevent it!
                // path.dot().symlink(result.dot());
                if (ignoreItemPositionForCm())
                    folder->scanForNewFile(name);
                else
                    folder->scanForNewFile(name, pos.x(), pos.y());
            }
        }
    }
    else if (name == "bookmarkFolder") {
        MvQBookmarks::addItem(folder);
    }
    else if (name == "paste") {
        if (ignoreItemPositionForCm())
            fromClipboard(pos);
        else
            fromClipboard(pos);
    }
    else if (name == "recheck_type") {
        if (folder) {
            folder->recheckKidsIconClass();
        }
    }
    else {
        if (ignoreItemPositionForCm())
            desktopCommand(name, pos);
        else
            desktopCommand(name, pos);
    }
}

void MvQFolderViewBase::copyToClipboard(IconObject* obj, QList<IconObject*> lst)
{
    MvQIconMimeData* data = buildClipboardData(obj, lst, false, true);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::clipboard()->setMimeData(data);
#else
    QApplication::clipboard()->setMimeData(data);
#endif
}

void MvQFolderViewBase::cutToClipboard(IconObject* obj, QList<IconObject*> lst)
{
    MvQIconMimeData* data = buildClipboardData(obj, lst, false, false);
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QGuiApplication::clipboard()->setMimeData(data);
#else
    QApplication::clipboard()->setMimeData(data);
#endif
}

void MvQFolderViewBase::copyPathToClipboard(IconObject* obj, QList<IconObject*>)
{
    if (!obj)
        return;

    QString path = QString::fromStdString(obj->path().str());
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    QClipboard* cb = QGuiApplication::clipboard();
    cb->setText(path, QClipboard::Clipboard);
    cb->setText(path, QClipboard::Selection);
#else
    QClipboard* cb = QApplication::clipboard();
    cb->setText(path, QClipboard::Clipboard);
    cb->setText(path, QClipboard::Selection);
#endif
}

void MvQFolderViewBase::fromClipboard(QPoint pos)
{
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
    const auto* mimeData =
        qobject_cast<const MvQIconMimeData*>(QGuiApplication::clipboard()->mimeData());
#else
    const MvQIconMimeData* mimeData =
        qobject_cast<const MvQIconMimeData*>(QApplication::clipboard()->mimeData());
#endif

    // If there is data we simulate drop
    if (mimeData && !mimeData->isEmpty()) {
        if (mimeData->clipboardAction() == MvQIconMimeData::CopyAction)
            performDrop(Qt::CopyAction, mimeData, pos, false);
        else if (mimeData->clipboardAction() == MvQIconMimeData::CutAction)
            performDrop(Qt::MoveAction, mimeData, pos, false);
    }
}

bool MvQFolderViewBase::confirmDelete(int count)
{
    QString txt1, txt2;
    if (count == 1) {
        txt1 = QObject::tr("Delete icon");
        txt2 = QObject::tr("Do you really want to delete this icon? It will be <b>permanently</b> deleted from disk!");
    }
    else {
        txt1 = QObject::tr("Delete icons");
        txt2 = QObject::tr("Do you really want to delete these ") + QString::number(count) + QObject::tr(" icons? They will be <b>permanently</b> deleted from disk!");
    }

    return (QMessageBox::warning(nullptr, txt1, txt2,
                                 QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel) == QMessageBox::Yes);
}

QString MvQFolderViewBase::formatFileSize(qint64 /*size*/)
{
#if 0
if(size < 1024)
	  	return QString::number(size) + " B";
	else if(size < 1024*1024)
	  	return QString::number(size/1024) + " KB";
	else if(size < 1024*1024*1024)
	  	return QString::number(size/(1024*1024)) + " MB";
	else
	  	return QString::number(size/(1024*1024*1024)) + " GB";
#endif

    return {};
}

QString MvQFolderViewBase::formatFilePermissions(const QFile::Permissions& perm)
{
    // There is a bug in Qt 4.6.3 (it was the version used for the developments)!
    // QFileSystemModel::permissions() gives inversed results for the
    // write premissions. So the the code below had to be changed to cope
    // with it (the original code is still kept but commented out).

    return "-" +
           QString((perm.testFlag(QFile::ReadOwner)) ? "r" : "-") +
           QString((perm.testFlag(QFile::WriteOwner)) ? "-" : "w") +
           QString((perm.testFlag(QFile::ExeOwner)) ? "x" : "-") +
           QString((perm.testFlag(QFile::ReadGroup)) ? "r" : "-") +
           QString((perm.testFlag(QFile::WriteGroup)) ? "-" : "w") +
           QString((perm.testFlag(QFile::ExeGroup)) ? "x" : "-") +
           QString((perm.testFlag(QFile::ReadOther)) ? "r" : "-") +
           QString((perm.testFlag(QFile::WriteOther)) ? "-" : "w") +
           QString((perm.testFlag(QFile::ExeOther)) ? "x" : "-");

#if 0 
  	return "-" +
	       QString((perm.testFlag(QFile::ReadOwner))?"r":"-") +
	       QString((perm.testFlag(QFile::WriteOwner))?"w":"-") +
	       QString((perm.testFlag(QFile::ExeOwner))?"x":"-") +
	       QString((perm.testFlag(QFile::ReadGroup))?"r":"-") +
	       QString((perm.testFlag(QFile::WriteGroup))?"w":"-") +
	       QString((perm.testFlag(QFile::ExeGroup))?"x":"-") +
	       QString((perm.testFlag(QFile::ReadOther))?"r":"-") +
	       QString((perm.testFlag(QFile::WriteOther))?"w":"-") +
	       QString((perm.testFlag(QFile::ExeOther))?"x":"-");
#endif
}

bool MvQFolderViewBase::isAccepted(const IconClass& kind)
{
    return folderModel_->isAccepted(kind);
}

bool MvQFolderViewBase::isAccepted(IconObject* obj)
{
    const IconClass& ic = obj->iconClass();
    return isAccepted(ic);
}

int MvQFolderViewBase::getIconSize()
{
    return itemProp_->iconSize();
}

void MvQFolderViewBase::showLastCreated()
{
    QModelIndex index = filterModel_->mapFromSource(folderModel_->lastArrived());
    blink(index);
}


QPixmap MvQFolderViewBase::dragPixmapWithCount(IconObject* dragObj, int cnt, QRect& iconPix)
{
    QFont f = MvQ::findMonospaceFont();
    f.setPointSize(9);
    f.setBold(true);

    QFontMetrics fm(f);
    QRect textRect(0, 0, MvQ::textWidth(fm, QString::number(cnt)), fm.height());
    int padding = 2;
    int overlapx = 4;
    int overlapy = 10;

    QPixmap pix(getIconSize() + textRect.width() + 2 * padding + 2 + 1 - overlapx,
                getIconSize() + textRect.height() + 2 * padding + 2 + 1 - overlapy);

    pix.fill(Qt::transparent);

    QPainter painter(&pix);

    painter.drawPixmap(0, pix.height() - getIconSize(),
                       MvQIconProvider::pixmap(dragObj->iconClass(), getIconSize()));

    iconPix = QRect(0, pix.height() - getIconSize(), getIconSize(), getIconSize());

    // Item count indicator
    QPen pen = MvQTheme::pen("folderview", "drag_count_pen");
    QBrush brush = MvQTheme::brush("folderview", "drag_count_brush");
    painter.setPen(pen);
    painter.setBrush(brush);
    painter.setFont(f);

    // painter.setRenderHint(QPainter::Antialiasing,true);

    QRect cntRect(getIconSize() - overlapx + 2, 1, textRect.width() + 2 * padding, textRect.height() + 2 * padding);
    painter.drawRoundedRect(cntRect, 2, 2);

    cntRect.adjust(-2, 2, -2, 2);
    painter.drawRoundedRect(cntRect, 2, 2);

    painter.setPen(Qt::black);
    painter.drawText(cntRect, Qt::AlignVCenter | Qt::AlignHCenter, QString::number(cnt));

    QPoint hotSpot(getIconSize(), pix.height());

    return pix;
}


MvQIconMimeData* MvQFolderViewBase::buildClipboardData(IconObject* dragObj, QList<IconObject*> objLst, bool fromHelper, bool copy)
{
    if (dragObj && objLst.contains(dragObj)) {
        // Hotspot is the bottom right corner of the dragged object's pixmap
        QPoint hotSpotInPix(1, 1);

        // It has to have as many elements as there are in objLst
        QList<QPoint> pixDistanceLst;  // It has

        // Multiple items
        if (objLst.count() > 1) {
            // Pixmap rect of the drag object
            QRect rect = pixmapRect(dragObj);

            for (int i = 0; i < objLst.count(); i++) {
                QRect vr = pixmapRect(objLst[i]);

                // Normalised distance of the pixmap's bottom right corner from the
                // bottom right corner of the dragged object's pixmap
                pixDistanceLst << (vr.bottomRight() - rect.bottomRight()) / getIconSize();
            }
        }

        // Single item
        else {
            pixDistanceLst << QPoint(0, 0);
        }

        MvQIconMimeData::ClipboardAction ca;
        if (copy)
            ca = MvQIconMimeData::CopyAction;
        else
            ca = MvQIconMimeData::CutAction;

        // Create and set drag mime data
        auto* mimeData = new MvQIconMimeData(folderModel_,
                                             dragObj, hotSpotInPix,
                                             objLst, pixDistanceLst, ca);
        mimeData->setFromHelper(fromHelper);

        return mimeData;
    }

    return nullptr;
}

QDrag* MvQFolderViewBase::buildDrag(IconObject* dragObj, QList<IconObject*> objLst, bool fromHelper, QWidget* dragParent, QPoint dragPos)
{
    if (dragObj && objLst.contains(dragObj)) {
        auto* drag = new QDrag(dragParent);

        // Hotspot is the bottom right corner of the dragged object's pixmap
        // we overwrite it if dragPos is null!! We use normalised coordinates
        QPointF hotSpotInPix(1, 1);

        // It has to have as many elements as there are in objLst
        QList<QPoint> pixDistanceLst;

        // Multiple items
        if (objLst.count() > 1) {
            if (MvQDesktopSettings::dragPolicy() == Desktop::DragShowAllIcons) {
                // Pixmap rect of the drag object
                QRect rect = pixmapRect(dragObj);

                // Find bounding box for selected pixmap rects
                QRect bbox = pixmapRect(objLst);

                // Render the drag pixmap --> will contain all the icons pixmaps!
                QPixmap pix(bbox.size());
                pix.fill(Qt::transparent);
                QPainter painter(&pix);

                for (int i = 0; i < objLst.count(); i++) {
                    QRect vr = pixmapRect(objLst[i]);
                    painter.drawPixmap(vr.topLeft() - bbox.topLeft(),
                                       MvQIconProvider::pixmap(objLst.at(i)->iconClass(), getIconSize()));

                    // Normalised distance of the pixmap's bottom right corner from the
                    // bottom right corner of the dragged object's pixmap
                    pixDistanceLst << (vr.bottomRight() - rect.bottomRight()) / getIconSize();
                }

                drag->setPixmap(pix);

                QPoint hotSpot;

                // Hotspot is the bottom right corner of the dragged object's pixmap
                if (dragPos.isNull()) {
                    hotSpot = rect.bottomRight() - bbox.topLeft();
                    hotSpotInPix = QPointF(1, 1);
                }
                // We use the real drag position if dragPos in not present
                else {
                    hotSpot = dragPos - bbox.topLeft();
                    hotSpotInPix = QPointF(dragPos.x() - (rect.center().x() - getIconSize() / 2),
                                           dragPos.y() - rect.top());
                    hotSpotInPix /= getIconSize();
                }

                drag->setHotSpot(hotSpot);
            }
            else {
                // Pixmap rect of the drag object
                QRect rect = pixmapRect(dragObj);

                for (int i = 0; i < objLst.count(); i++) {
                    QRect vr = pixmapRect(objLst[i]);

                    // Normalised distance of the pixmap's bottom right corner from the
                    // bottom right corner of the dragged object's pixmap
                    pixDistanceLst << (vr.bottomRight() - rect.bottomRight()) / getIconSize();
                }

                QRect iconPix;
                QPixmap pix = dragPixmapWithCount(dragObj, objLst.count(), iconPix);
                drag->setPixmap(pix);

                // Hotspot is the bottom right corner of the dragged object's pixmap
                QPoint hotSpot(iconPix.bottomRight());
                hotSpotInPix = QPointF(1, 1);
                drag->setHotSpot(hotSpot);
            }
        }

        // Single item
        else {
            drag->setPixmap(MvQIconProvider::pixmap(dragObj->iconClass(), getIconSize()));

            QPoint hotSpot;
            if (dragPos.isNull()) {
                hotSpot = QPoint(getIconSize(), getIconSize());
                hotSpotInPix = QPointF(1, 1);
            }
            // We use the real drag position
            else {
                // Pixmap rect of the drag object
                QRect rect = pixmapRect(dragObj);

                hotSpot = QPoint(dragPos.x() - (rect.center().x() - getIconSize() / 2),
                                 dragPos.y() - rect.top());

                hotSpotInPix = hotSpot;
                hotSpotInPix /= getIconSize();
            }

            drag->setHotSpot(hotSpot);

            pixDistanceLst << QPoint(0, 0);
        }


        // Create and set drag mime data
        auto* mimeData = new MvQIconMimeData(folderModel_,
                                             dragObj, hotSpotInPix,
                                             objLst, pixDistanceLst);

        mimeData->setFromHelper(fromHelper);

        drag->setMimeData(mimeData);

        return drag;
    }

    return nullptr;
}

void MvQFolderViewBase::dropToFolder(Folder* folder, QDropEvent* event)
{
    if (!folder || folder->locked()) {
        event->ignore();
        return;
    }

    if (!event->source()) {
        event->ignore();
        return;
    }

    if (event->proposedAction() != Qt::CopyAction &&
        event->proposedAction() != Qt::MoveAction) {
        event->ignore();
        return;
    }

    //--------------------------------------
    // Drag and drop from another Metview folder
    //--------------------------------------

    if (event->mimeData()->hasFormat("metview/icon")) {
        const auto* mimeData = qobject_cast<const MvQIconMimeData*>(event->mimeData());

        if (!mimeData) {
            event->ignore();
            return;
        }

        IconObject* dragObj = mimeData->dragObject();
        if (!dragObj) {
            event->ignore();
            return;
        }

        // Ignore drops from the same folder - except when it comes from an editor
        if (dragObj->parent() == folder && !mimeData->fromEditor()) {
            event->ignore();
            return;
        }

        // Drop from another view/model or from the clipboard  or from an editor --> copy or move

        for (int i = 0; i < mimeData->objects().count(); i++) {
            IconObject* obj = mimeData->objects().at(i);

            // We set the object position to zero. This will instuct the icon folder view
            //(when the folder is next open in it) to find meaningful positions to the
            // icons.

            // Copy
            if (event->proposedAction() == Qt::CopyAction) {
                obj->clone(folder, false);
            }

            // Move
            else if (event->proposedAction() == Qt::MoveAction) {
                obj->position(IconInfo::undefX(), IconInfo::undefY());
                folder->adopt(obj);
            }
        }

        event->accept();
        return;
    }
    else if (event->mimeData()->hasFormat("metview/new_icon")) {
        const auto* mimeData = qobject_cast<const MvQNewIconMimeData*>(event->mimeData());

        if (!mimeData) {
            event->ignore();
            return;
        }

        if (mimeData->iconDefType() == MvQNewIconMimeData::UserDef) {
            const IconClass& kind = IconClass::find(mimeData->className().toStdString());
            kind.createOne(folder);
            event->accept();
            return;
        }
    }


    event->ignore();
}

void MvQFolderViewBase::showIcon(IconObject* obj)
{
    if (!obj)
        return;

    QModelIndex index = filterModel_->mapFromSource(folderModel_->indexFromObject(obj));
    if (index.isValid())
        showIcon(index);
}
