File indexing completed on 2023-12-03 05:56:30

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