1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the examples of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:BSD$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** BSD License Usage
18** Alternatively, you may use this file under the terms of the BSD license
19** as follows:
20**
21** "Redistribution and use in source and binary forms, with or without
22** modification, are permitted provided that the following conditions are
23** met:
24** * Redistributions of source code must retain the above copyright
25** notice, this list of conditions and the following disclaimer.
26** * Redistributions in binary form must reproduce the above copyright
27** notice, this list of conditions and the following disclaimer in
28** the documentation and/or other materials provided with the
29** distribution.
30** * Neither the name of The Qt Company Ltd nor the names of its
31** contributors may be used to endorse or promote products derived
32** from this software without specific prior written permission.
33**
34**
35** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
36** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
37** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
38** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
39** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
40** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
41** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
42** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
43** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
44** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
45** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
46**
47** $QT_END_LICENSE$
48**
49****************************************************************************/
50
51#include <QtWidgets>
52
53#include "window.h"
54
55//! [17]
56enum { absoluteFileNameRole = Qt::UserRole + 1 };
57//! [17]
58
59//! [18]
60static inline QString fileNameOfItem(const QTableWidgetItem *item)
61{
62 return item->data(absoluteFileNameRole).toString();
63}
64//! [18]
65
66//! [14]
67static inline void openFile(const QString &fileName)
68{
69 QDesktopServices::openUrl(QUrl::fromLocalFile(fileName));
70}
71//! [14]
72
73//! [0]
74Window::Window(QWidget *parent)
75 : QWidget(parent)
76{
77 setWindowTitle(tr("Find Files"));
78 QPushButton *browseButton = new QPushButton(tr("&Browse..."), this);
79 connect(browseButton, &QAbstractButton::clicked, this, &Window::browse);
80 findButton = new QPushButton(tr("&Find"), this);
81 connect(findButton, &QAbstractButton::clicked, this, &Window::find);
82
83 fileComboBox = createComboBox(tr("*"));
84 connect(fileComboBox->lineEdit(), &QLineEdit::returnPressed,
85 this, &Window::animateFindClick);
86 textComboBox = createComboBox();
87 connect(textComboBox->lineEdit(), &QLineEdit::returnPressed,
88 this, &Window::animateFindClick);
89 directoryComboBox = createComboBox(QDir::toNativeSeparators(QDir::currentPath()));
90 connect(directoryComboBox->lineEdit(), &QLineEdit::returnPressed,
91 this, &Window::animateFindClick);
92
93 filesFoundLabel = new QLabel;
94
95 createFilesTable();
96
97 QGridLayout *mainLayout = new QGridLayout(this);
98 mainLayout->addWidget(new QLabel(tr("Named:")), 0, 0);
99 mainLayout->addWidget(fileComboBox, 0, 1, 1, 2);
100 mainLayout->addWidget(new QLabel(tr("Containing text:")), 1, 0);
101 mainLayout->addWidget(textComboBox, 1, 1, 1, 2);
102 mainLayout->addWidget(new QLabel(tr("In directory:")), 2, 0);
103 mainLayout->addWidget(directoryComboBox, 2, 1);
104 mainLayout->addWidget(browseButton, 2, 2);
105 mainLayout->addWidget(filesTable, 3, 0, 1, 3);
106 mainLayout->addWidget(filesFoundLabel, 4, 0, 1, 2);
107 mainLayout->addWidget(findButton, 4, 2);
108//! [0]
109
110//! [1]
111 connect(new QShortcut(QKeySequence::Quit, this), &QShortcut::activated,
112 qApp, &QApplication::quit);
113//! [1]
114}
115
116//! [2]
117void Window::browse()
118{
119 QString directory =
120 QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Find Files"), QDir::currentPath()));
121
122 if (!directory.isEmpty()) {
123 if (directoryComboBox->findText(directory) == -1)
124 directoryComboBox->addItem(directory);
125 directoryComboBox->setCurrentIndex(directoryComboBox->findText(directory));
126 }
127}
128//! [2]
129
130static void updateComboBox(QComboBox *comboBox)
131{
132 if (comboBox->findText(comboBox->currentText()) == -1)
133 comboBox->addItem(comboBox->currentText());
134}
135
136//! [3]
137void Window::find()
138{
139 filesTable->setRowCount(0);
140
141 QString fileName = fileComboBox->currentText();
142 QString text = textComboBox->currentText();
143 QString path = QDir::cleanPath(directoryComboBox->currentText());
144 currentDir = QDir(path);
145//! [3]
146
147 updateComboBox(fileComboBox);
148 updateComboBox(textComboBox);
149 updateComboBox(directoryComboBox);
150
151//! [4]
152 QStringList filter;
153 if (!fileName.isEmpty())
154 filter << fileName;
155 QDirIterator it(path, filter, QDir::AllEntries | QDir::NoSymLinks | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
156 QStringList files;
157 while (it.hasNext())
158 files << it.next();
159 if (!text.isEmpty())
160 files = findFiles(files, text);
161 files.sort();
162 showFiles(files);
163}
164//! [4]
165
166void Window::animateFindClick()
167{
168 findButton->animateClick();
169}
170
171//! [5]
172QStringList Window::findFiles(const QStringList &files, const QString &text)
173{
174 QProgressDialog progressDialog(this);
175 progressDialog.setCancelButtonText(tr("&Cancel"));
176 progressDialog.setRange(0, files.size());
177 progressDialog.setWindowTitle(tr("Find Files"));
178
179//! [5] //! [6]
180 QMimeDatabase mimeDatabase;
181 QStringList foundFiles;
182
183 for (int i = 0; i < files.size(); ++i) {
184 progressDialog.setValue(i);
185 progressDialog.setLabelText(tr("Searching file number %1 of %n...", nullptr, files.size()).arg(i));
186 QCoreApplication::processEvents();
187//! [6]
188
189 if (progressDialog.wasCanceled())
190 break;
191
192//! [7]
193 const QString fileName = files.at(i);
194 const QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileName);
195 if (mimeType.isValid() && !mimeType.inherits(QStringLiteral("text/plain"))) {
196 qWarning() << "Not searching binary file " << QDir::toNativeSeparators(fileName);
197 continue;
198 }
199 QFile file(fileName);
200 if (file.open(QIODevice::ReadOnly)) {
201 QString line;
202 QTextStream in(&file);
203 while (!in.atEnd()) {
204 if (progressDialog.wasCanceled())
205 break;
206 line = in.readLine();
207 if (line.contains(text, Qt::CaseInsensitive)) {
208 foundFiles << files[i];
209 break;
210 }
211 }
212 }
213 }
214 return foundFiles;
215}
216//! [7]
217
218//! [8]
219void Window::showFiles(const QStringList &paths)
220{
221 for (const QString &filePath : paths) {
222 const QString toolTip = QDir::toNativeSeparators(filePath);
223 const QString relativePath = QDir::toNativeSeparators(currentDir.relativeFilePath(filePath));
224 const qint64 size = QFileInfo(filePath).size();
225 QTableWidgetItem *fileNameItem = new QTableWidgetItem(relativePath);
226 fileNameItem->setData(absoluteFileNameRole, QVariant(filePath));
227 fileNameItem->setToolTip(toolTip);
228 fileNameItem->setFlags(fileNameItem->flags() ^ Qt::ItemIsEditable);
229 QTableWidgetItem *sizeItem = new QTableWidgetItem(QLocale().formattedDataSize(size));
230 sizeItem->setData(absoluteFileNameRole, QVariant(filePath));
231 sizeItem->setToolTip(toolTip);
232 sizeItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
233 sizeItem->setFlags(sizeItem->flags() ^ Qt::ItemIsEditable);
234
235 int row = filesTable->rowCount();
236 filesTable->insertRow(row);
237 filesTable->setItem(row, 0, fileNameItem);
238 filesTable->setItem(row, 1, sizeItem);
239 }
240 filesFoundLabel->setText(tr("%n file(s) found (Double click on a file to open it)", nullptr, paths.size()));
241 filesFoundLabel->setWordWrap(true);
242}
243//! [8]
244
245//! [10]
246QComboBox *Window::createComboBox(const QString &text)
247{
248 QComboBox *comboBox = new QComboBox;
249 comboBox->setEditable(true);
250 comboBox->addItem(text);
251 comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
252 return comboBox;
253}
254//! [10]
255
256//! [11]
257void Window::createFilesTable()
258{
259 filesTable = new QTableWidget(0, 2);
260 filesTable->setSelectionBehavior(QAbstractItemView::SelectRows);
261
262 QStringList labels;
263 labels << tr("Filename") << tr("Size");
264 filesTable->setHorizontalHeaderLabels(labels);
265 filesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch);
266 filesTable->verticalHeader()->hide();
267 filesTable->setShowGrid(false);
268//! [15]
269 filesTable->setContextMenuPolicy(Qt::CustomContextMenu);
270 connect(filesTable, &QTableWidget::customContextMenuRequested,
271 this, &Window::contextMenu);
272 connect(filesTable, &QTableWidget::cellActivated,
273 this, &Window::openFileOfItem);
274//! [15]
275}
276//! [11]
277
278
279//! [12]
280
281void Window::openFileOfItem(int row, int /* column */)
282{
283 const QTableWidgetItem *item = filesTable->item(row, 0);
284 openFile(fileNameOfItem(item));
285}
286
287//! [12]
288
289//! [16]
290void Window::contextMenu(const QPoint &pos)
291{
292 const QTableWidgetItem *item = filesTable->itemAt(pos);
293 if (!item)
294 return;
295 QMenu menu;
296#ifndef QT_NO_CLIPBOARD
297 QAction *copyAction = menu.addAction("Copy Name");
298#endif
299 QAction *openAction = menu.addAction("Open");
300 QAction *action = menu.exec(filesTable->mapToGlobal(pos));
301 if (!action)
302 return;
303 const QString fileName = fileNameOfItem(item);
304 if (action == openAction)
305 openFile(fileName);
306#ifndef QT_NO_CLIPBOARD
307 else if (action == copyAction)
308 QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(fileName));
309#endif
310}
311//! [16]
312