From d1a30be5abcc2d5e5340866434b2691275a135a6 Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Tue, 5 Jul 2016 13:26:53 +0200 Subject: Polish the findfiles example to be actually useful MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Simplify the code, remove unused members - Fix the translations of plurals to use %n - Add tooltip displaying full paths in list - Add context menu allowing to copy the name and open - Display the correct slashes on Windows - Connect the returnPressed() signals of the line edits - Make the search recursive - Do not search binary files by checking the mime type Change-Id: I3663799c88931db1f58c03ea35211e7ab03737ec Reviewed-by: Topi Reiniƶ --- examples/widgets/dialogs/findfiles/window.cpp | 139 +++++++++++++++++++------- examples/widgets/dialogs/findfiles/window.h | 6 +- examples/widgets/doc/src/findfiles.qdoc | 38 +++++-- 3 files changed, 135 insertions(+), 48 deletions(-) (limited to 'examples') diff --git a/examples/widgets/dialogs/findfiles/window.cpp b/examples/widgets/dialogs/findfiles/window.cpp index 780c398ad5..ce53dd8c83 100644 --- a/examples/widgets/dialogs/findfiles/window.cpp +++ b/examples/widgets/dialogs/findfiles/window.cpp @@ -42,51 +42,72 @@ #include "window.h" +//! [17] +enum { absoluteFileNameRole = Qt::UserRole + 1 }; +//! [17] + +//! [18] +static inline QString fileNameOfItem(const QTableWidgetItem *item) +{ + return item->data(absoluteFileNameRole).toString(); +} +//! [18] + +//! [14] +static inline void openFile(const QString &fileName) +{ + QDesktopServices::openUrl(QUrl::fromLocalFile(fileName)); +} +//! [14] + //! [0] Window::Window(QWidget *parent) : QWidget(parent) { - browseButton = new QPushButton(tr("&Browse..."), this); + QPushButton *browseButton = new QPushButton(tr("&Browse..."), this); connect(browseButton, &QAbstractButton::clicked, this, &Window::browse); findButton = new QPushButton(tr("&Find"), this); connect(findButton, &QAbstractButton::clicked, this, &Window::find); fileComboBox = createComboBox(tr("*")); + connect(fileComboBox->lineEdit(), &QLineEdit::returnPressed, + this, &Window::animateFindClick); textComboBox = createComboBox(); - directoryComboBox = createComboBox(QDir::currentPath()); + connect(textComboBox->lineEdit(), &QLineEdit::returnPressed, + this, &Window::animateFindClick); + directoryComboBox = createComboBox(QDir::toNativeSeparators(QDir::currentPath())); + connect(directoryComboBox->lineEdit(), &QLineEdit::returnPressed, + this, &Window::animateFindClick); - fileLabel = new QLabel(tr("Named:")); - textLabel = new QLabel(tr("Containing text:")); - directoryLabel = new QLabel(tr("In directory:")); filesFoundLabel = new QLabel; createFilesTable(); //! [0] //! [1] - QGridLayout *mainLayout = new QGridLayout; - mainLayout->addWidget(fileLabel, 0, 0); + QGridLayout *mainLayout = new QGridLayout(this); + mainLayout->addWidget(new QLabel(tr("Named:")), 0, 0); mainLayout->addWidget(fileComboBox, 0, 1, 1, 2); - mainLayout->addWidget(textLabel, 1, 0); + mainLayout->addWidget(new QLabel(tr("Containing text:")), 1, 0); mainLayout->addWidget(textComboBox, 1, 1, 1, 2); - mainLayout->addWidget(directoryLabel, 2, 0); + mainLayout->addWidget(new QLabel(tr("In directory:")), 2, 0); mainLayout->addWidget(directoryComboBox, 2, 1); mainLayout->addWidget(browseButton, 2, 2); mainLayout->addWidget(filesTable, 3, 0, 1, 3); mainLayout->addWidget(filesFoundLabel, 4, 0, 1, 2); mainLayout->addWidget(findButton, 4, 2); - setLayout(mainLayout); setWindowTitle(tr("Find Files")); - resize(700, 300); + const QRect screenGeometry = QApplication::desktop()->screenGeometry(this); + resize(screenGeometry.width() / 2, screenGeometry.height() / 3); } //! [1] //! [2] void Window::browse() { - QString directory = QFileDialog::getExistingDirectory(this, - tr("Find Files"), QDir::currentPath()); + QString directory = + QDir::toNativeSeparators(QFileDialog::getExistingDirectory(this, tr("Find Files"), QDir::currentPath())); if (!directory.isEmpty()) { if (directoryComboBox->findText(directory) == -1) @@ -102,14 +123,28 @@ static void updateComboBox(QComboBox *comboBox) comboBox->addItem(comboBox->currentText()); } +//! [13] + +static void findRecursion(const QString &path, const QString &pattern, QStringList *result) +{ + QDir currentDir(path); + const QString prefix = path + QLatin1Char('/'); + foreach (const QString &match, currentDir.entryList(QStringList(pattern), QDir::Files | QDir::NoSymLinks)) + result->append(prefix + match); + foreach (const QString &dir, currentDir.entryList(QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot)) + findRecursion(prefix + dir, pattern, result); +} + +//! [13] //! [3] + void Window::find() { filesTable->setRowCount(0); QString fileName = fileComboBox->currentText(); QString text = textComboBox->currentText(); - QString path = directoryComboBox->currentText(); + QString path = QDir::cleanPath(directoryComboBox->currentText()); //! [3] updateComboBox(fileComboBox); @@ -117,19 +152,21 @@ void Window::find() updateComboBox(directoryComboBox); //! [4] + currentDir = QDir(path); QStringList files; - if (fileName.isEmpty()) - fileName = "*"; - files = currentDir.entryList(QStringList(fileName), - QDir::Files | QDir::NoSymLinks); - + findRecursion(path, fileName.isEmpty() ? QStringLiteral("*") : fileName, &files); if (!text.isEmpty()) files = findFiles(files, text); showFiles(files); } //! [4] +void Window::animateFindClick() +{ + findButton->animateClick(); +} + //! [5] QStringList Window::findFiles(const QStringList &files, const QString &text) { @@ -139,21 +176,26 @@ QStringList Window::findFiles(const QStringList &files, const QString &text) progressDialog.setWindowTitle(tr("Find Files")); //! [5] //! [6] + QMimeDatabase mimeDatabase; QStringList foundFiles; for (int i = 0; i < files.size(); ++i) { progressDialog.setValue(i); - progressDialog.setLabelText(tr("Searching file number %1 of %2...") - .arg(i).arg(files.size())); - qApp->processEvents(); + progressDialog.setLabelText(tr("Searching file number %1 of %n...", 0, files.size()).arg(i)); + QCoreApplication::processEvents(); //! [6] if (progressDialog.wasCanceled()) break; //! [7] - QFile file(currentDir.absoluteFilePath(files[i])); - + const QString fileName = files.at(i); + const QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileName); + if (mimeType.isValid() && !mimeType.inherits(QStringLiteral("text/plain"))) { + qWarning() << "Not searching binary file " << QDir::toNativeSeparators(fileName); + continue; + } + QFile file(fileName); if (file.open(QIODevice::ReadOnly)) { QString line; QTextStream in(&file); @@ -161,7 +203,7 @@ QStringList Window::findFiles(const QStringList &files, const QString &text) if (progressDialog.wasCanceled()) break; line = in.readLine(); - if (line.contains(text)) { + if (line.contains(text, Qt::CaseInsensitive)) { foundFiles << files[i]; break; } @@ -176,13 +218,18 @@ QStringList Window::findFiles(const QStringList &files, const QString &text) void Window::showFiles(const QStringList &files) { for (int i = 0; i < files.size(); ++i) { - QFile file(currentDir.absoluteFilePath(files[i])); - qint64 size = QFileInfo(file).size(); - - QTableWidgetItem *fileNameItem = new QTableWidgetItem(files[i]); + const QString &fileName = files.at(i); + const QString toolTip = QDir::toNativeSeparators(fileName); + const QString relativePath = QDir::toNativeSeparators(currentDir.relativeFilePath(fileName)); + const qint64 size = QFileInfo(fileName).size(); + QTableWidgetItem *fileNameItem = new QTableWidgetItem(relativePath); + fileNameItem->setData(absoluteFileNameRole, QVariant(fileName)); + fileNameItem->setToolTip(toolTip); fileNameItem->setFlags(fileNameItem->flags() ^ Qt::ItemIsEditable); QTableWidgetItem *sizeItem = new QTableWidgetItem(tr("%1 KB") .arg(int((size + 1023) / 1024))); + sizeItem->setData(absoluteFileNameRole, QVariant(fileName)); + sizeItem->setToolTip(toolTip); sizeItem->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter); sizeItem->setFlags(sizeItem->flags() ^ Qt::ItemIsEditable); @@ -191,8 +238,7 @@ void Window::showFiles(const QStringList &files) filesTable->setItem(row, 0, fileNameItem); filesTable->setItem(row, 1, sizeItem); } - filesFoundLabel->setText(tr("%1 file(s) found").arg(files.size()) + - (" (Double click on a file to open it)")); + filesFoundLabel->setText(tr("%n file(s) found (Double click on a file to open it)", 0, files.size())); filesFoundLabel->setWordWrap(true); } //! [8] @@ -220,20 +266,43 @@ void Window::createFilesTable() filesTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); filesTable->verticalHeader()->hide(); filesTable->setShowGrid(false); - +//! [15] + filesTable->setContextMenuPolicy(Qt::CustomContextMenu); + connect(filesTable, &QTableWidget::customContextMenuRequested, + this, &Window::contextMenu); connect(filesTable, &QTableWidget::cellActivated, this, &Window::openFileOfItem); +//! [15] } //! [11] + //! [12] void Window::openFileOfItem(int row, int /* column */) { - QTableWidgetItem *item = filesTable->item(row, 0); - - QDesktopServices::openUrl(QUrl::fromLocalFile(currentDir.absoluteFilePath(item->text()))); + const QTableWidgetItem *item = filesTable->item(row, 0); + openFile(fileNameOfItem(item)); } //! [12] +//! [16] +void Window::contextMenu(const QPoint &pos) +{ + const QTableWidgetItem *item = filesTable->itemAt(pos); + if (!item) + return; + QMenu menu; + QAction *copyAction = menu.addAction("Copy Name"); + QAction *openAction = menu.addAction("Open"); + QAction *action = menu.exec(filesTable->mapToGlobal(pos)); + if (!action) + return; + const QString fileName = fileNameOfItem(item); + if (action == copyAction) + QGuiApplication::clipboard()->setText(QDir::toNativeSeparators(fileName)); + else if (action == openAction) + openFile(fileName); +} +//! [16] diff --git a/examples/widgets/dialogs/findfiles/window.h b/examples/widgets/dialogs/findfiles/window.h index 89dd87b83b..119c0fa100 100644 --- a/examples/widgets/dialogs/findfiles/window.h +++ b/examples/widgets/dialogs/findfiles/window.h @@ -63,7 +63,9 @@ public: private slots: void browse(); void find(); + void animateFindClick(); void openFileOfItem(int row, int column); + void contextMenu(const QPoint &pos); private: QStringList findFiles(const QStringList &files, const QString &text); @@ -74,11 +76,7 @@ private: QComboBox *fileComboBox; QComboBox *textComboBox; QComboBox *directoryComboBox; - QLabel *fileLabel; - QLabel *textLabel; - QLabel *directoryLabel; QLabel *filesFoundLabel; - QPushButton *browseButton; QPushButton *findButton; QTableWidget *filesTable; diff --git a/examples/widgets/doc/src/findfiles.qdoc b/examples/widgets/doc/src/findfiles.qdoc index dd06ed8bb4..df2ef40768 100644 --- a/examples/widgets/doc/src/findfiles.qdoc +++ b/examples/widgets/doc/src/findfiles.qdoc @@ -120,10 +120,12 @@ \snippet dialogs/findfiles/window.cpp 4 We use the directory's path to create a QDir; the QDir class - provides access to directory structures and their contents. We - create a list of the files (contained in the newly created QDir) - that match the specified file name. If the file name is empty - the list will contain all the files in the directory. + provides access to directory structures and their contents. + + \snippet dialogs/findfiles/window.cpp 13 + + We recursively create a list of the files (contained in the newl + created QDir) that match the specified file name. Then we search through all the files in the list, using the private \c findFiles() function, eliminating the ones that don't contain @@ -173,9 +175,7 @@ \snippet dialogs/findfiles/window.cpp 7 - After updating the QProgressDialog, we create a QFile using the - QDir::absoluteFilePath() function which returns the absolute path - name of a file in the directory. We open the file in read-only + After updating the QProgressDialog, we open the file in read-only mode, and read one line at a time using QTextStream. The QTextStream class provides a convenient interface for reading @@ -194,9 +194,18 @@ Both the \c findFiles() and \c showFiles() functions are called from the \c find() slot. In the \c showFiles() function we run through - the provided list of file names, adding each file name to the + the provided list of file names, adding each relative file name to the first column in the table widget and retrieving the file's size using - QFile and QFileInfo for the second column. + QFileInfo for the second column. For later use, we set + the absolute path as a data on the QTableWidget using the + the role absoluteFileNameRole defined to be Qt::UserRole + 1. + + \snippet dialogs/findfiles/window.cpp 17 + + This allows for retrieving the name of an item using a + convenience function: + + \snippet dialogs/findfiles/window.cpp 18 We also update the total number of files found. @@ -236,8 +245,19 @@ \snippet dialogs/findfiles/window.cpp 12 + \snippet dialogs/findfiles/window.cpp 14 + The \c openFileOfItem() slot is invoked when the user double clicks on a cell in the table. The QDesktopServices::openUrl() knows how to open a file given the file name. + + \snippet dialogs/findfiles/window.cpp 15 + \snippet dialogs/findfiles/window.cpp 16 + + We set the context menu policy to of the table view to Qt::CustomContextMenu + and connect a slot contextMenu() to its signal + customContextMenuRequested(). We retrieve the absolute file name + from the data of the QTableWidgetItem and populate the context menu + with actions offering to copy the file name and to open the file. */ -- cgit v1.2.3