// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only

/*!
    \example dialogs/findfiles
    \title Find Files Example
    \ingroup examples-dialogs

    \brief A dialog for finding files in a specified folder.

    The Find Files application allows the user to search for files in a
    specified directory, matching a given file name or wildcard,
    and containing a specified string (if filled in). The search
    result is displayed in a table containing the names of the files
    and their sizes. The application also shows the number of files found.

    The Find Files example illustrates the use of several classes:

    \table
    \row
       \li QProgressDialog
       \li Provide feedback on the progress of a search operation
    \row
       \li QFileDialog
       \li Browse through a file list
    \row
       \li QTextStream
       \li Use stream operators to read a file
    \row
       \li QTableWidget
       \li Browse through the search results in a table
    \row
       \li QDesktopServices
       \li Open files in the result list in a suitable application
    \endtable

    \image findfiles-example.png Screenshot of the Find Files example


    \section1 Window Class Definition

    The \c Window class inherits QWidget, and is the main application
    widget. It shows the search options and displays the search
    results.

    \snippet dialogs/findfiles/window.h 0

    The application has two private slots:
    \table
    \row
        \li The \c browse() slot
        \li Called whenever the user wants to browse for a directory to search in
    \row
        \li The \c find() slot
        \li Called whenever the user launches a search with the \uicontrol Find button
    \endtable

    In addition we declare several private functions:

    \table
        \row
            \li findFiles()
            \li Search for files matching the search parameters
        \row
            \li showFiles()
            \li Display the search result
        \row
            \li ceateButton()
            \li Construct the widget
        \row
            \li createComboBox()
            \li Construct the widget
        \row
            \li createFilesTable()
            \li Construct the widget
    \endtable

    \section1 Window Class Implementation

    In the constructor we first create the application's widgets.

    \snippet dialogs/findfiles/window.cpp 0

    We create the widgets to build up the UI, and we add them to a main layout
    using QGridLayout. We have, however, put the \c Find and \c Quit buttons
    and a stretchable space in a separate \l QHBoxLayout first, to make the
    buttons appear in the \c Window widget's bottom right corner.

    Alternatively, we could have used Qt Designer to construct a UI file,
    and \l {uic} to generate this code.

    \snippet dialogs/findfiles/window.cpp 1

    We did not create a \l QMenuBar with a \uicontrol Quit menu item; but we
    would still like to have a keyboard shortcut for quitting. Since we
    construct a \l QShortcut with \l QKeySequence::Quit, and connect it to
    \l QApplication::quit(), on most platforms it will be possible to press
    Control-Q to quit (or whichever standard Quit key is configured on that platform).
    (On \macos, this is redundant, because every application gets a
    \uicontrol Quit menu item automatically; but it helps to make the application portable.)

    \snippet dialogs/findfiles/window.cpp 2

    The \c browse() slot presents a file dialog to the user, using the
    QFileDialog class. QFileDialog enables a user to traverse the file
    system in order to select one or many files or a directory. The
    easiest way to create a QFileDialog is to use the convenience
    static functions.

    Here we use the static QFileDialog::getExistingDirectory()
    function which returns an existing directory selected by the
    user. Then we display the directory in the directory combobox
    using the QComboBox::addItem() function and update the current
    index.

    QComboBox::addItem() adds an item to the combobox with the given
    text (if not already present in the list), and containing
    the specified userData. The item is appended to the list of
    existing items.

    \snippet dialogs/findfiles/window.cpp 3

    The \c find() slot is called whenever the user requests a new
    search by pressing the \uicontrol Find button.

    First we eliminate any previous search results by setting the
    table widgets row count to zero. Then we retrieve the
    specified file name, text, and directory path from the respective
    comboboxes.

    \snippet dialogs/findfiles/window.cpp 4

    We use the directory's path to create a QDir; the QDir class
    provides access to the directory structure and its contents.

    We use QDirIterator to iterate over the files that match the
    specified file name and build a QStringList of paths.

    Then we search through all the files in the list, using the private
    \c findFiles() function, eliminating the ones that don't contain the
    specified text. We sort them (because QDirIterator did not). And finally,
    we display the results using the private \c showFiles() function.

    If the user didn't specify any text, there is no reason to search
    through the files, so we sort and display the results immediately.

    \image findfiles_progress_dialog.png Screenshot of the Progress Dialog

    \snippet dialogs/findfiles/window.cpp 5

    In the private \c findFiles() function we search through a list of
    files, looking for the ones that contain a specified text. This
    can be a very slow operation depending on the number of files as
    well as their sizes. QProgressDialog displays a progress dialog
    if the application has to search through a large number of files,
    or if some of the files have a large size. QProgressDialog can
    also allow the user to abort the operation if it takes too much
    time.

    \snippet dialogs/findfiles/window.cpp 6

    We run through the files, one at a time, and for each file we
    update the QProgressDialog value. This property holds the current
    amount of progress made. We also update the progress dialog's
    label.

    Then we call the QCoreApplication::processEvents() function using
    the QApplication object. In this way we interleave the display of
    the progress made with the process of searching through the files
    so the application doesn't appear to be frozen.

    The QApplication class manages the GUI application's control flow
    and main settings. It contains the main event loop, where all
    events from the window system and other sources are processed and
    dispatched. QApplication inherits QCoreApplication.  The
    QCoreApplication::processEvents() function processes all pending
    events according to the specified QEventLoop::ProcessEventFlags
    until there are no more events to process. The default flags are
    QEventLoop::AllEvents.

    \snippet dialogs/findfiles/window.cpp 7

    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
    and writing text. Using QTextStream's streaming operators, you can
    conveniently read and write words, lines and numbers.

    For each line we read we check if the QProgressDialog has been
    canceled. If it has, we abort the operation, otherwise we check if
    the line contains the specified text. When we find the text within
    one of the files, we add the file's name to a list of found files
    that contain the specified text, and start searching a new file.

    Finally, we return the list of the files found.

    \snippet dialogs/findfiles/window.cpp 8

    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 relative file name to the
    first column in the table widget and retrieving the file's size using
    QFileInfo for the second column. We use \l QLocale::formattedDataSize()
    to format the file size in a human-readable form. 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.

    \snippet dialogs/findfiles/window.cpp 10

    The private \c createComboBox() function is also called from the
    constructor. We create a QComboBox with the given text, and make it
    editable.

    When the user enters a new string in an editable combobox, the
    widget may or may not insert it, and it can insert it in several
    locations, depending on the QComboBox::InsertPolicy. The default
    policy is is QComboBox::InsertAtBottom.

    Then we add the provided text to the combobox, and specify the
    widget's size policies, before we return a pointer to the
    combobox.

    \snippet dialogs/findfiles/window.cpp 11

    The private \c createFilesTable() function is called from the
    constructor. In this function we create the QTableWidget that
    will display the search results. We set its horizontal headers and
    their resize mode.

    QTableWidget inherits QTableView which provides a default
    model/view implementation of a table view. The
    QTableView::horizontalHeader() function returns the table view's
    horizontal header as a QHeaderView. The QHeaderView class provides
    a header row or header column for item views, and the
    QHeaderView::setResizeMode() function sets the constraints on how
    the section in the header can be resized.

    Finally, we hide the QTableWidget's vertical headers using the
    QWidget::hide() function, and remove the default grid drawn for
    the table using the QTableView::setShowGrid() function.

    \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.
*/

