Merge lp:~michael-sheldon/webbrowser-app/implement-download-folder into lp:webbrowser-app
- implement-download-folder
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Olivier Tilloy | ||||
Approved revision: | 1224 | ||||
Merged at revision: | 1301 | ||||
Proposed branch: | lp:~michael-sheldon/webbrowser-app/implement-download-folder | ||||
Merge into: | lp:webbrowser-app | ||||
Diff against target: |
3136 lines (+2222/-266) 40 files modified
debian/control (+0/-1) debian/webbrowser-app-apparmor.manifest (+5/-2) debian/webbrowser-app.install (+1/-0) src/app/ContentDownloadDialog.qml (+0/-50) src/app/ContentPickerDialog.qml (+0/-97) src/app/ContentShareDialog.qml (+1/-1) src/app/Downloader.qml (+17/-12) src/app/FilePickerDialog.qml (+0/-45) src/app/MimeTypeMapper.js (+20/-0) src/app/Share.qml (+1/-1) src/app/WebViewImpl.qml (+13/-13) src/app/mime-database.cpp (+30/-0) src/app/mime-database.h (+3/-0) src/app/webbrowser/Browser.qml (+133/-24) src/app/webbrowser/BrowserPageHeader.qml (+27/-12) src/app/webbrowser/CMakeLists.txt (+6/-0) src/app/webbrowser/ContentDownloadDialog.qml (+159/-0) src/app/webbrowser/ContentHandler.qml (+35/-0) src/app/webbrowser/ContentPickerDialog.qml (+110/-0) src/app/webbrowser/DownloadDelegate.qml (+226/-0) src/app/webbrowser/DownloadHandler.qml (+45/-0) src/app/webbrowser/DownloadsModel.qml (+24/-0) src/app/webbrowser/DownloadsPage.qml (+253/-0) src/app/webbrowser/IndeterminateProgressBar.qml (+51/-0) src/app/webbrowser/SettingsPage.qml (+4/-4) src/app/webbrowser/downloads-model.cpp (+412/-0) src/app/webbrowser/downloads-model.h (+110/-0) src/app/webbrowser/webbrowser-app-content-hub.json (+5/-0) src/app/webbrowser/webbrowser-app.cpp (+2/-0) src/app/webcontainer/ContentDownloadDialog.qml (+67/-0) src/app/webcontainer/ContentPickerDialog.qml (+96/-0) src/app/webcontainer/WebViewImplOxide.qml (+25/-0) tests/autopilot/webbrowser_app/emulators/browser.py (+37/-2) tests/autopilot/webbrowser_app/tests/__init__.py (+9/-0) tests/autopilot/webbrowser_app/tests/http_server.py (+12/-0) tests/autopilot/webbrowser_app/tests/test_downloads.py (+77/-0) tests/autopilot/webbrowser_app/tests/test_settings.py (+2/-2) tests/unittests/CMakeLists.txt (+1/-0) tests/unittests/downloads-model/CMakeLists.txt (+13/-0) tests/unittests/downloads-model/tst_DownloadsModelTests.cpp (+190/-0) |
||||
To merge this branch: | bzr merge lp:~michael-sheldon/webbrowser-app/implement-download-folder | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Olivier Tilloy | Approve | ||
PS Jenkins bot | continuous-integration | Needs Fixing | |
Review via email: mp+268815@code.launchpad.net |
Commit message
Add support for handling downloads internally within the browser.
Description of the change
Add support for handling downloads internally within the browser.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 1123. By Michael Sheldon
-
Only display 'Choose aplication' button in download dialog when we have an application that can open it installed
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1123
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1124. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1124
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1125. By Michael Sheldon
-
Remove unused import of urlManagement.js
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1125
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1126. By Michael Sheldon
-
Attempt to find mimetype from filename if a generic octet-stream mimetype is provided
- 1127. By Michael Sheldon
-
Move mimetype database querying functions out of DownloadsModel and into new MimeDatabase class
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1127
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1128. By Michael Sheldon
-
Fix focus when showing downloads page
- 1129. By Michael Sheldon
-
Update tests to use renamed BrowserPageHeader instead of SettingsPageHeader
- 1130. By Michael Sheldon
-
Implement download selection
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1130
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1131. By Michael Sheldon
-
Implement QSortFilterProx
yModel for DownloadsModel based on Mimetypes so we only show files matching the requested content type when performing content-hub transfers - 1132. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1132
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1133. By Michael Sheldon
-
Handle the case where the user wishes to upload one of their previous downloads internally within the browser
- 1134. By Michael Sheldon
-
Fix check for whether we're in the browser or a webapp when performing an internal file upload
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1133
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1134
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1135. By Michael Sheldon
-
Fix single item selection from downloads page
- 1136. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1136
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1137. By Michael Sheldon
-
Fix typo
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1137
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1138. By Michael Sheldon
-
Capitalize mimetype names
- 1139. By Michael Sheldon
-
Fix support for downloads in webapps
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1138
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1139
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1140. By Michael Sheldon
-
Add tests for new download dialog
- 1141. By Michael Sheldon
-
Add support for selection mode and multiple delete on downloads page and provide generic action support to BrowserPageHeader
- 1142. By Michael Sheldon
-
Fix flake8 tests
- 1143. By Michael Sheldon
-
Merge from trunk
- 1144. By Michael Sheldon
-
Fix flake8 tests
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1141
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1144
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1145. By Michael Sheldon
-
Add text/vcard to content-hub mimetype mappings
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1145
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1146. By Michael Sheldon
-
Add basic empty state to downloads page
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1146
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1147. By Michael Sheldon
-
Fallback to generic icons for mimetypes that don't have an icon in the current theme
- 1148. By Michael Sheldon
-
Show filename in download dialog
- 1149. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1147
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1150. By Michael Sheldon
-
Remove unused signal from BrowserPageHeader and update docs
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1149
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1150
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1151. By Michael Sheldon
-
Bring download dialog style closer to visual spec
- 1152. By Michael Sheldon
-
Fix download icon fallback when the system theme doesn't have a generic icon
- 1153. By Michael Sheldon
-
Update download dialog style based on visual spec
- 1154. By Michael Sheldon
-
Only show internal browser downloads on the downloads page
- 1155. By Michael Sheldon
-
Remove ubuntu shape from download page icons to match visual spec
- 1156. By Michael Sheldon
-
Remove unused extension property from downloads model
- 1157. By Michael Sheldon
-
Use Images instead of Icons in DownloadDelegate for faster load time and smoother list scrolling
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1154
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1157
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1158. By Michael Sheldon
-
Only show download dialog description when content-hub has an application that can open the download directly
- 1159. By Michael Sheldon
-
Calculate download filename prior to setting metadata title
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1158
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1159
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1160. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1160
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1161. By Michael Sheldon
-
Fix merge error
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1161
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1162. By Michael Sheldon
-
Remove unecessary check for -x-generic icon now that we provide a fallback in the iconForMimetype method when this isn't available in the theme
- 1163. By Michael Sheldon
-
Add edit button to downloads page
- 1164. By Michael Sheldon
-
Fix eliding and spacing on DownloadDelegate labels
- 1165. By Michael Sheldon
-
Update DownloadDelegate label sizes to match visual design
- 1166. By Michael Sheldon
-
Update BrowserPageHeader and DownloadsPage styles to match visual spec
- 1167. By Michael Sheldon
-
Fix multi-selection on DownloadsPage when using the edit button
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1166
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1167
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1168. By Michael Sheldon
-
Fetch download items from database in batches instead of loading all at once
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1168
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1169. By Michael Sheldon
-
Take the user directly to the downloads page when a download starts
- 1170. By Michael Sheldon
-
Fix thumbnailing on download page whilst downloading videos or images
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1170
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
FAILURE: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1171. By Michael Sheldon
-
Add progress bar and controls for downloads (not yet connected to UDM)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1171
http://
Executed test runs:
UNSTABLE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1172. By Michael Sheldon
-
Merge from trunk
- 1173. By Michael Sheldon
-
Connect progress and cancel buttons to running downloads
- 1174. By Michael Sheldon
-
Avoid duplication of new downloads at both the bottom and top of the downloads list
- 1175. By Michael Sheldon
-
Remove downloads that have been cancelled while the browser was closed
- 1176. By Michael Sheldon
-
Add incomplete downloads to the download model when restoring database on startup
- 1177. By Michael Sheldon
-
Allow access to ~/Downloads in the app armor manifest
- 1178. By Michael Sheldon
-
Switch to default apparmor template to allow for thumbnailer support
- 1179. By Michael Sheldon
-
Move downloads based on signal from global DownloadManager object, so we can handle downloads that finished after an app restart
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1179
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1180. By Michael Sheldon
-
Merge from trunk
- 1181. By Michael Sheldon
-
Fix typo in app armor profile
- 1182. By Michael Sheldon
-
Fix usage of renamed SettingsPageHeader -> BrowserPageHeader
- 1183. By Michael Sheldon
-
Fix download model index position when removing downloads
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1183
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1184. By Michael Sheldon
-
Display error messages for failed downloads
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1184
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1185. By Michael Sheldon
-
Don't display delete action on downloads that are still in progress
- 1186. By Michael Sheldon
-
Update error display on downloads page to match visual design
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1185
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1186
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1187. By Michael Sheldon
-
Remove progress storage from download database as this is now available via the UDM downloads model
- 1188. By Michael Sheldon
-
Override server mimetype with detected mimetype for downloads when they complete
- 1189. By Michael Sheldon
-
Merge from trunk
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1189
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
Click here to trigger a rebuild:
http://
- 1190. By Michael Sheldon
-
Update UDM import version
- 1191. By Michael Sheldon
-
Allow the browser to handle conditions where a download has been paused, cancelled or resumed by another process (e.g. by the transfer indicator)
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1191
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1192. By Michael Sheldon
-
Fix download tests
- 1193. By Michael Sheldon
-
Fix flake8 failures
- 1194. By Michael Sheldon
-
Only allow downloads on non-desktop form factors
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1193
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1194
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1195. By Michael Sheldon
-
Use success of download Loader to determine whether UDM bindings are available on this platform
- 1196. By Michael Sheldon
-
Merge from trunk
- 1197. By Michael Sheldon
-
Set download menu option enabled property instead of visibile property when UDM isn't available
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1195
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1197
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 1198. By Michael Sheldon
-
Add copyright notice to DownloadHandler
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1198
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1199. By Michael Sheldon
-
Merge from trunk
- 1200. By Michael Sheldon
-
Add 'Ctrl-J' as a shortcut to the downloads page and disable shortcuts whilst the download page is visible
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1200
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
Can the autopilot tests detect at run time whether the platform supports downloads, and be skipped where relevant (typically a desktop for udm is not installed)?
What (and how) does the test [typeof(webapp) == "undefined"] check exactly?
In src/app/
var downloadPage = browser.
Can’t we have the dialog emit a signal with the necessary parameters, which WebViewImpl would connect to and forward, and Browser would connect to that signal from the webview and act accordingly?
The same applies to Downloader.qml.
In mimeTypeRegexFo
And for documents, shouldn’t it accept the mimetypes listed at https:/
In MimeDatabase:
In MimeDatabase:
In Browser.qml, version 1.0 of Ubuntu.Content is imported. There are other places in the code where version 0.1 is being imported. Can this be made consistent?
On a related note, if Browser.qml imports Ubuntu.Content, then a runtime dependency on qtdeclarative5-
DownloadDelegat
In the file picker when uploading a file, the "select all" button should be disable/not visible if multiple files are not allowed. And how do I actually pick a file to upload? I’m seeing the selection mode, I can tick the checkbox for one given file, but how do I validate (there’s only the selectAll and delete actions in the header)? If there isn’t one already, an autopilot test to validate this use case would be good to have.
Could the DownloadsMimety
Ideally, the DownloadsModel class should be unit tested, with a decent test coverage. Let’s not block on this, but let’s also plan on adding the missing tests soon. Or as a temporary trade-off, can you...
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1200
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1201. By Michael Sheldon
-
Reuse existing QMimeDatabase instead of instantiating new ones
- 1202. By Michael Sheldon
-
Return empty string if we don't have a mimetype icon and replace with the 'save' icon on the caller side
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1202
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1203. By Michael Sheldon
-
Replace DownloadsMimety
peModel with a SortFilterModel
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1203
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1204. By Michael Sheldon
-
Fix internal content picking
- 1205. By Michael Sheldon
-
Expand mimetype regexes for contacts and documents to cover all supported cases
- 1206. By Michael Sheldon
-
Consistently import the same version of the content hub bindings
- 1207. By Michael Sheldon
-
Load ContentHub bindings for export handling dynamically to allow for systems where ContentHub isn't installed
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1207
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1208. By Michael Sheldon
-
Add unit tests for downloads model
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1208
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
Thanks for adding the unit tests for DownloadsModel. The current coverage is 63.3%, this is not bad but it could easily be improved. The following methods are not covered by the unit tests:
DownloadsModel
DownloadsModel
DownloadsModel
DownloadsModel
DownloadsModel
DownloadsModel
DownloadsModel
DownloadsModel
Also, since this is a list model, please consider monitoring the rowsAdded(), rowsRemoved(), rowsMoved() and dataChanged() signals, and verifying that they are emitted when expected, with the correct parameters.
- 1209. By Michael Sheldon
-
Implement separate ContentDownload
Dialogs for webbrowser and webcontainer
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1209
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
The ContentDownload
The decoupling done at revision 1209 looks good. I suppose you’re aware of it, but just as a reminder to myself: src/app/
- 1210. By Michael Sheldon
-
Split ContentPickerDialog into specific webbrowser and webcontainer implementation and remove FilePickerDialog (no longer functional under confinement)
Olivier Tilloy (osomon) wrote : | # |
In Browser.qml, the condition for the enabled-ness of the keyboard shortcuts could be improved by merging the changes in lp:~osomon/webbrowser-app/keyboard-shortcuts-focus-fixes and ensuring that tabContainer is not visible when downloadsContainer is.
Alternatively, if we’re in a rush to land this, I’ll merge back your changes in my branch after it lands, and will fix it there.
Olivier Tilloy (osomon) wrote : | # |
The runtime dependency on qml-module-
- 1211. By Michael Sheldon
-
Disable download tests on desktop systems
- 1212. By Michael Sheldon
-
Fix autopilot tests for download dialog after parenting change
- 1213. By Michael Sheldon
-
Remove no longer required dependence on qml-module-
qtquick- dialogs
Olivier Tilloy (osomon) wrote : | # |
src/app/
onCancelPre
Instead, you should do:
onCancelPre
(the 'WebView.view' attached property points to the parent webview).
By the way, is this still needed, and if so shouldn’t it be WebView.
- 1214. By Michael Sheldon
-
Don't show incomplete downloads in picker/selection mode
- 1215. By Michael Sheldon
-
Remove unnecessary focus changes
- 1216. By Michael Sheldon
-
Fix flake8 tests
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1210
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1217. By Michael Sheldon
-
Fix encapsulation for ContentPickerDialog
- 1218. By Michael Sheldon
-
Simplify download delegate visibility condition
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1216
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1218
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
When uploading a file and selecting the browser from the list of peers, nothing happens, and I’m seeing the following error in the logs:
file://
If I add an "import com.canonical.Oxide 1.0" statement at the top of that file, the error becomes:
file://
This is because the "WebView.view" attached property is valid only in the context of the top-level element (picker). Adding a top level [readonly property var webview: WebView.view] property to picker, and referring to it with [var downloadPage = picker.
Olivier Tilloy (osomon) wrote : | # |
Note that with the suggestions above, the following warning is printed out in the logs, but it’s harmless:
QQmlExpression: Expression file://
OxideQQuick
Olivier Tilloy (osomon) wrote : | # |
When showing the downloads page (both from the drawer menu or as a file picker), I’m seeing the following warnings in the console:
file://
file://
file://
Can they be addressed?
Olivier Tilloy (osomon) wrote : | # |
Note: a way to avoid the warning about the non-NOTIFYable property is to not define a top-level property, and instead in onPeerSelected do the following:
var downloadPage = picker.
- 1219. By Michael Sheldon
-
Fix access to downloads page when performing internal uploads
- 1220. By Michael Sheldon
-
Fix warnings from BrowserPageHeader
- 1221. By Michael Sheldon
-
Move all content hub imports to 1.3
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1219
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1221
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1222. By Michael Sheldon
-
Update content hub imports to 1.3
- 1223. By Michael Sheldon
-
Don't destroy SingleDownloads when they finish as they're still refered to by the shared downloads model
- 1224. By Michael Sheldon
-
Fix peer picker autopilot test
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1223
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1225. By Michael Sheldon
-
Fix icon sizing when thumbnailer returns an invalid icon
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1224
http://
Executed test runs:
FAILURE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
FAILURE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Olivier Tilloy (osomon) wrote : | # |
Looks good to me now. I have a bunch of minor comments (on the code itself, no functional issue), but I’ll keep them for a bug report after we manage to land this, let’s not delay this any longer.
Preview Diff
1 | === modified file 'debian/control' | |||
2 | --- debian/control 2015-09-30 09:26:57 +0000 | |||
3 | +++ debian/control 2015-12-16 16:25:40 +0000 | |||
4 | @@ -44,7 +44,6 @@ | |||
5 | 44 | qml-module-qt-labs-folderlistmodel, | 44 | qml-module-qt-labs-folderlistmodel, |
6 | 45 | qml-module-qt-labs-settings, | 45 | qml-module-qt-labs-settings, |
7 | 46 | qml-module-qtquick2 (>= 5.4), | 46 | qml-module-qtquick2 (>= 5.4), |
8 | 47 | qml-module-qtquick-dialogs, | ||
9 | 48 | qml-module-qtquick-window2 (>= 5.3), | 47 | qml-module-qtquick-window2 (>= 5.3), |
10 | 49 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3), | 48 | qtdeclarative5-ubuntu-ui-toolkit-plugin (>= 1.3) | qtdeclarative5-ubuntu-ui-toolkit-plugin-gles (>= 1.3), |
11 | 50 | qtdeclarative5-ubuntu-web-plugin (= ${binary:Version}), | 49 | qtdeclarative5-ubuntu-web-plugin (= ${binary:Version}), |
12 | 51 | 50 | ||
13 | === modified file 'debian/webbrowser-app-apparmor.manifest' | |||
14 | --- debian/webbrowser-app-apparmor.manifest 2015-10-22 15:07:38 +0000 | |||
15 | +++ debian/webbrowser-app-apparmor.manifest 2015-12-16 16:25:40 +0000 | |||
16 | @@ -3,7 +3,6 @@ | |||
17 | 3 | "webbrowser-app": { | 3 | "webbrowser-app": { |
18 | 4 | "binary": "/usr/bin/webbrowser-app", | 4 | "binary": "/usr/bin/webbrowser-app", |
19 | 5 | "profile_name": "webbrowser-app", | 5 | "profile_name": "webbrowser-app", |
20 | 6 | "template": "ubuntu-webapp", | ||
21 | 7 | "policy_vendor": "ubuntu", | 6 | "policy_vendor": "ubuntu", |
22 | 8 | "policy_version": 1.3, | 7 | "policy_version": 1.3, |
23 | 9 | "policy_groups": [ | 8 | "policy_groups": [ |
24 | @@ -31,8 +30,12 @@ | |||
25 | 31 | }, | 30 | }, |
26 | 32 | "read_path": [ | 31 | "read_path": [ |
27 | 33 | "/usr/share/applications/", | 32 | "/usr/share/applications/", |
28 | 33 | "/custom/vendor/here/location-provider/consent/*.html", | ||
29 | 34 | "@{HOME}/.local/share/applications/", | 34 | "@{HOME}/.local/share/applications/", |
31 | 35 | "/custom/vendor/here/location-provider/consent/*.html" | 35 | "@{HOME}/Downloads/" |
32 | 36 | ], | ||
33 | 37 | "write_path": [ | ||
34 | 38 | "@{HOME}/Downloads/" | ||
35 | 36 | ] | 39 | ] |
36 | 37 | } | 40 | } |
37 | 38 | } | 41 | } |
38 | 39 | 42 | ||
39 | === modified file 'debian/webbrowser-app.install' | |||
40 | --- debian/webbrowser-app.install 2015-10-05 09:49:21 +0000 | |||
41 | +++ debian/webbrowser-app.install 2015-12-16 16:25:40 +0000 | |||
42 | @@ -7,4 +7,5 @@ | |||
43 | 7 | usr/share/applications/webbrowser-app.desktop | 7 | usr/share/applications/webbrowser-app.desktop |
44 | 8 | usr/share/locale/*/LC_MESSAGES/webbrowser-app.mo | 8 | usr/share/locale/*/LC_MESSAGES/webbrowser-app.mo |
45 | 9 | usr/share/url-dispatcher/ | 9 | usr/share/url-dispatcher/ |
46 | 10 | usr/share/content-hub/peers/webbrowser-app | ||
47 | 10 | debian/usr.bin.webbrowser-app etc/apparmor.d | 11 | debian/usr.bin.webbrowser-app etc/apparmor.d |
48 | 11 | 12 | ||
49 | === removed file 'src/app/ContentDownloadDialog.qml' | |||
50 | --- src/app/ContentDownloadDialog.qml 2015-08-10 15:22:00 +0000 | |||
51 | +++ src/app/ContentDownloadDialog.qml 1970-01-01 00:00:00 +0000 | |||
52 | @@ -1,50 +0,0 @@ | |||
53 | 1 | /* | ||
54 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
55 | 3 | * | ||
56 | 4 | * This file is part of webbrowser-app. | ||
57 | 5 | * | ||
58 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
59 | 7 | * it under the terms of the GNU General Public License as published by | ||
60 | 8 | * the Free Software Foundation; version 3. | ||
61 | 9 | * | ||
62 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
63 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
64 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
65 | 13 | * GNU General Public License for more details. | ||
66 | 14 | * | ||
67 | 15 | * You should have received a copy of the GNU General Public License | ||
68 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
69 | 17 | */ | ||
70 | 18 | |||
71 | 19 | import QtQuick 2.4 | ||
72 | 20 | import Ubuntu.Components 1.3 | ||
73 | 21 | import Ubuntu.Components.Popups 1.3 | ||
74 | 22 | import Ubuntu.Content 0.1 | ||
75 | 23 | |||
76 | 24 | PopupBase { | ||
77 | 25 | id: downloadDialog | ||
78 | 26 | anchors.fill: parent | ||
79 | 27 | property var activeTransfer | ||
80 | 28 | property var downloadId | ||
81 | 29 | property alias contentType: peerPicker.contentType | ||
82 | 30 | |||
83 | 31 | Rectangle { | ||
84 | 32 | anchors.fill: parent | ||
85 | 33 | ContentPeerPicker { | ||
86 | 34 | id: peerPicker | ||
87 | 35 | handler: ContentHandler.Destination | ||
88 | 36 | visible: parent.visible | ||
89 | 37 | |||
90 | 38 | onPeerSelected: { | ||
91 | 39 | activeTransfer = peer.request() | ||
92 | 40 | activeTransfer.downloadId = downloadDialog.downloadId | ||
93 | 41 | activeTransfer.state = ContentTransfer.Downloading | ||
94 | 42 | PopupUtils.close(downloadDialog) | ||
95 | 43 | } | ||
96 | 44 | |||
97 | 45 | onCancelPressed: { | ||
98 | 46 | PopupUtils.close(downloadDialog) | ||
99 | 47 | } | ||
100 | 48 | } | ||
101 | 49 | } | ||
102 | 50 | } | ||
103 | 51 | 0 | ||
104 | === removed file 'src/app/ContentPickerDialog.qml' | |||
105 | --- src/app/ContentPickerDialog.qml 2015-08-18 07:51:11 +0000 | |||
106 | +++ src/app/ContentPickerDialog.qml 1970-01-01 00:00:00 +0000 | |||
107 | @@ -1,97 +0,0 @@ | |||
108 | 1 | /* | ||
109 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
110 | 3 | * | ||
111 | 4 | * This file is part of webbrowser-app. | ||
112 | 5 | * | ||
113 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
114 | 7 | * it under the terms of the GNU General Public License as published by | ||
115 | 8 | * the Free Software Foundation; version 3. | ||
116 | 9 | * | ||
117 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
118 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
119 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
120 | 13 | * GNU General Public License for more details. | ||
121 | 14 | * | ||
122 | 15 | * You should have received a copy of the GNU General Public License | ||
123 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
124 | 17 | */ | ||
125 | 18 | |||
126 | 19 | import QtQuick 2.4 | ||
127 | 20 | import Ubuntu.Components 1.3 | ||
128 | 21 | import Ubuntu.Components.Popups 1.3 as Popups | ||
129 | 22 | import Ubuntu.Content 0.1 | ||
130 | 23 | import "MimeTypeMapper.js" as MimeTypeMapper | ||
131 | 24 | |||
132 | 25 | Component { | ||
133 | 26 | Popups.PopupBase { | ||
134 | 27 | id: picker | ||
135 | 28 | objectName: "contentPickerDialog" | ||
136 | 29 | |||
137 | 30 | // Set the parent at construction time, instead of letting show() | ||
138 | 31 | // set it later on, which for some reason results in the size of | ||
139 | 32 | // the dialog not being updated. | ||
140 | 33 | parent: QuickUtils.rootItem(this) | ||
141 | 34 | |||
142 | 35 | property var activeTransfer | ||
143 | 36 | |||
144 | 37 | Rectangle { | ||
145 | 38 | anchors.fill: parent | ||
146 | 39 | |||
147 | 40 | ContentTransferHint { | ||
148 | 41 | anchors.fill: parent | ||
149 | 42 | activeTransfer: picker.activeTransfer | ||
150 | 43 | } | ||
151 | 44 | |||
152 | 45 | ContentPeerPicker { | ||
153 | 46 | id: peerPicker | ||
154 | 47 | anchors.fill: parent | ||
155 | 48 | visible: true | ||
156 | 49 | contentType: ContentType.All | ||
157 | 50 | handler: ContentHandler.Source | ||
158 | 51 | |||
159 | 52 | onPeerSelected: { | ||
160 | 53 | if (model.allowMultipleFiles) { | ||
161 | 54 | peer.selectionType = ContentTransfer.Multiple | ||
162 | 55 | } else { | ||
163 | 56 | peer.selectionType = ContentTransfer.Single | ||
164 | 57 | } | ||
165 | 58 | picker.activeTransfer = peer.request() | ||
166 | 59 | stateChangeConnection.target = picker.activeTransfer | ||
167 | 60 | } | ||
168 | 61 | |||
169 | 62 | onCancelPressed: { | ||
170 | 63 | webview.focus = true | ||
171 | 64 | model.reject() | ||
172 | 65 | } | ||
173 | 66 | } | ||
174 | 67 | } | ||
175 | 68 | |||
176 | 69 | Connections { | ||
177 | 70 | id: stateChangeConnection | ||
178 | 71 | target: null | ||
179 | 72 | onStateChanged: { | ||
180 | 73 | if (picker.activeTransfer.state === ContentTransfer.Charged) { | ||
181 | 74 | var selectedItems = [] | ||
182 | 75 | for(var i in picker.activeTransfer.items) { | ||
183 | 76 | selectedItems.push(String(picker.activeTransfer.items[i].url).replace("file://", "")) | ||
184 | 77 | } | ||
185 | 78 | model.accept(selectedItems) | ||
186 | 79 | } | ||
187 | 80 | } | ||
188 | 81 | } | ||
189 | 82 | |||
190 | 83 | Component.onCompleted: { | ||
191 | 84 | if(acceptTypes.length === 1) { | ||
192 | 85 | var contentType = MimeTypeMapper.mimeTypeToContentType(acceptTypes[0]) | ||
193 | 86 | if(contentType == ContentType.Unknown) { | ||
194 | 87 | // If we don't recognise the type, allow uploads from any app | ||
195 | 88 | contentType = ContentType.All | ||
196 | 89 | } | ||
197 | 90 | peerPicker.contentType = contentType | ||
198 | 91 | } else { | ||
199 | 92 | peerPicker.contentType = ContentType.All | ||
200 | 93 | } | ||
201 | 94 | show() | ||
202 | 95 | } | ||
203 | 96 | } | ||
204 | 97 | } | ||
205 | 98 | 0 | ||
206 | === modified file 'src/app/ContentShareDialog.qml' | |||
207 | --- src/app/ContentShareDialog.qml 2015-08-10 15:22:00 +0000 | |||
208 | +++ src/app/ContentShareDialog.qml 2015-12-16 16:25:40 +0000 | |||
209 | @@ -19,7 +19,7 @@ | |||
210 | 19 | import QtQuick 2.4 | 19 | import QtQuick 2.4 |
211 | 20 | import Ubuntu.Components 1.3 | 20 | import Ubuntu.Components 1.3 |
212 | 21 | import Ubuntu.Components.Popups 1.3 | 21 | import Ubuntu.Components.Popups 1.3 |
214 | 22 | import Ubuntu.Content 0.1 | 22 | import Ubuntu.Content 1.3 |
215 | 23 | 23 | ||
216 | 24 | PopupBase { | 24 | PopupBase { |
217 | 25 | id: shareDialog | 25 | id: shareDialog |
218 | 26 | 26 | ||
219 | === modified file 'src/app/Downloader.qml' | |||
220 | --- src/app/Downloader.qml 2015-08-10 15:22:00 +0000 | |||
221 | +++ src/app/Downloader.qml 2015-12-16 16:25:40 +0000 | |||
222 | @@ -19,18 +19,18 @@ | |||
223 | 19 | import QtQuick 2.4 | 19 | import QtQuick 2.4 |
224 | 20 | import Ubuntu.Components 1.3 | 20 | import Ubuntu.Components 1.3 |
225 | 21 | import Ubuntu.Components.Popups 1.3 | 21 | import Ubuntu.Components.Popups 1.3 |
228 | 22 | import Ubuntu.DownloadManager 0.1 | 22 | import Ubuntu.DownloadManager 1.2 |
229 | 23 | import Ubuntu.Content 0.1 | 23 | import Ubuntu.Content 1.3 |
230 | 24 | import "MimeTypeMapper.js" as MimeTypeMapper | 24 | import "MimeTypeMapper.js" as MimeTypeMapper |
231 | 25 | import "FileExtensionMapper.js" as FileExtensionMapper | 25 | import "FileExtensionMapper.js" as FileExtensionMapper |
232 | 26 | 26 | ||
233 | 27 | Item { | 27 | Item { |
234 | 28 | id: downloadItem | 28 | id: downloadItem |
235 | 29 | 29 | ||
240 | 30 | Component { | 30 | property string filename |
241 | 31 | id: downloadDialog | 31 | property string mimeType |
242 | 32 | ContentDownloadDialog { } | 32 | |
243 | 33 | } | 33 | signal showDownloadDialog(string downloadId, var contentType, var downloader, string filename, string mimeType) |
244 | 34 | 34 | ||
245 | 35 | Component { | 35 | Component { |
246 | 36 | id: metadataComponent | 36 | id: metadataComponent |
247 | @@ -42,15 +42,13 @@ | |||
248 | 42 | Component { | 42 | Component { |
249 | 43 | id: downloadComponent | 43 | id: downloadComponent |
250 | 44 | SingleDownload { | 44 | SingleDownload { |
251 | 45 | id: downloader | ||
252 | 45 | autoStart: false | 46 | autoStart: false |
253 | 46 | property var contentType | 47 | property var contentType |
254 | 48 | property string url | ||
255 | 49 | |||
256 | 47 | onDownloadIdChanged: { | 50 | onDownloadIdChanged: { |
263 | 48 | PopupUtils.open(downloadDialog, downloadItem, {"contentType" : contentType, "downloadId" : downloadId}) | 51 | showDownloadDialog(downloadId, contentType, downloader, downloadItem.filename, downloadItem.mimeType) |
258 | 49 | } | ||
259 | 50 | |||
260 | 51 | onFinished: { | ||
261 | 52 | metadata.destroy() | ||
262 | 53 | destroy() | ||
264 | 54 | } | 52 | } |
265 | 55 | } | 53 | } |
266 | 56 | } | 54 | } |
267 | @@ -62,11 +60,13 @@ | |||
268 | 62 | singleDownload.headers = headers | 60 | singleDownload.headers = headers |
269 | 63 | } | 61 | } |
270 | 64 | singleDownload.metadata = metadata | 62 | singleDownload.metadata = metadata |
271 | 63 | singleDownload.url = url | ||
272 | 65 | singleDownload.download(url) | 64 | singleDownload.download(url) |
273 | 66 | } | 65 | } |
274 | 67 | 66 | ||
275 | 68 | function downloadPicture(url, headers) { | 67 | function downloadPicture(url, headers) { |
276 | 69 | var metadata = metadataComponent.createObject(downloadItem) | 68 | var metadata = metadataComponent.createObject(downloadItem) |
277 | 69 | downloadItem.mimeType = "image/*" | ||
278 | 70 | download(url, ContentType.Pictures, headers, metadata) | 70 | download(url, ContentType.Pictures, headers, metadata) |
279 | 71 | } | 71 | } |
280 | 72 | 72 | ||
281 | @@ -86,7 +86,12 @@ | |||
282 | 86 | contentType = ContentType.Music | 86 | contentType = ContentType.Music |
283 | 87 | metadata.extract = true | 87 | metadata.extract = true |
284 | 88 | } | 88 | } |
285 | 89 | if (!filename) { | ||
286 | 90 | filename = url.toString().split("/").pop() | ||
287 | 91 | } | ||
288 | 89 | metadata.title = filename | 92 | metadata.title = filename |
289 | 93 | downloadItem.filename = filename | ||
290 | 94 | downloadItem.mimeType = mimeType | ||
291 | 90 | download(url, contentType, headers, metadata) | 95 | download(url, contentType, headers, metadata) |
292 | 91 | } | 96 | } |
293 | 92 | 97 | ||
294 | 93 | 98 | ||
295 | === removed file 'src/app/FilePickerDialog.qml' | |||
296 | --- src/app/FilePickerDialog.qml 2015-08-25 08:36:06 +0000 | |||
297 | +++ src/app/FilePickerDialog.qml 1970-01-01 00:00:00 +0000 | |||
298 | @@ -1,45 +0,0 @@ | |||
299 | 1 | /* | ||
300 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
301 | 3 | * | ||
302 | 4 | * This file is part of webbrowser-app. | ||
303 | 5 | * | ||
304 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
305 | 7 | * it under the terms of the GNU General Public License as published by | ||
306 | 8 | * the Free Software Foundation; version 3. | ||
307 | 9 | * | ||
308 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
309 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
310 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
311 | 13 | * GNU General Public License for more details. | ||
312 | 14 | * | ||
313 | 15 | * You should have received a copy of the GNU General Public License | ||
314 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
315 | 17 | */ | ||
316 | 18 | |||
317 | 19 | import QtQuick 2.4 | ||
318 | 20 | import QtQuick.Dialogs 1.0 | ||
319 | 21 | import Ubuntu.Components 1.3 | ||
320 | 22 | import Ubuntu.Components.Popups 1.3 as Popups | ||
321 | 23 | |||
322 | 24 | Component { | ||
323 | 25 | Popups.Dialog { | ||
324 | 26 | FileDialog { | ||
325 | 27 | id: fileDialog | ||
326 | 28 | title: i18n.tr("Please choose a file") | ||
327 | 29 | selectMultiple: model.allowMultipleFiles | ||
328 | 30 | |||
329 | 31 | onAccepted: { | ||
330 | 32 | var selectedFiles = [] | ||
331 | 33 | for(var i in fileDialog.fileUrls) { | ||
332 | 34 | selectedFiles.push(fileDialog.fileUrls[i].replace("file://", "")) | ||
333 | 35 | } | ||
334 | 36 | model.accept(selectedFiles) | ||
335 | 37 | } | ||
336 | 38 | |||
337 | 39 | onRejected: { | ||
338 | 40 | model.reject() | ||
339 | 41 | } | ||
340 | 42 | Component.onCompleted: visible = true | ||
341 | 43 | } | ||
342 | 44 | } | ||
343 | 45 | } | ||
344 | 46 | 0 | ||
345 | === modified file 'src/app/MimeTypeMapper.js' | |||
346 | --- src/app/MimeTypeMapper.js 2015-09-23 14:00:09 +0000 | |||
347 | +++ src/app/MimeTypeMapper.js 2015-12-16 16:25:40 +0000 | |||
348 | @@ -42,3 +42,23 @@ | |||
349 | 42 | return ContentType.Unknown; | 42 | return ContentType.Unknown; |
350 | 43 | } | 43 | } |
351 | 44 | } | 44 | } |
352 | 45 | |||
353 | 46 | function mimeTypeRegexForContentType(contentType) { | ||
354 | 47 | switch (contentType) { | ||
355 | 48 | case ContentType.Pictures: | ||
356 | 49 | return /image\/.*/; | ||
357 | 50 | case ContentType.Music: | ||
358 | 51 | return /audio\/.*/; | ||
359 | 52 | case ContentType.Videos: | ||
360 | 53 | return /video\/.*/; | ||
361 | 54 | case ContentType.Contacts: | ||
362 | 55 | return /text\/(x-vcard|vcard)/; | ||
363 | 56 | case ContentType.EBooks: | ||
364 | 57 | return /application\/(epub.*|vnd.amazon.ebook|x-mobipocket-ebook|x-fictionbook+xml|x-ms-reader)/; | ||
365 | 58 | case ContentType.Documents: | ||
366 | 59 | return /(text\/.*|application\/pdf|application\/x-pdf|application\/vnd\.pdf|application\/vnd\.oasis\.opendocument.*|application\/msword|application\/vnd\.openxmlformats-officedocument\.wordprocessingml\.document|application\/vnd\.openxmlformats-officedocument\.spreadsheetml\.sheet|application\/vnd\.openxmlformats-officedocument\.presentationml\.presentation|application\/vnd\.ms-excel|application\/vnd\.ms-powerpoint)/; | ||
367 | 60 | case ContentType.Unknown: | ||
368 | 61 | case ContentType.All: | ||
369 | 62 | return /.*/; | ||
370 | 63 | } | ||
371 | 64 | } | ||
372 | 45 | 65 | ||
373 | === modified file 'src/app/Share.qml' | |||
374 | --- src/app/Share.qml 2015-09-01 12:41:13 +0000 | |||
375 | +++ src/app/Share.qml 2015-12-16 16:25:40 +0000 | |||
376 | @@ -19,7 +19,7 @@ | |||
377 | 19 | import QtQuick 2.4 | 19 | import QtQuick 2.4 |
378 | 20 | import Ubuntu.Components 1.3 | 20 | import Ubuntu.Components 1.3 |
379 | 21 | import Ubuntu.Components.Popups 1.3 | 21 | import Ubuntu.Components.Popups 1.3 |
381 | 22 | import Ubuntu.Content 0.1 | 22 | import Ubuntu.Content 1.3 |
382 | 23 | 23 | ||
383 | 24 | Item { | 24 | Item { |
384 | 25 | id: shareItem | 25 | id: shareItem |
385 | 26 | 26 | ||
386 | === modified file 'src/app/WebViewImpl.qml' | |||
387 | --- src/app/WebViewImpl.qml 2015-09-01 07:02:13 +0000 | |||
388 | +++ src/app/WebViewImpl.qml 2015-12-16 16:25:40 +0000 | |||
389 | @@ -34,7 +34,8 @@ | |||
390 | 34 | confirmDialog: ConfirmDialog {} | 34 | confirmDialog: ConfirmDialog {} |
391 | 35 | promptDialog: PromptDialog {} | 35 | promptDialog: PromptDialog {} |
392 | 36 | beforeUnloadDialog: BeforeUnloadDialog {} | 36 | beforeUnloadDialog: BeforeUnloadDialog {} |
394 | 37 | filePicker: filePickerLoader.item | 37 | |
395 | 38 | signal showDownloadDialog(string downloadId, var contentType, var downloader, string filename, string mimeType) | ||
396 | 38 | 39 | ||
397 | 39 | QtObject { | 40 | QtObject { |
398 | 40 | id: internal | 41 | id: internal |
399 | @@ -62,9 +63,10 @@ | |||
400 | 62 | } | 63 | } |
401 | 63 | headers["User-Agent"] = webview.context.userAgent | 64 | headers["User-Agent"] = webview.context.userAgent |
402 | 64 | // Work around https://launchpad.net/bugs/1487090 by guessing the mime type | 65 | // Work around https://launchpad.net/bugs/1487090 by guessing the mime type |
404 | 65 | // from the suggested filename or URL if oxide hasn’t provided one. | 66 | // from the suggested filename or URL if oxide hasn’t provided one, or if |
405 | 67 | // the server has provided the generic application/octet-stream mime type. | ||
406 | 66 | var mimeType = request.mimeType | 68 | var mimeType = request.mimeType |
408 | 67 | if (!mimeType) { | 69 | if (!mimeType || mimeType == "application/octet-stream") { |
409 | 68 | mimeType = MimeDatabase.filenameToMimeType(request.suggestedFilename) | 70 | mimeType = MimeDatabase.filenameToMimeType(request.suggestedFilename) |
410 | 69 | } | 71 | } |
411 | 70 | if (!mimeType) { | 72 | if (!mimeType) { |
412 | @@ -87,20 +89,18 @@ | |||
413 | 87 | } | 89 | } |
414 | 88 | 90 | ||
415 | 89 | Loader { | 91 | Loader { |
416 | 90 | id: filePickerLoader | ||
417 | 91 | source: formFactor == "desktop" ? "FilePickerDialog.qml" : "ContentPickerDialog.qml" | ||
418 | 92 | asynchronous: true | ||
419 | 93 | } | ||
420 | 94 | |||
421 | 95 | Loader { | ||
422 | 96 | id: downloadLoader | 92 | id: downloadLoader |
427 | 97 | // TODO: Use the ubuntu download manager on desktop as well | 93 | source: "Downloader.qml" |
424 | 98 | // (https://launchpad.net/bugs/1477310). This will require to have | ||
425 | 99 | // ubuntu-download-manager in main (https://launchpad.net/bugs/1488425). | ||
426 | 100 | source: formFactor == "desktop" ? "" : "Downloader.qml" | ||
428 | 101 | asynchronous: true | 94 | asynchronous: true |
429 | 102 | } | 95 | } |
430 | 103 | 96 | ||
431 | 97 | Connections { | ||
432 | 98 | target: downloadLoader.item | ||
433 | 99 | onShowDownloadDialog: { | ||
434 | 100 | showDownloadDialog(downloadId, contentType, downloader, filename, mimeType) | ||
435 | 101 | } | ||
436 | 102 | } | ||
437 | 103 | |||
438 | 104 | function requestGeolocationPermission(request) { | 104 | function requestGeolocationPermission(request) { |
439 | 105 | PopupUtils.open(Qt.resolvedUrl("GeolocationPermissionRequest.qml"), | 105 | PopupUtils.open(Qt.resolvedUrl("GeolocationPermissionRequest.qml"), |
440 | 106 | webview.currentWebview, {"request": request}) | 106 | webview.currentWebview, {"request": request}) |
441 | 107 | 107 | ||
442 | === modified file 'src/app/mime-database.cpp' | |||
443 | --- src/app/mime-database.cpp 2015-08-26 11:29:00 +0000 | |||
444 | +++ src/app/mime-database.cpp 2015-12-16 16:25:40 +0000 | |||
445 | @@ -16,6 +16,8 @@ | |||
446 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
447 | 17 | */ | 17 | */ |
448 | 18 | 18 | ||
449 | 19 | #include <QIcon> | ||
450 | 20 | |||
451 | 19 | #include "mime-database.h" | 21 | #include "mime-database.h" |
452 | 20 | 22 | ||
453 | 21 | MimeDatabase::MimeDatabase(QObject* parent) | 23 | MimeDatabase::MimeDatabase(QObject* parent) |
454 | @@ -31,3 +33,31 @@ | |||
455 | 31 | } | 33 | } |
456 | 32 | return QString(); | 34 | return QString(); |
457 | 33 | } | 35 | } |
458 | 36 | |||
459 | 37 | /*! | ||
460 | 38 | Provide the system icon name for a given mimetype | ||
461 | 39 | */ | ||
462 | 40 | QString MimeDatabase::iconForMimetype(const QString& mimetypeString) const | ||
463 | 41 | { | ||
464 | 42 | QMimeType mimetype = m_database.mimeTypeForName(mimetypeString); | ||
465 | 43 | if (mimetype.iconName().isEmpty() || !QIcon::hasThemeIcon(mimetype.iconName())) { | ||
466 | 44 | if (QIcon::hasThemeIcon(mimetype.genericIconName())) { | ||
467 | 45 | return mimetype.genericIconName(); | ||
468 | 46 | } else { | ||
469 | 47 | return ""; | ||
470 | 48 | } | ||
471 | 49 | } else { | ||
472 | 50 | return mimetype.iconName(); | ||
473 | 51 | } | ||
474 | 52 | } | ||
475 | 53 | |||
476 | 54 | /*! | ||
477 | 55 | Provide the user friendly name for a given mimetype | ||
478 | 56 | */ | ||
479 | 57 | QString MimeDatabase::nameForMimetype(const QString& mimetypeString) const | ||
480 | 58 | { | ||
481 | 59 | QMimeType mimetype = m_database.mimeTypeForName(mimetypeString); | ||
482 | 60 | return mimetype.comment(); | ||
483 | 61 | } | ||
484 | 62 | |||
485 | 63 | |||
486 | 34 | 64 | ||
487 | === modified file 'src/app/mime-database.h' | |||
488 | --- src/app/mime-database.h 2015-08-26 11:29:00 +0000 | |||
489 | +++ src/app/mime-database.h 2015-12-16 16:25:40 +0000 | |||
490 | @@ -31,6 +31,9 @@ | |||
491 | 31 | explicit MimeDatabase(QObject* parent=0); | 31 | explicit MimeDatabase(QObject* parent=0); |
492 | 32 | 32 | ||
493 | 33 | Q_INVOKABLE QString filenameToMimeType(const QString& filename) const; | 33 | Q_INVOKABLE QString filenameToMimeType(const QString& filename) const; |
494 | 34 | Q_INVOKABLE QString iconForMimetype(const QString& mimetypeString) const; | ||
495 | 35 | Q_INVOKABLE QString nameForMimetype(const QString& mimetypeString) const; | ||
496 | 36 | |||
497 | 34 | 37 | ||
498 | 35 | private: | 38 | private: |
499 | 36 | QMimeDatabase m_database; | 39 | QMimeDatabase m_database; |
500 | 37 | 40 | ||
501 | === modified file 'src/app/webbrowser/Browser.qml' | |||
502 | --- src/app/webbrowser/Browser.qml 2015-12-04 11:06:47 +0000 | |||
503 | +++ src/app/webbrowser/Browser.qml 2015-12-16 16:25:40 +0000 | |||
504 | @@ -37,6 +37,9 @@ | |||
505 | 37 | 37 | ||
506 | 38 | currentWebview: tabsModel && tabsModel.currentTab ? tabsModel.currentTab.webview : null | 38 | currentWebview: tabsModel && tabsModel.currentTab ? tabsModel.currentTab.webview : null |
507 | 39 | 39 | ||
508 | 40 | property var downloadsModel: (downloadsModelLoader.status == Loader.Ready) ? downloadsModelLoader.item : null | ||
509 | 41 | property var downloadManager: (downloadHandlerLoader.status == Loader.Ready) ? downloadHandlerLoader.item : null | ||
510 | 42 | |||
511 | 40 | property bool newSession: false | 43 | property bool newSession: false |
512 | 41 | 44 | ||
513 | 42 | property bool incognito: false | 45 | property bool incognito: false |
514 | @@ -168,7 +171,7 @@ | |||
515 | 168 | 171 | ||
516 | 169 | FocusScope { | 172 | FocusScope { |
517 | 170 | anchors.fill: parent | 173 | anchors.fill: parent |
519 | 171 | visible: !settingsContainer.visible && !historyViewLoader.active && !bookmarksViewLoader.active | 174 | visible: !settingsContainer.visible && !historyViewLoader.active && !bookmarksViewLoader.active && !downloadsContainer.visible |
520 | 172 | 175 | ||
521 | 173 | FocusScope { | 176 | FocusScope { |
522 | 174 | id: tabContainer | 177 | id: tabContainer |
523 | @@ -537,6 +540,15 @@ | |||
524 | 537 | onTriggered: chrome.findInPageMode = true | 540 | onTriggered: chrome.findInPageMode = true |
525 | 538 | }, | 541 | }, |
526 | 539 | Action { | 542 | Action { |
527 | 543 | objectName: "downloads" | ||
528 | 544 | text: i18n.tr("Downloads") | ||
529 | 545 | iconName: "save" | ||
530 | 546 | enabled: downloadHandlerLoader.status == Loader.Ready | ||
531 | 547 | onTriggered: { | ||
532 | 548 | currentWebview.showDownloadsPage() | ||
533 | 549 | } | ||
534 | 550 | }, | ||
535 | 551 | Action { | ||
536 | 540 | objectName: "privatemode" | 552 | objectName: "privatemode" |
537 | 541 | text: browser.incognito ? i18n.tr("Leave Private Mode") : i18n.tr("Private Mode") | 553 | text: browser.incognito ? i18n.tr("Leave Private Mode") : i18n.tr("Private Mode") |
538 | 542 | iconName: "private-browsing" | 554 | iconName: "private-browsing" |
539 | @@ -905,6 +917,28 @@ | |||
540 | 905 | } | 917 | } |
541 | 906 | } | 918 | } |
542 | 907 | 919 | ||
543 | 920 | FocusScope { | ||
544 | 921 | id: downloadsContainer | ||
545 | 922 | |||
546 | 923 | visible: children.length > 0 | ||
547 | 924 | anchors.fill: parent | ||
548 | 925 | |||
549 | 926 | Component { | ||
550 | 927 | id: downloadsComponent | ||
551 | 928 | |||
552 | 929 | DownloadsPage { | ||
553 | 930 | anchors.fill: parent | ||
554 | 931 | focus: true | ||
555 | 932 | downloadsModel: browser.downloadsModel | ||
556 | 933 | onDone: destroy() | ||
557 | 934 | Keys.onEscapePressed: { | ||
558 | 935 | destroy() | ||
559 | 936 | internal.resetFocus() | ||
560 | 937 | } | ||
561 | 938 | } | ||
562 | 939 | } | ||
563 | 940 | } | ||
564 | 941 | |||
565 | 908 | TabsModel { | 942 | TabsModel { |
566 | 909 | id: publicTabsModel | 943 | id: publicTabsModel |
567 | 910 | } | 944 | } |
568 | @@ -930,6 +964,17 @@ | |||
569 | 930 | } | 964 | } |
570 | 931 | } | 965 | } |
571 | 932 | 966 | ||
572 | 967 | Loader { | ||
573 | 968 | id: downloadsModelLoader | ||
574 | 969 | source: "DownloadsModel.qml" | ||
575 | 970 | asynchronous: true | ||
576 | 971 | } | ||
577 | 972 | |||
578 | 973 | Loader { | ||
579 | 974 | id: downloadHandlerLoader | ||
580 | 975 | source: "DownloadHandler.qml" | ||
581 | 976 | } | ||
582 | 977 | |||
583 | 933 | Component { | 978 | Component { |
584 | 934 | id: tabComponent | 979 | id: tabComponent |
585 | 935 | 980 | ||
586 | @@ -950,6 +995,7 @@ | |||
587 | 950 | readonly property bool current: tab.current | 995 | readonly property bool current: tab.current |
588 | 951 | 996 | ||
589 | 952 | currentWebview: browser.currentWebview | 997 | currentWebview: browser.currentWebview |
590 | 998 | filePicker: filePickerLoader.item | ||
591 | 953 | 999 | ||
592 | 954 | anchors.fill: parent | 1000 | anchors.fill: parent |
593 | 955 | focus: true | 1001 | focus: true |
594 | @@ -1241,6 +1287,29 @@ | |||
595 | 1241 | Component.onDestruction: bottomEdgeHint.forceShow = false | 1287 | Component.onDestruction: bottomEdgeHint.forceShow = false |
596 | 1242 | } | 1288 | } |
597 | 1243 | } | 1289 | } |
598 | 1290 | |||
599 | 1291 | onShowDownloadDialog: { | ||
600 | 1292 | if (downloadDialogLoader.status === Loader.Ready) { | ||
601 | 1293 | var downloadDialog = PopupUtils.open(downloadDialogLoader.item, browser, {"contentType" : contentType, | ||
602 | 1294 | "downloadId" : downloadId, | ||
603 | 1295 | "singleDownload" : downloader, | ||
604 | 1296 | "filename" : filename, | ||
605 | 1297 | "mimeType" : mimeType}) | ||
606 | 1298 | downloadDialog.startDownload.connect(startDownload) | ||
607 | 1299 | } | ||
608 | 1300 | } | ||
609 | 1301 | |||
610 | 1302 | function showDownloadsPage() { | ||
611 | 1303 | downloadsContainer.focus = true | ||
612 | 1304 | return downloadsComponent.createObject(downloadsContainer) | ||
613 | 1305 | } | ||
614 | 1306 | |||
615 | 1307 | function startDownload(downloadId, download, mimeType) { | ||
616 | 1308 | downloadsModel.add(downloadId, download.url, mimeType) | ||
617 | 1309 | download.start() | ||
618 | 1310 | showDownloadsPage() | ||
619 | 1311 | } | ||
620 | 1312 | |||
621 | 1244 | } | 1313 | } |
622 | 1245 | } | 1314 | } |
623 | 1246 | } | 1315 | } |
624 | @@ -1743,13 +1812,13 @@ | |||
625 | 1743 | KeyboardShortcut { | 1812 | KeyboardShortcut { |
626 | 1744 | modifiers: Qt.ControlModifier | 1813 | modifiers: Qt.ControlModifier |
627 | 1745 | key: Qt.Key_Tab | 1814 | key: Qt.Key_Tab |
629 | 1746 | enabled: chrome.visible || recentView.visible | 1815 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
630 | 1747 | onTriggered: internal.switchToNextTab() | 1816 | onTriggered: internal.switchToNextTab() |
631 | 1748 | } | 1817 | } |
632 | 1749 | KeyboardShortcut { | 1818 | KeyboardShortcut { |
633 | 1750 | modifiers: Qt.ControlModifier | 1819 | modifiers: Qt.ControlModifier |
634 | 1751 | key: Qt.Key_PageDown | 1820 | key: Qt.Key_PageDown |
636 | 1752 | enabled: chrome.visible || recentView.visible | 1821 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
637 | 1753 | onTriggered: internal.switchToNextTab() | 1822 | onTriggered: internal.switchToNextTab() |
638 | 1754 | } | 1823 | } |
639 | 1755 | 1824 | ||
640 | @@ -1757,13 +1826,13 @@ | |||
641 | 1757 | KeyboardShortcut { | 1826 | KeyboardShortcut { |
642 | 1758 | modifiers: Qt.ControlModifier | 1827 | modifiers: Qt.ControlModifier |
643 | 1759 | key: Qt.Key_Backtab | 1828 | key: Qt.Key_Backtab |
645 | 1760 | enabled: chrome.visible || recentView.visible | 1829 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
646 | 1761 | onTriggered: internal.switchToPreviousTab() | 1830 | onTriggered: internal.switchToPreviousTab() |
647 | 1762 | } | 1831 | } |
648 | 1763 | KeyboardShortcut { | 1832 | KeyboardShortcut { |
649 | 1764 | modifiers: Qt.ControlModifier | 1833 | modifiers: Qt.ControlModifier |
650 | 1765 | key: Qt.Key_PageUp | 1834 | key: Qt.Key_PageUp |
652 | 1766 | enabled: chrome.visible || recentView.visible | 1835 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
653 | 1767 | onTriggered: internal.switchToPreviousTab() | 1836 | onTriggered: internal.switchToPreviousTab() |
654 | 1768 | } | 1837 | } |
655 | 1769 | 1838 | ||
656 | @@ -1771,14 +1840,14 @@ | |||
657 | 1771 | KeyboardShortcut { | 1840 | KeyboardShortcut { |
658 | 1772 | modifiers: Qt.ControlModifier | Qt.ShiftModifier | 1841 | modifiers: Qt.ControlModifier | Qt.ShiftModifier |
659 | 1773 | key: Qt.Key_W | 1842 | key: Qt.Key_W |
661 | 1774 | enabled: chrome.visible || recentView.visible | 1843 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
662 | 1775 | onTriggered: internal.undoCloseTab() | 1844 | onTriggered: internal.undoCloseTab() |
663 | 1776 | } | 1845 | } |
664 | 1777 | 1846 | ||
665 | 1778 | KeyboardShortcut { | 1847 | KeyboardShortcut { |
666 | 1779 | modifiers: Qt.ControlModifier | Qt.ShiftModifier | 1848 | modifiers: Qt.ControlModifier | Qt.ShiftModifier |
667 | 1780 | key: Qt.Key_T | 1849 | key: Qt.Key_T |
669 | 1781 | enabled: chrome.visible || recentView.visible | 1850 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
670 | 1782 | onTriggered: internal.undoCloseTab() | 1851 | onTriggered: internal.undoCloseTab() |
671 | 1783 | } | 1852 | } |
672 | 1784 | 1853 | ||
673 | @@ -1786,13 +1855,13 @@ | |||
674 | 1786 | KeyboardShortcut { | 1855 | KeyboardShortcut { |
675 | 1787 | modifiers: Qt.ControlModifier | 1856 | modifiers: Qt.ControlModifier |
676 | 1788 | key: Qt.Key_W | 1857 | key: Qt.Key_W |
678 | 1789 | enabled: chrome.visible || recentView.visible | 1858 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
679 | 1790 | onTriggered: internal.closeCurrentTab() | 1859 | onTriggered: internal.closeCurrentTab() |
680 | 1791 | } | 1860 | } |
681 | 1792 | KeyboardShortcut { | 1861 | KeyboardShortcut { |
682 | 1793 | modifiers: Qt.ControlModifier | 1862 | modifiers: Qt.ControlModifier |
683 | 1794 | key: Qt.Key_F4 | 1863 | key: Qt.Key_F4 |
685 | 1795 | enabled: chrome.visible || recentView.visible | 1864 | enabled: (chrome.visible || recentView.visible) && !downloadsContainer.visible |
686 | 1796 | onTriggered: internal.closeCurrentTab() | 1865 | onTriggered: internal.closeCurrentTab() |
687 | 1797 | } | 1866 | } |
688 | 1798 | 1867 | ||
689 | @@ -1800,7 +1869,7 @@ | |||
690 | 1800 | KeyboardShortcut { | 1869 | KeyboardShortcut { |
691 | 1801 | modifiers: Qt.ControlModifier | 1870 | modifiers: Qt.ControlModifier |
692 | 1802 | key: Qt.Key_T | 1871 | key: Qt.Key_T |
694 | 1803 | enabled: chrome.visible || recentView.visible || bookmarksViewLoader.active || historyViewLoader.active | 1872 | enabled: (chrome.visible || recentView.visible || bookmarksViewLoader.active || historyViewLoader.active) && !downloadsContainer.visible |
695 | 1804 | onTriggered: { | 1873 | onTriggered: { |
696 | 1805 | openUrlInNewTab("", true) | 1874 | openUrlInNewTab("", true) |
697 | 1806 | if (recentView.visible) recentView.reset() | 1875 | if (recentView.visible) recentView.reset() |
698 | @@ -1814,18 +1883,18 @@ | |||
699 | 1814 | KeyboardShortcut { | 1883 | KeyboardShortcut { |
700 | 1815 | modifiers: Qt.ControlModifier | 1884 | modifiers: Qt.ControlModifier |
701 | 1816 | key: Qt.Key_L | 1885 | key: Qt.Key_L |
703 | 1817 | enabled: chrome.visible | 1886 | enabled: chrome.visible && !downloadsContainer.visible |
704 | 1818 | onTriggered: internal.focusAddressBar(true) | 1887 | onTriggered: internal.focusAddressBar(true) |
705 | 1819 | } | 1888 | } |
706 | 1820 | KeyboardShortcut { | 1889 | KeyboardShortcut { |
707 | 1821 | modifiers: Qt.AltModifier | 1890 | modifiers: Qt.AltModifier |
708 | 1822 | key: Qt.Key_D | 1891 | key: Qt.Key_D |
710 | 1823 | enabled: chrome.visible | 1892 | enabled: chrome.visible && !downloadsContainer.visible |
711 | 1824 | onTriggered: internal.focusAddressBar(true) | 1893 | onTriggered: internal.focusAddressBar(true) |
712 | 1825 | } | 1894 | } |
713 | 1826 | KeyboardShortcut { | 1895 | KeyboardShortcut { |
714 | 1827 | key: Qt.Key_F6 | 1896 | key: Qt.Key_F6 |
716 | 1828 | enabled: chrome.visible | 1897 | enabled: chrome.visible && !downloadsContainer.visible |
717 | 1829 | onTriggered: internal.focusAddressBar(true) | 1898 | onTriggered: internal.focusAddressBar(true) |
718 | 1830 | } | 1899 | } |
719 | 1831 | 1900 | ||
720 | @@ -1833,7 +1902,7 @@ | |||
721 | 1833 | KeyboardShortcut { | 1902 | KeyboardShortcut { |
722 | 1834 | modifiers: Qt.ControlModifier | 1903 | modifiers: Qt.ControlModifier |
723 | 1835 | key: Qt.Key_D | 1904 | key: Qt.Key_D |
725 | 1836 | enabled: chrome.visible | 1905 | enabled: chrome.visible && !downloadsContainer.visible |
726 | 1837 | onTriggered: { | 1906 | onTriggered: { |
727 | 1838 | if (currentWebview) { | 1907 | if (currentWebview) { |
728 | 1839 | if (BookmarksModel.contains(currentWebview.url)) { | 1908 | if (BookmarksModel.contains(currentWebview.url)) { |
729 | @@ -1849,7 +1918,7 @@ | |||
730 | 1849 | KeyboardShortcut { | 1918 | KeyboardShortcut { |
731 | 1850 | modifiers: Qt.ControlModifier | 1919 | modifiers: Qt.ControlModifier |
732 | 1851 | key: Qt.Key_H | 1920 | key: Qt.Key_H |
734 | 1852 | enabled: chrome.visible | 1921 | enabled: chrome.visible && !downloadsContainer.visible |
735 | 1853 | onTriggered: historyViewLoader.active = true | 1922 | onTriggered: historyViewLoader.active = true |
736 | 1854 | } | 1923 | } |
737 | 1855 | 1924 | ||
738 | @@ -1857,7 +1926,7 @@ | |||
739 | 1857 | KeyboardShortcut { | 1926 | KeyboardShortcut { |
740 | 1858 | modifiers: Qt.ControlModifier | Qt.ShiftModifier | 1927 | modifiers: Qt.ControlModifier | Qt.ShiftModifier |
741 | 1859 | key: Qt.Key_O | 1928 | key: Qt.Key_O |
743 | 1860 | enabled: chrome.visible | 1929 | enabled: chrome.visible && !downloadsContainer.visible |
744 | 1861 | onTriggered: bookmarksViewLoader.active = true | 1930 | onTriggered: bookmarksViewLoader.active = true |
745 | 1862 | } | 1931 | } |
746 | 1863 | 1932 | ||
747 | @@ -1865,12 +1934,12 @@ | |||
748 | 1865 | KeyboardShortcut { | 1934 | KeyboardShortcut { |
749 | 1866 | modifiers: Qt.AltModifier | 1935 | modifiers: Qt.AltModifier |
750 | 1867 | key: Qt.Key_Left | 1936 | key: Qt.Key_Left |
752 | 1868 | enabled: chrome.visible | 1937 | enabled: chrome.visible && !downloadsContainer.visible |
753 | 1869 | onTriggered: internal.historyGoBack() | 1938 | onTriggered: internal.historyGoBack() |
754 | 1870 | } | 1939 | } |
755 | 1871 | KeyboardShortcut { | 1940 | KeyboardShortcut { |
756 | 1872 | key: Qt.Key_Backspace | 1941 | key: Qt.Key_Backspace |
758 | 1873 | enabled: chrome.visible | 1942 | enabled: chrome.visible && !downloadsContainer.visible |
759 | 1874 | onTriggered: internal.historyGoBack() | 1943 | onTriggered: internal.historyGoBack() |
760 | 1875 | } | 1944 | } |
761 | 1876 | 1945 | ||
762 | @@ -1878,26 +1947,26 @@ | |||
763 | 1878 | KeyboardShortcut { | 1947 | KeyboardShortcut { |
764 | 1879 | modifiers: Qt.AltModifier | 1948 | modifiers: Qt.AltModifier |
765 | 1880 | key: Qt.Key_Right | 1949 | key: Qt.Key_Right |
767 | 1881 | enabled: chrome.visible | 1950 | enabled: chrome.visible && !downloadsContainer.visible |
768 | 1882 | onTriggered: internal.historyGoForward() | 1951 | onTriggered: internal.historyGoForward() |
769 | 1883 | } | 1952 | } |
770 | 1884 | KeyboardShortcut { | 1953 | KeyboardShortcut { |
771 | 1885 | modifiers: Qt.ShiftModifier | 1954 | modifiers: Qt.ShiftModifier |
772 | 1886 | key: Qt.Key_Backspace | 1955 | key: Qt.Key_Backspace |
774 | 1887 | enabled: chrome.visible | 1956 | enabled: chrome.visible && !downloadsContainer.visible |
775 | 1888 | onTriggered: internal.historyGoForward() | 1957 | onTriggered: internal.historyGoForward() |
776 | 1889 | } | 1958 | } |
777 | 1890 | 1959 | ||
778 | 1891 | // F5 or Ctrl+R: Reload current Tab | 1960 | // F5 or Ctrl+R: Reload current Tab |
779 | 1892 | KeyboardShortcut { | 1961 | KeyboardShortcut { |
780 | 1893 | key: Qt.Key_F5 | 1962 | key: Qt.Key_F5 |
782 | 1894 | enabled: chrome.visible | 1963 | enabled: chrome.visible && !downloadsContainer.visible |
783 | 1895 | onTriggered: if (currentWebview) currentWebview.reload() | 1964 | onTriggered: if (currentWebview) currentWebview.reload() |
784 | 1896 | } | 1965 | } |
785 | 1897 | KeyboardShortcut { | 1966 | KeyboardShortcut { |
786 | 1898 | modifiers: Qt.ControlModifier | 1967 | modifiers: Qt.ControlModifier |
787 | 1899 | key: Qt.Key_R | 1968 | key: Qt.Key_R |
789 | 1900 | enabled: chrome.visible | 1969 | enabled: chrome.visible && !downloadsContainer.visible |
790 | 1901 | onTriggered: if (currentWebview) currentWebview.reload() | 1970 | onTriggered: if (currentWebview) currentWebview.reload() |
791 | 1902 | } | 1971 | } |
792 | 1903 | 1972 | ||
793 | @@ -1905,8 +1974,48 @@ | |||
794 | 1905 | KeyboardShortcut { | 1974 | KeyboardShortcut { |
795 | 1906 | modifiers: Qt.ControlModifier | 1975 | modifiers: Qt.ControlModifier |
796 | 1907 | key: Qt.Key_F | 1976 | key: Qt.Key_F |
798 | 1908 | enabled: !newTabViewLoader.active && !bookmarksViewLoader.active | 1977 | enabled: !newTabViewLoader.active && !bookmarksViewLoader.active && !downloadsContainer.visible |
799 | 1909 | onTriggered: chrome.findInPageMode = true | 1978 | onTriggered: chrome.findInPageMode = true |
800 | 1910 | } | 1979 | } |
802 | 1911 | } | 1980 | |
803 | 1981 | // Ctrl + J: Show downloads page | ||
804 | 1982 | KeyboardShortcut { | ||
805 | 1983 | modifiers: Qt.ControlModifier | ||
806 | 1984 | key: Qt.Key_J | ||
807 | 1985 | enabled: chrome.visible && !downloadsContainer.visible | ||
808 | 1986 | onTriggered: currentWebview.showDownloadsPage() | ||
809 | 1987 | } | ||
810 | 1988 | } | ||
811 | 1989 | |||
812 | 1990 | Loader { | ||
813 | 1991 | id: contentHandlerLoader | ||
814 | 1992 | source: "ContentHandler.qml" | ||
815 | 1993 | } | ||
816 | 1994 | |||
817 | 1995 | Connections { | ||
818 | 1996 | target: contentHandlerLoader.item | ||
819 | 1997 | onExportFromDownloads: { | ||
820 | 1998 | if (downloadHandlerLoader.status == Loader.Ready) { | ||
821 | 1999 | downloadsContainer.focus = true | ||
822 | 2000 | var downloadPage = downloadsComponent.createObject(downloadsContainer) | ||
823 | 2001 | downloadPage.mimetypeFilter = mimetypeFilter | ||
824 | 2002 | downloadPage.activeTransfer = transfer | ||
825 | 2003 | downloadPage.multiSelect = multiSelect | ||
826 | 2004 | downloadPage.pickingMode = true | ||
827 | 2005 | } | ||
828 | 2006 | } | ||
829 | 2007 | } | ||
830 | 2008 | |||
831 | 2009 | Loader { | ||
832 | 2010 | id: downloadDialogLoader | ||
833 | 2011 | source: "ContentDownloadDialog.qml" | ||
834 | 2012 | asynchronous: true | ||
835 | 2013 | } | ||
836 | 2014 | |||
837 | 2015 | Loader { | ||
838 | 2016 | id: filePickerLoader | ||
839 | 2017 | source: "ContentPickerDialog.qml" | ||
840 | 2018 | asynchronous: true | ||
841 | 2019 | } | ||
842 | 2020 | |||
843 | 1912 | } | 2021 | } |
844 | 1913 | 2022 | ||
845 | === renamed file 'src/app/webbrowser/SettingsPageHeader.qml' => 'src/app/webbrowser/BrowserPageHeader.qml' | |||
846 | --- src/app/webbrowser/SettingsPageHeader.qml 2015-08-10 15:22:00 +0000 | |||
847 | +++ src/app/webbrowser/BrowserPageHeader.qml 2015-12-16 16:25:40 +0000 | |||
848 | @@ -21,18 +21,22 @@ | |||
849 | 21 | import Ubuntu.Components.ListItems 1.3 as ListItem | 21 | import Ubuntu.Components.ListItems 1.3 as ListItem |
850 | 22 | 22 | ||
851 | 23 | /* | 23 | /* |
853 | 24 | * Component to use as page header in settings page and subpages | 24 | * Component to use as page header in settings page, download page and |
854 | 25 | * subpages | ||
855 | 25 | * | 26 | * |
858 | 26 | * It has a back() signal fired when back button is pressed and a text | 27 | * It has a back() signal fired when back button is pressed, a text |
859 | 27 | * property to set the page title | 28 | * property to set the page title and an actions property which |
860 | 29 | * displays action icons on the right of header. | ||
861 | 28 | */ | 30 | */ |
862 | 29 | 31 | ||
864 | 30 | Column { | 32 | Item { |
865 | 31 | id: root | 33 | id: root |
866 | 32 | signal back() | 34 | signal back() |
867 | 33 | property string text | 35 | property string text |
868 | 36 | property alias actions: actionBar.actions | ||
869 | 37 | property alias color: title.color | ||
870 | 34 | 38 | ||
872 | 35 | height: childrenRect.height | 39 | height: title.height + divider.height |
873 | 36 | 40 | ||
874 | 37 | anchors { | 41 | anchors { |
875 | 38 | left: parent.left | 42 | left: parent.left |
876 | @@ -42,13 +46,9 @@ | |||
877 | 42 | Rectangle { | 46 | Rectangle { |
878 | 43 | id: title | 47 | id: title |
879 | 44 | 48 | ||
881 | 45 | height: units.gu(7) - divider.height | 49 | height: units.gu(6) - divider.height |
882 | 46 | anchors { left: parent.left; right: parent.right } | 50 | anchors { left: parent.left; right: parent.right } |
888 | 47 | 51 | color: "#f6f6f6" | |
884 | 48 | Rectangle { | ||
885 | 49 | anchors.fill: parent | ||
886 | 50 | color: "#f6f6f6" | ||
887 | 51 | } | ||
889 | 52 | 52 | ||
890 | 53 | AbstractButton { | 53 | AbstractButton { |
891 | 54 | id: backButton | 54 | id: backButton |
892 | @@ -89,9 +89,24 @@ | |||
893 | 89 | text: root.text | 89 | text: root.text |
894 | 90 | fontSize: 'x-large' | 90 | fontSize: 'x-large' |
895 | 91 | } | 91 | } |
896 | 92 | |||
897 | 93 | ActionBar { | ||
898 | 94 | id: actionBar | ||
899 | 95 | anchors.right: parent.right | ||
900 | 96 | anchors.verticalCenter: parent.verticalCenter | ||
901 | 97 | } | ||
902 | 98 | |||
903 | 99 | |||
904 | 92 | } | 100 | } |
905 | 93 | 101 | ||
907 | 94 | ListItem.Divider { | 102 | Rectangle { |
908 | 95 | id: divider | 103 | id: divider |
909 | 104 | anchors { | ||
910 | 105 | left: parent.left | ||
911 | 106 | right: parent.right | ||
912 | 107 | bottom: parent.bottom | ||
913 | 108 | } | ||
914 | 109 | height: units.dp(1) | ||
915 | 110 | color: Qt.darker(title.color, 1.1) | ||
916 | 96 | } | 111 | } |
917 | 97 | } | 112 | } |
918 | 98 | 113 | ||
919 | === modified file 'src/app/webbrowser/CMakeLists.txt' | |||
920 | --- src/app/webbrowser/CMakeLists.txt 2015-11-23 09:41:48 +0000 | |||
921 | +++ src/app/webbrowser/CMakeLists.txt 2015-12-16 16:25:40 +0000 | |||
922 | @@ -13,6 +13,7 @@ | |||
923 | 13 | bookmarks-model.cpp | 13 | bookmarks-model.cpp |
924 | 14 | bookmarks-folder-model.cpp | 14 | bookmarks-folder-model.cpp |
925 | 15 | bookmarks-folderlist-model.cpp | 15 | bookmarks-folderlist-model.cpp |
926 | 16 | downloads-model.cpp | ||
927 | 16 | history-domain-model.cpp | 17 | history-domain-model.cpp |
928 | 17 | history-domainlist-model.cpp | 18 | history-domainlist-model.cpp |
929 | 18 | history-lastvisitdatelist-model.cpp | 19 | history-lastvisitdatelist-model.cpp |
930 | @@ -74,3 +75,8 @@ | |||
931 | 74 | 75 | ||
932 | 75 | install(FILES "webbrowser-app.url-dispatcher" | 76 | install(FILES "webbrowser-app.url-dispatcher" |
933 | 76 | DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls) | 77 | DESTINATION ${CMAKE_INSTALL_DATADIR}/url-dispatcher/urls) |
934 | 78 | |||
935 | 79 | install(FILES "webbrowser-app-content-hub.json" | ||
936 | 80 | DESTINATION ${CMAKE_INSTALL_DATADIR}/content-hub/peers | ||
937 | 81 | RENAME webbrowser-app | ||
938 | 82 | ) | ||
939 | 77 | 83 | ||
940 | === added file 'src/app/webbrowser/ContentDownloadDialog.qml' | |||
941 | --- src/app/webbrowser/ContentDownloadDialog.qml 1970-01-01 00:00:00 +0000 | |||
942 | +++ src/app/webbrowser/ContentDownloadDialog.qml 2015-12-16 16:25:40 +0000 | |||
943 | @@ -0,0 +1,159 @@ | |||
944 | 1 | /* | ||
945 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
946 | 3 | * | ||
947 | 4 | * This file is part of webbrowser-app. | ||
948 | 5 | * | ||
949 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
950 | 7 | * it under the terms of the GNU General Public License as published by | ||
951 | 8 | * the Free Software Foundation; version 3. | ||
952 | 9 | * | ||
953 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
954 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
955 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
956 | 13 | * GNU General Public License for more details. | ||
957 | 14 | * | ||
958 | 15 | * You should have received a copy of the GNU General Public License | ||
959 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
960 | 17 | */ | ||
961 | 18 | |||
962 | 19 | import QtQuick 2.4 | ||
963 | 20 | import Ubuntu.Components 1.3 | ||
964 | 21 | import Ubuntu.Components.Popups 1.3 | ||
965 | 22 | import Ubuntu.Content 1.3 | ||
966 | 23 | import webbrowsercommon.private 0.1 | ||
967 | 24 | |||
968 | 25 | Component { | ||
969 | 26 | PopupBase { | ||
970 | 27 | id: downloadDialog | ||
971 | 28 | objectName: "downloadDialog" | ||
972 | 29 | anchors.fill: parent | ||
973 | 30 | property var activeTransfer | ||
974 | 31 | property string downloadId | ||
975 | 32 | property var singleDownload | ||
976 | 33 | property string mimeType | ||
977 | 34 | property string filename | ||
978 | 35 | property string icon: MimeDatabase.iconForMimetype(mimeType) | ||
979 | 36 | property alias contentType: peerPicker.contentType | ||
980 | 37 | |||
981 | 38 | signal startDownload(string downloadId, var download, string mimeType) | ||
982 | 39 | |||
983 | 40 | Component { | ||
984 | 41 | id: downloadOptionsComponent | ||
985 | 42 | Dialog { | ||
986 | 43 | id: downloadOptionsDialog | ||
987 | 44 | objectName: "downloadOptionsDialog" | ||
988 | 45 | Column { | ||
989 | 46 | spacing: units.gu(2) | ||
990 | 47 | |||
991 | 48 | Item { | ||
992 | 49 | width: parent.width | ||
993 | 50 | height: mimetypeIcon.height | ||
994 | 51 | |||
995 | 52 | Icon { | ||
996 | 53 | id: mimetypeIcon | ||
997 | 54 | name: icon != "" ? icon : "save" | ||
998 | 55 | height: units.gu(4.5) | ||
999 | 56 | width: height | ||
1000 | 57 | } | ||
1001 | 58 | |||
1002 | 59 | Label { | ||
1003 | 60 | id: filenameLabel | ||
1004 | 61 | anchors.top: mimetypeIcon.top | ||
1005 | 62 | anchors.left: mimetypeIcon.right | ||
1006 | 63 | anchors.leftMargin: units.gu(2) | ||
1007 | 64 | anchors.right: parent.right | ||
1008 | 65 | anchors.rightMargin: units.gu(2) | ||
1009 | 66 | elide: Text.ElideMiddle | ||
1010 | 67 | text: downloadDialog.filename | ||
1011 | 68 | } | ||
1012 | 69 | |||
1013 | 70 | Label { | ||
1014 | 71 | anchors.top: filenameLabel.bottom | ||
1015 | 72 | anchors.left: filenameLabel.left | ||
1016 | 73 | anchors.right: filenameLabel.right | ||
1017 | 74 | elide: Text.ElideRight | ||
1018 | 75 | font.capitalization: Font.Capitalize | ||
1019 | 76 | text: MimeDatabase.nameForMimetype(downloadDialog.mimeType) | ||
1020 | 77 | } | ||
1021 | 78 | } | ||
1022 | 79 | |||
1023 | 80 | Label { | ||
1024 | 81 | width: parent.width | ||
1025 | 82 | text: i18n.tr("Choose an application to open this file or add it to the downloads folder.") | ||
1026 | 83 | wrapMode: Text.Wrap | ||
1027 | 84 | visible: peerModel.peers.length > 0 | ||
1028 | 85 | } | ||
1029 | 86 | |||
1030 | 87 | Button { | ||
1031 | 88 | text: i18n.tr("Choose an application") | ||
1032 | 89 | objectName: "chooseAppButton" | ||
1033 | 90 | anchors.horizontalCenter: parent.horizontalCenter | ||
1034 | 91 | width: units.gu(22) | ||
1035 | 92 | height: units.gu(4) | ||
1036 | 93 | visible: peerModel.peers.length > 0 | ||
1037 | 94 | onClicked: { | ||
1038 | 95 | PopupUtils.close(downloadOptionsDialog) | ||
1039 | 96 | pickerRect.visible = true | ||
1040 | 97 | } | ||
1041 | 98 | } | ||
1042 | 99 | |||
1043 | 100 | Button { | ||
1044 | 101 | text: i18n.tr("Download") | ||
1045 | 102 | objectName: "downloadFileButton" | ||
1046 | 103 | anchors.horizontalCenter: parent.horizontalCenter | ||
1047 | 104 | width: units.gu(22) | ||
1048 | 105 | height: units.gu(4) | ||
1049 | 106 | onClicked: { | ||
1050 | 107 | startDownload(downloadId, singleDownload, mimeType) | ||
1051 | 108 | PopupUtils.close(downloadDialog) | ||
1052 | 109 | } | ||
1053 | 110 | } | ||
1054 | 111 | |||
1055 | 112 | Button { | ||
1056 | 113 | text: i18n.tr("Cancel") | ||
1057 | 114 | objectName: "cancelDownloadButton" | ||
1058 | 115 | anchors.horizontalCenter: parent.horizontalCenter | ||
1059 | 116 | width: units.gu(22) | ||
1060 | 117 | height: units.gu(4) | ||
1061 | 118 | onClicked: PopupUtils.close(downloadDialog) | ||
1062 | 119 | } | ||
1063 | 120 | |||
1064 | 121 | } | ||
1065 | 122 | } | ||
1066 | 123 | |||
1067 | 124 | } | ||
1068 | 125 | |||
1069 | 126 | ContentPeerModel { | ||
1070 | 127 | id: peerModel | ||
1071 | 128 | handler: ContentHandler.Destination | ||
1072 | 129 | contentType: downloadDialog.contentType | ||
1073 | 130 | } | ||
1074 | 131 | |||
1075 | 132 | Rectangle { | ||
1076 | 133 | id: pickerRect | ||
1077 | 134 | anchors.fill: parent | ||
1078 | 135 | visible: false | ||
1079 | 136 | ContentPeerPicker { | ||
1080 | 137 | id: peerPicker | ||
1081 | 138 | handler: ContentHandler.Destination | ||
1082 | 139 | objectName: "contentPeerPicker" | ||
1083 | 140 | visible: parent.visible | ||
1084 | 141 | |||
1085 | 142 | onPeerSelected: { | ||
1086 | 143 | activeTransfer = peer.request() | ||
1087 | 144 | activeTransfer.downloadId = downloadDialog.downloadId | ||
1088 | 145 | activeTransfer.state = ContentTransfer.Downloading | ||
1089 | 146 | PopupUtils.close(downloadDialog) | ||
1090 | 147 | } | ||
1091 | 148 | |||
1092 | 149 | onCancelPressed: { | ||
1093 | 150 | PopupUtils.close(downloadDialog) | ||
1094 | 151 | } | ||
1095 | 152 | } | ||
1096 | 153 | } | ||
1097 | 154 | |||
1098 | 155 | Component.onCompleted: { | ||
1099 | 156 | PopupUtils.open(downloadOptionsComponent, downloadDialog) | ||
1100 | 157 | } | ||
1101 | 158 | } | ||
1102 | 159 | } | ||
1103 | 0 | 160 | ||
1104 | === added file 'src/app/webbrowser/ContentHandler.qml' | |||
1105 | --- src/app/webbrowser/ContentHandler.qml 1970-01-01 00:00:00 +0000 | |||
1106 | +++ src/app/webbrowser/ContentHandler.qml 2015-12-16 16:25:40 +0000 | |||
1107 | @@ -0,0 +1,35 @@ | |||
1108 | 1 | /* | ||
1109 | 2 | * Copyright 2015 Canonical Ltd. | ||
1110 | 3 | * | ||
1111 | 4 | * This file is part of webbrowser-app. | ||
1112 | 5 | * | ||
1113 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1114 | 7 | * it under the terms of the GNU General Public License as published by | ||
1115 | 8 | * the Free Software Foundation; version 3. | ||
1116 | 9 | * | ||
1117 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1118 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1119 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1120 | 13 | * GNU General Public License for more details. | ||
1121 | 14 | * | ||
1122 | 15 | * You should have received a copy of the GNU General Public License | ||
1123 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1124 | 17 | */ | ||
1125 | 18 | |||
1126 | 19 | import QtQuick 2.4 | ||
1127 | 20 | import Ubuntu.Content 1.3 | ||
1128 | 21 | import "../MimeTypeMapper.js" as MimeTypeMapper | ||
1129 | 22 | |||
1130 | 23 | Item { | ||
1131 | 24 | signal exportFromDownloads(var transfer, var mimetypeFilter, bool multiSelect) | ||
1132 | 25 | |||
1133 | 26 | Connections { | ||
1134 | 27 | target: ContentHub | ||
1135 | 28 | onExportRequested: { | ||
1136 | 29 | exportFromDownloads(transfer, | ||
1137 | 30 | MimeTypeMapper.mimeTypeRegexForContentType(transfer.contentType), | ||
1138 | 31 | transfer.selectionType === ContentTransfer.Multiple) | ||
1139 | 32 | |||
1140 | 33 | } | ||
1141 | 34 | } | ||
1142 | 35 | } | ||
1143 | 0 | 36 | ||
1144 | === added file 'src/app/webbrowser/ContentPickerDialog.qml' | |||
1145 | --- src/app/webbrowser/ContentPickerDialog.qml 1970-01-01 00:00:00 +0000 | |||
1146 | +++ src/app/webbrowser/ContentPickerDialog.qml 2015-12-16 16:25:40 +0000 | |||
1147 | @@ -0,0 +1,110 @@ | |||
1148 | 1 | /* | ||
1149 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
1150 | 3 | * | ||
1151 | 4 | * This file is part of webbrowser-app. | ||
1152 | 5 | * | ||
1153 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1154 | 7 | * it under the terms of the GNU General Public License as published by | ||
1155 | 8 | * the Free Software Foundation; version 3. | ||
1156 | 9 | * | ||
1157 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1158 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1159 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1160 | 13 | * GNU General Public License for more details. | ||
1161 | 14 | * | ||
1162 | 15 | * You should have received a copy of the GNU General Public License | ||
1163 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1164 | 17 | */ | ||
1165 | 18 | |||
1166 | 19 | import QtQuick 2.4 | ||
1167 | 20 | import Ubuntu.Components 1.3 | ||
1168 | 21 | import Ubuntu.Components.Popups 1.3 as Popups | ||
1169 | 22 | import Ubuntu.Content 1.3 | ||
1170 | 23 | import com.canonical.Oxide 1.8 | ||
1171 | 24 | import "../MimeTypeMapper.js" as MimeTypeMapper | ||
1172 | 25 | |||
1173 | 26 | Component { | ||
1174 | 27 | Popups.PopupBase { | ||
1175 | 28 | id: picker | ||
1176 | 29 | objectName: "contentPickerDialog" | ||
1177 | 30 | |||
1178 | 31 | // Set the parent at construction time, instead of letting show() | ||
1179 | 32 | // set it later on, which for some reason results in the size of | ||
1180 | 33 | // the dialog not being updated. | ||
1181 | 34 | parent: QuickUtils.rootItem(this) | ||
1182 | 35 | |||
1183 | 36 | property var activeTransfer | ||
1184 | 37 | |||
1185 | 38 | Rectangle { | ||
1186 | 39 | anchors.fill: parent | ||
1187 | 40 | |||
1188 | 41 | ContentTransferHint { | ||
1189 | 42 | anchors.fill: parent | ||
1190 | 43 | activeTransfer: picker.activeTransfer | ||
1191 | 44 | } | ||
1192 | 45 | |||
1193 | 46 | ContentPeerPicker { | ||
1194 | 47 | id: peerPicker | ||
1195 | 48 | anchors.fill: parent | ||
1196 | 49 | visible: true | ||
1197 | 50 | contentType: ContentType.All | ||
1198 | 51 | handler: ContentHandler.Source | ||
1199 | 52 | |||
1200 | 53 | onPeerSelected: { | ||
1201 | 54 | if (peer.appId == "webbrowser-app") { | ||
1202 | 55 | // If we're inside the browser and the user has | ||
1203 | 56 | // requested content from the browser then we | ||
1204 | 57 | // need to handle the transfer internally | ||
1205 | 58 | var downloadPage = picker.WebView.view.showDownloadsPage() | ||
1206 | 59 | downloadPage.mimetypeFilter = MimeTypeMapper.mimeTypeRegexForContentType(contentType) | ||
1207 | 60 | downloadPage.multiSelect = model.allowMultipleFiles | ||
1208 | 61 | downloadPage.selectMode = false | ||
1209 | 62 | downloadPage.pickingMode = true | ||
1210 | 63 | downloadPage.internalFilePicker = model | ||
1211 | 64 | Popups.PopupUtils.close(picker) | ||
1212 | 65 | } else { | ||
1213 | 66 | if (model.allowMultipleFiles) { | ||
1214 | 67 | peer.selectionType = ContentTransfer.Multiple | ||
1215 | 68 | } else { | ||
1216 | 69 | peer.selectionType = ContentTransfer.Single | ||
1217 | 70 | } | ||
1218 | 71 | picker.activeTransfer = peer.request() | ||
1219 | 72 | stateChangeConnection.target = picker.activeTransfer | ||
1220 | 73 | } | ||
1221 | 74 | } | ||
1222 | 75 | |||
1223 | 76 | onCancelPressed: { | ||
1224 | 77 | model.reject() | ||
1225 | 78 | } | ||
1226 | 79 | } | ||
1227 | 80 | } | ||
1228 | 81 | |||
1229 | 82 | Connections { | ||
1230 | 83 | id: stateChangeConnection | ||
1231 | 84 | target: null | ||
1232 | 85 | onStateChanged: { | ||
1233 | 86 | if (picker.activeTransfer.state === ContentTransfer.Charged) { | ||
1234 | 87 | var selectedItems = [] | ||
1235 | 88 | for(var i in picker.activeTransfer.items) { | ||
1236 | 89 | selectedItems.push(String(picker.activeTransfer.items[i].url).replace("file://", "")) | ||
1237 | 90 | } | ||
1238 | 91 | model.accept(selectedItems) | ||
1239 | 92 | } | ||
1240 | 93 | } | ||
1241 | 94 | } | ||
1242 | 95 | |||
1243 | 96 | Component.onCompleted: { | ||
1244 | 97 | if(acceptTypes.length === 1) { | ||
1245 | 98 | var contentType = MimeTypeMapper.mimeTypeToContentType(acceptTypes[0]) | ||
1246 | 99 | if(contentType == ContentType.Unknown) { | ||
1247 | 100 | // If we don't recognise the type, allow uploads from any app | ||
1248 | 101 | contentType = ContentType.All | ||
1249 | 102 | } | ||
1250 | 103 | peerPicker.contentType = contentType | ||
1251 | 104 | } else { | ||
1252 | 105 | peerPicker.contentType = ContentType.All | ||
1253 | 106 | } | ||
1254 | 107 | show() | ||
1255 | 108 | } | ||
1256 | 109 | } | ||
1257 | 110 | } | ||
1258 | 0 | 111 | ||
1259 | === added file 'src/app/webbrowser/DownloadDelegate.qml' | |||
1260 | --- src/app/webbrowser/DownloadDelegate.qml 1970-01-01 00:00:00 +0000 | |||
1261 | +++ src/app/webbrowser/DownloadDelegate.qml 2015-12-16 16:25:40 +0000 | |||
1262 | @@ -0,0 +1,226 @@ | |||
1263 | 1 | /* | ||
1264 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
1265 | 3 | * | ||
1266 | 4 | * This file is part of webbrowser-app. | ||
1267 | 5 | * | ||
1268 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1269 | 7 | * it under the terms of the GNU General Public License as published by | ||
1270 | 8 | * the Free Software Foundation; version 3. | ||
1271 | 9 | * | ||
1272 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1273 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1274 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1275 | 13 | * GNU General Public License for more details. | ||
1276 | 14 | * | ||
1277 | 15 | * You should have received a copy of the GNU General Public License | ||
1278 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1279 | 17 | */ | ||
1280 | 18 | |||
1281 | 19 | import QtQuick 2.0 | ||
1282 | 20 | import Ubuntu.Components 1.3 | ||
1283 | 21 | import ".." | ||
1284 | 22 | |||
1285 | 23 | ListItem { | ||
1286 | 24 | id: downloadDelegate | ||
1287 | 25 | |||
1288 | 26 | property alias icon: mimeicon.name | ||
1289 | 27 | property alias image: thumbimage.source | ||
1290 | 28 | property alias title: title.text | ||
1291 | 29 | property alias url: url.text | ||
1292 | 30 | property string errorMessage | ||
1293 | 31 | property bool incomplete: false | ||
1294 | 32 | property string downloadId | ||
1295 | 33 | property var download | ||
1296 | 34 | property int progress: download ? download.progress : 0 | ||
1297 | 35 | property bool paused | ||
1298 | 36 | |||
1299 | 37 | divider.visible: false | ||
1300 | 38 | |||
1301 | 39 | signal removed() | ||
1302 | 40 | signal cancelled() | ||
1303 | 41 | |||
1304 | 42 | height: visible ? (incomplete ? (paused ? units.gu(13) : units.gu(10)) : units.gu(7)) : 0 | ||
1305 | 43 | |||
1306 | 44 | Component.onCompleted: { | ||
1307 | 45 | if (incomplete) { | ||
1308 | 46 | // Connect to download object | ||
1309 | 47 | for(var i = 0; i < downloadManager.downloads.length; i++) { | ||
1310 | 48 | if (downloadManager.downloads[i].downloadId == downloadId) { | ||
1311 | 49 | download = downloadManager.downloads[i] | ||
1312 | 50 | } | ||
1313 | 51 | } | ||
1314 | 52 | } | ||
1315 | 53 | } | ||
1316 | 54 | |||
1317 | 55 | Item { | ||
1318 | 56 | |||
1319 | 57 | anchors { | ||
1320 | 58 | verticalCenter: parent.verticalCenter | ||
1321 | 59 | left: parent.left | ||
1322 | 60 | leftMargin: units.gu(2) | ||
1323 | 61 | right: parent.right | ||
1324 | 62 | } | ||
1325 | 63 | |||
1326 | 64 | Item { | ||
1327 | 65 | id: iconContainer | ||
1328 | 66 | width: units.gu(3) | ||
1329 | 67 | height: width | ||
1330 | 68 | anchors.verticalCenter: parent.verticalCenter | ||
1331 | 69 | anchors.verticalCenterOffset: downloadDelegate.incomplete ? -units.gu(1) : 0 | ||
1332 | 70 | |||
1333 | 71 | Image { | ||
1334 | 72 | id: thumbimage | ||
1335 | 73 | asynchronous: true | ||
1336 | 74 | width: parent.width | ||
1337 | 75 | height: parent.height | ||
1338 | 76 | fillMode: Image.PreserveAspectFit | ||
1339 | 77 | sourceSize.width: parent.width | ||
1340 | 78 | sourceSize.height: parent.height | ||
1341 | 79 | anchors.verticalCenter: parent.verticalCenter | ||
1342 | 80 | } | ||
1343 | 81 | |||
1344 | 82 | Image { | ||
1345 | 83 | id: mimeicon | ||
1346 | 84 | asynchronous: true | ||
1347 | 85 | anchors.fill: parent | ||
1348 | 86 | anchors.margins: units.gu(0.2) | ||
1349 | 87 | source: "image://theme/%1".arg(name != "" ? name : "save") | ||
1350 | 88 | visible: thumbimage.status !== Image.Ready | ||
1351 | 89 | cache: true | ||
1352 | 90 | property string name | ||
1353 | 91 | } | ||
1354 | 92 | } | ||
1355 | 93 | |||
1356 | 94 | Item { | ||
1357 | 95 | anchors.top: iconContainer.top | ||
1358 | 96 | anchors.left: iconContainer.right | ||
1359 | 97 | anchors.leftMargin: units.gu(2) | ||
1360 | 98 | anchors.right: parent.right | ||
1361 | 99 | |||
1362 | 100 | Column { | ||
1363 | 101 | id: detailsColumn | ||
1364 | 102 | width: parent.width - cancelColumn.width | ||
1365 | 103 | height: parent.height | ||
1366 | 104 | |||
1367 | 105 | Label { | ||
1368 | 106 | id: title | ||
1369 | 107 | fontSize: "x-small" | ||
1370 | 108 | color: "#5d5d5d" | ||
1371 | 109 | elide: Text.ElideRight | ||
1372 | 110 | width: parent.width | ||
1373 | 111 | } | ||
1374 | 112 | |||
1375 | 113 | Label { | ||
1376 | 114 | id: url | ||
1377 | 115 | fontSize: "x-small" | ||
1378 | 116 | color: "#5d5d5d" | ||
1379 | 117 | elide: Text.ElideRight | ||
1380 | 118 | width: parent.width | ||
1381 | 119 | } | ||
1382 | 120 | |||
1383 | 121 | Item { | ||
1384 | 122 | height: error.visible ? units.gu(1) : units.gu(2) | ||
1385 | 123 | width: parent.width | ||
1386 | 124 | visible: downloadDelegate.incomplete | ||
1387 | 125 | } | ||
1388 | 126 | |||
1389 | 127 | Item { | ||
1390 | 128 | id: error | ||
1391 | 129 | visible: incomplete && download === undefined || errorMessage !== "" | ||
1392 | 130 | height: units.gu(3) | ||
1393 | 131 | width: parent.width | ||
1394 | 132 | |||
1395 | 133 | Icon { | ||
1396 | 134 | id: errorIcon | ||
1397 | 135 | width: units.gu(2) | ||
1398 | 136 | height: width | ||
1399 | 137 | anchors.verticalCenter: parent.verticalCenter | ||
1400 | 138 | name: "dialog-warning-symbolic" | ||
1401 | 139 | color: UbuntuColors.red | ||
1402 | 140 | } | ||
1403 | 141 | |||
1404 | 142 | Label { | ||
1405 | 143 | width: parent.width - errorIcon.width | ||
1406 | 144 | anchors.left: errorIcon.right | ||
1407 | 145 | anchors.leftMargin: units.gu(1) | ||
1408 | 146 | anchors.verticalCenter: errorIcon.verticalCenter | ||
1409 | 147 | fontSize: "x-small" | ||
1410 | 148 | color: UbuntuColors.red | ||
1411 | 149 | text: errorMessage !== "" ? errorMessage | ||
1412 | 150 | : (incomplete && download === undefined) ? i18n.tr("Download failed") | ||
1413 | 151 | : "" | ||
1414 | 152 | elide: Text.ElideRight | ||
1415 | 153 | } | ||
1416 | 154 | } | ||
1417 | 155 | |||
1418 | 156 | IndeterminateProgressBar { | ||
1419 | 157 | id: progressBar | ||
1420 | 158 | width: parent.width | ||
1421 | 159 | height: units.gu(0.5) | ||
1422 | 160 | visible: downloadDelegate.incomplete && !error.visible | ||
1423 | 161 | progress: downloadDelegate.progress | ||
1424 | 162 | // Work around UDM bug #1450144 | ||
1425 | 163 | indeterminateProgress: downloadDelegate.progress < 0 || downloadDelegate.progress > 100 | ||
1426 | 164 | } | ||
1427 | 165 | } | ||
1428 | 166 | |||
1429 | 167 | Column { | ||
1430 | 168 | id: cancelColumn | ||
1431 | 169 | spacing: units.gu(1) | ||
1432 | 170 | anchors.top: detailsColumn.top | ||
1433 | 171 | anchors.left: detailsColumn.right | ||
1434 | 172 | anchors.leftMargin: units.gu(2) | ||
1435 | 173 | width: downloadDelegate.incomplete && !error.visible ? cancelButton.width + units.gu(2) : 0 | ||
1436 | 174 | |||
1437 | 175 | Button { | ||
1438 | 176 | visible: downloadDelegate.incomplete && !error.visible | ||
1439 | 177 | id: cancelButton | ||
1440 | 178 | text: i18n.tr("Cancel") | ||
1441 | 179 | onClicked: { | ||
1442 | 180 | if (download) { | ||
1443 | 181 | download.cancel() | ||
1444 | 182 | cancelled() | ||
1445 | 183 | } | ||
1446 | 184 | } | ||
1447 | 185 | } | ||
1448 | 186 | |||
1449 | 187 | Label { | ||
1450 | 188 | visible: !progressBar.indeterminateProgress && downloadDelegate.incomplete | ||
1451 | 189 | && !error.visible | ||
1452 | 190 | && !downloadDelegate.paused | ||
1453 | 191 | width: cancelButton.width | ||
1454 | 192 | horizontalAlignment: Text.AlignHCenter | ||
1455 | 193 | fontSize: "x-small" | ||
1456 | 194 | text: progressBar.progress + "%" | ||
1457 | 195 | } | ||
1458 | 196 | |||
1459 | 197 | Button { | ||
1460 | 198 | visible: downloadDelegate.paused | ||
1461 | 199 | text: i18n.tr("Resume") | ||
1462 | 200 | width: cancelButton.width | ||
1463 | 201 | onClicked: { | ||
1464 | 202 | if (download) { | ||
1465 | 203 | download.resume() | ||
1466 | 204 | } | ||
1467 | 205 | } | ||
1468 | 206 | } | ||
1469 | 207 | } | ||
1470 | 208 | |||
1471 | 209 | } | ||
1472 | 210 | } | ||
1473 | 211 | |||
1474 | 212 | leadingActions: error.visible || !downloadDelegate.incomplete ? deleteActionList : null | ||
1475 | 213 | |||
1476 | 214 | ListItemActions { | ||
1477 | 215 | id: deleteActionList | ||
1478 | 216 | actions: [ | ||
1479 | 217 | Action { | ||
1480 | 218 | objectName: "leadingAction.delete" | ||
1481 | 219 | iconName: "delete" | ||
1482 | 220 | enabled: error.visible || !downloadDelegate.incomplete | ||
1483 | 221 | onTriggered: error.visible ? downloadDelegate.cancelled() | ||
1484 | 222 | : downloadDelegate.removed() | ||
1485 | 223 | } | ||
1486 | 224 | ] | ||
1487 | 225 | } | ||
1488 | 226 | } | ||
1489 | 0 | 227 | ||
1490 | === added file 'src/app/webbrowser/DownloadHandler.qml' | |||
1491 | --- src/app/webbrowser/DownloadHandler.qml 1970-01-01 00:00:00 +0000 | |||
1492 | +++ src/app/webbrowser/DownloadHandler.qml 2015-12-16 16:25:40 +0000 | |||
1493 | @@ -0,0 +1,45 @@ | |||
1494 | 1 | /* | ||
1495 | 2 | * Copyright 2015 Canonical Ltd. | ||
1496 | 3 | * | ||
1497 | 4 | * This file is part of webbrowser-app. | ||
1498 | 5 | * | ||
1499 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1500 | 7 | * it under the terms of the GNU General Public License as published by | ||
1501 | 8 | * the Free Software Foundation; version 3. | ||
1502 | 9 | * | ||
1503 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1504 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1505 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1506 | 13 | * GNU General Public License for more details. | ||
1507 | 14 | * | ||
1508 | 15 | * You should have received a copy of the GNU General Public License | ||
1509 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1510 | 17 | */ | ||
1511 | 18 | |||
1512 | 19 | import QtQuick 2.4 | ||
1513 | 20 | import Ubuntu.DownloadManager 1.2 | ||
1514 | 21 | |||
1515 | 22 | DownloadManager { | ||
1516 | 23 | id: downloadManager | ||
1517 | 24 | |||
1518 | 25 | onDownloadFinished: { | ||
1519 | 26 | downloadsModel.moveToDownloads(download.downloadId, path) | ||
1520 | 27 | downloadsModel.setComplete(download.downloadId, true) | ||
1521 | 28 | } | ||
1522 | 29 | |||
1523 | 30 | onDownloadPaused: { | ||
1524 | 31 | downloadsModel.pauseDownload(download.downloadId) | ||
1525 | 32 | } | ||
1526 | 33 | |||
1527 | 34 | onDownloadResumed: { | ||
1528 | 35 | downloadsModel.resumeDownload(download.downloadId) | ||
1529 | 36 | } | ||
1530 | 37 | |||
1531 | 38 | onDownloadCanceled: { | ||
1532 | 39 | downloadsModel.cancelDownload(download.downloadId) | ||
1533 | 40 | } | ||
1534 | 41 | |||
1535 | 42 | onErrorFound: { | ||
1536 | 43 | downloadsModel.setError(download.downloadId, download.errorMessage) | ||
1537 | 44 | } | ||
1538 | 45 | } | ||
1539 | 0 | 46 | ||
1540 | === added file 'src/app/webbrowser/DownloadsModel.qml' | |||
1541 | --- src/app/webbrowser/DownloadsModel.qml 1970-01-01 00:00:00 +0000 | |||
1542 | +++ src/app/webbrowser/DownloadsModel.qml 2015-12-16 16:25:40 +0000 | |||
1543 | @@ -0,0 +1,24 @@ | |||
1544 | 1 | /* | ||
1545 | 2 | * Copyright 2015 Canonical Ltd. | ||
1546 | 3 | * | ||
1547 | 4 | * This file is part of webbrowser-app. | ||
1548 | 5 | * | ||
1549 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1550 | 7 | * it under the terms of the GNU General Public License as published by | ||
1551 | 8 | * the Free Software Foundation; version 3. | ||
1552 | 9 | * | ||
1553 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1554 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1555 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1556 | 13 | * GNU General Public License for more details. | ||
1557 | 14 | * | ||
1558 | 15 | * You should have received a copy of the GNU General Public License | ||
1559 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1560 | 17 | */ | ||
1561 | 18 | |||
1562 | 19 | import QtQuick 2.0 | ||
1563 | 20 | import webbrowserapp.private 0.1 | ||
1564 | 21 | |||
1565 | 22 | DownloadsModel { | ||
1566 | 23 | databasePath: dataLocation + "/downloads.sqlite" | ||
1567 | 24 | } | ||
1568 | 0 | 25 | ||
1569 | === added file 'src/app/webbrowser/DownloadsPage.qml' | |||
1570 | --- src/app/webbrowser/DownloadsPage.qml 1970-01-01 00:00:00 +0000 | |||
1571 | +++ src/app/webbrowser/DownloadsPage.qml 2015-12-16 16:25:40 +0000 | |||
1572 | @@ -0,0 +1,253 @@ | |||
1573 | 1 | /* | ||
1574 | 2 | * Copyright 2015 Canonical Ltd. | ||
1575 | 3 | * | ||
1576 | 4 | * This file is part of webbrowser-app. | ||
1577 | 5 | * | ||
1578 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1579 | 7 | * it under the terms of the GNU General Public License as published by | ||
1580 | 8 | * the Free Software Foundation; version 3. | ||
1581 | 9 | * | ||
1582 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1583 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1584 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1585 | 13 | * GNU General Public License for more details. | ||
1586 | 14 | * | ||
1587 | 15 | * You should have received a copy of the GNU General Public License | ||
1588 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1589 | 17 | */ | ||
1590 | 18 | |||
1591 | 19 | import QtQuick 2.0 | ||
1592 | 20 | import Qt.labs.settings 1.0 | ||
1593 | 21 | import Ubuntu.Components 1.3 | ||
1594 | 22 | import Ubuntu.Components.Popups 1.0 | ||
1595 | 23 | import Ubuntu.Thumbnailer 0.1 | ||
1596 | 24 | import Ubuntu.Content 1.3 | ||
1597 | 25 | import Ubuntu.Web 0.2 | ||
1598 | 26 | import webbrowserapp.private 0.1 | ||
1599 | 27 | import webbrowsercommon.private 0.1 | ||
1600 | 28 | |||
1601 | 29 | import "../MimeTypeMapper.js" as MimeTypeMapper | ||
1602 | 30 | |||
1603 | 31 | Item { | ||
1604 | 32 | id: downloadsItem | ||
1605 | 33 | |||
1606 | 34 | property QtObject downloadsModel | ||
1607 | 35 | |||
1608 | 36 | // We can get file picking requests either via content-hub (activeTransfer) | ||
1609 | 37 | // Or via the internal oxide file picker (internalFilePicker) in the case | ||
1610 | 38 | // where the user wishes to upload a file from their previous downloads. | ||
1611 | 39 | property var activeTransfer | ||
1612 | 40 | property var internalFilePicker | ||
1613 | 41 | |||
1614 | 42 | property bool selectMode | ||
1615 | 43 | property bool pickingMode | ||
1616 | 44 | property bool multiSelect | ||
1617 | 45 | property alias mimetypeFilter: downloadModelFilter.pattern | ||
1618 | 46 | |||
1619 | 47 | signal done() | ||
1620 | 48 | |||
1621 | 49 | Rectangle { | ||
1622 | 50 | anchors.fill: parent | ||
1623 | 51 | color: "#fbfbfb" | ||
1624 | 52 | } | ||
1625 | 53 | |||
1626 | 54 | BrowserPageHeader { | ||
1627 | 55 | id: title | ||
1628 | 56 | text: i18n.tr("Downloads") | ||
1629 | 57 | color: "#f7f7f7" | ||
1630 | 58 | actions: [ | ||
1631 | 59 | Action { | ||
1632 | 60 | text: i18n.tr("Confirm selection") | ||
1633 | 61 | iconName: "tick" | ||
1634 | 62 | visible: pickingMode | ||
1635 | 63 | enabled: downloadsListView.ViewItems.selectedIndices.length > 0 | ||
1636 | 64 | onTriggered: { | ||
1637 | 65 | var results = [] | ||
1638 | 66 | if (internalFilePicker) { | ||
1639 | 67 | for (var i = 0; i < downloadsListView.ViewItems.selectedIndices.length; i++) { | ||
1640 | 68 | var selectedDownload = downloadsListView.model.get(downloadsListView.ViewItems.selectedIndices[i]) | ||
1641 | 69 | results.push(selectedDownload.path) | ||
1642 | 70 | } | ||
1643 | 71 | internalFilePicker.accept(results) | ||
1644 | 72 | } else { | ||
1645 | 73 | for (var i = 0; i < downloadsListView.ViewItems.selectedIndices.length; i++) { | ||
1646 | 74 | var selectedDownload = downloadsListView.model.get(downloadsListView.ViewItems.selectedIndices[i]) | ||
1647 | 75 | results.push(resultComponent.createObject(downloadsItem, {"url": "file://" + selectedDownload.path})) | ||
1648 | 76 | } | ||
1649 | 77 | activeTransfer.items = results | ||
1650 | 78 | activeTransfer.state = ContentTransfer.Charged | ||
1651 | 79 | } | ||
1652 | 80 | downloadsItem.done() | ||
1653 | 81 | } | ||
1654 | 82 | }, | ||
1655 | 83 | Action { | ||
1656 | 84 | text: i18n.tr("Select all") | ||
1657 | 85 | iconName: "select" | ||
1658 | 86 | visible: selectMode | ||
1659 | 87 | onTriggered: { | ||
1660 | 88 | if (downloadsListView.ViewItems.selectedIndices.length === downloadsListView.count) { | ||
1661 | 89 | downloadsListView.ViewItems.selectedIndices = [] | ||
1662 | 90 | } else { | ||
1663 | 91 | var indices = [] | ||
1664 | 92 | for (var i = 0; i < downloadsListView.count; ++i) { | ||
1665 | 93 | indices.push(i) | ||
1666 | 94 | } | ||
1667 | 95 | downloadsListView.ViewItems.selectedIndices = indices | ||
1668 | 96 | } | ||
1669 | 97 | } | ||
1670 | 98 | }, | ||
1671 | 99 | Action { | ||
1672 | 100 | text: i18n.tr("Delete") | ||
1673 | 101 | iconName: "delete" | ||
1674 | 102 | visible: selectMode | ||
1675 | 103 | onTriggered: { | ||
1676 | 104 | var toDelete = [] | ||
1677 | 105 | for (var i = 0; i < downloadsListView.ViewItems.selectedIndices.length; i++) { | ||
1678 | 106 | var selectedDownload = downloadsListView.model.get(downloadsListView.ViewItems.selectedIndices[i]) | ||
1679 | 107 | toDelete.push(selectedDownload.path) | ||
1680 | 108 | } | ||
1681 | 109 | for (var i = 0; i < toDelete.length; i++) { | ||
1682 | 110 | downloadsModel.deleteDownload(toDelete[i]) | ||
1683 | 111 | } | ||
1684 | 112 | downloadsListView.ViewItems.selectedIndices = [] | ||
1685 | 113 | downloadsItem.selectMode = false | ||
1686 | 114 | } | ||
1687 | 115 | }, | ||
1688 | 116 | Action { | ||
1689 | 117 | iconName: "edit" | ||
1690 | 118 | visible: !selectMode && !pickingMode | ||
1691 | 119 | onTriggered: { | ||
1692 | 120 | selectMode = true | ||
1693 | 121 | multiSelect = true | ||
1694 | 122 | } | ||
1695 | 123 | } | ||
1696 | 124 | ] | ||
1697 | 125 | onBack: { | ||
1698 | 126 | if (selectMode) { | ||
1699 | 127 | selectMode = false | ||
1700 | 128 | } else { | ||
1701 | 129 | if (activeTransfer) { | ||
1702 | 130 | activeTransfer.state = ContentTransfer.Aborted | ||
1703 | 131 | } | ||
1704 | 132 | if (internalFilePicker) { | ||
1705 | 133 | internalFilePicker.reject() | ||
1706 | 134 | } | ||
1707 | 135 | downloadsItem.done() | ||
1708 | 136 | } | ||
1709 | 137 | } | ||
1710 | 138 | } | ||
1711 | 139 | |||
1712 | 140 | Component { | ||
1713 | 141 | id: resultComponent | ||
1714 | 142 | ContentItem { } | ||
1715 | 143 | } | ||
1716 | 144 | |||
1717 | 145 | ListView { | ||
1718 | 146 | id: downloadsListView | ||
1719 | 147 | clip: true | ||
1720 | 148 | |||
1721 | 149 | anchors { | ||
1722 | 150 | top: title.bottom | ||
1723 | 151 | left: parent.left | ||
1724 | 152 | right: parent.right | ||
1725 | 153 | bottom: parent.bottom | ||
1726 | 154 | rightMargin: units.gu(2) | ||
1727 | 155 | } | ||
1728 | 156 | |||
1729 | 157 | model: SortFilterModel { | ||
1730 | 158 | model: downloadsModel | ||
1731 | 159 | filter { | ||
1732 | 160 | id: downloadModelFilter | ||
1733 | 161 | property: "mimetype" | ||
1734 | 162 | } | ||
1735 | 163 | } | ||
1736 | 164 | |||
1737 | 165 | delegate: DownloadDelegate { | ||
1738 | 166 | downloadId: model.downloadId | ||
1739 | 167 | title: model.filename ? model.filename : model.url.toString().split('/').pop().split('?').shift() | ||
1740 | 168 | url: model.url | ||
1741 | 169 | image: model.complete && (model.mimetype.indexOf("image") === 0 || model.mimetype.indexOf("video") === 0) ? "image://thumbnailer/file://" + model.path : "" | ||
1742 | 170 | icon: MimeDatabase.iconForMimetype(model.mimetype) | ||
1743 | 171 | incomplete: !model.complete | ||
1744 | 172 | selectMode: downloadsItem.selectMode || downloadsItem.pickingMode | ||
1745 | 173 | visible: !(selectMode && incomplete) | ||
1746 | 174 | errorMessage: model.error | ||
1747 | 175 | paused: model.paused | ||
1748 | 176 | // Work around bug #1493880 | ||
1749 | 177 | property bool lastSelected | ||
1750 | 178 | |||
1751 | 179 | onSelectedChanged: { | ||
1752 | 180 | if (!multiSelect && selected && lastSelected != selected) { | ||
1753 | 181 | downloadsListView.ViewItems.selectedIndices = [index] | ||
1754 | 182 | } | ||
1755 | 183 | lastSelected = selected | ||
1756 | 184 | } | ||
1757 | 185 | |||
1758 | 186 | onClicked: { | ||
1759 | 187 | if (model.complete) { | ||
1760 | 188 | if (selectMode) { | ||
1761 | 189 | selected = !selected | ||
1762 | 190 | } else { | ||
1763 | 191 | exportPeerPicker.contentType = MimeTypeMapper.mimeTypeToContentType(model.mimetype) | ||
1764 | 192 | exportPeerPicker.visible = true | ||
1765 | 193 | exportPeerPicker.path = model.path | ||
1766 | 194 | } | ||
1767 | 195 | } | ||
1768 | 196 | } | ||
1769 | 197 | |||
1770 | 198 | onPressAndHold: { | ||
1771 | 199 | downloadsItem.selectMode = true | ||
1772 | 200 | downloadsItem.multiSelect = true | ||
1773 | 201 | if (downloadsItem.selectMode) { | ||
1774 | 202 | downloadsListView.ViewItems.selectedIndices = [index] | ||
1775 | 203 | } | ||
1776 | 204 | } | ||
1777 | 205 | |||
1778 | 206 | onRemoved: { | ||
1779 | 207 | if (model.complete) { | ||
1780 | 208 | downloadsModel.deleteDownload(model.path) | ||
1781 | 209 | } | ||
1782 | 210 | } | ||
1783 | 211 | |||
1784 | 212 | onCancelled: { | ||
1785 | 213 | downloadsModel.cancelDownload(model.downloadId) | ||
1786 | 214 | } | ||
1787 | 215 | } | ||
1788 | 216 | |||
1789 | 217 | } | ||
1790 | 218 | |||
1791 | 219 | Label { | ||
1792 | 220 | id: emptyLabel | ||
1793 | 221 | anchors.centerIn: parent | ||
1794 | 222 | visible: downloadsListView.count == 0 | ||
1795 | 223 | wrapMode: Text.Wrap | ||
1796 | 224 | width: parent.width | ||
1797 | 225 | horizontalAlignment: Text.AlignHCenter | ||
1798 | 226 | text: i18n.tr("No downloads available") | ||
1799 | 227 | } | ||
1800 | 228 | |||
1801 | 229 | Component { | ||
1802 | 230 | id: contentItemComponent | ||
1803 | 231 | ContentItem {} | ||
1804 | 232 | } | ||
1805 | 233 | |||
1806 | 234 | ContentPeerPicker { | ||
1807 | 235 | id: exportPeerPicker | ||
1808 | 236 | visible: false | ||
1809 | 237 | anchors.fill: parent | ||
1810 | 238 | handler: ContentHandler.Destination | ||
1811 | 239 | property string path | ||
1812 | 240 | onPeerSelected: { | ||
1813 | 241 | var transfer = peer.request() | ||
1814 | 242 | if (transfer.state === ContentTransfer.InProgress) { | ||
1815 | 243 | transfer.items = [contentItemComponent.createObject(downloadsItem, {"url": path})] | ||
1816 | 244 | transfer.state = ContentTransfer.Charged | ||
1817 | 245 | } | ||
1818 | 246 | visible = false | ||
1819 | 247 | } | ||
1820 | 248 | onCancelPressed: { | ||
1821 | 249 | visible = false | ||
1822 | 250 | } | ||
1823 | 251 | } | ||
1824 | 252 | |||
1825 | 253 | } | ||
1826 | 0 | 254 | ||
1827 | === added file 'src/app/webbrowser/IndeterminateProgressBar.qml' | |||
1828 | --- src/app/webbrowser/IndeterminateProgressBar.qml 1970-01-01 00:00:00 +0000 | |||
1829 | +++ src/app/webbrowser/IndeterminateProgressBar.qml 2015-12-16 16:25:40 +0000 | |||
1830 | @@ -0,0 +1,51 @@ | |||
1831 | 1 | /* | ||
1832 | 2 | * Copyright 2015 Canonical Ltd. | ||
1833 | 3 | * | ||
1834 | 4 | * This file is part of webbrowser-app. | ||
1835 | 5 | * | ||
1836 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1837 | 7 | * it under the terms of the GNU General Public License as published by | ||
1838 | 8 | * the Free Software Foundation; version 3. | ||
1839 | 9 | * | ||
1840 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1841 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1842 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1843 | 13 | * GNU General Public License for more details. | ||
1844 | 14 | * | ||
1845 | 15 | * You should have received a copy of the GNU General Public License | ||
1846 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1847 | 17 | */ | ||
1848 | 18 | |||
1849 | 19 | import QtQuick 2.4 | ||
1850 | 20 | import Ubuntu.Components 1.3 | ||
1851 | 21 | |||
1852 | 22 | Rectangle { | ||
1853 | 23 | id: progressBar | ||
1854 | 24 | |||
1855 | 25 | property real progress | ||
1856 | 26 | property bool indeterminateProgress: false | ||
1857 | 27 | |||
1858 | 28 | radius: width/3 | ||
1859 | 29 | color: Theme.palette.normal.base | ||
1860 | 30 | |||
1861 | 31 | Rectangle { | ||
1862 | 32 | id: currentProgress | ||
1863 | 33 | height: parent.height | ||
1864 | 34 | radius: parent.radius | ||
1865 | 35 | anchors.left: parent.left | ||
1866 | 36 | anchors.leftMargin: 0 | ||
1867 | 37 | anchors.top: parent.top | ||
1868 | 38 | color: UbuntuColors.orange | ||
1869 | 39 | width: indeterminateProgress ? parent.width / 6 : (progress / 100) * parent.width | ||
1870 | 40 | |||
1871 | 41 | SequentialAnimation { | ||
1872 | 42 | running: indeterminateProgress | ||
1873 | 43 | onRunningChanged: { | ||
1874 | 44 | currentProgress.anchors.leftMargin = 0; | ||
1875 | 45 | } | ||
1876 | 46 | loops: Animation.Infinite | ||
1877 | 47 | PropertyAnimation { target: currentProgress.anchors; property: "leftMargin"; from: 0.0; to: parent.width - parent.width / 6; duration: UbuntuAnimation.SleepyDuration; easing.type: Easing.InOutQuad; } | ||
1878 | 48 | PropertyAnimation { target: currentProgress.anchors; property: "leftMargin"; from: parent.width - parent.width / 6; to: 0; duration: UbuntuAnimation.SleepyDuration; easing.type: Easing.InOutQuad; } | ||
1879 | 49 | } | ||
1880 | 50 | } | ||
1881 | 51 | } | ||
1882 | 0 | 52 | ||
1883 | === modified file 'src/app/webbrowser/SettingsPage.qml' | |||
1884 | --- src/app/webbrowser/SettingsPage.qml 2015-11-17 16:25:30 +0000 | |||
1885 | +++ src/app/webbrowser/SettingsPage.qml 2015-12-16 16:25:40 +0000 | |||
1886 | @@ -43,7 +43,7 @@ | |||
1887 | 43 | searchPaths: searchEnginesSearchPaths | 43 | searchPaths: searchEnginesSearchPaths |
1888 | 44 | } | 44 | } |
1889 | 45 | 45 | ||
1891 | 46 | SettingsPageHeader { | 46 | BrowserPageHeader { |
1892 | 47 | id: title | 47 | id: title |
1893 | 48 | 48 | ||
1894 | 49 | onBack: settingsItem.done() | 49 | onBack: settingsItem.done() |
1895 | @@ -168,7 +168,7 @@ | |||
1896 | 168 | color: "#f6f6f6" | 168 | color: "#f6f6f6" |
1897 | 169 | } | 169 | } |
1898 | 170 | 170 | ||
1900 | 171 | SettingsPageHeader { | 171 | BrowserPageHeader { |
1901 | 172 | id: searchEngineTitle | 172 | id: searchEngineTitle |
1902 | 173 | 173 | ||
1903 | 174 | onBack: searchEngineItem.destroy() | 174 | onBack: searchEngineItem.destroy() |
1904 | @@ -221,7 +221,7 @@ | |||
1905 | 221 | color: "#f6f6f6" | 221 | color: "#f6f6f6" |
1906 | 222 | } | 222 | } |
1907 | 223 | 223 | ||
1909 | 224 | SettingsPageHeader { | 224 | BrowserPageHeader { |
1910 | 225 | id: privacyTitle | 225 | id: privacyTitle |
1911 | 226 | onBack: privacyItem.destroy() | 226 | onBack: privacyItem.destroy() |
1912 | 227 | text: i18n.tr("Privacy & permissions") | 227 | text: i18n.tr("Privacy & permissions") |
1913 | @@ -379,7 +379,7 @@ | |||
1914 | 379 | color: "#f6f6f6" | 379 | color: "#f6f6f6" |
1915 | 380 | } | 380 | } |
1916 | 381 | 381 | ||
1918 | 382 | SettingsPageHeader { | 382 | BrowserPageHeader { |
1919 | 383 | id: mediaAccessTitle | 383 | id: mediaAccessTitle |
1920 | 384 | 384 | ||
1921 | 385 | onBack: mediaAccessItem.destroy() | 385 | onBack: mediaAccessItem.destroy() |
1922 | 386 | 386 | ||
1923 | === added file 'src/app/webbrowser/downloads-model.cpp' | |||
1924 | --- src/app/webbrowser/downloads-model.cpp 1970-01-01 00:00:00 +0000 | |||
1925 | +++ src/app/webbrowser/downloads-model.cpp 2015-12-16 16:25:40 +0000 | |||
1926 | @@ -0,0 +1,412 @@ | |||
1927 | 1 | /* | ||
1928 | 2 | * Copyright 2015 Canonical Ltd. | ||
1929 | 3 | * | ||
1930 | 4 | * This file is part of webbrowser-app. | ||
1931 | 5 | * | ||
1932 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
1933 | 7 | * it under the terms of the GNU General Public License as published by | ||
1934 | 8 | * the Free Software Foundation; version 3. | ||
1935 | 9 | * | ||
1936 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
1937 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1938 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1939 | 13 | * GNU General Public License for more details. | ||
1940 | 14 | * | ||
1941 | 15 | * You should have received a copy of the GNU General Public License | ||
1942 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1943 | 17 | */ | ||
1944 | 18 | |||
1945 | 19 | #include "downloads-model.h" | ||
1946 | 20 | |||
1947 | 21 | #include <QtCore/QDebug> | ||
1948 | 22 | #include <QtCore/QDir> | ||
1949 | 23 | #include <QtSql/QSqlQuery> | ||
1950 | 24 | #include <QtCore/QFile> | ||
1951 | 25 | #include <QtCore/QFileInfo> | ||
1952 | 26 | #include <QtCore/QStandardPaths> | ||
1953 | 27 | #include <QtCore/QMimeDatabase> | ||
1954 | 28 | #include <QtCore/QMimeType> | ||
1955 | 29 | |||
1956 | 30 | #define CONNECTION_NAME "webbrowser-app-downloads" | ||
1957 | 31 | |||
1958 | 32 | /*! | ||
1959 | 33 | \class DownloadsModel | ||
1960 | 34 | \brief List model that stores information about downloaded files. | ||
1961 | 35 | |||
1962 | 36 | DownloadsModel is a list model that stores information about files that | ||
1963 | 37 | have been downloaded by the browser and stored permanently | ||
1964 | 38 | (e.g. in ~/Downloads), as opposed to those that were sent directly to | ||
1965 | 39 | another application after download. For each download the original URL, the | ||
1966 | 40 | path to the downloaded file, the file mimetype and the download time are | ||
1967 | 41 | stored. The model is sorted chronologically to display the most recent | ||
1968 | 42 | download first. | ||
1969 | 43 | |||
1970 | 44 | The information is persistently stored on disk in a SQLite database. | ||
1971 | 45 | The database is read at startup to populate the model, and whenever a new | ||
1972 | 46 | entry is added to the model or an entry is removed from the model | ||
1973 | 47 | the database is updated. Removing a download from the model also results | ||
1974 | 48 | in it being deleted from the disk. | ||
1975 | 49 | The model doesn’t monitor the database for external changes, but does check | ||
1976 | 50 | that downloaded files still exist when first populating. | ||
1977 | 51 | */ | ||
1978 | 52 | DownloadsModel::DownloadsModel(QObject* parent) | ||
1979 | 53 | : QAbstractListModel(parent) | ||
1980 | 54 | , m_numRows(0) | ||
1981 | 55 | , m_fetchedCount(0) | ||
1982 | 56 | , m_canFetchMore(true) | ||
1983 | 57 | { | ||
1984 | 58 | m_database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), CONNECTION_NAME); | ||
1985 | 59 | } | ||
1986 | 60 | |||
1987 | 61 | DownloadsModel::~DownloadsModel() | ||
1988 | 62 | { | ||
1989 | 63 | m_database.close(); | ||
1990 | 64 | m_database = QSqlDatabase(); | ||
1991 | 65 | QSqlDatabase::removeDatabase(CONNECTION_NAME); | ||
1992 | 66 | } | ||
1993 | 67 | |||
1994 | 68 | void DownloadsModel::resetDatabase(const QString& databaseName) | ||
1995 | 69 | { | ||
1996 | 70 | beginResetModel(); | ||
1997 | 71 | m_orderedEntries.clear(); | ||
1998 | 72 | m_database.close(); | ||
1999 | 73 | m_database.setDatabaseName(databaseName); | ||
2000 | 74 | m_database.open(); | ||
2001 | 75 | m_numRows = 0; | ||
2002 | 76 | m_fetchedCount = 0; | ||
2003 | 77 | m_canFetchMore = true; | ||
2004 | 78 | createOrAlterDatabaseSchema(); | ||
2005 | 79 | endResetModel(); | ||
2006 | 80 | Q_EMIT rowCountChanged(); | ||
2007 | 81 | } | ||
2008 | 82 | |||
2009 | 83 | void DownloadsModel::createOrAlterDatabaseSchema() | ||
2010 | 84 | { | ||
2011 | 85 | QSqlQuery createQuery(m_database); | ||
2012 | 86 | QString query = QLatin1String("CREATE TABLE IF NOT EXISTS downloads " | ||
2013 | 87 | "(downloadId VARCHAR, url VARCHAR, path VARCHAR, " | ||
2014 | 88 | "mimetype VARCHAR, complete BOOL, paused BOOL, " | ||
2015 | 89 | "error VARCHAR, created DATETIME DEFAULT " | ||
2016 | 90 | "CURRENT_TIMESTAMP);"); | ||
2017 | 91 | createQuery.prepare(query); | ||
2018 | 92 | createQuery.exec(); | ||
2019 | 93 | } | ||
2020 | 94 | |||
2021 | 95 | void DownloadsModel::fetchMore(const QModelIndex &parent) | ||
2022 | 96 | { | ||
2023 | 97 | QSqlQuery populateQuery(m_database); | ||
2024 | 98 | QString query = QLatin1String("SELECT downloadId, url, path, mimetype, " | ||
2025 | 99 | "complete, error, created, paused " | ||
2026 | 100 | "FROM downloads ORDER BY created DESC LIMIT 100 OFFSET ?;"); | ||
2027 | 101 | populateQuery.prepare(query); | ||
2028 | 102 | populateQuery.addBindValue(m_fetchedCount); | ||
2029 | 103 | populateQuery.exec(); | ||
2030 | 104 | int count = 0; // size() isn't supported on the sqlite backend | ||
2031 | 105 | while (populateQuery.next()) { | ||
2032 | 106 | DownloadEntry entry; | ||
2033 | 107 | entry.downloadId = populateQuery.value(0).toString(); | ||
2034 | 108 | entry.url = populateQuery.value(1).toUrl(); | ||
2035 | 109 | entry.path = populateQuery.value(2).toString(); | ||
2036 | 110 | entry.mimetype = populateQuery.value(3).toString(); | ||
2037 | 111 | entry.complete = populateQuery.value(4).toBool(); | ||
2038 | 112 | entry.error = populateQuery.value(5).toString(); | ||
2039 | 113 | entry.created = QDateTime::fromTime_t(populateQuery.value(6).toInt()); | ||
2040 | 114 | entry.paused = populateQuery.value(7).toBool(); | ||
2041 | 115 | QFileInfo fileInfo(entry.path); | ||
2042 | 116 | if (fileInfo.exists()) { | ||
2043 | 117 | entry.filename = fileInfo.fileName(); | ||
2044 | 118 | } | ||
2045 | 119 | |||
2046 | 120 | // Only list a completed entry if its file exists, however we don't | ||
2047 | 121 | // remove the entry if the file is missing as it may be stored on a | ||
2048 | 122 | // removable medium like an SD card in the future, so could reappear. | ||
2049 | 123 | if (!entry.complete || fileInfo.exists()) { | ||
2050 | 124 | beginInsertRows(QModelIndex(), m_numRows, m_numRows); | ||
2051 | 125 | m_orderedEntries.append(entry); | ||
2052 | 126 | endInsertRows(); | ||
2053 | 127 | m_numRows++; | ||
2054 | 128 | } | ||
2055 | 129 | count++; | ||
2056 | 130 | } | ||
2057 | 131 | m_fetchedCount += count; | ||
2058 | 132 | if (count == 0) { | ||
2059 | 133 | m_canFetchMore = false; | ||
2060 | 134 | } | ||
2061 | 135 | } | ||
2062 | 136 | |||
2063 | 137 | QHash<int, QByteArray> DownloadsModel::roleNames() const | ||
2064 | 138 | { | ||
2065 | 139 | static QHash<int, QByteArray> roles; | ||
2066 | 140 | if (roles.isEmpty()) { | ||
2067 | 141 | roles[DownloadId] = "downloadId"; | ||
2068 | 142 | roles[Url] = "url"; | ||
2069 | 143 | roles[Path] = "path"; | ||
2070 | 144 | roles[Filename] = "filename"; | ||
2071 | 145 | roles[Mimetype] = "mimetype"; | ||
2072 | 146 | roles[Complete] = "complete"; | ||
2073 | 147 | roles[Paused] = "paused"; | ||
2074 | 148 | roles[Error] = "error"; | ||
2075 | 149 | roles[Created] = "created"; | ||
2076 | 150 | } | ||
2077 | 151 | return roles; | ||
2078 | 152 | } | ||
2079 | 153 | |||
2080 | 154 | int DownloadsModel::rowCount(const QModelIndex& parent) const | ||
2081 | 155 | { | ||
2082 | 156 | Q_UNUSED(parent); | ||
2083 | 157 | return m_orderedEntries.count(); | ||
2084 | 158 | } | ||
2085 | 159 | |||
2086 | 160 | QVariant DownloadsModel::data(const QModelIndex& index, int role) const | ||
2087 | 161 | { | ||
2088 | 162 | if (!index.isValid()) { | ||
2089 | 163 | return QVariant(); | ||
2090 | 164 | } | ||
2091 | 165 | const DownloadEntry& entry = m_orderedEntries.at(index.row()); | ||
2092 | 166 | switch (role) { | ||
2093 | 167 | case DownloadId: | ||
2094 | 168 | return entry.downloadId; | ||
2095 | 169 | case Url: | ||
2096 | 170 | return entry.url; | ||
2097 | 171 | case Path: | ||
2098 | 172 | return entry.path; | ||
2099 | 173 | case Filename: | ||
2100 | 174 | return entry.filename; | ||
2101 | 175 | case Mimetype: | ||
2102 | 176 | return entry.mimetype; | ||
2103 | 177 | case Complete: | ||
2104 | 178 | return entry.complete; | ||
2105 | 179 | case Paused: | ||
2106 | 180 | return entry.paused; | ||
2107 | 181 | case Error: | ||
2108 | 182 | return entry.error; | ||
2109 | 183 | case Created: | ||
2110 | 184 | return entry.created; | ||
2111 | 185 | default: | ||
2112 | 186 | return QVariant(); | ||
2113 | 187 | } | ||
2114 | 188 | } | ||
2115 | 189 | |||
2116 | 190 | const QString DownloadsModel::databasePath() const | ||
2117 | 191 | { | ||
2118 | 192 | return m_database.databaseName(); | ||
2119 | 193 | } | ||
2120 | 194 | |||
2121 | 195 | void DownloadsModel::setDatabasePath(const QString& path) | ||
2122 | 196 | { | ||
2123 | 197 | if (path != databasePath()) { | ||
2124 | 198 | if (path.isEmpty()) { | ||
2125 | 199 | resetDatabase(":memory:"); | ||
2126 | 200 | } else { | ||
2127 | 201 | resetDatabase(path); | ||
2128 | 202 | } | ||
2129 | 203 | Q_EMIT databasePathChanged(); | ||
2130 | 204 | } | ||
2131 | 205 | } | ||
2132 | 206 | |||
2133 | 207 | /*! | ||
2134 | 208 | Add a download to the database. This should happen as soon as the download | ||
2135 | 209 | is started. | ||
2136 | 210 | */ | ||
2137 | 211 | void DownloadsModel::add(const QString& downloadId, const QUrl& url, const QString& mimetype) | ||
2138 | 212 | { | ||
2139 | 213 | beginInsertRows(QModelIndex(), 0, 0); | ||
2140 | 214 | DownloadEntry entry; | ||
2141 | 215 | entry.downloadId = downloadId; | ||
2142 | 216 | entry.complete = false; | ||
2143 | 217 | entry.paused = false; | ||
2144 | 218 | entry.url = url; | ||
2145 | 219 | entry.mimetype = mimetype; | ||
2146 | 220 | m_orderedEntries.prepend(entry); | ||
2147 | 221 | m_numRows++; | ||
2148 | 222 | m_fetchedCount++; | ||
2149 | 223 | endInsertRows(); | ||
2150 | 224 | Q_EMIT added(downloadId, url, mimetype); | ||
2151 | 225 | insertNewEntryInDatabase(entry); | ||
2152 | 226 | Q_EMIT rowCountChanged(); | ||
2153 | 227 | } | ||
2154 | 228 | |||
2155 | 229 | void DownloadsModel::setPath(const QString& downloadId, const QString& path) | ||
2156 | 230 | { | ||
2157 | 231 | QSqlQuery query(m_database); | ||
2158 | 232 | |||
2159 | 233 | // Override reported mimetype from server with detected mimetype from file once downloaded | ||
2160 | 234 | QMimeDatabase mimeDatabase; | ||
2161 | 235 | QString mimetype = mimeDatabase.mimeTypeForFile(path).name(); | ||
2162 | 236 | |||
2163 | 237 | static QString updateStatement = QLatin1String("UPDATE downloads SET mimetype = ?, " | ||
2164 | 238 | "path = ? WHERE downloadId = ?"); | ||
2165 | 239 | query.prepare(updateStatement); | ||
2166 | 240 | query.addBindValue(mimetype); | ||
2167 | 241 | query.addBindValue(path); | ||
2168 | 242 | query.addBindValue(downloadId); | ||
2169 | 243 | query.exec(); | ||
2170 | 244 | Q_EMIT pathChanged(downloadId, path); | ||
2171 | 245 | } | ||
2172 | 246 | |||
2173 | 247 | void DownloadsModel::setComplete(const QString& downloadId, const bool complete) | ||
2174 | 248 | { | ||
2175 | 249 | QSqlQuery query(m_database); | ||
2176 | 250 | static QString updateStatement = QLatin1String("UPDATE downloads SET complete = ? " | ||
2177 | 251 | "WHERE downloadId = ?"); | ||
2178 | 252 | query.prepare(updateStatement); | ||
2179 | 253 | query.addBindValue(complete); | ||
2180 | 254 | query.addBindValue(downloadId); | ||
2181 | 255 | query.exec(); | ||
2182 | 256 | Q_EMIT completeChanged(downloadId, complete); | ||
2183 | 257 | reload(); | ||
2184 | 258 | } | ||
2185 | 259 | |||
2186 | 260 | void DownloadsModel::setError(const QString& downloadId, const QString& error) | ||
2187 | 261 | { | ||
2188 | 262 | QSqlQuery query(m_database); | ||
2189 | 263 | static QString updateStatement = QLatin1String("UPDATE downloads SET error = ? " | ||
2190 | 264 | "WHERE downloadId = ?"); | ||
2191 | 265 | query.prepare(updateStatement); | ||
2192 | 266 | query.addBindValue(error); | ||
2193 | 267 | query.addBindValue(downloadId); | ||
2194 | 268 | query.exec(); | ||
2195 | 269 | Q_EMIT errorChanged(downloadId, error); | ||
2196 | 270 | reload(); | ||
2197 | 271 | } | ||
2198 | 272 | |||
2199 | 273 | void DownloadsModel::moveToDownloads(const QString& downloadId, const QString& path) | ||
2200 | 274 | { | ||
2201 | 275 | QFile file(path); | ||
2202 | 276 | if (file.exists()) { | ||
2203 | 277 | QFileInfo fi(path); | ||
2204 | 278 | QString suffix = fi.completeSuffix(); | ||
2205 | 279 | QString filename = fi.fileName(); | ||
2206 | 280 | QString filenameWithoutSuffix = filename.left(filename.size() - suffix.size()); | ||
2207 | 281 | QString dir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); | ||
2208 | 282 | QString destination = dir + QDir::separator() + filenameWithoutSuffix + suffix; | ||
2209 | 283 | // Avoid filename collision by automatically inserting an incremented | ||
2210 | 284 | // number into the filename if the original name already exists. | ||
2211 | 285 | if (QFile::exists(destination)) { | ||
2212 | 286 | int append = 1; | ||
2213 | 287 | do { | ||
2214 | 288 | destination = QString("%1%2.%3").arg(dir + QDir::separator() + filenameWithoutSuffix, QString::number(append), suffix); | ||
2215 | 289 | append++; | ||
2216 | 290 | } while (QFile::exists(destination)); | ||
2217 | 291 | } | ||
2218 | 292 | if (file.rename(destination)) { | ||
2219 | 293 | setPath(downloadId, destination); | ||
2220 | 294 | } else { | ||
2221 | 295 | qWarning() << "Failed moving file from " << path << " to " << destination; | ||
2222 | 296 | } | ||
2223 | 297 | } else { | ||
2224 | 298 | qWarning() << "Download not found: " << path; | ||
2225 | 299 | } | ||
2226 | 300 | } | ||
2227 | 301 | |||
2228 | 302 | void DownloadsModel::insertNewEntryInDatabase(const DownloadEntry& entry) | ||
2229 | 303 | { | ||
2230 | 304 | QSqlQuery query(m_database); | ||
2231 | 305 | static QString insertStatement = QLatin1String("INSERT INTO downloads (downloadId, url, " | ||
2232 | 306 | "mimetype) " | ||
2233 | 307 | "VALUES (?, ?, ?);"); | ||
2234 | 308 | query.prepare(insertStatement); | ||
2235 | 309 | query.addBindValue(entry.downloadId); | ||
2236 | 310 | query.addBindValue(entry.url); | ||
2237 | 311 | query.addBindValue(entry.mimetype); | ||
2238 | 312 | query.exec(); | ||
2239 | 313 | } | ||
2240 | 314 | |||
2241 | 315 | /*! | ||
2242 | 316 | Remove a downloaded file from the list of downloads and | ||
2243 | 317 | delete the file. | ||
2244 | 318 | */ | ||
2245 | 319 | void DownloadsModel::deleteDownload(const QString& path) | ||
2246 | 320 | { | ||
2247 | 321 | int index = 0; | ||
2248 | 322 | Q_FOREACH(DownloadEntry entry, m_orderedEntries) { | ||
2249 | 323 | if (entry.path == path) { | ||
2250 | 324 | beginRemoveRows(QModelIndex(), index, index); | ||
2251 | 325 | m_orderedEntries.removeAt(index); | ||
2252 | 326 | endRemoveRows(); | ||
2253 | 327 | Q_EMIT deleted(path); | ||
2254 | 328 | removeExistingEntryFromDatabase(path); | ||
2255 | 329 | m_fetchedCount--; | ||
2256 | 330 | m_numRows--; | ||
2257 | 331 | Q_EMIT rowCountChanged(); | ||
2258 | 332 | QFile::remove(path); | ||
2259 | 333 | return; | ||
2260 | 334 | } else { | ||
2261 | 335 | index++; | ||
2262 | 336 | } | ||
2263 | 337 | } | ||
2264 | 338 | } | ||
2265 | 339 | |||
2266 | 340 | /*! | ||
2267 | 341 | Remove a cancelled download from the model and the database. | ||
2268 | 342 | */ | ||
2269 | 343 | void DownloadsModel::cancelDownload(const QString& downloadId) | ||
2270 | 344 | { | ||
2271 | 345 | int index=0; | ||
2272 | 346 | Q_FOREACH(DownloadEntry entry, m_orderedEntries) { | ||
2273 | 347 | if (entry.downloadId == downloadId) { | ||
2274 | 348 | beginRemoveRows(QModelIndex(), index, index); | ||
2275 | 349 | m_orderedEntries.removeAt(index); | ||
2276 | 350 | QSqlQuery query(m_database); | ||
2277 | 351 | static QString deleteStatement = QLatin1String("DELETE FROM downloads WHERE downloadId=?;"); | ||
2278 | 352 | query.prepare(deleteStatement); | ||
2279 | 353 | query.addBindValue(downloadId); | ||
2280 | 354 | query.exec(); | ||
2281 | 355 | endRemoveRows(); | ||
2282 | 356 | m_fetchedCount--; | ||
2283 | 357 | m_numRows--; | ||
2284 | 358 | Q_EMIT rowCountChanged(); | ||
2285 | 359 | return; | ||
2286 | 360 | } else { | ||
2287 | 361 | index++; | ||
2288 | 362 | } | ||
2289 | 363 | } | ||
2290 | 364 | } | ||
2291 | 365 | |||
2292 | 366 | void DownloadsModel::pauseDownload(const QString& downloadId) | ||
2293 | 367 | { | ||
2294 | 368 | QSqlQuery query(m_database); | ||
2295 | 369 | static QString pauseStatement = QLatin1String("UPDATE downloads SET paused=1 WHERE downloadId=?;"); | ||
2296 | 370 | query.prepare(pauseStatement); | ||
2297 | 371 | query.addBindValue(downloadId); | ||
2298 | 372 | query.exec(); | ||
2299 | 373 | reload(); | ||
2300 | 374 | } | ||
2301 | 375 | |||
2302 | 376 | void DownloadsModel::resumeDownload(const QString& downloadId) | ||
2303 | 377 | { | ||
2304 | 378 | QSqlQuery query(m_database); | ||
2305 | 379 | static QString resumeStatement = QLatin1String("UPDATE downloads SET paused=0 WHERE downloadId=?;"); | ||
2306 | 380 | query.prepare(resumeStatement); | ||
2307 | 381 | query.addBindValue(downloadId); | ||
2308 | 382 | query.exec(); | ||
2309 | 383 | reload(); | ||
2310 | 384 | } | ||
2311 | 385 | |||
2312 | 386 | void DownloadsModel::removeExistingEntryFromDatabase(const QString& path) | ||
2313 | 387 | { | ||
2314 | 388 | QSqlQuery query(m_database); | ||
2315 | 389 | static QString deleteStatement = QLatin1String("DELETE FROM downloads WHERE path=?;"); | ||
2316 | 390 | query.prepare(deleteStatement); | ||
2317 | 391 | query.addBindValue(path); | ||
2318 | 392 | query.exec(); | ||
2319 | 393 | } | ||
2320 | 394 | |||
2321 | 395 | bool DownloadsModel::canFetchMore(const QModelIndex &parent) const | ||
2322 | 396 | { | ||
2323 | 397 | Q_UNUSED(parent) | ||
2324 | 398 | |||
2325 | 399 | return m_canFetchMore; | ||
2326 | 400 | } | ||
2327 | 401 | |||
2328 | 402 | void DownloadsModel::reload() | ||
2329 | 403 | { | ||
2330 | 404 | beginResetModel(); | ||
2331 | 405 | m_orderedEntries.clear(); | ||
2332 | 406 | m_canFetchMore = true; | ||
2333 | 407 | m_fetchedCount = 0; | ||
2334 | 408 | m_numRows = 0; | ||
2335 | 409 | endResetModel(); | ||
2336 | 410 | fetchMore(); | ||
2337 | 411 | Q_EMIT rowCountChanged(); | ||
2338 | 412 | } | ||
2339 | 0 | 413 | ||
2340 | === added file 'src/app/webbrowser/downloads-model.h' | |||
2341 | --- src/app/webbrowser/downloads-model.h 1970-01-01 00:00:00 +0000 | |||
2342 | +++ src/app/webbrowser/downloads-model.h 2015-12-16 16:25:40 +0000 | |||
2343 | @@ -0,0 +1,110 @@ | |||
2344 | 1 | /* | ||
2345 | 2 | * Copyright 2015 Canonical Ltd. | ||
2346 | 3 | * | ||
2347 | 4 | * This file is part of webbrowser-app. | ||
2348 | 5 | * | ||
2349 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
2350 | 7 | * it under the terms of the GNU General Public License as published by | ||
2351 | 8 | * the Free Software Foundation; version 3. | ||
2352 | 9 | * | ||
2353 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
2354 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2355 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2356 | 13 | * GNU General Public License for more details. | ||
2357 | 14 | * | ||
2358 | 15 | * You should have received a copy of the GNU General Public License | ||
2359 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2360 | 17 | */ | ||
2361 | 18 | |||
2362 | 19 | #ifndef __DOWNLOADS_MODEL_H__ | ||
2363 | 20 | #define __DOWNLOADS_MODEL_H__ | ||
2364 | 21 | |||
2365 | 22 | #include <QtCore/QAbstractListModel> | ||
2366 | 23 | #include <QtCore/QDateTime> | ||
2367 | 24 | #include <QtCore/QList> | ||
2368 | 25 | #include <QtCore/QSet> | ||
2369 | 26 | #include <QtCore/QString> | ||
2370 | 27 | #include <QtCore/QUrl> | ||
2371 | 28 | #include <QtSql/QSqlDatabase> | ||
2372 | 29 | |||
2373 | 30 | class DownloadsModel : public QAbstractListModel | ||
2374 | 31 | { | ||
2375 | 32 | Q_OBJECT | ||
2376 | 33 | |||
2377 | 34 | Q_PROPERTY(QString databasePath READ databasePath WRITE setDatabasePath NOTIFY databasePathChanged) | ||
2378 | 35 | Q_PROPERTY(int count READ rowCount NOTIFY rowCountChanged) | ||
2379 | 36 | |||
2380 | 37 | Q_ENUMS(Roles) | ||
2381 | 38 | |||
2382 | 39 | public: | ||
2383 | 40 | DownloadsModel(QObject* parent=0); | ||
2384 | 41 | ~DownloadsModel(); | ||
2385 | 42 | |||
2386 | 43 | enum Roles { | ||
2387 | 44 | DownloadId = Qt::UserRole + 1, | ||
2388 | 45 | Url, | ||
2389 | 46 | Path, | ||
2390 | 47 | Filename, | ||
2391 | 48 | Mimetype, | ||
2392 | 49 | Complete, | ||
2393 | 50 | Paused, | ||
2394 | 51 | Error, | ||
2395 | 52 | Created | ||
2396 | 53 | }; | ||
2397 | 54 | |||
2398 | 55 | // reimplemented from QAbstractListModel | ||
2399 | 56 | QHash<int, QByteArray> roleNames() const; | ||
2400 | 57 | int rowCount(const QModelIndex& parent=QModelIndex()) const; | ||
2401 | 58 | QVariant data(const QModelIndex& index, int role) const; | ||
2402 | 59 | bool canFetchMore(const QModelIndex &parent = QModelIndex()) const; | ||
2403 | 60 | void fetchMore(const QModelIndex &parent = QModelIndex()); | ||
2404 | 61 | |||
2405 | 62 | const QString databasePath() const; | ||
2406 | 63 | void setDatabasePath(const QString& path); | ||
2407 | 64 | |||
2408 | 65 | Q_INVOKABLE void add(const QString &downloadId, const QUrl& url, const QString& mimetype); | ||
2409 | 66 | Q_INVOKABLE void moveToDownloads(const QString& downloadId, const QString& path); | ||
2410 | 67 | Q_INVOKABLE void setPath(const QString& downloadId, const QString& path); | ||
2411 | 68 | Q_INVOKABLE void setComplete(const QString& downloadId, const bool complete); | ||
2412 | 69 | Q_INVOKABLE void setError(const QString& downloadId, const QString& error); | ||
2413 | 70 | Q_INVOKABLE void deleteDownload(const QString& path); | ||
2414 | 71 | Q_INVOKABLE void cancelDownload(const QString& downloadId); | ||
2415 | 72 | Q_INVOKABLE void pauseDownload(const QString& downloadId); | ||
2416 | 73 | Q_INVOKABLE void resumeDownload(const QString& downloadId); | ||
2417 | 74 | |||
2418 | 75 | Q_SIGNALS: | ||
2419 | 76 | void databasePathChanged() const; | ||
2420 | 77 | void added(const QString& downloadId, const QUrl& url, const QString& mimetype) const; | ||
2421 | 78 | void pathChanged(const QString& downloadId, const QString& path) const; | ||
2422 | 79 | void completeChanged(const QString& downloadId, const bool complete) const; | ||
2423 | 80 | void errorChanged(const QString& downloadId, const QString& error) const; | ||
2424 | 81 | void deleted(const QString& path) const; | ||
2425 | 82 | void rowCountChanged(); | ||
2426 | 83 | |||
2427 | 84 | private: | ||
2428 | 85 | QSqlDatabase m_database; | ||
2429 | 86 | int m_numRows; | ||
2430 | 87 | int m_fetchedCount; | ||
2431 | 88 | bool m_canFetchMore; | ||
2432 | 89 | |||
2433 | 90 | struct DownloadEntry { | ||
2434 | 91 | QString downloadId; | ||
2435 | 92 | QUrl url; | ||
2436 | 93 | QString path; | ||
2437 | 94 | QString filename; | ||
2438 | 95 | QString mimetype; | ||
2439 | 96 | bool complete; | ||
2440 | 97 | bool paused; | ||
2441 | 98 | QString error; | ||
2442 | 99 | QDateTime created; | ||
2443 | 100 | }; | ||
2444 | 101 | QList<DownloadEntry> m_orderedEntries; | ||
2445 | 102 | |||
2446 | 103 | void resetDatabase(const QString& databaseName); | ||
2447 | 104 | void createOrAlterDatabaseSchema(); | ||
2448 | 105 | void insertNewEntryInDatabase(const DownloadEntry& entry); | ||
2449 | 106 | void removeExistingEntryFromDatabase(const QString& path); | ||
2450 | 107 | void reload(); | ||
2451 | 108 | }; | ||
2452 | 109 | |||
2453 | 110 | #endif // __DOWNLOADS_MODEL_H__ | ||
2454 | 0 | 111 | ||
2455 | === added file 'src/app/webbrowser/webbrowser-app-content-hub.json' | |||
2456 | --- src/app/webbrowser/webbrowser-app-content-hub.json 1970-01-01 00:00:00 +0000 | |||
2457 | +++ src/app/webbrowser/webbrowser-app-content-hub.json 2015-12-16 16:25:40 +0000 | |||
2458 | @@ -0,0 +1,5 @@ | |||
2459 | 1 | { | ||
2460 | 2 | "source": [ | ||
2461 | 3 | "all" | ||
2462 | 4 | ] | ||
2463 | 5 | } | ||
2464 | 0 | 6 | ||
2465 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' | |||
2466 | --- src/app/webbrowser/webbrowser-app.cpp 2015-11-23 09:41:48 +0000 | |||
2467 | +++ src/app/webbrowser/webbrowser-app.cpp 2015-12-16 16:25:40 +0000 | |||
2468 | @@ -20,6 +20,7 @@ | |||
2469 | 20 | #include "bookmarks-folderlist-model.h" | 20 | #include "bookmarks-folderlist-model.h" |
2470 | 21 | #include "cache-deleter.h" | 21 | #include "cache-deleter.h" |
2471 | 22 | #include "config.h" | 22 | #include "config.h" |
2472 | 23 | #include "downloads-model.h" | ||
2473 | 23 | #include "file-operations.h" | 24 | #include "file-operations.h" |
2474 | 24 | #include "history-domainlist-model.h" | 25 | #include "history-domainlist-model.h" |
2475 | 25 | #include "history-lastvisitdatelist-model.h" | 26 | #include "history-lastvisitdatelist-model.h" |
2476 | @@ -87,6 +88,7 @@ | |||
2477 | 87 | qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory); | 88 | qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory); |
2478 | 88 | qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine"); | 89 | qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine"); |
2479 | 89 | qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory); | 90 | qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory); |
2480 | 91 | qmlRegisterType<DownloadsModel>(uri, 0, 1, "DownloadsModel"); | ||
2481 | 90 | qmlRegisterType<TextSearchFilterModel>(uri, 0, 1, "TextSearchFilterModel"); | 92 | qmlRegisterType<TextSearchFilterModel>(uri, 0, 1, "TextSearchFilterModel"); |
2482 | 91 | 93 | ||
2483 | 92 | if (BrowserApplication::initialize("webbrowser/webbrowser-app.qml")) { | 94 | if (BrowserApplication::initialize("webbrowser/webbrowser-app.qml")) { |
2484 | 93 | 95 | ||
2485 | === added file 'src/app/webcontainer/ContentDownloadDialog.qml' | |||
2486 | --- src/app/webcontainer/ContentDownloadDialog.qml 1970-01-01 00:00:00 +0000 | |||
2487 | +++ src/app/webcontainer/ContentDownloadDialog.qml 2015-12-16 16:25:40 +0000 | |||
2488 | @@ -0,0 +1,67 @@ | |||
2489 | 1 | /* | ||
2490 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
2491 | 3 | * | ||
2492 | 4 | * This file is part of webbrowser-app. | ||
2493 | 5 | * | ||
2494 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
2495 | 7 | * it under the terms of the GNU General Public License as published by | ||
2496 | 8 | * the Free Software Foundation; version 3. | ||
2497 | 9 | * | ||
2498 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
2499 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2500 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2501 | 13 | * GNU General Public License for more details. | ||
2502 | 14 | * | ||
2503 | 15 | * You should have received a copy of the GNU General Public License | ||
2504 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2505 | 17 | */ | ||
2506 | 18 | |||
2507 | 19 | import QtQuick 2.4 | ||
2508 | 20 | import Ubuntu.Components 1.3 | ||
2509 | 21 | import Ubuntu.Components.Popups 1.3 | ||
2510 | 22 | import Ubuntu.Content 1.3 | ||
2511 | 23 | import webbrowsercommon.private 0.1 | ||
2512 | 24 | |||
2513 | 25 | Component { | ||
2514 | 26 | PopupBase { | ||
2515 | 27 | id: downloadDialog | ||
2516 | 28 | objectName: "downloadDialog" | ||
2517 | 29 | anchors.fill: parent | ||
2518 | 30 | property var activeTransfer | ||
2519 | 31 | property var downloadId | ||
2520 | 32 | property var singleDownload | ||
2521 | 33 | property var mimeType | ||
2522 | 34 | property var filename | ||
2523 | 35 | property var icon: MimeDatabase.iconForMimetype(mimeType) | ||
2524 | 36 | property alias contentType: peerPicker.contentType | ||
2525 | 37 | |||
2526 | 38 | ContentPeerModel { | ||
2527 | 39 | id: peerModel | ||
2528 | 40 | handler: ContentHandler.Destination | ||
2529 | 41 | contentType: downloadDialog.contentType | ||
2530 | 42 | } | ||
2531 | 43 | |||
2532 | 44 | Rectangle { | ||
2533 | 45 | id: pickerRect | ||
2534 | 46 | anchors.fill: parent | ||
2535 | 47 | visible: true | ||
2536 | 48 | ContentPeerPicker { | ||
2537 | 49 | id: peerPicker | ||
2538 | 50 | handler: ContentHandler.Destination | ||
2539 | 51 | objectName: "contentPeerPicker" | ||
2540 | 52 | visible: parent.visible | ||
2541 | 53 | |||
2542 | 54 | onPeerSelected: { | ||
2543 | 55 | activeTransfer = peer.request() | ||
2544 | 56 | activeTransfer.downloadId = downloadDialog.downloadId | ||
2545 | 57 | activeTransfer.state = ContentTransfer.Downloading | ||
2546 | 58 | PopupUtils.close(downloadDialog) | ||
2547 | 59 | } | ||
2548 | 60 | |||
2549 | 61 | onCancelPressed: { | ||
2550 | 62 | PopupUtils.close(downloadDialog) | ||
2551 | 63 | } | ||
2552 | 64 | } | ||
2553 | 65 | } | ||
2554 | 66 | } | ||
2555 | 67 | } | ||
2556 | 0 | 68 | ||
2557 | === added file 'src/app/webcontainer/ContentPickerDialog.qml' | |||
2558 | --- src/app/webcontainer/ContentPickerDialog.qml 1970-01-01 00:00:00 +0000 | |||
2559 | +++ src/app/webcontainer/ContentPickerDialog.qml 2015-12-16 16:25:40 +0000 | |||
2560 | @@ -0,0 +1,96 @@ | |||
2561 | 1 | /* | ||
2562 | 2 | * Copyright 2014-2015 Canonical Ltd. | ||
2563 | 3 | * | ||
2564 | 4 | * This file is part of webbrowser-app. | ||
2565 | 5 | * | ||
2566 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
2567 | 7 | * it under the terms of the GNU General Public License as published by | ||
2568 | 8 | * the Free Software Foundation; version 3. | ||
2569 | 9 | * | ||
2570 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
2571 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2572 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2573 | 13 | * GNU General Public License for more details. | ||
2574 | 14 | * | ||
2575 | 15 | * You should have received a copy of the GNU General Public License | ||
2576 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2577 | 17 | */ | ||
2578 | 18 | |||
2579 | 19 | import QtQuick 2.4 | ||
2580 | 20 | import Ubuntu.Components 1.3 | ||
2581 | 21 | import Ubuntu.Components.Popups 1.3 as Popups | ||
2582 | 22 | import Ubuntu.Content 1.3 | ||
2583 | 23 | import "../MimeTypeMapper.js" as MimeTypeMapper | ||
2584 | 24 | |||
2585 | 25 | Component { | ||
2586 | 26 | Popups.PopupBase { | ||
2587 | 27 | id: picker | ||
2588 | 28 | objectName: "contentPickerDialog" | ||
2589 | 29 | |||
2590 | 30 | // Set the parent at construction time, instead of letting show() | ||
2591 | 31 | // set it later on, which for some reason results in the size of | ||
2592 | 32 | // the dialog not being updated. | ||
2593 | 33 | parent: QuickUtils.rootItem(this) | ||
2594 | 34 | |||
2595 | 35 | property var activeTransfer | ||
2596 | 36 | |||
2597 | 37 | Rectangle { | ||
2598 | 38 | anchors.fill: parent | ||
2599 | 39 | |||
2600 | 40 | ContentTransferHint { | ||
2601 | 41 | anchors.fill: parent | ||
2602 | 42 | activeTransfer: picker.activeTransfer | ||
2603 | 43 | } | ||
2604 | 44 | |||
2605 | 45 | ContentPeerPicker { | ||
2606 | 46 | id: peerPicker | ||
2607 | 47 | anchors.fill: parent | ||
2608 | 48 | visible: true | ||
2609 | 49 | contentType: ContentType.All | ||
2610 | 50 | handler: ContentHandler.Source | ||
2611 | 51 | |||
2612 | 52 | onPeerSelected: { | ||
2613 | 53 | if (model.allowMultipleFiles) { | ||
2614 | 54 | peer.selectionType = ContentTransfer.Multiple | ||
2615 | 55 | } else { | ||
2616 | 56 | peer.selectionType = ContentTransfer.Single | ||
2617 | 57 | } | ||
2618 | 58 | picker.activeTransfer = peer.request() | ||
2619 | 59 | stateChangeConnection.target = picker.activeTransfer | ||
2620 | 60 | } | ||
2621 | 61 | |||
2622 | 62 | onCancelPressed: { | ||
2623 | 63 | model.reject() | ||
2624 | 64 | } | ||
2625 | 65 | } | ||
2626 | 66 | } | ||
2627 | 67 | |||
2628 | 68 | Connections { | ||
2629 | 69 | id: stateChangeConnection | ||
2630 | 70 | target: null | ||
2631 | 71 | onStateChanged: { | ||
2632 | 72 | if (picker.activeTransfer.state === ContentTransfer.Charged) { | ||
2633 | 73 | var selectedItems = [] | ||
2634 | 74 | for(var i in picker.activeTransfer.items) { | ||
2635 | 75 | selectedItems.push(String(picker.activeTransfer.items[i].url).replace("file://", "")) | ||
2636 | 76 | } | ||
2637 | 77 | model.accept(selectedItems) | ||
2638 | 78 | } | ||
2639 | 79 | } | ||
2640 | 80 | } | ||
2641 | 81 | |||
2642 | 82 | Component.onCompleted: { | ||
2643 | 83 | if(acceptTypes.length === 1) { | ||
2644 | 84 | var contentType = MimeTypeMapper.mimeTypeToContentType(acceptTypes[0]) | ||
2645 | 85 | if(contentType == ContentType.Unknown) { | ||
2646 | 86 | // If we don't recognise the type, allow uploads from any app | ||
2647 | 87 | contentType = ContentType.All | ||
2648 | 88 | } | ||
2649 | 89 | peerPicker.contentType = contentType | ||
2650 | 90 | } else { | ||
2651 | 91 | peerPicker.contentType = ContentType.All | ||
2652 | 92 | } | ||
2653 | 93 | show() | ||
2654 | 94 | } | ||
2655 | 95 | } | ||
2656 | 96 | } | ||
2657 | 0 | 97 | ||
2658 | === modified file 'src/app/webcontainer/WebViewImplOxide.qml' | |||
2659 | --- src/app/webcontainer/WebViewImplOxide.qml 2015-08-20 10:42:09 +0000 | |||
2660 | +++ src/app/webcontainer/WebViewImplOxide.qml 2015-12-16 16:25:40 +0000 | |||
2661 | @@ -53,6 +53,7 @@ | |||
2662 | 53 | property bool runningLocalApplication: false | 53 | property bool runningLocalApplication: false |
2663 | 54 | 54 | ||
2664 | 55 | currentWebview: webview | 55 | currentWebview: webview |
2665 | 56 | filePicker: filePickerLoader.item | ||
2666 | 56 | 57 | ||
2667 | 57 | context: WebContext { | 58 | context: WebContext { |
2668 | 58 | dataPath: webview.dataPath | 59 | dataPath: webview.dataPath |
2669 | @@ -253,4 +254,28 @@ | |||
2670 | 253 | request.accept() | 254 | request.accept() |
2671 | 254 | } | 255 | } |
2672 | 255 | } | 256 | } |
2673 | 257 | |||
2674 | 258 | onShowDownloadDialog: { | ||
2675 | 259 | if (downloadDialogLoader.status === Loader.Ready) { | ||
2676 | 260 | var downloadDialog = PopupUtils.open(downloadDialogLoader.item, webview, {"contentType" : contentType, | ||
2677 | 261 | "downloadId" : downloadId, | ||
2678 | 262 | "singleDownload" : downloader, | ||
2679 | 263 | "filename" : filename, | ||
2680 | 264 | "mimeType" : mimeType}) | ||
2681 | 265 | downloadDialog.startDownload.connect(startDownload) | ||
2682 | 266 | } | ||
2683 | 267 | } | ||
2684 | 268 | |||
2685 | 269 | Loader { | ||
2686 | 270 | id: downloadDialogLoader | ||
2687 | 271 | source: "ContentDownloadDialog.qml" | ||
2688 | 272 | asynchronous: true | ||
2689 | 273 | } | ||
2690 | 274 | |||
2691 | 275 | Loader { | ||
2692 | 276 | id: filePickerLoader | ||
2693 | 277 | source: "ContentPickerDialog.qml" | ||
2694 | 278 | asynchronous: true | ||
2695 | 279 | } | ||
2696 | 280 | |||
2697 | 256 | } | 281 | } |
2698 | 257 | 282 | ||
2699 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' | |||
2700 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2015-12-04 10:04:30 +0000 | |||
2701 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-12-16 16:25:40 +0000 | |||
2702 | @@ -149,11 +149,40 @@ | |||
2703 | 149 | def get_settings_page(self): | 149 | def get_settings_page(self): |
2704 | 150 | return self.wait_select_single(SettingsPage, visible=True) | 150 | return self.wait_select_single(SettingsPage, visible=True) |
2705 | 151 | 151 | ||
2706 | 152 | def get_downloads_page(self): | ||
2707 | 153 | return self.wait_select_single(DownloadsPage, visible=True) | ||
2708 | 154 | |||
2709 | 152 | def get_content_picker_dialog(self): | 155 | def get_content_picker_dialog(self): |
2710 | 153 | # only on devices | 156 | # only on devices |
2711 | 154 | return self.wait_select_single("PopupBase", | 157 | return self.wait_select_single("PopupBase", |
2712 | 155 | objectName="contentPickerDialog") | 158 | objectName="contentPickerDialog") |
2713 | 156 | 159 | ||
2714 | 160 | def get_download_dialog(self): | ||
2715 | 161 | return self.wait_select_single("PopupBase", | ||
2716 | 162 | objectName="downloadDialog") | ||
2717 | 163 | |||
2718 | 164 | def get_peer_picker(self): | ||
2719 | 165 | return self.wait_select_single(objectName="contentPeerPicker") | ||
2720 | 166 | |||
2721 | 167 | def get_download_options_dialog(self): | ||
2722 | 168 | return self.wait_select_single("Dialog", | ||
2723 | 169 | objectName="downloadOptionsDialog") | ||
2724 | 170 | |||
2725 | 171 | def click_cancel_download_button(self): | ||
2726 | 172 | button = self.select_single("Button", | ||
2727 | 173 | objectName="cancelDownloadButton") | ||
2728 | 174 | self.pointing_device.click_object(button) | ||
2729 | 175 | |||
2730 | 176 | def click_choose_app_button(self): | ||
2731 | 177 | button = self.select_single("Button", | ||
2732 | 178 | objectName="chooseAppButton") | ||
2733 | 179 | self.pointing_device.click_object(button) | ||
2734 | 180 | |||
2735 | 181 | def click_download_file_button(self): | ||
2736 | 182 | button = self.select_single("Button", | ||
2737 | 183 | objectName="downloadFileButton") | ||
2738 | 184 | self.pointing_device.click_object(button) | ||
2739 | 185 | |||
2740 | 157 | def get_bottom_edge_hint(self): | 186 | def get_bottom_edge_hint(self): |
2741 | 158 | return self.select_single("QQuickImage", objectName="bottomEdgeHint") | 187 | return self.select_single("QQuickImage", objectName="bottomEdgeHint") |
2742 | 159 | 188 | ||
2743 | @@ -473,7 +502,7 @@ | |||
2744 | 473 | class SettingsPage(uitk.UbuntuUIToolkitCustomProxyObjectBase): | 502 | class SettingsPage(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2745 | 474 | 503 | ||
2746 | 475 | def get_header(self): | 504 | def get_header(self): |
2748 | 476 | return self.select_single(SettingsPageHeader) | 505 | return self.select_single(BrowserPageHeader) |
2749 | 477 | 506 | ||
2750 | 478 | def get_searchengine_entry(self): | 507 | def get_searchengine_entry(self): |
2751 | 479 | return self.select_single("Subtitled", objectName="searchengine") | 508 | return self.select_single("Subtitled", objectName="searchengine") |
2752 | @@ -502,7 +531,13 @@ | |||
2753 | 502 | return self.select_single("Standard", objectName="reset") | 531 | return self.select_single("Standard", objectName="reset") |
2754 | 503 | 532 | ||
2755 | 504 | 533 | ||
2757 | 505 | class SettingsPageHeader(uitk.UbuntuUIToolkitCustomProxyObjectBase): | 534 | class DownloadsPage(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
2758 | 535 | |||
2759 | 536 | def get_header(self): | ||
2760 | 537 | return self.select_single(BrowserPageHeader) | ||
2761 | 538 | |||
2762 | 539 | |||
2763 | 540 | class BrowserPageHeader(uitk.UbuntuUIToolkitCustomProxyObjectBase): | ||
2764 | 506 | 541 | ||
2765 | 507 | @autopilot.logging.log_action(logger.info) | 542 | @autopilot.logging.log_action(logger.info) |
2766 | 508 | def click_back_button(self): | 543 | def click_back_button(self): |
2767 | 509 | 544 | ||
2768 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' | |||
2769 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2015-10-15 19:09:59 +0000 | |||
2770 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2015-12-16 16:25:40 +0000 | |||
2771 | @@ -213,6 +213,15 @@ | |||
2772 | 213 | self.pointing_device.click_object(history_action) | 213 | self.pointing_device.click_object(history_action) |
2773 | 214 | return self.main_window.get_history_view() | 214 | return self.main_window.get_history_view() |
2774 | 215 | 215 | ||
2775 | 216 | def open_downloads(self): | ||
2776 | 217 | chrome = self.main_window.chrome | ||
2777 | 218 | drawer_button = chrome.get_drawer_button() | ||
2778 | 219 | self.pointing_device.click_object(drawer_button) | ||
2779 | 220 | chrome.get_drawer() | ||
2780 | 221 | downloads_action = chrome.get_drawer_action("downloads") | ||
2781 | 222 | self.pointing_device.click_object(downloads_action) | ||
2782 | 223 | return self.main_window.get_downloads_page() | ||
2783 | 224 | |||
2784 | 216 | def assert_number_webviews_eventually(self, count): | 225 | def assert_number_webviews_eventually(self, count): |
2785 | 217 | self.assertThat(lambda: len(self.main_window.get_webviews()), | 226 | self.assertThat(lambda: len(self.main_window.get_webviews()), |
2786 | 218 | Eventually(Equals(count))) | 227 | Eventually(Equals(count))) |
2787 | 219 | 228 | ||
2788 | === modified file 'tests/autopilot/webbrowser_app/tests/http_server.py' | |||
2789 | --- tests/autopilot/webbrowser_app/tests/http_server.py 2015-09-29 20:58:36 +0000 | |||
2790 | +++ tests/autopilot/webbrowser_app/tests/http_server.py 2015-12-16 16:25:40 +0000 | |||
2791 | @@ -180,6 +180,18 @@ | |||
2792 | 180 | self.send_response(200) | 180 | self.send_response(200) |
2793 | 181 | name = self.path[len("/tab/"):] | 181 | name = self.path[len("/tab/"):] |
2794 | 182 | self.send_html('<html><body>' + name + '</body></html>') | 182 | self.send_html('<html><body>' + name + '</body></html>') |
2795 | 183 | elif self.path.startswith("/downloadpdfgenericmime"): | ||
2796 | 184 | self.send_response(200) | ||
2797 | 185 | self.send_header("Content-Type", "application/octet-stream") | ||
2798 | 186 | self.send_header("Content-Disposition", | ||
2799 | 187 | "attachment; filename='test.pdf'") | ||
2800 | 188 | self.end_headers() | ||
2801 | 189 | elif self.path.startswith("/downloadpdf"): | ||
2802 | 190 | self.send_response(200) | ||
2803 | 191 | self.send_header("Content-Type", "application/pdf") | ||
2804 | 192 | self.send_header("Content-Disposition", | ||
2805 | 193 | "attachment; filename='test.pdf'") | ||
2806 | 194 | self.end_headers() | ||
2807 | 183 | elif self.path.startswith("/basicauth"): | 195 | elif self.path.startswith("/basicauth"): |
2808 | 184 | login = "user" | 196 | login = "user" |
2809 | 185 | password = "pass" | 197 | password = "pass" |
2810 | 186 | 198 | ||
2811 | === added file 'tests/autopilot/webbrowser_app/tests/test_downloads.py' | |||
2812 | --- tests/autopilot/webbrowser_app/tests/test_downloads.py 1970-01-01 00:00:00 +0000 | |||
2813 | +++ tests/autopilot/webbrowser_app/tests/test_downloads.py 2015-12-16 16:25:40 +0000 | |||
2814 | @@ -0,0 +1,77 @@ | |||
2815 | 1 | # -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*- | ||
2816 | 2 | # | ||
2817 | 3 | # Copyright 2015 Canonical | ||
2818 | 4 | # | ||
2819 | 5 | # This program is free software: you can redistribute it and/or modify it | ||
2820 | 6 | # under the terms of the GNU General Public License version 3, as published | ||
2821 | 7 | # by the Free Software Foundation. | ||
2822 | 8 | # | ||
2823 | 9 | # This program is distributed in the hope that it will be useful, | ||
2824 | 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2825 | 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2826 | 12 | # GNU General Public License for more details. | ||
2827 | 13 | # | ||
2828 | 14 | # You should have received a copy of the GNU General Public License | ||
2829 | 15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2830 | 16 | |||
2831 | 17 | from webbrowser_app.tests import StartOpenRemotePageTestCaseBase | ||
2832 | 18 | |||
2833 | 19 | from autopilot.matchers import Eventually | ||
2834 | 20 | from autopilot.platform import model | ||
2835 | 21 | |||
2836 | 22 | from testtools.matchers import Equals | ||
2837 | 23 | |||
2838 | 24 | import testtools | ||
2839 | 25 | |||
2840 | 26 | |||
2841 | 27 | @testtools.skipIf(model() == "Desktop", "Don't run on desktop, as " | ||
2842 | 28 | "dependencies aren't guaranteed") | ||
2843 | 29 | class TestDownloads(StartOpenRemotePageTestCaseBase): | ||
2844 | 30 | |||
2845 | 31 | def test_open_close_downloads_page(self): | ||
2846 | 32 | downloads_page = self.open_downloads() | ||
2847 | 33 | downloads_page.get_header().click_back_button() | ||
2848 | 34 | downloads_page.wait_until_destroyed() | ||
2849 | 35 | |||
2850 | 36 | def test_mimetype_download(self): | ||
2851 | 37 | self.main_window.go_to_url(self.base_url + "/downloadpdf") | ||
2852 | 38 | dialog = self.main_window.get_download_dialog() | ||
2853 | 39 | options_dialog = self.main_window.get_download_options_dialog() | ||
2854 | 40 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2855 | 41 | self.assertThat(dialog.mimeType, Eventually(Equals("application/pdf"))) | ||
2856 | 42 | |||
2857 | 43 | def test_generic_mimetype_download(self): | ||
2858 | 44 | self.main_window.go_to_url(self.base_url + "/downloadpdfgenericmime") | ||
2859 | 45 | dialog = self.main_window.get_download_dialog() | ||
2860 | 46 | options_dialog = self.main_window.get_download_options_dialog() | ||
2861 | 47 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2862 | 48 | self.assertThat(dialog.mimeType, Eventually(Equals("application/pdf"))) | ||
2863 | 49 | |||
2864 | 50 | def test_filename(self): | ||
2865 | 51 | self.main_window.go_to_url(self.base_url + "/downloadpdf") | ||
2866 | 52 | dialog = self.main_window.get_download_dialog() | ||
2867 | 53 | options_dialog = self.main_window.get_download_options_dialog() | ||
2868 | 54 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2869 | 55 | self.assertThat(dialog.filename, Eventually(Equals("test.pdf"))) | ||
2870 | 56 | |||
2871 | 57 | def test_close_dialog(self): | ||
2872 | 58 | self.main_window.go_to_url(self.base_url + "/downloadpdf") | ||
2873 | 59 | options_dialog = self.main_window.get_download_options_dialog() | ||
2874 | 60 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2875 | 61 | self.main_window.click_cancel_download_button() | ||
2876 | 62 | |||
2877 | 63 | def test_picker(self): | ||
2878 | 64 | self.main_window.go_to_url(self.base_url + "/downloadpdf") | ||
2879 | 65 | options_dialog = self.main_window.get_download_options_dialog() | ||
2880 | 66 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2881 | 67 | self.main_window.click_choose_app_button() | ||
2882 | 68 | picker = self.main_window.get_peer_picker() | ||
2883 | 69 | self.assertThat(picker.visible, Eventually(Equals(True))) | ||
2884 | 70 | |||
2885 | 71 | def test_download(self): | ||
2886 | 72 | self.main_window.go_to_url(self.base_url + "/downloadpdf") | ||
2887 | 73 | options_dialog = self.main_window.get_download_options_dialog() | ||
2888 | 74 | self.assertThat(options_dialog.visible, Eventually(Equals(True))) | ||
2889 | 75 | self.main_window.click_download_file_button() | ||
2890 | 76 | downloads_page = self.main_window.get_downloads_page() | ||
2891 | 77 | self.assertThat(downloads_page.visible, Eventually(Equals(True))) | ||
2892 | 0 | 78 | ||
2893 | === modified file 'tests/autopilot/webbrowser_app/tests/test_settings.py' | |||
2894 | --- tests/autopilot/webbrowser_app/tests/test_settings.py 2015-07-06 13:51:36 +0000 | |||
2895 | +++ tests/autopilot/webbrowser_app/tests/test_settings.py 2015-12-16 16:25:40 +0000 | |||
2896 | @@ -50,7 +50,7 @@ | |||
2897 | 50 | self.pointing_device.click_object(searchengine) | 50 | self.pointing_device.click_object(searchengine) |
2898 | 51 | searchengine_page = settings.get_searchengine_page() | 51 | searchengine_page = settings.get_searchengine_page() |
2899 | 52 | searchengine_header = searchengine_page.select_single( | 52 | searchengine_header = searchengine_page.select_single( |
2901 | 53 | browser.SettingsPageHeader) | 53 | browser.BrowserPageHeader) |
2902 | 54 | searchengine_header.click_back_button() | 54 | searchengine_header.click_back_button() |
2903 | 55 | searchengine_page.wait_until_destroyed() | 55 | searchengine_page.wait_until_destroyed() |
2904 | 56 | self.assertThat(searchengine.subText, Equals(old_engine)) | 56 | self.assertThat(searchengine.subText, Equals(old_engine)) |
2905 | @@ -120,7 +120,7 @@ | |||
2906 | 120 | privacy = settings.get_privacy_entry() | 120 | privacy = settings.get_privacy_entry() |
2907 | 121 | self.pointing_device.click_object(privacy) | 121 | self.pointing_device.click_object(privacy) |
2908 | 122 | privacy_page = settings.get_privacy_page() | 122 | privacy_page = settings.get_privacy_page() |
2910 | 123 | privacy_header = privacy_page.select_single(browser.SettingsPageHeader) | 123 | privacy_header = privacy_page.select_single(browser.BrowserPageHeader) |
2911 | 124 | privacy_header.click_back_button() | 124 | privacy_header.click_back_button() |
2912 | 125 | privacy_page.wait_until_destroyed() | 125 | privacy_page.wait_until_destroyed() |
2913 | 126 | 126 | ||
2914 | 127 | 127 | ||
2915 | === modified file 'tests/unittests/CMakeLists.txt' | |||
2916 | --- tests/unittests/CMakeLists.txt 2015-11-23 09:41:48 +0000 | |||
2917 | +++ tests/unittests/CMakeLists.txt 2015-12-16 16:25:40 +0000 | |||
2918 | @@ -20,3 +20,4 @@ | |||
2919 | 20 | add_subdirectory(intent-filter) | 20 | add_subdirectory(intent-filter) |
2920 | 21 | add_subdirectory(search-engine) | 21 | add_subdirectory(search-engine) |
2921 | 22 | add_subdirectory(text-search-filter-model) | 22 | add_subdirectory(text-search-filter-model) |
2922 | 23 | add_subdirectory(downloads-model) | ||
2923 | 23 | 24 | ||
2924 | === added directory 'tests/unittests/downloads-model' | |||
2925 | === added file 'tests/unittests/downloads-model/CMakeLists.txt' | |||
2926 | --- tests/unittests/downloads-model/CMakeLists.txt 1970-01-01 00:00:00 +0000 | |||
2927 | +++ tests/unittests/downloads-model/CMakeLists.txt 2015-12-16 16:25:40 +0000 | |||
2928 | @@ -0,0 +1,13 @@ | |||
2929 | 1 | find_package(Qt5Core REQUIRED) | ||
2930 | 2 | find_package(Qt5Sql REQUIRED) | ||
2931 | 3 | find_package(Qt5Test REQUIRED) | ||
2932 | 4 | set(TEST tst_DownloadsModelTests) | ||
2933 | 5 | add_executable(${TEST} tst_DownloadsModelTests.cpp) | ||
2934 | 6 | include_directories(${webbrowser-app_SOURCE_DIR}) | ||
2935 | 7 | target_link_libraries(${TEST} | ||
2936 | 8 | Qt5::Core | ||
2937 | 9 | Qt5::Sql | ||
2938 | 10 | Qt5::Test | ||
2939 | 11 | webbrowser-app-models | ||
2940 | 12 | ) | ||
2941 | 13 | add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) | ||
2942 | 0 | 14 | ||
2943 | === added file 'tests/unittests/downloads-model/tst_DownloadsModelTests.cpp' | |||
2944 | --- tests/unittests/downloads-model/tst_DownloadsModelTests.cpp 1970-01-01 00:00:00 +0000 | |||
2945 | +++ tests/unittests/downloads-model/tst_DownloadsModelTests.cpp 2015-12-16 16:25:40 +0000 | |||
2946 | @@ -0,0 +1,190 @@ | |||
2947 | 1 | /* | ||
2948 | 2 | * Copyright 2015 Canonical Ltd. | ||
2949 | 3 | * | ||
2950 | 4 | * This file is part of webbrowser-app. | ||
2951 | 5 | * | ||
2952 | 6 | * webbrowser-app is free software; you can redistribute it and/or modify | ||
2953 | 7 | * it under the terms of the GNU General Public License as published by | ||
2954 | 8 | * the Free Software Foundation; version 3. | ||
2955 | 9 | * | ||
2956 | 10 | * webbrowser-app is distributed in the hope that it will be useful, | ||
2957 | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2958 | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2959 | 13 | * GNU General Public License for more details. | ||
2960 | 14 | * | ||
2961 | 15 | * You should have received a copy of the GNU General Public License | ||
2962 | 16 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2963 | 17 | */ | ||
2964 | 18 | |||
2965 | 19 | #include <QtCore/QDir> | ||
2966 | 20 | #include <QtCore/QTemporaryFile> | ||
2967 | 21 | #include <QtTest/QSignalSpy> | ||
2968 | 22 | #include <QtTest/QtTest> | ||
2969 | 23 | #include "downloads-model.h" | ||
2970 | 24 | |||
2971 | 25 | class DownloadsModelTests : public QObject | ||
2972 | 26 | { | ||
2973 | 27 | Q_OBJECT | ||
2974 | 28 | |||
2975 | 29 | private: | ||
2976 | 30 | DownloadsModel* model; | ||
2977 | 31 | |||
2978 | 32 | private Q_SLOTS: | ||
2979 | 33 | void init() | ||
2980 | 34 | { | ||
2981 | 35 | model = new DownloadsModel; | ||
2982 | 36 | model->setDatabasePath(":memory:"); | ||
2983 | 37 | } | ||
2984 | 38 | |||
2985 | 39 | void cleanup() | ||
2986 | 40 | { | ||
2987 | 41 | delete model; | ||
2988 | 42 | } | ||
2989 | 43 | |||
2990 | 44 | void shouldBeInitiallyEmpty() | ||
2991 | 45 | { | ||
2992 | 46 | QCOMPARE(model->rowCount(), 0); | ||
2993 | 47 | } | ||
2994 | 48 | |||
2995 | 49 | void shouldExposeRoleNames() | ||
2996 | 50 | { | ||
2997 | 51 | QList<QByteArray> roleNames = model->roleNames().values(); | ||
2998 | 52 | QVERIFY(roleNames.contains("downloadId")); | ||
2999 | 53 | QVERIFY(roleNames.contains("url")); | ||
3000 | 54 | QVERIFY(roleNames.contains("path")); | ||
3001 | 55 | QVERIFY(roleNames.contains("filename")); | ||
3002 | 56 | QVERIFY(roleNames.contains("mimetype")); | ||
3003 | 57 | QVERIFY(roleNames.contains("complete")); | ||
3004 | 58 | QVERIFY(roleNames.contains("paused")); | ||
3005 | 59 | QVERIFY(roleNames.contains("error")); | ||
3006 | 60 | QVERIFY(roleNames.contains("created")); | ||
3007 | 61 | } | ||
3008 | 62 | |||
3009 | 63 | void shouldAddNewEntries() | ||
3010 | 64 | { | ||
3011 | 65 | QSignalSpy spy(model, SIGNAL(added(QString, QUrl, QString))); | ||
3012 | 66 | |||
3013 | 67 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3014 | 68 | QCOMPARE(model->rowCount(), 1); | ||
3015 | 69 | QCOMPARE(spy.count(), 1); | ||
3016 | 70 | QVariantList args = spy.takeFirst(); | ||
3017 | 71 | QCOMPARE(args.at(0).toString(), QString("testid")); | ||
3018 | 72 | QCOMPARE(args.at(1).toUrl(), QUrl("http://example.org/")); | ||
3019 | 73 | QCOMPARE(args.at(2).toString(), QString("text/plain")); | ||
3020 | 74 | |||
3021 | 75 | model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf"); | ||
3022 | 76 | QCOMPARE(model->rowCount(), 2); | ||
3023 | 77 | QCOMPARE(spy.count(), 1); | ||
3024 | 78 | args = spy.takeFirst(); | ||
3025 | 79 | QCOMPARE(args.at(0).toString(), QString("testid2")); | ||
3026 | 80 | QCOMPARE(args.at(1).toUrl(), QUrl("http://example.org/pdf")); | ||
3027 | 81 | QCOMPARE(args.at(2).toString(), QString("application/pdf")); | ||
3028 | 82 | } | ||
3029 | 83 | |||
3030 | 84 | void shouldRemoveCancelled() | ||
3031 | 85 | { | ||
3032 | 86 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3033 | 87 | model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf"); | ||
3034 | 88 | model->add("testid3", QUrl("https://example.org/secure.png"), "image/png"); | ||
3035 | 89 | QCOMPARE(model->rowCount(), 3); | ||
3036 | 90 | |||
3037 | 91 | model->cancelDownload("testid2"); | ||
3038 | 92 | QCOMPARE(model->rowCount(), 2); | ||
3039 | 93 | |||
3040 | 94 | model->cancelDownload("invalid"); | ||
3041 | 95 | QCOMPARE(model->rowCount(), 2); | ||
3042 | 96 | } | ||
3043 | 97 | |||
3044 | 98 | void shouldCompleteDownloads() | ||
3045 | 99 | { | ||
3046 | 100 | QSignalSpy spy(model, SIGNAL(completeChanged(QString, bool))); | ||
3047 | 101 | |||
3048 | 102 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3049 | 103 | QVERIFY(!model->data(model->index(0, 0), DownloadsModel::Complete).toBool()); | ||
3050 | 104 | model->setComplete("testid", true); | ||
3051 | 105 | QCOMPARE(spy.count(), 1); | ||
3052 | 106 | QVariantList args = spy.takeFirst(); | ||
3053 | 107 | QCOMPARE(args.at(0).toString(), QString("testid")); | ||
3054 | 108 | QCOMPARE(args.at(1).toBool(), true); | ||
3055 | 109 | } | ||
3056 | 110 | |||
3057 | 111 | void shouldKeepEntriesSortedChronologically() | ||
3058 | 112 | { | ||
3059 | 113 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3060 | 114 | model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf"); | ||
3061 | 115 | model->add("testid3", QUrl("https://example.org/secure.png"), "image/png"); | ||
3062 | 116 | |||
3063 | 117 | QCOMPARE(model->data(model->index(0, 0), DownloadsModel::DownloadId).toString(), QString("testid3")); | ||
3064 | 118 | QCOMPARE(model->data(model->index(1, 0), DownloadsModel::DownloadId).toString(), QString("testid2")); | ||
3065 | 119 | QCOMPARE(model->data(model->index(2, 0), DownloadsModel::DownloadId).toString(), QString("testid")); | ||
3066 | 120 | } | ||
3067 | 121 | |||
3068 | 122 | void shouldReturnData() | ||
3069 | 123 | { | ||
3070 | 124 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3071 | 125 | QVERIFY(!model->data(QModelIndex(), DownloadsModel::DownloadId).isValid()); | ||
3072 | 126 | QVERIFY(!model->data(model->index(-1, 0), DownloadsModel::DownloadId).isValid()); | ||
3073 | 127 | QVERIFY(!model->data(model->index(3, 0), DownloadsModel::DownloadId).isValid()); | ||
3074 | 128 | QCOMPARE(model->data(model->index(0, 0), DownloadsModel::DownloadId).toString(), QString("testid")); | ||
3075 | 129 | QCOMPARE(model->data(model->index(0, 0), DownloadsModel::Url).toUrl(), QUrl("http://example.org/")); | ||
3076 | 130 | QCOMPARE(model->data(model->index(0, 0), DownloadsModel::Mimetype).toString(), QString("text/plain")); | ||
3077 | 131 | QVERIFY(model->data(model->index(0, 0), DownloadsModel::Created).toDateTime() <= QDateTime::currentDateTime()); | ||
3078 | 132 | QVERIFY(!model->data(model->index(0, 0), DownloadsModel::Complete).toBool()); | ||
3079 | 133 | } | ||
3080 | 134 | |||
3081 | 135 | void shouldReturnDatabasePath() | ||
3082 | 136 | { | ||
3083 | 137 | QCOMPARE(model->databasePath(), QString(":memory:")); | ||
3084 | 138 | } | ||
3085 | 139 | |||
3086 | 140 | void shouldNotifyWhenSettingDatabasePath() | ||
3087 | 141 | { | ||
3088 | 142 | QSignalSpy spyPath(model, SIGNAL(databasePathChanged())); | ||
3089 | 143 | QSignalSpy spyReset(model, SIGNAL(modelReset())); | ||
3090 | 144 | |||
3091 | 145 | model->setDatabasePath(":memory:"); | ||
3092 | 146 | QVERIFY(spyPath.isEmpty()); | ||
3093 | 147 | QVERIFY(spyReset.isEmpty()); | ||
3094 | 148 | |||
3095 | 149 | model->setDatabasePath(""); | ||
3096 | 150 | QCOMPARE(spyPath.count(), 1); | ||
3097 | 151 | QCOMPARE(spyReset.count(), 1); | ||
3098 | 152 | QCOMPARE(model->databasePath(), QString(":memory:")); | ||
3099 | 153 | } | ||
3100 | 154 | |||
3101 | 155 | void shouldSerializeOnDisk() | ||
3102 | 156 | { | ||
3103 | 157 | QTemporaryFile tempFile; | ||
3104 | 158 | tempFile.open(); | ||
3105 | 159 | QString fileName = tempFile.fileName(); | ||
3106 | 160 | delete model; | ||
3107 | 161 | model = new DownloadsModel; | ||
3108 | 162 | model->setDatabasePath(fileName); | ||
3109 | 163 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3110 | 164 | model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf"); | ||
3111 | 165 | delete model; | ||
3112 | 166 | model = new DownloadsModel; | ||
3113 | 167 | model->setDatabasePath(fileName); | ||
3114 | 168 | model->fetchMore(); | ||
3115 | 169 | QCOMPARE(model->rowCount(), 2); | ||
3116 | 170 | } | ||
3117 | 171 | |||
3118 | 172 | void shouldCountNumberOfEntries() | ||
3119 | 173 | { | ||
3120 | 174 | QCOMPARE(model->property("count").toInt(), 0); | ||
3121 | 175 | QCOMPARE(model->rowCount(), 0); | ||
3122 | 176 | model->add("testid", QUrl("http://example.org/"), "text/plain"); | ||
3123 | 177 | QCOMPARE(model->property("count").toInt(), 1); | ||
3124 | 178 | QCOMPARE(model->rowCount(), 1); | ||
3125 | 179 | model->add("testid2", QUrl("http://example.org/pdf"), "application/pdf"); | ||
3126 | 180 | QCOMPARE(model->property("count").toInt(), 2); | ||
3127 | 181 | QCOMPARE(model->rowCount(), 2); | ||
3128 | 182 | model->add("testid3", QUrl("https://example.org/secure.png"), "image/png"); | ||
3129 | 183 | QCOMPARE(model->property("count").toInt(), 3); | ||
3130 | 184 | QCOMPARE(model->rowCount(), 3); | ||
3131 | 185 | } | ||
3132 | 186 | |||
3133 | 187 | }; | ||
3134 | 188 | |||
3135 | 189 | QTEST_MAIN(DownloadsModelTests) | ||
3136 | 190 | #include "tst_DownloadsModelTests.moc" |
FAILED: Continuous integration, rev:1122 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 2131/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 3829 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 885 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 885 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 885/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 885 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-mako/ 3146 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3826 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 3826/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 22761
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 2131/rebuild
http://