1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    By using JUCE, you agree to the terms of both the JUCE 6 End-User License
11    Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
12 
13    End User License Agreement: www.juce.com/juce-6-licence
14    Privacy Policy: www.juce.com/juce-privacy-policy
15 
16    Or: You may also use this code under the terms of the GPL v3 (see
17    www.gnu.org/licenses).
18 
19    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21    DISCLAIMED.
22 
23   ==============================================================================
24 */
25 
26 namespace juce
27 {
28 
29 //==============================================================================
30 /**
31     A class to asynchronously scan for details about the files in a directory.
32 
33     This keeps a list of files and some information about them, using a background
34     thread to scan for more files. As files are found, it broadcasts change messages
35     to tell any listeners.
36 
37     @see FileListComponent, FileBrowserComponent
38 
39     @tags{GUI}
40 */
41 class JUCE_API  DirectoryContentsList   : public ChangeBroadcaster,
42                                           private TimeSliceClient
43 {
44 public:
45     //==============================================================================
46     /** Creates a directory list.
47 
48         To set the directory it should point to, use setDirectory(), which will
49         also start it scanning for files on the background thread.
50 
51         When the background thread finds and adds new files to this list, the
52         ChangeBroadcaster class will send a change message, so you can register
53         listeners and update them when the list changes.
54 
55         @param fileFilter       an optional filter to select which files are
56                                 included in the list. If this is nullptr, then all files
57                                 and directories are included. Make sure that the filter
58                                 doesn't get deleted during the lifetime of this object
59         @param threadToUse      a thread object that this list can use
60                                 to scan for files as a background task. Make sure
61                                 that the thread you give it has been started, or you
62                                 won't get any files!
63     */
64     DirectoryContentsList (const FileFilter* fileFilter,
65                            TimeSliceThread& threadToUse);
66 
67     /** Destructor. */
68     ~DirectoryContentsList() override;
69 
70 
71     //==============================================================================
72     /** Returns the directory that's currently being used. */
getDirectory()73     const File& getDirectory() const noexcept               { return root; }
74 
75     /** Sets the directory to look in for files.
76 
77         If the directory that's passed in is different to the current one, this will
78         also start the background thread scanning it for files.
79     */
80     void setDirectory (const File& directory,
81                        bool includeDirectories,
82                        bool includeFiles);
83 
84     /** Returns true if this list contains directories.
85         @see setDirectory
86     */
isFindingDirectories()87     bool isFindingDirectories() const noexcept              { return (fileTypeFlags & File::findDirectories) != 0; }
88 
89     /** Returns true if this list contains files.
90         @see setDirectory
91     */
isFindingFiles()92     bool isFindingFiles() const noexcept                    { return (fileTypeFlags & File::findFiles) != 0; }
93 
94     /** Clears the list, and stops the thread scanning for files. */
95     void clear();
96 
97     /** Clears the list and restarts scanning the directory for files. */
98     void refresh();
99 
100     /** True if the background thread hasn't yet finished scanning for files. */
101     bool isStillLoading() const;
102 
103     /** Tells the list whether or not to ignore hidden files.
104         By default these are ignored.
105     */
106     void setIgnoresHiddenFiles (bool shouldIgnoreHiddenFiles);
107 
108     /** Returns true if hidden files are ignored.
109         @see setIgnoresHiddenFiles
110     */
111     bool ignoresHiddenFiles() const;
112 
113     /** Replaces the current FileFilter.
114         This can be nullptr to have no filter. The DirectoryContentList does not take
115         ownership of this object - it just keeps a pointer to it, so you must manage its
116         lifetime.
117         Note that this only replaces the filter, it doesn't refresh the list - you'll
118         probably want to call refresh() after calling this.
119     */
120     void setFileFilter (const FileFilter* newFileFilter);
121 
122     //==============================================================================
123     /** Contains cached information about one of the files in a DirectoryContentsList.
124     */
125     struct FileInfo
126     {
127         //==============================================================================
128         /** The filename.
129 
130             This isn't a full pathname, it's just the last part of the path, same as you'd
131             get from File::getFileName().
132 
133             To get the full pathname, use DirectoryContentsList::getDirectory().getChildFile (filename).
134         */
135         String filename;
136 
137         /** File size in bytes. */
138         int64 fileSize;
139 
140         /** File modification time.
141             As supplied by File::getLastModificationTime().
142         */
143         Time modificationTime;
144 
145         /** File creation time.
146             As supplied by File::getCreationTime().
147         */
148         Time creationTime;
149 
150         /** True if the file is a directory. */
151         bool isDirectory;
152 
153         /** True if the file is read-only. */
154         bool isReadOnly;
155     };
156 
157     //==============================================================================
158     /** Returns the number of files currently available in the list.
159 
160         The info about one of these files can be retrieved with getFileInfo() or getFile().
161 
162         Obviously as the background thread runs and scans the directory for files, this
163         number will change.
164 
165         @see getFileInfo, getFile
166     */
167     int getNumFiles() const noexcept;
168 
169     /** Returns the cached information about one of the files in the list.
170 
171         If the index is in-range, this will return true and will copy the file's details
172         to the structure that is passed-in.
173 
174         If it returns false, then the index wasn't in range, and the structure won't
175         be affected.
176 
177         @see getNumFiles, getFile
178     */
179     bool getFileInfo (int index, FileInfo& resultInfo) const;
180 
181     /** Returns one of the files in the list.
182 
183         @param index    should be less than getNumFiles(). If this is out-of-range, the
184                         return value will be a default File() object
185         @see getNumFiles, getFileInfo
186     */
187     File getFile (int index) const;
188 
189     /** Returns the file filter being used.
190         The filter is specified in the constructor.
191     */
getFilter()192     const FileFilter* getFilter() const noexcept            { return fileFilter; }
193 
194     /** Returns true if the list contains the specified file. */
195     bool contains (const File&) const;
196 
197     //==============================================================================
198     /** @internal */
getTimeSliceThread()199     TimeSliceThread& getTimeSliceThread() const noexcept    { return thread; }
200 
201 private:
202     File root;
203     const FileFilter* fileFilter = nullptr;
204     TimeSliceThread& thread;
205     int fileTypeFlags = File::ignoreHiddenFiles | File::findFiles;
206 
207     CriticalSection fileListLock;
208     OwnedArray<FileInfo> files;
209 
210     std::unique_ptr<RangedDirectoryIterator> fileFindHandle;
211     std::atomic<bool> shouldStop { true };
212 
213     bool wasEmpty = true;
214 
215     int useTimeSlice() override;
216     void stopSearching();
217     void changed();
218     bool checkNextFile (bool& hasChanged);
219     bool addFile (const File&, bool isDir, int64 fileSize, Time modTime,
220                   Time creationTime, bool isReadOnly);
221     void setTypeFlags (int);
222 
223     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectoryContentsList)
224 };
225 
226 } // namespace juce
227