kfinddlg.cpp 9.1 KB
Newer Older
1 2
/*******************************************************************
* kfinddlg.cpp
Laurent Montel's avatar
Laurent Montel committed
3
*
4 5
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
Laurent Montel's avatar
Laurent Montel committed
6
* published by the Free Software Foundation; either version 2 of
7
* the License, or (at your option) any later version.
Laurent Montel's avatar
Laurent Montel committed
8
*
9 10 11 12
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
Laurent Montel's avatar
Laurent Montel committed
13
*
14 15
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
Laurent Montel's avatar
Laurent Montel committed
16
*
17 18
******************************************************************/

Max Blazejak's avatar
Max Blazejak committed
19
#include "kfinddlg.h"
20

Yuri Chornoivan's avatar
Yuri Chornoivan committed
21 22
#include <QDialogButtonBox>
#include <QHBoxLayout>
23
#include <QPushButton>
24
#include <QStatusBar>
25

Yuri Chornoivan's avatar
Yuri Chornoivan committed
26
#include <KAboutApplicationDialog>
27
#include <KAboutData>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
28 29
#include <KGuiItem>
#include <KHelpMenu>
Laurent Montel's avatar
Laurent Montel committed
30
#include <KLocalizedString>
Yuri Chornoivan's avatar
Yuri Chornoivan committed
31
#include <KMessageBox>
32 33 34

#include "kftabdlg.h"
#include "kquery.h"
35
#include "kfindtreeview.h"
36

Laurent Montel's avatar
Laurent Montel committed
37
KfindDlg::KfindDlg(const QUrl &url, QWidget *parent)
Yuri Chornoivan's avatar
Yuri Chornoivan committed
38
    : QDialog(parent)
39
{
Yuri Chornoivan's avatar
Yuri Chornoivan committed
40 41
    QHBoxLayout *mainLayout = new QHBoxLayout;
    setLayout(mainLayout);
Laurent Montel's avatar
Laurent Montel committed
42 43
    setModal(true);
    QWidget::setWindowTitle(i18nc("@title:window", "Find Files/Folders"));
44

Laurent Montel's avatar
Laurent Montel committed
45
    isResultReported = false;
46

Laurent Montel's avatar
Laurent Montel committed
47
    QFrame *frame = new QFrame;
Yuri Chornoivan's avatar
Yuri Chornoivan committed
48
    mainLayout->addWidget(frame);
49

Laurent Montel's avatar
Laurent Montel committed
50 51
    // create tabwidget
    tabWidget = new KfindTabWidget(frame);
Yuri Chornoivan's avatar
Yuri Chornoivan committed
52
    mainLayout->addWidget(tabWidget);
Laurent Montel's avatar
Laurent Montel committed
53
    tabWidget->setURL(url);
54

Laurent Montel's avatar
Laurent Montel committed
55 56
    // prepare window for find results
    win = new KFindTreeView(frame, this);
57

58 59 60 61 62 63 64 65 66 67 68 69 70
    mStatusBar = new QStatusBar(frame);
    
    m_labelStatus = new QLabel(mStatusBar);
    m_labelStatus->setAlignment (Qt::AlignLeft | Qt::AlignVCenter);
    m_labelStatus->setText(i18nc("the application is currently idle, there is no active search", "Idle."));

    m_labelProgress = new QLabel(mStatusBar);
    m_labelProgress->setAlignment (Qt::AlignRight | Qt::AlignVCenter);
    m_labelProgress->setText(QString());
    
    mStatusBar->addWidget(m_labelStatus, 1);
    mStatusBar->addWidget(m_labelProgress, 0);
    
Laurent Montel's avatar
Laurent Montel committed
71 72 73 74
    QVBoxLayout *vBox = new QVBoxLayout(frame);
    vBox->addWidget(tabWidget, 0);
    vBox->addWidget(win, 1);
    vBox->addWidget(mStatusBar, 0);
75

Yuri Chornoivan's avatar
Yuri Chornoivan committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
    QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close, this);
    m_findButton = new QPushButton;
    buttonBox->addButton(m_findButton, QDialogButtonBox::ActionRole);
    m_stopButton = new QPushButton;
    buttonBox->addButton(m_stopButton, QDialogButtonBox::ActionRole);
    m_saveAsButton = new QPushButton;
    buttonBox->addButton(m_saveAsButton, QDialogButtonBox::ActionRole);
    buttonBox->setOrientation(Qt::Vertical);
    m_findButton->setDefault(true);

    KGuiItem::assign(m_findButton, KStandardGuiItem::find());
    KGuiItem::assign(m_stopButton, KStandardGuiItem::stop());
    KGuiItem::assign(m_saveAsButton, KStandardGuiItem::saveAs());

    m_findButton->setEnabled(true); // Enable "Find"
    m_stopButton->setEnabled(false); // Disable "Stop"
    m_saveAsButton->setEnabled(false); // Disable "Save As..."

    mainLayout->addWidget(buttonBox);

    connect(tabWidget, &KfindTabWidget::startSearch, this, &KfindDlg::startSearch);
    connect(m_findButton, &QPushButton::clicked, this, &KfindDlg::startSearch);
    connect(m_stopButton, &QPushButton::clicked, this, &KfindDlg::stopSearch);
    connect(m_saveAsButton, &QPushButton::clicked, win, &KFindTreeView::saveResults);
100

Yuri Chornoivan's avatar
Yuri Chornoivan committed
101
    connect(buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &KfindDlg::finishAndClose);
102

Yuri Chornoivan's avatar
Yuri Chornoivan committed
103
    connect(win, &KFindTreeView::resultSelected, this, &KfindDlg::resultSelected);
104

Laurent Montel's avatar
Laurent Montel committed
105
    query = new KQuery(frame);
106 107
    connect(query, &KQuery::result, this, &KfindDlg::slotResult);
    connect(query, &KQuery::foundFileList, this, &KfindDlg::addFiles);
108

Laurent Montel's avatar
Laurent Montel committed
109
    KHelpMenu *helpMenu = new KHelpMenu(this, KAboutData::applicationData(), true);
Yuri Chornoivan's avatar
Yuri Chornoivan committed
110
    buttonBox->button(QDialogButtonBox::Help)->setMenu(helpMenu->menu());
Laurent Montel's avatar
Laurent Montel committed
111
    dirwatch = nullptr;
112 113
    
    setFocus();
114 115 116 117
}

KfindDlg::~KfindDlg()
{
Laurent Montel's avatar
Laurent Montel committed
118 119 120
    stopSearch();

    delete dirwatch;
121 122
}

123
void KfindDlg::finishAndClose()
124
{
Laurent Montel's avatar
Laurent Montel committed
125 126 127
    //Stop the current search and closes the dialog
    stopSearch();
    close();
128 129 130 131
}

void KfindDlg::setProgressMsg(const QString &msg)
{
132
    m_labelProgress->setText(msg);
133 134 135 136
}

void KfindDlg::setStatusMsg(const QString &msg)
{
137
    m_labelStatus->setText(msg);
138 139 140 141
}

void KfindDlg::startSearch()
{
Laurent Montel's avatar
Laurent Montel committed
142
    tabWidget->setQuery(query);
143

Laurent Montel's avatar
Laurent Montel committed
144
    isResultReported = false;
145

Laurent Montel's avatar
Laurent Montel committed
146
    // Reset count - use the same i18n as below
147
    setProgressMsg(i18n("0 items found"));
148

Laurent Montel's avatar
Laurent Montel committed
149 150
    emit resultSelected(false);
    emit haveResults(false);
151

Yuri Chornoivan's avatar
Yuri Chornoivan committed
152 153 154
    m_findButton->setEnabled(false); // Disable "Find"
    m_stopButton->setEnabled(true); // Enable "Stop"
    m_saveAsButton->setEnabled(false); // Disable "Save As..."
155

Laurent Montel's avatar
Laurent Montel committed
156 157
    delete dirwatch;
    dirwatch = new KDirWatch();
158 159
    connect(dirwatch, &KDirWatch::created, this, &KfindDlg::slotNewItems);
    connect(dirwatch, &KDirWatch::deleted, this, &KfindDlg::slotDeleteItem);
Laurent Montel's avatar
Laurent Montel committed
160
    dirwatch->addDir(query->url().toLocalFile(), KDirWatch::WatchFiles);
161

162
#if 0
Laurent Montel's avatar
Laurent Montel committed
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
    // waba: Watching for updates is disabled for now because even with FAM it causes too
    // much problems. See BR68220, BR77854, BR77846, BR79512 and BR85802
    // There are 3 problems:
    // 1) addDir() keeps looping on recursive symlinks
    // 2) addDir() scans all subdirectories, so it basically does the same as the process that
    // is started by KQuery but in-process, undoing the advantages of using a separate find process
    // A solution could be to let KQuery emit all the directories it has searched in.
    // Either way, putting dirwatchers on a whole file system is probably just too much.
    // 3) FAM has a tendency to deadlock with so many files (See BR77854) This has hopefully
    // been fixed in KDirWatch, but that has not yet been confirmed.

    //Getting a list of all subdirs
    if (tabWidget->isSearchRecursive() && (dirwatch->internalMethod() == KDirWatch::FAM)) {
        const QStringList subdirs = getAllSubdirs(query->url().path());
        for (QStringList::const_iterator it = subdirs.constBegin(); it != subdirs.constEnd(); ++it) {
            dirwatch->addDir(*it, true);
        }
    }
181
#endif
Stephan Binner's avatar
Stephan Binner committed
182

Laurent Montel's avatar
Laurent Montel committed
183 184
    win->beginSearch(query->url());
    tabWidget->beginSearch();
185

Laurent Montel's avatar
Laurent Montel committed
186 187
    setStatusMsg(i18n("Searching..."));
    query->start();
188 189 190 191
}

void KfindDlg::stopSearch()
{
Laurent Montel's avatar
Laurent Montel committed
192
    query->kill();
193 194 195 196
}

void KfindDlg::newSearch()
{
Laurent Montel's avatar
Laurent Montel committed
197 198
    // WABA: Not used any longer?
    stopSearch();
199

Laurent Montel's avatar
Laurent Montel committed
200
    tabWidget->setDefaults();
201

Laurent Montel's avatar
Laurent Montel committed
202 203
    emit haveResults(false);
    emit resultSelected(false);
204

Laurent Montel's avatar
Laurent Montel committed
205
    setFocus();
206 207 208 209
}

void KfindDlg::slotResult(int errorCode)
{
Laurent Montel's avatar
Laurent Montel committed
210 211 212 213 214 215 216 217 218 219 220 221 222 223
    if (errorCode == 0) {
        setStatusMsg(i18nc("the application is currently idle, there is no active search", "Idle."));
    } else if (errorCode == KIO::ERR_USER_CANCELED) {
        setStatusMsg(i18n("Canceled."));
    } else if (errorCode == KIO::ERR_MALFORMED_URL) {
        setStatusMsg(i18n("Error."));
        KMessageBox::sorry(this, i18n("Please specify an absolute path in the \"Look in\" box."));
    } else if (errorCode == KIO::ERR_DOES_NOT_EXIST) {
        setStatusMsg(i18n("Error."));
        KMessageBox::sorry(this, i18n("Could not find the specified folder."));
    } else {
        setStatusMsg(i18n("Error."));
    }

Yuri Chornoivan's avatar
Yuri Chornoivan committed
224 225 226
    m_findButton->setEnabled(true); // Enable "Find"
    m_stopButton->setEnabled(false); // Disable "Stop"
    m_saveAsButton->setEnabled(true); // Enable "Save As..."
227

Laurent Montel's avatar
Laurent Montel committed
228 229 230
    win->endSearch();
    tabWidget->endSearch();
    setFocus();
231 232
}

Laurent Montel's avatar
Laurent Montel committed
233
void KfindDlg::addFiles(const QList< QPair<KFileItem, QString> > &pairs)
234
{
Laurent Montel's avatar
Laurent Montel committed
235
    win->insertItems(pairs);
236

Laurent Montel's avatar
Laurent Montel committed
237 238 239 240
    if (!isResultReported) {
        emit haveResults(true);
        isResultReported = true;
    }
241

242
    const QString str = i18np("one item found", "%1 items found", win->itemCount());
Laurent Montel's avatar
Laurent Montel committed
243
    setProgressMsg(str);
244 245 246 247
}

void KfindDlg::setFocus()
{
Laurent Montel's avatar
Laurent Montel committed
248
    tabWidget->setFocus();
249 250 251 252
}

void KfindDlg::copySelection()
{
Laurent Montel's avatar
Laurent Montel committed
253
    win->copySelection();
254 255
}

Laurent Montel's avatar
Laurent Montel committed
256
void KfindDlg::about()
257
{
258
    KAboutApplicationDialog dlg(KAboutData::applicationData(), KAboutApplicationDialog::NoOptions, this);
Laurent Montel's avatar
Laurent Montel committed
259
    dlg.exec();
260
}
261

Laurent Montel's avatar
Laurent Montel committed
262
void KfindDlg::slotDeleteItem(const QString &file)
263
{
Laurent Montel's avatar
Laurent Montel committed
264 265
    win->removeItem(QUrl::fromLocalFile(file));

266
    const QString str = i18np("one item found", "%1 items found", win->itemCount());
Laurent Montel's avatar
Laurent Montel committed
267
    setProgressMsg(str);
268 269
}

Laurent Montel's avatar
Laurent Montel committed
270
void KfindDlg::slotNewItems(const QString &file)
271
{
272
    const QUrl url = QUrl::fromLocalFile(file);
Laurent Montel's avatar
Laurent Montel committed
273 274 275 276 277

    if (query->url().isParentOf(url)) {
        if (!win->isInserted(url)) {
            query->slotListEntries(QStringList() << file);
        }
278 279
    }
}
280 281 282

QStringList KfindDlg::getAllSubdirs(QDir d)
{
Laurent Montel's avatar
Laurent Montel committed
283 284 285 286 287 288
    QStringList dirs;
    QStringList subdirs;

    d.setFilter(QDir::Dirs);
    dirs = d.entryList();

Laurent Montel's avatar
Laurent Montel committed
289 290
    const QStringList::const_iterator end(dirs.constEnd());
    for (QStringList::const_iterator it = dirs.constBegin(); it != end; ++it) {
Laurent Montel's avatar
Laurent Montel committed
291 292 293 294 295 296 297
        if ((*it == QLatin1String(".")) || (*it == QLatin1String(".."))) {
            continue;
        }
        subdirs.append(d.path()+QLatin1Char('/')+*it);
        subdirs += getAllSubdirs(QString(d.path()+QLatin1Char('/')+*it));
    }
    return subdirs;
298
}