~carlos-mazieri/ubuntu-filemanager-app/model

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/*
 * Copyright (C) 2012 Robin Burchell <robin+nemo@viroteck.net>
 *
 * You may use this file under the terms of the BSD license as follows:
 *
 * "Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Nemo Mobile nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 */

#ifndef DIRMODEL_H
#define DIRMODEL_H


#include <QStringList>
#include <QDir>

#include "iorequest.h"
#include "filecompare.h"
#include "diritemabstractlistmodel.h"
#include "diriteminfo.h"

class FileSystemAction;
class ExternalFSWatcher;
class Clipboard;
class DirSelection;

/*!
 *  When the External File System Wathcer is enabled,
 *  this is the interval used to check if there has been any change in the current path
 *
 *  \sa setEnabledExternalFSWatcher()
 */
#define EX_FS_WATCHER_TIMER_INTERVAL   900

class DirModel : public DirItemAbstractListModel
{
    Q_OBJECT
public:
    enum Roles {
        FileNameRole = Qt::UserRole,
        AccessedDateRole,
        CreationDateRole,
        ModifiedDateRole,
        FileSizeRole,
        IconSourceRole,
        FilePathRole,
        IsDirRole,
        IsFileRole,
        IsReadableRole,
        IsWritableRole,
        IsExecutableRole,
        IsSelectedRole,
        TrackTitleRole,
        TrackArtistRole,
        TrackAlbumRole,
        TrackYearRole,
        TrackNumberRole,
        TrackGenreRole,
        TrackLengthRole,
        TrackCoverRole
    };

public:
    explicit DirModel(QObject *parent = 0);
    ~DirModel();

    static void registerMetaTypes();

    //DirItemAbstractListModel
    virtual int                 getIndex(const QString& name);
    virtual void                notifyItemChanged(int row);

    int rowCount(const QModelIndex &index = QModelIndex()) const
    {
        if (index.parent() != QModelIndex())
            return 0;
        return mDirectoryContents.count();
    }

    // TODO: this won't be safe if the model can change under the holder of the row
    Q_INVOKABLE QVariant data(int row, const QByteArray &stringRole) const;

    QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;

    Q_INVOKABLE void refresh()
    {
        // just some syntactical sugar really
        setPath(path());
    }

    Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
    inline QString path() const { return mCurrentDir; }
    void setPath(const QString &pathName);

    Q_INVOKABLE QDateTime   curPathAccessedDate() const;
    Q_INVOKABLE QDateTime   curPathCreatedDate()  const;
    Q_INVOKABLE QDateTime   curPathModifiedDate() const;
    Q_INVOKABLE QString     curPathAccessedDateLocaleShort() const;
    Q_INVOKABLE QString     curPathCreatedDateLocaleShort()  const;
    Q_INVOKABLE QString     curPathModifiedDateLocaleShort() const;
    Q_INVOKABLE bool        curPathIsWritable() const;

    Q_PROPERTY(bool awaitingResults READ awaitingResults NOTIFY awaitingResultsChanged)
    bool awaitingResults() const;

    Q_INVOKABLE void rm(const QStringList &paths);

    Q_INVOKABLE bool rename(const QString& oldName, const QString& newName);
    Q_INVOKABLE bool rename(int row, const QString &newName);

    Q_INVOKABLE void mkdir(const QString &newdir);

    Q_PROPERTY(bool filterDirectories READ filterDirectories WRITE setFilterDirectories NOTIFY filterDirectoriesChanged)
    bool filterDirectories() const;

    Q_PROPERTY(bool isRecursive READ isRecursive WRITE setIsRecursive NOTIFY isRecursiveChanged)
    bool isRecursive() const;

    Q_PROPERTY(bool readsMediaMetadata READ readsMediaMetadata WRITE setReadsMediaMetadata NOTIFY readsMediaMetadataChanged)
    bool readsMediaMetadata() const;

    Q_PROPERTY(bool showDirectories READ showDirectories WRITE setShowDirectories NOTIFY showDirectoriesChanged)
    bool showDirectories() const;

    Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
    QStringList nameFilters() const;
    void setNameFilters(const QStringList &nameFilters);

public slots:
    void onItemsAdded(const DirItemInfoList &newFiles);
    void onResultsFetched();

signals:
    void awaitingResultsChanged();
    void nameFiltersChanged();
    void filterDirectoriesChanged();
    void isRecursiveChanged();
    void readsMediaMetadataChanged();
    void showDirectoriesChanged();
    void pathChanged(const QString& newPath);
    void error(const QString &errorTitle, const QString &errorMessage);

private:
    QHash<int, QByteArray> buildRoleNames() const;   
    QHash<int, QByteArray> roleNames() const;
    QStringList mNameFilters;
    bool mFilterDirectories;
    bool mShowDirectories;
    bool mAwaitingResults;
    bool mIsRecursive;
    bool mReadsMediaMetadata;
    QString mCurrentDir;
    DirItemInfoList  mDirectoryContents;

public:

    Q_INVOKABLE DirSelection * selectionObject() const ;

    //[0] new stuff Ubuntu File Manager
    Q_PROPERTY(QString parentPath READ parentPath NOTIFY pathChanged)
    QString parentPath() const;

    Q_PROPERTY(bool showHiddenFiles READ getShowHiddenFiles WRITE setShowHiddenFiles NOTIFY showHiddenFilesChanged)
    bool getShowHiddenFiles() const;

    Q_ENUMS(SortBy)
    enum SortBy
    {
        SortByName,
        SortByDate
    };
    Q_PROPERTY(SortBy sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged)
    SortBy getSortBy() const;

    Q_ENUMS(SortOrder)
    enum SortOrder
    {
        SortAscending   = Qt::AscendingOrder,
        SortDescending = Qt::DescendingOrder
    };
    Q_PROPERTY(SortOrder sortOrder READ getSortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
    SortOrder getSortOrder() const;

    Q_PROPERTY(int clipboardUrlsCounter READ getClipboardUrlsCounter NOTIFY clipboardChanged)
    int getClipboardUrlsCounter() const;

    Q_PROPERTY(bool enableExternalFSWatcher READ getEnabledExternalFSWatcher WRITE setEnabledExternalFSWatcher)
    bool  getEnabledExternalFSWatcher() const;

    Q_INVOKABLE QString homePath() const;

    /*!
     *    \brief Tries to make the directory pointed by row as the current to be browsed
     *    \return true if row points to a directory and the directory is readble, false otherwise
     */
    Q_INVOKABLE  bool cdIntoIndex(int row);
    Q_INVOKABLE  bool cdIntoPath(const QString& filename);

    /*!
     * \brief copyIndex() puts the item pointed by \a row (dir or file) into the clipboard
     * \param row points to the item file or directory
     */
    Q_INVOKABLE void  copyIndex(int row);

    /*!
     *  \brief copyPaths(const QStringList& urls) several items (dirs or files) into the clipboard
     *  \param items  fullpathnames or names only
     */
    Q_INVOKABLE void  copyPaths(const QStringList& items);

    /*!
     * \brief cutIndex() puts the item into the clipboard as \ref copy(),
     *        mark the item to be removed after \ref paste()
     * \param row points to the item file or directory
     */
    Q_INVOKABLE void  cutIndex(int row);

    /*!
     *  \brief cut() puts several items (dirs or files) into the clipboard as \ref copy(),
     *         mark the item to be removed after \ref paste()
     *   \param items  fullpathnames or names only
     */
    Q_INVOKABLE void  cutPaths(const QStringList& items);

    /*!
     * \brief removeIndex();  remove a item file or directory
     *
     * I gets the item indicated by \row and calls \ref rm()
     *
     * \param row points to the item to b e removed
     * \return true if it was possible to remove the item
     */
    Q_INVOKABLE void removeIndex(int row);

    /*!
     *  Just calls \ref rm()
     */
    Q_INVOKABLE void removePaths(const QStringList& items);

    /*!
     *  Tries to open a file using a suitable application, if the index points to a directory
     *  it goes into it using \ref cdIntoIndex() or \ref cdIntoPath()
     *
     *  \note Qt uses Qt QDesktopServices::openUrl()
     */
    Q_INVOKABLE bool  openIndex(int row);

    /*!
     *  Same as \ref openIndex() but using a file name instead of index
     *
     *  It allows to open directories and files using absoulte paths
     *
     *  \sa \ref cdIntoPath()
     */
    Q_INVOKABLE bool  openPath(const QString& filename);

    /*!
     *   \brief getProgressCounter() returns the number of \ref progress() notifications an Action will perform
     *
     *   It may be useful to decide about showing or not a progress dialog for Remove/Copy/Cut/Paste Actions
     *
     *   This function can be called just after receiving first \ref progress() notification
     *
     *   \note In the future this \ref getProgressCounter() and \ref progress() will merge to single signal that
     *         will send the Action full information, it will allow to have multi thread Actions.
     *         Also \ref cancelAction() needs to  change
     */
    Q_INVOKABLE int   getProgressCounter() const;

    // some helper functions that can be useful to other QML applications than File Manager
    Q_INVOKABLE  bool  existsDir(const QString&  folderName) const;
    Q_INVOKABLE  bool  canReadDir(const QString& folderName) const;
    Q_INVOKABLE  bool  existsFile(const QString& fileName)   const;
    Q_INVOKABLE  bool  canReadFile(const QString& fileName)  const;

public slots:
    /*!
     * \brief goHome() goes to user home dir
     *  Go to user home dir, we may have a tab for places or something like that
     */
    void  goHome();

    /*!
     * \brief cdUp() sets the parent directory as current directory
     *
     *  It can work as a back function if there is no user input path
     * \return true if it was possible to change to parent dir, otherwise false
     */
    bool  cdUp();

    /*!
     * \brief paste() copy item(s) from \ref copy() and \ref paste() into the current directory
     *
     *  If the operation was \ref cut(), then remove the original item
     */
    void paste();

    /*!
     * \brief cancelAction() any copy/cut/remove can be cancelled
     */
    void cancelAction();    

    void setIsRecursive(bool isRecursive);
    void setReadsMediaMetadata(bool readsMediaMetadata);
    void setFilterDirectories(bool filterDirectories);
    void setShowDirectories(bool showDirectories);
    void setShowHiddenFiles(bool show);
    void setSortBy(SortBy field);
    void setSortOrder(SortOrder order);
    void setEnabledExternalFSWatcher(bool enable);


    void toggleShowDirectories();
    void toggleShowHiddenFiles();
    void toggleSortOrder();
    void toggleSortBy();

signals:
    /*!
     * \brief insertedItem()
     *
     *  It happens when a new file is inserted in an existent view,
     *  for example from  \ref mkdir() or \ref paste()
     *
     *  It can be used to make the new row visible to the user doing a scroll to
     */
    void  insertedRow(int row);
    /*!
     * \brief progress()
     *  Sends status about recursive and multi-items remove/move/copy
     *
     * \param curItem     current item being handled
     * \param totalItems  total of items including recursive directories content
     * \param percent     a percent done
     */
    void     progress(int curItem, int totalItems, int percent);

    void     showHiddenFilesChanged();
    void     sortByChanged();
    void     sortOrderChanged();

    void     clipboardChanged();

private slots:
    void onItemRemoved(const QString&);
    void onItemRemoved(const DirItemInfo&);
    void onItemAdded(const QString&);
    void onItemAdded(const DirItemInfo&);
    void onItemChanged(const DirItemInfo&);

private:
    int           addItem(const DirItemInfo& fi);
    void          setCompareAndReorder();
    int           rowOfItem(const DirItemInfo& fi);
    QDir::Filter  currentDirFilter()  const;
    QString       dirItems(const DirItemInfo& fi) const;
    bool          cdInto(const DirItemInfo& fi);
    bool          openItem(const DirItemInfo& fi);
    DirListWorker * createWorkerRequest(IORequest::RequestType requestType,
                                                  const QString& pathName);
    bool          canReadDir(const QFileInfo& d)   const;
    bool          canReadFile(const QFileInfo& f)  const;
    QFileInfo     setParentIfRelative(const QString &fileOrDir) const;

private:
    void          startExternalFsWatcher();
    void          stoptExternalFsWatcher();
    void          clear();
private slots:
    void          onItemAddedOutsideFm(const DirItemInfo&fi);
    void          onItemRemovedOutSideFm(const DirItemInfo&);
    void          onItemChangedOutSideFm(const DirItemInfo&fi);
    void          onThereAreExternalChanges();
    void          onExternalFsWorkerFinished(int);


private:
    bool                mShowHiddenFiles;
    SortBy              mSortBy;
    SortOrder           mSortOrder;
    CompareFunction     mCompareFunction;
    ExternalFSWatcher*  mExtFSWatcher;
    Clipboard *         mClipboard;
    DirSelection *      mSelection;


private:
    FileSystemAction  *  m_fsAction;  //!< it does file system recursive remove/copy/move
    QString  fileSize(qint64 size)  const;
#ifndef DO_NOT_USE_TAG_LIB
    QVariant getAudioMetaData(const QFileInfo& fi, int role) const;
#endif
//[0]

#if defined(REGRESSION_TEST_FOLDERLISTMODEL)
    //make this work with tables
    virtual int columnCount(const QModelIndex &parent = QModelIndex()) const
    {
        Q_UNUSED(parent);
        return TrackCoverRole - FileNameRole + 1;
    }
    virtual QVariant  headerData(int section, Qt::Orientation orientation, int role) const;
    friend class TestDirModel;
#endif
};


#endif // DIRMODEL_H