Hydra 0.20
|
00001 00002 /* 00003 * Copyright (c) 2010 Aleksander B. Demko 00004 * This source code is distributed under the MIT license. 00005 * See the accompanying file LICENSE.MIT.txt for details. 00006 */ 00007 00008 #ifndef __INCLUDED_HYDRADESKTOP_FILELIST_H__ 00009 #define __INCLUDED_HYDRADESKTOP_FILELIST_H__ 00010 00011 #include <assert.h> 00012 00013 #include <vector> 00014 00015 #include <QString> 00016 #include <QMutex> 00017 #include <QAbstractListModel> 00018 #include <QAbstractItemView> 00019 #include <QProgressDialog> 00020 #include <QFileSystemWatcher> 00021 00022 #include <hydra/TR1.h> 00023 #include <hydra/Records.h> 00024 #include <hydra/Query.h> 00025 00026 #include <desktop/FileEntry.h> 00027 #include <desktop/FileEntryCache.h> 00028 00029 namespace desktop 00030 { 00031 class FileEntryCache; //forward 00032 00033 class FileList; 00034 class FileEntryLessThan; 00035 class FileListLoader; 00036 class FileListReloader; 00037 00038 class FileListListener; //forward 00039 00040 class FileSystemWatcher; 00041 } 00042 00043 /** 00044 * A class that monitors the file system and updates the FileList as needed 00045 * 00046 * @author Aleksander Demko 00047 */ 00048 class desktop::FileSystemWatcher : public QObject 00049 { 00050 Q_OBJECT 00051 00052 public: 00053 FileSystemWatcher(FileList *parent); 00054 00055 void setWatchDir(const QString &dir); 00056 00057 private slots: 00058 void onDirChanged(void); 00059 void onTimer(void); 00060 00061 private: 00062 FileList *dm_parent; 00063 QFileSystemWatcher dm_watcher; 00064 QString dm_currentdir; 00065 00066 bool dm_queued; 00067 bool dm_requeue; 00068 }; 00069 00070 /** 00071 * Holds a file (image) list. Not all the images may be loaded. 00072 * Can be backed by a FileMonitor. 00073 * 00074 * @author Aleksander Demko 00075 */ 00076 class desktop::FileList : public QAbstractListModel 00077 { 00078 public: 00079 /// constructor 00080 FileList(FileEntryCache *_cache); 00081 /// destructor 00082 virtual ~FileList(); 00083 00084 // QAbstractListModel 00085 virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; 00086 virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; 00087 virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 00088 virtual QVariant data(const QModelIndex &index, int role) const; 00089 virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); 00090 00091 // may be null 00092 FileList *peer(void) const { return dm_peer; } 00093 00094 // sets the peer, may take null 00095 void setPeer(FileList *peer); 00096 00097 // base stuff 00098 00099 const QString & baseDir(void) const { return dm_base_dir; } 00100 bool isBaseRecurse(void) const { return dm_base_recurse; } 00101 bool isBaseShuffle(void) const { return dm_base_shuffle; } 00102 bool hasBaseQuery(void) const { return !dm_base_query.isEmpty(); } 00103 const QString & baseQueryString(void) const { return dm_base_query; } 00104 std::shared_ptr<hydra::Token> baseQueryToken(void) const { return dm_base_query_token; } 00105 00106 // file stuff 00107 00108 /** 00109 * Does the given index refer to a valid file entry? 00110 * 00111 * @author Aleksander Demko 00112 */ 00113 bool isValid(int index) const { return index>=0 && index<dm_filtered_files.size(); } 00114 00115 /** 00116 * Is there a currently selected file? 00117 * 00118 * @author Aleksander Demko 00119 */ 00120 bool isValid(void) const { return isValid(selectedFileIndex()); } 00121 00122 /** 00123 * Return the FileEntry at the given index 00124 * 00125 * @author Aleksander Demko 00126 */ 00127 const FileEntry & fileAt(int index) const { 00128 assert(isValid(index)); 00129 return *dm_filtered_files[index]; 00130 } 00131 00132 /** 00133 * Return the FileEntry at the given index 00134 * 00135 * @author Aleksander Demko 00136 */ 00137 FileEntry & fileAt(int index) { 00138 assert(isValid(index)); 00139 return *dm_filtered_files[index]; 00140 } 00141 00142 /** 00143 * Return the FileEntry at the given QModelIndex 00144 * 00145 * @author Aleksander Demko 00146 */ 00147 const FileEntry & fileAt(const QModelIndex & index) const { 00148 return fileAt(index.row()); 00149 } 00150 00151 /** 00152 * Return the FileEntry at the given QModelIndex 00153 * 00154 * @author Aleksander Demko 00155 */ 00156 FileEntry & fileAt(const QModelIndex & index) { 00157 return fileAt(index.row()); 00158 } 00159 00160 /** 00161 * Returns the number of files in the list (basically a size() function) 00162 * 00163 * @author Aleksander Demko 00164 */ 00165 int numFiles(void) const { return static_cast<int>(dm_filtered_files.size()); } 00166 00167 /** 00168 * Returns the currently selected file's index. 00169 * Returns -1 if nothing is currently selected 00170 * 00171 * helper for selectionModel. 00172 * 00173 * @author Aleksander Demko 00174 */ 00175 int selectedFileIndex(void) const; 00176 00177 /** 00178 * Returns the currently selected file index, which must be valid. 00179 * 00180 * Also a helper for selectionModel() 00181 * 00182 * @author Aleksander Demko 00183 */ 00184 FileEntry & selectedFile(void) { return fileAt(selectedFileIndex()); } 00185 00186 /** 00187 * Finds the file index that has the given fullfilename. 00188 * Returns -1 on failure. 00189 * 00190 * @author Aleksander Demko 00191 */ 00192 int findFileIndexByName(const QString &fullfilename) const; 00193 00194 /** 00195 * Sets the currently selected file. Will do nothing if the index 00196 * is out of range, or is already the current file. 00197 * This will fire an event if the current file is changed. 00198 * 00199 * Helper for selectionModel() 00200 * 00201 * @author Aleksander Demko 00202 */ 00203 void setSelectedFile(int index); 00204 00205 // quickcopy an existing list 00206 //void quickCopy(FileList *src); 00207 00208 // possibly obsolete, will keep around for now 00209 void addListener(FileListListener *v); 00210 // possibly obsolete, will keep around for now 00211 void removeListener(FileListListener *v); 00212 00213 /** 00214 * Calls onBaseChange on all the Views that are listenting to the FileList. 00215 * 00216 * This works through thr FileListListener system. 00217 * 00218 * @author Aleksander Demko 00219 */ 00220 void emitBaseChange(FileListListener *source = 0); 00221 00222 /** 00223 * Calls onImageChange on all the Views that are listenting to the FileList. 00224 * 00225 * This is usually called when the image is changed, usually because its rotation 00226 * has been changed. 00227 * 00228 * This works through thr FileListListener system. 00229 * 00230 * @author Aleksander Demko 00231 */ 00232 void emitImageChange(int fileIndex, FileListListener *source = 0); 00233 00234 /** 00235 * Returns the internal selection model. 00236 * This keeps tracks of which files are selected (often more than one.) 00237 * Use isSelected(fileList()->index(x,0)) to test. 00238 * 00239 * @author Aleksander Demko 00240 */ 00241 QItemSelectionModel * selectionModel(void) { return &dm_selectionmodel; } 00242 00243 00244 /** 00245 * Smits a signal for the data model that all the tags changed. 00246 * This translates into a QAbstractListModel::dataChanged() call. 00247 * 00248 * This works through the QAbstractListModel system. 00249 * 00250 * @author Aleksander Demko 00251 */ 00252 void emitChangedTagsAll(void); 00253 00254 /** 00255 * Smits a signal for the data model that all the tags changed 00256 * for this particular index. 00257 * This translates into a QAbstractListModel::dataChanged() call. 00258 * 00259 * This works through the QAbstractListModel system. 00260 * 00261 * @author Aleksander Demko 00262 */ 00263 void emitChangedTagsOne(int idx); 00264 00265 /// signals all the jobs to die quickly 00266 void flushJobs(void); 00267 00268 /** 00269 * Using the worker/background thread pool system, enqueue 00270 * a file info load for the given file. 00271 * 00272 * Upon completion, a tag emit event will be sent. 00273 * 00274 * @author Aleksander Demko 00275 */ 00276 void enqueueLoad(const QString &fullfilename, int idx); 00277 00278 private: 00279 static inline bool EntryListComp(const desktop::cache_ptr<FileEntry> &lhs, 00280 const desktop::cache_ptr<FileEntry> &rhs) { 00281 return lhs->fullfilename() < rhs->fullfilename(); 00282 } 00283 00284 void metaLoaderFunc(const QString &fullfilename, int idx); 00285 void metaLoaderFuncCommit(const QString &fullfilename, int idx, const QString &hash); 00286 00287 private: 00288 friend class desktop::FileListLoader; 00289 friend class desktop::FileListReloader; 00290 00291 // related to metaLoaderFunc 00292 QMutex dm_pendingjobs_lock; 00293 std::set<QString> dm_pendingjobs; 00294 00295 FileEntryCache *dm_cache; 00296 00297 FileList *dm_peer; 00298 00299 QString dm_base_dir; 00300 bool dm_base_recurse, dm_base_shuffle; 00301 QString dm_base_query; 00302 std::shared_ptr<hydra::Token> dm_base_query_token; 00303 00304 typedef std::vector<desktop::cache_ptr<FileEntry> > EntryList; 00305 EntryList dm_filtered_files, dm_all_files; 00306 00307 typedef std::list<desktop::FileListListener *> listeners_t; 00308 listeners_t dm_listeners; 00309 00310 QItemSelectionModel dm_selectionmodel; 00311 00312 FileSystemWatcher dm_watcher; 00313 }; 00314 00315 /** 00316 * A flexible sorting functor suitable for passing to qSort. 00317 * 00318 * @author Aleksander Demko 00319 */ 00320 class desktop::FileEntryLessThan 00321 { 00322 public: 00323 enum { 00324 colJustName, 00325 colFullFileName, 00326 colRecordTags, 00327 colFileLastModified, 00328 colFileSize, 00329 }; 00330 00331 public: 00332 FileEntryLessThan(short col, Qt::SortOrder order = Qt::AscendingOrder); 00333 00334 bool operator()(const desktop::cache_ptr<desktop::FileEntry> &left, const desktop::cache_ptr<desktop::FileEntry> &right) const; 00335 00336 static bool tagsLessThan(const hydra::FileItemRecord::tags_t &left, 00337 const hydra::FileItemRecord::tags_t &right); 00338 00339 private: 00340 short dm_col; 00341 Qt::SortOrder dm_order; 00342 }; 00343 00344 /** 00345 * A class that is used to populate and load entries into a FileList 00346 * while taking care of signal emission and consolidation. 00347 * 00348 * You instantite the class, and set various options. Note that the options are 00349 * by default, based on the existing filelist. Upon destruction, the list will be loaded 00350 * and signals sent. 00351 * 00352 * @author Aleksander Demko 00353 */ 00354 class desktop::FileListLoader 00355 { 00356 public: 00357 /// constructor 00358 /// future: progress dialog option/system? 00359 FileListLoader(FileList &filelist, FileListListener *source = 0, bool resetSelectionTo0 = true); 00360 /// destructor will commit the changes and emit some signals 00361 ~FileListLoader(); 00362 00363 /// sets the base dir 00364 void setBaseDir(const QString &fulldirname); 00365 /// sets weither directory recursion will be applied 00366 /// this may be removed in the future as dir-monitoting in this case 00367 /// may be impossible 00368 void setBaseRecurse(bool recurse = true); 00369 /// either to shuffle the loaded list 00370 void setBaseShuffle(bool shuffle = true); 00371 /// sets the query that will be used to filter the list 00372 /// returns true on succesful parse 00373 bool setBaseQuery(const QString &query); 00374 00375 /// simply flag that the list, with its current params, needs to be reloaded 00376 void setReload(void); 00377 00378 /// remove all files from the current list 00379 void clearList(void); 00380 00381 private: 00382 /** 00383 * Reads a dir and sets the first file as current. 00384 * 00385 * @return the number of entries scanned 00386 * @author Aleksander Demko 00387 */ 00388 int appendListDir(const QString &dirname); 00389 00390 private: 00391 FileList &dm_filelist; 00392 FileListListener *dm_source; 00393 00394 bool dm_resetselection; 00395 00396 bool dm_changed_base; 00397 bool dm_changed_base_query; 00398 bool dm_changed_shuffle; 00399 bool dm_changed_list; 00400 00401 std::auto_ptr<QProgressDialog> dm_progdlg; 00402 }; 00403 00404 /** 00405 * Does a "fast" update/merge with the files on disk. 00406 * 00407 * @author Aleksander Demko 00408 */ 00409 class desktop::FileListReloader 00410 { 00411 public: 00412 /// constructor 00413 FileListReloader(FileList &filelist, FileListListener *source = 0); 00414 /// destructor 00415 ~FileListReloader(); 00416 00417 private: 00418 FileList &dm_filelist; 00419 FileListListener *dm_source; 00420 }; 00421 00422 #endif 00423