File indexing completed on 2024-04-14 05:43:21

0001 /*
0002     kfindtreeview.cpp
0003     SPDX-FileCopyrightText: 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007 */
0008 
0009 #include "kfindtreeview.h"
0010 #include "kfinddlg.h"
0011 
0012 #include <QApplication>
0013 #include <QClipboard>
0014 #include <QFileDialog>
0015 #include <QFileInfo>
0016 #include <QHeaderView>
0017 #include <QMenu>
0018 #include <QMimeData>
0019 #include <QScrollBar>
0020 #include <QTextCodec>
0021 #include <QTextStream>
0022 
0023 #include <KActionCollection>
0024 #include <KFileItemActions>
0025 #include <KFileItemListProperties>
0026 #include <KJobWidgets>
0027 #include <KLocalizedString>
0028 #include <KMessageBox>
0029 #include <KPropertiesDialog>
0030 
0031 #include <KIO/CopyJob>
0032 #include <KIO/DeleteJob>
0033 #include <kio_version.h>
0034 #include <KIO/JobUiDelegateFactory>
0035 #include <KIO/JobUiDelegate>
0036 #include <KIO/OpenFileManagerWindowJob>
0037 #include <KIO/OpenUrlJob>
0038 #include <KIO/DeleteOrTrashJob>
0039 
0040 // Permission strings
0041 #include <ki18n_version.h>
0042 #include <KLazyLocalizedString>
0043 
0044 const KLazyLocalizedString perm[4] = {
0045     kli18n("Read-write"),
0046     kli18n("Read-only"),
0047     kli18n("Write-only"),
0048     kli18n("Inaccessible")
0049 };
0050 #define RW 0
0051 #define RO 1
0052 #define WO 2
0053 #define NA 3
0054 
0055 //BEGIN KFindItemModel
0056 
0057 KFindItemModel::KFindItemModel(KFindTreeView *parentView)
0058     : QAbstractTableModel(parentView)
0059 {
0060     m_view = parentView;
0061 }
0062 
0063 QVariant KFindItemModel::headerData(int section, Qt::Orientation orientation, int role) const
0064 {
0065     if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
0066         switch (section) {
0067         case 0:
0068             return i18nc("file name column", "Name");
0069         case 1:
0070             return i18nc("name of the containing folder", "In Subfolder");
0071         case 2:
0072             return i18nc("file size column", "Size");
0073         case 3:
0074             return i18nc("modified date column", "Modified");
0075         case 4:
0076             return i18nc("file permissions column", "Permissions");
0077         case 5:
0078             return i18nc("first matching line of the query string in this file", "First Matching Line");
0079         default:
0080             return QVariant();
0081         }
0082     }
0083     return QVariant();
0084 }
0085 
0086 void KFindItemModel::insertFileItems(const QList<QPair<KFileItem, QString>> &pairs)
0087 {
0088     if (!pairs.isEmpty()) {
0089         beginInsertRows(QModelIndex(), m_itemList.size(), m_itemList.size()+pairs.size()-1);
0090 
0091         for (const auto &[item, dir] : pairs) {
0092             const QString subDir = m_view->reducedDir(item.url().adjusted(QUrl::RemoveFilename).path());
0093             m_itemList.append(KFindItem(item, subDir, dir));
0094         }
0095 
0096         endInsertRows();
0097     }
0098 }
0099 
0100 int KFindItemModel::rowCount(const QModelIndex &parent) const
0101 {
0102     if (!parent.isValid()) {
0103         return m_itemList.count(); //Return itemcount for toplevel
0104     } else {
0105         return 0;
0106     }
0107 }
0108 
0109 KFindItem KFindItemModel::itemAtIndex(const QModelIndex &index) const
0110 {
0111     if (index.isValid() && m_itemList.size() >= index.row()) {
0112         return m_itemList.at(index.row());
0113     }
0114 
0115     return KFindItem();
0116 }
0117 
0118 QList<KFindItem> KFindItemModel::getItemList() const
0119 {
0120     return m_itemList;
0121 }
0122 
0123 QVariant KFindItemModel::data(const QModelIndex &index, int role) const
0124 {
0125     if (!index.isValid()) {
0126         return QVariant();
0127     }
0128 
0129     if (index.column() > 6 || index.row() >= m_itemList.count()) {
0130         return QVariant();
0131     }
0132 
0133     switch (role) {
0134     case Qt::DisplayRole:
0135     case Qt::DecorationRole:
0136     case Qt::UserRole:
0137         return m_itemList.at(index.row()).data(index.column(), role);
0138     default:
0139         return QVariant();
0140     }
0141     return QVariant();
0142 }
0143 
0144 void KFindItemModel::removeItem(const QUrl &url)
0145 {
0146     const int itemCount = m_itemList.size();
0147     for (int i = 0; i < itemCount; i++) {
0148         KFindItem item = m_itemList.at(i);
0149         if (item.getFileItem().url() == url) {
0150             beginRemoveRows(QModelIndex(), i, i);
0151             m_itemList.removeAt(i);
0152             endRemoveRows();
0153             return;
0154         }
0155     }
0156 }
0157 
0158 bool KFindItemModel::isInserted(const QUrl &url)
0159 {
0160     const int itemCount = m_itemList.size();
0161     for (int i = 0; i < itemCount; i++) {
0162         KFindItem item = m_itemList.at(i);
0163         if (item.getFileItem().url() == url) {
0164             return true;
0165         }
0166     }
0167     return false;
0168 }
0169 
0170 void KFindItemModel::clear()
0171 {
0172     if (!m_itemList.isEmpty()) {
0173         beginRemoveRows(QModelIndex(), 0, m_itemList.size());
0174         m_itemList.clear();
0175         endRemoveRows();
0176     }
0177 }
0178 
0179 Qt::DropActions KFindItemModel::supportedDropActions() const
0180 {
0181     return Qt::CopyAction | Qt::MoveAction;
0182 }
0183 
0184 Qt::ItemFlags KFindItemModel::flags(const QModelIndex &index) const
0185 {
0186     const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
0187     if (index.isValid()) {
0188         return Qt::ItemIsDragEnabled | defaultFlags;
0189     }
0190     return defaultFlags;
0191 }
0192 
0193 QMimeData *KFindItemModel::mimeData(const QModelIndexList &indexes) const
0194 {
0195     QList<QUrl> uris;
0196 
0197     for (const QModelIndex &index : indexes) {
0198         if (index.isValid()) {
0199             if (index.column() == 0) { //Only use the first column item
0200                 uris.append(m_itemList.at(index.row()).getFileItem().url());
0201             }
0202         }
0203     }
0204 
0205     if (uris.isEmpty()) {
0206         return nullptr;
0207     }
0208 
0209     QMimeData *mimeData = new QMimeData();
0210     mimeData->setUrls(uris);
0211 
0212     return mimeData;
0213 }
0214 
0215 int KFindItemModel::columnCount(const QModelIndex &parent) const
0216 {
0217     Q_UNUSED(parent);
0218     return 6;
0219 }
0220 
0221 //END KFindItemModel
0222 
0223 //BEGIN KFindItem
0224 
0225 KFindItem::KFindItem(const KFileItem &_fileItem, const QString &subDir, const QString &matchingLine)
0226 {
0227     m_fileItem = _fileItem;
0228     m_subDir = subDir;
0229     m_matchingLine = matchingLine;
0230 
0231     //TODO more caching ?
0232     if (!m_fileItem.isNull() && m_fileItem.isLocalFile()) {
0233         QFileInfo fileInfo(m_fileItem.url().toLocalFile());
0234 
0235         int perm_index;
0236         if (fileInfo.isReadable()) {
0237             perm_index = fileInfo.isWritable() ? RW : RO;
0238         } else {
0239             perm_index = fileInfo.isWritable() ? WO : NA;
0240         }
0241         m_permission = KLocalizedString(perm[perm_index]).toString();
0242 
0243         m_icon = QIcon::fromTheme(m_fileItem.iconName());
0244     }
0245 }
0246 
0247 QVariant KFindItem::data(int column, int role) const
0248 {
0249     if (m_fileItem.isNull()) {
0250         return QVariant();
0251     }
0252 
0253     if (role == Qt::DecorationRole) {
0254         if (column == 0) {
0255             return m_icon;
0256         } else {
0257             return QVariant();
0258         }
0259     }
0260 
0261     if (role == Qt::DisplayRole) {
0262         switch (column) {
0263         case 0:
0264             return m_fileItem.url().fileName();
0265         case 1:
0266             return m_subDir;
0267         case 2:
0268             return KIO::convertSize(m_fileItem.size());
0269         case 3:
0270             return m_fileItem.timeString(KFileItem::ModificationTime);
0271         case 4:
0272             return m_permission;
0273         case 5:
0274             return m_matchingLine;
0275         default:
0276             return QVariant();
0277         }
0278     }
0279 
0280     if (role == Qt::UserRole) {
0281         switch (column) {
0282         case 2:
0283             return m_fileItem.size();
0284         case 3:
0285             return m_fileItem.time(KFileItem::ModificationTime).toSecsSinceEpoch();
0286         default:
0287             return QVariant();
0288         }
0289     }
0290 
0291     return QVariant();
0292 }
0293 
0294 KFileItem KFindItem::getFileItem() const
0295 {
0296     return m_fileItem;
0297 }
0298 
0299 bool KFindItem::isValid() const
0300 {
0301     return !m_fileItem.isNull();
0302 }
0303 
0304 //END KFindItem
0305 
0306 //BEGIN KFindSortFilterProxyModel
0307 
0308 bool KFindSortFilterProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
0309 {
0310     //Order by UserData size in bytes or unix date
0311     if (left.column() == 2 || left.column() == 3) {
0312         qulonglong leftData = sourceModel()->data(left, Qt::UserRole).toULongLong();
0313         qulonglong rightData = sourceModel()->data(right, Qt::UserRole).toULongLong();
0314         return leftData < rightData;
0315     }
0316     // Default sorting rules for string values
0317     else {
0318         return QSortFilterProxyModel::lessThan(left, right);
0319     }
0320 }
0321 
0322 //END KFindSortFilterProxyModel
0323 
0324 //BEGIN KFindTreeView
0325 
0326 KFindTreeView::KFindTreeView(QWidget *parent, KfindDlg *findDialog)
0327     : QTreeView(parent)
0328     , m_model(new KFindItemModel(this))
0329     , m_proxyModel(new KFindSortFilterProxyModel(this))
0330     , m_kfindDialog(findDialog)
0331 {
0332     m_proxyModel->setSourceModel(m_model);
0333     setModel(m_proxyModel);
0334 
0335     //Configure QTreeView
0336     setRootIsDecorated(false);
0337     setSelectionMode(QAbstractItemView::ExtendedSelection);
0338     setSortingEnabled(true);
0339     setDragEnabled(true);
0340     setContextMenuPolicy(Qt::CustomContextMenu);
0341 
0342     connect(this, &KFindTreeView::customContextMenuRequested, this, &KFindTreeView::contextMenuRequested);
0343 
0344     //Mouse single/double click settings
0345     reconfigureMouseSettings();
0346 
0347     // TODO: this is a workaround until  Qt-issue 176832 has been fixed (from Dolphin)
0348     connect(this, &KFindTreeView::pressed, this, &KFindTreeView::updateMouseButtons);
0349 
0350     //Generate popup menu actions
0351     m_actionCollection = new KActionCollection(this);
0352     m_actionCollection->addAssociatedWidget(this);
0353 
0354     QAction *open = KStandardAction::open(this, SLOT(slotExecuteSelected()), this);
0355     m_actionCollection->addAction(QStringLiteral("file_open"), open);
0356 
0357     QAction *copy = KStandardAction::copy(this, SLOT(copySelection()), this);
0358     m_actionCollection->addAction(QStringLiteral("edit_copy"), copy);
0359 
0360     QAction *copyPathAction = m_actionCollection->addAction(QStringLiteral("copy_location"));
0361     copyPathAction->setText(i18nc("@action:incontextmenu", "Copy Location"));
0362     copyPathAction->setWhatsThis(i18nc("@info:whatsthis copy_location", "This will copy the path of the first selected item into the clipboard."));
0363     copyPathAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-copy-path")));
0364     KActionCollection::setDefaultShortcuts(copyPathAction, {Qt::CTRL | Qt::ALT | Qt::Key_C});
0365     connect(copyPathAction, &QAction::triggered, this, &KFindTreeView::copySelectionPath);
0366 
0367     QAction *openFolder = new QAction(QIcon::fromTheme(QStringLiteral("window-new")), i18n("&Open containing folder(s)"), this);
0368     connect(openFolder, &QAction::triggered, this, &KFindTreeView::openContainingFolder);
0369     m_actionCollection->addAction(QStringLiteral("openfolder"), openFolder);
0370 
0371     QAction *del = new QAction(QIcon::fromTheme(QStringLiteral("edit-delete")), i18n("&Delete"), this);
0372     connect(del, &QAction::triggered, this, &KFindTreeView::deleteSelectedFiles);
0373     KActionCollection::setDefaultShortcut(del, Qt::SHIFT | Qt::Key_Delete);
0374 
0375     QAction *trash = new QAction(QIcon::fromTheme(QStringLiteral("user-trash")), i18n("&Move to Trash"), this);
0376     connect(trash, &QAction::triggered, this, &KFindTreeView::moveToTrashSelectedFiles);
0377     KActionCollection::setDefaultShortcut(trash, Qt::Key_Delete);
0378     m_actionCollection->addAction(QStringLiteral("trash"), trash);
0379 
0380     header()->setStretchLastSection(true);
0381 
0382     sortByColumn(0, Qt::AscendingOrder);
0383 }
0384 
0385 KFindTreeView::~KFindTreeView()
0386 {
0387     delete m_model;
0388     delete m_proxyModel;
0389     delete m_actionCollection;
0390 }
0391 
0392 void KFindTreeView::resizeToContents()
0393 {
0394     resizeColumnToContents(0);
0395     resizeColumnToContents(1);
0396     resizeColumnToContents(2);
0397     resizeColumnToContents(3);
0398 }
0399 
0400 QString KFindTreeView::reducedDir(const QString &fullDir)
0401 {
0402     QString relDir = m_baseDir.relativeFilePath(fullDir);
0403     if (!relDir.startsWith(QLatin1String(".."))) {
0404         return relDir;
0405     }
0406     return fullDir;
0407 }
0408 
0409 int KFindTreeView::itemCount() const
0410 {
0411     return m_model->rowCount();
0412 }
0413 
0414 void KFindTreeView::beginSearch(const QUrl &baseUrl)
0415 {
0416     m_baseDir = QDir(baseUrl.toLocalFile());
0417     m_model->clear();
0418 }
0419 
0420 void KFindTreeView::endSearch()
0421 {
0422     resizeToContents();
0423 }
0424 
0425 QList<QPersistentModelIndex> KFindTreeView::selectedVisibleIndexes() {
0426     QModelIndexList selected = selectedIndexes();
0427     if (selected.empty()) {
0428         return QList<QPersistentModelIndex>();
0429     }
0430     QModelIndex index = indexAt(QPoint(0, 0));
0431     QModelIndex bottomIndex = indexAt(viewport()->rect().bottomLeft());
0432     QList<QPersistentModelIndex> result;
0433     while (index.isValid()) {
0434         if (selected.contains(index)) {
0435             result.append(QPersistentModelIndex(index));
0436         }
0437         if (index == bottomIndex) {
0438             break;
0439         }
0440         index = indexBelow(index);
0441     }
0442     return result;
0443 }
0444 
0445 void KFindTreeView::insertItems(const QList< QPair<KFileItem, QString> > &pairs)
0446 {
0447     auto hScroll = horizontalScrollBar()->value();
0448     QPersistentModelIndex topIndex(indexAt(rect().topLeft()));
0449     QList<QPersistentModelIndex> selectedVisible = selectedVisibleIndexes();
0450 
0451     m_model->insertFileItems(pairs);
0452 
0453     if (topIndex.isValid()) {
0454         if (verticalScrollBar()->value() > 0) {
0455             scrollTo(topIndex, QAbstractItemView::PositionAtTop);
0456         }
0457     }
0458     if (!selectedVisible.empty()) {
0459         scrollTo(selectedVisible.last());
0460     }
0461     horizontalScrollBar()->setValue(hScroll);
0462 }
0463 
0464 void KFindTreeView::removeItem(const QUrl &url)
0465 {
0466     QList<QUrl> list = selectedUrls();
0467     if (list.contains(url)) {
0468         //Close menu
0469         delete m_contextMenu;
0470         m_contextMenu = nullptr;
0471     }
0472     m_model->removeItem(url);
0473 }
0474 
0475 bool KFindTreeView::isInserted(const QUrl &url) const
0476 {
0477     return m_model->isInserted(url);
0478 }
0479 
0480 // copy to clipboard
0481 void KFindTreeView::copySelection()
0482 {
0483     QMimeData *mime = m_model->mimeData(m_proxyModel->mapSelectionToSource(selectionModel()->selection()).indexes());
0484     if (mime) {
0485         QClipboard *cb = qApp->clipboard();
0486         cb->setMimeData(mime);
0487     }
0488 }
0489 
0490 void KFindTreeView::copySelectionPath()
0491 {
0492     const auto selectedIndexes = m_proxyModel->mapSelectionToSource(selectionModel()->selection()).indexes();
0493     if (selectedIndexes.isEmpty()) {
0494         return;
0495     }
0496 
0497     const auto item = m_model->itemAtIndex(selectedIndexes.at(0)).getFileItem();
0498 
0499     QString path = item.localPath();
0500     if (path.isEmpty()) {
0501         path = item.url().toDisplayString();
0502     }
0503 
0504     QClipboard *clipboard = QApplication::clipboard();
0505     if (!clipboard) {
0506         return;
0507     }
0508     clipboard->setText(path);
0509 }
0510 
0511 void KFindTreeView::saveResults()
0512 {
0513     QFileDialog dialog;
0514     dialog.setAcceptMode(QFileDialog::AcceptSave);
0515     dialog.setWindowTitle(i18nc("@title:window", "Save Results As"));
0516     dialog.setMimeTypeFilters({
0517         QStringLiteral("text/html"),
0518         QStringLiteral("text/plain")
0519     });
0520 
0521     if (!dialog.exec()) {
0522         return;
0523     }
0524 
0525     if (dialog.selectedUrls().isEmpty()) {
0526         return;
0527     }
0528 
0529     const QUrl u = dialog.selectedUrls().constFirst();
0530     if (!u.isValid() || !u.isLocalFile()) {
0531         return;
0532     }
0533 
0534     QString filename = u.toLocalFile();
0535 
0536     QFile file(filename);
0537 
0538     if (!file.open(QIODevice::WriteOnly)) {
0539         KMessageBox::error(parentWidget(),
0540                            i18n("Unable to save results."));
0541     } else {
0542         QTextStream stream(&file);
0543         stream.setEncoding(QStringConverter::System);
0544 
0545         const QList<KFindItem> itemList = m_model->getItemList();
0546         if (dialog.selectedMimeTypeFilter() == QLatin1String("text/html")) {
0547             stream << QString::fromLatin1("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
0548                                           "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\"><html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
0549                                           "<head>\n"
0550                                           "<title>%2</title></head>\n"
0551                                           "<meta charset=\"%1\">\n"
0552                                           "<body>\n<h1>%2</h1>\n"
0553                                           "<dl>\n")
0554                 .arg(QString::fromLatin1(QTextCodec::codecForLocale()->name()), i18n("KFind Results File"));
0555 
0556             for (const KFindItem &item : itemList) {
0557                 const KFileItem fileItem = item.getFileItem();
0558                 stream << QStringLiteral("<dt><a href=\"%1\">%2</a></dt>\n").arg(
0559                     fileItem.url().url(), fileItem.url().toDisplayString());
0560             }
0561             stream << QStringLiteral("</dl>\n</body>\n</html>\n");
0562         } else {
0563             for (const KFindItem &item : itemList) {
0564                 stream << item.getFileItem().url().url() << Qt::endl;
0565             }
0566         }
0567 
0568         file.close();
0569         m_kfindDialog->setStatusMsg(i18nc("%1=filename", "Results were saved to: %1", filename));
0570     }
0571 }
0572 
0573 void KFindTreeView::openContainingFolder()
0574 {
0575     KIO::highlightInFileManager(selectedUrls());
0576 }
0577 
0578 void KFindTreeView::slotExecuteSelected()
0579 {
0580     const QModelIndexList selected = m_proxyModel->mapSelectionToSource(selectionModel()->selection()).indexes();
0581     if (selected.isEmpty()) {
0582         return;
0583     }
0584 
0585     //TODO if >X add a warn ?
0586     for (const QModelIndex &index : selected) {
0587         if (index.column() == 0) {
0588             KFindItem item = m_model->itemAtIndex(index);
0589             if (item.isValid()) {
0590                 auto *job = new KIO::OpenUrlJob(item.getFileItem().targetUrl());
0591                 job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0592                 job->start();
0593             }
0594         }
0595     }
0596 }
0597 
0598 void KFindTreeView::slotExecute(const QModelIndex &index)
0599 {
0600     if ((m_mouseButtons & Qt::LeftButton) && QApplication::keyboardModifiers() == Qt::NoModifier) {
0601         if (!index.isValid()) {
0602             return;
0603         }
0604 
0605         QModelIndex realIndex = m_proxyModel->mapToSource(index);
0606         if (!realIndex.isValid()) {
0607             return;
0608         }
0609 
0610         const KFindItem item = m_model->itemAtIndex(realIndex);
0611         if (item.isValid()) {
0612             auto *job = new KIO::OpenUrlJob(item.getFileItem().targetUrl());
0613             job->setUiDelegate(KIO::createDefaultJobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, this));
0614             job->start();
0615         }
0616     }
0617 }
0618 
0619 void KFindTreeView::contextMenuRequested(const QPoint &p)
0620 {
0621     KFileItemList fileList;
0622 
0623     const QModelIndexList selected = m_proxyModel->mapSelectionToSource(selectionModel()->selection()).indexes();
0624     if (selected.isEmpty()) {
0625         return;
0626     }
0627 
0628     for (const QModelIndex &index : selected) {
0629         if (index.column() == 0) {
0630             const KFindItem item = m_model->itemAtIndex(index);
0631             if (item.isValid()) {
0632                 fileList.append(item.getFileItem());
0633             }
0634         }
0635     }
0636 
0637     delete m_contextMenu;
0638     m_contextMenu = new QMenu(this);
0639     m_contextMenu->addAction(m_actionCollection->action(QStringLiteral("file_open")));
0640     m_contextMenu->addAction(m_actionCollection->action(QStringLiteral("openfolder")));
0641     m_contextMenu->addAction(m_actionCollection->action(QStringLiteral("edit_copy")));
0642 
0643     QAction *copyLocationAction = m_actionCollection->action(QStringLiteral("copy_location"));
0644     // It's not worth it to track the selection and update the action live.
0645     copyLocationAction->setEnabled(fileList.count() == 1);
0646     m_contextMenu->addAction(copyLocationAction);
0647 
0648     //m_contextMenu->addAction(m_actionCollection->action(QLatin1String("del")));
0649     m_contextMenu->addAction(m_actionCollection->action(QStringLiteral("trash")));
0650     m_contextMenu->addSeparator();
0651 
0652     // Open With...
0653     KFileItemActions menuActions;
0654     KFileItemListProperties fileProperties(fileList);
0655     menuActions.setItemListProperties(fileProperties);
0656     menuActions.insertOpenWithActionsTo(nullptr, m_contextMenu, QStringList());
0657     // 'Actions' submenu
0658     menuActions.addActionsTo(m_contextMenu);
0659 
0660     m_contextMenu->addSeparator();
0661 
0662     // Properties
0663     if (KPropertiesDialog::canDisplay(fileList)) {
0664         QAction *act = new QAction(m_contextMenu);
0665         act->setText(i18n("&Properties"));
0666         QObject::connect(act, &QAction::triggered, [this, fileList]() {
0667             KPropertiesDialog::showDialog(fileList, this, false /*non modal*/);
0668         });
0669         m_contextMenu->addAction(act);
0670     }
0671 
0672     m_contextMenu->exec(this->mapToGlobal(p));
0673 }
0674 
0675 QList<QUrl> KFindTreeView::selectedUrls() const
0676 {
0677     QList<QUrl> uris;
0678 
0679     const QModelIndexList indexes = m_proxyModel->mapSelectionToSource(selectionModel()->selection()).indexes();
0680     for (const QModelIndex &index : indexes) {
0681         if (index.column() == 0 && index.isValid()) {
0682             KFindItem item = m_model->itemAtIndex(index);
0683             if (item.isValid()) {
0684                 uris.append(item.getFileItem().url());
0685             }
0686         }
0687     }
0688 
0689     return uris;
0690 }
0691 
0692 void KFindTreeView::deleteSelectedFiles()
0693 {
0694     QList<QUrl> uris = selectedUrls();
0695     if (uris.isEmpty()) {
0696         return;
0697     }
0698 
0699     using Iface = KIO::AskUserActionInterface;
0700     auto *trashJob = new KIO::DeleteOrTrashJob(uris, Iface::Delete, Iface::ForceConfirmation, this);
0701     trashJob->start();
0702 }
0703 
0704 void KFindTreeView::moveToTrashSelectedFiles()
0705 {
0706     QList<QUrl> uris = selectedUrls();
0707     if (uris.isEmpty()) {
0708         return;
0709     }
0710 
0711     using Iface = KIO::AskUserActionInterface;
0712     auto *trashJob = new KIO::DeleteOrTrashJob(uris, Iface::Trash, Iface::ForceConfirmation, this);
0713     trashJob->start();
0714 }
0715 
0716 void KFindTreeView::reconfigureMouseSettings()
0717 {
0718     disconnect(this, &KFindTreeView::clicked, this, &KFindTreeView::slotExecute);
0719     disconnect(this, &KFindTreeView::doubleClicked, this, &KFindTreeView::slotExecute);
0720 
0721     if (m_kfindDialog->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) {
0722         connect(this, &KFindTreeView::clicked, this, &KFindTreeView::slotExecute);
0723     } else {
0724         connect(this, &KFindTreeView::doubleClicked, this, &KFindTreeView::slotExecute);
0725     }
0726 }
0727 
0728 void KFindTreeView::updateMouseButtons()
0729 {
0730     m_mouseButtons = QApplication::mouseButtons();
0731 }
0732 
0733 void KFindTreeView::dragMoveEvent(QDragMoveEvent *e)
0734 {
0735     e->accept();
0736 }
0737 
0738 //END KFindTreeView
0739 
0740 #include "moc_kfindtreeview.cpp"