Merge lp:~carlos-mazieri/ubuntu-filemanager-app/app-devel-pre2 into lp:ubuntu-filemanager-app

Proposed by Carlos Jose Mazieri
Status: Merged
Approved by: Arto Jalkanen
Approved revision: 172
Merged at revision: 180
Proposed branch: lp:~carlos-mazieri/ubuntu-filemanager-app/app-devel-pre2
Merge into: lp:ubuntu-filemanager-app
Prerequisite: lp:~carlos-mazieri/ubuntu-filemanager-app/app-devel-pre1
Diff against target: 336 lines (+108/-57)
5 files modified
src/plugin/folderlistmodel/dirmodel.cpp (+6/-4)
src/plugin/folderlistmodel/dirmodel.h (+1/-1)
src/plugin/folderlistmodel/externalfswatcher.cpp (+59/-25)
src/plugin/folderlistmodel/externalfswatcher.h (+30/-15)
src/plugin/test_folderlistmodel/regression/tst_folderlistmodel.cpp (+12/-12)
To merge this branch: bzr merge lp:~carlos-mazieri/ubuntu-filemanager-app/app-devel-pre2
Reviewer Review Type Date Requested Status
Arto Jalkanen Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Review via email: mp+217978@code.launchpad.net

Description of the change

summary: class ExternalFSWatcher changed to allow watching more than a path.
reason: It is intended to watch external changes in the Trash and Trash can be compounded for more than one path.

To post a comment you must log in.
Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Pope 🍺🐧🐱 πŸ¦„ (popey) wrote :

I've asked Michael Spencer to take a look at this and the other related branches and review. If he has difficulty with that I've asked him to get back to me so we can find another reviewer.

Revision history for this message
Arto Jalkanen (ajalkane) wrote :

Function setCurrentPath(const QString &curPath) seems potentially confusing. ExternalFSWatcher allows watching several directories, and if that's the case and you try setting it to watch only one directory with setCurrentPath() then nothing happens (setCurrentPath does only changes if no paths watched or _only_ one path is watched, with several paths watched it seems to ignore the request).

m_setPath should probably be m_setPaths as it is a list of paths.

There might be something I don't properly understand about ExternalFSWatcher code. You can give it several paths to watch. But if there are changes happening at the same time at more than one path, the code seems to only send a notification of the change to the first. This can't be the intention?

externalfswatcher.h: The comments for the class still talk like it handles only one path. That should corrected.

review: Needs Information
171. By Carlos Jose Mazieri

ExternalFSWatcher:
   * improved comments
   * m_setPath changed to m_setPaths
   * fixed bug about restoring paths in the slotFireChanges()

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

This class used to watch just one path (the current on File Manager) that is why setPath() is a slot and it is called every time file manager changes its current path and emits DirModel::pathChanged(). For the normal path in the file system there will be always one path only. The idea here was to avoid notifications when the user changes the current path in the file system and lots of notifications when there is an Action going on which is causing lots of file system modifications.

It was changed to support a list of paths because of the Trash, again: Trash can show files from more than one Disk Trash, each mounted file system can have its own Disk Trash; all them (if more than one) are supposed to be watched.

The idea with more than one path is to notify the LAST (if more than one path was modified) not the first, see "m_lastChangedIndex". In the current implementation for the Trash it does not matter which Trash Disk path was modified, it gathers all them in a new DirItemInfoList and compares the content with the current content, see ExternalFileSystemChangesWorker::compareItems().

It should have a bug which I tried to fix, it used to not restore the entire path list. Now there is a comment in the ExternalFSWatcher::slotDirChanged() which may help a better understanding.

ExternalFSWatcher is used a little different for Trash, setCurrentPaths() will be called just once when entering in the Trash root, for sub-folders inside the Trash it is not used in the current implementation, TrashLocation::fetchItems() calls DiskLocation::stoptExternalFsWatcher() when it is not browsing Trash root items.

Current implementation keeps just one ExternalFSWatcher alive either for Normal File System or for Trash, when there will be support for other Locations like Network there will not be any instance of ExternalFSWatcher alive, see Location::startWorking() and Location::stopWorking() in the next MP.

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Arto Jalkanen (ajalkane) wrote :

Thanks,

I probably (hope so) now understand it better. But still this setCurrentPath function perplexes me:

72 + if (m_setPaths.count() == 1 && m_setPaths.at(0) != curPath)
73 + {
74 + m_setPaths.removeFirst();
75 + }
76 + if (m_setPaths.count() == 0)
77 + {
78 + m_setPaths.append(curPath);
79 + QFileSystemWatcher::addPath(curPath);
80 + }

It seems like a bug? If a path has been set, and then directory is changed resulting in another path being set. It does only m_setPaths.removeFirst(). It does not call removing the path from QFileSystemWatcher. Then it adds the new path to be watched, so now it's watching two paths?

If setCurrentPaths() does not really notify of all the paths put there, but only of the last one changed, I think the intention should be documented a bit more. I think I understand the use case and it works as intended for it, but anyone looking only this piece of code would have hard time realizing this subtlety. I wonder if setCurrentPaths() could have a more descriptive function name. Or could the intention be better communicated by renaming the signal from pathModified(QString) to something like "atLeastOneOfPathsModified()"? Is the path argument needed in the signal? Quickly perusing the code I didn't see it being needed.

Let me know what you think, I might have not understood all the details here.

Revision history for this message
Arto Jalkanen (ajalkane) wrote :

Also, as how setCurrentPath currently works it seems one should not use setCurrentPath on an object where setCurrentPaths has been used. So it should at least be documented that only one or the other should be used on a created object.

Although I don't quite understand why setCurrentPath is that complex, couldn't you make it much simpler by doing for example only:

    clearPaths();
    m_setPaths.clear()
    m_setPaths.append(curPath);
    QFileSystemWatcher::addPath(curPath);

Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

> Also, as how setCurrentPath currently works it seems one should not use
> setCurrentPath on an object where setCurrentPaths has been used. So it should
> at least be documented that only one or the other should be used on a created
> object.
>
> Although I don't quite understand why setCurrentPath is that complex, couldn't
> you make it much simpler by doing for example only:
>
> clearPaths();
> m_setPaths.clear()
> m_setPaths.append(curPath);
> QFileSystemWatcher::addPath(curPath);

You're right it works when using it separated (I have a couple of tests for normal folders), when mixing setCurrentPath() and setCurrentPaths() in the same object it will not work.

I will improve that code as you suggested.
Thanks.

172. By Carlos Jose Mazieri

* ExternalFSWatcher::setCurrentPath() improved/simplified
* created documentation for ExternalFSWatcher::slotDirChanged()
* improved documentation of ExternalFSWatcher::slotFireChanges()
* improved documentation of ExternalFSWatcher class in externalfswatcher.h

Revision history for this message
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Approve (continuous-integration)
Revision history for this message
Carlos Jose Mazieri (carlos-mazieri) wrote :

Some improvement provided.

Revision history for this message
Arto Jalkanen (ajalkane) wrote :

Looks good to me now, thanks!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/plugin/folderlistmodel/dirmodel.cpp'
2--- src/plugin/folderlistmodel/dirmodel.cpp 2014-05-01 17:38:23 +0000
3+++ src/plugin/folderlistmodel/dirmodel.cpp 2014-05-13 23:29:05 +0000
4@@ -1189,8 +1189,8 @@
5 connect(this, SIGNAL(pathChanged(QString)),
6 mExtFSWatcher, SLOT(setCurrentPath(QString)));
7
8- connect(mExtFSWatcher, SIGNAL(pathModified()),
9- this, SLOT(onThereAreExternalChanges()));
10+ connect(mExtFSWatcher, SIGNAL(pathModified(QString)),
11+ this, SLOT(onThereAreExternalChanges(QString)));
12
13 //setCurrentPath() checks for empty paths
14 mExtFSWatcher->setCurrentPath(mCurrentDir);
15@@ -1216,13 +1216,15 @@
16 }
17
18
19-void DirModel::onThereAreExternalChanges()
20+void DirModel::onThereAreExternalChanges(const QString& pathModifiedOutside)
21 {
22 if ( IS_FILE_MANAGER_IDLE() )
23 {
24 #if DEBUG_EXT_FS_WATCHER
25 qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
26- << Q_FUNC_INFO << this << "File System modified";
27+ << Q_FUNC_INFO << this << "File System modified" << pathModifiedOutside;
28+#else
29+ Q_UNUSED(pathModifiedOutside);
30 #endif
31 DirListWorker *w =
32 createWorkerRequest(IORequest::DirListExternalFSChanges,
33
34=== modified file 'src/plugin/folderlistmodel/dirmodel.h'
35--- src/plugin/folderlistmodel/dirmodel.h 2014-05-01 17:38:23 +0000
36+++ src/plugin/folderlistmodel/dirmodel.h 2014-05-13 23:29:05 +0000
37@@ -392,7 +392,7 @@
38 void onItemAddedOutsideFm(const DirItemInfo&fi);
39 void onItemRemovedOutSideFm(const DirItemInfo&);
40 void onItemChangedOutSideFm(const DirItemInfo&fi);
41- void onThereAreExternalChanges();
42+ void onThereAreExternalChanges(const QString &);
43 void onExternalFsWorkerFinished(int);
44
45
46
47=== modified file 'src/plugin/folderlistmodel/externalfswatcher.cpp'
48--- src/plugin/folderlistmodel/externalfswatcher.cpp 2013-11-17 14:27:10 +0000
49+++ src/plugin/folderlistmodel/externalfswatcher.cpp 2014-05-13 23:29:05 +0000
50@@ -28,7 +28,7 @@
51 #if DEBUG_EXT_FS_WATCHER
52 # define DEBUG_FSWATCHER() \
53 qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") \
54- << Q_FUNC_INFO << "m_setPath:" << m_setPath \
55+ << Q_FUNC_INFO << "m_setPath:" << m_setPaths \
56 << "m_changedPath:" << m_changedPath \
57 << "m_waitingEmit:" << m_waitingEmitCounter
58 #else
59@@ -40,6 +40,7 @@
60 QFileSystemWatcher(parent)
61 , m_waitingEmitCounter(0)
62 , m_msWaitTime(DEFAULT_NOTICATION_PERIOD)
63+ , m_lastChangedIndex(-1)
64 {
65 connect(this, SIGNAL(directoryChanged(QString)),
66 this, SLOT(slotDirChanged(QString)));
67@@ -48,32 +49,59 @@
68
69 void ExternalFSWatcher::setCurrentPath(const QString &curPath)
70 {
71- if (!curPath.isEmpty())
72+ if (!curPath.isEmpty() && (m_setPaths.count() != 1 || m_setPaths.at(0) != curPath))
73 {
74- if (m_setPath != curPath)
75- {
76- if (!m_setPath.isEmpty())
77- {
78- removePath(m_setPath);
79- }
80- m_setPath = curPath;
81- addPath(m_setPath);
82- }
83+ clearPaths();
84+ m_setPaths.clear();
85+ m_setPaths.append(curPath);
86+ QFileSystemWatcher::addPath(curPath);
87 }
88 DEBUG_FSWATCHER();
89 }
90
91
92+void ExternalFSWatcher::setCurrentPaths(const QStringList &paths)
93+{
94+ QStringList myPaths(paths);
95+ ::qSort(myPaths);
96+ clearPaths();
97+ m_setPaths = myPaths;
98+ QFileSystemWatcher::addPaths(paths);
99+}
100+
101+
102+void ExternalFSWatcher::clearPaths()
103+{
104+ QStringList existentPaths = QFileSystemWatcher::directories();
105+ if (existentPaths.count() > 0)
106+ {
107+ QFileSystemWatcher::removePaths(existentPaths);
108+ }
109+}
110+
111+
112+/*!
113+ * \brief ExternalFSWatcher::slotDirChanged() schedules a Disk change to be notified
114+ *
115+ * Once path that belongs to \a m_setPaths is modified in the Disk it becomes the \a m_changedPath and
116+ * its change is scheculed to notified later. This path is taken out from QFileSystemWatcher to avoid
117+ * lots of continuous notifications from QFileSystemWatcher when having hevy disk io.
118+ *
119+ * \param dir directory changed in the File System
120+ */
121 void ExternalFSWatcher::slotDirChanged(const QString &dir)
122 {
123 DEBUG_FSWATCHER();
124- if ( (m_setPath == dir)
125- && ( m_waitingEmitCounter == 0 || m_setPath != m_changedPath )
126- )
127+ int index = m_setPaths.indexOf(dir);
128+ if (index != -1 && (m_waitingEmitCounter == 0 || dir != m_changedPath))
129 {
130- removePath(m_setPath);
131+ m_lastChangedIndex = index;
132+ //changed path is taken from the QFileSystemWatcher and it becomes the current changed
133+ //in this case there will not be slotDirChanged() for this path until slotFireChanges()
134+ //restores the path in the QFileSystemWatcher
135+ removePath(m_setPaths.at(m_lastChangedIndex));
136 ++m_waitingEmitCounter;
137- m_changedPath = m_setPath;
138+ m_changedPath = dir;
139 QTimer::singleShot(m_msWaitTime, this, SLOT(slotFireChanges()));
140 }
141 }
142@@ -81,22 +109,28 @@
143
144 /*!
145 * \brief ExternalFSWatcher::slotFireChanges() emits \ref pathModified() only when it is sure
146- * that the current path was changed.
147- *
148- * A change for the current path (the last current) MUST be notified at least once.
149+ * that the LAST current path was changed.
150+ *
151+ * The notification will be sent out only for the LAST modified path (if more than one) from the \a m_setPaths
152+ *
153+ * \sa \ref ExternalFSWatcher class
154 */
155 void ExternalFSWatcher::slotFireChanges()
156 {
157- if (--m_waitingEmitCounter == 0)
158- {
159- addPath(m_setPath);
160- if (m_setPath == m_changedPath)
161- {
162- emit pathModified();
163+ if ( --m_waitingEmitCounter == 0
164+ && m_lastChangedIndex != -1
165+ && m_lastChangedIndex < m_setPaths.count() )
166+ {
167+ if (m_setPaths.at(m_lastChangedIndex) == m_changedPath)
168+ {
169+ emit pathModified(m_changedPath);
170 #if DEBUG_EXT_FS_WATCHER
171 DEBUG_FSWATCHER() << "emit pathModified()";
172 #endif
173 }
174+ //restore the original list in QFileSystemWatcher
175+ clearPaths();
176+ QFileSystemWatcher::addPaths(m_setPaths);
177 }
178 }
179
180
181=== modified file 'src/plugin/folderlistmodel/externalfswatcher.h'
182--- src/plugin/folderlistmodel/externalfswatcher.h 2013-10-20 16:58:41 +0000
183+++ src/plugin/folderlistmodel/externalfswatcher.h 2014-05-13 23:29:05 +0000
184@@ -23,22 +23,30 @@
185 #define EXTERNALFSWATCHER_H
186
187 #include <QFileSystemWatcher>
188+#include <QStringList>
189
190 #define DEFAULT_NOTICATION_PERIOD 500
191
192
193 /*!
194- * \brief The ExternalFSWatcher class notifies the owner when the File System when the current path \a m_setPath has changed
195- * emitting pathModified() signal.
196- *
197- * The current path \a m_setPath is set by using the slot \ref setCurrentPath()
198- *
199- * The idea of this class is to minimize notifications as the current path can change quickly.
200+ * \brief The ExternalFSWatcher class watches for external changes in Disk emitting pathModified() signal.
201+ *
202+ * The path(s) being watched is/are set by using the slot \ref setCurrentPath() or \ref setCurrentPaths()
203+ *
204+ * The idea of this class is to minimize notifications as the current path in the File Manager can change quickly.
205 * A notification will occur if it was requested for a path and this path is still the current at the moment
206 * of the notification.
207 *
208- * Once it detects a change it will wait \ref getIntervalToNotifyChanges() milliseconds to notify that change.
209- * At this moment it checks if no \ref setCurrentPath() has been called during this time and then notifies that change.
210+ * Once it detects a Disk change it will wait \ref getIntervalToNotifyChanges() milliseconds to notify that change.
211+ * During the time it waits:
212+ * \li the notified path will NOT be watched until pathModified() is emitted
213+ * \li another call to \ref setCurrentPath() or \ref setCurrentPaths() invalidades the current change,
214+ * that mean the signal pathModified() will NOT be emitted.
215+ *
216+ * \note When more than one path is being watched by using \ref setCurrentPaths() and changes happen in
217+ * more than one path before the getIntervalToNotifyChanges() expires, only the LAST path modified
218+ * will be notified as changed. It may possible that it goes to a loop if all the paths were modified,
219+ * but the loop finishes when the last one from the list is modified.
220 */
221 class ExternalFSWatcher : public QFileSystemWatcher
222 {
223@@ -47,22 +55,29 @@
224 explicit ExternalFSWatcher(QObject *parent = 0);
225 int getIntervalToNotifyChanges() const;
226
227+ inline const QStringList& pathsWatched() const { return m_setPaths;}
228+
229 signals:
230- void pathModified();
231+ void pathModified(const QString& path);
232
233- public slots:
234+public slots:
235 void setCurrentPath(const QString& curPath);
236+ void setCurrentPaths(const QStringList& paths);
237 void setIntervalToNotifyChanges(int ms);
238
239 private slots:
240 void slotDirChanged(const QString&);
241 void slotFireChanges();
242
243- private:
244- QString m_setPath;
245- QString m_changedPath;
246- unsigned m_waitingEmitCounter;
247- int m_msWaitTime;
248+private:
249+ void clearPaths();
250+
251+private:
252+ QStringList m_setPaths;
253+ QString m_changedPath;
254+ unsigned m_waitingEmitCounter;
255+ int m_msWaitTime;
256+ int m_lastChangedIndex;
257 };
258
259 #endif // EXTERNALFSWATCHER_H
260
261=== modified file 'src/plugin/test_folderlistmodel/regression/tst_folderlistmodel.cpp'
262--- src/plugin/test_folderlistmodel/regression/tst_folderlistmodel.cpp 2014-03-03 17:19:49 +0000
263+++ src/plugin/test_folderlistmodel/regression/tst_folderlistmodel.cpp 2014-05-13 23:29:05 +0000
264@@ -77,7 +77,7 @@
265 void cancel(int index, int, int percent);
266 void slotclipboardChanged();
267 void slotError(QString title, QString message);
268- void slotExtFsWatcherPathModified() { ++m_extFSWatcherPathModifiedCounter; }
269+ void slotExtFsWatcherPathModified(const QString&) { ++m_extFSWatcherPathModifiedCounter; }
270 void slotSelectionChanged(int counter) { m_selectedItemsCounter = counter; }
271 void slotSelectionModeChanged(int m) { m_selectionMode = m;}
272
273@@ -1859,8 +1859,8 @@
274 void TestDirModel::extFsWatcherChangePathManyTimesModifyAllPathsLessLast()
275 {
276 ExternalFSWatcher watcher;
277- connect(&watcher, SIGNAL(pathModified()),
278- this, SLOT(slotExtFsWatcherPathModified()));
279+ connect(&watcher, SIGNAL(pathModified(QString)),
280+ this, SLOT(slotExtFsWatcherPathModified(QString)));
281
282 const int items = 150;
283 QVector<DeepDir *> deepDirs;
284@@ -1896,8 +1896,8 @@
285 void TestDirModel::extFsWatcherChangePathManyTimesModifyManyTimes()
286 {
287 ExternalFSWatcher watcher;
288- connect(&watcher, SIGNAL(pathModified()),
289- this, SLOT(slotExtFsWatcherPathModified()));
290+ connect(&watcher, SIGNAL(pathModified(QString)),
291+ this, SLOT(slotExtFsWatcherPathModified(QString)));
292
293 const int items = 50;
294 QVector<DeepDir *> deepDirs;
295@@ -1930,8 +1930,8 @@
296 void TestDirModel::extFsWatcherModifySamePathManyTimesWithInInterval()
297 {
298 ExternalFSWatcher watcher;
299- connect(&watcher, SIGNAL(pathModified()),
300- this, SLOT(slotExtFsWatcherPathModified()));
301+ connect(&watcher, SIGNAL(pathModified(QString)),
302+ this, SLOT(slotExtFsWatcherPathModified(QString)));
303
304 QString dirName("extFsWatcher_expects_just_one_signal");
305 m_deepDir_01 = new DeepDir(dirName,0);
306@@ -1957,8 +1957,8 @@
307 void TestDirModel::extFsWatcherSetPathAndModifyManyTimesWithInInterval()
308 {
309 ExternalFSWatcher watcher;
310- connect(&watcher, SIGNAL(pathModified()),
311- this, SLOT(slotExtFsWatcherPathModified()));
312+ connect(&watcher, SIGNAL(pathModified(QString)),
313+ this, SLOT(slotExtFsWatcherPathModified(QString)));
314
315 QList<DeepDir *> deepDirs;
316
317@@ -1995,8 +1995,8 @@
318 {
319 bool updateAndSetModificationTime(const QString& filename, QDateTime& desiredTime);
320
321- connect( m_dirModel_01->mExtFSWatcher, SIGNAL(pathModified()),
322- this, SLOT(slotExtFsWatcherPathModified()));
323+ connect( m_dirModel_01->getExternalFSWatcher(), SIGNAL(pathModified(QString)),
324+ this, SLOT(slotExtFsWatcherPathModified(QString)));
325
326 QString dirName("extFsWatcher_generate_fileswithsameTimeStamp");
327 m_deepDir_01 = new DeepDir(dirName,0);
328@@ -2029,7 +2029,7 @@
329 QFileInfo firstFile(firstPathName);
330 QCOMPARE(firstFile.lastModified(), timeStamp);
331
332- const int maxWait = m_dirModel_01->mExtFSWatcher->getIntervalToNotifyChanges() + 10;
333+ const int maxWait = m_dirModel_01->getExternalFSWatcher()->getIntervalToNotifyChanges() + 10;
334 int counter = 0;
335
336 //make sure ExternalFSWatcher has not notified any change

Subscribers

People subscribed via source and target branches