Hydra 0.20
hydra.src/desktop/FileList.h
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 
 All Classes Namespaces Functions Variables