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

 Copyright 2014 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 "MvQIconFolderView.h"

#include "Desktop.h"
#include "IconClass.h"
#include "IconObject.h"
#include "IconInfo.h"
#include "Folder.h"
#include "FolderSettings.h"

#include "MvQActionList.h"
#include "MvQContextMenu.h"
#include "MvQDesktopSettings.h"
#include "MvQDropTarget.h"
#include "MvQFolderViewDelegate.h"
#include "MvQFolderItemProperty.h"
#include "MvQFolderModel.h"
#include "MvQIconProvider.h"
#include "MvQIconMimeData.h"
#include "MvQMethods.h"
#include "MvQTheme.h"

#include <QtGlobal>
#include <QApplication>
#include <QClipboard>
#include <QDrag>
#include <QDropEvent>
#include <QLinearGradient>
#include <QLineEdit>
#include <QScrollBar>
#include <QShortcut>
#include <QTimer>


QPen MvQIconItem::editPen_ = QColor(Qt::yellow);
QBrush MvQIconItem::editBrush_;
QPen MvQIconItem::MvQIconItem::hoverPen_;
QBrush MvQIconItem::hoverBrush_;
QPen MvQIconItem::selectPen_;
QBrush MvQIconItem::selectBrush_;
int MvQIconItem::cornerRad_ = 4;

//================================================================
//
// MvQIconItemTimer
//
//================================================================

MvQIconItemTimer::MvQIconItemTimer(MvQIconItem* item, QObject* parent) :
    QTimer(parent),
    item_(item),
    cnt_(0),
    maxCnt_(10)
{
    connect(this, SIGNAL(timeout()),
            this, SLOT(slotTimeout()));
}

void MvQIconItemTimer::setItem(MvQIconItem* item)
{
    stop();
    if (item_)
        item->setBlinkState(MvQIconItem::NoBlink);
    item_ = item;
    cnt_ = 0;
}

void MvQIconItemTimer::blink()
{
    if (item_) {
        cnt_ = 0;
        start(150);
    }
}

void MvQIconItemTimer::slotTimeout()
{
    if (item_ && cnt_ < maxCnt_) {
        item_->setBlinkState((cnt_ % 2 == 0) ? MvQIconItem::BlinkOn : MvQIconItem::BlinkOff);
        // item_->update();
        cnt_++;
    }
    else {
        stop();
        cnt_ = 0;
        if (item_)
            item_->setBlinkState(MvQIconItem::NoBlink);
        item_ = nullptr;
    }
}

//================================================================
//
// MvQEditorProxy
//
//================================================================

MvQEditorProxy::MvQEditorProxy(MvQIconItem* item, QGraphicsItem* parent) :
    QGraphicsProxyWidget(parent),
    item_(item)
{
    QString text = item->text();
    QFont f = item->font();
    QFontMetrics fm(f);

    editor_ = new QLineEdit(text);
    editor_->setFont(item->font());
    editor_->setMinimumWidth(MvQ::textWidth(fm, text) + 16);
    editor_->setMaximumWidth(MvQ::textWidth(fm, text) + MvQ::textWidth(fm, "AAAAAAA"));
    editor_->selectAll();
    // editor_->setCursorPosition(0);
    // editor_->deselect();

    setWidget(editor_);
}

QLineEdit* MvQEditorProxy::editor()
{
    return editor_;
}

MvQIconItem* MvQEditorProxy::item()
{
    return item_;
}

void MvQEditorProxy::keyPressEvent(QKeyEvent* e)
{
    // qDebug() << "ketPressEvent" << e->key() << Qt::Key_Return << Qt::Key_Enter;
    QGraphicsProxyWidget::keyPressEvent(e);
    if ((e->key() == Qt::Key_Return || e->key() == Qt::Key_Enter) &&
        editor_->selectedText() != editor_->text()) {
        Q_EMIT finishedEditing();
    }
}


//================================================================
//
// MvQIconItem
//
//================================================================

MvQIconItem::MvQIconItem(const QModelIndex& index, MvQFolderItemProperty* itemProp, MvQIconFolderView* view, QGraphicsItem* parent) :
    QGraphicsItem(parent),
    view_(view),
    itemProp_(itemProp),
    index_(index),
    hover_(false),
    dropTarget_(false),
    blinkState_(NoBlink)
{
    // Setup colours - static memebers
    if (editPen_.color() == Qt::yellow) {
        QString group = "folderview";
        editPen_ = QPen(MvQTheme::colour(group, "edit_pen"));
        editBrush_ = MvQTheme::brush(group, "edit_brush");
        hoverPen_ = QPen(MvQTheme::colour(group, "hover_pen"));
        hoverBrush_ = MvQTheme::brush(group, "hover_brush");
        selectPen_ = QPen(MvQTheme::colour(group, "select_pen"));
        selectBrush_ = MvQTheme::brush(group, "select_brush");
    }

    setAcceptHoverEvents(true);

    setFlag(QGraphicsItem::ItemIsSelectable, true);

    index_ = index;

    adjust();
}

QRectF MvQIconItem::boundingRect() const
{
    return bRect_;
}

void MvQIconItem::paint(QPainter* painter, const QStyleOptionGraphicsItem*, QWidget*)
{
    auto pixmap = index_.data(Qt::DecorationRole).value<QPixmap>();

    // Save painter state
    painter->save();

    QString text = index_.data(Qt::DisplayRole).toString();

    // Background
    QRectF fillRect = bRect_;

    if (blinkState_ == BlinkOn) {
        painter->fillRect(fillRect, Qt::black);
    }
    // If the icon editor is open we render a transparent rect above the item
    else if (index_.data(MvQFolderModel::EditorRole).toBool()) {
        painter->setPen(editPen_);
        painter->setBrush(editBrush_);
        painter->drawRoundedRect(fillRect, cornerRad_, cornerRad_);
    }
    else if (isSelected()) {
        painter->setPen(selectPen_);
        painter->setBrush(selectBrush_);
        painter->drawRoundedRect(fillRect, cornerRad_, cornerRad_);
    }
    else if (hover_) {
        painter->setPen(hoverPen_);
        painter->setBrush(hoverBrush_);
        painter->drawRoundedRect(fillRect, cornerRad_, cornerRad_);
    }
    else if (dropTarget_) {
        painter->setPen(hoverPen_);
        painter->setBrush(selectBrush_);
        painter->drawRoundedRect(fillRect, cornerRad_, cornerRad_);
    }

    painter->setBrush(QBrush());

    // Render icon
    painter->drawPixmap(iconRect_.toRect(), pixmap);

    // Tender text
    auto font = index_.data(Qt::FontRole).value<QFont>();
    painter->setFont(font);

    if (blinkState_ == BlinkOn)
        painter->setPen(Qt::white);
    else
        painter->setPen(index_.data(Qt::ForegroundRole).value<QColor>());

    painter->drawText(textRect_, Qt::AlignCenter, text);

    painter->restore();
}

void MvQIconItem::adjust()
{
    prepareGeometryChange();

    QString name = index_.data(Qt::DisplayRole).toString();
    bRect_ = QRectF(QPointF(0, 0), itemProp_->size(name));
    QPoint refPos = index_.data(MvQFolderModel::PositionRole).toPoint();
    setPos(itemProp_->position(name, refPos));

    textRect_ = itemProp_->textRect(name);
    iconRect_ = itemProp_->iconRect(name);
}

bool MvQIconItem::textContains(QPointF pos)
{
    return textRect_.contains(pos);
}

QPointF MvQIconItem::textPos()
{
    return pos() + textRect_.topLeft();
}

QRect MvQIconItem::pixmapRect()
{
    return iconRect_.toRect();
}

const QPersistentModelIndex& MvQIconItem::index() const
{
    return index_;
}

QString MvQIconItem::text()
{
    return index_.data(Qt::DisplayRole).toString();
}

QFont MvQIconItem::font()
{
    return index_.data(Qt::FontRole).value<QFont>();
}

void MvQIconItem::hoverEnterEvent(QGraphicsSceneHoverEvent*)
{
    hover_ = true;
    update();
    view_->notifyItemEntered(this);
}

void MvQIconItem::hoverLeaveEvent(QGraphicsSceneHoverEvent*)
{
    hover_ = false;
    update();
    view_->notifyItemLeft(this);
}

void MvQIconItem::setDropTarget(bool t)
{
    dropTarget_ = t;
    update();
}

void MvQIconItem::setBlinkState(BlinkState b)
{
    blinkState_ = b;
    update();
}

//=================================================================
//
//  MvQIconFolderView
//
//=================================================================

MvQIconFolderView::MvQIconFolderView(MvQFolderModel* folderModel,
                                     MvQActionList* iconActions,
                                     MvQActionList* desktopActions, QWidget* parent) :
    QGraphicsView(parent),
    MvQFolderViewBase(folderModel, parent),
    allowMoveAction_(true),
    enterFolders_(true),
    mousePos_(0, 0),
    canDrag_(false),
    editor_(nullptr),
    timer_(nullptr),
    defaultShortCut_(nullptr),
    appIconActions_(iconActions),
    appDesktopActions_(desktopActions)
{
    // setBackgroundBrush(QColor(125,124,123));

    setAlignment(Qt::AlignLeft | Qt::AlignTop);
    setDragMode(QGraphicsView::RubberBandDrag);

    auto* scene_ = new QGraphicsScene(this);
    setScene(scene_);

    // qDebug() << maximumViewportSize() << items().count();
    // qDebug() << "scenerect" << sceneRect();

    setContextMenuPolicy(Qt::CustomContextMenu);

    // Context menu
    connect(this, SIGNAL(customContextMenuRequested(const QPoint&)),
            this, SLOT(slotContextMenu(const QPoint&)));

    // Set the style
    setProperty("mvStyle", "IconFolderView");

    // We set up the shortcuts here!!
    setupShortCut();

    // We attach the model because by default the view is enabled. At this point the model is empty so
    // it is a cheap operation!!
    attachModel();
}

MvQIconFolderView::~MvQIconFolderView() = default;

// Connect the models signal to the view
void MvQIconFolderView::attachModel()
{
    // Standard signals from the model

    connect(filterModel_, SIGNAL(modelReset()),
            this, SLOT(reset()));

    connect(filterModel_, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&)),
            this, SLOT(slotDataChanged(const QModelIndex&, const QModelIndex&)));

    // Custom signals from the model
    connect(folderModel_, SIGNAL(iconSizeChanged()),
            this, SLOT(slotIconSizeChanged()));

    connect(folderModel_, SIGNAL(objectRenamed(const QModelIndex&, QString)),
            this, SLOT(slotObjectRenamed(const QModelIndex&, QString)));

    // We need to call it to be sure that the view show the actual state of the model!!!
    // doReset();
}

// Disconnect the model from the view
void MvQIconFolderView::detachModel()
{
    disconnect(filterModel_, nullptr, this, nullptr);
    disconnect(folderModel_, nullptr, this, nullptr);
}

void MvQIconFolderView::doReset()
{
    reset();
}

QWidget* MvQIconFolderView::concreteWidget()
{
    return this;
}

//-----------------------------------------
// Event
//-----------------------------------------

bool MvQIconFolderView::event(QEvent* event)
{
    // qDebug() << "TYPE" << event->type();
    return QGraphicsView::event(event);
}

void MvQIconFolderView::changeEvent(QEvent* event)
{
    if (event->type() == QEvent::EnabledChange) {
        if (isEnabled()) {
            attachModel();
            // Reset will be called by the handler!!!!
            blockSignals(false);
        }
        // When the view is diabled we do not want to receive any signals from the model
        else {
            detachModel();
            clearContents();
            blockSignals(true);
        }
    }
}

//-----------------------------------------
// Accessing indexes, objects or items
//-----------------------------------------

QModelIndex MvQIconFolderView::indexAt(QPointF scenePos)
{
    QGraphicsItem* item = scene()->itemAt(scenePos, QTransform());
    if (item) {
        if (auto* icItem = dynamic_cast<MvQIconItem*>(item)) {
            return icItem->index();
        }
    }
    return {};
}

IconObject* MvQIconFolderView::iconObjectAt(QPointF scenePos)
{
    QGraphicsItem* item = scene()->itemAt(scenePos, QTransform());
    if (item) {
        auto* icItem = dynamic_cast<MvQIconItem*>(item);
        return folderModel_->objectFromIndex(icItem->index());
    }
    return nullptr;
}

MvQIconItem* MvQIconFolderView::objectToItem(IconObject* obj)
{
    QPersistentModelIndex idx(filterModel_->mapFromSource(folderModel_->indexFromObject(obj)));

    QMap<QPersistentModelIndex, MvQIconItem*>::iterator it = items_.find(idx);
    return (it != items_.end()) ? it.value() : 0;
}

MvQIconItem* MvQIconFolderView::indexToItem(const QModelIndex& index)
{
    QPersistentModelIndex idx(index);
    QMap<QPersistentModelIndex, MvQIconItem*>::iterator it = items_.find(idx);
    return (it != items_.end()) ? it.value() : 0;
}

const QPersistentModelIndex& MvQIconFolderView::itemToIndex(QGraphicsItem* item)
{
    static QModelIndex emptyIndex = QModelIndex();
    static QPersistentModelIndex emptyPersIndex(emptyIndex);

    if (item) {
        auto* icItem = dynamic_cast<MvQIconItem*>(item);
        return icItem->index();
    }
    return emptyPersIndex;
}

IconObject* MvQIconFolderView::itemToObject(QGraphicsItem* item)
{
    if (item) {
        auto* icItem = dynamic_cast<MvQIconItem*>(item);
        return folderModel_->objectFromIndex(icItem->index());
    }
    return nullptr;
}

QRect MvQIconFolderView::pixmapRect(QList<IconObject*> objLst)
{
    QRect bbox;
    foreach (IconObject* obj, objLst) {
        QRect b = pixmapRect(obj);
        if (!b.isNull())
            bbox = bbox.united(b);
    }

    return bbox;
}

QRect MvQIconFolderView::pixmapRect(IconObject* obj)
{
    if (MvQIconItem* item = objectToItem(obj)) {
        return item->mapToScene(item->pixmapRect()).boundingRect().toRect();
    }

    return {};
}


QRect MvQIconFolderView::itemRect(QList<IconObject*> objLst)
{
    QRect bbox;
    foreach (IconObject* obj, objLst) {
        if (MvQIconItem* item = objectToItem(obj))
            bbox = bbox.united(itemRect(item).toRect());
    }

    return bbox;
}

QRect MvQIconFolderView::itemRect(IconObject* obj)
{
    if (MvQIconItem* item = objectToItem(obj)) {
        return item->mapToScene(item->boundingRect()).boundingRect().toRect();
    }

    return {};
}

QRectF MvQIconFolderView::itemRect(QGraphicsItem* item)
{
    if (item) {
        return item->mapToScene(item->boundingRect()).boundingRect();
    }

    return {};
}

QModelIndexList MvQIconFolderView::selectedIndexes()
{
    QModelIndexList lst;
    foreach (QGraphicsItem* item, items()) {
        if (item->isSelected()) {
            auto index = itemToIndex(item);
            if (index.isValid())
                lst << index;
        }
    }

    return lst;
}

void MvQIconFolderView::selectOnlyOneItem(QGraphicsItem* item)
{
    if (item) {
        if (!item->isSelected()) {
            scene()->clearSelection();
            item->setSelected(true);
        }
    }
}
//-----------------------------------------
// Keypress, shortcut and context menu
//-----------------------------------------

void MvQIconFolderView::notifyItemEntered(MvQIconItem* item)
{
    emit itemEntered(itemInfo(item->index(), FilterModelIndex));
}

void MvQIconFolderView::notifyItemLeft(MvQIconItem* /*item*/)
{
    emit itemEntered(nullptr);
}

MvQContextItemSet* MvQIconFolderView::cmSet()
{
    static MvQContextItemSet cmItems("IconFolderView");
    return &cmItems;
}

void MvQIconFolderView::slotContextMenu(const QPoint& position)
{
    // QGraphicsItem *item=itemAt(position);
    // Relay the context menu event ot the editor!
    if (editor_) {
        QPointF scenePos = mapToScene(position);
        if (editor_->contains(editor_->mapFromScene(scenePos))) {
            QContextMenuEvent gme(QContextMenuEvent::Mouse, position, mapToGlobal(position));
            QApplication::sendEvent(editor_->editor(), &gme);
            return;
        }
    }

    QModelIndexList lst = selectedIndexes();
    // qDebug() << "selected" << lst;

    auto index = indexAt(mapToScene(position));

    QPoint scrollOffset(horizontalScrollBar()->value(), verticalScrollBar()->value());
    handleContextMenu(index, lst, mapToGlobal(position), position + scrollOffset, this);

    /*QModelIndexList lst=selectedIndexes();
    QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());
    handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this);*/

    /*if(lst.count() == 1)
    {
        QModelIndex index=indexAt(position);
        QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value());

        handleContextMenu(index,mapToGlobal(position),position+scrollOffset,this);
    }
    else if(lst.count() > 1)
    {
        handleContextMenu(lst,mapToGlobal(position),position+scrollOffset,this);
    } */
}

void MvQIconFolderView::mouseDoubleClickEvent(QMouseEvent* event)
{
    QModelIndexList lst = selectedIndexes();
    const QModelIndex& index = indexAt(mapToScene(event->pos()));
    if (lst.count() == 1 && lst.at(0) == index)
        handleDoubleClick(index);
}


void MvQIconFolderView::iconCommandFromMain(QString name)
{
    handleIconCommand(selectedIndexes(), name);
}

//-------------------------
//
//-------------------------

void MvQIconFolderView::folderChanged()
{
    emit currentFolderChanged(currentFolder());
}

void MvQIconFolderView::iconCommand(QString name, IconObjectH obj)
{
    if (name == "rename") {
    }
    else
        emit iconCommandRequested(name, obj);
}

void MvQIconFolderView::desktopCommand(QString name, QPoint pos)
{
    emit desktopCommandRequested(name, pos);
}

void MvQIconFolderView::initImportedHalfSizeItems()
{
    if (filterModel_->rowCount() == 0)
        return;

    if (Folder* f = folderModel_->folder()) {
        FolderSettings* fs = f->settings();
        if (fs->halfSizeImported() &&
            itemProp_->viewMode() == Desktop::SimpleViewMode &&
            itemProp_->iconSize() == 16) {
            for (int i = 0; i < filterModel_->rowCount(); i++) {
                QModelIndex idx = filterModel_->index(i, 0);
                QString name = filterModel_->data(idx, Qt::DisplayRole).toString();

                // Get the original pos taken from MetviewUI (padding addded!). It is valid for 16px icon size!
                QPoint refPos = filterModel_->data(idx, MvQFolderModel::PositionRole).toPoint();

                QPoint storedPos = itemProp_->storedPosition(refPos);

                // If the stored position is valid:
                // We need to scale it up to the reference position and set it as the real position!!
                if (IconInfo::validPos(storedPos.x(), storedPos.y())) {
                    filterModel_->setData(idx, itemProp_->referencePosition(name, refPos),
                                          MvQFolderModel::PositionRole);
                }
            }

            fs->halfSizeImportUsed();
        }
    }
}


//------------------------------------------
// Reflecting changes in the model
//------------------------------------------

void MvQIconFolderView::clearContents()
{
    removeEditor();
    removeDropTarget();
    scene()->clear();
    items_.clear();
}


void MvQIconFolderView::reset()
{
    removeEditor();
    removeDropTarget();
    scene()->clear();
    items_.clear();

    initImportedHalfSizeItems();

    for (int i = 0; i < filterModel_->rowCount(); i++) {
        QModelIndex idx = filterModel_->index(i, 0);

        auto* item = new MvQIconItem(idx, itemProp_, this);
        scene()->addItem(item);

        QPersistentModelIndex idxP(idx);
        items_[idxP] = item;
    }

    // Checks if there are icons with (0,0) positions and layout them
    // nicely
    checkPositions();

    adjustSceneRect();
}

void MvQIconFolderView::slotDataChanged(const QModelIndex& from, const QModelIndex&)
{
    if (from.isValid()) {
        if (MvQIconItem* item = indexToItem(from))
            item->update();
    }
}

void MvQIconFolderView::slotObjectArrived(const QModelIndex&)
{
    // showLastCreated();
}

//===============================================
// Icon positions
//===============================================

void MvQIconFolderView::adjustSceneRect()
{
    QRectF r = scene()->itemsBoundingRect();
    r.setTopLeft(QPointF(0, 0));
    r.setBottom(r.bottom() + 10);
    scene()->setSceneRect(r);
}

void MvQIconFolderView::adjustItems()
{
    QMap<QPersistentModelIndex, MvQIconItem*>::const_iterator it = items_.constBegin();
    while (it != items_.constEnd()) {
        it.value()->adjust();
        it++;
    }
}

void MvQIconFolderView::toGrid(Desktop::GridSortMode mode)
{
    computeGrid(mode);
    adjustSceneRect();
}

void MvQIconFolderView::slotIconSizeChanged()
{
    adjustItems();
    adjustSceneRect();
}

QSize MvQIconFolderView::maxItemSize()
{
    int h = 0;
    int w = itemProp_->iconSize();

    foreach (QGraphicsItem* item, items()) {
        QSize s = item->boundingRect().size().toSize();
        if (s.width() > w)
            w = s.width();
        if (h == 0)
            h = s.height();
    }

    return {w, h};
}

void MvQIconFolderView::computeGrid(Desktop::GridSortMode sortMode)
{
    if (!folderModel_->folder())
        return;

    QSize itemSize = maxItemSize();

    int dx = itemSize.width() + 2;
    int colNum = static_cast<int>(round(static_cast<float>(viewport()->size().width()) / static_cast<float>(dx)));
    if (colNum == 0)
        colNum = 1;
    int dy = itemSize.height() + 3 * itemProp_->scaling();

    // qDebug() << "DELTA" << itemSize.height() << itemProp_->scaling() << dy;

    int x = 5;
    int y = 5;

    std::multimap<QString, QModelIndex> items;

    if (sortMode == Desktop::GridSortByName) {
        for (int i = 0; i < filterModel_->rowCount(); i++) {
            QModelIndex idx = filterModel_->index(i, 0);
            items.insert(std::pair<QString, QModelIndex>(filterModel_->data(idx, Qt::DisplayRole).toString(), filterModel_->index(i, 0)));
        }
    }
    else if (sortMode == Desktop::GridSortByType) {
        for (int i = 0; i < filterModel_->rowCount(); i++) {
            QModelIndex idx = filterModel_->index(i, 1);
            items.insert(std::pair<QString, QModelIndex>(filterModel_->data(idx, Qt::DisplayRole).toString(), filterModel_->index(i, 0)));
        }
    }
    else if (sortMode == Desktop::GridSortBySize) {
        std::multimap<qint64, QModelIndex> sizeItems;
        for (int i = 0; i < filterModel_->rowCount(); i++) {
            QModelIndex idx = filterModel_->index(i, 2);
            sizeItems.insert(std::pair<qint64, QModelIndex>(filterModel_->data(idx, Qt::UserRole).toLongLong(), filterModel_->index(i, 0)));
        }
        int cntS = 0;
        for (auto it = sizeItems.begin(); it != sizeItems.end(); it++, cntS++) {
            items.insert(std::pair<QString, QModelIndex>(QString::number(cntS), it->second));
        }
    }

    int cnt = 0;
    for (auto it = items.begin(); it != items.end(); it++, cnt++) {
        MvQIconItem* item = indexToItem(it->second);

        QPoint pos;
        if (itemProp_->viewMode() == Desktop::IconViewMode) {
            int w = item->boundingRect().width();
            pos = QPoint(x + (dx - w) / 2, y);
        }
        else {
            pos = QPoint(x, y);
        }

        QString name = filterModel_->data(it->second, Qt::DisplayRole).toString();
        filterModel_->setData(it->second, itemProp_->referencePosition(name, pos), MvQFolderModel::PositionRole);
        item->adjust();

        x += dx;
        if (cnt > 0 && cnt % colNum == colNum - 1) {
            x = 5;
            y += dy;
        }
    }
}

void MvQIconFolderView::checkPositions()
{
    if (!folderModel_->folder())
        return;

    QList<MvQIconItem*> needPos;
    MvQIconItem* bottomItem = nullptr;

    int h = 0;
    int maxWidth = itemProp_->iconSize();
    int maxX = 0;
    int maxY = 0;

    QMap<QPersistentModelIndex, MvQIconItem*>::const_iterator it = items_.constBegin();
    while (it != items_.constEnd()) {
        // Reference pos
        QPoint refPos = it.key().data(MvQFolderModel::PositionRole).toPoint();

        // stored pos
        QPoint storedPos = itemProp_->storedPosition(refPos);

        if (IconInfo::validPos(storedPos.x(), storedPos.y())) {
            QPointF pos = it.value()->pos();
            if (pos.x() > maxX)
                maxX = pos.x();
            if (pos.y() > maxY) {
                maxY = pos.y();
                bottomItem = it.value();
            }
        }
        else {
            needPos << it.value();
        }

        QSize s = it.value()->boundingRect().size().toSize();
        if (s.width() > maxWidth)
            maxWidth = s.width();
        if (h == 0)
            h = s.height();

        it++;
    }

    if (needPos.count() == 0)
        return;

    int dx = maxWidth + 2;

    // Find out the available widthfor layouting
    int sceneWidth = sceneRect().size().width();

    // The actual viewport can be bigger than the scenerect width.
    if (viewport()->size().width() > sceneWidth)
        sceneWidth = viewport()->size().width();

    // If the window is not yet visible the viewport and the scenerect do not have the correct size!!!!
    // So we estimate the available size form the window size (it is "almost" correctly set although it is not visible)
    if (QWidget* win = window()) {
        if (!win->isVisible())
            sceneWidth = win->size().width() - 20;
    }

    int dy = h + 3 * itemProp_->scaling();

    // Find the staring pos of the new items
    int x = 5;
    int y = maxY + ((maxY == 0) ? 10 : dy);
    // int sceneWidth = sceneRect().size().width();

    if (sceneWidth < 20) {
        sceneWidth = maxX + maxWidth + 20;
    }

    // Try to find a starting postion in the last "row" of the "old" items
    if (bottomItem) {
        // See if other items intersect the bottom item's row
        QRectF bottomRect = bottomItem->boundingRect();
        bottomRect.moveTo(bottomItem->pos());

        // This is the rect at the right edge of the available area in the bottomItem's row.
        QRectF r(QPointF(sceneWidth - bottomRect.width(), bottomRect.y()), bottomRect.size());

        int rectStep = 0;
        while (r.x() > 0 && r.width() > 0) {
            // r.translate(-r.width(), 0);
            // See if we have overlapping items with the rect!!
            QList<QGraphicsItem*> intersectVector = scene()->items(r, Qt::IntersectsItemShape, Qt::AscendingOrder);
            if (!intersectVector.isEmpty()) {
                // If the first rect overlaps we need to start a new row
                // otherwise ...
                if (rectStep > 0) {
                    //.. we need to find the rightmost overlapping rect
                    QRectF endRect = intersectVector.at(0)->boundingRect();
                    endRect.moveTo(intersectVector.at(0)->pos());

                    for (int iv = 1; iv < intersectVector.count(); iv++) {
                        QRectF actRect = intersectVector.at(iv)->boundingRect();
                        actRect.moveTo(intersectVector.at(iv)->pos());
                        if (actRect.right() > endRect.right())
                            endRect = actRect;
                    }

                    if (intersectVector.count() == 1 &&
                        intersectVector.at(0) == bottomItem) {
                        x = bottomRect.x() - (dx - bottomRect.width()) / 2 + dx;
                        y = bottomRect.y();
                    }
                    else {
                        x = endRect.x() - (dx - endRect.width()) / 2 + dx;
                        y = bottomRect.y();
                    }
                }

                break;
            }

            // We move the rect to the left
            r.translate(-r.width(), 0);

            rectStep++;
        }
    }

    for (auto item : needPos) {
        QPoint pos;
        int w = item->boundingRect().width();
        if (itemProp_->viewMode() == Desktop::IconViewMode) {
            pos = QPoint(x + (dx - w) / 2, y);
            if (pos.x() + w > sceneWidth) {
                x = 5;
                y += dy;
            }
            pos = QPoint(x + (dx - w) / 2, y);
        }
        else {
            pos = QPoint(x, y);
            if (pos.x() + w > sceneWidth) {
                x = 5;
                y += dy;
            }
            pos = QPoint(x, y);
        }

        QString name = filterModel_->data(item->index(), Qt::DisplayRole).toString();
        filterModel_->setData(item->index(), itemProp_->referencePosition(name, pos), MvQFolderModel::PositionRole);
        item->adjust();

        // qDebug() << name << pos << w << sceneWidth;

        x += dx;
    }
}

//===========================
// Drag
//===========================

void MvQIconFolderView::mousePressEvent(QMouseEvent* event)
{
    canDrag_ = false;

    mousePos_ = event->pos();

    QPointF scenePos = mapToScene(event->pos());

    if (editor_) {
        // This is the standard behaviour on various systems: when we click
        // outside the editor we finish editing and confirm the rename operation. However,
        // the editor can have a context menu open so we need to check if we click inside
        // the context menu. Unfortunately QWidget::childAt() and Qwidget::childrenRect() does not
        // work properly to decide if the position is inside the context menu. So we have to check the
        // children one by one.
        if (!editor_->contains(editor_->mapFromScene(scenePos))) {
            bool outside = true;
            foreach (QObject* chObj, editor_->editor()->children()) {
                if (chObj->isWidgetType()) {
                    if (auto* w = dynamic_cast<QWidget*>(chObj)) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
                        QPoint posInW = w->mapFromGlobal(event->globalPosition().toPoint());
#else
                        QPoint posInW = w->mapFromGlobal(event->globalPos());
#endif
                        if (w->rect().contains(posInW)) {
                            outside = false;
                            break;
                        }
                    }
                }
            }

            if (outside) {
                // qDebug() << "click outside editor";
                slotRename();
                return;
            }
        }

        QGraphicsView::mousePressEvent(event);
        return;
    }

    // At this point the item cannot be the editor!!!
    if (event->button() == Qt::LeftButton || event->button() == Qt::MiddleButton) {
        MvQIconItem* item = dynamic_cast<MvQIconItem*>(itemAt(event->pos()));
        if (item) {
            // check if editor is needed
            if (item->textContains(item->mapFromScene(scenePos))) {
                if (IconObject* obj = itemToObject(item)) {
                    if (obj->renamable()) {
                        selectOnlyOneItem(item);
                        edit(item);
                    }
                }
                return;
            }

            // Set startpos in Viewport coordinates and enable drag
            startPos_ = event->pos();
            canDrag_ = true;

            if (event->button() == Qt::MiddleButton) {
                selectOnlyOneItem(item);
                event->accept();
                return;
            }
        }
    }
    else if (event->button() == Qt::RightButton) {
        QGraphicsItem* item = itemAt(event->pos());
        selectOnlyOneItem(item);
        event->accept();
        return;
    }

    QGraphicsView::mousePressEvent(event);
}

void MvQIconFolderView::mouseMoveEvent(QMouseEvent* event)
{
    if (canDrag_ && event->buttons() & (Qt::LeftButton | Qt::MiddleButton)) {
        int distance = (event->pos() - startPos_).manhattanLength();
        if (distance >= QApplication::startDragDistance()) {
            canDrag_ = false;

            QPoint scenePos = mapToScene(startPos_).toPoint();

            if (allowMoveAction_) {
                if (event->buttons() & Qt::LeftButton)
                    performDrag(Qt::MoveAction, scenePos);
                else if (event->buttons() & Qt::MiddleButton)
                    performDrag(Qt::CopyAction, scenePos);
            }
            else {
                performDrag(Qt::CopyAction, scenePos);
            }
        }

        return;
    }

    QGraphicsView::mouseMoveEvent(event);
}

void MvQIconFolderView::performDrag(Qt::DropAction dropAction, QPoint scenePos)
{
    // The object that was dragged
    // IconObject* dragObj=iconObjectAt(mapFromScene(scenePos));
    IconObject* dragObj = iconObjectAt(scenePos);


    // List of object to drag
    QList<IconObject*> objLst;
    foreach (QGraphicsItem* gt, scene()->selectedItems())
        objLst << itemToObject(gt);

    // Need a solution!!!!!!!!!!!!!!!!!1
    bool fromHelper = (!allowMoveAction_);

    QDrag* drag = buildDrag(dragObj, objLst, fromHelper, this, scenePos);

    if (drag) {
        // We always initiate a copy or move  action
        drag->exec(Qt::CopyAction | Qt::MoveAction, dropAction);
    }
    else {
        qDebug() << "Could not build drag object!!!";
    }
}

//===========================
// Drop
//===========================

void MvQIconFolderView::checkDropTarget(QDropEvent* event)
{
    IconObject* dragObj = nullptr;
    if (event->mimeData()->hasFormat("metview/icon")) {
        const auto* mimeData = qobject_cast<const MvQIconMimeData*>(event->mimeData());

        if (mimeData)
            dragObj = mimeData->dragObject();
    }


    if (!dragObj) {
        removeDropTarget();
        return;
    }

    QPointF scenePos = mapToScene(MvQ::eventPos(event));
    Qt::DropAction dropAction = event->proposedAction();

    IconObject* obj = iconObjectAt(scenePos);

    if (obj != dragObj && obj && obj->isFolder() && !obj->locked()) {
        MvQDropTarget::Instance()->reset(QString::fromStdString(obj->name()), (dropAction == Qt::MoveAction));
        if (window()) {
            MvQDropTarget::Instance()->move(mapToGlobal(MvQ::eventPos(event)) + QPoint(20, 20));
        }
        return;
    }
    else {
        removeDropTarget();
    }
}

void MvQIconFolderView::removeDropTarget()
{
    MvQDropTarget::Instance()->hide();
}

void MvQIconFolderView::dragEnterEvent(QDragEnterEvent* event)
{
    // qDebug() << event->mimeData()->formats();
    // qDebug() << event->mimeData()->text();
    // qDebug() << event->proposedAction();

    if (event->source() &&
        (event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        event->accept();
    }
    else
        event->ignore();
}

void MvQIconFolderView::dragMoveEvent(QDragMoveEvent* event)
{
    if (event->source() &&
        (event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        checkDropTarget(event);
        event->accept();
    }
    else {
        removeDropTarget();
        event->ignore();
    }
}

void MvQIconFolderView::dragLeaveEvent(QDragLeaveEvent* event)
{
    removeDropTarget();
    event->accept();
}

void MvQIconFolderView::dropEvent(QDropEvent* event)
{
    removeDropTarget();

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

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

    QPoint pos = mapToScene(MvQ::eventPos(event)).toPoint();

    //--------------------------------------
    // 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();
        MvQFolderModel* model = mimeData->model();

        if (dragObj && (model || mimeData->fromEditor())) {
            bool fromSameView = false;

            if (model) {
                fromSameView = (model == folderModel_ || model->folder() == folderModel_->folder());
            }
            else if (mimeData->fromEditor()) {
                fromSameView = (folderModel_->folder() == dragObj->parent());
            }

            // We cannot drop icons into a locked folder!
            if (!fromSameView && folderModel_->folder()->locked()) {
                event->ignore();
                return;
            }

            performDrop(event->proposedAction(), mimeData, pos, (model == folderModel_));
            event->accept();
            return;
        }
    }
    else if (event->mimeData()->hasFormat("metview/new_icon")) {
        // We cannot drop icons into a locked folder!
        if (folderModel_->folder()->locked()) {
            event->ignore();
            return;
        }

        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());

            QString cname = QString::fromStdString(kind.defaultName());

            QPoint bottomRight(1, 1);
            QPoint newPos = itemProp_->positionFromHotSpot(cname, pos, bottomRight);
            QPoint refPos = itemProp_->referencePosition(cname, newPos);
            kind.createOne(folderModel_->folder(), refPos.x(), refPos.y());

            event->accept();
            return;
        }
    }


    event->ignore();
}

void MvQIconFolderView::performDrop(Qt::DropAction dropAction, const MvQIconMimeData* data,
                                    QPoint cursorScenePos, bool fromSameView)
{
    IconObject* dragObj = data->dragObject();

    if (!dragObj)
        return;

    // Find out the itemRect topleft point in scene (scaled)
    QPoint scenePos = itemProp_->positionFromHotSpot(QString::fromStdString(dragObj->name()),
                                                     cursorScenePos, data->hotSpotInVisRect());

    // The object position - ignoring the scaling
    QPoint refPos = itemProp_->referencePosition(QString::fromStdString(dragObj->name()), scenePos);
    QPoint deltaPos = refPos - itemProp_->referencePosition(dragObj);

    bool toSameFolder = true;
    Folder* folder = folderModel_->folder();
    IconObject* objAt = iconObjectAt(cursorScenePos);

    // See if we drop the icons onto a folder icon. We do not allow dropping a folder into itself!!
    if (objAt != nullptr && objAt->isFolder() && !data->objects().contains(objAt)) {
        folder = dynamic_cast<Folder*>(objAt);
        toSameFolder = false;
    }

    if (folder == nullptr) {
        return;
    }

    bool doAdjustScene = false;
    QRectF scRect = scene()->sceneRect();
    float rightEdge = scRect.left();
    float bottomEdge = scRect.bottom();

    QPoint bottomRight(1, 1);
    for (int i = 0; i < data->objects().count(); i++) {
        IconObject* obj = data->objects().at(i);

        if (!obj || !isAccepted(obj))
            continue;

        // If the drop target is a folder icon in the current folder we set the coordinates to
        // undef. This will instruct the view to find maningful new positions to the icons,
        if (toSameFolder == false && folder) {
            // Copy
            if (dropAction == Qt::CopyAction) {
                obj->clone(folder, false);
            }
            // Move
            else if (dropAction == Qt::MoveAction) {
                if (!obj->locked()) {
                    obj->position(IconInfo::undefX(), IconInfo::undefY());
                    folder->adopt(obj);
                }
            }
        }
        // Otherwise we compute the possible new positions according to the position of the drag object
        else {
            QPoint newPos;
            QPoint newRefPos;

            if (obj == dragObj) {
                newRefPos = refPos;
                newPos = scenePos;
            }
            else {
                QPoint dp = data->pixDistances().at(i);
                newPos = itemProp_->positionFromHotSpot(QString::fromStdString(obj->name()),
                                                        cursorScenePos + dp * getIconSize(),
                                                        bottomRight);

                newRefPos = itemProp_->referencePosition(QString::fromStdString(obj->name()), newPos);

                // Icons can be off screen vertically or horizontally so we need to
                // adjust their positions
                if (newRefPos.y() < 0)
                    newRefPos.setY(0);
                if (newRefPos.x() < 0)
                    newRefPos.setX(0);
            }

            if (!doAdjustScene) {
                QSize objSize = itemProp_->size(QString::fromStdString(obj->name()));
                if (newPos.x() + objSize.width() + 2 > rightEdge ||
                    newPos.y() + objSize.height() + 2 > bottomEdge)
                    doAdjustScene = true;
            }

            // Copy
            if (dropAction == Qt::CopyAction) {
                obj->clone(folder, newRefPos.x(), newRefPos.y(), false);
            }
            // Move
            else if (dropAction == Qt::MoveAction) {
                // Drag from the same view/model
                if (fromSameView) {
                    QString name = QString::fromStdString(obj->name());
                    QPoint oriPos = itemProp_->referencePosition(obj);
                    QPoint newPos = oriPos + deltaPos;

                    // Icons can be off screen vertically or horizontally so we need to
                    // adjust their positions
                    if (newPos.y() < 0)
                        newPos.setY(0);
                    if (newPos.x() < 0)
                        newPos.setX(0);

                    // We need to store the storedPos in the object!!!
                    QPoint storedPos = itemProp_->storedPosition(newPos);
                    obj->position(storedPos.x(), storedPos.y());

                    if (MvQIconItem* item = objectToItem(obj)) {
                        item->setPos(itemProp_->position(name, newPos));
                    }
                }

                // Drop from another view/model
                else {
                    if (!obj->locked()) {
                        // We need to store the storedPos in the object!!!
                        QPoint storedPos = itemProp_->storedPosition(newRefPos);
                        obj->position(storedPos.x(), storedPos.y());
                        folder->adopt(obj);
                    }
                }
            }
        }
    }

    if (doAdjustScene)
        adjustSceneRect();
}


//===============================================
// Renaming
//===============================================

void MvQIconFolderView::rename(IconObject* obj)
{
    if (MvQIconItem* item = objectToItem(obj))
        edit(item);
}


void MvQIconFolderView::slotRename()
{
    if (editor_) {
        // qDebug() << "slotRename()";

        folderModel_->setData(editor_->item()->index(), editor_->editor()->text(), Qt::EditRole);
        removeEditor();
    }
}

// Called from the source model when the item was renamed!
void MvQIconFolderView::slotObjectRenamed(const QModelIndex& sourceIndex, QString oriName)
{
    QModelIndex index = filterModel_->mapFromSource(sourceIndex);
    MvQIconItem* item = indexToItem(index);
    if (item) {
        if (itemProp_->viewMode() == Desktop::IconViewMode) {
            QString name = filterModel_->data(index, Qt::DisplayRole).toString();
            QPoint pos = filterModel_->data(index, MvQFolderModel::PositionRole).toPoint();
            QPoint newPos = itemProp_->adjsutReferencePosition(name, oriName, pos);
            filterModel_->setData(index, newPos, MvQFolderModel::PositionRole);
        }
        item->adjust();
    }
}


void MvQIconFolderView::edit(QGraphicsItem* item)
{
    if (item && !editor_) {
        // qDebug() << "create editor";

        auto* ic = dynamic_cast<MvQIconItem*>(item);
        editor_ = new MvQEditorProxy(ic);

        // Warning: editingFinished is emitted twice !!! It is a known qt bug!!
        // So we needed to use returnPressed here.
        // connect(editor_->editor(),SIGNAL(returnPressed()),
        connect(editor_, SIGNAL(finishedEditing()),
                this, SLOT(slotRename()));

        scene()->addItem(editor_);
        // editor_->setPos(item->pos().x(),item->pos().y()+getIconSize()+5);
        editor_->setPos(ic->textPos());
        scene()->setFocusItem(editor_, Qt::MouseFocusReason);
        editor_->editor()->setFocus(Qt::MouseFocusReason);

        if (defaultShortCut_)
            defaultShortCut_->setEnabled(false);
    }
}

void MvQIconFolderView::removeEditor()
{
    if (editor_) {
        scene()->removeItem(editor_);
        editor_->deleteLater();
        editor_ = nullptr;

        if (defaultShortCut_)
            defaultShortCut_->setEnabled(true);
    }
}

//==============================================================
//  Keys
//==============================================================

void MvQIconFolderView::setupShortCut()
{
    if (shortCutInit_)
        return;
    else
        shortCutInit_ = true;

    if (MvQContextItemSet* cms = cmSet()) {
        foreach (MvQContextItem* cm, cms->icon()) {
            if (!appIconActions_ || !appIconActions_->shortCutDefined(cm->shortCut())) {
                if (QShortcut* sc = cm->makeShortCut(this)) {
                    connect(sc, SIGNAL(activated()),
                            this, SLOT(slotIconShortCut()));
                }
            }
        }

        foreach (MvQContextItem* cm, cms->desktop()) {
            if (!appDesktopActions_ || !appDesktopActions_->shortCutDefined(cm->shortCut())) {
                if (QShortcut* sc = cm->makeShortCut(this)) {
                    connect(sc, SIGNAL(activated()),
                            this, SLOT(slotDesktopShortCut()));
                }
            }
        }

        if (QShortcut* sc = cms->makeDefaultIconShortCut(this)) {
            defaultShortCut_ = sc;
            connect(defaultShortCut_, SIGNAL(activated()),
                    this, SLOT(slotDefaultShortCut()));
        }
    }
}

void MvQIconFolderView::slotIconShortCut()
{
    auto* sc = dynamic_cast<QShortcut*>(QObject::sender());
    if (sc) {
        QModelIndexList lst = selectedIndexes();

        // qDebug() << "shortcuts" << sc << lst;
        if (lst.count() > 0)
            handleIconShortCut(sc, lst);
    }
}

void MvQIconFolderView::slotDefaultShortCut()
{
    if (defaultShortCut_) {
        QModelIndexList lst = selectedIndexes();

        // qDebug() << "default shortcut" << lst;
        if (lst.count() == 1)
            handleDoubleClick(lst.at(0));
    }
}

void MvQIconFolderView::slotDesktopShortCut()
{
    auto* sc = dynamic_cast<QShortcut*>(QObject::sender());
    if (sc) {
        QPoint scrollOffset(horizontalScrollBar()->value(), verticalScrollBar()->value());
        handleDesktopShortCut(sc, mousePos_ + scrollOffset, this);
    }
}


void MvQIconFolderView::keyPressEvent(QKeyEvent* event)
{
    QGraphicsItem* current = nullptr;

    // qDebug() << "KEY" <<  event->key();

    if (editor_) {
        QGraphicsView::keyPressEvent(event);
        return;
    }

    bool hasCtrl = event->modifiers() & Qt::ControlModifier;
    bool hasShift = event->modifiers() & Qt::ShiftModifier;

    // Ctrl+Shift
    if (hasCtrl && hasShift) {
        event->ignore();
        return;
    }

    switch (event->key()) {
        case Qt::Key_Left:
        case Qt::Key_Right:
        case Qt::Key_Up:
        case Qt::Key_Down:
        case Qt::Key_Home:
        case Qt::Key_End: {
            current = findByKey(event->key());
            if (current) {
                if (!current->isSelected()) {
                    scene()->clearSelection();
                    current->setSelected(true);
                    ensureVisible(current);
                }
            }
            event->accept();
        } break;
        default:
            bool modified = (event->modifiers() & (Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier));
            if (!event->text().isEmpty() && !modified) {
                current = findByText(event->text());
                if (current) {
                    if (!current->isSelected()) {
                        scene()->clearSelection();
                        current->setSelected(true);
                        ensureVisible(current);
                    }
                }
                event->accept();
            }
            else {
                event->ignore();
            }
            break;
    }

    // QGraphicsView::keyPressEvent(event);
}


//---------------------------------
// Keypad navigation
//---------------------------------

QGraphicsItem* MvQIconFolderView::findByKey(int key)
{
    if (items().count() == 0)
        return nullptr;

    QGraphicsItem* current = items().at(0);
    QList<QGraphicsItem*> sLst = scene()->selectedItems();
    if (sLst.size() > 0)
        current = sLst.at(0);

    QRectF rect = current->boundingRect();
    rect.moveTo(current->pos());

    QSizeF contents = sceneRect().size();
    QList<QGraphicsItem*> intersectVector;

    switch (key) {
        case Qt::Key_Left:
            while (intersectVector.isEmpty()) {
                rect.translate(-rect.width(), 0);
                if (rect.right() <= 0) {
                    rect.moveTo(contents.width() - rect.width(), rect.y() - rect.height());
                    if (rect.bottom() <= 0)
                        return current;
                    if (rect.top() < 0)
                        rect.setTop(0);
                }
                if (rect.left() < 0)
                    rect.setLeft(0);

                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);
        case Qt::Key_Right:
            while (intersectVector.isEmpty()) {
                rect.translate(rect.width(), 0);
                if (rect.left() >= contents.width()) {
                    rect.moveTo(0, rect.y() + rect.height());
                    if (rect.top() >= contents.height())
                        return current;
                    if (rect.bottom() > contents.height())
                        rect.setBottom(contents.height());
                }
                if (rect.right() > contents.width())
                    rect.setRight(contents.width());

                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);

        case Qt::Key_Up:
            while (intersectVector.isEmpty()) {
                rect.translate(0, -rect.height());
                if (rect.bottom() <= 0)
                    return current;
                if (rect.top() < 0)
                    rect.setTop(0);

                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);

        case Qt::Key_Down:
            while (intersectVector.isEmpty()) {
                rect.translate(0, rect.height());
                if (rect.top() >= contents.height())
                    return current;
                if (rect.bottom() > contents.height())
                    rect.setBottom(contents.height());
                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);

        case Qt::Key_Home:
            rect.moveTo(-rect.width(), 0);
            while (intersectVector.isEmpty()) {
                rect.translate(rect.width(), 0);
                if (rect.left() >= contents.width()) {
                    rect.moveTo(0, rect.y() + rect.height());
                    if (rect.top() >= contents.height())
                        return current;
                    if (rect.bottom() > contents.height())
                        rect.setBottom(contents.height());
                }
                if (rect.right() > contents.width())
                    rect.setRight(contents.width());

                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);

        case Qt::Key_End:
            rect.moveTo(contents.width() - 1, contents.height() - rect.height());
            while (intersectVector.isEmpty()) {
                rect.translate(-rect.width(), 0);
                if (rect.right() <= 0) {
                    rect.moveTo(contents.width() - rect.width(), rect.y() - rect.height());
                    if (rect.bottom() <= 0)
                        return current;
                    if (rect.top() < 0)
                        rect.setTop(0);
                }
                if (rect.left() < 0)
                    rect.setLeft(0);

                intersectVector = scene()->items(rect, Qt::IntersectsItemShape, Qt::AscendingOrder);
                intersectVector.removeAll(current);
            }
            return intersectVector.at(0);

        default:
            return current;
    }
}

QGraphicsItem* MvQIconFolderView::findByText(QString search)
{
    if (!folderModel_->folder() || items().count() == 0)
        return nullptr;

    // Get current item and index
    QGraphicsItem* currentItem = items().at(0);
    QList<QGraphicsItem*> sLst = scene()->selectedItems();
    if (sLst.size() > 0)
        currentItem = sLst.at(0);
    QModelIndex current = itemToIndex(currentItem);

    QTime now(QTime::currentTime());
    if (search.isEmpty() || (keyboardInputTime_.msecsTo(now) > QApplication::keyboardInputInterval())) {
        keyboardInput_ = search;
    }
    else {
        keyboardInput_ += search;
    }
    keyboardInputTime_ = now;


    bool sameKey = false;
    if (keyboardInput_.length() > 1) {
        int c = keyboardInput_.count(keyboardInput_.at(keyboardInput_.length() - 1));
        sameKey = (c == keyboardInput_.length());
    }

    const QString searchString = sameKey ? QString(keyboardInput_.at(0)) : keyboardInput_;
    QModelIndexList match = filterModel_->match(current, Qt::DisplayRole, searchString, -1,
                                                Qt::MatchStartsWith | Qt::MatchWrap);

    match.removeOne(current);
    return (match.count() > 0) ? indexToItem(match.at(0)) : currentItem;
}

//==============================================
// Effects
//==============================================

void MvQIconFolderView::blink(const QModelIndex& index)
{
    if (MvQIconItem* item = indexToItem(index)) {
        ensureVisible(item);
        if (!timer_) {
            timer_ = new MvQIconItemTimer(item, this);
        }
        else {
            timer_->setItem(item);
        }

        timer_->blink();
    }
}

void MvQIconFolderView::showIcon(const QModelIndex& index)
{
    if (MvQIconItem* item = indexToItem(index)) {
        ensureVisible(item);
        selectOnlyOneItem(item);
    }
}
