File indexing completed on 2024-12-01 05:18:25

0001 /*
0002     kfinddlg.cpp
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 
0006 */
0007 
0008 #include "kfinddlg.h"
0009 
0010 #include <QDialogButtonBox>
0011 #include <QHBoxLayout>
0012 #include <QPushButton>
0013 #include <QStatusBar>
0014 
0015 #include <KAboutApplicationDialog>
0016 #include <KAboutData>
0017 #include <KGuiItem>
0018 #include <KHelpMenu>
0019 #include <KLocalizedString>
0020 #include <KMessageBox>
0021 
0022 #include "kftabdlg.h"
0023 #include "kquery.h"
0024 #include "kfindtreeview.h"
0025 
0026 KfindDlg::KfindDlg(const QUrl &url, QWidget *parent)
0027     : QDialog(parent)
0028 {
0029     QHBoxLayout *mainLayout = new QHBoxLayout(this);
0030     setModal(true);
0031     QWidget::setWindowTitle(i18nc("@title:window", "Find Files/Folders"));
0032 
0033     QFrame *frame = new QFrame;
0034     mainLayout->addWidget(frame);
0035 
0036     // create tabwidget
0037     tabWidget = new KfindTabWidget(frame);
0038     mainLayout->addWidget(tabWidget);
0039     tabWidget->setURL(url);
0040 
0041     // prepare window for find results
0042     win = new KFindTreeView(frame, this);
0043 
0044     mStatusBar = new QStatusBar(frame);
0045     
0046     m_labelStatus = new QLabel(mStatusBar);
0047     m_labelStatus->setAlignment (Qt::AlignLeft | Qt::AlignVCenter);
0048     m_labelStatus->setText(i18nc("the application is currently idle, there is no active search", "Idle."));
0049 
0050     m_labelProgress = new QLabel(mStatusBar);
0051     m_labelProgress->setAlignment (Qt::AlignRight | Qt::AlignVCenter);
0052     m_labelProgress->setText(QString());
0053     
0054     mStatusBar->addWidget(m_labelStatus, 1);
0055     mStatusBar->addWidget(m_labelProgress, 0);
0056     
0057     QVBoxLayout *vBox = new QVBoxLayout(frame);
0058     vBox->addWidget(tabWidget, 0);
0059     vBox->addWidget(win, 1);
0060     vBox->addWidget(mStatusBar, 0);
0061 
0062     QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close, this);
0063     m_findButton = new QPushButton;
0064     buttonBox->addButton(m_findButton, QDialogButtonBox::ActionRole);
0065     m_stopButton = new QPushButton;
0066     buttonBox->addButton(m_stopButton, QDialogButtonBox::ActionRole);
0067     m_saveAsButton = new QPushButton;
0068     buttonBox->addButton(m_saveAsButton, QDialogButtonBox::ActionRole);
0069     buttonBox->setOrientation(Qt::Vertical);
0070     m_findButton->setDefault(true);
0071 
0072     KGuiItem::assign(m_findButton, KStandardGuiItem::find());
0073     KGuiItem::assign(m_stopButton, KStandardGuiItem::stop());
0074     KGuiItem::assign(m_saveAsButton, KStandardGuiItem::saveAs());
0075 
0076     m_findButton->setEnabled(true); // Enable "Find"
0077     m_stopButton->setEnabled(false); // Disable "Stop"
0078     m_saveAsButton->setEnabled(false); // Disable "Save As..."
0079 
0080     mainLayout->addWidget(buttonBox);
0081 
0082     connect(tabWidget, &KfindTabWidget::startSearch, this, &KfindDlg::startSearch);
0083     connect(m_findButton, &QPushButton::clicked, this, &KfindDlg::startSearch);
0084     connect(m_stopButton, &QPushButton::clicked, this, &KfindDlg::stopSearch);
0085     connect(m_saveAsButton, &QPushButton::clicked, win, &KFindTreeView::saveResults);
0086 
0087     connect(buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &KfindDlg::finishAndClose);
0088 
0089     connect(win, &KFindTreeView::resultSelected, this, &KfindDlg::resultSelected);
0090 
0091     query = new KQuery(frame);
0092     connect(query, &KQuery::result, this, &KfindDlg::slotResult);
0093     connect(query, &KQuery::foundFileList, this, &KfindDlg::addFiles);
0094 
0095     KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true);
0096     buttonBox->button(QDialogButtonBox::Help)->setMenu(helpMenu->menu());
0097     dirwatch = nullptr;
0098     
0099     setFocus();
0100 }
0101 
0102 KfindDlg::~KfindDlg()
0103 {
0104     stopSearch();
0105 
0106     delete dirwatch;
0107 }
0108 
0109 void KfindDlg::finishAndClose()
0110 {
0111     //Stop the current search and closes the dialog
0112     stopSearch();
0113     close();
0114 }
0115 
0116 void KfindDlg::setProgressMsg(const QString &msg)
0117 {
0118     m_labelProgress->setText(msg);
0119 }
0120 
0121 void KfindDlg::setStatusMsg(const QString &msg)
0122 {
0123     m_labelStatus->setText(msg);
0124 }
0125 
0126 void KfindDlg::startSearch()
0127 {
0128     tabWidget->setQuery(query);
0129 
0130     isResultReported = false;
0131 
0132     // Reset count - use the same i18n as below
0133     setProgressMsg(i18n("0 items found"));
0134 
0135     Q_EMIT resultSelected(false);
0136     Q_EMIT haveResults(false);
0137 
0138     m_findButton->setEnabled(false); // Disable "Find"
0139     m_stopButton->setEnabled(true); // Enable "Stop"
0140     m_saveAsButton->setEnabled(false); // Disable "Save As..."
0141 
0142     delete dirwatch;
0143     dirwatch = new KDirWatch();
0144     connect(dirwatch, &KDirWatch::created, this, &KfindDlg::slotNewItems);
0145     connect(dirwatch, &KDirWatch::deleted, this, &KfindDlg::slotDeleteItem);
0146     dirwatch->addDir(query->url().toLocalFile(), KDirWatch::WatchFiles);
0147 
0148 #if 0
0149     // waba: Watching for updates is disabled for now because even with FAM it causes too
0150     // much problems. See BR68220, BR77854, BR77846, BR79512 and BR85802
0151     // There are 3 problems:
0152     // 1) addDir() keeps looping on recursive symlinks
0153     // 2) addDir() scans all subdirectories, so it basically does the same as the process that
0154     // is started by KQuery but in-process, undoing the advantages of using a separate find process
0155     // A solution could be to let KQuery Q_EMIT all the directories it has searched in.
0156     // Either way, putting dirwatchers on a whole file system is probably just too much.
0157     // 3) FAM has a tendency to deadlock with so many files (See BR77854) This has hopefully
0158     // been fixed in KDirWatch, but that has not yet been confirmed.
0159 
0160     //Getting a list of all subdirs
0161     if (tabWidget->isSearchRecursive() && (dirwatch->internalMethod() == KDirWatch::FAM)) {
0162         const QStringList subdirs = getAllSubdirs(query->url().path());
0163         for (QStringList::const_iterator it = subdirs.constBegin(); it != subdirs.constEnd(); ++it) {
0164             dirwatch->addDir(*it, true);
0165         }
0166     }
0167 #endif
0168 
0169     win->beginSearch(query->url());
0170     tabWidget->beginSearch();
0171 
0172     setStatusMsg(i18n("Searching..."));
0173     query->start();
0174 }
0175 
0176 void KfindDlg::stopSearch()
0177 {
0178     query->kill();
0179 }
0180 
0181 void KfindDlg::newSearch()
0182 {
0183     // WABA: Not used any longer?
0184     stopSearch();
0185 
0186     tabWidget->setDefaults();
0187 
0188     Q_EMIT haveResults(false);
0189     Q_EMIT resultSelected(false);
0190 
0191     setFocus();
0192 }
0193 
0194 void KfindDlg::slotResult(int errorCode)
0195 {
0196     if (errorCode == 0) {
0197         setStatusMsg(i18nc("the application is currently idle, there is no active search", "Idle."));
0198     } else if (errorCode == KIO::ERR_USER_CANCELED) {
0199         setStatusMsg(i18n("Canceled."));
0200     } else if (errorCode == KIO::ERR_MALFORMED_URL) {
0201         setStatusMsg(i18n("Error."));
0202         KMessageBox::error(this, i18n("Please specify an absolute path in the \"Look in\" box."));
0203     } else if (errorCode == KIO::ERR_DOES_NOT_EXIST) {
0204         setStatusMsg(i18n("Error."));
0205         KMessageBox::error(this, i18n("Could not find the specified folder."));
0206     } else {
0207         setStatusMsg(i18n("Error."));
0208     }
0209 
0210     m_findButton->setEnabled(true); // Enable "Find"
0211     m_stopButton->setEnabled(false); // Disable "Stop"
0212     m_saveAsButton->setEnabled(true); // Enable "Save As..."
0213 
0214     win->endSearch();
0215     tabWidget->endSearch();
0216     setFocus();
0217 }
0218 
0219 void KfindDlg::addFiles(const QList< QPair<KFileItem, QString> > &pairs)
0220 {
0221     win->insertItems(pairs);
0222 
0223     if (!isResultReported) {
0224         Q_EMIT haveResults(true);
0225         isResultReported = true;
0226     }
0227 
0228     const QString str = i18np("one item found", "%1 items found", win->itemCount());
0229     setProgressMsg(str);
0230 }
0231 
0232 void KfindDlg::setFocus()
0233 {
0234     tabWidget->setFocus();
0235 }
0236 
0237 void KfindDlg::copySelection()
0238 {
0239     win->copySelection();
0240 }
0241 
0242 void KfindDlg::about()
0243 {
0244     KAboutApplicationDialog dlg(KAboutData::applicationData(), KAboutApplicationDialog::NoOptions, this);
0245     dlg.exec();
0246 }
0247 
0248 void KfindDlg::slotDeleteItem(const QString &file)
0249 {
0250     win->removeItem(QUrl::fromLocalFile(file));
0251 
0252     const QString str = i18np("one item found", "%1 items found", win->itemCount());
0253     setProgressMsg(str);
0254 }
0255 
0256 void KfindDlg::slotNewItems(const QString &file)
0257 {
0258     const QUrl url = QUrl::fromLocalFile(file);
0259 
0260     if (query->url().isParentOf(url)) {
0261         if (!win->isInserted(url)) {
0262             query->slotListEntries(QStringList() << file);
0263         }
0264     }
0265 }
0266 
0267 QStringList KfindDlg::getAllSubdirs(QDir d)
0268 {
0269     d.setFilter(QDir::Dirs);
0270     const QStringList dirs = d.entryList(QDir::NoDotAndDotDot);
0271     QStringList subdirs;
0272     subdirs.reserve(dirs.size());
0273 
0274     for (const auto &dir : dirs) {
0275         subdirs.append(d.path() + QLatin1Char('/') + dir);
0276         subdirs += getAllSubdirs(QDir(d.path() + QLatin1Char('/') + dir));
0277     }
0278     return subdirs;
0279 }
0280 
0281 #include "moc_kfinddlg.cpp"