#include "filemanager.h"
#include "ui_filemanager.h"
#include "ui_fitskeyword.h"

#include <QSettings>
#include <QStandardPaths>
#include <QDesktopServices>
#include <QMimeData>
#include <QClipboard>
#include <QThread>
#include <QDirIterator>
#include "loadimage.h"

class FileTimes
{
public:
    explicit FileTimes(const QString &path)
    {
        QFile file(path);
#ifndef Q_OS_WIN
        birthTime = file.fileTime(QFileDevice::FileBirthTime);
#endif
        modificationTime = file.fileTime(QFileDevice::FileModificationTime);
        accessTime = file.fileTime(QFileDevice::FileAccessTime);
    }
    void apply(const QString &path)
    {
        QFile file(path);
        if(file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::ExistingOnly))
        {
#ifndef Q_OS_WIN // Only windows allow changing birth time
            file.setFileTime(birthTime, QFileDevice::FileBirthTime);
#endif
            file.setFileTime(accessTime, QFileDevice::FileAccessTime);
            file.setFileTime(modificationTime, QFileDevice::FileModificationTime);
        }
    }
private:
    QDateTime birthTime;
    QDateTime modificationTime;
    QDateTime accessTime;
};

FileTransfer::FileTransfer(FileManager *fm) :
    _fm(fm)
{
}

FileTransfer::~FileTransfer()
{
    _run = false;
}

void FileTransfer::copy(const QStringList &src, const QString &dst)
{
    _run = true;
    perform(src, dst, true);
    emit finished();
}

void FileTransfer::move(const QStringList &src, const QString &dst)
{
    _run = true;
    perform(src, dst, false);
    emit finished();
}

void FileTransfer::cancel()
{
    _run = false;
}

void FileTransfer::perform(const QStringList &src, const QString &dst, bool copy)
{
    QDir dstDir(dst);
    if(!dstDir.exists())
    {
        emit error(tr("Error"), tr("Destination directory %1 doesn't exists").arg(dstDir.absolutePath()));
        return;
    }

    QList<Action> actions;
    QStringList dirs;

    emit progress(0);

    for(const QString &i : src)
    {
        QFileInfo srcInfo(i);
        if(srcInfo.absolutePath() == dst || dst.startsWith(srcInfo.absoluteFilePath()))
            return;

        if(srcInfo.isDir())
        {
            QDir srcDir(i);
            //qDebug() << "dir" << srcInfo.absoluteFilePath() << srcInfo.fileName();
            if(!copy && !dstDir.exists(srcInfo.fileName()))
            {
                if(QFile::rename(srcInfo.absoluteFilePath(), dstDir.absoluteFilePath(srcInfo.fileName())))
                    continue;
            }
            actions.append({srcInfo.absoluteFilePath(), srcInfo.fileName(), true});
            if(!copy)dirs.prepend(srcInfo.absoluteFilePath());
            QDirIterator it(i, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks);
            while(it.hasNext())
            {
                QFileInfo info = it.nextFileInfo();
                if(info.fileName() == "." || info.fileName() == "..")
                    continue;

                QString relativePath = srcDir.dirName() + "/" + srcDir.relativeFilePath(info.absoluteFilePath());
                if(info.isDir())
                {
                    actions.append({"", relativePath, true});
                    if(!copy)dirs.prepend(info.absoluteFilePath());
                    //qDebug() << "dir" << info.absoluteFilePath() << relativePath;
                }
                else
                {
                    actions.append({info.absoluteFilePath(), dstDir.absoluteFilePath(relativePath), false});
                    //qDebug() << "file" << info.absoluteFilePath() << dstDir.absoluteFilePath(relativePath);
                }
            }
        }
        else
        {
            actions.append({srcInfo.absoluteFilePath(), dstDir.absoluteFilePath(srcInfo.fileName())});
            //qDebug() << "file" << srcInfo.absoluteFilePath() << dstDir.absoluteFilePath(srcInfo.fileName());
        }
    }

    bool overwriteAll = false;
    bool skipAll = false;
    int total = actions.size();
    int i = 0;
    for(auto &a : actions)
    {
        if(!_run)
            return;

        if(a.dir)
        {
            dstDir.mkpath(a.dst);
        }
        else
        {
            QFileInfo dstInfo(a.dst);
            if(dstInfo.exists())
            {
                if(overwriteAll)
                {
                    QFile::remove(dstInfo.absoluteFilePath());
                }
                else if(skipAll)
                {
                    emit progress(i++ * 100 / total);
                    continue;
                }
                else
                {
                    QMessageBox::StandardButton ret;
                    QMetaObject::invokeMethod(_fm, "overwrite", Qt::BlockingQueuedConnection, Q_RETURN_ARG(QMessageBox::StandardButton, ret), Q_ARG(QString, dstInfo.fileName()));
                    switch(ret)
                    {
                    case QMessageBox::YesToAll:
                        overwriteAll = true;//break; is intentionally missing
                    case QMessageBox::Yes:
                        QFile::remove(dstInfo.absoluteFilePath());
                        break;
                    case QMessageBox::NoToAll:
                        skipAll = true;//break; is intentionally missing
                    case QMessageBox::No:
                        emit progress(i++ * 100 / total);
                        continue;
                        break;
                    case QMessageBox::Cancel:
                        return;
                    default:
                        break;
                    }
                }
            }
            FileTimes t(a.src);
            if(copy)
            {
                if(!QFile::copy(a.src, a.dst))
                {
                    emit error(tr("Copy failed"), tr("Failed to copy file %1 to %2").arg(a.src).arg(a.dst));
                    return;
                }
            }
            else
            {
                if(!QFile::rename(a.src, a.dst))
                {
                    emit error(tr("Move failed"), tr("Failed to move file %1 to %2").arg(a.src).arg(a.dst));
                    return;
                }
            }
            t.apply(a.dst);
        }
        emit progress(i++ * 100 / total);
    }

    if(!copy)
    {
        for(const QString &d : dirs)
        {
            QDir dir(d);
            if(dir.isEmpty())
                dir.removeRecursively();
        }
    }
}

PathTabBar::PathTabBar(const QStringList &tabs) :
    _tabs(tabs)
{
    setTabsClosable(true);
    setExpanding(false);

    for(auto &t : _tabs)
    {
        QDir dir(t);
        int i = addTab(tabName(t));
        setTabToolTip(i, t);
    }

    connect(this, &QTabBar::currentChanged, [this](int index){
        QString path = _tabs.at(index);
        emit pathChanged(path);
    });
    connect(this, &QTabBar::tabCloseRequested, [this](int index){
        if(_tabs.size() >= 2)
        {
            _tabs.remove(index);
            removeTab(index);
        }
    });
    connect(this, &QTabBar::currentChanged, [this](int index){
        emit tabChanged(_tabs[index]);
    });
}

QHBoxLayout *PathTabBar::createLayout()
{
    QHBoxLayout *hlayout = new QHBoxLayout();

    hlayout->addWidget(this);
    hlayout->addStretch(2);
    QPushButton *addButton = new QPushButton("+");
    connect(addButton, &QPushButton::clicked, [this](){
        QString path = _tabs[currentIndex()];
        _tabs.append(path);
        int i = addTab(tabName(path));
        setTabToolTip(i, path);
    });
    hlayout->addWidget(addButton);
    return hlayout;
}

const QStringList &PathTabBar::tabPaths() const
{
    return _tabs;
}

QString PathTabBar::currentTabPath() const
{
    int index = std::clamp(currentIndex(), 0, (int)_tabs.size());
    return _tabs[index];
}

void PathTabBar::pathChanged(const QString &path)
{
    QDir dir(path);
    int index = currentIndex();
    setTabText(index, tabName(path));
    setTabToolTip(index, path);
    _tabs[index] = path;
}

QString PathTabBar::tabName(const QString &path)
{
    QDir dir(path);
    if(dir.dirName().isEmpty())
        return path;
    else
        return dir.dirName();
}

FITSSelection::FITSSelection(const QStringList &keywords, QWidget *parent) : QDialog(parent)
    ,ui(new Ui::FITSKeyword)
{
    ui->setupUi(this);
    connect(ui->addButton, &QPushButton::clicked, [this](){
        auto item = ui->keywordList->findItems(ui->keyword->text(), Qt::MatchFixedString | Qt::MatchCaseSensitive);
        if(item.size())return;
        ui->keywordList->addItem(ui->keyword->text());
    });
    connect(ui->removeButton, &QPushButton::clicked, [this](){
        auto items = ui->keywordList->selectedItems();
        for(auto item : items)
            delete item;
    });

    ui->keywordList->addItems(keywords);
}

FITSSelection::~FITSSelection()
{
    delete ui;
}

QStringList FITSSelection::FITSKeywords() const
{
    QStringList keywords;
    for(int i = 0; i < ui->keywordList->count(); i++)
        keywords.append(ui->keywordList->item(i)->text());

    return keywords;
}

FileManager::FileManager(const QSet<QString> &openFilter, QWidget *parent) : QMainWindow(parent)
    ,ui(new Ui::FileManager)
{
    ui->setupUi(this);

    QStringList standardLocations = QStandardPaths::standardLocations(QStandardPaths::PicturesLocation);
    QString picturesPath;
    if(standardLocations.size())
        picturesPath = standardLocations.first();

    QSettings settings;
    QStringList leftTabs = settings.value("filemanager/leftTabPaths", picturesPath).toStringList();
    QStringList rightTabs = settings.value("filemanager/rightTabPaths", picturesPath).toStringList();
    if(leftTabs.empty())leftTabs.append(picturesPath);
    if(rightTabs.empty())rightTabs.append(picturesPath);

    ui->leftTab->setOpenFilter(openFilter);
    ui->rightTab->setOpenFilter(openFilter);

    _rightTabBar = new PathTabBar(rightTabs);
    ui->rightLayout->insertLayout(0, _rightTabBar->createLayout());
    connect(_rightTabBar, &PathTabBar::tabChanged, ui->rightTab, &DirView::setDir);

    _leftTabBar = new PathTabBar(leftTabs);
    ui->leftLayout->insertLayout(0, _leftTabBar->createLayout());
    connect(_leftTabBar, &PathTabBar::tabChanged, ui->leftTab, &DirView::setDir);

    connect(ui->leftTab, &DirView::dirChanged, ui->leftPath, &QLineEdit::setText);
    connect(ui->leftTab, &DirView::dirChanged, _leftTabBar, &PathTabBar::pathChanged);
    connect(ui->rightTab, &DirView::dirChanged, ui->rightPath, &QLineEdit::setText);
    connect(ui->rightTab, &DirView::dirChanged, _rightTabBar, &PathTabBar::pathChanged);
    connect(ui->leftTab, &DirView::openFile, this, &FileManager::openFile);
    connect(ui->rightTab, &DirView::openFile, this, &FileManager::openFile);
    connect(ui->leftTab, &DirView::filesAction, this, &FileManager::copyMoveFiles, Qt::QueuedConnection);
    connect(ui->rightTab, &DirView::filesAction, this, &FileManager::copyMoveFiles, Qt::QueuedConnection);
    connect(ui->actionLoad_FITS_keywordsLeft, &QAction::toggled, ui->leftTab, &DirView::loadFitsKeywords);
    connect(ui->actionLoad_FITS_keywordsRight, &QAction::toggled, ui->rightTab, &DirView::loadFitsKeywords);

    ui->leftTab->setDir(_leftTabBar->currentTabPath());
    ui->leftTab->setFITSKeywords(settings.value("filemanager/leftFitsKeywords", QStringList("OBJECT")).toStringList());
    ui->leftTab->header()->restoreState(settings.value("filemanager/leftTabHeader").toByteArray());
    ui->rightTab->setDir(_rightTabBar->currentTabPath());
    ui->rightTab->setFITSKeywords(settings.value("filemanager/rightFitsKeywords", QStringList("OBJECT")).toStringList());
    ui->rightTab->header()->restoreState(settings.value("filemanager/rightTabHeader").toByteArray());

    ui->actionLoad_FITS_keywordsLeft->setChecked(settings.value("filemanager/leftLoadFitsKeywords", true).toBool());
    ui->actionLoad_FITS_keywordsRight->setChecked(settings.value("filemanager/rightLoadFitsKeywords", true).toBool());
    restoreGeometry(settings.value("filemanager/geometry").toByteArray());

    setAttribute(Qt::WA_DeleteOnClose);

    connect(ui->actionSelect_columnsLeft, &QAction::triggered, this, &FileManager::selectFITSKeywords);
    connect(ui->actionSelect_columnsRight, &QAction::triggered, this, &FileManager::selectFITSKeywords);
    connect(ui->actionCopySelectedFilesPathsLeft, &QAction::triggered, this, &FileManager::copySelectedFilesPaths);
    connect(ui->actionCopySelectedFilesPathsRight, &QAction::triggered, this, &FileManager::copySelectedFilesPaths);
    connect(ui->leftPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);
    connect(ui->rightPath, &QLineEdit::returnPressed, this, &FileManager::pathEdited);

    QFileInfoList drives = QDir::drives();
    for(auto &drive : drives)
    {
        QString path = drive.absoluteFilePath();
        ui->menuLeft_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->leftTab->setDir(path); });
        ui->menuRight_Tab->addAction(drive.absoluteFilePath(), [path, this](){ ui->rightTab->setDir(path); });
    }

    ui->progressBar->hide();
    ui->cancelButton->hide();

    _thread = new QThread(this);
    _thread->start();
    _fileTransfer = new FileTransfer(this);
    _fileTransfer->moveToThread(_thread);
    connect(_fileTransfer, &FileTransfer::progress, ui->progressBar, &QProgressBar::setValue);
    connect(_fileTransfer, &FileTransfer::error, this, &FileManager::errorMessage);
    connect(_fileTransfer, &FileTransfer::finished, [this](){
        ui->leftTab->setDragEnabled(true);
        ui->rightTab->setDragEnabled(true);
        ui->progressBar->hide();
        ui->cancelButton->hide();
    });
    connect(this, &FileManager::copy, _fileTransfer, &FileTransfer::copy);
    connect(this, &FileManager::move, _fileTransfer, &FileTransfer::move);
    connect(ui->cancelButton, &QPushButton::clicked, [this](){ _fileTransfer->cancel(); });
}

FileManager::~FileManager()
{
    QSettings settings;
    settings.setValue("filemanager/leftFitsKeywords", ui->leftTab->FITSKeywords());
    settings.setValue("filemanager/leftTabPaths", _leftTabBar->tabPaths());
    settings.setValue("filemanager/leftTabHeader", ui->leftTab->header()->saveState());
    settings.setValue("filemanager/rightFitsKeywords", ui->rightTab->FITSKeywords());
    settings.setValue("filemanager/rightTabPaths", _rightTabBar->tabPaths());
    settings.setValue("filemanager/rightTabHeader", ui->rightTab->header()->saveState());
    settings.setValue("filemanager/leftLoadFitsKeywords", ui->actionLoad_FITS_keywordsLeft->isChecked());
    settings.setValue("filemanager/rightLoadFitsKeywords", ui->actionLoad_FITS_keywordsRight->isChecked());
    settings.setValue("filemanager/geometry", saveGeometry());
    delete ui;

    _fileTransfer->cancel();
    _thread->quit();
    _thread->wait();

    delete _fileTransfer;
}

void FileManager::selectFITSKeywords()
{
    QStringList columns;
    if(sender() == ui->actionSelect_columnsLeft)
        columns = ui->leftTab->FITSKeywords();
    if(sender() == ui->actionSelect_columnsRight)
        columns = ui->rightTab->FITSKeywords();

    FITSSelection selection(columns, this);
    int ret = selection.exec();

    if(ret == QDialog::Accepted)
    {
        if(sender() == ui->actionSelect_columnsLeft)
            ui->leftTab->setFITSKeywords(selection.FITSKeywords());

        if(sender() == ui->actionSelect_columnsRight)
            ui->rightTab->setFITSKeywords(selection.FITSKeywords());
    }
}

void FileManager::copySelectedFilesPaths()
{
    if(sender() == ui->actionCopySelectedFilesPathsLeft)
        ui->leftTab->copySelectedFilesPathsToClipboard();
    if(sender() == ui->actionCopySelectedFilesPathsRight)
        ui->rightTab->copySelectedFilesPathsToClipboard();
}

void FileManager::pathEdited()
{
    if(sender() == ui->leftPath)
    {
        QDir dir(ui->leftPath->text());
        if(dir.exists())
            ui->leftTab->setDir(dir.absolutePath());
    }
    if(sender() == ui->rightPath)
    {
        QDir dir(ui->rightPath->text());
        if(dir.exists())
            ui->rightTab->setDir(dir.absolutePath());
    }
}

void FileManager::copyMoveFiles(Qt::DropAction action, const QStringList &src, const QString &dst)
{
    ui->leftTab->setDragEnabled(false);
    ui->rightTab->setDragEnabled(false);
    ui->progressBar->show();
    ui->cancelButton->show();

    switch(action)
    {
    case Qt::CopyAction:
        emit copy(src, dst);
        break;
    case Qt::MoveAction:
        emit move(src, dst);
        break;
    case Qt::LinkAction:
    default:
        break;
    }
}

QMessageBox::StandardButton FileManager::overwrite(const QString &dst)
{
    QMessageBox::StandardButton button = QMessageBox::question(this, tr("Overwrite file?"), tr("Destination file %1 already exists. Overwrite?").arg(dst),
                                                               QMessageBox::Yes | QMessageBox::YesToAll | QMessageBox::No | QMessageBox::NoToAll | QMessageBox::Cancel);

    return button;
}

void FileManager::errorMessage(const QString &title, const QString &text)
{
    QMessageBox::critical(this, title, text);
}

QCache<QString, ImageInfoData>* DirFileSystemModel::getCacheInstance()
{
    static bool init = true;
    static QCache<QString, ImageInfoData> cache;
    if(!init)
    {
        cache.setMaxCost(10000);
        init = false;
    }
    return &cache;
}

DirFileSystemModel::DirFileSystemModel(QWidget *parentWidget) : QFileSystemModel(parentWidget)
    ,_parentWidget(parentWidget)
{
    _cache = getCacheInstance();
    setFilter(QDir::AllEntries | QDir::NoDot);
    _fitsKeywords = {"OBJECT"};
}

void DirFileSystemModel::setDir(const QString &path)
{
    _dir = index(path);
}

QString DirFileSystemModel::dir() const
{
    return fileInfo(_dir).canonicalFilePath();
}

void DirFileSystemModel::setFITSKeywords(const QStringList &keywords)
{
    beginResetModel();
    _fitsKeywords = keywords;
    endResetModel();
}

const QStringList &DirFileSystemModel::FITSKeywords() const
{
    return _fitsKeywords;
}

Qt::ItemFlags DirFileSystemModel::flags(const QModelIndex &index) const
{
    Qt::ItemFlags ret = QFileSystemModel::flags(index) & ~Qt::ItemIsEditable;
    if(index.row() == 0)ret &= ~Qt::ItemIsDragEnabled;
    return ret;
}

int DirFileSystemModel::columnCount(const QModelIndex &parent) const
{
    return QFileSystemModel::columnCount(parent) + _fitsKeywords.size();
}

QVariant DirFileSystemModel::data(const QModelIndex &index, int role) const
{
    if(index.column() >= QFileSystemModel::columnCount() && role == Qt::DisplayRole)
    {
        QFileInfo info = fileInfo(index);
        QString path = info.canonicalFilePath();
        QString suffix = info.suffix();
        ImageInfoData *infoData = nullptr;
        if(_cache->contains(path))
        {
            infoData = _cache->object(path);
        }
        else
        {
            if(_loadFitsKeywords)
            {
                infoData = new ImageInfoData;
                if(isFITS(suffix))
                    readFITSHeader(path, *infoData);
                else if(isXISF(suffix))
                    readXISFHeader(path, *infoData);
                _cache->insert(path, infoData);
            }
        }
        if(infoData)
        {
            int column = index.column() - QFileSystemModel::columnCount();
            if(column < _fitsKeywords.size())
            {
                const QString &key = _fitsKeywords.at(column);
                for(auto &record : infoData->fitsHeader)
                    if(record.key == key)
                        return record.value;
            }
        }
        return "";
    }
    return QFileSystemModel::data(index, role);
}

QVariant DirFileSystemModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal && role == Qt::DisplayRole && section >= QFileSystemModel::columnCount())
        return _fitsKeywords.at(section - QFileSystemModel::columnCount());

    return QFileSystemModel::headerData(section, orientation, role);
}

bool DirFileSystemModel::hasChildren(const QModelIndex &parent) const
{
    if(parent.parent() == _dir)return false;

    return QFileSystemModel::hasChildren(parent);
}

bool DirFileSystemModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
{
    Q_UNUSED(row);
    Q_UNUSED(column);
    if(data->hasUrls())
    {
        QStringList srcPaths;
        for(auto &url : data->urls())
            srcPaths.append(url.toLocalFile());
        emit filesAction(action, srcPaths, filePath(parent));
    }

    return false;
}

void DirFileSystemModel::loadFitsKeywords(bool enable)
{
    _loadFitsKeywords = enable;
}

DirView::DirView(QWidget *parent) : QTreeView(parent)
{
    _dirFileSystemModel = new DirFileSystemModel(this);
    _dirFileSystemModel->setRootPath(QDir::drives().first().path());
    _dirFileSystemModel->setReadOnly(false);
    setDragEnabled(true);
    setAcceptDrops(true);

    connect(_dirFileSystemModel, &DirFileSystemModel::filesAction, this, &DirView::filesAction);

    setModel(_dirFileSystemModel);
    setSelectionMode(QAbstractItemView::ExtendedSelection);

    connect(this, &QTreeView::doubleClicked, [this](const QModelIndex &index){
        QFileInfo info = _dirFileSystemModel->fileInfo(index);
        if(_dirFileSystemModel->isDir(index))
        {
            setDir(info.canonicalFilePath());
        }
        else if(info.isFile())
        {
            if(_openFilter.contains(info.suffix().toLower()))
                emit openFile(info.absoluteFilePath());
            else
                QDesktopServices::openUrl(QUrl::fromLocalFile(info.absoluteFilePath()));
        }
    });

    header()->setContextMenuPolicy(Qt::CustomContextMenu);
    connect(header(), &QHeaderView::customContextMenuRequested, this, &DirView::headerContextMenu);
}

void DirView::setDir(const QString &path)
{
    QString oldPath = _dirFileSystemModel->dir();
#ifdef Q_OS_WINDOWS
    const int ROOT_LEN = 3;
#else
    const int ROOT_LEN = 1;
#endif

    if(oldPath.left(ROOT_LEN) != path.left(ROOT_LEN))
        _dirFileSystemModel->setRootPath(path.left(ROOT_LEN));

    QString newPath = path;
    if(!QFileInfo::exists(path))
    {
        QDir dir(path);
        do
        {
            dir.setPath(QDir::cleanPath(dir.filePath("..")));
        }while(!dir.exists() && !dir.isRoot());
        newPath = dir.path();
    }

    _dirFileSystemModel->setDir(newPath);
    setRootIndex(_dirFileSystemModel->index(newPath, 0));
    clearSelection();
    if(oldPath != newPath)emit dirChanged(newPath);
}

QString DirView::dir() const
{
    return _dirFileSystemModel->dir();
}

void DirView::setOpenFilter(const QSet<QString> &openFilter)
{
    _openFilter = openFilter;
}

void DirView::setFITSKeywords(const QStringList &keywords)
{
    QString d = dir();
    _dirFileSystemModel->setFITSKeywords(keywords);
    setDir(d);
}

const QStringList &DirView::FITSKeywords() const
{
    return _dirFileSystemModel->FITSKeywords();
}

void DirView::headerContextMenu(const QPoint &pos)
{
    QHeaderView *head = header();
    QMenu menu;
    int count = head->count();
    for(int i = 0; i < count; i++)
    {
        QAction *a = menu.addAction(head->model()->headerData(i, Qt::Horizontal).toString());
        a->setCheckable(true);
        a->setChecked(!head->isSectionHidden(i));
        a->setData(i);
    }

    QAction *a = menu.exec(mapToGlobal(pos));
    if(a)
    {
        if(a->isChecked())head->showSection(a->data().toInt());
        else head->hideSection(a->data().toInt());
    }
}

void DirView::loadFitsKeywords(bool enable)
{
    _dirFileSystemModel->loadFitsKeywords(enable);
}

void DirView::copySelectedFilesPathsToClipboard() const
{
    QList<QUrl> urls;
    QString text;
    auto selected = selectionModel()->selectedRows();
    for(auto &item : selected)
    {
        if(item.column() == 0)
        {
            QString path = _dirFileSystemModel->filePath(item);
            text.append(path); text.append('\n');
            urls.append(QUrl::fromLocalFile(path));
        }
    }
    QClipboard *clipboard = QApplication::clipboard();
    QMimeData *mimeData = new QMimeData();
    mimeData->setUrls(urls);
    mimeData->setText(text);
    clipboard->setMimeData(mimeData);
}
