Merge lp:~dpm/ubuntu-filemanager-app/include-plugin into lp:ubuntu-filemanager-app

Proposed by David Planella
Status: Merged
Approved by: Nicholas Skaggs
Approved revision: 171
Merged at revision: 160
Proposed branch: lp:~dpm/ubuntu-filemanager-app/include-plugin
Merge into: lp:ubuntu-filemanager-app
Diff against target: 15999 lines (+15391/-104)
69 files modified
.bzrignore (+2/-0)
CMakeLists.txt (+25/-15)
apparmor.json (+2/-2)
click/CMakeLists.txt (+0/-8)
debian/changelog (+7/-0)
debian/control (+19/-2)
debian/qtdeclarative5-nemo-qml-plugin-folderlistmodel.install (+1/-0)
manifest.json (+12/-11)
plugins.json (+0/-6)
src/CMakeLists.txt (+2/-0)
src/app/CMakeLists.txt (+24/-0)
src/app/main.cpp (+114/-0)
src/plugin/CMakeLists.txt (+7/-0)
src/plugin/folderlistmodel/CMakeLists.txt (+54/-0)
src/plugin/folderlistmodel/README (+14/-0)
src/plugin/folderlistmodel/clipboard.cpp (+495/-0)
src/plugin/folderlistmodel/clipboard.h (+132/-0)
src/plugin/folderlistmodel/diritemabstractlistmodel.h (+46/-0)
src/plugin/folderlistmodel/diriteminfo.cpp (+345/-0)
src/plugin/folderlistmodel/diriteminfo.h (+123/-0)
src/plugin/folderlistmodel/dirmodel.cpp (+1563/-0)
src/plugin/folderlistmodel/dirmodel.h (+430/-0)
src/plugin/folderlistmodel/dirselection.cpp (+298/-0)
src/plugin/folderlistmodel/dirselection.h (+116/-0)
src/plugin/folderlistmodel/externalfswatcher.cpp (+114/-0)
src/plugin/folderlistmodel/externalfswatcher.h (+68/-0)
src/plugin/folderlistmodel/filecompare.cpp (+108/-0)
src/plugin/folderlistmodel/filecompare.h (+51/-0)
src/plugin/folderlistmodel/filesystemaction.cpp (+1302/-0)
src/plugin/folderlistmodel/filesystemaction.h (+241/-0)
src/plugin/folderlistmodel/fmutil.cpp (+132/-0)
src/plugin/folderlistmodel/fmutil.h (+46/-0)
src/plugin/folderlistmodel/folderlistmodel.pri (+44/-0)
src/plugin/folderlistmodel/folderlistmodel.pro (+39/-0)
src/plugin/folderlistmodel/imageprovider.cpp (+93/-0)
src/plugin/folderlistmodel/imageprovider.h (+29/-0)
src/plugin/folderlistmodel/iorequest.cpp (+185/-0)
src/plugin/folderlistmodel/iorequest.h (+109/-0)
src/plugin/folderlistmodel/iorequestworker.cpp (+100/-0)
src/plugin/folderlistmodel/iorequestworker.h (+61/-0)
src/plugin/folderlistmodel/ioworkerthread.cpp (+64/-0)
src/plugin/folderlistmodel/ioworkerthread.h (+52/-0)
src/plugin/folderlistmodel/plugin.cpp (+58/-0)
src/plugin/folderlistmodel/plugin.h (+75/-0)
src/plugin/folderlistmodel/qmldir (+2/-0)
src/plugin/folderlistmodel/trash/qtrashdir.cpp (+335/-0)
src/plugin/folderlistmodel/trash/qtrashdir.h (+128/-0)
src/plugin/test_folderlistmodel/regression/media_asx.h (+21/-0)
src/plugin/test_folderlistmodel/regression/media_xspf.h (+135/-0)
src/plugin/test_folderlistmodel/regression/regression_folderlilstmodel.pro (+30/-0)
src/plugin/test_folderlistmodel/regression/sound_7200_amr.h (+87/-0)
src/plugin/test_folderlistmodel/regression/sound_mp3.h (+3598/-0)
src/plugin/test_folderlistmodel/regression/tempfiles.cpp (+207/-0)
src/plugin/test_folderlistmodel/regression/tempfiles.h (+95/-0)
src/plugin/test_folderlistmodel/regression/testonly_pdf.h (+641/-0)
src/plugin/test_folderlistmodel/regression/tst_folderlistmodel.cpp (+2483/-0)
src/plugin/test_folderlistmodel/regression/ubuntu_touch_run.sh (+11/-0)
src/plugin/test_folderlistmodel/results/DesktopQt4.74.txt (+47/-0)
src/plugin/test_folderlistmodel/results/DesktopQt5.0.txt (+53/-0)
src/plugin/test_folderlistmodel/results/NemoEmulatorQ8.43.txt (+36/-0)
src/plugin/test_folderlistmodel/results/openFiles.Readme.txt (+7/-0)
src/plugin/test_folderlistmodel/simpleUI/main.cpp (+32/-0)
src/plugin/test_folderlistmodel/simpleUI/simplelist.cpp (+281/-0)
src/plugin/test_folderlistmodel/simpleUI/simplelist.h (+74/-0)
src/plugin/test_folderlistmodel/simpleUI/simplelist.ui (+219/-0)
src/plugin/test_folderlistmodel/simpleUI/simpleui.pro (+32/-0)
tests/autopilot/ubuntu_filemanager_app/tests/__init__.py (+35/-37)
ubuntu-filemanager-app.in (+0/-3)
ubuntu-filemanager-app.qmlproject (+0/-20)
To merge this branch: bzr merge lp:~dpm/ubuntu-filemanager-app/include-plugin
Reviewer Review Type Date Requested Status
Nicholas Skaggs (community) Approve
Ubuntu Phone Apps Jenkins Bot continuous-integration Approve
Carlos Jose Mazieri Approve
Sergio Schvezov (community) Needs Fixing
Review via email: mp+213368@code.launchpad.net

Commit message

Merge the C++ folderlistmodel plugin into the app's source tree

Description of the change

After a conversation with sergiusens it seems we can no longer build the File Manager app click package in our CI infrastructure if the plugin lives outside of the app's source tree.

This merge proposal does exactly that: bring the plugin into the app's source tree. It should be considered as work in progress, but as I'm not a cmake expert, I'm sending it as a request for feedback before merging it in.

What works:
- Project builds in Qt Creator
- click-buddy builds and packages the plugin and the app successfully
- Ctrl+R in Qt Creator runs the app
- Debian packaging: two independent packages (app and plugin) are generated

What's not yet implemented:
- The plugin depends on taglib1-dev. If it's not installed, cmake will fail. It'd be nice to add a check for it, but it seems cmake has only in-built checks for a certain number of libraries.

What will be addressed in a separate MP:
- Fix copyright headers of the plugin, which don't seem to point to the original sources

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: Needs Fixing (continuous-integration)
Revision history for this message
Sergio Schvezov (sergiusens) wrote :

20 + set(EXEC "qmlscene -qt5 qml/${MAIN_QML} -I ../plugin")

Prefer to use the upstart app launch set import dirs of lib/$arch/ and remove -I ../plugin"
If not set QT_IMPORTS_DIR will need to be set to this.

Additionally, and not related, I thought this project was forked from org.nemo but the copyright headers have:
197 + * Copyright 2014 Canonical Ltd.
198 + * Copyright 2014 Carlos J Mazieri <email address hidden>

and no trace of the original.

review: Needs Fixing
Revision history for this message
David Planella (dpm) wrote :

On Tue, Apr 1, 2014 at 10:12 AM, Sergio Schvezov <
<email address hidden>> wrote:

> Review: Needs Fixing
>
> 20 + set(EXEC "qmlscene -qt5 qml/${MAIN_QML} -I ../plugin")
>
> Prefer to use the upstart app launch set import dirs of lib/$arch/ and
> remove -I ../plugin"
> If not set QT_IMPORTS_DIR will need to be set to this.
>
>
Ok, thanks for the review! I think I've addressed this now, at least for
the click package build.

>
> Additionally, and not related, I thought this project was forked from
> org.nemo but the copyright headers have:
> 197 + * Copyright 2014 Canonical Ltd.
> 198 + * Copyright 2014 Carlos J Mazieri <email address hidden>
>
> and no trace of the original.
>

Oh, wow, I hadn't seen it. Yes, this needs to be addressed for all headers,
I'll send a separate MP fixing this.

Revision history for this message
Sergio Schvezov (sergiusens) wrote :

> On Tue, Apr 1, 2014 at 10:12 AM, Sergio Schvezov <
> <email address hidden>> wrote:
>
> > Review: Needs Fixing
> >
> > 20 + set(EXEC "qmlscene -qt5 qml/${MAIN_QML} -I ../plugin")
> >
> > Prefer to use the upstart app launch set import dirs of lib/$arch/ and
> > remove -I ../plugin"
> > If not set QT_IMPORTS_DIR will need to be set to this.
> >
> >
> Ok, thanks for the review! I think I've addressed this now, at least for
> the click package build.

Seems this looks good now

Revision history for this message
Sergio Schvezov (sergiusens) wrote :

95 +add_executable(filemanager ${filemanager_SRCS})
108 + install(TARGETS filemanager DESTINATION ${BIN_DIR})

I thought filemanager was launched with qmlscene $@

Is there a reason for building this? If so the exec needs to indeed change; but I'm guessing this isn't supposed to be there

Revision history for this message
David Planella (dpm) wrote :

On Tue, Apr 1, 2014 at 4:28 PM, Sergio Schvezov <
<email address hidden>> wrote:

> 95 +add_executable(filemanager ${filemanager_SRCS})
> 108 + install(TARGETS filemanager DESTINATION ${BIN_DIR})
>
> I thought filemanager was launched with qmlscene $@
>
> Is there a reason for building this? If so the exec needs to indeed
> change; but I'm guessing this isn't supposed to be there
>

This is intentional. The original intention was to use qmlscene, but as
pointed out in the original description, then Qt Creator could not launch
the app. After digging deeper into this and a conversation with zbenjamin,
it seems Qt Creator's cmake plugin cannot handle mixed (QML + C++) projects
to be launched with qmlscene, as it cannot actually detect which type of
project it is.

So the suggestion was to use a binary that does the work of qmlscene, so
that Qt Creator can launch that. Essentially, we're now doing what
Reminders does too.

So if this is needed, which exec needs to be changed?

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

> 20 + set(EXEC "qmlscene -qt5 qml/${MAIN_QML} -I ../plugin")
>
> Prefer to use the upstart app launch set im3port dirs of lib/$arch/ and remove
> -I ../plugin"
> If not set QT_IMPORTS_DIR will need to be set to this.
>
>
> Additionally, and not related, I thought this project was forked from org.nemo
> but the copyright headers have:
> 197 + * Copyright 2014 Canonical Ltd.
> 198 + * Copyright 2014 Carlos J Mazieri <email address hidden>
>
> and no trace of the original.

The project was forked from Nemo Mobile, orignal files were using BSD license, several files were added I just mentioned GPLv3 as result of some discussion we had about it in the meetings.

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
Ubuntu Phone Apps Jenkins Bot (ubuntu-phone-apps-jenkins-bot) wrote :
review: Needs Fixing (continuous-integration)
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
Carlos Jose Mazieri (carlos-mazieri) wrote :

It looks OK to me, everything is there.

I downloaded and tested https://code.launchpad.net/~dpm/ubuntu-filemanager-app/include-plugin using "qmake" instead of "cmake".

review: Approve
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
Nicholas Skaggs (nskaggs) wrote :

Looking close!

Revision history for this message
Nicholas Skaggs (nskaggs) wrote :

I'm proposing a merge to solve the AP launch issue.

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
Nicholas Skaggs (nskaggs) wrote :

Work happening here:

https://code.launchpad.net/~nskaggs/ubuntu-filemanager-app/ap-binary-support/+merge/215706

I've kicked off a build of jenkins including my commits, and I'll iterate if needed till we see proper launching.

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
Nicholas Skaggs (nskaggs) wrote :
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
Nicholas Skaggs (nskaggs) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2013-12-30 20:31:12 +0000
3+++ .bzrignore 2014-04-15 08:19:22 +0000
4@@ -6,3 +6,5 @@
5 debian/app-template/
6 debian/*.debhelper.log
7 debian/*.substvars
8+.excludes
9+*.user
10
11=== modified file 'CMakeLists.txt'
12--- CMakeLists.txt 2014-03-26 14:25:39 +0000
13+++ CMakeLists.txt 2014-04-15 08:19:22 +0000
14@@ -1,6 +1,9 @@
15-project(com.ubuntu.filemanager)
16+project(com.ubuntu.filemanager C CXX)
17 cmake_minimum_required(VERSION 2.8.9)
18
19+set(CMAKE_AUTOMOC ON)
20+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fno-permissive -pedantic -Wall -Wextra -fPIC")
21+
22 # Standard install paths
23 include(GNUInstallDirs)
24
25@@ -8,49 +11,56 @@
26 option(CLICK_MODE "Installs to a contained location" off)
27
28 set(APP_NAME filemanager)
29-set(DESKTOP_FILE "${PROJECT_NAME}_${APP_NAME}.desktop")
30+set(DESKTOP_FILE "${PROJECT_NAME}.desktop")
31 set(URLS_FILE "${PROJECT_NAME}_${APP_NAME}.url-dispatcher")
32 set(APP_HARDCODE ubuntu-filemanager-app)
33 set(MAIN_QML ${APP_HARDCODE}.qml)
34 set(ICON_FILE filemanager64.png)
35 set(AUTOPILOT_DIR ubuntu_filemanager_app)
36+set(EXEC "filemanager")
37
38 if(CLICK_MODE)
39 if(NOT DEFINED BZR_SOURCE)
40 set(BZR_SOURCE "lp:${APP_HARDCODE}")
41 message("-- Setting BZR_SOURCE to ${BZR_SOURCE}")
42 endif(NOT DEFINED BZR_SOURCE)
43+ # Find out the architecture for package building
44+ # to determine the plugin's installation path
45+ execute_process(
46+ COMMAND dpkg-architecture -qDEB_HOST_MULTIARCH
47+ OUTPUT_VARIABLE ARCH_TRIPLET
48+ OUTPUT_STRIP_TRAILING_WHITESPACE
49+ )
50 set(CMAKE_INSTALL_PREFIX /)
51 set(CMAKE_INSTALL_BINDIR /)
52 set(DATA_DIR /)
53 set(ICON ${ICON_FILE})
54- set(EXEC "qmlscene -qt5 ${MAIN_QML}")
55+ set(QT_IMPORTS_DIR "/lib/${ARCH_TRIPLET}")
56+ set(BIN_DIR /lib/${ARCH_TRIPLET}/bin)
57 set(DESKTOP_DIR ${DATA_DIR})
58 set(URLS_DIR ${DATA_DIR})
59+ install(FILES manifest.json apparmor.json DESTINATION ${CMAKE_INSTALL_PREFIX})
60 else(CLICK_MODE)
61+ execute_process(
62+ COMMAND qmake -query QT_INSTALL_QML
63+ OUTPUT_VARIABLE QT_IMPORTS_DIR
64+ OUTPUT_STRIP_TRAILING_WHITESPACE
65+ )
66 set(DATA_DIR ${CMAKE_INSTALL_DATADIR}/${APP_HARDCODE})
67- set(EXEC ${APP_HARDCODE})
68 set(ICON ${CMAKE_INSTALL_PREFIX}/${DATA_DIR}/${ICON_FILE})
69- configure_file(${APP_HARDCODE}.in
70- ${CMAKE_CURRENT_BINARY_DIR}/${APP_HARDCODE})
71- install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/${APP_HARDCODE}
72- DESTINATION ${CMAKE_INSTALL_BINDIR})
73 set(DESKTOP_DIR ${CMAKE_INSTALL_DATADIR}/applications)
74 set(URLS_DIR ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls)
75 endif(CLICK_MODE)
76
77 file(GLOB_RECURSE I18N_SRC_FILES
78 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
79- rc/**.qml desktop/**.desktop.in)
80+ src/app/**.qml *.desktop.in)
81 list(SORT I18N_SRC_FILES)
82
83-
84 file(GLOB SRC_FILES
85 RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
86- *.qml *.js *.png *.js *.json)
87-install(DIRECTORY ui components icons
88- DESTINATION ${DATA_DIR})
89-install(FILES ${SRC_FILES} ${ICON_FILE} DESTINATION ${DATA_DIR})
90+ *.qml *.js *.png *.js)
91+install(FILES ${SRC_FILES} DESTINATION ${DATA_DIR})
92
93 configure_file(${DESKTOP_FILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE})
94 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${DESKTOP_FILE}
95@@ -61,6 +71,6 @@
96 # Tests
97 enable_testing()
98
99-add_subdirectory(click)
100 add_subdirectory(po)
101 add_subdirectory(tests)
102+add_subdirectory(src)
103
104=== renamed file 'click/apparmor.json' => 'apparmor.json'
105--- click/apparmor.json 2014-01-31 20:03:12 +0000
106+++ apparmor.json 2014-04-15 08:19:22 +0000
107@@ -1,4 +1,4 @@
108 {
109- "policy_version": 1.0,
110+ "policy_version": 1.1,
111 "template": "unconfined"
112-}
113\ No newline at end of file
114+}
115
116=== removed directory 'click'
117=== removed file 'click/CMakeLists.txt'
118--- click/CMakeLists.txt 2014-01-31 20:03:12 +0000
119+++ click/CMakeLists.txt 1970-01-01 00:00:00 +0000
120@@ -1,8 +0,0 @@
121-if(CLICK_MODE)
122- if(NOT BZR_REVNO)
123- set(BZR_REVNO "latest")
124- endif(NOT BZR_REVNO)
125- configure_file(manifest.json.in ${CMAKE_CURRENT_BINARY_DIR}/manifest.json)
126- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/manifest.json apparmor.json
127- DESTINATION ${CMAKE_INSTALL_PREFIX})
128-endif(CLICK_MODE)
129
130=== renamed file 'com.ubuntu.filemanager_filemanager.desktop.in' => 'com.ubuntu.filemanager.desktop.in'
131=== modified file 'debian/changelog'
132--- debian/changelog 2013-04-19 20:30:19 +0000
133+++ debian/changelog 2014-04-15 08:19:22 +0000
134@@ -1,3 +1,10 @@
135+ubuntu-filemanager-app (0.2) trusty; urgency=medium
136+
137+ * Merged the plugin code into the main tree, now the main source package
138+ generates a binary package for the app and another for the plugin
139+
140+ -- David Planella <david.planella@ubuntu.com> Thu, 10 Apr 2014 09:23:41 +0200
141+
142 ubuntu-filemanager-app (0.1.1) raring; urgency=low
143
144 * Initial release
145
146=== modified file 'debian/control'
147--- debian/control 2014-03-07 21:10:42 +0000
148+++ debian/control 2014-04-15 08:19:22 +0000
149@@ -4,14 +4,19 @@
150 Build-Depends: cmake,
151 debhelper (>= 9),
152 python,
153-Standards-Version: 3.9.4
154+ qtbase5-dev,
155+ qtdeclarative5-dev,
156+ qt5-default,
157+ pkg-kde-tools,
158+ libtag1-dev,
159+Standards-Version: 3.9.5
160 Section: misc
161 Homepage: https://launchpad.net/ubuntu-filemanager-app
162 Vcs-Bzr: https://code.launchpad.net/~ubuntu-filemanager-dev/ubuntu-filemanager-app/trunk
163
164 Package: ubuntu-filemanager-app
165 Section: misc
166-Architecture: all
167+Architecture: any
168 Depends: qmlscene,
169 qtdeclarative5-hud1.0,
170 qtdeclarative5-nemo-qml-plugin-folderlistmodel,
171@@ -22,6 +27,18 @@
172 Description: File Manager application
173 Core File Manager application
174
175+Package: qtdeclarative5-nemo-qml-plugin-folderlistmodel
176+Architecture: any
177+Multi-Arch: same
178+Depends: ${misc:Depends},
179+ ${shlibs:Depends},
180+Description: Nemo QML plugin - folder list model
181+ Qt is a cross-platform C++ application framework. Qt's primary feature
182+ is its rich set of widgets that provide standard GUI functionality.
183+ .
184+ This package contains the Folder List model plugin of the Nemo QML
185+ plugins collection.
186+
187 Package: ubuntu-filemanager-app-autopilot
188 Architecture: all
189 Depends: libautopilot-qt (>= 1.4),
190
191=== added file 'debian/qtdeclarative5-nemo-qml-plugin-folderlistmodel.install'
192--- debian/qtdeclarative5-nemo-qml-plugin-folderlistmodel.install 1970-01-01 00:00:00 +0000
193+++ debian/qtdeclarative5-nemo-qml-plugin-folderlistmodel.install 2014-04-15 08:19:22 +0000
194@@ -0,0 +1,1 @@
195+usr/lib/*/qt5/qml/org/nemomobile/folderlistmodel/
196
197=== renamed file 'click/manifest.json.in' => 'manifest.json'
198--- click/manifest.json.in 2014-04-11 22:15:18 +0000
199+++ manifest.json 2014-04-15 08:19:22 +0000
200@@ -1,23 +1,24 @@
201 {
202 "description": "File Manager application",
203- "framework": "ubuntu-sdk-13.10",
204+ "framework": "ubuntu-sdk-14.04-dev1",
205+ "architecture": "armhf",
206 "hooks": {
207- "@APP_NAME@": {
208+ "filemanager": {
209 "apparmor": "apparmor.json",
210- "desktop": "@DESKTOP_FILE@",
211- "urls": "@URLS_FILE@"
212+ "desktop": "com.ubuntu.filemanager.desktop",
213+ "urls": "com.ubuntu.filemanager_filemanager.url-dispatcher"
214 }
215 },
216- "icon": "@ICON@",
217+ "icon": "filemanager64.png",
218 "maintainer": "Ubuntu App Cats <ubuntu-touch-coreapps@lists.launchpad.net>",
219- "name": "@PROJECT_NAME@",
220+ "name": "com.ubuntu.filemanager",
221 "title": "File Manager",
222- "version": "0.1.1.@BZR_REVNO@",
223+ "version": "0.3",
224 "x-source": {
225- "vcs-bzr": "@BZR_SOURCE@",
226- "vcs-bzr-revno": "@BZR_REVNO@"
227+ "vcs-bzr": "lp:ubuntu-filemanager-app",
228+ "vcs-bzr-revno": "latest"
229 },
230 "x-test": {
231- "autopilot": "@AUTOPILOT_DIR@"
232+ "autopilot": "ubuntu_filemanager_app"
233 }
234-}
235+}
236\ No newline at end of file
237
238=== removed file 'plugins.json'
239--- plugins.json 2013-10-10 16:43:14 +0000
240+++ plugins.json 1970-01-01 00:00:00 +0000
241@@ -1,6 +0,0 @@
242-[
243-{
244- "package": "qtdeclarative5-nemo-qml-plugin-folderlistmodel",
245- "ppa": "ppa:ubuntu-touch-coreapps-drivers/daily"
246-}
247-]
248
249=== added directory 'src'
250=== added file 'src/CMakeLists.txt'
251--- src/CMakeLists.txt 1970-01-01 00:00:00 +0000
252+++ src/CMakeLists.txt 2014-04-15 08:19:22 +0000
253@@ -0,0 +1,2 @@
254+add_subdirectory(plugin)
255+add_subdirectory(app)
256
257=== added directory 'src/app'
258=== added file 'src/app/CMakeLists.txt'
259--- src/app/CMakeLists.txt 1970-01-01 00:00:00 +0000
260+++ src/app/CMakeLists.txt 2014-04-15 08:19:22 +0000
261@@ -0,0 +1,24 @@
262+file(GLOB_RECURSE QML_SRCS *.qml *.js)
263+
264+set(filemanager_SRCS
265+ main.cpp
266+ ${QML_SRCS}
267+)
268+
269+add_executable(filemanager ${filemanager_SRCS})
270+
271+qt5_use_modules(filemanager Gui Qml Quick)
272+
273+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
274+add_custom_target(filemanager-qmlfiles ALL
275+ COMMAND cp -r ${CMAKE_CURRENT_SOURCE_DIR}/qml ${CMAKE_CURRENT_BINARY_DIR}
276+ DEPENDS ${QMLFILES}
277+)
278+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
279+
280+install(DIRECTORY qml DESTINATION ${DATA_DIR})
281+if(CLICK_MODE)
282+ install(TARGETS filemanager DESTINATION ${BIN_DIR})
283+else()
284+ install(TARGETS filemanager RUNTIME DESTINATION bin)
285+endif()
286
287=== added file 'src/app/main.cpp'
288--- src/app/main.cpp 1970-01-01 00:00:00 +0000
289+++ src/app/main.cpp 2014-04-15 08:19:22 +0000
290@@ -0,0 +1,114 @@
291+/*
292+ * Copyright: 2013 - 2014 Canonical, Ltd
293+ *
294+ * This file is part of ubuntu-filemanager-app
295+ *
296+ * ubuntu-filemanager-app is free software: you can redistribute it and/or modify
297+ * it under the terms of the GNU General Public License as published by
298+ * the Free Software Foundation, either version 3 of the License, or
299+ * (at your option) any later version.
300+ *
301+ * ubuntu-filemanager-app is distributed in the hope that it will be useful,
302+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
303+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304+ * GNU General Public License for more details.
305+ *
306+ * You should have received a copy of the GNU General Public License
307+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
308+ *
309+ * Authors: Michael Zanetti <michael.zanetti@canonical.com>
310+ * Riccardo Padovani <rpadovani@ubuntu.com>
311+ * David Planella <david.planella@ubuntu.com>
312+ */
313+
314+#include <QtGui/QGuiApplication>
315+#include <QtQuick/QQuickView>
316+#include <QtQml/QtQml>
317+#include <QLibrary>
318+
319+#include <QDebug>
320+
321+int main(int argc, char *argv[])
322+{
323+ QGuiApplication a(argc, argv);
324+ QQuickView view;
325+ view.setResizeMode(QQuickView::SizeRootObjectToView);
326+
327+ // Set up import paths
328+ QStringList importPathList = view.engine()->importPathList();
329+ importPathList.append(QDir::currentPath() + "/../plugin/");
330+
331+ QStringList args = a.arguments();
332+ if (args.contains("-h") || args.contains("--help")) {
333+ qDebug() << "usage: " + args.at(0) + " [-p|--phone] [-t|--tablet] [-h|--help] [-I <path>]";
334+ qDebug() << " -p|--phone If running on Desktop, start in a phone sized window.";
335+ qDebug() << " -t|--tablet If running on Desktop, start in a tablet sized window.";
336+ qDebug() << " -h|--help Print this help.";
337+ qDebug() << " -I <path> Give a path for an additional QML import directory. May be used multiple times.";
338+ qDebug() << " -q <qmlfile> Give an alternative location for the main qml file.";
339+ return 0;
340+ }
341+
342+ QString qmlfile;
343+ for (int i = 0; i < args.count(); i++) {
344+ if (args.at(i) == "-I" && args.count() > i + 1) {
345+ QString addedPath = args.at(i+1);
346+ if (addedPath.startsWith('.')) {
347+ addedPath = addedPath.right(addedPath.length() - 1);
348+ addedPath.prepend(QDir::currentPath());
349+ }
350+ importPathList.append(addedPath);
351+ } else if (args.at(i) == "-q" && args.count() > i + 1) {
352+ qmlfile = args.at(i+1);
353+ }
354+ }
355+
356+ if (args.contains(QLatin1String("-testability")) || getenv("QT_LOAD_TESTABILITY")) {
357+ QLibrary testLib(QLatin1String("qttestability"));
358+ if (testLib.load()) {
359+ typedef void (*TasInitialize)(void);
360+ TasInitialize initFunction = (TasInitialize)testLib.resolve("qt_testability_init");
361+ if (initFunction) {
362+ initFunction();
363+ } else {
364+ qCritical("Library qttestability resolve failed!");
365+ }
366+ } else {
367+ qCritical("Library qttestability load failed!");
368+ }
369+ }
370+
371+ view.engine()->rootContext()->setContextProperty("tablet", QVariant(false));
372+ view.engine()->rootContext()->setContextProperty("phone", QVariant(false));
373+ if (args.contains("-t") || args.contains("--tablet")) {
374+ qDebug() << "running in tablet mode";
375+ view.engine()->rootContext()->setContextProperty("tablet", QVariant(true));
376+ } else if (args.contains("-p") || args.contains("--phone")){
377+ qDebug() << "running in phone mode";
378+ view.engine()->rootContext()->setContextProperty("phone", QVariant(true));
379+ } else if (qgetenv("QT_QPA_PLATFORM") != "ubuntumirclient") {
380+ // Default to tablet size on X11
381+ view.engine()->rootContext()->setContextProperty("tablet", QVariant(true));
382+ }
383+
384+ view.engine()->setImportPathList(importPathList);
385+
386+ // load the qml file
387+ if (qmlfile.isEmpty()) {
388+ QStringList paths = QStandardPaths::standardLocations(QStandardPaths::DataLocation);
389+ paths.prepend(".");
390+
391+ foreach (const QString &path, paths) {
392+ QFileInfo fi(path + "/qml/ubuntu-filemanager-app.qml");
393+ if (fi.exists()) {
394+ qmlfile = path + "/qml/ubuntu-filemanager-app.qml";
395+ break;
396+ }
397+ }
398+ }
399+ qDebug() << "using main qml file from:" << qmlfile;
400+ view.setSource(QUrl::fromLocalFile(qmlfile));
401+ view.show();
402+
403+ return a.exec();
404+}
405
406=== renamed directory 'components' => 'src/app/qml'
407=== added directory 'src/app/qml/components'
408=== renamed file 'components/AutoSpacedGrid.qml' => 'src/app/qml/components/AutoSpacedGrid.qml'
409=== renamed file 'components/FolderIconDelegate.qml' => 'src/app/qml/components/FolderIconDelegate.qml'
410=== renamed file 'components/FolderIconView.qml' => 'src/app/qml/components/FolderIconView.qml'
411=== renamed file 'components/FolderListDelegate.qml' => 'src/app/qml/components/FolderListDelegate.qml'
412=== renamed file 'components/FolderListView.qml' => 'src/app/qml/components/FolderListView.qml'
413=== renamed file 'components/PathBar.qml' => 'src/app/qml/components/PathBar.qml'
414=== renamed file 'components/PlacesSidebar.qml' => 'src/app/qml/components/PlacesSidebar.qml'
415=== renamed file 'components/Sidebar.qml' => 'src/app/qml/components/Sidebar.qml'
416=== renamed file 'components/SuruSheetStyle.qml' => 'src/app/qml/components/SuruSheetStyle.qml'
417=== renamed file 'components/VerticalDivider.qml' => 'src/app/qml/components/VerticalDivider.qml'
418=== renamed file 'ubuntu-filemanager-app.qml' => 'src/app/qml/ubuntu-filemanager-app.qml'
419=== renamed directory 'ui' => 'src/app/qml/ui'
420=== added directory 'src/plugin'
421=== added file 'src/plugin/CMakeLists.txt'
422--- src/plugin/CMakeLists.txt 1970-01-01 00:00:00 +0000
423+++ src/plugin/CMakeLists.txt 2014-04-15 08:19:22 +0000
424@@ -0,0 +1,7 @@
425+include(FindPkgConfig)
426+find_package(Qt5Core)
427+find_package(Qt5Qml)
428+find_package(Qt5Quick)
429+find_package(Qt5Widgets)
430+
431+add_subdirectory(folderlistmodel)
432
433=== added directory 'src/plugin/folderlistmodel'
434=== added file 'src/plugin/folderlistmodel/CMakeLists.txt'
435--- src/plugin/folderlistmodel/CMakeLists.txt 1970-01-01 00:00:00 +0000
436+++ src/plugin/folderlistmodel/CMakeLists.txt 2014-04-15 08:19:22 +0000
437@@ -0,0 +1,54 @@
438+include_directories(
439+ ${CMAKE_CURRENT_SOURCE_DIR}
440+)
441+
442+set(folderlistmodel_SRCS
443+ clipboard.cpp
444+ clipboard.h
445+ diritemabstractlistmodel.h
446+ diriteminfo.cpp
447+ diriteminfo.h
448+ dirmodel.cpp
449+ dirmodel.h
450+ dirselection.cpp
451+ dirselection.h
452+ externalfswatcher.cpp
453+ externalfswatcher.h
454+ filecompare.cpp
455+ filecompare.h
456+ filesystemaction.cpp
457+ filesystemaction.h
458+ fmutil.cpp
459+ fmutil.h
460+ imageprovider.cpp
461+ imageprovider.h
462+ iorequest.cpp
463+ iorequest.h
464+ iorequestworker.cpp
465+ iorequestworker.h
466+ ioworkerthread.cpp
467+ ioworkerthread.h
468+ plugin.cpp
469+ plugin.h
470+ trash/qtrashdir.cpp
471+ trash/qtrashdir.h
472+)
473+
474+add_library(folderlistmodel MODULE
475+ ${folderlistmodel_SRCS}
476+)
477+
478+qt5_use_modules(folderlistmodel Gui Qml Quick Widgets)
479+
480+# Copy qmldir file to build dir for running in QtCreator
481+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
482+ add_custom_target(folderlistmodel-qmldir ALL
483+ COMMAND cp ${CMAKE_CURRENT_SOURCE_DIR}/qmldir ${CMAKE_CURRENT_BINARY_DIR}
484+ DEPENDS ${QMLFILES}
485+ )
486+endif(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_BINARY_DIR}")
487+
488+# Install plugin file
489+install(TARGETS folderlistmodel DESTINATION ${QT_IMPORTS_DIR}/org/nemomobile/folderlistmodel/)
490+install(FILES qmldir DESTINATION ${QT_IMPORTS_DIR}/org/nemomobile/folderlistmodel/)
491+
492
493=== added file 'src/plugin/folderlistmodel/README'
494--- src/plugin/folderlistmodel/README 1970-01-01 00:00:00 +0000
495+++ src/plugin/folderlistmodel/README 2014-04-15 08:19:22 +0000
496@@ -0,0 +1,14 @@
497+Building and installing
498+=======================
499+
500+qmake [available-defines]
501+
502+ where available-debug-messages are:
503+ DEBUG_MESSAGES -> enable generic debug messages use: qmake "DEINES+=DEBUG_MESSAGES"
504+ DEBUG_EXT_FS_WATCHER -> enable External File System Watcher messages use: qmake "DEINES+=DEBUG_EXT_FS_WATCHER"
505+ DEBUG_REMOVE -> enable message about every remove use: qmake "DEINES+=DEBUG_REMOVE"
506+ DO_NOT_USE_TAG_LIB -> disable using TAGLIB and getting metada use: qmake "DEINES+=DO_NOT_USE_TAG_LIB"
507+
508+make
509+sudo make install
510+
511
512=== added file 'src/plugin/folderlistmodel/clipboard.cpp'
513--- src/plugin/folderlistmodel/clipboard.cpp 1970-01-01 00:00:00 +0000
514+++ src/plugin/folderlistmodel/clipboard.cpp 2014-04-15 08:19:22 +0000
515@@ -0,0 +1,495 @@
516+/**************************************************************************
517+ *
518+ * Copyright 2014 Canonical Ltd.
519+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
520+ *
521+ * You may use this file under the terms of the BSD license as follows:
522+ *
523+ * "Redistribution and use in source and binary forms, with or without
524+ * modification, are permitted provided that the following conditions are
525+ * met:
526+ * * Redistributions of source code must retain the above copyright
527+ * notice, this list of conditions and the following disclaimer.
528+ * * Redistributions in binary form must reproduce the above copyright
529+ * notice, this list of conditions and the following disclaimer in
530+ * the documentation and/or other materials provided with the
531+ * distribution.
532+ * * Neither the name of Nemo Mobile nor the names of its contributors
533+ * may be used to endorse or promote products derived from this
534+ * software without specific prior written permission.
535+ *
536+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
537+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
538+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
539+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
540+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
541+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
542+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
543+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
544+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
545+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
546+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
547+ *
548+ * File: clipboard.cpp
549+ * Date: 1/22/2014
550+ */
551+
552+#include "clipboard.h"
553+
554+#include <QClipboard>
555+#include <QApplication>
556+#include <QDir>
557+#include <QFileInfo>
558+#include <QDebug>
559+
560+static QLatin1String GNOME_COPIED_MIME_TYPE ("x-special/gnome-copied-files");
561+static QLatin1String KDE_CUT_MIME_TYPE ("application/x-kde-cutselection");
562+
563+
564+int DirModelMimeData::m_instances = 0;
565+DirModelMimeData* DirModelMimeData::m_globalMimeData = 0;
566+
567+
568+bool DirModelMimeData::hasFormat ( const QString & mimeType ) const
569+{
570+ bool ret = false;
571+ if ( mimeType == KDE_CUT_MIME_TYPE )
572+ {
573+ ret = true;
574+ }
575+ else
576+ {
577+ ret = m_formats.contains(mimeType);
578+ }
579+ return ret;
580+}
581+
582+//===============================================================================================
583+/*!
584+ * \brief DirModelMimeData::DirModelMimeData
585+ */
586+DirModelMimeData::DirModelMimeData() :
587+ QMimeData()
588+ , m_appMime(0)
589+{
590+ m_formats.append("text/uri-list");
591+ m_formats.append(GNOME_COPIED_MIME_TYPE);
592+ m_formats.append("text/plain");
593+ m_formats.append("COMPOUND_TEXT");
594+ m_formats.append("TARGETS");
595+ m_formats.append("MULTIPLE");
596+ m_formats.append("TIMESTAMP");
597+ m_formats.append("SAVE_TARGETS");
598+
599+ ++m_instances;
600+#if DEBUG_MESSAGES
601+ qDebug() << Q_FUNC_INFO << this << "instances" << m_instances;
602+#endif
603+}
604+
605+
606+
607+
608+DirModelMimeData::~DirModelMimeData()
609+{
610+ --m_instances;
611+#if DEBUG_MESSAGES
612+ qDebug() << Q_FUNC_INFO << this << "instances" << m_instances
613+ << "m_globalMimeData" << m_globalMimeData;
614+#endif
615+ if (m_instances == 1 && m_globalMimeData)
616+ {
617+ DirModelMimeData * tmp = m_globalMimeData;
618+ m_globalMimeData = 0;
619+ delete tmp;
620+ }
621+}
622+
623+//===============================================================================================
624+/*!
625+ * \brief DirModelMimeData::gnomeUrls
626+ * \param mime
627+ * \param operation
628+ * \return
629+ */
630+QList<QUrl>
631+DirModelMimeData::gnomeUrls(const QMimeData * mime,
632+ ClipboardOperation& operation)
633+{
634+ QList<QUrl> urls;
635+ if (mime->hasFormat(GNOME_COPIED_MIME_TYPE))
636+ {
637+ QByteArray bytes = mime->data(GNOME_COPIED_MIME_TYPE);
638+ QList<QString> d = QString(bytes).split(QLatin1String("\n"),
639+ QString::SkipEmptyParts);
640+ operation = ClipboardCopy;
641+ if (d.count() > 0)
642+ {
643+ if (d.at(0).trimmed().startsWith(QLatin1String("cut")))
644+ {
645+ operation = ClipboardCut;
646+ }
647+ for (int counter= 1; counter < d.count(); counter++)
648+ {
649+ urls.append(d.at(counter).trimmed());
650+ }
651+ }
652+ }
653+ return urls;
654+}
655+
656+//===============================================================================================
657+/*!
658+ * \brief DirModelMimeData::clipBoardOperation()
659+ * \param mime
660+ * \return
661+ */
662+ClipboardOperation DirModelMimeData::clipBoardOperation()
663+{
664+ ClipboardOperation op = ClipboardCopy;
665+ m_appMime = clipboardMimeData();
666+ if (m_appMime)
667+ {
668+ //first check for GNOME clipboard format, op comes with Copy/Cut
669+ if (gnomeUrls(m_appMime, op).count() == 0)
670+ { // there is no gnome format, tries KDE format
671+ QStringList formats = m_appMime->formats();
672+ int f = formats.count();
673+ while(f--)
674+ {
675+ const QString &mi = formats.at(f);
676+ if(mi.startsWith(QLatin1String("application/x-kde")) )
677+ {
678+ if (mi.contains(QLatin1String("cut")))
679+ {
680+ op = ClipboardCut;
681+ break;
682+ }
683+ }
684+ }
685+ }
686+ }
687+ return op;
688+}
689+
690+
691+//===============================================================================================
692+/*!
693+ * \brief DirModelMimeData::setIntoClipboard
694+ *
695+ * Try to put data in the global cliboard
696+ *
697+ * \note:
698+ * On mobile devices clipboard might not work, in this case a local Clipboard is simulated
699+ *
700+ * \param files
701+ * \param path
702+ * \param isCut
703+ * \return who is owner of clipboard data
704+ */
705+DirModelMimeData::ClipBoardDataOwner
706+DirModelMimeData::setIntoClipboard(const QStringList &files, const QString& path, ClipboardOperation operation)
707+{
708+ static bool firstTime = true;
709+ DirModelMimeData::ClipBoardDataOwner ret = Nobody;
710+ QClipboard *clipboard = QApplication::clipboard();
711+ if (clipboard)
712+ {
713+ ret = Application;
714+ DirModelMimeData *mime = m_globalMimeData ? m_globalMimeData
715+ : new DirModelMimeData();
716+ if (mime->fillClipboard(files, path, operation))
717+ {
718+ clipboard->setMimeData(mime);
719+ //it looks like some mobile devices does not have X or Clipboard does work for other reason
720+ //in this case we simulate our own clipboard, the QClipboard::dataChanged() signal is also
721+ //checked in \ref Clipboard::storeOnClipboard()
722+ if (firstTime)
723+ {
724+ firstTime = false;
725+ if (!m_globalMimeData && !testClipboardContent(files, path))
726+ {
727+ qWarning() << "QClipboard does not work, using own QMimeData storage";
728+ m_globalMimeData = mime;
729+ }
730+ }
731+#if DEBUG_MESSAGES
732+ qDebug() << Q_FUNC_INFO << "mime" << mime
733+ << "own Clipboard Mime Data" << m_globalMimeData;
734+#endif
735+ }
736+ else
737+ if (m_globalMimeData != mime)
738+ {
739+ delete mime;
740+ }
741+ //check if it is necessary to send notification about Clipboard changed
742+ if (m_globalMimeData)
743+ {
744+ ret = MySelf;
745+ }
746+ }
747+ return ret;
748+}
749+
750+
751+
752+bool DirModelMimeData::fillClipboard(const QStringList& files, const QString &path, ClipboardOperation operation)
753+{
754+ bool ret = false;
755+ int index = m_formats.indexOf(KDE_CUT_MIME_TYPE);
756+ if (index != -1 && operation != ClipboardCut)
757+ {
758+ m_formats.removeAt(index);
759+ }
760+ else
761+ if (operation == ClipboardCut)
762+ {
763+ m_formats.append(KDE_CUT_MIME_TYPE);
764+ }
765+ m_urls.clear();
766+ m_gnomeData.clear();
767+ m_gnomeData += operation == ClipboardCut ?
768+ QLatin1String("cut") :
769+ QLatin1String("copy");
770+ QStringList fullPaths = makeFullPath(files, path);
771+ for(int counter = 0; counter < fullPaths.count(); counter++)
772+ {
773+ QUrl item = QUrl::fromLocalFile(fullPaths.at((counter)));
774+ m_urls.append(item);
775+ m_gnomeData += QLatin1Char('\n') + item.toEncoded() ;
776+ }
777+ if (m_urls.count() > 0)
778+ {
779+ setData(GNOME_COPIED_MIME_TYPE, m_gnomeData);
780+ setUrls(m_urls);
781+ ret = true;
782+ }
783+ else
784+ {
785+ // emit error( QObject::tr("Item does not exist"), item);
786+ }
787+ return ret;
788+}
789+
790+//===============================================================================================
791+/*!
792+ * \brief DirModelMimeData::clipboardMimeData
793+ * \return
794+ */
795+const QMimeData *DirModelMimeData::clipboardMimeData()
796+{
797+ const QMimeData *ret = 0;
798+ QClipboard *clipboard = QApplication::clipboard();
799+ if (m_globalMimeData)
800+ {
801+ ret = m_globalMimeData;
802+ }
803+ else
804+ if (clipboard)
805+ {
806+ ret = clipboard->mimeData();
807+ }
808+#if DEBUG_MESSAGES
809+ qDebug() << Q_FUNC_INFO << "clipboard" << clipboard
810+ << "m_ownClipboardMimeData" << m_globalMimeData
811+ << "clipboard->mimeData()" << ret;
812+#endif
813+ return ret;
814+}
815+
816+//===============================================================================================
817+/*!
818+ * \brief DirModelMimeData::localUrls
819+ * \return
820+ */
821+QStringList
822+DirModelMimeData::localUrls(ClipboardOperation& operation)
823+{
824+ m_appMime = clipboardMimeData();
825+ QStringList paths;
826+ //it may have external urls
827+ if (m_appMime)
828+ {
829+ QList<QUrl> urls;
830+ if (m_appMime->hasUrls())
831+ {
832+ urls = m_appMime->urls();
833+ operation = clipBoardOperation();
834+ }
835+ else
836+ {
837+ urls = gnomeUrls(m_appMime, operation);
838+ }
839+ for (int counter=0; counter < urls.count(); counter++)
840+ {
841+ if (urls.at(counter).toString().startsWith(QLatin1String("file://")))
842+ {
843+ paths.append(urls.at(counter).toLocalFile());
844+ }
845+ }
846+ }
847+#if DEBUG_MESSAGES
848+ qDebug() << Q_FUNC_INFO << paths;
849+#endif
850+ return paths;
851+}
852+
853+
854+//===============================================================================================
855+/*!
856+ * \brief DirModelMimeData::testClipboardContent() Gets the clipboard content and compare with data previously stored
857+ * \param files
858+ * \param path
859+ * \return true if clipboard has content and it matches data previously stored
860+ */
861+bool DirModelMimeData::testClipboardContent(const QStringList &files, const QString &path)
862+{
863+ bool ret = false;
864+ ClipboardOperation tmpOperation;
865+ QStringList expectedList = makeFullPath(files,path);
866+ QStringList realList = localUrls(tmpOperation);
867+ if (realList == expectedList)
868+ {
869+ ret = true;
870+ }
871+ else
872+ {
873+ qWarning() << Q_FUNC_INFO << "FAILED, Clipboard does not work";
874+ }
875+ return ret;
876+}
877+
878+//===============================================================================================
879+/*!
880+ * \brief DirModelMimeData::makeFullPath() Just creates a fulpath file list when they do exist
881+ * \param files
882+ * \param path
883+ * \return the list itself
884+ */
885+QStringList DirModelMimeData::makeFullPath(const QStringList& files, const QString &path)
886+{
887+ QStringList fullPathnameList;
888+ QFileInfo fi;
889+ for(int counter = 0; counter < files.count(); counter++)
890+ {
891+ const QString& item = files.at(counter);
892+ fi.setFile(item);
893+ if (!fi.isAbsolute())
894+ {
895+ fi.setFile(path + QDir::separator() + item);
896+ }
897+ if (fi.exists())
898+ {
899+ fullPathnameList.append(fi.absoluteFilePath());
900+ }
901+ }
902+ return fullPathnameList;
903+}
904+
905+
906+//===========================================================================
907+//
908+//===========================================================================
909+Clipboard::Clipboard(QObject *parent):
910+ QObject(parent)
911+ , m_mimeData ( new DirModelMimeData() )
912+ , m_clipboardModifiedByOther(false)
913+{
914+ QClipboard *clipboard = QApplication::clipboard();
915+
916+ connect(clipboard, SIGNAL(dataChanged()), this, SIGNAL(clipboardChanged()));
917+ connect(clipboard, SIGNAL(dataChanged()), this, SLOT(onClipboardChanged()));
918+}
919+
920+
921+Clipboard::~Clipboard()
922+{
923+ delete m_mimeData;
924+}
925+
926+//================================================================================
927+/*!
928+ * \brief Clipboard::clipboardHasChanged() used to identify if the clipboard changed during a Cut operation
929+ *
930+ * \sa \ref endCurrentAction()
931+ */
932+void Clipboard::onClipboardChanged()
933+{
934+ m_clipboardModifiedByOther = true;
935+}
936+
937+
938+//==================================================================
939+/*!
940+ * \brief Clipboard::storeOnClipboard() store data on Clipboard
941+ * \param pathnames files list
942+ * \param op \ref ClipboardOperation as \ref ClipboardCopy or \ref ClipboardCut
943+ *
944+ * Stores data on clipboard by calling \ref DirModelMimeData::setIntoClipboard() which uses Qt class QClipboard
945+ * It is expected that QClipboard class emits the dataChanged() signal when a new content is set into it,
946+ * if it does we caught that signal in \ref clipboardHasChanged() which sets \ref m_clipboardModifiedByOther to true.
947+ */
948+void Clipboard::storeOnClipboard(const QStringList &names, ClipboardOperation op, const QString& curPath)
949+{
950+#if DEBUG_MESSAGES
951+ qDebug() << Q_FUNC_INFO << names << "ClipboardOperation" << op;
952+#endif
953+ DirModelMimeData::ClipBoardDataOwner owner =
954+ m_mimeData->setIntoClipboard(names, curPath, op);
955+ if (owner == DirModelMimeData::MySelf || !m_clipboardModifiedByOther)
956+ {
957+ emit clipboardChanged();
958+ }
959+ m_clipboardModifiedByOther = false;
960+}
961+
962+//===============================================================================================
963+/*!
964+ * \brief Clipboard::copy
965+ * \param pathnames
966+ */
967+void Clipboard::copy(const QStringList &names, const QString& path)
968+{
969+ storeOnClipboard(names, ClipboardCopy, path);
970+}
971+
972+//===============================================================================================
973+/*!
974+ * \brief Clipboard::cut
975+ * \param pathnames
976+ */
977+void Clipboard::cut(const QStringList &names, const QString &path)
978+{
979+ storeOnClipboard(names, ClipboardCut, path);
980+}
981+
982+
983+//=======================================================
984+/*!
985+ * \brief Clipboard::clipboardLocalUrlsCounter
986+ * \return
987+ */
988+int Clipboard::clipboardLocalUrlsCounter()
989+{
990+ ClipboardOperation operation;
991+ return m_mimeData->localUrls(operation).count();
992+}
993+
994+
995+//=======================================================
996+/*!
997+ * \brief Clipboard::paste
998+ * \param operation
999+ * \return
1000+ */
1001+QStringList Clipboard::paste(ClipboardOperation &operation)
1002+{
1003+ QStringList items = m_mimeData->localUrls(operation);
1004+ if (operation == ClipboardCut)
1005+ {
1006+ //this must still be false when cut finishes to change the clipboard to the target
1007+ m_clipboardModifiedByOther = false;
1008+ }
1009+ return items;
1010+}
1011
1012=== added file 'src/plugin/folderlistmodel/clipboard.h'
1013--- src/plugin/folderlistmodel/clipboard.h 1970-01-01 00:00:00 +0000
1014+++ src/plugin/folderlistmodel/clipboard.h 2014-04-15 08:19:22 +0000
1015@@ -0,0 +1,132 @@
1016+/**************************************************************************
1017+ *
1018+ * Copyright 2014 Canonical Ltd.
1019+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
1020+ *
1021+ * You may use this file under the terms of the BSD license as follows:
1022+ *
1023+ * "Redistribution and use in source and binary forms, with or without
1024+ * modification, are permitted provided that the following conditions are
1025+ * met:
1026+ * * Redistributions of source code must retain the above copyright
1027+ * notice, this list of conditions and the following disclaimer.
1028+ * * Redistributions in binary form must reproduce the above copyright
1029+ * notice, this list of conditions and the following disclaimer in
1030+ * the documentation and/or other materials provided with the
1031+ * distribution.
1032+ * * Neither the name of Nemo Mobile nor the names of its contributors
1033+ * may be used to endorse or promote products derived from this
1034+ * software without specific prior written permission.
1035+ *
1036+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1037+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1038+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1039+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1040+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1041+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1042+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1043+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1044+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1045+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1046+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1047+ *
1048+ * File: clipboard.h
1049+ * Date: 1/22/2014
1050+ */
1051+
1052+#ifndef CLIPBOARD_H
1053+#define CLIPBOARD_H
1054+
1055+#include <QMimeData>
1056+#include <QUrl>
1057+#include <QStringList>
1058+
1059+class DirModelMimeData;
1060+
1061+enum ClipboardOperation
1062+{
1063+ NoClipboard, ClipboardCopy, ClipboardCut
1064+};
1065+
1066+
1067+
1068+/*!
1069+ * \brief The Clipboard class handles global clipboard storage
1070+ */
1071+class Clipboard : public QObject
1072+{
1073+ Q_OBJECT
1074+public:
1075+ explicit Clipboard(QObject *parent = 0);
1076+ ~Clipboard();
1077+ QStringList paste(ClipboardOperation& operation);
1078+ int clipboardLocalUrlsCounter();
1079+ inline bool hasClipboardModifiedByOtherApplication() const {return m_clipboardModifiedByOther;}
1080+
1081+public slots:
1082+ void cut(const QStringList& names, const QString &path);
1083+ void copy(const QStringList& names, const QString &path);
1084+
1085+
1086+signals:
1087+ void clipboardChanged();
1088+
1089+private slots:
1090+ void onClipboardChanged ();
1091+
1092+private:
1093+ void storeOnClipboard(const QStringList &names,
1094+ ClipboardOperation op,
1095+ const QString &curPath);
1096+private:
1097+ DirModelMimeData * m_mimeData;
1098+ bool m_clipboardModifiedByOther;
1099+};
1100+
1101+
1102+
1103+/*!
1104+ * \brief The DirModelMimeData class is the storage on Clipboard
1105+ */
1106+class DirModelMimeData : public QMimeData
1107+{
1108+public:
1109+ explicit DirModelMimeData();
1110+ ~DirModelMimeData();
1111+ virtual QStringList formats() const { return m_formats; }
1112+ virtual bool hasFormat ( const QString & mimeType ) const;
1113+
1114+public:
1115+ enum ClipBoardDataOwner
1116+ {
1117+ Nobody, // might have failed
1118+ Application,
1119+ MySelf
1120+ };
1121+
1122+ ClipBoardDataOwner setIntoClipboard(const QStringList& files,
1123+ const QString &path,
1124+ ClipboardOperation operation);
1125+ const QMimeData * clipboardMimeData();
1126+ QStringList localUrls(ClipboardOperation& operation);
1127+
1128+private:
1129+ static QList<QUrl> gnomeUrls(const QMimeData *mime, ClipboardOperation& operation);
1130+ ClipboardOperation clipBoardOperation();
1131+ bool fillClipboard(const QStringList& files, const QString &path, ClipboardOperation operation);
1132+ QStringList makeFullPath(const QStringList& files, const QString &path);
1133+ bool testClipboardContent(const QStringList& files, const QString &path);
1134+
1135+private:
1136+ QStringList m_formats;
1137+ const QMimeData * m_appMime;
1138+ QByteArray m_gnomeData;
1139+ QList<QUrl> m_urls;
1140+ static DirModelMimeData* m_globalMimeData; //!< some mobile devices do not use X, they may not have clipboard
1141+ static int m_instances;
1142+};
1143+
1144+
1145+
1146+#endif //CLIPBOARD_H
1147+
1148
1149=== added file 'src/plugin/folderlistmodel/diritemabstractlistmodel.h'
1150--- src/plugin/folderlistmodel/diritemabstractlistmodel.h 1970-01-01 00:00:00 +0000
1151+++ src/plugin/folderlistmodel/diritemabstractlistmodel.h 2014-04-15 08:19:22 +0000
1152@@ -0,0 +1,46 @@
1153+/**************************************************************************
1154+ *
1155+ * Copyright 2014 Canonical Ltd.
1156+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
1157+ *
1158+ * This program is free software; you can redistribute it and/or modify
1159+ * it under the terms of the GNU Lesser General Public License as published by
1160+ * the Free Software Foundation; version 3.
1161+ *
1162+ * This program is distributed in the hope that it will be useful,
1163+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1164+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1165+ * GNU Lesser General Public License for more details.
1166+ *
1167+ * You should have received a copy of the GNU Lesser General Public License
1168+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1169+ *
1170+ * File: diritemabstractlistmodel.h
1171+ * Date: 30/01/2014
1172+ */
1173+
1174+#ifndef DIRITEMABSTRACTLISTMODEL_H
1175+#define DIRITEMABSTRACTLISTMODEL_H
1176+
1177+#include <QObject>
1178+
1179+#include <QAbstractListModel>
1180+
1181+
1182+class DirItemInfo;
1183+class DirItemModel;
1184+
1185+class DirItemAbstractListModel : public QAbstractListModel
1186+{
1187+ Q_OBJECT
1188+public:
1189+ virtual int getIndex(const QString& name) = 0;
1190+ virtual void notifyItemChanged(int index) = 0;
1191+protected:
1192+ explicit DirItemAbstractListModel(QObject *parent = 0) :
1193+ QAbstractListModel(parent)
1194+ {
1195+ }
1196+};
1197+
1198+#endif // DIRITEMABSTRACTLISTMODEL_H
1199
1200=== added file 'src/plugin/folderlistmodel/diriteminfo.cpp'
1201--- src/plugin/folderlistmodel/diriteminfo.cpp 1970-01-01 00:00:00 +0000
1202+++ src/plugin/folderlistmodel/diriteminfo.cpp 2014-04-15 08:19:22 +0000
1203@@ -0,0 +1,345 @@
1204+/**************************************************************************
1205+ *
1206+ * Copyright 2014 Canonical Ltd()->
1207+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
1208+ *
1209+ * This program is free software; you can redistribute it and/or modify
1210+ * it under the terms of the GNU Lesser General Public License as published by
1211+ * the Free Software Foundation; version 3.
1212+ *
1213+ * This program is distributed in the hope that it will be useful,
1214+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1215+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1216+ * GNU Lesser General Public License for more details.
1217+ *
1218+ * You should have received a copy of the GNU Lesser General Public License
1219+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1220+ *
1221+ * File: diriteminfo.cpp
1222+ * Date: 30/01/2014
1223+ */
1224+
1225+#include "diriteminfo.h"
1226+
1227+
1228+
1229+
1230+#include <QUrl>
1231+#include <QDir>
1232+
1233+
1234+class DirItemInfoPrivate : public QSharedData
1235+{
1236+public:
1237+ DirItemInfoPrivate();
1238+ DirItemInfoPrivate(const DirItemInfoPrivate& other);
1239+ DirItemInfoPrivate(const QFileInfo& fi);
1240+ void setFileInfo(const QFileInfo&);
1241+
1242+public:
1243+ bool _isValid :1;
1244+ bool _isLocal :1;
1245+ bool _isRemote :1;
1246+ bool _isSelected :1;
1247+ bool _isAbsolute :1;
1248+ bool _exists :1;
1249+ bool _isFile :1;
1250+ bool _isDir :1;
1251+ bool _isSymLink :1;
1252+ bool _isRoot :1;
1253+ bool _isReadable :1;
1254+ bool _isWritable :1;
1255+ bool _isExecutable:1;
1256+ QFile::Permissions _permissions;
1257+ qint64 _size;
1258+ QDateTime _created;
1259+ QDateTime _lastModified;
1260+ QDateTime _lastRead;
1261+ QString _path;
1262+ QString _fileName;
1263+ static QMimeDatabase mimeDatabase;
1264+};
1265+
1266+
1267+QMimeDatabase DirItemInfoPrivate::mimeDatabase;
1268+
1269+
1270+DirItemInfoPrivate::DirItemInfoPrivate() :
1271+ _isValid(false)
1272+ ,_isLocal(false)
1273+ ,_isRemote(false)
1274+ ,_isSelected(false)
1275+ ,_isAbsolute(false)
1276+ ,_exists(false)
1277+ ,_isDir(false)
1278+ ,_isSymLink(false)
1279+ ,_isRoot(false)
1280+ ,_isReadable(false)
1281+ ,_isWritable(false)
1282+ ,_isExecutable(false)
1283+ ,_permissions(0)
1284+ ,_size(0)
1285+{
1286+
1287+}
1288+
1289+
1290+DirItemInfoPrivate::DirItemInfoPrivate(const DirItemInfoPrivate &other):
1291+ QSharedData(other)
1292+ ,_isValid(other._isValid)
1293+ ,_isLocal(other._isLocal)
1294+ ,_isRemote(other._isRemote)
1295+ ,_isSelected(other._isSelected)
1296+ ,_isAbsolute(other._isAbsolute)
1297+ ,_exists(other._exists)
1298+ ,_isDir(other._isDir)
1299+ ,_isSymLink(other._isSymLink)
1300+ ,_isRoot(other._isRoot)
1301+ ,_isReadable(other._isReadable)
1302+ ,_isWritable(other._isWritable)
1303+ ,_isExecutable(other._isExecutable)
1304+ ,_permissions(other._permissions)
1305+ ,_size(other._size)
1306+ ,_created(other._created)
1307+ ,_lastModified(other._lastModified)
1308+ ,_lastRead(other._lastRead)
1309+ ,_path(other._path)
1310+ ,_fileName(other._fileName)
1311+{
1312+
1313+}
1314+
1315+
1316+DirItemInfoPrivate::DirItemInfoPrivate(const QFileInfo &fi):
1317+ _isValid(true)
1318+ ,_isLocal(true)
1319+ ,_isRemote(false)
1320+ ,_isSelected(false)
1321+{
1322+ setFileInfo(fi);
1323+}
1324+
1325+void DirItemInfoPrivate::setFileInfo(const QFileInfo &fi)
1326+{
1327+ if (fi.exists() && fi.isRelative())
1328+ {
1329+ QFileInfo abs(fi.absoluteFilePath());
1330+ setFileInfo(abs);
1331+ }
1332+ else
1333+ {
1334+ _path = fi.absolutePath();
1335+ _fileName = fi.fileName();
1336+ _isAbsolute = fi.isAbsolute();
1337+ _exists = fi.exists();
1338+ _isDir = fi.isDir();
1339+ _isFile = fi.isFile();
1340+ _isSymLink = fi.isSymLink();
1341+ _isRoot = fi.isRoot();
1342+ _isReadable = fi.isReadable();
1343+ _isWritable = fi.isWritable();
1344+ _isExecutable = fi.isExecutable();
1345+ _permissions = fi.permissions();
1346+ _size = fi.size();
1347+ _created = fi.created();
1348+ _lastRead = fi.lastRead();
1349+ _lastModified = fi.lastModified();
1350+ }
1351+}
1352+
1353+//================================================================
1354+
1355+DirItemInfo::DirItemInfo(): d_ptr(new DirItemInfoPrivate())
1356+{
1357+}
1358+
1359+
1360+DirItemInfo::~DirItemInfo()
1361+{
1362+}
1363+
1364+
1365+
1366+DirItemInfo::DirItemInfo(const QFileInfo &fi):
1367+ d_ptr(new DirItemInfoPrivate(fi))
1368+{
1369+
1370+}
1371+
1372+
1373+
1374+DirItemInfo::DirItemInfo(const QString& urlOrPath):
1375+ d_ptr( new DirItemInfoPrivate(QFileInfo(urlOrPath)) )
1376+{
1377+
1378+}
1379+
1380+
1381+DirItemInfo::DirItemInfo(const DirItemInfo& other)
1382+{
1383+ d_ptr = other.d_ptr;
1384+}
1385+
1386+
1387+bool DirItemInfo::isSelected() const
1388+{
1389+ return d_ptr->_isSelected;
1390+}
1391+
1392+/*!
1393+ * \brief DirItemInfo::setSelection()
1394+ * \param selected true/false new selection state
1395+ * \return true if a new state was set, false if the selection is already equal to \a selected
1396+ */
1397+bool DirItemInfo::setSelection(bool selected)
1398+{
1399+ bool ret = selected != isSelected();
1400+ d_ptr->_isSelected = selected;
1401+ return ret;
1402+}
1403+
1404+
1405+bool DirItemInfo::isValid() const
1406+{
1407+ return d_ptr->_isValid;
1408+}
1409+
1410+bool DirItemInfo::isLocal() const
1411+{
1412+ return d_ptr->_isLocal;
1413+}
1414+
1415+bool DirItemInfo::isRemote() const
1416+{
1417+ return d_ptr->_isRemote;
1418+}
1419+
1420+bool DirItemInfo::exists() const
1421+{
1422+ return d_ptr->_exists;
1423+}
1424+
1425+QString DirItemInfo::filePath() const
1426+{
1427+ QString filepath;
1428+ if (!d_ptr->_path.isEmpty())
1429+ {
1430+ filepath = d_ptr->_path;
1431+ if (d_ptr->_path != QDir::rootPath())
1432+ {
1433+ filepath += QDir::separator();
1434+ }
1435+ }
1436+ filepath += d_ptr->_fileName;
1437+ return filepath;
1438+}
1439+
1440+QString DirItemInfo::fileName() const
1441+{
1442+ return d_ptr->_fileName;
1443+}
1444+
1445+QString DirItemInfo::absoluteFilePath() const
1446+{
1447+ return filePath();
1448+}
1449+
1450+QString DirItemInfo::absolutePath() const
1451+{
1452+ return d_ptr->_path;
1453+}
1454+
1455+bool DirItemInfo::isReadable() const
1456+{
1457+ return d_ptr->_isReadable;
1458+}
1459+
1460+bool DirItemInfo::isWritable() const
1461+{
1462+ return d_ptr->_isWritable;
1463+}
1464+
1465+bool DirItemInfo::isExecutable() const
1466+{
1467+ return d_ptr->_isExecutable;
1468+}
1469+
1470+bool DirItemInfo::isRelative() const
1471+{
1472+ return ! isAbsolute();
1473+}
1474+
1475+bool DirItemInfo::isAbsolute() const
1476+{
1477+ return d_ptr->_isAbsolute;
1478+}
1479+
1480+bool DirItemInfo::isFile() const
1481+{
1482+ return d_ptr->_isFile;
1483+}
1484+
1485+bool DirItemInfo::isDir() const
1486+{
1487+ return d_ptr->_isDir;
1488+}
1489+
1490+bool DirItemInfo::isSymLink() const
1491+{
1492+ return d_ptr->_isSymLink;
1493+}
1494+
1495+bool DirItemInfo::isRoot() const
1496+{
1497+ return d_ptr->_isRoot;
1498+}
1499+
1500+QFile::Permissions DirItemInfo::permissions() const
1501+{
1502+ return d_ptr->_permissions;
1503+}
1504+
1505+qint64 DirItemInfo::size() const
1506+{
1507+ return d_ptr->_size;
1508+}
1509+
1510+QDateTime DirItemInfo::created() const
1511+{
1512+ return d_ptr->_created;
1513+}
1514+
1515+QDateTime DirItemInfo::lastModified() const
1516+{
1517+ return d_ptr->_lastModified;
1518+}
1519+
1520+QDateTime DirItemInfo::lastRead() const
1521+{
1522+ return d_ptr->_lastRead;
1523+}
1524+
1525+void DirItemInfo::setFile(const QString &dir, const QString &file)
1526+{
1527+ QFileInfo f;
1528+ f.setFile(dir,file);
1529+ d_ptr->setFileInfo(f);
1530+}
1531+
1532+QFileInfo DirItemInfo::diskFileInfo() const
1533+{
1534+ QFileInfo fi(absoluteFilePath());
1535+ return fi;
1536+}
1537+
1538+QString DirItemInfo::path() const
1539+{
1540+ return d_ptr->_path;
1541+}
1542+
1543+
1544+QMimeType DirItemInfo::mimeType() const
1545+{
1546+ return d_ptr->mimeDatabase.mimeTypeForFile(diskFileInfo());
1547+}
1548+
1549
1550=== added file 'src/plugin/folderlistmodel/diriteminfo.h'
1551--- src/plugin/folderlistmodel/diriteminfo.h 1970-01-01 00:00:00 +0000
1552+++ src/plugin/folderlistmodel/diriteminfo.h 2014-04-15 08:19:22 +0000
1553@@ -0,0 +1,123 @@
1554+/**************************************************************************
1555+ *
1556+ * Copyright 2014 Canonical Ltd.
1557+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
1558+ *
1559+ * This program is free software; you can redistribute it and/or modify
1560+ * it under the terms of the GNU Lesser General Public License as published by
1561+ * the Free Software Foundation; version 3.
1562+ *
1563+ * This program is distributed in the hope that it will be useful,
1564+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1565+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1566+ * GNU Lesser General Public License for more details.
1567+ *
1568+ * You should have received a copy of the GNU Lesser General Public License
1569+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1570+ *
1571+ * File: diriteminfo.h
1572+ * Date: 30/01/2014
1573+ */
1574+
1575+#ifndef DIRITEMINFO_H
1576+#define DIRITEMINFO_H
1577+
1578+#include <QtGlobal>
1579+#include <QVector>
1580+#include <QFileInfo>
1581+#include <QSharedData>
1582+#include <QDateTime>
1583+
1584+#include <QMimeType>
1585+#include <QMimeDatabase>
1586+
1587+class DirItemInfoPrivate;
1588+
1589+
1590+/*!
1591+ * \brief The DirItemInfo class
1592+ *
1593+ * It intends to provide the same information as QFileInfo for Local Files:
1594+ * * selection state
1595+ * * any information about the item type, if it is Local/Remove
1596+ * *
1597+ */
1598+class DirItemInfo
1599+{
1600+public:
1601+ DirItemInfo();
1602+ DirItemInfo(const QString& urlOrPath);
1603+ DirItemInfo(const QFileInfo&);
1604+ DirItemInfo(const DirItemInfo& other);
1605+
1606+ virtual ~DirItemInfo();
1607+
1608+public:
1609+ bool isSelected() const;
1610+ bool setSelection(bool selected);
1611+ virtual bool isValid() const;
1612+
1613+ /*!
1614+ * \brief isLocal()
1615+ * \return true if the file is the disk: valid for Trash and any mounted FS
1616+ */
1617+ virtual bool isLocal() const;
1618+
1619+ /*!
1620+ * \brief isRemote()
1621+ * \return true if the file is in any remote host, mounted File Sharing is considered as Local
1622+ */
1623+ virtual bool isRemote() const;
1624+
1625+ QFileInfo diskFileInfo() const;
1626+
1627+ inline void swap(DirItemInfo &other)
1628+ { qSwap(d_ptr, other.d_ptr); }
1629+
1630+ inline DirItemInfo& operator=(const DirItemInfo &other)
1631+ { swap(*(const_cast<DirItemInfo*>(&other))); return *this; }
1632+
1633+ virtual bool exists() const;
1634+ virtual QString filePath() const;
1635+ virtual QString fileName() const;
1636+ virtual QString path() const;
1637+ virtual QString absolutePath() const;
1638+ virtual QString absoluteFilePath() const;
1639+ virtual bool isReadable() const;
1640+ virtual bool isWritable() const;
1641+ virtual bool isExecutable() const;
1642+ virtual bool isRelative() const;
1643+ virtual bool isAbsolute() const;
1644+ virtual bool isFile() const;
1645+ virtual bool isDir() const;
1646+ virtual bool isSymLink() const;
1647+ virtual bool isRoot() const;
1648+ virtual QFile::Permissions permissions() const;
1649+ virtual qint64 size() const;
1650+ virtual QDateTime created() const;
1651+ virtual QDateTime lastModified() const;
1652+ virtual QDateTime lastRead() const;
1653+ virtual QMimeType mimeType() const;
1654+
1655+ virtual void setFile(const QString &dir, const QString & file);
1656+
1657+#if 0
1658+ virtual QString path() const;
1659+ virtual QString owner() const;
1660+ virtual uint ownerId() const;
1661+ virtual QString group() const;
1662+ virtual uint groupId() const;
1663+ virtual bool permission(QFile::Permissions permissions) const;
1664+#endif
1665+
1666+
1667+protected:
1668+ QSharedDataPointer<DirItemInfoPrivate> d_ptr;
1669+};
1670+
1671+typedef QVector<DirItemInfo> DirItemInfoList;
1672+
1673+Q_DECLARE_SHARED(DirItemInfo)
1674+Q_DECLARE_METATYPE(DirItemInfo)
1675+
1676+#endif // DIRITEMINFO_H
1677
1678=== added file 'src/plugin/folderlistmodel/dirmodel.cpp'
1679--- src/plugin/folderlistmodel/dirmodel.cpp 1970-01-01 00:00:00 +0000
1680+++ src/plugin/folderlistmodel/dirmodel.cpp 2014-04-15 08:19:22 +0000
1681@@ -0,0 +1,1563 @@
1682+/*
1683+ * Copyright (C) 2012 Robin Burchell <robin+nemo@viroteck.net>
1684+ *
1685+ * You may use this file under the terms of the BSD license as follows:
1686+ *
1687+ * "Redistribution and use in source and binary forms, with or without
1688+ * modification, are permitted provided that the following conditions are
1689+ * met:
1690+ * * Redistributions of source code must retain the above copyright
1691+ * notice, this list of conditions and the following disclaimer.
1692+ * * Redistributions in binary form must reproduce the above copyright
1693+ * notice, this list of conditions and the following disclaimer in
1694+ * the documentation and/or other materials provided with the
1695+ * distribution.
1696+ * * Neither the name of Nemo Mobile nor the names of its contributors
1697+ * may be used to endorse or promote products derived from this
1698+ * software without specific prior written permission.
1699+ *
1700+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1701+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1702+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
1703+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1704+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1705+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1706+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1707+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
1708+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
1709+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
1710+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
1711+ */
1712+
1713+#include "dirselection.h"
1714+#include "dirmodel.h"
1715+#include "iorequest.h"
1716+#include "ioworkerthread.h"
1717+#include "filesystemaction.h"
1718+#include "externalfswatcher.h"
1719+#include "clipboard.h"
1720+#include "fmutil.h"
1721+
1722+
1723+#ifndef DO_NOT_USE_TAG_LIB
1724+#include <taglib/attachedpictureframe.h>
1725+#include <taglib/id3v2tag.h>
1726+#include <taglib/fileref.h>
1727+#include <taglib/mpegfile.h>
1728+#include <taglib/tag.h>
1729+#include <taglib/audioproperties.h>
1730+#endif
1731+
1732+#include <errno.h>
1733+#include <string.h>
1734+#include <QDirIterator>
1735+#include <QDir>
1736+#include <QDebug>
1737+#include <QFileIconProvider>
1738+#include <QUrl>
1739+#include <QDesktopServices>
1740+#include <QMetaType>
1741+#include <QDateTime>
1742+#include <QMimeType>
1743+
1744+#if defined(REGRESSION_TEST_FOLDERLISTMODEL)
1745+# include <QColor>
1746+# include <QBrush>
1747+#endif
1748+
1749+
1750+
1751+
1752+
1753+#define IS_VALID_ROW(row) (row >=0 && row < mDirectoryContents.count())
1754+#define WARN_ROW_OUT_OF_RANGE(row) qWarning() << Q_FUNC_INFO << this << "row:" << row << "Out of bounds access"
1755+
1756+#define IS_FILE_MANAGER_IDLE() (!mAwaitingResults)
1757+
1758+
1759+Q_GLOBAL_STATIC(IOWorkerThread, ioWorkerThread)
1760+
1761+namespace {
1762+ QHash<QByteArray, int> roleMapping;
1763+}
1764+
1765+
1766+
1767+/*!
1768+ * Sort was originally done in \ref onItemsAdded() and that code is now in \ref addItem(),
1769+ * the reason to keep doing sort and do not let QDir does it is that when adding new items
1770+ * by \ref mkdir() or \paste() it is not necessary to call refresh() to load the entire directory
1771+ * to organize it items again. New items order/position are organized by \ref addItem()
1772+ *
1773+ */
1774+static CompareFunction availableCompareFunctions[2][2] =
1775+{
1776+ {fileCompareAscending, fileCompareDescending}
1777+ ,{dateCompareAscending, dateCompareDescending}
1778+};
1779+
1780+
1781+DirModel::DirModel(QObject *parent)
1782+ : DirItemAbstractListModel(parent)
1783+ , mFilterDirectories(false)
1784+ , mShowDirectories(true)
1785+ , mAwaitingResults(false)
1786+ , mIsRecursive(false)
1787+ , mReadsMediaMetadata(false)
1788+ , mShowHiddenFiles(false)
1789+ , mSortBy(SortByName)
1790+ , mSortOrder(SortAscending)
1791+ , mCompareFunction(0)
1792+ , mExtFSWatcher(0)
1793+ , mClipboard(new Clipboard(this))
1794+ , m_fsAction(new FileSystemAction(this) )
1795+{
1796+ mNameFilters = QStringList() << "*";
1797+
1798+ mSelection = new DirSelection(this, &mDirectoryContents);
1799+
1800+ connect(m_fsAction, SIGNAL(progress(int,int,int)),
1801+ this, SIGNAL(progress(int,int,int)));
1802+
1803+ connect(m_fsAction, SIGNAL(added(DirItemInfo)),
1804+ this, SLOT(onItemAdded(DirItemInfo)));
1805+
1806+ connect(m_fsAction, SIGNAL(added(QString)),
1807+ this, SLOT(onItemAdded(QString)));
1808+
1809+ connect(m_fsAction, SIGNAL(removed(DirItemInfo)),
1810+ this, SLOT(onItemRemoved(DirItemInfo)));
1811+
1812+ connect(m_fsAction, SIGNAL(removed(QString)),
1813+ this, SLOT(onItemRemoved(QString)));
1814+
1815+ connect(m_fsAction, SIGNAL(error(QString,QString)),
1816+ this, SIGNAL(error(QString,QString)));
1817+
1818+ connect(this, SIGNAL(pathChanged(QString)),
1819+ m_fsAction, SLOT(pathChanged(QString)));
1820+
1821+ connect(mClipboard, SIGNAL(clipboardChanged()),
1822+ this, SIGNAL(clipboardChanged()));
1823+
1824+ connect(m_fsAction, SIGNAL(changed(DirItemInfo)),
1825+ this, SLOT(onItemChanged(DirItemInfo)));
1826+
1827+ connect(mClipboard, SIGNAL(clipboardChanged()),
1828+ m_fsAction, SLOT(onClipboardChanged()));
1829+
1830+ connect(m_fsAction, SIGNAL(recopy(QStringList,QString)),
1831+ mClipboard, SLOT(copy(QStringList,QString)));
1832+
1833+ setCompareAndReorder();
1834+
1835+ if (QIcon::themeName().isEmpty() && !FMUtil::hasTriedThemeName())
1836+ {
1837+ FMUtil::setThemeName();
1838+ }
1839+}
1840+
1841+
1842+
1843+DirModel::~DirModel()
1844+{
1845+ stoptExternalFsWatcher();
1846+}
1847+
1848+
1849+
1850+QHash<int, QByteArray> DirModel::roleNames() const
1851+{
1852+ static QHash<int, QByteArray> roles;
1853+ if (roles.isEmpty()) {
1854+ roles = buildRoleNames();
1855+ }
1856+
1857+ return roles;
1858+}
1859+
1860+
1861+
1862+
1863+QHash<int, QByteArray> DirModel::buildRoleNames() const
1864+{
1865+ QHash<int, QByteArray> roles;
1866+ roles.insert(FileNameRole, QByteArray("fileName"));
1867+ roles.insert(AccessedDateRole, QByteArray("accessedDate"));
1868+ roles.insert(CreationDateRole, QByteArray("creationDate"));
1869+ roles.insert(ModifiedDateRole, QByteArray("modifiedDate"));
1870+ roles.insert(FileSizeRole, QByteArray("fileSize"));
1871+ roles.insert(IconSourceRole, QByteArray("iconSource"));
1872+ roles.insert(FilePathRole, QByteArray("filePath"));
1873+ roles.insert(IsDirRole, QByteArray("isDir"));
1874+ roles.insert(IsFileRole, QByteArray("isFile"));
1875+ roles.insert(IsReadableRole, QByteArray("isReadable"));
1876+ roles.insert(IsWritableRole, QByteArray("isWritable"));
1877+ roles.insert(IsExecutableRole, QByteArray("isExecutable"));
1878+ roles.insert(IsSelectedRole, QByteArray("isSelected"));
1879+ roles.insert(TrackTitleRole, QByteArray("trackTitle"));
1880+ roles.insert(TrackArtistRole, QByteArray("trackArtist"));
1881+ roles.insert(TrackAlbumRole, QByteArray("trackAlbum"));
1882+ roles.insert(TrackYearRole, QByteArray("trackYear"));
1883+ roles.insert(TrackNumberRole, QByteArray("trackNumber"));
1884+ roles.insert(TrackGenreRole, QByteArray("trackGenre"));
1885+ roles.insert(TrackLengthRole, QByteArray("trackLength"));
1886+ roles.insert(TrackCoverRole, QByteArray("trackCover"));
1887+
1888+ // populate reverse mapping
1889+ if (roleMapping.isEmpty()) {
1890+ QHash<int, QByteArray>::ConstIterator it = roles.constBegin();
1891+ for (;it != roles.constEnd(); ++it)
1892+ roleMapping.insert(it.value(), it.key());
1893+
1894+ // make sure we cover all roles
1895+ // Q_ASSERT(roles.count() == IsFileRole - FileNameRole);
1896+ }
1897+
1898+ return roles;
1899+}
1900+
1901+QVariant DirModel::data(int row, const QByteArray &stringRole) const
1902+{
1903+ QHash<QByteArray, int>::ConstIterator it = roleMapping.constFind(stringRole);
1904+
1905+ if (it == roleMapping.constEnd())
1906+ return QVariant();
1907+
1908+ return data(index(row, 0), *it);
1909+}
1910+
1911+QVariant DirModel::data(const QModelIndex &index, int role) const
1912+{
1913+//its not for QML
1914+#if defined(REGRESSION_TEST_FOLDERLISTMODEL)
1915+ if (!index.isValid() ||
1916+ (role != Qt::DisplayRole && role != Qt::DecorationRole && role != Qt::BackgroundRole)
1917+ )
1918+ {
1919+ return QVariant();
1920+ }
1921+ if (role == Qt::DecorationRole && index.column() == 0)
1922+ {
1923+ QIcon icon;
1924+ QMimeType mime = mDirectoryContents.at(index.row()).mimeType();
1925+ if (mime.isValid())
1926+ {
1927+ if (QIcon::hasThemeIcon(mime.iconName()) ) {
1928+ icon = QIcon::fromTheme(mime.iconName());
1929+ }
1930+ else if (QIcon::hasThemeIcon(mime.genericIconName())) {
1931+ icon = QIcon::fromTheme(mime.genericIconName());
1932+ }
1933+ }
1934+ if (icon.isNull())
1935+ {
1936+ if (mDirectoryContents.at(index.row()).isLocal())
1937+ {
1938+ icon = QFileIconProvider().icon(mDirectoryContents.at(index.row()).diskFileInfo());
1939+ }
1940+ else
1941+ if (mDirectoryContents.at(index.row()).isDir())
1942+ {
1943+ icon = QFileIconProvider().icon(QFileIconProvider::Folder);
1944+ }
1945+ else
1946+ {
1947+ icon = QFileIconProvider().icon(QFileIconProvider::File);
1948+ }
1949+ }
1950+ return icon;
1951+ }
1952+ if (role == Qt::BackgroundRole && index.column() == 0)
1953+ {
1954+ if (mDirectoryContents.at(index.row()).isSelected())
1955+ {
1956+ //TODO it'd better to get some style or other default
1957+ // background color
1958+ return QBrush(Qt::lightGray);
1959+ }
1960+ return QVariant();
1961+ }
1962+ role = FileNameRole + index.column();
1963+#else
1964+ if (role < FileNameRole || role > TrackCoverRole) {
1965+ qWarning() << Q_FUNC_INFO << this << "Got an out of range role: " << role;
1966+ return QVariant();
1967+ }
1968+
1969+ if (index.row() < 0 || index.row() >= mDirectoryContents.count()) {
1970+ qWarning() << "Attempted to access out of range row: " << index.row();
1971+ return QVariant();
1972+ }
1973+
1974+ if (index.column() != 0)
1975+ return QVariant();
1976+#endif
1977+
1978+ const DirItemInfo &fi = mDirectoryContents.at(index.row());
1979+
1980+ switch (role) {
1981+ case FileNameRole:
1982+ return fi.fileName();
1983+ case AccessedDateRole:
1984+ return fi.lastRead();
1985+ case CreationDateRole:
1986+ return fi.created();
1987+ case ModifiedDateRole:
1988+ return fi.lastModified();
1989+ case FileSizeRole: {
1990+ if (fi.isDir() && fi.isLocal())
1991+ {
1992+ return dirItems(fi.diskFileInfo());
1993+ }
1994+ return fileSize(fi.size());
1995+ }
1996+ case IconSourceRole: {
1997+ const QString &fileName = fi.fileName();
1998+
1999+ if (fi.isDir())
2000+ return QLatin1String("image://theme/icon-m-common-directory");
2001+
2002+ if (fileName.endsWith(QLatin1String(".jpg"), Qt::CaseInsensitive) ||
2003+ fileName.endsWith(QLatin1String(".png"), Qt::CaseInsensitive)) {
2004+ return QLatin1String("image://nemoThumbnail/") + fi.filePath();
2005+ }
2006+
2007+ return "image://theme/icon-m-content-document";
2008+ }
2009+ case FilePathRole:
2010+ return fi.filePath();
2011+ case IsDirRole:
2012+ return fi.isDir();
2013+ case IsFileRole:
2014+ return !fi.isDir();
2015+ case IsReadableRole:
2016+ return fi.isReadable();
2017+ case IsWritableRole:
2018+ return fi.isWritable();
2019+ case IsExecutableRole:
2020+ return fi.isExecutable();
2021+ case IsSelectedRole:
2022+ return fi.isSelected();
2023+#ifndef DO_NOT_USE_TAG_LIB
2024+ case TrackTitleRole:
2025+ case TrackArtistRole:
2026+ case TrackAlbumRole:
2027+ case TrackYearRole:
2028+ case TrackNumberRole:
2029+ case TrackGenreRole:
2030+ case TrackLengthRole:
2031+ case TrackCoverRole:
2032+ if (mReadsMediaMetadata && fi.isLocal())
2033+ {
2034+ return getAudioMetaData(fi.diskFileInfo(), role);
2035+ }
2036+ break;
2037+#endif
2038+ default:
2039+#if !defined(REGRESSION_TEST_FOLDERLISTMODEL)
2040+ // this should not happen, ever
2041+ Q_ASSERT(false);
2042+ qWarning() << Q_FUNC_INFO << this << "Got an unknown role: " << role;
2043+#endif
2044+ break;
2045+ }
2046+
2047+ return QVariant();
2048+}
2049+
2050+void DirModel::setPath(const QString &pathName)
2051+{
2052+ if (pathName.isEmpty())
2053+ return;
2054+
2055+ if (!canReadDir(pathName))
2056+ {
2057+ emit error(tr("cannot read path"), pathName);
2058+ return;
2059+ }
2060+
2061+ if (mAwaitingResults) {
2062+ // TODO: handle the case where pathName != our current path, cancel old
2063+ // request, start a new one
2064+ qDebug() << Q_FUNC_INFO << this << "Ignoring path change request, request already running";
2065+ return;
2066+ }
2067+
2068+ mAwaitingResults = true;
2069+ emit awaitingResultsChanged();
2070+#if DEBUG_MESSAGES
2071+ qDebug() << Q_FUNC_INFO << this << "Changing to " << pathName << " on " << QThread::currentThreadId();
2072+#endif
2073+
2074+ clear();
2075+
2076+ DirListWorker *dlw = createWorkerRequest(IORequest::DirList, pathName);
2077+ connect(dlw, SIGNAL(itemsAdded(DirItemInfoList)), SLOT(onItemsAdded(DirItemInfoList)));
2078+ connect(dlw, SIGNAL(workerFinished()), SLOT(onResultsFetched()));
2079+ ioWorkerThread()->addRequest(dlw);
2080+
2081+ mCurrentDir = pathName;
2082+ emit pathChanged(pathName);
2083+}
2084+
2085+
2086+void DirModel::onResultsFetched() {
2087+ if (mAwaitingResults) {
2088+#if DEBUG_MESSAGES
2089+ qDebug() << Q_FUNC_INFO << this << "No longer awaiting results";
2090+#endif
2091+ mAwaitingResults = false;
2092+ emit awaitingResultsChanged();
2093+ }
2094+}
2095+
2096+void DirModel::onItemsAdded(const DirItemInfoList &newFiles)
2097+{
2098+#if DEBUG_MESSAGES
2099+ qDebug() << Q_FUNC_INFO << this << "Got new files: " << newFiles.count();
2100+#endif
2101+
2102+ if (newFiles.count() > 0)
2103+ {
2104+ mDirectoryContents.reserve(newFiles.count()) ;
2105+ }
2106+ foreach (const DirItemInfo &fi, newFiles) {
2107+
2108+ bool doAdd = false;
2109+ foreach (const QString &nameFilter, mNameFilters) {
2110+ // TODO: using QRegExp for wildcard matching is slow
2111+ QRegExp re(nameFilter, Qt::CaseInsensitive, QRegExp::Wildcard);
2112+ if (re.exactMatch(fi.fileName()) || (fi.isDir() && !mFilterDirectories)) {
2113+ doAdd = true;
2114+ break;
2115+ }
2116+ }
2117+
2118+ if (!doAdd)
2119+ continue;
2120+
2121+ addItem(fi);
2122+ }
2123+}
2124+
2125+void DirModel::rm(const QStringList &paths)
2126+{
2127+ m_fsAction->remove(paths);
2128+}
2129+
2130+
2131+bool DirModel::rename(const QString& oldName, const QString &newName)
2132+{
2133+ return rename(getIndex(oldName), newName);
2134+}
2135+
2136+
2137+bool DirModel::rename(int row, const QString &newName)
2138+{
2139+#if DEBUG_MESSAGES
2140+ qDebug() << Q_FUNC_INFO << this << "Renaming " << row << " to " << newName;
2141+#endif
2142+
2143+ if (!IS_VALID_ROW(row)) {
2144+ WARN_ROW_OUT_OF_RANGE(row);
2145+ return false;
2146+ }
2147+
2148+ const DirItemInfo &fi = mDirectoryContents.at(row);
2149+ QString newFullFilename(fi.absolutePath() + QDir::separator() + newName);
2150+
2151+ //QFile::rename() works for File and Dir
2152+ QFile f(fi.absoluteFilePath());
2153+ bool retval = f.rename(newFullFilename);
2154+ if (!retval)
2155+ {
2156+ qDebug() << Q_FUNC_INFO << this << "Rename returned error code: " << f.error() << f.errorString();
2157+ emit(QObject::tr("Rename error"), f.errorString());
2158+ }
2159+ else
2160+ {
2161+ bool isSelected = mDirectoryContents.at(row).isSelected();
2162+ onItemRemoved(mDirectoryContents.at(row));
2163+ int newRow = addItem(DirItemInfo(QFileInfo(newFullFilename)));
2164+ //keep previous selected state, selection takes care of everything
2165+ mSelection->setIndex(newRow,isSelected);
2166+ }
2167+ return retval;
2168+}
2169+
2170+void DirModel::mkdir(const QString &newDir)
2171+{
2172+ QDir dir(mCurrentDir);
2173+ bool retval = dir.mkdir(newDir);
2174+ if (!retval) {
2175+ const char *errorStr = strerror(errno);
2176+ qDebug() << Q_FUNC_INFO << this << "Error creating new directory: " << errno << " (" << errorStr << ")";
2177+ emit error(QObject::tr("Error creating new folder"), errorStr);
2178+ } else {
2179+ onItemAdded(dir.filePath(newDir));
2180+ }
2181+}
2182+
2183+bool DirModel::showDirectories() const
2184+{
2185+ return mShowDirectories;
2186+}
2187+
2188+void DirModel::setShowDirectories(bool showDirectories)
2189+{
2190+ mShowDirectories = showDirectories;
2191+ refresh();
2192+ emit showDirectoriesChanged();
2193+}
2194+
2195+bool DirModel::isRecursive() const
2196+{
2197+ return mIsRecursive;
2198+}
2199+
2200+void DirModel::setIsRecursive(bool isRecursive)
2201+{
2202+ mIsRecursive = isRecursive;
2203+ refresh();
2204+ emit isRecursiveChanged();
2205+}
2206+
2207+bool DirModel::readsMediaMetadata() const
2208+{
2209+ return mReadsMediaMetadata;
2210+}
2211+
2212+void DirModel::setReadsMediaMetadata(bool readsMediaMetadata)
2213+{
2214+ mReadsMediaMetadata = readsMediaMetadata;
2215+ refresh();
2216+ emit readsMediaMetadataChanged();
2217+}
2218+
2219+bool DirModel::filterDirectories() const
2220+{
2221+ return mFilterDirectories;
2222+}
2223+
2224+void DirModel::setFilterDirectories(bool filterDirectories)
2225+{
2226+ mFilterDirectories = filterDirectories;
2227+ refresh();
2228+ emit filterDirectoriesChanged();
2229+}
2230+
2231+QStringList DirModel::nameFilters() const
2232+{
2233+ return mNameFilters;
2234+}
2235+
2236+void DirModel::setNameFilters(const QStringList &nameFilters)
2237+{
2238+ mNameFilters = nameFilters;
2239+ refresh();
2240+ emit nameFiltersChanged();
2241+}
2242+
2243+bool DirModel::awaitingResults() const
2244+{
2245+ return mAwaitingResults;
2246+}
2247+
2248+
2249+QString DirModel::parentPath() const
2250+{
2251+ QDir dir(mCurrentDir);
2252+ if (dir.isRoot()) {
2253+ qDebug() << Q_FUNC_INFO << this << "already at root";
2254+ return mCurrentDir;
2255+ }
2256+
2257+ bool success = dir.cdUp();
2258+ if (!success) {
2259+ qWarning() << Q_FUNC_INFO << this << "Failed to to go to parent of " << mCurrentDir;
2260+ return mCurrentDir;
2261+ }
2262+ qDebug() << Q_FUNC_INFO << this << "returning" << dir.absolutePath();
2263+ return dir.absolutePath();
2264+}
2265+
2266+QString DirModel::homePath() const
2267+{
2268+ return QDir::homePath();
2269+}
2270+
2271+#if defined(REGRESSION_TEST_FOLDERLISTMODEL)
2272+ QVariant DirModel::headerData(int section, Qt::Orientation orientation, int role) const
2273+ {
2274+ if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
2275+ {
2276+ QVariant ret;
2277+ QHash<int, QByteArray> roles = this->roleNames();
2278+ section += FileNameRole;
2279+ if (roles.contains(section))
2280+ {
2281+ QString header= QString(roles.value(section));
2282+ ret = header;
2283+ }
2284+ return ret;
2285+ }
2286+ return QAbstractItemModel::headerData(section, orientation, role);
2287+ }
2288+#endif
2289+
2290+
2291+void DirModel::goHome()
2292+{
2293+ setPath(QDir::homePath());
2294+}
2295+
2296+
2297+bool DirModel::cdUp()
2298+{
2299+ int ret = false;
2300+ if (!mCurrentDir.isEmpty()) // we are in any dir
2301+ {
2302+ QDir current(mCurrentDir);
2303+ if (current.cdUp())
2304+ {
2305+ setPath(current.absolutePath());
2306+ ret = true;
2307+ }
2308+ }
2309+ return ret;
2310+}
2311+
2312+
2313+void DirModel::removeIndex(int row)
2314+{
2315+ if (IS_VALID_ROW(row))
2316+ {
2317+ const DirItemInfo &fi = mDirectoryContents.at(row);
2318+ QStringList list(fi.absoluteFilePath());
2319+ this->rm(list);
2320+ }
2321+ else
2322+ {
2323+ WARN_ROW_OUT_OF_RANGE(row);
2324+ }
2325+}
2326+
2327+void DirModel::removePaths(const QStringList& items)
2328+{
2329+ this->rm(items);
2330+}
2331+
2332+void DirModel::copyIndex(int row)
2333+{
2334+ if (IS_VALID_ROW(row))
2335+ {
2336+ const DirItemInfo &fi = mDirectoryContents.at(row);
2337+ QStringList list(fi.absoluteFilePath());
2338+ this->copyPaths(list);
2339+ }
2340+ else
2341+ {
2342+ WARN_ROW_OUT_OF_RANGE(row);
2343+ }
2344+}
2345+
2346+void DirModel::copyPaths(const QStringList &items)
2347+{
2348+ mClipboard->copy(items, mCurrentDir);
2349+}
2350+
2351+
2352+void DirModel::cutIndex(int row)
2353+{
2354+ if (IS_VALID_ROW(row))
2355+ {
2356+ const DirItemInfo &fi = mDirectoryContents.at(row);
2357+ QStringList list(fi.absoluteFilePath());
2358+ this->cutPaths(list);
2359+ }
2360+ else
2361+ {
2362+ WARN_ROW_OUT_OF_RANGE(row);
2363+ }
2364+}
2365+
2366+
2367+void DirModel::cutPaths(const QStringList &items)
2368+{
2369+ mClipboard->cut(items, mCurrentDir);
2370+}
2371+
2372+
2373+void DirModel::paste()
2374+{
2375+ ClipboardOperation operation;
2376+ QStringList items = mClipboard->paste(operation);
2377+ if (operation == ClipboardCut)
2378+ {
2379+ m_fsAction->moveIntoCurrentPath(items);
2380+ }
2381+ else
2382+ {
2383+ m_fsAction->copyIntoCurrentPath(items);
2384+ }
2385+}
2386+
2387+
2388+bool DirModel::cdIntoIndex(int row)
2389+{
2390+ bool ret = false;
2391+ if (IS_VALID_ROW(row))
2392+ {
2393+ ret = cdInto(mDirectoryContents.at(row));
2394+ }
2395+ else
2396+ {
2397+ WARN_ROW_OUT_OF_RANGE(row);
2398+ }
2399+ return ret;
2400+}
2401+
2402+
2403+bool DirModel::cdIntoPath(const QString &filename)
2404+{
2405+ bool ret = false;
2406+ DirItemInfo fi(filename);
2407+ if (fi.isValid())
2408+ {
2409+ if (fi.isRelative())
2410+ {
2411+ fi.setFile(mCurrentDir, filename);
2412+ }
2413+ ret = cdInto(fi);
2414+ }
2415+ return ret;
2416+}
2417+
2418+
2419+bool DirModel::cdInto(const DirItemInfo &fi)
2420+{
2421+ bool ret = false;
2422+ if (canReadDir(fi.diskFileInfo()))
2423+ {
2424+ if (fi.isRelative())
2425+ {
2426+ QDir childDir(mCurrentDir);
2427+ if ( childDir.cd(fi.fileName()) )
2428+ {
2429+ setPath(childDir.absolutePath());
2430+ ret = true;
2431+ }
2432+ }
2433+ else
2434+ {
2435+ ret = true;
2436+ setPath(fi.absoluteFilePath());
2437+ }
2438+ }
2439+ return ret;
2440+}
2441+
2442+/*!
2443+ * \brief DirModel::onItemRemoved()
2444+ * \param pathname full pathname of removed file
2445+ */
2446+void DirModel::onItemRemoved(const QString &pathname)
2447+{
2448+ DirItemInfo info(pathname);
2449+ onItemRemoved(info);
2450+}
2451+
2452+
2453+void DirModel::onItemRemoved(const DirItemInfo &fi)
2454+{
2455+ int row = rowOfItem(fi);
2456+#if DEBUG_MESSAGES || DEBUG_EXT_FS_WATCHER
2457+ qDebug() << Q_FUNC_INFO << this
2458+ << "row" << row
2459+ << "name" << fi.absoluteFilePath()
2460+ << "removed[True|False]:" << (row >= 0);
2461+#endif
2462+ if (row >= 0)
2463+ {
2464+ beginRemoveRows(QModelIndex(), row, row);
2465+ if (mDirectoryContents.at(row).isSelected())
2466+ {
2467+ mSelection->itemGoingToBeRemoved(mDirectoryContents.at(row));
2468+ }
2469+ mDirectoryContents.remove(row,1);
2470+ endRemoveRows();
2471+ }
2472+}
2473+
2474+
2475+/*!
2476+ * \brief DirModel::onItemAdded()
2477+ * \param pathname full pathname of the added file
2478+ */
2479+void DirModel::onItemAdded(const QString &pathname)
2480+{
2481+ DirItemInfo info(pathname);
2482+ onItemAdded(info);
2483+}
2484+
2485+
2486+void DirModel::onItemAdded(const DirItemInfo &fi)
2487+{
2488+ int newRow = addItem(fi);
2489+ emit insertedRow(newRow);
2490+}
2491+
2492+/*!
2493+ * \brief DirModel::addItem() adds an item into the model
2494+ * This code was moved from onItemsAdded(const QVector<QFileInfo> &newFiles),
2495+ * the reason is: this code now is used for \ref mkdir() and for \ref paste() operations
2496+ * that inserts new items
2497+ * \param fi
2498+ * \return the index where it was inserted, it can be used in the view
2499+ * \sa insertedRow()
2500+ */
2501+int DirModel::addItem(const DirItemInfo &fi)
2502+{
2503+ DirItemInfoList::Iterator it = qLowerBound(mDirectoryContents.begin(),
2504+ mDirectoryContents.end(),
2505+ fi,
2506+ mCompareFunction);
2507+ int idx = mDirectoryContents.count();
2508+
2509+ if (it == mDirectoryContents.end()) {
2510+ beginInsertRows(QModelIndex(), idx, idx);
2511+ mDirectoryContents.append(fi);
2512+ endInsertRows();
2513+ } else {
2514+ idx = it - mDirectoryContents.begin();
2515+ beginInsertRows(QModelIndex(), idx, idx);
2516+ mDirectoryContents.insert(it, fi);
2517+ endInsertRows();
2518+ }
2519+ return idx;
2520+}
2521+
2522+/*!
2523+ * \brief DirModel::onItemChanged() Changes an item data
2524+ *
2525+ * \note If the item does not exist it is inserted
2526+ *
2527+ * \param fi DirItemInfo of the item
2528+ */
2529+void DirModel::onItemChanged(const DirItemInfo &fi)
2530+{
2531+ int row = rowOfItem(fi);
2532+ if (row >= 0)
2533+ {
2534+ if (mDirectoryContents.at(row).isSelected())
2535+ {
2536+ mSelection->itemGoingToBeReplaced(mDirectoryContents.at(row), fi);
2537+ DirItemInfo *myFi = const_cast<DirItemInfo*> (&fi);
2538+ myFi->setSelection(true);
2539+ }
2540+ mDirectoryContents[row] = fi;
2541+ notifyItemChanged(row);
2542+ }
2543+ else
2544+ { // it simplifies some logic outside, when removing and adding on the same operation
2545+ onItemAdded(fi);
2546+ }
2547+}
2548+
2549+
2550+void DirModel::cancelAction()
2551+{
2552+ m_fsAction->cancel();
2553+}
2554+
2555+
2556+QString DirModel::fileSize(qint64 size) const
2557+{
2558+ struct UnitSizes
2559+ {
2560+ qint64 bytes;
2561+ const char *name;
2562+ };
2563+
2564+ static UnitSizes m_unitBytes[5] =
2565+ {
2566+ { 1, "Bytes" }
2567+ ,{1024, "kB"}
2568+ // got it from http://wiki.answers.com/Q/How_many_bytes_are_in_a_megabyte
2569+ ,{1000 * 1000, "MB"}
2570+ ,{1000 * m_unitBytes[2].bytes, "GB"}
2571+ ,{1000 * m_unitBytes[3].bytes, "TB"}
2572+ };
2573+
2574+ QString ret;
2575+ int unit = sizeof(m_unitBytes)/sizeof(m_unitBytes[0]);
2576+ while( unit-- > 1 && size < m_unitBytes[unit].bytes );
2577+ if (unit > 0 )
2578+ {
2579+ ret.sprintf("%0.1f %s", (float)size/m_unitBytes[unit].bytes,
2580+ m_unitBytes[unit].name);
2581+ }
2582+ else
2583+ {
2584+ ret.sprintf("%ld %s", (long int)size, m_unitBytes[0].name);
2585+ }
2586+ return ret;
2587+}
2588+
2589+
2590+
2591+bool DirModel::getShowHiddenFiles() const
2592+{
2593+ return mShowHiddenFiles;
2594+}
2595+
2596+
2597+void DirModel::setShowHiddenFiles(bool show)
2598+{
2599+ if (show != mShowHiddenFiles)
2600+ {
2601+ mShowHiddenFiles = show;
2602+ refresh();
2603+ emit showHiddenFilesChanged();
2604+ }
2605+}
2606+
2607+
2608+void DirModel::toggleShowDirectories()
2609+{
2610+ setShowDirectories(!mShowDirectories);
2611+}
2612+
2613+
2614+void DirModel::toggleShowHiddenFiles()
2615+{
2616+ setShowHiddenFiles(!mShowHiddenFiles);
2617+}
2618+
2619+
2620+DirModel::SortBy
2621+DirModel::getSortBy() const
2622+{
2623+ return mSortBy;
2624+}
2625+
2626+
2627+void DirModel::setSortBy(SortBy field)
2628+{
2629+ if (field != mSortBy)
2630+ {
2631+ mSortBy = field;
2632+ setCompareAndReorder();
2633+ emit sortByChanged();
2634+ }
2635+}
2636+
2637+
2638+DirModel::SortOrder
2639+DirModel::getSortOrder() const
2640+{
2641+ return mSortOrder;
2642+}
2643+
2644+void DirModel::setSortOrder(SortOrder order)
2645+{
2646+ if ( order != mSortOrder )
2647+ {
2648+ mSortOrder = order;
2649+ setCompareAndReorder();
2650+ emit sortOrderChanged();
2651+ }
2652+}
2653+
2654+
2655+void DirModel::toggleSortOrder()
2656+{
2657+ SortOrder order = static_cast<SortOrder> (mSortOrder ^ 1);
2658+ setSortOrder(order);
2659+}
2660+
2661+
2662+void DirModel::toggleSortBy()
2663+{
2664+ SortBy by = static_cast<SortBy> (mSortBy ^ 1);
2665+ setSortBy(by);
2666+}
2667+
2668+/*!
2669+ * \brief DirModel::setCompareAndReorder() called when SortOrder or SortBy change
2670+ *
2671+ * It does not reload items from disk, just reorganize items from \a mDirectoryContents array
2672+ */
2673+void DirModel::setCompareAndReorder()
2674+{
2675+ mCompareFunction = availableCompareFunctions[mSortBy][mSortOrder];
2676+ if (mDirectoryContents.count() > 0 && !mAwaitingResults )
2677+ {
2678+ DirItemInfoList tmpDirectoryContents = mDirectoryContents;
2679+ beginResetModel();
2680+ mDirectoryContents.clear();
2681+ endResetModel();
2682+ for(int counter=0; counter < tmpDirectoryContents.count(); counter++)
2683+ {
2684+ addItem(tmpDirectoryContents.at(counter));
2685+ }
2686+ }
2687+}
2688+
2689+
2690+int DirModel::getClipboardUrlsCounter() const
2691+{
2692+ return mClipboard->clipboardLocalUrlsCounter();
2693+}
2694+
2695+
2696+int DirModel::rowOfItem(const DirItemInfo& fi)
2697+{
2698+ int row = -1;
2699+ //to use qBinaryFind() the array needs to be ordered ascending
2700+ if (mCompareFunction == fileCompareAscending)
2701+ {
2702+ DirItemInfoList::Iterator it = qBinaryFind(mDirectoryContents.begin(),
2703+ mDirectoryContents.end(),
2704+ fi,
2705+ fileCompareExists);
2706+ if (it != mDirectoryContents.end())
2707+ {
2708+ row = it - mDirectoryContents.begin();
2709+ }
2710+ }
2711+ else //walk through whole array
2712+ {
2713+ //TODO improve this search
2714+ int counter = mDirectoryContents.count();
2715+ while (counter--)
2716+ {
2717+ if ( 0 == QString::localeAwareCompare(fi.absoluteFilePath(),
2718+ mDirectoryContents.at(counter).absoluteFilePath()) )
2719+ {
2720+ row = counter;
2721+ break;
2722+ }
2723+ }
2724+ }
2725+ return row;
2726+}
2727+
2728+
2729+QDir::Filter DirModel::currentDirFilter() const
2730+{
2731+ int filter = QDir::AllEntries | QDir::NoDotAndDotDot ;
2732+ if (!mShowDirectories)
2733+ {
2734+ filter &= ~QDir::AllDirs;
2735+ filter &= ~QDir::Dirs;
2736+ }
2737+ if (mShowHiddenFiles)
2738+ {
2739+ filter |= QDir::Hidden;
2740+ }
2741+ if (mIsRecursive)
2742+ {
2743+ filter |= QDir::NoSymLinks;
2744+ }
2745+ QDir::Filter dirFilter = static_cast<QDir::Filter>(filter);
2746+ return dirFilter;
2747+}
2748+
2749+QString DirModel::dirItems(const DirItemInfo& fi) const
2750+{
2751+ int counter = 0;
2752+ QDir d(fi.absoluteFilePath(), QString(), QDir::NoSort, currentDirFilter());
2753+ counter = d.count();
2754+ if (counter < 0)
2755+ {
2756+ counter = 0;
2757+ }
2758+ QString ret (QString::number(counter) + QLatin1Char(' '));
2759+ ret += QObject::tr("items");
2760+ return ret;
2761+}
2762+
2763+
2764+bool DirModel::openIndex(int row)
2765+{
2766+ bool ret = false;
2767+ if (IS_VALID_ROW(row))
2768+ {
2769+ ret = openItem(mDirectoryContents.at(row));
2770+ }
2771+ else
2772+ {
2773+ WARN_ROW_OUT_OF_RANGE(row);
2774+ }
2775+ return ret;
2776+}
2777+
2778+bool DirModel::openPath(const QString &filename)
2779+{
2780+ DirItemInfo fi(setParentIfRelative(filename));
2781+ return openItem(fi);
2782+}
2783+
2784+/*!
2785+ * \brief DirModel::openItem() opens a directory/file
2786+ * \param fi
2787+ * \return true it could open the item
2788+ */
2789+bool DirModel::openItem(const DirItemInfo &fi)
2790+{
2791+ bool ret = false;
2792+ if (fi.isLocal())
2793+ {
2794+ if (canReadDir(fi.diskFileInfo()))
2795+ {
2796+ ret = cdInto(fi.diskFileInfo());
2797+ }
2798+ else
2799+ {
2800+ //TODO open executables
2801+ if (canReadFile(fi.diskFileInfo()))
2802+ {
2803+ ret = QDesktopServices::openUrl(QUrl::fromLocalFile(fi.absoluteFilePath()));
2804+ }
2805+ }
2806+ }
2807+ return ret;
2808+}
2809+
2810+/*!
2811+ * \brief DirModel::createWorkerRequest() create a request for IORequestWorker
2812+ * \param requestType the common IORequest::DirList to fill a directory content
2813+ * or IORequest::DirAutoRefresh that will verify any external File System modification
2814+ * \param pathName the path to get content
2815+ * \return the thread object
2816+ */
2817+DirListWorker * DirModel::createWorkerRequest(IORequest::RequestType requestType,
2818+ const QString& pathName)
2819+{
2820+ DirListWorker * reqThread = 0;
2821+ QDir::Filter dirFilter = currentDirFilter();
2822+ if (requestType == IORequest::DirList)
2823+ {
2824+ // TODO: we need to set a spinner active before we start getting results from DirListWorker
2825+ reqThread = new DirListWorker(pathName, dirFilter, mIsRecursive);
2826+ }
2827+ else
2828+ {
2829+ reqThread = new ExternalFileSystemChangesWorker(mDirectoryContents,
2830+ pathName,
2831+ dirFilter, mIsRecursive);
2832+ }
2833+ return reqThread;
2834+}
2835+
2836+
2837+
2838+/*!
2839+ * \brief DirModel::startExternalFsWatcher() starts the External File System Watcher
2840+ */
2841+void DirModel::startExternalFsWatcher()
2842+{
2843+#if DEBUG_EXT_FS_WATCHER
2844+ qDebug() << "[extFsWorker]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2845+ << Q_FUNC_INFO << this;
2846+
2847+#endif
2848+ if (!mExtFSWatcher)
2849+ {
2850+ mExtFSWatcher = new ExternalFSWatcher(this);
2851+ mExtFSWatcher->setIntervalToNotifyChanges(EX_FS_WATCHER_TIMER_INTERVAL);
2852+ connect(this, SIGNAL(pathChanged(QString)),
2853+ mExtFSWatcher, SLOT(setCurrentPath(QString)));
2854+
2855+ connect(mExtFSWatcher, SIGNAL(pathModified()),
2856+ this, SLOT(onThereAreExternalChanges()));
2857+
2858+ //setCurrentPath() checks for empty paths
2859+ mExtFSWatcher->setCurrentPath(mCurrentDir);
2860+ }
2861+}
2862+
2863+
2864+
2865+/*!
2866+ * \brief DirModel::stoptExternalFsWatcher stops the External File System Watcher
2867+ */
2868+void DirModel::stoptExternalFsWatcher()
2869+{
2870+#if DEBUG_EXT_FS_WATCHER
2871+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2872+ << Q_FUNC_INFO << this;
2873+#endif
2874+ if (mExtFSWatcher)
2875+ {
2876+ delete mExtFSWatcher;
2877+ mExtFSWatcher = 0;
2878+ }
2879+}
2880+
2881+
2882+void DirModel::onThereAreExternalChanges()
2883+{
2884+ if ( IS_FILE_MANAGER_IDLE() )
2885+ {
2886+#if DEBUG_EXT_FS_WATCHER
2887+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2888+ << Q_FUNC_INFO << this << "File System modified";
2889+#endif
2890+ DirListWorker *w =
2891+ createWorkerRequest(IORequest::DirListExternalFSChanges,
2892+ mCurrentDir
2893+ );
2894+ ExternalFileSystemChangesWorker *extFsWorker =
2895+ static_cast<ExternalFileSystemChangesWorker*> (w);
2896+
2897+ connect(extFsWorker, SIGNAL(added(DirItemInfo)),
2898+ this, SLOT(onItemAddedOutsideFm(DirItemInfo)));
2899+ connect(extFsWorker, SIGNAL(removed(DirItemInfo)),
2900+ this, SLOT(onItemRemovedOutSideFm(DirItemInfo)));
2901+ connect(extFsWorker, SIGNAL(changed(DirItemInfo)),
2902+ this, SLOT(onItemChangedOutSideFm(DirItemInfo)));
2903+ connect(extFsWorker, SIGNAL(finished(int)),
2904+ this, SLOT(onExternalFsWorkerFinished(int)));
2905+
2906+ ioWorkerThread()->addRequest(extFsWorker);
2907+ }
2908+#if DEBUG_EXT_FS_WATCHER
2909+ else
2910+ {
2911+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2912+ << Q_FUNC_INFO << this << "Busy, nothing to do";
2913+ }
2914+#endif
2915+}
2916+
2917+/*!
2918+ * \brief DirModel::onItemAddedOutsideFm() It receives a signal saying an item was added by other application
2919+ * \param fi
2920+ */
2921+void DirModel::onItemAddedOutsideFm(const DirItemInfo &fi)
2922+{
2923+#if DEBUG_EXT_FS_WATCHER
2924+ int before = rowCount();
2925+#endif
2926+ if (IS_FILE_MANAGER_IDLE())
2927+ {
2928+ int row = rowOfItem(fi);
2929+ if (row == -1)
2930+ {
2931+ onItemAdded(fi);
2932+ }
2933+ }
2934+#if DEBUG_EXT_FS_WATCHER
2935+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2936+ << Q_FUNC_INFO << this
2937+ << "counterBefore:" << before
2938+ << "added" << fi.absoluteFilePath()
2939+ << "counterAfter:" << rowCount();
2940+#endif
2941+}
2942+
2943+/*!
2944+ * \brief DirModel::onItemRemovedOutSideFm() It receives a signal saying an item was removed by other application
2945+ *
2946+ * Just calls \ref onItemRemoved() which already checks if the item exists
2947+ *
2948+ * \param fi
2949+ */
2950+void DirModel::onItemRemovedOutSideFm(const DirItemInfo &fi)
2951+{
2952+ if (IS_FILE_MANAGER_IDLE())
2953+ {
2954+#if DEBUG_EXT_FS_WATCHER
2955+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2956+ << Q_FUNC_INFO << this << "removed" << fi.absoluteFilePath();
2957+#endif
2958+ onItemRemoved(fi);
2959+ }
2960+}
2961+
2962+/*!
2963+ * \brief DirModel::onItemChangedOutSideFm()
2964+ *
2965+ * A File or a Dir modified by other applications: size,date, permissions
2966+ */
2967+void DirModel::onItemChangedOutSideFm(const DirItemInfo &fi)
2968+{
2969+ if (IS_FILE_MANAGER_IDLE())
2970+ {
2971+ onItemChanged(fi);
2972+#if DEBUG_EXT_FS_WATCHER
2973+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2974+ << Q_FUNC_INFO << this << "changed" << fi.absoluteFilePath()
2975+ << "from row" << rowOfItem(fi);
2976+#endif
2977+ }
2978+}
2979+
2980+
2981+/*!
2982+ * \brief DirModel::onExternalFsWatcherFinihed()
2983+ */
2984+void DirModel::onExternalFsWorkerFinished(int currentDirCounter)
2985+{
2986+
2987+#if DEBUG_EXT_FS_WATCHER
2988+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz")
2989+ << Q_FUNC_INFO << this
2990+ << "currentDirCounter:" << currentDirCounter;
2991+
2992+#endif
2993+ if (currentDirCounter == 0 && IS_FILE_MANAGER_IDLE())
2994+ {
2995+ clear();
2996+ }
2997+}
2998+
2999+
3000+/*!
3001+ * \brief DirModel:getEnabledExternalFSWatcher()
3002+ * \return true if the External File System Watcher is enabled
3003+ */
3004+bool DirModel::getEnabledExternalFSWatcher() const
3005+{
3006+ return mExtFSWatcher ? true : false;
3007+}
3008+
3009+
3010+/*!
3011+ * \brief DirModel::setEnabledExternalFSWatcher() enable/disable External File Sysmte Watcher
3012+ * \param enable
3013+ */
3014+void DirModel::setEnabledExternalFSWatcher(bool enable)
3015+{
3016+ if(enable)
3017+ {
3018+ startExternalFsWatcher();
3019+ }
3020+ else
3021+ {
3022+ stoptExternalFsWatcher();
3023+ }
3024+}
3025+
3026+
3027+bool DirModel::existsDir(const QString &folderName) const
3028+{
3029+ DirItemInfo d(setParentIfRelative(folderName));
3030+ return d.exists() && d.isDir();
3031+}
3032+
3033+bool DirModel::canReadDir(const QString &folderName) const
3034+{
3035+ DirItemInfo d(setParentIfRelative(folderName));
3036+ return canReadDir(d.diskFileInfo());
3037+}
3038+
3039+bool DirModel::canReadDir(const QFileInfo & d) const
3040+{
3041+ return d.exists() && d.isDir() && d.isReadable() && d.isExecutable();
3042+}
3043+
3044+bool DirModel::existsFile(const QString &fileName) const
3045+{
3046+ DirItemInfo f(setParentIfRelative(fileName));
3047+ return f.exists() && f.isFile();
3048+}
3049+
3050+bool DirModel::canReadFile(const QString &fileName) const
3051+{
3052+ DirItemInfo f(setParentIfRelative(fileName));
3053+ return canReadFile(f.diskFileInfo());
3054+}
3055+
3056+bool DirModel::canReadFile(const QFileInfo &f) const
3057+{
3058+ return f.exists() && f.isFile() && f.isReadable();
3059+}
3060+
3061+
3062+
3063+QDateTime DirModel::curPathCreatedDate() const
3064+{
3065+ QDateTime d;
3066+ QFileInfo f(mCurrentDir);
3067+ if (f.exists())
3068+ {
3069+ d = f.created();
3070+ }
3071+ return d;
3072+}
3073+
3074+
3075+QDateTime DirModel::curPathModifiedDate() const
3076+{
3077+ QDateTime d;
3078+ QFileInfo f(mCurrentDir);
3079+ if (f.exists())
3080+ {
3081+ d = f.lastModified();
3082+ }
3083+ return d;
3084+}
3085+
3086+
3087+QDateTime DirModel::curPathAccessedDate() const
3088+{
3089+ QDateTime d;
3090+ QFileInfo f(mCurrentDir);
3091+ if (f.exists())
3092+ {
3093+ d = f.lastRead();
3094+ }
3095+ return d;
3096+}
3097+
3098+
3099+bool DirModel::curPathIsWritable() const
3100+{
3101+ QFileInfo f(mCurrentDir);
3102+ return f.exists() && f.isWritable();
3103+}
3104+
3105+QString DirModel::curPathCreatedDateLocaleShort() const
3106+{
3107+ QString date;
3108+ QDateTime d(curPathCreatedDate());
3109+ if (!d.isNull())
3110+ {
3111+ date = d.toString(Qt::SystemLocaleShortDate);
3112+ }
3113+ return date;
3114+}
3115+
3116+
3117+QString DirModel::curPathModifiedDateLocaleShort() const
3118+{
3119+ QString date;
3120+ QDateTime d(curPathModifiedDate());
3121+ if (!d.isNull())
3122+ {
3123+ date = d.toString(Qt::SystemLocaleShortDate);
3124+ }
3125+ return date;
3126+}
3127+
3128+
3129+QString DirModel::curPathAccessedDateLocaleShort() const
3130+{
3131+ QString date;
3132+ QDateTime d(curPathAccessedDate());
3133+ if (!d.isNull())
3134+ {
3135+ date = d.toString(Qt::SystemLocaleShortDate);
3136+ }
3137+ return date;
3138+}
3139+
3140+
3141+QFileInfo DirModel::setParentIfRelative(const QString &fileOrDir) const
3142+{
3143+ QFileInfo myFi(fileOrDir);
3144+ if (myFi.isRelative())
3145+ {
3146+ myFi.setFile(mCurrentDir, fileOrDir);
3147+ QFileInfo abs(myFi.absoluteFilePath());
3148+ myFi = abs;
3149+ }
3150+ return myFi;
3151+}
3152+
3153+
3154+int DirModel::getProgressCounter() const
3155+{
3156+ return m_fsAction->getProgressCounter();
3157+}
3158+
3159+
3160+void DirModel::clear()
3161+{
3162+ beginResetModel();
3163+ mDirectoryContents.clear();
3164+ mSelection->clear();
3165+ endResetModel();
3166+}
3167+
3168+
3169+DirSelection * DirModel::selectionObject() const
3170+{
3171+ return mSelection;
3172+}
3173+
3174+
3175+void DirModel::registerMetaTypes()
3176+{
3177+ qRegisterMetaType<DirItemInfoList>("DirItemInfoList");
3178+ qRegisterMetaType<DirItemInfo>("DirItemInfo");
3179+}
3180+
3181+void DirModel::notifyItemChanged(int row)
3182+{
3183+ QModelIndex first = index(row,0);
3184+#if REGRESSION_TEST_FOLDERLISTMODEL
3185+ QModelIndex last = index(row, columnCount()); //Table only when testing
3186+#else
3187+ QModelIndex last = first; //QML uses Listview, just one column
3188+#endif
3189+ emit dataChanged(first, last);
3190+}
3191+
3192+
3193+int DirModel::getIndex(const QString &name)
3194+{
3195+ QFileInfo i(name);
3196+ return rowOfItem(DirItemInfo(i));
3197+}
3198+
3199+
3200+#ifndef DO_NOT_USE_TAG_LIB
3201+QVariant DirModel::getAudioMetaData(const QFileInfo& fi, int role) const
3202+{
3203+ QVariant empty;
3204+ if (!fi.isDir()) {
3205+ TagLib::FileRef f(fi.absoluteFilePath().toStdString().c_str(), true, TagLib::AudioProperties::Fast);
3206+ TagLib::MPEG::File mp3(fi.absoluteFilePath().toStdString().c_str(), true, TagLib::MPEG::Properties::Fast);
3207+ TagLib::Tag *tag = f.tag();
3208+ if (tag)
3209+ {
3210+ TagLib::ID3v2::FrameList list = mp3.ID3v2Tag()->frameListMap()["APIC"];
3211+ switch (role) {
3212+ case TrackTitleRole:
3213+ return QString::fromUtf8(tag->title().toCString(true));
3214+ case TrackArtistRole:
3215+ return QString::fromUtf8(tag->artist().toCString(true));
3216+ case TrackAlbumRole:
3217+ return QString::fromUtf8(tag->album().toCString(true));
3218+ case TrackYearRole:
3219+ return QString::number(tag->year());
3220+ case TrackNumberRole:
3221+ return QString::number(tag->track());
3222+ case TrackGenreRole:
3223+ return QString::fromUtf8(tag->genre().toCString(true));
3224+ case TrackLengthRole:
3225+ if(!f.isNull() && f.audioProperties()) {
3226+ return QString::number(f.audioProperties()->length());
3227+ } else {
3228+ return QString::number(0);
3229+ }
3230+ case TrackCoverRole:
3231+ if(!list.isEmpty()) {
3232+ TagLib::ID3v2::AttachedPictureFrame *Pic = static_cast<TagLib::ID3v2::AttachedPictureFrame *>(list.front());
3233+ QImage img;
3234+ img.loadFromData((const uchar *) Pic->picture().data(), Pic->picture().size());
3235+ return img;
3236+ }
3237+ default:
3238+ break;
3239+ } //switch
3240+ }//if (tag)
3241+ }
3242+ return empty;
3243+}
3244+#endif
3245
3246=== added file 'src/plugin/folderlistmodel/dirmodel.h'
3247--- src/plugin/folderlistmodel/dirmodel.h 1970-01-01 00:00:00 +0000
3248+++ src/plugin/folderlistmodel/dirmodel.h 2014-04-15 08:19:22 +0000
3249@@ -0,0 +1,430 @@
3250+/*
3251+ * Copyright (C) 2012 Robin Burchell <robin+nemo@viroteck.net>
3252+ *
3253+ * You may use this file under the terms of the BSD license as follows:
3254+ *
3255+ * "Redistribution and use in source and binary forms, with or without
3256+ * modification, are permitted provided that the following conditions are
3257+ * met:
3258+ * * Redistributions of source code must retain the above copyright
3259+ * notice, this list of conditions and the following disclaimer.
3260+ * * Redistributions in binary form must reproduce the above copyright
3261+ * notice, this list of conditions and the following disclaimer in
3262+ * the documentation and/or other materials provided with the
3263+ * distribution.
3264+ * * Neither the name of Nemo Mobile nor the names of its contributors
3265+ * may be used to endorse or promote products derived from this
3266+ * software without specific prior written permission.
3267+ *
3268+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3269+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
3270+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
3271+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
3272+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
3273+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
3274+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
3275+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
3276+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
3277+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3278+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
3279+ */
3280+
3281+#ifndef DIRMODEL_H
3282+#define DIRMODEL_H
3283+
3284+
3285+#include <QStringList>
3286+#include <QDir>
3287+
3288+#include "iorequest.h"
3289+#include "filecompare.h"
3290+#include "diritemabstractlistmodel.h"
3291+#include "diriteminfo.h"
3292+
3293+class FileSystemAction;
3294+class ExternalFSWatcher;
3295+class Clipboard;
3296+class DirSelection;
3297+
3298+/*!
3299+ * When the External File System Wathcer is enabled,
3300+ * this is the interval used to check if there has been any change in the current path
3301+ *
3302+ * \sa setEnabledExternalFSWatcher()
3303+ */
3304+#define EX_FS_WATCHER_TIMER_INTERVAL 900
3305+
3306+class DirModel : public DirItemAbstractListModel
3307+{
3308+ Q_OBJECT
3309+public:
3310+ enum Roles {
3311+ FileNameRole = Qt::UserRole,
3312+ AccessedDateRole,
3313+ CreationDateRole,
3314+ ModifiedDateRole,
3315+ FileSizeRole,
3316+ IconSourceRole,
3317+ FilePathRole,
3318+ IsDirRole,
3319+ IsFileRole,
3320+ IsReadableRole,
3321+ IsWritableRole,
3322+ IsExecutableRole,
3323+ IsSelectedRole,
3324+ TrackTitleRole,
3325+ TrackArtistRole,
3326+ TrackAlbumRole,
3327+ TrackYearRole,
3328+ TrackNumberRole,
3329+ TrackGenreRole,
3330+ TrackLengthRole,
3331+ TrackCoverRole
3332+ };
3333+
3334+public:
3335+ explicit DirModel(QObject *parent = 0);
3336+ ~DirModel();
3337+
3338+ static void registerMetaTypes();
3339+
3340+ //DirItemAbstractListModel
3341+ virtual int getIndex(const QString& name);
3342+ virtual void notifyItemChanged(int row);
3343+
3344+ int rowCount(const QModelIndex &index = QModelIndex()) const
3345+ {
3346+ if (index.parent() != QModelIndex())
3347+ return 0;
3348+ return mDirectoryContents.count();
3349+ }
3350+
3351+ // TODO: this won't be safe if the model can change under the holder of the row
3352+ Q_INVOKABLE QVariant data(int row, const QByteArray &stringRole) const;
3353+
3354+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
3355+
3356+ Q_INVOKABLE void refresh()
3357+ {
3358+ // just some syntactical sugar really
3359+ setPath(path());
3360+ }
3361+
3362+ Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
3363+ inline QString path() const { return mCurrentDir; }
3364+ void setPath(const QString &pathName);
3365+
3366+ Q_INVOKABLE QDateTime curPathAccessedDate() const;
3367+ Q_INVOKABLE QDateTime curPathCreatedDate() const;
3368+ Q_INVOKABLE QDateTime curPathModifiedDate() const;
3369+ Q_INVOKABLE QString curPathAccessedDateLocaleShort() const;
3370+ Q_INVOKABLE QString curPathCreatedDateLocaleShort() const;
3371+ Q_INVOKABLE QString curPathModifiedDateLocaleShort() const;
3372+ Q_INVOKABLE bool curPathIsWritable() const;
3373+
3374+ Q_PROPERTY(bool awaitingResults READ awaitingResults NOTIFY awaitingResultsChanged)
3375+ bool awaitingResults() const;
3376+
3377+ Q_INVOKABLE void rm(const QStringList &paths);
3378+
3379+ Q_INVOKABLE bool rename(const QString& oldName, const QString& newName);
3380+ Q_INVOKABLE bool rename(int row, const QString &newName);
3381+
3382+ Q_INVOKABLE void mkdir(const QString &newdir);
3383+
3384+ Q_PROPERTY(bool filterDirectories READ filterDirectories WRITE setFilterDirectories NOTIFY filterDirectoriesChanged)
3385+ bool filterDirectories() const;
3386+
3387+ Q_PROPERTY(bool isRecursive READ isRecursive WRITE setIsRecursive NOTIFY isRecursiveChanged)
3388+ bool isRecursive() const;
3389+
3390+ Q_PROPERTY(bool readsMediaMetadata READ readsMediaMetadata WRITE setReadsMediaMetadata NOTIFY readsMediaMetadataChanged)
3391+ bool readsMediaMetadata() const;
3392+
3393+ Q_PROPERTY(bool showDirectories READ showDirectories WRITE setShowDirectories NOTIFY showDirectoriesChanged)
3394+ bool showDirectories() const;
3395+
3396+ Q_PROPERTY(QStringList nameFilters READ nameFilters WRITE setNameFilters NOTIFY nameFiltersChanged)
3397+ QStringList nameFilters() const;
3398+ void setNameFilters(const QStringList &nameFilters);
3399+
3400+public slots:
3401+ void onItemsAdded(const DirItemInfoList &newFiles);
3402+ void onResultsFetched();
3403+
3404+signals:
3405+ void awaitingResultsChanged();
3406+ void nameFiltersChanged();
3407+ void filterDirectoriesChanged();
3408+ void isRecursiveChanged();
3409+ void readsMediaMetadataChanged();
3410+ void showDirectoriesChanged();
3411+ void pathChanged(const QString& newPath);
3412+ void error(const QString &errorTitle, const QString &errorMessage);
3413+
3414+private:
3415+ QHash<int, QByteArray> buildRoleNames() const;
3416+ QHash<int, QByteArray> roleNames() const;
3417+ QStringList mNameFilters;
3418+ bool mFilterDirectories;
3419+ bool mShowDirectories;
3420+ bool mAwaitingResults;
3421+ bool mIsRecursive;
3422+ bool mReadsMediaMetadata;
3423+ QString mCurrentDir;
3424+ DirItemInfoList mDirectoryContents;
3425+
3426+public:
3427+
3428+ Q_INVOKABLE DirSelection * selectionObject() const ;
3429+
3430+ //[0] new stuff Ubuntu File Manager
3431+ Q_PROPERTY(QString parentPath READ parentPath NOTIFY pathChanged)
3432+ QString parentPath() const;
3433+
3434+ Q_PROPERTY(bool showHiddenFiles READ getShowHiddenFiles WRITE setShowHiddenFiles NOTIFY showHiddenFilesChanged)
3435+ bool getShowHiddenFiles() const;
3436+
3437+ Q_ENUMS(SortBy)
3438+ enum SortBy
3439+ {
3440+ SortByName,
3441+ SortByDate
3442+ };
3443+ Q_PROPERTY(SortBy sortBy READ getSortBy WRITE setSortBy NOTIFY sortByChanged)
3444+ SortBy getSortBy() const;
3445+
3446+ Q_ENUMS(SortOrder)
3447+ enum SortOrder
3448+ {
3449+ SortAscending = Qt::AscendingOrder,
3450+ SortDescending = Qt::DescendingOrder
3451+ };
3452+ Q_PROPERTY(SortOrder sortOrder READ getSortOrder WRITE setSortOrder NOTIFY sortOrderChanged)
3453+ SortOrder getSortOrder() const;
3454+
3455+ Q_PROPERTY(int clipboardUrlsCounter READ getClipboardUrlsCounter NOTIFY clipboardChanged)
3456+ int getClipboardUrlsCounter() const;
3457+
3458+ Q_PROPERTY(bool enableExternalFSWatcher READ getEnabledExternalFSWatcher WRITE setEnabledExternalFSWatcher)
3459+ bool getEnabledExternalFSWatcher() const;
3460+
3461+ Q_INVOKABLE QString homePath() const;
3462+
3463+ /*!
3464+ * \brief Tries to make the directory pointed by row as the current to be browsed
3465+ * \return true if row points to a directory and the directory is readble, false otherwise
3466+ */
3467+ Q_INVOKABLE bool cdIntoIndex(int row);
3468+ Q_INVOKABLE bool cdIntoPath(const QString& filename);
3469+
3470+ /*!
3471+ * \brief copyIndex() puts the item pointed by \a row (dir or file) into the clipboard
3472+ * \param row points to the item file or directory
3473+ */
3474+ Q_INVOKABLE void copyIndex(int row);
3475+
3476+ /*!
3477+ * \brief copyPaths(const QStringList& urls) several items (dirs or files) into the clipboard
3478+ * \param items fullpathnames or names only
3479+ */
3480+ Q_INVOKABLE void copyPaths(const QStringList& items);
3481+
3482+ /*!
3483+ * \brief cutIndex() puts the item into the clipboard as \ref copy(),
3484+ * mark the item to be removed after \ref paste()
3485+ * \param row points to the item file or directory
3486+ */
3487+ Q_INVOKABLE void cutIndex(int row);
3488+
3489+ /*!
3490+ * \brief cut() puts several items (dirs or files) into the clipboard as \ref copy(),
3491+ * mark the item to be removed after \ref paste()
3492+ * \param items fullpathnames or names only
3493+ */
3494+ Q_INVOKABLE void cutPaths(const QStringList& items);
3495+
3496+ /*!
3497+ * \brief removeIndex(); remove a item file or directory
3498+ *
3499+ * I gets the item indicated by \row and calls \ref rm()
3500+ *
3501+ * \param row points to the item to b e removed
3502+ * \return true if it was possible to remove the item
3503+ */
3504+ Q_INVOKABLE void removeIndex(int row);
3505+
3506+ /*!
3507+ * Just calls \ref rm()
3508+ */
3509+ Q_INVOKABLE void removePaths(const QStringList& items);
3510+
3511+ /*!
3512+ * Tries to open a file using a suitable application, if the index points to a directory
3513+ * it goes into it using \ref cdIntoIndex() or \ref cdIntoPath()
3514+ *
3515+ * \note Qt uses Qt QDesktopServices::openUrl()
3516+ */
3517+ Q_INVOKABLE bool openIndex(int row);
3518+
3519+ /*!
3520+ * Same as \ref openIndex() but using a file name instead of index
3521+ *
3522+ * It allows to open directories and files using absoulte paths
3523+ *
3524+ * \sa \ref cdIntoPath()
3525+ */
3526+ Q_INVOKABLE bool openPath(const QString& filename);
3527+
3528+ /*!
3529+ * \brief getProgressCounter() returns the number of \ref progress() notifications an Action will perform
3530+ *
3531+ * It may be useful to decide about showing or not a progress dialog for Remove/Copy/Cut/Paste Actions
3532+ *
3533+ * This function can be called just after receiving first \ref progress() notification
3534+ *
3535+ * \note In the future this \ref getProgressCounter() and \ref progress() will merge to single signal that
3536+ * will send the Action full information, it will allow to have multi thread Actions.
3537+ * Also \ref cancelAction() needs to change
3538+ */
3539+ Q_INVOKABLE int getProgressCounter() const;
3540+
3541+ // some helper functions that can be useful to other QML applications than File Manager
3542+ Q_INVOKABLE bool existsDir(const QString& folderName) const;
3543+ Q_INVOKABLE bool canReadDir(const QString& folderName) const;
3544+ Q_INVOKABLE bool existsFile(const QString& fileName) const;
3545+ Q_INVOKABLE bool canReadFile(const QString& fileName) const;
3546+
3547+public slots:
3548+ /*!
3549+ * \brief goHome() goes to user home dir
3550+ * Go to user home dir, we may have a tab for places or something like that
3551+ */
3552+ void goHome();
3553+
3554+ /*!
3555+ * \brief cdUp() sets the parent directory as current directory
3556+ *
3557+ * It can work as a back function if there is no user input path
3558+ * \return true if it was possible to change to parent dir, otherwise false
3559+ */
3560+ bool cdUp();
3561+
3562+ /*!
3563+ * \brief paste() copy item(s) from \ref copy() and \ref paste() into the current directory
3564+ *
3565+ * If the operation was \ref cut(), then remove the original item
3566+ */
3567+ void paste();
3568+
3569+ /*!
3570+ * \brief cancelAction() any copy/cut/remove can be cancelled
3571+ */
3572+ void cancelAction();
3573+
3574+ void setIsRecursive(bool isRecursive);
3575+ void setReadsMediaMetadata(bool readsMediaMetadata);
3576+ void setFilterDirectories(bool filterDirectories);
3577+ void setShowDirectories(bool showDirectories);
3578+ void setShowHiddenFiles(bool show);
3579+ void setSortBy(SortBy field);
3580+ void setSortOrder(SortOrder order);
3581+ void setEnabledExternalFSWatcher(bool enable);
3582+
3583+
3584+ void toggleShowDirectories();
3585+ void toggleShowHiddenFiles();
3586+ void toggleSortOrder();
3587+ void toggleSortBy();
3588+
3589+signals:
3590+ /*!
3591+ * \brief insertedItem()
3592+ *
3593+ * It happens when a new file is inserted in an existent view,
3594+ * for example from \ref mkdir() or \ref paste()
3595+ *
3596+ * It can be used to make the new row visible to the user doing a scroll to
3597+ */
3598+ void insertedRow(int row);
3599+ /*!
3600+ * \brief progress()
3601+ * Sends status about recursive and multi-items remove/move/copy
3602+ *
3603+ * \param curItem current item being handled
3604+ * \param totalItems total of items including recursive directories content
3605+ * \param percent a percent done
3606+ */
3607+ void progress(int curItem, int totalItems, int percent);
3608+
3609+ void showHiddenFilesChanged();
3610+ void sortByChanged();
3611+ void sortOrderChanged();
3612+
3613+ void clipboardChanged();
3614+
3615+private slots:
3616+ void onItemRemoved(const QString&);
3617+ void onItemRemoved(const DirItemInfo&);
3618+ void onItemAdded(const QString&);
3619+ void onItemAdded(const DirItemInfo&);
3620+ void onItemChanged(const DirItemInfo&);
3621+
3622+private:
3623+ int addItem(const DirItemInfo& fi);
3624+ void setCompareAndReorder();
3625+ int rowOfItem(const DirItemInfo& fi);
3626+ QDir::Filter currentDirFilter() const;
3627+ QString dirItems(const DirItemInfo& fi) const;
3628+ bool cdInto(const DirItemInfo& fi);
3629+ bool openItem(const DirItemInfo& fi);
3630+ DirListWorker * createWorkerRequest(IORequest::RequestType requestType,
3631+ const QString& pathName);
3632+ bool canReadDir(const QFileInfo& d) const;
3633+ bool canReadFile(const QFileInfo& f) const;
3634+ QFileInfo setParentIfRelative(const QString &fileOrDir) const;
3635+
3636+private:
3637+ void startExternalFsWatcher();
3638+ void stoptExternalFsWatcher();
3639+ void clear();
3640+private slots:
3641+ void onItemAddedOutsideFm(const DirItemInfo&fi);
3642+ void onItemRemovedOutSideFm(const DirItemInfo&);
3643+ void onItemChangedOutSideFm(const DirItemInfo&fi);
3644+ void onThereAreExternalChanges();
3645+ void onExternalFsWorkerFinished(int);
3646+
3647+
3648+private:
3649+ bool mShowHiddenFiles;
3650+ SortBy mSortBy;
3651+ SortOrder mSortOrder;
3652+ CompareFunction mCompareFunction;
3653+ ExternalFSWatcher* mExtFSWatcher;
3654+ Clipboard * mClipboard;
3655+ DirSelection * mSelection;
3656+
3657+
3658+private:
3659+ FileSystemAction * m_fsAction; //!< it does file system recursive remove/copy/move
3660+ QString fileSize(qint64 size) const;
3661+#ifndef DO_NOT_USE_TAG_LIB
3662+ QVariant getAudioMetaData(const QFileInfo& fi, int role) const;
3663+#endif
3664+//[0]
3665+
3666+#if defined(REGRESSION_TEST_FOLDERLISTMODEL)
3667+ //make this work with tables
3668+ virtual int columnCount(const QModelIndex &parent = QModelIndex()) const
3669+ {
3670+ Q_UNUSED(parent);
3671+ return TrackCoverRole - FileNameRole + 1;
3672+ }
3673+ virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const;
3674+ friend class TestDirModel;
3675+#endif
3676+};
3677+
3678+
3679+#endif // DIRMODEL_H
3680
3681=== added file 'src/plugin/folderlistmodel/dirselection.cpp'
3682--- src/plugin/folderlistmodel/dirselection.cpp 1970-01-01 00:00:00 +0000
3683+++ src/plugin/folderlistmodel/dirselection.cpp 2014-04-15 08:19:22 +0000
3684@@ -0,0 +1,298 @@
3685+/**************************************************************************
3686+ *
3687+ * Copyright 2014 Canonical Ltd.
3688+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
3689+ *
3690+ * This program is free software; you can redistribute it and/or modify
3691+ * it under the terms of the GNU Lesser General Public License as published by
3692+ * the Free Software Foundation; version 3.
3693+ *
3694+ * This program is distributed in the hope that it will be useful,
3695+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3696+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3697+ * GNU Lesser General Public License for more details.
3698+ *
3699+ * You should have received a copy of the GNU Lesser General Public License
3700+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3701+ *
3702+ * File: dirselection.cpp
3703+ * Date: 29/01/2014
3704+ */
3705+
3706+#include "dirselection.h"
3707+#include "diritemabstractlistmodel.h"
3708+#include <QTimer>
3709+#include <QDebug>
3710+
3711+
3712+#define VALID_INDEX(index) (index >= 0 && index < m_model->rowCount())
3713+
3714+DirSelection::DirSelection(QObject *parent) : QObject(parent)
3715+{
3716+}
3717+
3718+DirSelection::DirSelection(DirItemAbstractListModel *parent, DirItemInfoList *listItems) :
3719+ QObject(parent)
3720+ ,m_selectedCounter(0)
3721+ ,m_model(parent)
3722+ ,m_listItems(listItems)
3723+ ,m_mode(Single)
3724+ ,m_lastSelectedItem(-1)
3725+{
3726+}
3727+
3728+
3729+
3730+QStringList DirSelection::selectedAbsFilePaths() const
3731+{
3732+ QStringList ret;
3733+ int counter = m_model->rowCount();
3734+ for(int index = 0 ; index < counter; ++index)
3735+ {
3736+ if (m_listItems->at(index).isSelected())
3737+ {
3738+ ret.append(m_listItems->at(index).absoluteFilePath());
3739+ }
3740+ }
3741+ return ret;
3742+}
3743+
3744+QStringList DirSelection::selectedNames() const
3745+{
3746+ QStringList ret;
3747+ int counter = m_model->rowCount();
3748+ for(int index = 0 ; index < counter; ++index)
3749+ {
3750+ if (m_listItems->at(index).isSelected())
3751+ {
3752+ ret.append(m_listItems->at(index).fileName());
3753+ }
3754+ }
3755+ return ret;
3756+}
3757+
3758+
3759+
3760+QList<int> DirSelection::selectedIndexes() const
3761+{
3762+ QList<int> ret;
3763+ int counter = m_model->rowCount();
3764+ for(int index = 0 ; index < counter; ++index)
3765+ {
3766+ if (m_listItems->at(index).isSelected())
3767+ {
3768+ ret.append(index);
3769+ }
3770+ }
3771+ return ret;
3772+}
3773+
3774+
3775+void DirSelection::clear()
3776+{
3777+ if (priv_clear())
3778+ {
3779+ notifyChanges();
3780+ }
3781+}
3782+
3783+
3784+bool DirSelection::priv_clear()
3785+{
3786+ bool notify = m_selectedCounter != 0;
3787+ if (notify)
3788+ {
3789+ int counter = m_model->rowCount();
3790+ DirItemInfo *data = m_listItems->data();
3791+ while (m_selectedCounter > 0 && counter-- )
3792+ {
3793+ if ( data[counter].setSelection(false) )
3794+ {
3795+ --m_selectedCounter;
3796+ m_model->notifyItemChanged(counter);
3797+ }
3798+ }
3799+ }
3800+ //force it to zero, works when cleaning the buffer first
3801+ m_selectedCounter = 0;
3802+ m_lastSelectedItem = -1;
3803+ return notify;
3804+}
3805+
3806+
3807+void DirSelection::selectAll()
3808+{
3809+ int counter = m_model->rowCount();
3810+ bool notify = m_selectedCounter != counter;
3811+ if (notify)
3812+ {
3813+ DirItemInfo *data = m_listItems->data();
3814+ while ( counter-- )
3815+ {
3816+ if ( data[counter].setSelection(true) )
3817+ {
3818+ ++m_selectedCounter;
3819+ m_model->notifyItemChanged(counter);
3820+ }
3821+ }
3822+ notifyChanges();
3823+ }
3824+}
3825+
3826+
3827+int DirSelection::counter() const
3828+{
3829+ return m_selectedCounter;
3830+}
3831+
3832+
3833+DirSelection::Mode DirSelection::mode() const
3834+{
3835+ return m_mode;
3836+}
3837+
3838+
3839+void DirSelection::itemGoingToBeRemoved(const DirItemInfo &item)
3840+{
3841+ if (m_selectedCounter > 0 && item.isSelected())
3842+ {
3843+ --m_selectedCounter;
3844+ notifyChanges();
3845+ }
3846+ // item is going to be removed, no QAbstractItemModel::dataChanged() signal is necessary to refresh views
3847+}
3848+
3849+
3850+void DirSelection::setIndex(int index, bool selected)
3851+{
3852+ if (VALID_INDEX(index))
3853+ {
3854+ int old_selectedCounter = m_selectedCounter;
3855+ if (selected && m_mode == Single && m_selectedCounter > 0)
3856+ {
3857+ priv_clear();
3858+ }
3859+ if ( priv_setIndex(index, selected)
3860+ || old_selectedCounter != m_selectedCounter
3861+ )
3862+ {
3863+ notifyChanges();
3864+ }
3865+ }
3866+}
3867+
3868+
3869+void DirSelection::toggleIndex(int index)
3870+{
3871+ if (VALID_INDEX(index))
3872+ {
3873+ setIndex(index, !m_listItems->at(index).isSelected());
3874+ }
3875+}
3876+
3877+
3878+void DirSelection::setMode(Mode m)
3879+{
3880+ if (m != m_mode)
3881+ {
3882+ m_mode = m;
3883+ emit modeChanged(m_mode);
3884+ }
3885+}
3886+
3887+
3888+void DirSelection::notifyChanges()
3889+{
3890+ emit selectionChanged(m_selectedCounter);
3891+}
3892+
3893+
3894+/*!
3895+ * \brief DirSelection::itemGoingToBeReplaced() it is supposed to control selection writable and readabble states
3896+ *
3897+ * So far it does nothing
3898+ *
3899+ * \param oldItemInfo
3900+ * \param newItemInfo
3901+ */
3902+void DirSelection::itemGoingToBeReplaced(const DirItemInfo &oldItemInfo,
3903+ const DirItemInfo &newItemInfo)
3904+{
3905+ if (oldItemInfo.isSelected())
3906+ {
3907+ // we may add selection writable state in the future
3908+ Q_UNUSED(newItemInfo);
3909+ }
3910+}
3911+
3912+
3913+void DirSelection::selectRange(int indexClicked)
3914+{
3915+ bool changed = false;
3916+ if ( VALID_INDEX(indexClicked)
3917+ && m_selectedCounter > 0
3918+ && indexClicked != m_lastSelectedItem
3919+ && VALID_INDEX(m_lastSelectedItem)
3920+ && !m_listItems->at(indexClicked).isSelected()
3921+ )
3922+ {
3923+ //go from indexClicked to m_lastSelectedItem
3924+ int increment = indexClicked > m_lastSelectedItem? -1 : 1;
3925+ int nextItem = indexClicked;
3926+ int saved_lastSelectedItem = m_lastSelectedItem;
3927+ while (priv_setIndex(nextItem, true) && nextItem != saved_lastSelectedItem)
3928+ {
3929+ nextItem += increment;
3930+ changed = true;
3931+ }
3932+ }
3933+ if (changed)
3934+ {
3935+ notifyChanges();
3936+ }
3937+}
3938+
3939+
3940+bool DirSelection::priv_setIndex(int index, bool selected)
3941+{
3942+ DirItemInfo *data = m_listItems->data();
3943+ bool changed = false;
3944+ if ((changed = data[index].setSelection(selected)))
3945+ {
3946+ m_model->notifyItemChanged(index);
3947+ if (selected)
3948+ {
3949+ ++m_selectedCounter;
3950+ m_lastSelectedItem = index;
3951+ }
3952+ else
3953+ {
3954+ --m_selectedCounter;
3955+ }
3956+ }
3957+ return changed;
3958+}
3959+
3960+
3961+void DirSelection::select(int index, bool range, bool multiSelection )
3962+{
3963+ if (range && VALID_INDEX(m_lastSelectedItem))
3964+ {
3965+ selectRange(index);
3966+ }
3967+ else
3968+ {
3969+ if (multiSelection || m_mode == Multi)
3970+ {
3971+ Mode saveMode = m_mode;
3972+ //set Multi selection do not call clear()
3973+ m_mode = Multi;
3974+ toggleIndex(index);
3975+ m_mode = saveMode;
3976+ }
3977+ else
3978+ {
3979+ setIndex(index, true);
3980+ }
3981+ }
3982+}
3983
3984=== added file 'src/plugin/folderlistmodel/dirselection.h'
3985--- src/plugin/folderlistmodel/dirselection.h 1970-01-01 00:00:00 +0000
3986+++ src/plugin/folderlistmodel/dirselection.h 2014-04-15 08:19:22 +0000
3987@@ -0,0 +1,116 @@
3988+/**************************************************************************
3989+ *
3990+ * Copyright 2014 Canonical Ltd.
3991+ * Copyright 2014 Carlos J Mazieri <carlos.mazieri@gmail.com>
3992+ *
3993+ * This program is free software; you can redistribute it and/or modify
3994+ * it under the terms of the GNU Lesser General Public License as published by
3995+ * the Free Software Foundation; version 3.
3996+ *
3997+ * This program is distributed in the hope that it will be useful,
3998+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3999+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4000+ * GNU Lesser General Public License for more details.
4001+ *
4002+ * You should have received a copy of the GNU Lesser General Public License
4003+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4004+ *
4005+ * File: dirselection.h
4006+ * Date: 29/01/2014
4007+ */
4008+
4009+#ifndef DIRSELECTION_H
4010+#define DIRSELECTION_H
4011+
4012+#include "diriteminfo.h"
4013+
4014+#include <QObject>
4015+#include <QStringList>
4016+
4017+
4018+class DirItemAbstractListModel;
4019+
4020+class DirSelection : public QObject
4021+{
4022+ Q_OBJECT
4023+public:
4024+ explicit DirSelection(DirItemAbstractListModel *parent, DirItemInfoList *listItems);
4025+ explicit DirSelection(QObject *parent = 0);
4026+public slots:
4027+ void selectRange(int indexClicked);
4028+ void selectAll();
4029+ void clear();
4030+ void toggleIndex(int index);
4031+ void setIndex(int index, bool selected);
4032+
4033+public:
4034+ Q_ENUMS(Mode)
4035+ enum Mode
4036+ {
4037+ Single,
4038+ Multi
4039+ };
4040+ Q_PROPERTY(int counter READ counter NOTIFY selectionChanged)
4041+ Q_PROPERTY(Mode mode READ mode WRITE setMode NOTIFY modeChanged)
4042+ Q_INVOKABLE QStringList selectedNames() const;
4043+ Q_INVOKABLE void setMode(Mode m);
4044+ Q_INVOKABLE QStringList selectedAbsFilePaths() const; //full path
4045+ Q_INVOKABLE QList<int> selectedIndexes() const;
4046+ int counter() const;
4047+ Mode mode() const;
4048+
4049+public:
4050+ /*!
4051+ * It allows to pass Control Modifiers directly to perform the most common selection behaviour.
4052+ *
4053+ * Usage Example:
4054+ * \li 1 When selecting an item with Shit key pressed it selects the rage calling \ref selectRange()
4055+ * \li 2 When selecting an item with Crtl key pressed it temporarily forces Multi Selection Mode
4056+ * calling \ref toggleIndex() instead of \ref setIndex();
4057+ *
4058+ * \param range when true it calls \ref selectRange() and does not consider the \a multiSelection parameter
4059+ *
4060+ * \param multiSelection when \a false it respects the current selection mode: calls \ref setIndex()
4061+ * for \ref Single selection mode or \ref toggleIndex() for \ref Multi selection mode.
4062+ * When \a true it calls \ref toggleIndex()
4063+ *
4064+ * QML example:
4065+ * \code
4066+ * property FolderListSelection selectionManager: pageModel.selectionObject()
4067+ * ...
4068+ *
4069+ * MouseArea {
4070+ * anchors.fill: parent
4071+ * onClicked: {
4072+ * selectionManager.select(model.index,
4073+ * (mouse.modifiers & Qt.ShiftModifier),
4074+ * (mouse.modifiers & Qt.ControlModifier) );
4075+ * }
4076+ * }
4077+ * \endcode
4078+ *
4079+ */
4080+ Q_INVOKABLE void select(int index, bool range, bool multiSelection );
4081+
4082+public:
4083+ void itemGoingToBeRemoved(const DirItemInfo& item);
4084+ void itemGoingToBeReplaced(const DirItemInfo& oldItemInfo, const DirItemInfo& newItemInfo);
4085+
4086+private:
4087+ bool priv_clear();
4088+ void notifyChanges();
4089+ bool priv_setIndex(int index, bool selected);
4090+
4091+signals:
4092+ void selectionChanged(int);
4093+ void modeChanged(int);
4094+
4095+private:
4096+ int m_selectedCounter;
4097+ DirItemAbstractListModel* m_model;
4098+ DirItemInfoList * m_listItems;
4099+ Mode m_mode;
4100+ int m_lastSelectedItem;
4101+};
4102+
4103+#endif // DIRSELECTION_H
4104
4105=== added file 'src/plugin/folderlistmodel/externalfswatcher.cpp'
4106--- src/plugin/folderlistmodel/externalfswatcher.cpp 1970-01-01 00:00:00 +0000
4107+++ src/plugin/folderlistmodel/externalfswatcher.cpp 2014-04-15 08:19:22 +0000
4108@@ -0,0 +1,114 @@
4109+/**************************************************************************
4110+ *
4111+ * Copyright 2013 Canonical Ltd.
4112+ * Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
4113+ *
4114+ * This program is free software; you can redistribute it and/or modify
4115+ * it under the terms of the GNU Lesser General Public License as published by
4116+ * the Free Software Foundation; version 3.
4117+ *
4118+ * This program is distributed in the hope that it will be useful,
4119+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4120+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4121+ * GNU Lesser General Public License for more details.
4122+ *
4123+ * You should have received a copy of the GNU Lesser General Public License
4124+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4125+ *
4126+ * File: externalfswatcher.cpp
4127+ * Date: 9/14/2013
4128+ */
4129+
4130+#include "externalfswatcher.h"
4131+
4132+#include <QTimer>
4133+#include <QDateTime>
4134+#include <QDebug>
4135+
4136+#if DEBUG_EXT_FS_WATCHER
4137+# define DEBUG_FSWATCHER() \
4138+ qDebug() << "[extFsWatcher]" << QDateTime::currentDateTime().toString("hh:mm:ss.zzz") \
4139+ << Q_FUNC_INFO << "m_setPath:" << m_setPath \
4140+ << "m_changedPath:" << m_changedPath \
4141+ << "m_waitingEmit:" << m_waitingEmitCounter
4142+#else
4143+# define DEBUG_FSWATCHER() /**/
4144+#endif //
4145+
4146+
4147+ExternalFSWatcher::ExternalFSWatcher(QObject *parent) :
4148+ QFileSystemWatcher(parent)
4149+ , m_waitingEmitCounter(0)
4150+ , m_msWaitTime(DEFAULT_NOTICATION_PERIOD)
4151+{
4152+ connect(this, SIGNAL(directoryChanged(QString)),
4153+ this, SLOT(slotDirChanged(QString)));
4154+}
4155+
4156+
4157+void ExternalFSWatcher::setCurrentPath(const QString &curPath)
4158+{
4159+ if (!curPath.isEmpty())
4160+ {
4161+ if (m_setPath != curPath)
4162+ {
4163+ if (!m_setPath.isEmpty())
4164+ {
4165+ removePath(m_setPath);
4166+ }
4167+ m_setPath = curPath;
4168+ addPath(m_setPath);
4169+ }
4170+ }
4171+ DEBUG_FSWATCHER();
4172+}
4173+
4174+
4175+void ExternalFSWatcher::slotDirChanged(const QString &dir)
4176+{
4177+ DEBUG_FSWATCHER();
4178+ if ( (m_setPath == dir)
4179+ && ( m_waitingEmitCounter == 0 || m_setPath != m_changedPath )
4180+ )
4181+ {
4182+ removePath(m_setPath);
4183+ ++m_waitingEmitCounter;
4184+ m_changedPath = m_setPath;
4185+ QTimer::singleShot(m_msWaitTime, this, SLOT(slotFireChanges()));
4186+ }
4187+}
4188+
4189+
4190+/*!
4191+ * \brief ExternalFSWatcher::slotFireChanges() emits \ref pathModified() only when it is sure
4192+ * that the current path was changed.
4193+ *
4194+ * A change for the current path (the last current) MUST be notified at least once.
4195+ */
4196+void ExternalFSWatcher::slotFireChanges()
4197+{
4198+ if (--m_waitingEmitCounter == 0)
4199+ {
4200+ addPath(m_setPath);
4201+ if (m_setPath == m_changedPath)
4202+ {
4203+ emit pathModified();
4204+#if DEBUG_EXT_FS_WATCHER
4205+ DEBUG_FSWATCHER() << "emit pathModified()";
4206+#endif
4207+ }
4208+ }
4209+}
4210+
4211+
4212+
4213+void ExternalFSWatcher::setIntervalToNotifyChanges(int ms)
4214+{
4215+ m_msWaitTime = ms;
4216+}
4217+
4218+
4219+int ExternalFSWatcher::getIntervalToNotifyChanges() const
4220+{
4221+ return m_msWaitTime;
4222+}
4223
4224=== added file 'src/plugin/folderlistmodel/externalfswatcher.h'
4225--- src/plugin/folderlistmodel/externalfswatcher.h 1970-01-01 00:00:00 +0000
4226+++ src/plugin/folderlistmodel/externalfswatcher.h 2014-04-15 08:19:22 +0000
4227@@ -0,0 +1,68 @@
4228+/**************************************************************************
4229+ *
4230+ * Copyright 2013 Canonical Ltd.
4231+ * Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
4232+ *
4233+ * This program is free software; you can redistribute it and/or modify
4234+ * it under the terms of the GNU Lesser General Public License as published by
4235+ * the Free Software Foundation; version 3.
4236+ *
4237+ * This program is distributed in the hope that it will be useful,
4238+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4239+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4240+ * GNU Lesser General Public License for more details.
4241+ *
4242+ * You should have received a copy of the GNU Lesser General Public License
4243+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4244+ *
4245+ * File: externalfswatcher.h
4246+ * Date: 9/14/2013
4247+ */
4248+
4249+#ifndef EXTERNALFSWATCHER_H
4250+#define EXTERNALFSWATCHER_H
4251+
4252+#include <QFileSystemWatcher>
4253+
4254+#define DEFAULT_NOTICATION_PERIOD 500
4255+
4256+
4257+/*!
4258+ * \brief The ExternalFSWatcher class notifies the owner when the File System when the current path \a m_setPath has changed
4259+ * emitting pathModified() signal.
4260+ *
4261+ * The current path \a m_setPath is set by using the slot \ref setCurrentPath()
4262+ *
4263+ * The idea of this class is to minimize notifications as the current path can change quickly.
4264+ * A notification will occur if it was requested for a path and this path is still the current at the moment
4265+ * of the notification.
4266+ *
4267+ * Once it detects a change it will wait \ref getIntervalToNotifyChanges() milliseconds to notify that change.
4268+ * At this moment it checks if no \ref setCurrentPath() has been called during this time and then notifies that change.
4269+ */
4270+class ExternalFSWatcher : public QFileSystemWatcher
4271+{
4272+ Q_OBJECT
4273+public:
4274+ explicit ExternalFSWatcher(QObject *parent = 0);
4275+ int getIntervalToNotifyChanges() const;
4276+
4277+signals:
4278+ void pathModified();
4279+
4280+ public slots:
4281+ void setCurrentPath(const QString& curPath);
4282+ void setIntervalToNotifyChanges(int ms);
4283+
4284+private slots:
4285+ void slotDirChanged(const QString&);
4286+ void slotFireChanges();
4287+
4288+ private:
4289+ QString m_setPath;
4290+ QString m_changedPath;
4291+ unsigned m_waitingEmitCounter;
4292+ int m_msWaitTime;
4293+};
4294+
4295+#endif // EXTERNALFSWATCHER_H
4296
4297=== added file 'src/plugin/folderlistmodel/filecompare.cpp'
4298--- src/plugin/folderlistmodel/filecompare.cpp 1970-01-01 00:00:00 +0000
4299+++ src/plugin/folderlistmodel/filecompare.cpp 2014-04-15 08:19:22 +0000
4300@@ -0,0 +1,108 @@
4301+/**************************************************************************
4302+ *
4303+ * Copyright 2013 Canonical Ltd.
4304+ * Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
4305+ *
4306+ * You may use this file under the terms of the BSD license as follows:
4307+ *
4308+ * "Redistribution and use in source and binary forms, with or without
4309+ * modification, are permitted provided that the following conditions are
4310+ * met:
4311+ * * Redistributions of source code must retain the above copyright
4312+ * notice, this list of conditions and the following disclaimer.
4313+ * * Redistributions in binary form must reproduce the above copyright
4314+ * notice, this list of conditions and the following disclaimer in
4315+ * the documentation and/or other materials provided with the
4316+ * distribution.
4317+ * * Neither the name of Nemo Mobile nor the names of its contributors
4318+ * may be used to endorse or promote products derived from this
4319+ * software without specific prior written permission.
4320+ *
4321+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4322+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4323+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
4324+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
4325+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4326+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
4327+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4328+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4329+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4330+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4331+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
4332+ *
4333+ * File: filecompare.cpp
4334+ * Date: 6/25/2013
4335+ */
4336+
4337+#include "filecompare.h"
4338+#include "diriteminfo.h"
4339+#include <QString>
4340+#include <QDateTime>
4341+#include <QDebug>
4342+
4343+
4344+
4345+bool fileCompareExists(const DirItemInfo &a, const DirItemInfo &b)
4346+{
4347+ if (a.isDir() && !b.isDir())
4348+ return true;
4349+
4350+ if (b.isDir() && !a.isDir())
4351+ return false;
4352+
4353+ bool ret = QString::localeAwareCompare(a.fileName(), b.fileName()) < 0;
4354+#if DEBUG_MESSAGES
4355+ qDebug() << Q_FUNC_INFO << ret << a.fileName() << b.fileName();
4356+#endif
4357+
4358+ return ret;
4359+}
4360+
4361+
4362+bool fileCompareAscending(const DirItemInfo &a, const DirItemInfo &b)
4363+{
4364+ if (a.isDir() && !b.isDir())
4365+ return true;
4366+
4367+ if (b.isDir() && !a.isDir())
4368+ return false;
4369+
4370+ return QString::localeAwareCompare(a.fileName(), b.fileName()) < 0;
4371+}
4372+
4373+
4374+bool fileCompareDescending(const DirItemInfo &a, const DirItemInfo &b)
4375+{
4376+ if (a.isDir() && !b.isDir())
4377+ return true;
4378+
4379+ if (b.isDir() && !a.isDir())
4380+ return false;
4381+
4382+ return QString::localeAwareCompare(a.fileName(), b.fileName()) > 0;
4383+}
4384+
4385+
4386+bool dateCompareDescending(const DirItemInfo &a, const DirItemInfo &b)
4387+{
4388+ if (a.isDir() && !b.isDir())
4389+ return true;
4390+
4391+ if (b.isDir() && !a.isDir())
4392+ return false;
4393+
4394+ return a.lastModified() > b.lastModified();
4395+}
4396+
4397+
4398+bool dateCompareAscending(const DirItemInfo &a, const DirItemInfo &b)
4399+{
4400+ if (a.isDir() && !b.isDir())
4401+ return true;
4402+
4403+ if (b.isDir() && !a.isDir())
4404+ return false;
4405+
4406+ return a.lastModified() < b.lastModified();
4407+}
4408+
4409
4410=== added file 'src/plugin/folderlistmodel/filecompare.h'
4411--- src/plugin/folderlistmodel/filecompare.h 1970-01-01 00:00:00 +0000
4412+++ src/plugin/folderlistmodel/filecompare.h 2014-04-15 08:19:22 +0000
4413@@ -0,0 +1,51 @@
4414+/**************************************************************************
4415+ *
4416+ * Copyright 2013 Canonical Ltd.
4417+ * Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
4418+ *
4419+ * You may use this file under the terms of the BSD license as follows:
4420+ *
4421+ * "Redistribution and use in source and binary forms, with or without
4422+ * modification, are permitted provided that the following conditions are
4423+ * met:
4424+ * * Redistributions of source code must retain the above copyright
4425+ * notice, this list of conditions and the following disclaimer.
4426+ * * Redistributions in binary form must reproduce the above copyright
4427+ * notice, this list of conditions and the following disclaimer in
4428+ * the documentation and/or other materials provided with the
4429+ * distribution.
4430+ * * Neither the name of Nemo Mobile nor the names of its contributors
4431+ * may be used to endorse or promote products derived from this
4432+ * software without specific prior written permission.
4433+ *
4434+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4435+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4436+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
4437+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
4438+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4439+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
4440+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4441+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4442+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4443+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4444+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
4445+ *
4446+ * File: filecompare.h
4447+ * Date: 6/25/2013
4448+ */
4449+
4450+#ifndef FILECOMPARE_H
4451+#define FILECOMPARE_H
4452+
4453+class DirItemInfo;
4454+
4455+typedef bool (*CompareFunction)(const DirItemInfo &a, const DirItemInfo &b);
4456+
4457+bool fileCompareExists(const DirItemInfo &a, const DirItemInfo &b);
4458+bool fileCompareAscending(const DirItemInfo &a, const DirItemInfo &b);
4459+bool fileCompareDescending(const DirItemInfo &a, const DirItemInfo &b);
4460+
4461+bool dateCompareDescending(const DirItemInfo &a, const DirItemInfo &b);
4462+bool dateCompareAscending(const DirItemInfo &a, const DirItemInfo &b);
4463+
4464+#endif // FILECOMPARE_H
4465
4466=== added file 'src/plugin/folderlistmodel/filesystemaction.cpp'
4467--- src/plugin/folderlistmodel/filesystemaction.cpp 1970-01-01 00:00:00 +0000
4468+++ src/plugin/folderlistmodel/filesystemaction.cpp 2014-04-15 08:19:22 +0000
4469@@ -0,0 +1,1302 @@
4470+/**************************************************************************
4471+ *
4472+ * Copyright 2013 Canonical Ltd.
4473+ * Copyright 2013 Carlos J Mazieri <carlos.mazieri@gmail.com>
4474+ *
4475+ * You may use this file under the terms of the BSD license as follows:
4476+ *
4477+ * "Redistribution and use in source and binary forms, with or without
4478+ * modification, are permitted provided that the following conditions are
4479+ * met:
4480+ * * Redistributions of source code must retain the above copyright
4481+ * notice, this list of conditions and the following disclaimer.
4482+ * * Redistributions in binary form must reproduce the above copyright
4483+ * notice, this list of conditions and the following disclaimer in
4484+ * the documentation and/or other materials provided with the
4485+ * distribution.
4486+ * * Neither the name of Nemo Mobile nor the names of its contributors
4487+ * may be used to endorse or promote products derived from this
4488+ * software without specific prior written permission.
4489+ *
4490+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4491+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
4492+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
4493+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
4494+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
4495+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
4496+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
4497+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
4498+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
4499+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
4500+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
4501+ *
4502+ * File: filesystemaction.cpp
4503+ * Date: 3/13/2013
4504+ */
4505+
4506+#include "filesystemaction.h"
4507+#include "clipboard.h"
4508+
4509+#if defined(Q_OS_UNIX)
4510+#include <sys/statvfs.h>
4511+#endif
4512+
4513+#include <errno.h>
4514+
4515+#include <QDirIterator>
4516+#include <QDebug>
4517+#include <QTimer>
4518+#include <QFileInfo>
4519+#include <QDir>
4520+#include <QThread>
4521+#include <QTemporaryFile>
4522+
4523+/*!
4524+ * number of the files to work on a step, when this number is reached a signal is emitted
4525+ */
4526+#define STEP_FILES 5
4527+
4528+/*!
4529+ * buffer size to to single read/write operation
4530+ */
4531+#define COPY_BUFFER_SIZE 4096
4532+
4533+/*!
4534+ * Auxiliar Actions do not emit progress() signal
4535+ * \sa moveDirToTempAndRemoveItLater()
4536+ */
4537+#define SHOULD_EMIT_PROGRESS_SIGNAL(action) (!action->isAux)
4538+
4539+#define COMMON_SIZE_ITEM 120
4540+
4541+
4542+
4543+
4544+void FileSystemAction::CopyFile::clear()
4545+{
4546+ bytesWritten = 0;
4547+ if (source) delete source;
4548+ if (target) delete target;
4549+ source = 0;
4550+ target = 0;
4551+}
4552+
4553+
4554+
4555+//===============================================================================================
4556+/*!
4557+ * \brief FileSystemAction::FileSystemAction
4558+ * \param parent
4559+ */
4560+FileSystemAction::FileSystemAction(QObject *parent) :
4561+ QObject(parent)
4562+ , m_curAction(0)
4563+ , m_cancelCurrentAction(false)
4564+ , m_busy(false)
4565+ , m_clipboardChanged(false)
4566+{
4567+
4568+}
4569+
4570+//===============================================================================================
4571+/*!
4572+ * \brief FileSystemAction::~FileSystemAction
4573+ */
4574+FileSystemAction::~FileSystemAction()
4575+{
4576+
4577+}
4578+
4579+//===============================================================================================
4580+/*!
4581+ * \brief FileSystemAction::remove
4582+ * \param paths
4583+ */
4584+void FileSystemAction::remove(const QStringList &paths)
4585+{
4586+ createAndProcessAction(ActionRemove, paths);
4587+}
4588+
4589+//===============================================================================================
4590+/*!
4591+ * \brief FileSystemAction::createAction
4592+ * \param type
4593+ * \param origBase
4594+ * \return
4595+ */
4596+FileSystemAction::Action* FileSystemAction::createAction(ActionType type, int origBase)
4597+{
4598+ Action * action = new Action();
4599+ action->type = type;
4600+ action->baseOrigSize = origBase;
4601+ action->targetPath = m_path;
4602+ action->totalItems = 0;
4603+ action->currItem = 0;
4604+ action->currEntryIndex = 0;
4605+ action->totalBytes = 0;
4606+ action->bytesWritten = 0;
4607+ action->done = false;
4608+ action->auxAction = 0;
4609+ action->isAux = false;
4610+ action->currEntry = 0;
4611+ action->steps = 1;
4612+
4613+ return action;
4614+}
4615+
4616+//===============================================================================================
4617+/*!
4618+ * \brief FileSystemAction::addEntry
4619+ * \param action
4620+ * \param pathname
4621+ */
4622+void FileSystemAction::addEntry(Action* action, const QString& pathname)
4623+{
4624+#if DEBUG_MESSAGES
4625+ qDebug() << Q_FUNC_INFO << pathname;
4626+#endif
4627+ DirItemInfo info(pathname);
4628+ if (!info.isAbsolute())
4629+ {
4630+ info.setFile(action->targetPath, pathname);
4631+ }
4632+ if (!info.exists())
4633+ {
4634+ emit error(QObject::tr("File or Directory does not exist"),
4635+ pathname + QObject::tr(" does not exist")
4636+ );
4637+ return;
4638+ }
4639+ ActionEntry * entry = new ActionEntry();
4640+ //this is the item being handled
4641+ entry->reversedOrder.append(info);
4642+ // verify if the destination item already exists
4643+ if (action->type == ActionCopy ||
4644+ action->type == ActionMove ||
4645+ action->type == ActionHardMoveCopy)
4646+ {
4647+ DirItemInfo destination(targetFom(info.absoluteFilePath(), action));
4648+ entry->alreadyExists = destination.exists();
4649+ }
4650+ //ActionMove will perform a rename, so no Directory expanding is necessary
4651+ if (action->type != ActionMove && info.isDir() && !info.isSymLink())
4652+ {
4653+ QDirIterator it(info.absoluteFilePath(),
4654+ QDir::AllEntries | QDir::System |
4655+ QDir::NoDotAndDotDot | QDir::Hidden,
4656+ QDirIterator::Subdirectories);
4657+ while (it.hasNext() && !it.next().isEmpty())
4658+ {
4659+ entry->reversedOrder.prepend(it.fileInfo());
4660+ }
4661+ }
4662+ //set steps and total bytes considering all items in the Entry
4663+ int counter = entry->reversedOrder.count();
4664+ qint64 size = 0;
4665+ int sizeSteps = 0;
4666+ int bufferSize = (COPY_BUFFER_SIZE * STEP_FILES);
4667+ while (counter--)
4668+ {
4669+ const DirItemInfo & item = entry->reversedOrder.at(counter);
4670+ size = (item.isFile() && !item.isDir() && !item.isSymLink()) ?
4671+ item.size() : COMMON_SIZE_ITEM;
4672+ action->totalBytes += size;
4673+ if (action->type == ActionCopy || action->type == ActionHardMoveCopy)
4674+ {
4675+ if ( (sizeSteps = size / bufferSize) )
4676+ {
4677+ if ( !(size % bufferSize) )
4678+ {
4679+ --sizeSteps;
4680+ }
4681+ action->steps += sizeSteps ;
4682+ }
4683+ }
4684+ }
4685+ //set final steps for the Entry based on Items number
4686+ int entrySteps = entry->reversedOrder.count() / STEP_FILES;
4687+ if ( entry->reversedOrder.count() % STEP_FILES) entrySteps++;
4688+ action->steps += entrySteps;
4689+ action->totalItems += entry->reversedOrder.count();
4690+#if DEBUG_MESSAGES
4691+ qDebug() << "entrySteps" << entrySteps << "from entry counter" << entry->reversedOrder.count()
4692+ << "total steps" << action->steps;
4693+#endif
4694+ //now put the Entry in the Action
4695+ action->entries.append(entry);
4696+}
4697+
4698+//===============================================================================================
4699+/*!
4700+ * \brief FileSystemAction::processAction
4701+ */
4702+void FileSystemAction::processAction()
4703+{
4704+ if (m_curAction)
4705+ {
4706+ //it will be ActionHardMoveRemove only when switched from ActionHardMoveCopy
4707+ //in this case the move is done in two steps COPY and REMOVE
4708+ if (m_curAction->type != ActionHardMoveCopy)
4709+ {
4710+ delete m_curAction;
4711+ m_curAction = 0;
4712+ }
4713+ }
4714+ if (!m_curAction && m_queuedActions.count())
4715+ {
4716+ m_curAction = m_queuedActions.at(0);
4717+ m_curAction->currEntry = static_cast<ActionEntry*>
4718+ ( m_curAction->entries.at(0));
4719+ m_queuedActions.remove(0,1);
4720+ }
4721+ if (m_curAction)
4722+ {
4723+#if DEBUG_MESSAGES
4724+ qDebug() << Q_FUNC_INFO << "performing action type" << m_curAction->type;
4725+#endif
4726+ m_busy = true;
4727+ m_cancelCurrentAction = false;
4728+ m_errorMsg.clear();
4729+ m_errorTitle.clear();
4730+ scheduleSlot(SLOT(processActionEntry()));
4731+ if (SHOULD_EMIT_PROGRESS_SIGNAL(m_curAction))
4732+ {
4733+ emit progress(0,m_curAction->totalItems, 0);
4734+ }
4735+ }
4736+ else
4737+ {
4738+ m_busy = false;
4739+ }
4740+}
4741+
4742+
4743+//===============================================================================================
4744+/*!
4745+ * \brief FileSystemAction::processActionEntry
4746+ */
4747+void FileSystemAction::processActionEntry()
4748+{
4749+#if DEBUG_MESSAGES
4750+ qDebug() << Q_FUNC_INFO;
4751+#endif
4752+
4753+ ActionEntry * curEntry = m_curAction->currEntry;
4754+
4755+#if defined(SIMULATE_LONG_ACTION)
4756+ {
4757+ unsigned int delay = SIMULATE_LONG_ACTION;
4758+ if (delay == 1)
4759+ {
4760+ delay = 100; //each (10 * STEP_FILES) files will waits a second
4761+ QThread::currentThread()->wait(delay);
4762+ }
4763+ }
4764+#endif
4765+ if (!m_cancelCurrentAction)
4766+ {
4767+ switch(m_curAction->type)
4768+ {
4769+ case ActionRemove:
4770+ case ActionHardMoveRemove:
4771+ removeEntry(curEntry);
4772+ endActionEntry();
4773+ break;
4774+ case ActionCopy:
4775+ case ActionHardMoveCopy:
4776+ processCopyEntry(); // specially: this is a slot
4777+ break;
4778+ case ActionMove:
4779+ moveEntry(curEntry);
4780+ endActionEntry();
4781+ break;
4782+ }
4783+ }
4784+}
4785+
4786+//===============================================================================================
4787+/*!
4788+ * \brief FileSystemAction::endActionEntry
4789+ */
4790+void FileSystemAction::endActionEntry()
4791+{
4792+#if DEBUG_MESSAGES
4793+ qDebug() << Q_FUNC_INFO;
4794+#endif
4795+ ActionEntry * curEntry = m_curAction->currEntry;
4796+
4797+ // first of all check for any error or a cancel issued by the user
4798+ if (m_cancelCurrentAction)
4799+ {
4800+ if (!m_errorTitle.isEmpty())
4801+ {
4802+ emit error(m_errorTitle, m_errorMsg);
4803+ }
4804+ //it may have other actions to do
4805+ scheduleSlot(SLOT(processAction()));
4806+ return;
4807+ }
4808+ // check if the current entry has finished
4809+ // if so Views need to receive the notification about that
4810+ if (curEntry->currItem == curEntry->reversedOrder.count())
4811+ {
4812+ const DirItemInfo & mainItem = curEntry->reversedOrder.at(curEntry->currItem -1);
4813+ m_curAction->currEntryIndex++;
4814+ switch(m_curAction->type)
4815+ {
4816+ case ActionRemove:
4817+ emit removed(mainItem);
4818+ break;
4819+ case ActionHardMoveRemove: // nothing to do
4820+ break;
4821+ case ActionHardMoveCopy:
4822+ //check if is doing a hard move and the copy part has finished
4823+ //if so switch the action to remove
4824+ if (m_curAction->currEntryIndex == m_curAction->entries.count())
4825+ {
4826+ m_curAction->type = ActionHardMoveRemove;
4827+ m_curAction->currEntryIndex = 0;
4828+ int entryCounter = m_curAction->entries.count();
4829+ ActionEntry * entry;
4830+ while (entryCounter--)
4831+ {
4832+ entry = m_curAction->entries.at(entryCounter);
4833+ entry->currItem = 0;
4834+ entry->currStep = 0;
4835+ }
4836+ }
4837+ case ActionCopy: // ActionHardMoveCopy is also checked here
4838+ case ActionMove:
4839+ {
4840+ QString addedItem = targetFom(mainItem.absoluteFilePath(), m_curAction);
4841+ if (!curEntry->added && !curEntry->alreadyExists)
4842+ {
4843+ emit added(addedItem);
4844+ curEntry->added = true;
4845+ }
4846+ else
4847+ {
4848+ emit changed(DirItemInfo(addedItem));
4849+ }
4850+ }
4851+ break;
4852+ }//switch
4853+
4854+ }//end if (curEntry->currItem == curEntry->reversedOrder.count())
4855+
4856+ if (curEntry->currStep == STEP_FILES)
4857+ {
4858+ curEntry->currStep = 0;
4859+ }
4860+
4861+ int percent = notifyProgress();
4862+ //Check if the current action has finished or cancelled
4863+ if (m_cancelCurrentAction ||
4864+ m_curAction->currEntryIndex == m_curAction->entries.count())
4865+ {
4866+ if (!m_cancelCurrentAction)
4867+ {
4868+ endCurrentAction();
4869+ if (percent < 100)
4870+ {
4871+ notifyProgress(100);
4872+ }
4873+ }
4874+ //it may have other actions to do
4875+ scheduleSlot(SLOT(processAction()));
4876+ }
4877+ else
4878+ {
4879+ m_curAction->currEntry = static_cast<ActionEntry*>
4880+ ( m_curAction->entries.at(m_curAction->currEntryIndex) );
4881+ //keep working on current Action maybe more entries
4882+ scheduleSlot(SLOT(processActionEntry()));
4883+ }
4884+}
4885+
4886+//===============================================================================================
4887+/*!
4888+ * \brief FileSystemAction::cancel
4889+ */
4890+void FileSystemAction::cancel()
4891+{
4892+ m_cancelCurrentAction = true;
4893+}
4894+
4895+//===============================================================================================
4896+/*!
4897+ * \brief FileSystemAction::removeEntry
4898+ * \param entry
4899+ */
4900+void FileSystemAction::removeEntry(ActionEntry *entry)
4901+{
4902+ QDir dir;
4903+ //do one step at least
4904+ for(; !m_cancelCurrentAction &&
4905+ entry->currStep < STEP_FILES &&
4906+ m_curAction->currItem < m_curAction->totalItems &&
4907+ entry->currItem < entry->reversedOrder.count()
4908+ ; entry->currStep++, m_curAction->currItem++, entry->currItem++
4909+ )
4910+
4911+ {
4912+ const DirItemInfo &fi = entry->reversedOrder.at(entry->currItem);
4913+ if (fi.isDir() && !fi.isSymLink())
4914+ {
4915+ m_cancelCurrentAction = !dir.rmdir(fi.absoluteFilePath());
4916+ }
4917+ else
4918+ {
4919+ m_cancelCurrentAction = !QFile::remove(fi.absoluteFilePath());
4920+ }
4921+#if DEBUG_REMOVE
4922+ qDebug() << Q_FUNC_INFO << "remove ret=" << !m_cancelCurrentAction << fi.absoluteFilePath();
4923+#endif
4924+ if (m_cancelCurrentAction)
4925+ {
4926+ m_errorTitle = QObject::tr("Could not remove the item ") +
4927+ fi.absoluteFilePath();
4928+ m_errorMsg = ::strerror(errno);
4929+ }
4930+ }
4931+}
4932+
4933+
4934+//===============================================================================================
4935+/*!
4936+ * \brief FileSystemAction::copyEntry
4937+ * \param entry
4938+ */
4939+void FileSystemAction::processCopyEntry()
4940+{
4941+ ActionEntry * entry = m_curAction->currEntry;
4942+
4943+#if DEBUG_MESSAGES
4944+ qDebug() << Q_FUNC_INFO << "processing"
4945+ << entry->reversedOrder.at(entry->reversedOrder.count() -1).absoluteFilePath();
4946+#endif
4947+ /*
4948+ * This flag will be true when processCopySingleFile() has put any slot in the execution queue
4949+ * it will work to stop the loop.
4950+ * Later processCopyEntry() will be called again to continue working
4951+ */
4952+ bool scheduleAnySlot = false;
4953+
4954+ //first item from an Entry,
4955+ if (entry->currItem == 0 && entry->alreadyExists && entry->newName == 0)
4956+ {
4957+ //making backup only if the targetpath == origPath, otherwise the item is overwritten
4958+ if (m_curAction->targetPath == m_curAction->origPath)
4959+ {
4960+ //it will check again if the target exists
4961+ //if so, sets the entry->newName
4962+ //then targetFom() will use entry->newName for
4963+ // sub items in the Entry if the Entry is a directory
4964+ if (!makeBackupNameForCurrentItem(m_curAction) )
4965+ {
4966+ m_cancelCurrentAction = true;
4967+ m_errorTitle = QObject::tr("Could not find a suitable name to backup");
4968+ m_errorMsg = entry->reversedOrder.at(
4969+ entry->reversedOrder.count() -1
4970+ ).absoluteFilePath();
4971+ }
4972+ }
4973+#if DEBUG_MESSAGES
4974+ else
4975+ {
4976+ qDebug() << entry->reversedOrder.at(entry->reversedOrder.count() -1).absoluteFilePath()
4977+ << " already exists and will be overwritten";
4978+ }
4979+#endif
4980+ }
4981+
4982+ for(; !m_cancelCurrentAction && !scheduleAnySlot &&
4983+ entry->currStep < STEP_FILES &&
4984+ m_curAction->currItem < m_curAction->totalItems &&
4985+ entry->currItem < entry->reversedOrder.count()
4986+ ; entry->currStep++, entry->currItem++
4987+ )
4988+
4989+ {
4990+ const DirItemInfo &fi = entry->reversedOrder.at(entry->currItem);
4991+ QString orig = fi.absoluteFilePath();
4992+ QString target = targetFom(orig, m_curAction);
4993+ QString path(target);
4994+ // do this here to allow progress send right item number, copySingleFile will emit progress()
4995+ m_curAction->currItem++;
4996+ //--
4997+ if (fi.isFile() || fi.isSymLink())
4998+ {
4999+ DirItemInfo t(target);
5000+ path = t.path();
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches