Merge lp:~osomon/webbrowser-app/bookmarks-proxy-model into lp:webbrowser-app
- bookmarks-proxy-model
- Merge into trunk
Status: | Needs review |
---|---|
Proposed branch: | lp:~osomon/webbrowser-app/bookmarks-proxy-model |
Merge into: | lp:webbrowser-app |
Diff against target: |
2398 lines (+1610/-331) 20 files modified
src/app/webbrowser/BookmarksFoldersView.qml (+92/-156) src/app/webbrowser/BookmarksView.qml (+3/-3) src/app/webbrowser/CMakeLists.txt (+1/-0) src/app/webbrowser/NewTabView.qml (+9/-7) src/app/webbrowser/bookmarks-model.cpp (+1/-1) src/app/webbrowser/collapsible-bookmarks-model.cpp (+572/-0) src/app/webbrowser/collapsible-bookmarks-model.h (+98/-0) src/app/webbrowser/webbrowser-app.cpp (+2/-0) tests/autopilot/webbrowser_app/emulators/browser.py (+83/-43) tests/autopilot/webbrowser_app/tests/__init__.py (+1/-6) tests/autopilot/webbrowser_app/tests/test_bookmark_options.py (+33/-29) tests/autopilot/webbrowser_app/tests/test_new_tab_view.py (+43/-77) tests/autopilot/webbrowser_app/tests/test_private.py (+4/-2) tests/autopilot/webbrowser_app/tests/test_site_previews.py (+1/-1) tests/unittests/CMakeLists.txt (+1/-0) tests/unittests/collapsible-bookmarks-model/CMakeLists.txt (+13/-0) tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp (+645/-0) tests/unittests/qml/CMakeLists.txt (+1/-0) tests/unittests/qml/tst_BookmarksView.qml (+5/-6) tests/unittests/qml/tst_QmlTests.cpp (+2/-0) |
To merge this branch: | bzr merge lp:~osomon/webbrowser-app/bookmarks-proxy-model |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
system-apps-ci-bot | continuous-integration | Needs Fixing | |
PS Jenkins bot | continuous-integration | Approve | |
Ubuntu Phablet Team | Pending | ||
Review via email: mp+279277@code.launchpad.net |
Commit message
Add a C++ model for the narrow bookmarks view: CollapsibleBook
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1308
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://
Olivier Tilloy (osomon) wrote : | # |
For reference, I measured the memory overhead induced by duplicating in memory all the entries of the source model, and this is roughly 1MB per 1000 bookmarks, i.e. 1KB per bookmark (tested with 10000 bookmarks, and with 100000).
I’ll see if this can be optimized by keeping in memory only the bookmarks for the expanded folders, and reconstructing the entries for a folder when it is being expanded, and will share my observations here.
system-apps-ci-bot (system-apps-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:1308
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unmerged revisions
- 1308. By Olivier Tilloy
-
Add documentation to the CollapsibleBook
marksModel class. - 1307. By Olivier Tilloy
-
Use the top-level bookmarks view in test_bookmark_
options. py, instead of going through the new tab view. - 1306. By Olivier Tilloy
-
Minor autopilot test improvements.
- 1305. By Olivier Tilloy
-
Fix a number of autopilot test failures.
- 1304. By Olivier Tilloy
-
More test coverage.
- 1303. By Olivier Tilloy
-
More test coverage.
- 1302. By Olivier Tilloy
-
More test coverage.
- 1301. By Olivier Tilloy
-
More test coverage.
- 1300. By Olivier Tilloy
-
More test coverage.
- 1299. By Olivier Tilloy
-
More test coverage.
Preview Diff
1 | === modified file 'src/app/webbrowser/BookmarksFoldersView.qml' |
2 | --- src/app/webbrowser/BookmarksFoldersView.qml 2015-11-12 16:16:15 +0000 |
3 | +++ src/app/webbrowser/BookmarksFoldersView.qml 2015-12-02 14:15:47 +0000 |
4 | @@ -20,171 +20,107 @@ |
5 | import Ubuntu.Components 1.3 |
6 | import Ubuntu.Components.ListItems 1.3 as ListItem |
7 | import webbrowserapp.private 0.1 |
8 | -import "BookmarksModelUtils.js" as BookmarksModelUtils |
9 | - |
10 | -FocusScope { |
11 | - id: bookmarksFoldersViewItem |
12 | - |
13 | - property alias interactive: bookmarksFolderListView.interactive |
14 | - property url homeBookmarkUrl |
15 | + |
16 | +ListView { |
17 | + objectName: "bookmarksFolderListView" |
18 | + |
19 | + property url homepageUrl |
20 | |
21 | signal bookmarkClicked(url url) |
22 | signal bookmarkRemoved(url url) |
23 | |
24 | - height: bookmarksFolderListView.contentHeight |
25 | - |
26 | - BookmarksFolderListModel { |
27 | - id: bookmarksFolderListModel |
28 | + model: CollapsibleBookmarksModel { |
29 | + id: bookmarksProxyModel |
30 | sourceModel: BookmarksModel |
31 | } |
32 | |
33 | - ListView { |
34 | - id: bookmarksFolderListView |
35 | - objectName: "bookmarksFolderListView" |
36 | - anchors.fill: parent |
37 | - interactive: false |
38 | - focus: true |
39 | - |
40 | - model: bookmarksFolderListModel |
41 | - delegate: Loader { |
42 | - objectName: "bookmarkFolderDelegateLoader" |
43 | - anchors { |
44 | - left: parent.left |
45 | - right: parent.right |
46 | + delegate: Loader { |
47 | + objectName: "bookmarkViewDelegateLoader" |
48 | + anchors { |
49 | + left: parent.left |
50 | + right: parent.right |
51 | + } |
52 | + readonly property string type: model.type |
53 | + readonly property bool isBookmark: type == "bookmark" |
54 | + readonly property bool isHomepage: type == "homepage" |
55 | + readonly property string folder: model.folder |
56 | + height: (isBookmark || isHomepage) ? units.gu(5) : units.gu(6.5) |
57 | + |
58 | + sourceComponent: (isBookmark || isHomepage) ? bookmarkComponent : folderComponent |
59 | + |
60 | + Component { |
61 | + id: bookmarkComponent |
62 | + UrlDelegate { |
63 | + objectName: "bookmarkViewBookmarkDelegate" |
64 | + readonly property string folderName: folder |
65 | + anchors.fill: parent |
66 | + removable: !isHomepage |
67 | + icon: isHomepage ? "" : model.icon |
68 | + title: isHomepage ? i18n.tr("Homepage") : model.title |
69 | + url: isHomepage ? homepageUrl : model.url |
70 | + onClicked: bookmarkClicked(url) |
71 | + onRemoved: bookmarkRemoved(url) |
72 | } |
73 | - |
74 | - height: active ? item.height : 0 |
75 | - active: entries.count > 0 |
76 | - |
77 | - sourceComponent: Item { |
78 | - objectName: "bookmarkFolderDelegate" |
79 | - |
80 | - property string folderName: folder |
81 | - |
82 | + } |
83 | + |
84 | + Component { |
85 | + id: folderComponent |
86 | + Item { |
87 | + objectName: "bookmarkViewFolderDelegate" |
88 | + readonly property string name: folder |
89 | anchors { |
90 | - left: parent ? parent.left : undefined |
91 | - right: parent ? parent.right : undefined |
92 | - } |
93 | - |
94 | - height: delegateColumn.height |
95 | - |
96 | - Column { |
97 | - id: delegateColumn |
98 | - |
99 | - property bool expanded: folderName ? false : true |
100 | - |
101 | - anchors { |
102 | - left: parent.left |
103 | - right: parent.right |
104 | - } |
105 | - |
106 | - Item { |
107 | - objectName: "bookmarkFolderHeader" |
108 | - |
109 | - anchors { |
110 | - left: parent.left |
111 | - right: parent.right |
112 | - leftMargin: units.gu(2) |
113 | - rightMargin: units.gu(2) |
114 | - } |
115 | - |
116 | - height: units.gu(6.5) |
117 | - |
118 | - Row { |
119 | - anchors { |
120 | - left: parent.left |
121 | - leftMargin: units.gu(1.5) |
122 | - right: parent.right |
123 | - } |
124 | - |
125 | - height: units.gu(6) |
126 | - spacing: units.gu(1.5) |
127 | - |
128 | - Icon { |
129 | - id: expandedIcon |
130 | - name: delegateColumn.expanded ? "go-down" : "go-next" |
131 | - |
132 | - height: units.gu(2) |
133 | - width: height |
134 | - |
135 | - anchors { |
136 | - leftMargin: units.gu(1) |
137 | - topMargin: units.gu(2) |
138 | - top: parent.top |
139 | - } |
140 | - } |
141 | - |
142 | - Label { |
143 | - width: parent.width - expandedIcon.width - units.gu(3) |
144 | - anchors.verticalCenter: expandedIcon.verticalCenter |
145 | - |
146 | - text: folderName ? folderName : i18n.tr("All Bookmarks") |
147 | - fontSize: "small" |
148 | - } |
149 | - } |
150 | - |
151 | - ListItem.ThinDivider { |
152 | - anchors { |
153 | - left: parent.left |
154 | - right: parent.right |
155 | - bottom: parent.bottom |
156 | - bottomMargin: units.gu(1) |
157 | - } |
158 | - } |
159 | - |
160 | - MouseArea { |
161 | - anchors.fill: parent |
162 | - onClicked: delegateColumn.expanded = !delegateColumn.expanded |
163 | - } |
164 | - } |
165 | - |
166 | - Loader { |
167 | - anchors { |
168 | - left: parent.left |
169 | - right: parent.right |
170 | - } |
171 | - |
172 | - height: item ? item.contentHeight : 0 |
173 | - |
174 | - visible: status == Loader.Ready |
175 | - |
176 | - active: delegateColumn.expanded |
177 | - sourceComponent: ListView { |
178 | - readonly property bool isAllBookmarksFolder: folder === "" |
179 | - |
180 | - interactive: false |
181 | - |
182 | - model: { |
183 | - if (isAllBookmarksFolder) { |
184 | - return BookmarksModelUtils.prependHomepageToBookmarks(entries, { |
185 | - title: i18n.tr("Homepage"), |
186 | - url: bookmarksFoldersViewItem.homeBookmarkUrl |
187 | - }) |
188 | - } |
189 | - |
190 | - return entries |
191 | - } |
192 | - |
193 | - delegate: UrlDelegate{ |
194 | - id: urlDelegate |
195 | - objectName: "urlDelegate_" + index |
196 | - |
197 | - property var entry: isAllBookmarksFolder ? modelData : model |
198 | - |
199 | - width: parent.width |
200 | - height: units.gu(5) |
201 | - |
202 | - removable: !isAllBookmarksFolder || index !== 0 |
203 | - |
204 | - icon: entry.icon ? entry.icon : "" |
205 | - title: entry.title ? entry.title : entry.url |
206 | - url: entry.url |
207 | - |
208 | - onClicked: bookmarksFoldersViewItem.bookmarkClicked(url) |
209 | - onRemoved: bookmarksFoldersViewItem.bookmarkRemoved(url) |
210 | - } |
211 | - } |
212 | - } |
213 | + fill: parent |
214 | + leftMargin: units.gu(2) |
215 | + rightMargin: units.gu(2) |
216 | + } |
217 | + |
218 | + Row { |
219 | + anchors { |
220 | + left: parent.left |
221 | + leftMargin: units.gu(1.5) |
222 | + right: parent.right |
223 | + } |
224 | + |
225 | + height: units.gu(6) |
226 | + spacing: units.gu(1.5) |
227 | + |
228 | + opacity: (type == "emptyFolder") ? 0.3 : 1.0 |
229 | + |
230 | + Icon { |
231 | + id: expandedIcon |
232 | + name: (type == "expandedFolder") ? "go-down" : "go-next" |
233 | + |
234 | + height: units.gu(2) |
235 | + width: height |
236 | + |
237 | + anchors { |
238 | + leftMargin: units.gu(1) |
239 | + topMargin: units.gu(2) |
240 | + top: parent.top |
241 | + } |
242 | + } |
243 | + |
244 | + Label { |
245 | + width: parent.width - expandedIcon.width - units.gu(3) |
246 | + anchors.verticalCenter: expandedIcon.verticalCenter |
247 | + |
248 | + text: folder || i18n.tr("All Bookmarks") |
249 | + fontSize: "small" |
250 | + } |
251 | + } |
252 | + |
253 | + ListItem.ThinDivider { |
254 | + anchors { |
255 | + left: parent.left |
256 | + right: parent.right |
257 | + bottom: parent.bottom |
258 | + bottomMargin: units.gu(1) |
259 | + } |
260 | + } |
261 | + |
262 | + MouseArea { |
263 | + anchors.fill: parent |
264 | + onClicked: bookmarksProxyModel.toggleFolderState(folder) |
265 | } |
266 | } |
267 | } |
268 | |
269 | === modified file 'src/app/webbrowser/BookmarksView.qml' |
270 | --- src/app/webbrowser/BookmarksView.qml 2015-11-12 16:16:45 +0000 |
271 | +++ src/app/webbrowser/BookmarksView.qml 2015-12-02 14:15:47 +0000 |
272 | @@ -24,7 +24,7 @@ |
273 | FocusScope { |
274 | id: bookmarksView |
275 | |
276 | - property alias homepageUrl: bookmarksFoldersView.homeBookmarkUrl |
277 | + property alias homepageUrl: bookmarksFoldersView.homepageUrl |
278 | |
279 | signal bookmarkEntryClicked(url url) |
280 | signal done() |
281 | @@ -51,10 +51,10 @@ |
282 | |
283 | onBookmarkClicked: bookmarksView.bookmarkEntryClicked(url) |
284 | onBookmarkRemoved: { |
285 | - if (BookmarksModel.count == 1) { |
286 | + BookmarksModel.remove(url) |
287 | + if (count == 0) { |
288 | done() |
289 | } |
290 | - BookmarksModel.remove(url) |
291 | } |
292 | } |
293 | |
294 | |
295 | === modified file 'src/app/webbrowser/CMakeLists.txt' |
296 | --- src/app/webbrowser/CMakeLists.txt 2015-10-18 19:21:48 +0000 |
297 | +++ src/app/webbrowser/CMakeLists.txt 2015-12-02 14:15:47 +0000 |
298 | @@ -13,6 +13,7 @@ |
299 | bookmarks-model.cpp |
300 | bookmarks-folder-model.cpp |
301 | bookmarks-folderlist-model.cpp |
302 | + collapsible-bookmarks-model.cpp |
303 | history-domain-model.cpp |
304 | history-domainlist-chronological-model.cpp |
305 | history-domainlist-model.cpp |
306 | |
307 | === modified file 'src/app/webbrowser/NewTabView.qml' |
308 | --- src/app/webbrowser/NewTabView.qml 2015-10-22 08:12:32 +0000 |
309 | +++ src/app/webbrowser/NewTabView.qml 2015-12-02 14:15:47 +0000 |
310 | @@ -63,10 +63,11 @@ |
311 | } |
312 | |
313 | Flickable { |
314 | + id: flickable |
315 | anchors.fill: parent |
316 | contentHeight: internal.seeMoreBookmarksView ? |
317 | - bookmarksFolderListViewLoader.height + units.gu(6) : |
318 | - contentColumn.height |
319 | + title.height + separator.height + bookmarksViewLoader.height : |
320 | + contentColumn.height |
321 | |
322 | Column { |
323 | id: contentColumn |
324 | @@ -78,6 +79,7 @@ |
325 | height: childrenRect.height |
326 | |
327 | Row { |
328 | + id: title |
329 | height: units.gu(6) |
330 | anchors { |
331 | left: parent.left |
332 | @@ -127,6 +129,7 @@ |
333 | } |
334 | |
335 | Rectangle { |
336 | + id: separator |
337 | height: units.gu(0.1) |
338 | anchors { |
339 | left: parent.left |
340 | @@ -137,20 +140,19 @@ |
341 | } |
342 | |
343 | Loader { |
344 | - id: bookmarksFolderListViewLoader |
345 | + id: bookmarksViewLoader |
346 | |
347 | anchors { |
348 | left: parent.left |
349 | right: parent.right |
350 | } |
351 | - |
352 | - height: status == Loader.Ready ? item.height : 0 |
353 | + height: active ? (flickable.height - title.height - separator.height) : 0 |
354 | |
355 | active: internal.seeMoreBookmarksView |
356 | + clip: active |
357 | |
358 | sourceComponent: BookmarksFoldersView { |
359 | - homeBookmarkUrl: newTabView.settingsObject.homepage |
360 | - |
361 | + homepageUrl: newTabView.settingsObject.homepage |
362 | onBookmarkClicked: newTabView.bookmarkClicked(url) |
363 | onBookmarkRemoved: newTabView.bookmarkRemoved(url) |
364 | } |
365 | |
366 | === modified file 'src/app/webbrowser/bookmarks-model.cpp' |
367 | --- src/app/webbrowser/bookmarks-model.cpp 2015-07-27 15:36:59 +0000 |
368 | +++ src/app/webbrowser/bookmarks-model.cpp 2015-12-02 14:15:47 +0000 |
369 | @@ -31,7 +31,7 @@ |
370 | BookmarksModel is a list model that stores bookmark entries for quick access |
371 | to favourite websites. For a given URL, the following information is stored: |
372 | page title and URL to the favorite icon if any. |
373 | - The model is sorted alphabetically at all times (by URL). |
374 | + The model is sorted by recency at all times (most recently created bookmark first). |
375 | |
376 | The information is persistently stored on disk in a SQLite database. |
377 | The database is read at startup to populate the model, and whenever a new |
378 | |
379 | === added file 'src/app/webbrowser/collapsible-bookmarks-model.cpp' |
380 | --- src/app/webbrowser/collapsible-bookmarks-model.cpp 1970-01-01 00:00:00 +0000 |
381 | +++ src/app/webbrowser/collapsible-bookmarks-model.cpp 2015-12-02 14:15:47 +0000 |
382 | @@ -0,0 +1,572 @@ |
383 | +/* |
384 | + * Copyright 2015 Canonical Ltd. |
385 | + * |
386 | + * This file is part of webbrowser-app. |
387 | + * |
388 | + * webbrowser-app is free software; you can redistribute it and/or modify |
389 | + * it under the terms of the GNU General Public License as published by |
390 | + * the Free Software Foundation; version 3. |
391 | + * |
392 | + * webbrowser-app is distributed in the hope that it will be useful, |
393 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
394 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
395 | + * GNU General Public License for more details. |
396 | + * |
397 | + * You should have received a copy of the GNU General Public License |
398 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
399 | + */ |
400 | + |
401 | +#include "collapsible-bookmarks-model.h" |
402 | + |
403 | +// Qt |
404 | +#include <QtCore/QUrl> |
405 | + |
406 | +/*! |
407 | + \class CollapsibleBookmarksModel |
408 | + \brief List model that exposes bookmarked websites in collapsible folders. |
409 | + |
410 | + CollapsibleBookmarksModel is a proxy model that takes a BookmarksModel as |
411 | + source and exposes its folders and bookmarks. Folders are collapsed by |
412 | + default (meaning that no bookmarks under them are advertised by the model) |
413 | + and can be expanded or collapsed again using the toggleFolderState() |
414 | + method. |
415 | + |
416 | + The first folder in the model has no name, it contains all bookmarks that |
417 | + are not in any other folder. It may also contain a special placeholder |
418 | + entry (always first) that can be used to display a special homepage |
419 | + bookmark. This is controlled by setting the 'prependHomepage' property |
420 | + (true by default). |
421 | + |
422 | + The folders are sorted by alphabetical order in the model, and inside each |
423 | + folder bookmarks are sorted by recency (most recently created first). |
424 | + |
425 | + By default, empty folders are hidden, but they can be shown by setting the |
426 | + 'showEmptyFolders' property to true. |
427 | + |
428 | + The roles exposed by the model are as follows: |
429 | + - "url" (url): for a bookmark, the associated URL |
430 | + - "title" (string): for a bookmark, the associated title |
431 | + - "icon" (url): for a bookmark, the associated favicon |
432 | + - "created" (datetime): for a bookmark, the creation timestamp |
433 | + - "folder" (string): the folder name |
434 | + - "type" (string): one of "homepage", "bookmark", "emptyFolder", |
435 | + "collapsedFolder", "expandedFolder" |
436 | +*/ |
437 | +CollapsibleBookmarksModel::CollapsibleBookmarksModel(QObject* parent) |
438 | + : QAbstractListModel(parent) |
439 | + , m_sourceModel(nullptr) |
440 | + , m_prependHomepage(true) |
441 | + , m_showEmptyFolders(false) |
442 | + , m_count(0) |
443 | +{} |
444 | + |
445 | +CollapsibleBookmarksModel::~CollapsibleBookmarksModel() |
446 | +{} |
447 | + |
448 | +BookmarksModel* CollapsibleBookmarksModel::sourceModel() const |
449 | +{ |
450 | + return m_sourceModel; |
451 | +} |
452 | + |
453 | +void CollapsibleBookmarksModel::setSourceModel(BookmarksModel* sourceModel) |
454 | +{ |
455 | + if (sourceModel != m_sourceModel) { |
456 | + beginResetModel(); |
457 | + if (m_sourceModel) { |
458 | + m_sourceModel->disconnect(this); |
459 | + } |
460 | + m_folders.clear(); |
461 | + m_sourceModel = sourceModel; |
462 | + populateModel(); |
463 | + if (m_sourceModel) { |
464 | + connect(m_sourceModel, SIGNAL(folderAdded(const QString&)), |
465 | + SLOT(onFolderAdded(const QString&))); |
466 | + connect(m_sourceModel, SIGNAL(rowsInserted(const QModelIndex&, int, int)), |
467 | + SLOT(onRowsInserted(const QModelIndex&, int, int))); |
468 | + connect(m_sourceModel, SIGNAL(removed(const QUrl&)), SLOT(onBookmarkRemoved(const QUrl&))); |
469 | + connect(m_sourceModel, SIGNAL(modelReset()), SLOT(onModelReset())); |
470 | + connect(m_sourceModel, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, QVector<int>)), |
471 | + SLOT(onDataChanged(const QModelIndex&, const QModelIndex&, QVector<int>))); |
472 | + } |
473 | + endResetModel(); |
474 | + Q_EMIT sourceModelChanged(); |
475 | + } |
476 | +} |
477 | + |
478 | +bool CollapsibleBookmarksModel::prependHomepage() const |
479 | +{ |
480 | + return m_prependHomepage; |
481 | +} |
482 | + |
483 | +void CollapsibleBookmarksModel::setPrependHomepage(bool value) |
484 | +{ |
485 | + if (value != m_prependHomepage) { |
486 | + m_prependHomepage = value; |
487 | + if (m_sourceModel) { |
488 | + QPair<bool, QList<QVariantMap>>& allFolder = m_folders[QStringLiteral("")]; |
489 | + int first = ((allFolder.second.isEmpty() && !m_showEmptyFolders) ? 0 : 1); |
490 | + int last = allFolder.first ? 1 : 0; |
491 | + if (last >= first) { |
492 | + if (m_prependHomepage) { |
493 | + beginInsertRows(QModelIndex(), first, last); |
494 | + m_count += (last - first + 1); |
495 | + endInsertRows(); |
496 | + } else { |
497 | + beginRemoveRows(QModelIndex(), first, last); |
498 | + m_count -= (last - first + 1); |
499 | + endRemoveRows(); |
500 | + } |
501 | + } |
502 | + } |
503 | + Q_EMIT prependHomepageChanged(); |
504 | + } |
505 | +} |
506 | + |
507 | +bool CollapsibleBookmarksModel::showEmptyFolders() const |
508 | +{ |
509 | + return m_showEmptyFolders; |
510 | +} |
511 | + |
512 | +void CollapsibleBookmarksModel::setShowEmptyFolders(bool value) |
513 | +{ |
514 | + if (value != m_showEmptyFolders) { |
515 | + m_showEmptyFolders = value; |
516 | + FoldersMap::const_iterator i; |
517 | + int index = -1; |
518 | + for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) { |
519 | + ++index; |
520 | + if (i.value().second.isEmpty() && !(i.key().isEmpty() && m_prependHomepage)) { |
521 | + if (m_showEmptyFolders) { |
522 | + beginInsertRows(QModelIndex(), index, index); |
523 | + ++m_count; |
524 | + endInsertRows(); |
525 | + } else { |
526 | + beginRemoveRows(QModelIndex(), index, index); |
527 | + --m_count; |
528 | + endRemoveRows(); |
529 | + --index; |
530 | + } |
531 | + } else { |
532 | + index += (i.value().first ? i.value().second.count() : 0); |
533 | + index += ((i.key().isEmpty() && i.value().first && m_prependHomepage) ? 1 : 0); |
534 | + } |
535 | + } |
536 | + Q_EMIT showEmptyFoldersChanged(); |
537 | + } |
538 | +} |
539 | + |
540 | +QHash<int, QByteArray> CollapsibleBookmarksModel::roleNames() const |
541 | +{ |
542 | + static QHash<int, QByteArray> roles; |
543 | + if (roles.isEmpty()) { |
544 | + roles[Url] = "url"; |
545 | + roles[Title] = "title"; |
546 | + roles[Icon] = "icon"; |
547 | + roles[Created] = "created"; |
548 | + roles[Folder] = "folder"; |
549 | + roles[Type] = "type"; |
550 | + } |
551 | + return roles; |
552 | +} |
553 | + |
554 | +int CollapsibleBookmarksModel::rowCount(const QModelIndex& parent) const |
555 | +{ |
556 | + Q_UNUSED(parent); |
557 | + return m_count; |
558 | +} |
559 | + |
560 | +QVariant CollapsibleBookmarksModel::data(const QModelIndex& index, int role) const |
561 | +{ |
562 | + if (!index.isValid()) { |
563 | + return QVariant(); |
564 | + } |
565 | + QString folder; |
566 | + QVariantMap bookmark; |
567 | + bool homepage = false; |
568 | + int row = index.row(); |
569 | + FoldersMap::const_iterator i; |
570 | + for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) { |
571 | + if (!m_showEmptyFolders && i.value().second.isEmpty() && |
572 | + !(i.key().isEmpty() && m_prependHomepage)) { |
573 | + continue; |
574 | + } |
575 | + if (row == 0) { |
576 | + folder = i.key(); |
577 | + break; |
578 | + } else if (i.value().first) { |
579 | + bool allBookmarksWithHomepage = (i.key().isEmpty() && m_prependHomepage); |
580 | + if (allBookmarksWithHomepage) { |
581 | + if (row == 1) { |
582 | + homepage = true; |
583 | + break; |
584 | + } else { |
585 | + --row; |
586 | + } |
587 | + } |
588 | + int count = i.value().second.count(); |
589 | + if (row <= count) { |
590 | + bookmark = i.value().second.at(row - 1); |
591 | + break; |
592 | + } else { |
593 | + row -= (count + 1); |
594 | + } |
595 | + } else { |
596 | + --row; |
597 | + } |
598 | + } |
599 | + switch(role) { |
600 | + case Url: |
601 | + return bookmark[QStringLiteral("url")]; |
602 | + case Title: |
603 | + return bookmark[QStringLiteral("title")]; |
604 | + case Icon: |
605 | + return bookmark[QStringLiteral("icon")]; |
606 | + case Created: |
607 | + return bookmark[QStringLiteral("created")]; |
608 | + case Folder: |
609 | + return (homepage ? QStringLiteral("") : (folder.isNull() ? bookmark[QStringLiteral("folder")] : folder)); |
610 | + case Type: |
611 | + if (homepage) { |
612 | + return QStringLiteral("homepage"); |
613 | + } else if (folder.isNull()) { |
614 | + return QStringLiteral("bookmark"); |
615 | + } else if (i.value().second.isEmpty() && !(i.key().isEmpty() && m_prependHomepage)) { |
616 | + return QStringLiteral("emptyFolder"); |
617 | + } else if (i.value().first) { |
618 | + return QStringLiteral("expandedFolder"); |
619 | + } else { |
620 | + return QStringLiteral("collapsedFolder"); |
621 | + } |
622 | + default: |
623 | + return QVariant(); |
624 | + } |
625 | +} |
626 | + |
627 | +void CollapsibleBookmarksModel::toggleFolderState(const QString& folder) { |
628 | + int index = folderIndex(folder); |
629 | + if (index == -1) { |
630 | + return; |
631 | + } |
632 | + QPair<bool, QList<QVariantMap>>& folderInfo = m_folders[folder]; |
633 | + if (folderInfo.second.isEmpty() && !(folder.isEmpty() && m_prependHomepage)) { |
634 | + return; |
635 | + } |
636 | + bool expanded = folderInfo.first; |
637 | + int first = index + 1; |
638 | + int last = index + folderInfo.second.count(); |
639 | + if (folder.isEmpty() && m_prependHomepage) { |
640 | + ++last; |
641 | + } |
642 | + if (expanded) { |
643 | + beginRemoveRows(QModelIndex(), first, last); |
644 | + m_count -= (last - first + 1); |
645 | + } else { |
646 | + beginInsertRows(QModelIndex(), first, last); |
647 | + m_count += (last - first + 1); |
648 | + } |
649 | + folderInfo.first = !expanded; |
650 | + if (expanded) { |
651 | + endRemoveRows(); |
652 | + } else { |
653 | + endInsertRows(); |
654 | + } |
655 | + QModelIndex modelIndex = this->index(index, 0); |
656 | + Q_EMIT dataChanged(modelIndex, modelIndex, QVector<int>() << Type); |
657 | +} |
658 | + |
659 | +void CollapsibleBookmarksModel::populateModel() |
660 | +{ |
661 | + if (!m_sourceModel) { |
662 | + return; |
663 | + } |
664 | + Q_FOREACH(const QString& folder, m_sourceModel->folders()) { |
665 | + m_folders[folder].first = false; |
666 | + } |
667 | + m_folders[QStringLiteral("")].first = true; |
668 | + int count = m_sourceModel->rowCount(); |
669 | + for (int i = 0; i < count; ++i) { |
670 | + QModelIndex index = m_sourceModel->index(i); |
671 | + QVariantMap variant; |
672 | + variant[QStringLiteral("url")] = m_sourceModel->data(index, BookmarksModel::Url); |
673 | + variant[QStringLiteral("title")] = m_sourceModel->data(index, BookmarksModel::Title); |
674 | + variant[QStringLiteral("icon")] = m_sourceModel->data(index, BookmarksModel::Icon); |
675 | + variant[QStringLiteral("created")] = m_sourceModel->data(index, BookmarksModel::Created); |
676 | + QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString(); |
677 | + variant[QStringLiteral("folder")] = folder; |
678 | + m_folders[folder].second.append(variant); |
679 | + } |
680 | + m_count = 0; |
681 | + FoldersMap::const_iterator i; |
682 | + for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) { |
683 | + bool empty = i.value().second.isEmpty(); |
684 | + if (!empty || m_showEmptyFolders || (i.key().isEmpty() && m_prependHomepage)) { |
685 | + ++m_count; |
686 | + } |
687 | + m_count += (i.value().first ? i.value().second.count() : 0); |
688 | + m_count += ((i.key().isEmpty() && i.value().first && m_prependHomepage) ? 1 : 0); |
689 | + } |
690 | +} |
691 | + |
692 | +int CollapsibleBookmarksModel::folderIndex(const QString& folder) const |
693 | +{ |
694 | + if (!m_showEmptyFolders && m_folders[folder].second.isEmpty() && |
695 | + !(folder.isEmpty() && m_prependHomepage)) { |
696 | + return -1; |
697 | + } |
698 | + int index = 0; |
699 | + FoldersMap::const_iterator i; |
700 | + for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) { |
701 | + if (i.key() == folder) { |
702 | + return index; |
703 | + } |
704 | + if (m_showEmptyFolders || !i.value().second.isEmpty() || |
705 | + (i.key().isEmpty() && m_prependHomepage)) { |
706 | + ++index; |
707 | + } |
708 | + if (i.value().first) { |
709 | + index += i.value().second.count(); |
710 | + index += ((i.key().isEmpty() && m_prependHomepage) ? 1 : 0); |
711 | + } |
712 | + } |
713 | + return -1; |
714 | +} |
715 | + |
716 | +QPair<QString, int> CollapsibleBookmarksModel::bookmarkPosition(const QUrl& url) const |
717 | +{ |
718 | + // This assumes url is not equal to the homepage, which should be safe as |
719 | + // the homepage is not supposed to be stored in the bookmarks model anyway. |
720 | + int index = 0; |
721 | + int homepageOffset = 0; |
722 | + FoldersMap::const_iterator i; |
723 | + for (i = m_folders.constBegin(); i != m_folders.constEnd(); ++i) { |
724 | + if (m_showEmptyFolders || !i.value().second.isEmpty() || |
725 | + (i.key().isEmpty() && m_prependHomepage)) { |
726 | + ++index; |
727 | + } |
728 | + if (i.key().isEmpty() && m_prependHomepage && i.value().first) { |
729 | + homepageOffset = 1; |
730 | + } |
731 | + const QList<QVariantMap>& bookmarks = i.value().second; |
732 | + for (int j = 0; j < bookmarks.count(); ++j) { |
733 | + if (bookmarks.at(j)["url"].toUrl() == url) { |
734 | + if (i.value().first) { |
735 | + return QPair<QString, int>(i.key(), index + j + homepageOffset); |
736 | + } else { |
737 | + return QPair<QString, int>(i.key(), -1); |
738 | + } |
739 | + } |
740 | + } |
741 | + index += (i.value().first ? bookmarks.count() : 0); |
742 | + } |
743 | + return QPair<QString, int>(QString(), -1); |
744 | +} |
745 | + |
746 | +void CollapsibleBookmarksModel::onFolderAdded(const QString& folder) |
747 | +{ |
748 | + if (m_folders.contains(folder)) { |
749 | + return; |
750 | + } |
751 | + m_folders[folder].first = false; |
752 | + int index = folderIndex(folder); |
753 | + if (index != -1) { |
754 | + beginInsertRows(QModelIndex(), index, index); |
755 | + ++m_count; |
756 | + endInsertRows(); |
757 | + } |
758 | +} |
759 | + |
760 | +void CollapsibleBookmarksModel::onRowsInserted(const QModelIndex& parent, int start, int end) |
761 | +{ |
762 | + for (int i = start; i <= end; ++i) { |
763 | + QModelIndex index = m_sourceModel->index(i); |
764 | + QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString(); |
765 | + int folderIndex = this->folderIndex(folder); |
766 | + QDateTime created = m_sourceModel->data(index, BookmarksModel::Created).toDateTime(); |
767 | + if (folderIndex == -1) { |
768 | + // new folder, insert it |
769 | + m_folders[folder].first = false; |
770 | + } |
771 | + bool expanded = m_folders[folder].first; |
772 | + QList<QVariantMap>& bookmarks = m_folders[folder].second; |
773 | + int newIndexInFolder = bookmarks.count(); |
774 | + for (int j = 0; j < newIndexInFolder; ++j) { |
775 | + if (created >= bookmarks.at(j)[QStringLiteral("created")].toDateTime()) { |
776 | + newIndexInFolder = j; |
777 | + break; |
778 | + } |
779 | + } |
780 | + if (expanded) { |
781 | + int newGlobalIndex = folderIndex + newIndexInFolder + 1; |
782 | + if (folder.isEmpty() && m_prependHomepage) { |
783 | + ++newGlobalIndex; |
784 | + } |
785 | + beginInsertRows(QModelIndex(), newGlobalIndex, newGlobalIndex); |
786 | + } |
787 | + QVariantMap variant; |
788 | + variant[QStringLiteral("url")] = m_sourceModel->data(index, BookmarksModel::Url); |
789 | + variant[QStringLiteral("title")] = m_sourceModel->data(index, BookmarksModel::Title); |
790 | + variant[QStringLiteral("icon")] = m_sourceModel->data(index, BookmarksModel::Icon); |
791 | + variant[QStringLiteral("created")] = created; |
792 | + variant[QStringLiteral("folder")] = folder; |
793 | + m_folders[folder].second.insert(newIndexInFolder, variant); |
794 | + if (expanded) { |
795 | + ++m_count; |
796 | + endInsertRows(); |
797 | + } else if (folderIndex == -1) { |
798 | + // this is a new folder, doing that now (after inserting the bookmark) |
799 | + // otherwise folderIndex() would return -1 |
800 | + folderIndex = this->folderIndex(folder); |
801 | + beginInsertRows(QModelIndex(), folderIndex, folderIndex); |
802 | + ++m_count; |
803 | + endInsertRows(); |
804 | + } |
805 | + } |
806 | +} |
807 | + |
808 | +void CollapsibleBookmarksModel::onBookmarkRemoved(const QUrl& url) |
809 | +{ |
810 | + QPair<QString, int> position = bookmarkPosition(url); |
811 | + if (position.second != -1) { |
812 | + int first = position.second; |
813 | + if (!m_showEmptyFolders && (m_folders[position.first].second.count() == 1) && |
814 | + !(position.first.isEmpty() && m_prependHomepage)) { |
815 | + // removed the last bookmark from the folder, which becomes empty: hide it |
816 | + --first; |
817 | + } |
818 | + beginRemoveRows(QModelIndex(), first, position.second); |
819 | + m_count -= (position.second - first + 1); |
820 | + } |
821 | + QList<QVariantMap>& bookmarks = m_folders[position.first].second; |
822 | + for (int i = 0; i < bookmarks.count(); ++i) { |
823 | + if (bookmarks.at(i)[QStringLiteral("url")].toUrl() == url) { |
824 | + bookmarks.removeAt(i); |
825 | + break; |
826 | + } |
827 | + } |
828 | + if (bookmarks.isEmpty() && !(position.first.isEmpty() && m_prependHomepage)) { |
829 | + // the folder has become empty, collapse it |
830 | + m_folders[position.first].first = false; |
831 | + if (m_showEmptyFolders) { |
832 | + QModelIndex folderModelIndex = this->index(folderIndex(position.first)); |
833 | + Q_EMIT dataChanged(folderModelIndex, folderModelIndex, QVector<int>() << Type); |
834 | + } |
835 | + } |
836 | + if (position.second != -1) { |
837 | + endRemoveRows(); |
838 | + } |
839 | +} |
840 | + |
841 | +void CollapsibleBookmarksModel::onModelReset() |
842 | +{ |
843 | + beginResetModel(); |
844 | + populateModel(); |
845 | + endResetModel(); |
846 | +} |
847 | + |
848 | +void CollapsibleBookmarksModel::onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, QVector<int> roles) |
849 | +{ |
850 | + Q_UNUSED(roles); |
851 | + int first = topLeft.row(); |
852 | + int last = bottomRight.row(); |
853 | + for (int i = first; i <= last; ++i) { |
854 | + QModelIndex index = m_sourceModel->index(i); |
855 | + QUrl url = m_sourceModel->data(index, BookmarksModel::Url).toUrl(); |
856 | + QString title = m_sourceModel->data(index, BookmarksModel::Title).toString(); |
857 | + QUrl icon = m_sourceModel->data(index, BookmarksModel::Icon).toUrl(); |
858 | + QString folder = m_sourceModel->data(index, BookmarksModel::Folder).toString(); |
859 | + int folderIndex = this->folderIndex(folder); |
860 | + QDateTime created = m_sourceModel->data(index, BookmarksModel::Created).toDateTime(); |
861 | + bool expanded = m_folders[folder].first; |
862 | + QPair<QString, int> position = bookmarkPosition(url); |
863 | + if (position.first != folder) { |
864 | + // the bookmark was moved to a different folder |
865 | + QList<QVariantMap>& targetFolder = m_folders[folder].second; |
866 | + int newIndexInFolder = targetFolder.count(); |
867 | + int newGlobalIndex = 0; |
868 | + for (int j = 0; j < newIndexInFolder; ++j) { |
869 | + if (created >= targetFolder.at(j)[QStringLiteral("created")].toDateTime()) { |
870 | + newIndexInFolder = j; |
871 | + break; |
872 | + } |
873 | + } |
874 | + if (expanded) { |
875 | + newGlobalIndex = folderIndex + newIndexInFolder + 1; |
876 | + if (folder.isEmpty() && m_prependHomepage) { |
877 | + ++newGlobalIndex; |
878 | + } |
879 | + } |
880 | + int sourceFolderIndex = this->folderIndex(position.first); |
881 | + if (expanded && (position.second != -1)) { |
882 | + // both source folder and target folder are expanded |
883 | + beginMoveRows(QModelIndex(), position.second, position.second, |
884 | + QModelIndex(), newGlobalIndex); |
885 | + if (position.second > newGlobalIndex) { |
886 | + ++sourceFolderIndex; |
887 | + } |
888 | + } else if (expanded) { |
889 | + // source folder is collapsed, target folder is expanded |
890 | + beginInsertRows(QModelIndex(), newGlobalIndex, newGlobalIndex); |
891 | + } else if (position.second != -1) { |
892 | + // source folder is expanded, target folder is collapsed |
893 | + beginRemoveRows(QModelIndex(), position.second, position.second); |
894 | + } |
895 | + // actually move the bookmark from one folder to the other |
896 | + QList<QVariantMap>& sourceFolder = m_folders[position.first].second; |
897 | + QList<QVariantMap>::iterator j; |
898 | + for (j = sourceFolder.begin(); j != sourceFolder.end(); ++j) { |
899 | + if ((*j)["url"].toUrl() == url) { |
900 | + sourceFolder.erase(j); |
901 | + break; |
902 | + } |
903 | + } |
904 | + QVariantMap bookmark; |
905 | + bookmark["url"] = url; |
906 | + bookmark["title"] = title; |
907 | + bookmark["icon"] = icon; |
908 | + bookmark["folder"] = folder; |
909 | + bookmark["created"] = created; |
910 | + targetFolder.insert(newIndexInFolder, bookmark); |
911 | + if (expanded && (position.second != -1)) { |
912 | + endMoveRows(); |
913 | + if (newGlobalIndex > position.second) { |
914 | + --newGlobalIndex; |
915 | + } |
916 | + QModelIndex changedIndex = this->index(newGlobalIndex); |
917 | + Q_EMIT dataChanged(changedIndex, changedIndex); |
918 | + } else if (expanded) { |
919 | + ++m_count; |
920 | + endInsertRows(); |
921 | + } else if (position.second != -1) { |
922 | + --m_count; |
923 | + endRemoveRows(); |
924 | + } |
925 | + if (sourceFolder.isEmpty() && !(position.first.isEmpty() && m_prependHomepage)) { |
926 | + // removed the last bookmark from the folder, which becomes empty, collapse it |
927 | + m_folders[position.first].first = false; |
928 | + if (m_showEmptyFolders) { |
929 | + QModelIndex folderModelIndex = this->index(sourceFolderIndex); |
930 | + Q_EMIT dataChanged(folderModelIndex, folderModelIndex, QVector<int>() << Type); |
931 | + } else { |
932 | + // hide it |
933 | + beginRemoveRows(QModelIndex(), sourceFolderIndex, sourceFolderIndex); |
934 | + --m_count; |
935 | + endRemoveRows(); |
936 | + } |
937 | + } |
938 | + } else if (position.second != -1) { |
939 | + // the bookmark has changed and its folder is currently expanded |
940 | + QList<QVariantMap>& bookmarks = m_folders[position.first].second; |
941 | + QList<QVariantMap>::iterator j = bookmarks.begin(); |
942 | + for (j = bookmarks.begin(); j != bookmarks.end(); ++j) { |
943 | + if ((*j)["url"].toUrl() == url) { |
944 | + (*j)["title"] = title; |
945 | + (*j)["icon"] = icon; |
946 | + (*j)["created"] = created; |
947 | + break; |
948 | + } |
949 | + } |
950 | + QModelIndex changedIndex = this->index(position.second); |
951 | + Q_EMIT dataChanged(changedIndex, changedIndex); |
952 | + } |
953 | + } |
954 | +} |
955 | |
956 | === added file 'src/app/webbrowser/collapsible-bookmarks-model.h' |
957 | --- src/app/webbrowser/collapsible-bookmarks-model.h 1970-01-01 00:00:00 +0000 |
958 | +++ src/app/webbrowser/collapsible-bookmarks-model.h 2015-12-02 14:15:47 +0000 |
959 | @@ -0,0 +1,98 @@ |
960 | +/* |
961 | + * Copyright 2015 Canonical Ltd. |
962 | + * |
963 | + * This file is part of webbrowser-app. |
964 | + * |
965 | + * webbrowser-app is free software; you can redistribute it and/or modify |
966 | + * it under the terms of the GNU General Public License as published by |
967 | + * the Free Software Foundation; version 3. |
968 | + * |
969 | + * webbrowser-app is distributed in the hope that it will be useful, |
970 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
971 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
972 | + * GNU General Public License for more details. |
973 | + * |
974 | + * You should have received a copy of the GNU General Public License |
975 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
976 | + */ |
977 | + |
978 | +#ifndef __BOOKMARKS_PROXY_MODEL_H__ |
979 | +#define __BOOKMARKS_PROXY_MODEL_H__ |
980 | + |
981 | +// Qt |
982 | +#include <QtCore/QAbstractListModel> |
983 | +#include <QtCore/QMap> |
984 | +#include <QtCore/QPair> |
985 | +#include <QtCore/QString> |
986 | +#include <QtCore/QVariant> |
987 | + |
988 | +#include "bookmarks-model.h" |
989 | + |
990 | +class QUrl; |
991 | + |
992 | +class CollapsibleBookmarksModel : public QAbstractListModel |
993 | +{ |
994 | + Q_OBJECT |
995 | + |
996 | + Q_PROPERTY(BookmarksModel* sourceModel READ sourceModel WRITE setSourceModel NOTIFY sourceModelChanged) |
997 | + Q_PROPERTY(bool prependHomepage READ prependHomepage WRITE setPrependHomepage NOTIFY prependHomepageChanged) |
998 | + Q_PROPERTY(bool showEmptyFolders READ showEmptyFolders WRITE setShowEmptyFolders NOTIFY showEmptyFoldersChanged) |
999 | + |
1000 | + Q_ENUMS(Roles) |
1001 | + |
1002 | +public: |
1003 | + CollapsibleBookmarksModel(QObject* parent=0); |
1004 | + ~CollapsibleBookmarksModel(); |
1005 | + |
1006 | + enum Roles { |
1007 | + Url = BookmarksModel::Url, |
1008 | + Title = BookmarksModel::Title, |
1009 | + Icon = BookmarksModel::Icon, |
1010 | + Created = BookmarksModel::Created, |
1011 | + Folder = BookmarksModel::Folder, |
1012 | + Type |
1013 | + }; |
1014 | + |
1015 | + // reimplemented from QAbstractListModel |
1016 | + QHash<int, QByteArray> roleNames() const; |
1017 | + int rowCount(const QModelIndex& parent=QModelIndex()) const; |
1018 | + QVariant data(const QModelIndex& index, int role) const; |
1019 | + |
1020 | + BookmarksModel* sourceModel() const; |
1021 | + void setSourceModel(BookmarksModel* sourceModel); |
1022 | + |
1023 | + bool prependHomepage() const; |
1024 | + void setPrependHomepage(bool value); |
1025 | + |
1026 | + bool showEmptyFolders() const; |
1027 | + void setShowEmptyFolders(bool value); |
1028 | + |
1029 | + Q_INVOKABLE void toggleFolderState(const QString& folder); |
1030 | + |
1031 | +Q_SIGNALS: |
1032 | + void sourceModelChanged(); |
1033 | + void prependHomepageChanged(); |
1034 | + void showEmptyFoldersChanged(); |
1035 | + |
1036 | +private: |
1037 | + BookmarksModel* m_sourceModel; |
1038 | + bool m_prependHomepage; |
1039 | + bool m_showEmptyFolders; |
1040 | + // the first folder in the map has an empty (non null) name, it contains all bookmarks that are not in a specific folder |
1041 | + typedef QMap<QString, QPair<bool, QList<QVariantMap>>> FoldersMap; |
1042 | + FoldersMap m_folders; |
1043 | + int m_count; |
1044 | + |
1045 | + void populateModel(); |
1046 | + int folderIndex(const QString& folder) const; |
1047 | + QPair<QString, int> bookmarkPosition(const QUrl& url) const; |
1048 | + |
1049 | +private Q_SLOTS: |
1050 | + void onFolderAdded(const QString& folder); |
1051 | + void onRowsInserted(const QModelIndex& parent, int start, int end); |
1052 | + void onBookmarkRemoved(const QUrl& url); |
1053 | + void onModelReset(); |
1054 | + void onDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, QVector<int> roles); |
1055 | +}; |
1056 | + |
1057 | +#endif // __BOOKMARKS_PROXY_MODEL_H__ |
1058 | |
1059 | === modified file 'src/app/webbrowser/webbrowser-app.cpp' |
1060 | --- src/app/webbrowser/webbrowser-app.cpp 2015-10-22 15:07:26 +0000 |
1061 | +++ src/app/webbrowser/webbrowser-app.cpp 2015-12-02 14:15:47 +0000 |
1062 | @@ -19,6 +19,7 @@ |
1063 | #include "bookmarks-model.h" |
1064 | #include "bookmarks-folderlist-model.h" |
1065 | #include "cache-deleter.h" |
1066 | +#include "collapsible-bookmarks-model.h" |
1067 | #include "config.h" |
1068 | #include "file-operations.h" |
1069 | #include "history-domainlist-chronological-model.h" |
1070 | @@ -92,6 +93,7 @@ |
1071 | qmlRegisterType<TabsModel>(uri, 0, 1, "TabsModel"); |
1072 | qmlRegisterSingletonType<BookmarksModel>(uri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory); |
1073 | qmlRegisterType<BookmarksFolderListModel>(uri, 0, 1, "BookmarksFolderListModel"); |
1074 | + qmlRegisterType<CollapsibleBookmarksModel>(uri, 0, 1, "CollapsibleBookmarksModel"); |
1075 | qmlRegisterSingletonType<FileOperations>(uri, 0, 1, "FileOperations", FileOperations_singleton_factory); |
1076 | qmlRegisterType<SearchEngine>(uri, 0, 1, "SearchEngine"); |
1077 | qmlRegisterSingletonType<CacheDeleter>(uri, 0, 1, "CacheDeleter", CacheDeleter_singleton_factory); |
1078 | |
1079 | === modified file 'tests/autopilot/webbrowser_app/emulators/browser.py' |
1080 | --- tests/autopilot/webbrowser_app/emulators/browser.py 2015-11-26 13:42:12 +0000 |
1081 | +++ tests/autopilot/webbrowser_app/emulators/browser.py 2015-12-02 14:15:47 +0000 |
1082 | @@ -134,9 +134,9 @@ |
1083 | |
1084 | def get_new_tab_view(self): |
1085 | if self.wide: |
1086 | - return self.wait_select_single("NewTabViewWide", visible=True) |
1087 | + return self.wait_select_single(NewTabViewWide, visible=True) |
1088 | else: |
1089 | - return self.wait_select_single("NewTabView", visible=True) |
1090 | + return self.wait_select_single(NewTabView, visible=True) |
1091 | |
1092 | # Since the NewPrivateTabView does not define any new QML property in its |
1093 | # extended file, it does not report itself to autopilot with the same name |
1094 | @@ -169,9 +169,9 @@ |
1095 | def get_bookmarks_view(self): |
1096 | try: |
1097 | if self.wide: |
1098 | - return self.select_single("BookmarksViewWide") |
1099 | + return self.select_single(BookmarksViewWide) |
1100 | else: |
1101 | - return self.select_single("BookmarksView") |
1102 | + return self.select_single(BookmarksView) |
1103 | except exceptions.StateNotFoundError: |
1104 | return None |
1105 | |
1106 | @@ -286,8 +286,7 @@ |
1107 | return self.select_single("ChromeButton", objectName="drawerButton") |
1108 | |
1109 | def get_drawer(self): |
1110 | - return self.wait_select_single("QQuickItem", objectName="drawer", |
1111 | - clip=False) |
1112 | + return self.wait_select_single(objectName="drawer", clip=False) |
1113 | |
1114 | def get_drawer_action(self, actionName): |
1115 | drawer = self.get_drawer() |
1116 | @@ -567,21 +566,19 @@ |
1117 | def get_notopsites_label(self): |
1118 | return self.select_single(objectName="notopsites") |
1119 | |
1120 | - def get_top_site_items(self): |
1121 | - return self.get_top_sites_list().get_delegates() |
1122 | - |
1123 | - def get_bookmarks_folder_list_view(self): |
1124 | + def _get_bookmarks_folder_list_view(self): |
1125 | return self.wait_select_single(BookmarksFoldersView) |
1126 | |
1127 | + def get_bookmark_folders(self): |
1128 | + return self._get_bookmarks_folder_list_view().get_folder_delegates() |
1129 | + |
1130 | + def get_bookmark_folder(self, folder): |
1131 | + view = self._get_bookmarks_folder_list_view() |
1132 | + return view.get_folder_delegate(folder) |
1133 | + |
1134 | def get_bookmarks(self, folder_name): |
1135 | - # assumes that the "more" button has been clicked |
1136 | - folders = self.get_bookmarks_folder_list_view() |
1137 | - folder_delegate = folders.get_folder_delegate(folder_name) |
1138 | - return folders.get_urls_from_folder(folder_delegate) |
1139 | - |
1140 | - def get_folder_names(self): |
1141 | - folders = self.get_bookmarks_folder_list_view().get_delegates() |
1142 | - return [folder.folderName for folder in folders] |
1143 | + view = self._get_bookmarks_folder_list_view() |
1144 | + return view.get_ordered_bookmark_delegates(folder_name) |
1145 | |
1146 | |
1147 | class NewTabViewWide(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1148 | @@ -596,7 +593,7 @@ |
1149 | list = self.select_single(uitk.QQuickListView, |
1150 | objectName="bookmarksList") |
1151 | return sorted(list.select_many("DraggableUrlDelegateWide", |
1152 | - objectName="bookmarkItem"), |
1153 | + objectName="bookmarkItem"), |
1154 | key=lambda delegate: delegate.globalRect.y) |
1155 | |
1156 | def get_top_sites_list(self): |
1157 | @@ -610,19 +607,63 @@ |
1158 | return sorted(list.select_many(objectName="folderItem"), |
1159 | key=lambda delegate: delegate.globalRect.y) |
1160 | |
1161 | - def get_top_site_items(self): |
1162 | - return self.get_top_sites_list().get_delegates() |
1163 | - |
1164 | def get_bookmarks(self, folder_name): |
1165 | - folders = self.get_folders_list() |
1166 | - matches = [folder for folder in folders if folder.name == folder_name] |
1167 | - if not len(matches) == 1: |
1168 | - return [] |
1169 | - self.pointing_device.click_object(matches[0]) |
1170 | + folder = self.get_bookmark_folder(folder_name) |
1171 | + self.pointing_device.click_object(folder) |
1172 | return self.get_bookmarks_list() |
1173 | |
1174 | - def get_folder_names(self): |
1175 | - return [folder.name for folder in self.get_folders_list()] |
1176 | + def get_bookmark_folders(self): |
1177 | + return self.get_folders_list() |
1178 | + |
1179 | + def get_bookmark_folder(self, folder): |
1180 | + self.go_to_section(1) |
1181 | + list = self.select_single(uitk.QQuickListView, |
1182 | + objectName="foldersList") |
1183 | + return list.select_single(objectName="folderItem", name=folder) |
1184 | + |
1185 | + |
1186 | +class BookmarksViewBase(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1187 | + |
1188 | + def close(self): |
1189 | + button = self.select_single(objectName="doneButton") |
1190 | + self.pointing_device.click_object(button) |
1191 | + |
1192 | + |
1193 | +class BookmarksView(BookmarksViewBase): |
1194 | + |
1195 | + def get_bookmark_folders(self): |
1196 | + return self.select_many(objectName="bookmarkViewFolderDelegate") |
1197 | + |
1198 | + def get_bookmark_folder(self, folder): |
1199 | + return self.select_single(objectName="bookmarkViewFolderDelegate", |
1200 | + name=folder) |
1201 | + |
1202 | + def get_bookmarks(self, folder_name): |
1203 | + bookmarks = self.select_many(objectName="bookmarkViewBookmarkDelegate", |
1204 | + folderName=folder_name) |
1205 | + return sorted(bookmarks, key=lambda delegate: delegate.globalRect.y) |
1206 | + |
1207 | + |
1208 | +class BookmarksViewWide(BookmarksViewBase): |
1209 | + |
1210 | + def get_bookmark_folders(self): |
1211 | + list = self.select_single(uitk.QQuickListView, |
1212 | + objectName="foldersList") |
1213 | + return list.select_many(objectName="folderItem") |
1214 | + |
1215 | + def get_bookmark_folder(self, folder): |
1216 | + list = self.select_single(uitk.QQuickListView, |
1217 | + objectName="foldersList") |
1218 | + return list.select_single(objectName="folderItem", name=folder) |
1219 | + |
1220 | + def get_bookmarks(self, folder_name): |
1221 | + folder = self.get_bookmark_folder(folder_name) |
1222 | + self.pointing_device.click_object(folder) |
1223 | + bookmarks = self.select_single(uitk.QQuickListView, |
1224 | + objectName="bookmarksList") |
1225 | + return sorted(bookmarks.select_many("DraggableUrlDelegateWide", |
1226 | + objectName="bookmarkItem"), |
1227 | + key=lambda delegate: delegate.globalRect.y) |
1228 | |
1229 | |
1230 | class UrlsList(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1231 | @@ -700,24 +741,23 @@ |
1232 | |
1233 | class BookmarksFoldersView(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1234 | |
1235 | - def get_delegates(self): |
1236 | - return sorted(self.select_many("QQuickItem", |
1237 | - objectName="bookmarkFolderDelegate"), |
1238 | - key=lambda delegate: delegate.globalRect.y) |
1239 | + def get_folder_delegates(self): |
1240 | + return sorted( |
1241 | + self.select_many(objectName="bookmarkViewFolderDelegate"), |
1242 | + key=lambda delegate: delegate.globalRect.y) |
1243 | |
1244 | def get_folder_delegate(self, folder): |
1245 | - return self.select_single("QQuickItem", |
1246 | - objectName="bookmarkFolderDelegate", |
1247 | - folderName=folder) |
1248 | - |
1249 | - def get_urls_from_folder(self, folder): |
1250 | - return sorted(folder.select_many(UrlDelegate), |
1251 | + return self.select_single(objectName="bookmarkViewFolderDelegate", |
1252 | + name=folder) |
1253 | + |
1254 | + def get_bookmark_delegates(self, folder): |
1255 | + return self.select_many(objectName="bookmarkViewBookmarkDelegate", |
1256 | + folderName=folder) |
1257 | + |
1258 | + def get_ordered_bookmark_delegates(self, folder): |
1259 | + return sorted(self.get_bookmark_delegates(folder), |
1260 | key=lambda delegate: delegate.globalRect.y) |
1261 | |
1262 | - def get_header_from_folder(self, folder): |
1263 | - return folder.wait_select_single("QQuickItem", |
1264 | - objectName="bookmarkFolderHeader") |
1265 | - |
1266 | |
1267 | class ContextMenuBase(uitk.UbuntuUIToolkitCustomProxyObjectBase): |
1268 | |
1269 | |
1270 | === modified file 'tests/autopilot/webbrowser_app/tests/__init__.py' |
1271 | --- tests/autopilot/webbrowser_app/tests/__init__.py 2015-10-15 19:09:59 +0000 |
1272 | +++ tests/autopilot/webbrowser_app/tests/__init__.py 2015-12-02 14:15:47 +0000 |
1273 | @@ -146,7 +146,7 @@ |
1274 | time.sleep(1) |
1275 | return tabs_view |
1276 | |
1277 | - def open_new_tab(self, open_tabs_view=False, expand_view=False): |
1278 | + def open_new_tab(self, open_tabs_view=False): |
1279 | if (self.main_window.incognito): |
1280 | count = len(self.main_window.get_incognito_webviews()) |
1281 | else: |
1282 | @@ -179,11 +179,6 @@ |
1283 | self.main_window.address_bar.activeFocus, |
1284 | Eventually(Equals(True))) |
1285 | |
1286 | - if not self.main_window.wide and expand_view: |
1287 | - more_button = new_tab_view.get_bookmarks_more_button() |
1288 | - self.assertThat(more_button.visible, Equals(True)) |
1289 | - self.pointing_device.click_object(more_button) |
1290 | - |
1291 | return new_tab_view |
1292 | |
1293 | def open_settings(self): |
1294 | |
1295 | === modified file 'tests/autopilot/webbrowser_app/tests/test_bookmark_options.py' |
1296 | --- tests/autopilot/webbrowser_app/tests/test_bookmark_options.py 2015-10-13 13:40:29 +0000 |
1297 | +++ tests/autopilot/webbrowser_app/tests/test_bookmark_options.py 2015-12-02 14:15:47 +0000 |
1298 | @@ -91,19 +91,19 @@ |
1299 | self.pointing_device.click_object(bookmark_toggle) |
1300 | return self.main_window.get_bookmark_options() |
1301 | |
1302 | - def _assert_bookmark_count_in_folder(self, tab, folder_name, count): |
1303 | - urls = tab.get_bookmarks(folder_name) |
1304 | - self.assertThat(lambda: len(urls), Eventually(Equals(count))) |
1305 | + def _assert_bookmark_count_in_folder(self, view, folder_name, count): |
1306 | + self.assertThat(lambda: len(view.get_bookmarks(folder_name)), |
1307 | + Eventually(Equals(count))) |
1308 | |
1309 | - def _toggle_bookmark_folder(self, tab, folder_name): |
1310 | - folders = tab.get_bookmarks_folder_list_view() |
1311 | - folder_delegate = folders.get_folder_delegate(folder_name) |
1312 | - self.pointing_device.click_object( |
1313 | - folders.get_header_from_folder(folder_delegate)) |
1314 | + def _toggle_bookmark_folder(self, view, folder_name): |
1315 | + delegate = view.get_bookmark_folder(folder_name) |
1316 | + self.pointing_device.click_object(delegate) |
1317 | |
1318 | def test_save_bookmarked_url_in_default_folder(self): |
1319 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1320 | - self._assert_bookmark_count_in_folder(new_tab, "", 5) |
1321 | + view = self.open_bookmarks() |
1322 | + self._assert_bookmark_count_in_folder(view, "", 5) |
1323 | + view.close() |
1324 | + view.wait_until_destroyed() |
1325 | |
1326 | url = self.base_url + "/test2" |
1327 | self.main_window.go_to_url(url) |
1328 | @@ -118,16 +118,18 @@ |
1329 | |
1330 | self.assertThat(chrome.bookmarked, Eventually(Equals(True))) |
1331 | |
1332 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1333 | - self._assert_bookmark_count_in_folder(new_tab, "", 6) |
1334 | + view = self.open_bookmarks() |
1335 | + self._assert_bookmark_count_in_folder(view, "", 6) |
1336 | |
1337 | def test_save_bookmarked_url_in_existing_folder(self): |
1338 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1339 | - self.assertThat(lambda: len(new_tab.get_folder_names()), |
1340 | + view = self.open_bookmarks() |
1341 | + self.assertThat(lambda: len(view.get_bookmark_folders()), |
1342 | Eventually(Equals(3))) |
1343 | if not self.main_window.wide: |
1344 | - self._toggle_bookmark_folder(new_tab, "Actinide") |
1345 | - self._assert_bookmark_count_in_folder(new_tab, "Actinide", 1) |
1346 | + self._toggle_bookmark_folder(view, "Actinide") |
1347 | + self._assert_bookmark_count_in_folder(view, "Actinide", 1) |
1348 | + view.close() |
1349 | + view.wait_until_destroyed() |
1350 | |
1351 | url = self.base_url + "/test2" |
1352 | self.main_window.go_to_url(url) |
1353 | @@ -154,17 +156,19 @@ |
1354 | |
1355 | self.assertThat(chrome.bookmarked, Eventually(Equals(True))) |
1356 | |
1357 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1358 | - self.assertThat(lambda: len(new_tab.get_folder_names()), |
1359 | + view = self.open_bookmarks() |
1360 | + self.assertThat(lambda: len(view.get_bookmark_folders()), |
1361 | Eventually(Equals(3))) |
1362 | if not self.main_window.wide: |
1363 | - self._toggle_bookmark_folder(new_tab, "Actinide") |
1364 | - self._assert_bookmark_count_in_folder(new_tab, "Actinide", 2) |
1365 | + self._toggle_bookmark_folder(view, "Actinide") |
1366 | + self._assert_bookmark_count_in_folder(view, "Actinide", 2) |
1367 | |
1368 | def test_save_bookmarked_url_in_new_folder(self): |
1369 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1370 | - self.assertThat(lambda: len(new_tab.get_folder_names()), |
1371 | + view = self.open_bookmarks() |
1372 | + self.assertThat(lambda: len(view.get_bookmark_folders()), |
1373 | Eventually(Equals(3))) |
1374 | + view.close() |
1375 | + view.wait_until_destroyed() |
1376 | |
1377 | url = self.base_url + "/test2" |
1378 | self.main_window.go_to_url(url) |
1379 | @@ -200,12 +204,12 @@ |
1380 | |
1381 | self.assertThat(chrome.bookmarked, Eventually(Equals(True))) |
1382 | |
1383 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1384 | - self.assertThat(lambda: len(new_tab.get_folder_names()), |
1385 | + view = self.open_bookmarks() |
1386 | + self.assertThat(lambda: len(view.get_bookmark_folders()), |
1387 | Eventually(Equals(4))) |
1388 | if not self.main_window.wide: |
1389 | - self._toggle_bookmark_folder(new_tab, "NewFolder") |
1390 | - self._assert_bookmark_count_in_folder(new_tab, "NewFolder", 1) |
1391 | + self._toggle_bookmark_folder(view, "NewFolder") |
1392 | + self._assert_bookmark_count_in_folder(view, "NewFolder", 1) |
1393 | |
1394 | def test_set_bookmark_title(self): |
1395 | url = self.base_url + "/blanktargetlink" |
1396 | @@ -229,10 +233,10 @@ |
1397 | |
1398 | self.assertThat(chrome.bookmarked, Eventually(Equals(True))) |
1399 | |
1400 | - new_tab = self.open_new_tab(open_tabs_view=True, expand_view=True) |
1401 | - self._assert_bookmark_count_in_folder(new_tab, "", 6) |
1402 | + view = self.open_bookmarks() |
1403 | + self._assert_bookmark_count_in_folder(view, "", 6) |
1404 | |
1405 | - bookmark = new_tab.get_bookmarks("")[1] |
1406 | + bookmark = view.get_bookmarks("")[1] |
1407 | self.assertThat(bookmark.title, Equals("NewTitle")) |
1408 | |
1409 | def test_bookmark_options_from_contextual_menu(self): |
1410 | |
1411 | === modified file 'tests/autopilot/webbrowser_app/tests/test_new_tab_view.py' |
1412 | --- tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2015-10-15 19:09:59 +0000 |
1413 | +++ tests/autopilot/webbrowser_app/tests/test_new_tab_view.py 2015-12-02 14:15:47 +0000 |
1414 | @@ -251,12 +251,9 @@ |
1415 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1416 | self.assertThat(more_button.visible, Equals(True)) |
1417 | self.pointing_device.click_object(more_button) |
1418 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1419 | - folder_delegate = folders.get_folder_delegate("") |
1420 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1421 | - folder_delegate)), |
1422 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1423 | Eventually(Equals(5))) |
1424 | - bookmark = folders.get_urls_from_folder(folder_delegate)[0] |
1425 | + bookmark = self.new_tab_view.get_bookmarks("")[0] |
1426 | url = bookmark.url |
1427 | self.pointing_device.click_object(bookmark) |
1428 | self.new_tab_view.wait_until_destroyed() |
1429 | @@ -273,10 +270,7 @@ |
1430 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1431 | self.assertThat(more_button.visible, Equals(True)) |
1432 | self.pointing_device.click_object(more_button) |
1433 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1434 | - folder_delegate = folders.get_folder_delegate("") |
1435 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1436 | - folder_delegate)), |
1437 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1438 | Eventually(Equals(5))) |
1439 | self.assertThat(top_sites.visible, Eventually(Equals(False))) |
1440 | # Collapse again |
1441 | @@ -296,23 +290,20 @@ |
1442 | Eventually(NotEquals(url))) |
1443 | |
1444 | def _remove_first_bookmark_from_folder(self, folder): |
1445 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1446 | - folder_delegate = folders.get_folder_delegate(folder) |
1447 | - delegate = folders.get_urls_from_folder(folder_delegate)[0] |
1448 | + delegates = self.new_tab_view.get_bookmarks(folder) |
1449 | + delegate = delegates[0] |
1450 | url = delegate.url |
1451 | - count = len(folders.get_urls_from_folder(folder_delegate)) |
1452 | + count = len(delegates) |
1453 | delegate.trigger_leading_action("leadingAction.delete", |
1454 | delegate.wait_until_destroyed) |
1455 | if ((count - 1) > 4): |
1456 | self.assertThat( |
1457 | - lambda: folders.get_urls_from_folder(folder_delegate)[0], |
1458 | + lambda: self.new_tab_view.get_bookmarks(folder)[0].url, |
1459 | Eventually(NotEquals(url))) |
1460 | |
1461 | def _toggle_bookmark_folder(self, folder): |
1462 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1463 | - folder_delegate = folders.get_folder_delegate(folder) |
1464 | - self.pointing_device.click_object( |
1465 | - folders.get_header_from_folder(folder_delegate)) |
1466 | + delegate = self.new_tab_view.get_bookmark_folder(folder) |
1467 | + self.pointing_device.click_object(delegate) |
1468 | |
1469 | def test_remove_bookmarks_when_collapsed(self): |
1470 | bookmarks = self.new_tab_view.get_bookmarks_list() |
1471 | @@ -329,10 +320,7 @@ |
1472 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1473 | self.assertThat(more_button.visible, Equals(True)) |
1474 | self.pointing_device.click_object(more_button) |
1475 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1476 | - folder_delegate = folders.get_folder_delegate("") |
1477 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1478 | - folder_delegate)), |
1479 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1480 | Eventually(Equals(5))) |
1481 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1482 | top_sites = self.new_tab_view.get_top_sites_list() |
1483 | @@ -347,89 +335,67 @@ |
1484 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1485 | self.assertThat(more_button.visible, Equals(True)) |
1486 | self.pointing_device.click_object(more_button) |
1487 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1488 | - self.assertThat(lambda: len(folders.get_delegates()), |
1489 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()), |
1490 | Eventually(Equals(3))) |
1491 | - folder_delegate = folders.get_folder_delegate("") |
1492 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1493 | - folder_delegate)), |
1494 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1495 | Eventually(Equals(5))) |
1496 | self._toggle_bookmark_folder("Actinide") |
1497 | - folder_delegate = folders.get_folder_delegate("Actinide") |
1498 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1499 | - folder_delegate)), |
1500 | - Eventually(Equals(1))) |
1501 | + self.assertThat( |
1502 | + lambda: len(self.new_tab_view.get_bookmarks("Actinide")), |
1503 | + Eventually(Equals(1))) |
1504 | self._toggle_bookmark_folder("NobleGas") |
1505 | - folder_delegate = folders.get_folder_delegate("NobleGas") |
1506 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1507 | - folder_delegate)), |
1508 | - Eventually(Equals(1))) |
1509 | + self.assertThat( |
1510 | + lambda: len(self.new_tab_view.get_bookmarks("NobleGas")), |
1511 | + Eventually(Equals(1))) |
1512 | |
1513 | def test_collapsed_bookmarks_folders_when_expanded(self): |
1514 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1515 | self.assertThat(more_button.visible, Equals(True)) |
1516 | self.pointing_device.click_object(more_button) |
1517 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1518 | - self.assertThat(lambda: len(folders.get_delegates()), |
1519 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()), |
1520 | Eventually(Equals(3))) |
1521 | - folder_delegate = folders.get_folder_delegate("") |
1522 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1523 | - folder_delegate)), |
1524 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1525 | Eventually(Equals(5))) |
1526 | - folder_delegate = folders.get_folder_delegate("Actinide") |
1527 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1528 | - folder_delegate)), |
1529 | - Eventually(Equals(0))) |
1530 | - folder_delegate = folders.get_folder_delegate("NobleGas") |
1531 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1532 | - folder_delegate)), |
1533 | - Eventually(Equals(0))) |
1534 | + self.assertThat( |
1535 | + lambda: len(self.new_tab_view.get_bookmarks("Actinide")), |
1536 | + Eventually(Equals(0))) |
1537 | + self.assertThat( |
1538 | + lambda: len(self.new_tab_view.get_bookmarks("NobleGas")), |
1539 | + Eventually(Equals(0))) |
1540 | |
1541 | def test_hide_empty_bookmarks_folders_when_expanded(self): |
1542 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1543 | self.assertThat(more_button.visible, Equals(True)) |
1544 | self.pointing_device.click_object(more_button) |
1545 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1546 | - self.assertThat(lambda: len(folders.get_delegates()), |
1547 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()), |
1548 | Eventually(Equals(3))) |
1549 | self._toggle_bookmark_folder("Actinide") |
1550 | - folder_delegate = folders.get_folder_delegate("Actinide") |
1551 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1552 | - folder_delegate)), |
1553 | - Eventually(Equals(1))) |
1554 | + self.assertThat( |
1555 | + lambda: len(self.new_tab_view.get_bookmarks("Actinide")), |
1556 | + Eventually(Equals(1))) |
1557 | self._remove_first_bookmark_from_folder("Actinide") |
1558 | - self.assertThat(lambda: len(folders.get_delegates()), |
1559 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()), |
1560 | Eventually(Equals(2))) |
1561 | - folder_delegate = folders.get_folder_delegate("") |
1562 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1563 | - folder_delegate)), |
1564 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1565 | Eventually(Equals(5))) |
1566 | self._toggle_bookmark_folder("NobleGas") |
1567 | - folder_delegate = folders.get_folder_delegate("NobleGas") |
1568 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1569 | - folder_delegate)), |
1570 | - Eventually(Equals(1))) |
1571 | + self.assertThat( |
1572 | + lambda: len(self.new_tab_view.get_bookmarks("NobleGas")), |
1573 | + Eventually(Equals(1))) |
1574 | |
1575 | def test_bookmarks_folder_expands_and_collapses(self): |
1576 | more_button = self.new_tab_view.get_bookmarks_more_button() |
1577 | self.assertThat(more_button.visible, Equals(True)) |
1578 | self.pointing_device.click_object(more_button) |
1579 | - folders = self.new_tab_view.get_bookmarks_folder_list_view() |
1580 | - self.assertThat(lambda: len(folders.get_delegates()), |
1581 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmark_folders()), |
1582 | Eventually(Equals(3))) |
1583 | - folder_delegate = folders.get_folder_delegate("") |
1584 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1585 | - folder_delegate)), |
1586 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1587 | Eventually(Equals(5))) |
1588 | - self.pointing_device.click_object( |
1589 | - folders.get_header_from_folder(folder_delegate)) |
1590 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1591 | - folder_delegate)), |
1592 | + self._toggle_bookmark_folder("") |
1593 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1594 | Eventually(Equals(0))) |
1595 | - self.pointing_device.click_object( |
1596 | - folders.get_header_from_folder(folder_delegate)) |
1597 | - self.assertThat(lambda: len(folders.get_urls_from_folder( |
1598 | - folder_delegate)), |
1599 | + self._toggle_bookmark_folder("") |
1600 | + self.assertThat(lambda: len(self.new_tab_view.get_bookmarks("")), |
1601 | Eventually(Equals(5))) |
1602 | |
1603 | def test_remove_top_sites(self): |
1604 | @@ -475,10 +441,10 @@ |
1605 | |
1606 | def test_remove_top_sites(self): |
1607 | view = self.new_tab_view |
1608 | - topsites = view.get_top_site_items() |
1609 | + topsites = view.get_top_sites_list().get_delegates() |
1610 | previous_count = len(topsites) |
1611 | topsites[0].hide_from_history(self.main_window) |
1612 | - self.assertThat(len(view.get_top_site_items()), |
1613 | + self.assertThat(len(view.get_top_sites_list().get_delegates()), |
1614 | Equals(previous_count - 1)) |
1615 | |
1616 | def test_drag_bookmarks(self): |
1617 | |
1618 | === modified file 'tests/autopilot/webbrowser_app/tests/test_private.py' |
1619 | --- tests/autopilot/webbrowser_app/tests/test_private.py 2015-11-23 17:12:15 +0000 |
1620 | +++ tests/autopilot/webbrowser_app/tests/test_private.py 2015-12-02 14:15:47 +0000 |
1621 | @@ -68,7 +68,8 @@ |
1622 | |
1623 | def test_url_showing_in_top_sites_in_and_out_private_mode(self): |
1624 | new_tab = self.open_new_tab(open_tabs_view=True) |
1625 | - urls = [site.url for site in new_tab.get_top_site_items()] |
1626 | + top_sites = new_tab.get_top_sites_list().get_delegates() |
1627 | + urls = [site.url for site in top_sites] |
1628 | self.assertIn(self.url, urls) |
1629 | |
1630 | self.main_window.enter_private_mode() |
1631 | @@ -82,7 +83,8 @@ |
1632 | Eventually(Equals(False))) |
1633 | |
1634 | new_tab = self.open_new_tab(open_tabs_view=True) |
1635 | - urls = [site.url for site in new_tab.get_top_site_items()] |
1636 | + top_sites = new_tab.get_top_sites_list().get_delegates() |
1637 | + urls = [site.url for site in top_sites] |
1638 | self.assertNotIn(url, urls) |
1639 | |
1640 | def test_public_tabs_should_not_be_visible_in_private_mode(self): |
1641 | |
1642 | === modified file 'tests/autopilot/webbrowser_app/tests/test_site_previews.py' |
1643 | --- tests/autopilot/webbrowser_app/tests/test_site_previews.py 2015-11-19 11:48:10 +0000 |
1644 | +++ tests/autopilot/webbrowser_app/tests/test_site_previews.py 2015-12-02 14:15:47 +0000 |
1645 | @@ -123,7 +123,7 @@ |
1646 | return cap |
1647 | |
1648 | def remove_top_site(self, new_tab_view, url): |
1649 | - top_sites = new_tab_view.get_top_site_items() |
1650 | + top_sites = new_tab_view.get_top_sites_list().get_delegates() |
1651 | top_sites = [d for d in top_sites if d.url == url] |
1652 | self.assertThat(len(top_sites), Equals(1)) |
1653 | delegate = top_sites[0] |
1654 | |
1655 | === modified file 'tests/unittests/CMakeLists.txt' |
1656 | --- tests/unittests/CMakeLists.txt 2015-10-06 09:48:38 +0000 |
1657 | +++ tests/unittests/CMakeLists.txt 2015-12-02 14:15:47 +0000 |
1658 | @@ -14,6 +14,7 @@ |
1659 | add_subdirectory(bookmarks-model) |
1660 | add_subdirectory(bookmarks-folder-model) |
1661 | add_subdirectory(bookmarks-folderlist-model) |
1662 | +add_subdirectory(collapsible-bookmarks-model) |
1663 | add_subdirectory(limit-proxy-model) |
1664 | add_subdirectory(container-url-patterns) |
1665 | add_subdirectory(cookie-store) |
1666 | |
1667 | === added directory 'tests/unittests/collapsible-bookmarks-model' |
1668 | === added file 'tests/unittests/collapsible-bookmarks-model/CMakeLists.txt' |
1669 | --- tests/unittests/collapsible-bookmarks-model/CMakeLists.txt 1970-01-01 00:00:00 +0000 |
1670 | +++ tests/unittests/collapsible-bookmarks-model/CMakeLists.txt 2015-12-02 14:15:47 +0000 |
1671 | @@ -0,0 +1,13 @@ |
1672 | +find_package(Qt5Core REQUIRED) |
1673 | +find_package(Qt5Sql REQUIRED) |
1674 | +find_package(Qt5Test REQUIRED) |
1675 | +set(TEST tst_CollapsibleBookmarksModelTests) |
1676 | +add_executable(${TEST} tst_CollapsibleBookmarksModelTests.cpp) |
1677 | +include_directories(${webbrowser-app_SOURCE_DIR}) |
1678 | +target_link_libraries(${TEST} |
1679 | + Qt5::Core |
1680 | + Qt5::Sql |
1681 | + Qt5::Test |
1682 | + webbrowser-app-models |
1683 | +) |
1684 | +add_test(${TEST} ${CMAKE_CURRENT_BINARY_DIR}/${TEST} -xunitxml -o ${TEST}.xml) |
1685 | |
1686 | === added file 'tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp' |
1687 | --- tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp 1970-01-01 00:00:00 +0000 |
1688 | +++ tests/unittests/collapsible-bookmarks-model/tst_CollapsibleBookmarksModelTests.cpp 2015-12-02 14:15:47 +0000 |
1689 | @@ -0,0 +1,645 @@ |
1690 | +/* |
1691 | + * Copyright 2015 Canonical Ltd. |
1692 | + * |
1693 | + * This file is part of webbrowser-app. |
1694 | + * |
1695 | + * webbrowser-app is free software; you can redistribute it and/or modify |
1696 | + * it under the terms of the GNU General Public License as published by |
1697 | + * the Free Software Foundation; version 3. |
1698 | + * |
1699 | + * webbrowser-app is distributed in the hope that it will be useful, |
1700 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1701 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1702 | + * GNU General Public License for more details. |
1703 | + * |
1704 | + * You should have received a copy of the GNU General Public License |
1705 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1706 | + */ |
1707 | + |
1708 | +// Qt |
1709 | +#include <QtCore/QMetaType> |
1710 | +#include <QtCore/QVariant> |
1711 | +#include <QtTest/QSignalSpy> |
1712 | +#include <QtTest/QtTest> |
1713 | + |
1714 | +// local |
1715 | +#include "collapsible-bookmarks-model.h" |
1716 | + |
1717 | +class CollapsibleBookmarksModelTests : public QObject |
1718 | +{ |
1719 | + Q_OBJECT |
1720 | + |
1721 | +private: |
1722 | + BookmarksModel* bookmarks; |
1723 | + CollapsibleBookmarksModel* model; |
1724 | + |
1725 | +private Q_SLOTS: |
1726 | + void init() |
1727 | + { |
1728 | + bookmarks = new BookmarksModel; |
1729 | + bookmarks->setDatabasePath(":memory:"); |
1730 | + model = new CollapsibleBookmarksModel; |
1731 | + model->setSourceModel(bookmarks); |
1732 | + } |
1733 | + |
1734 | + void populateBookmarksModel() |
1735 | + { |
1736 | + bookmarks->add(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("url1"), |
1737 | + QUrl(QStringLiteral("http://test/icon1")), QStringLiteral("")); |
1738 | + QTest::qWait(1); |
1739 | + bookmarks->add(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), |
1740 | + QUrl(QStringLiteral("http://test/icon2")), QStringLiteral("folder1")); |
1741 | + QTest::qWait(1); |
1742 | + bookmarks->add(QUrl(QStringLiteral("http://test/url3")), QStringLiteral("url3"), |
1743 | + QUrl(QStringLiteral("http://test/icon3")), QStringLiteral("folder2")); |
1744 | + QTest::qWait(1); |
1745 | + bookmarks->addFolder(QStringLiteral("folder3")); |
1746 | + bookmarks->add(QUrl(QStringLiteral("http://test/url4")), QStringLiteral("url4"), |
1747 | + QUrl(QStringLiteral("http://test/icon4")), QStringLiteral("folder4")); |
1748 | + } |
1749 | + |
1750 | + void cleanup() |
1751 | + { |
1752 | + delete model; |
1753 | + delete bookmarks; |
1754 | + } |
1755 | + |
1756 | + void test_default_values() |
1757 | + { |
1758 | + QCOMPARE(model->prependHomepage(), true); |
1759 | + QCOMPARE(model->showEmptyFolders(), false); |
1760 | + } |
1761 | + |
1762 | + void test_initial_contents_data() |
1763 | + { |
1764 | + QTest::addColumn<bool>("prependHomepage"); |
1765 | + QTest::addColumn<bool>("showEmptyFolders"); |
1766 | + QTest::addColumn<int>("count"); |
1767 | + QTest::newRow("no/no") << false << false << 0; |
1768 | + QTest::newRow("no/yes") << false << true << 1; |
1769 | + QTest::newRow("yes/no") << true << false << 2; |
1770 | + QTest::newRow("yes/yes") << true << true << 2; |
1771 | + } |
1772 | + |
1773 | + void test_initial_contents() |
1774 | + { |
1775 | + QFETCH(bool, prependHomepage); |
1776 | + QFETCH(bool, showEmptyFolders); |
1777 | + QFETCH(int, count); |
1778 | + model->setPrependHomepage(prependHomepage); |
1779 | + model->setShowEmptyFolders(showEmptyFolders); |
1780 | + QCOMPARE(model->rowCount(), count); |
1781 | + } |
1782 | + |
1783 | + void test_roleNames() |
1784 | + { |
1785 | + QList<QByteArray> roleNames = model->roleNames().values(); |
1786 | + QCOMPARE(roleNames.size(), 6); |
1787 | + QVERIFY(roleNames.contains("url")); |
1788 | + QVERIFY(roleNames.contains("title")); |
1789 | + QVERIFY(roleNames.contains("icon")); |
1790 | + QVERIFY(roleNames.contains("created")); |
1791 | + QVERIFY(roleNames.contains("folder")); |
1792 | + QVERIFY(roleNames.contains("type")); |
1793 | + } |
1794 | + |
1795 | + void test_setSourceModel() |
1796 | + { |
1797 | + QSignalSpy spy(model, SIGNAL(sourceModelChanged())); |
1798 | + QCOMPARE(model->sourceModel(), bookmarks); |
1799 | + |
1800 | + model->setSourceModel(bookmarks); |
1801 | + QVERIFY(spy.isEmpty()); |
1802 | + QCOMPARE(model->sourceModel(), bookmarks); |
1803 | + |
1804 | + model->setSourceModel(nullptr); |
1805 | + QCOMPARE(spy.count(), 1); |
1806 | + QCOMPARE(model->sourceModel(), (BookmarksModel*) nullptr); |
1807 | + |
1808 | + model->setSourceModel(bookmarks); |
1809 | + QCOMPARE(spy.count(), 2); |
1810 | + QCOMPARE(model->sourceModel(), bookmarks); |
1811 | + } |
1812 | + |
1813 | + void test_allBookmarks_initially_expanded() |
1814 | + { |
1815 | + populateBookmarksModel(); |
1816 | + model->setPrependHomepage(true); |
1817 | + model->setShowEmptyFolders(true); |
1818 | + QStringList expectedTypes; |
1819 | + expectedTypes << QStringLiteral("expandedFolder") << QStringLiteral("homepage") |
1820 | + << QStringLiteral("bookmark") << QStringLiteral("collapsedFolder") |
1821 | + << QStringLiteral("collapsedFolder") << QStringLiteral("emptyFolder") |
1822 | + << QStringLiteral("collapsedFolder"); |
1823 | + int count = expectedTypes.count(); |
1824 | + QCOMPARE(model->rowCount(), count); |
1825 | + for (int i = 0; i < count; ++i) { |
1826 | + QCOMPARE(model->data(model->index(i), CollapsibleBookmarksModel::Type).toString(), expectedTypes.at(i)); |
1827 | + } |
1828 | + } |
1829 | + |
1830 | + void test_modelData_data() { |
1831 | + QTest::addColumn<bool>("showEmptyFolders"); |
1832 | + QTest::addColumn<int>("index"); |
1833 | + QTest::addColumn<int>("role"); |
1834 | + QTest::addColumn<QVariant>("value"); |
1835 | + |
1836 | + int url = CollapsibleBookmarksModel::Url; |
1837 | + int title = CollapsibleBookmarksModel::Title; |
1838 | + int icon = CollapsibleBookmarksModel::Icon; |
1839 | + int created = CollapsibleBookmarksModel::Created; |
1840 | + int folder = CollapsibleBookmarksModel::Folder; |
1841 | + int type = CollapsibleBookmarksModel::Type; |
1842 | + |
1843 | + QTest::newRow("invalid index") << true << -1 << type << QVariant(); |
1844 | + QTest::newRow("invalid index 2") << true << 10 << type << QVariant(); |
1845 | + QTest::newRow("invalid role") << true << 0 << 25 << QVariant(); |
1846 | + |
1847 | + QTest::newRow("expanded folder url") << true << 0 << url << QVariant(); |
1848 | + QTest::newRow("expanded folder title") << true << 0 << title << QVariant(); |
1849 | + QTest::newRow("expanded folder icon") << true << 0 << icon << QVariant(); |
1850 | + QTest::newRow("expanded folder created") << true << 0 << created << QVariant(); |
1851 | + QTest::newRow("expanded folder name") << true << 0 << folder << QVariant(QStringLiteral("")); |
1852 | + QTest::newRow("expanded folder type") << true << 0 << type << QVariant(QStringLiteral("expandedFolder")); |
1853 | + |
1854 | + QTest::newRow("homepage url") << true << 1 << url << QVariant(); |
1855 | + QTest::newRow("homepage title") << true << 1 << title << QVariant(); |
1856 | + QTest::newRow("homepage icon") << true << 1 << icon << QVariant(); |
1857 | + QTest::newRow("homepage created") << true << 1 << created << QVariant(); |
1858 | + QTest::newRow("homepage folder") << true << 1 << folder << QVariant(QStringLiteral("")); |
1859 | + QTest::newRow("homepage type") << true << 1 << type << QVariant(QStringLiteral("homepage")); |
1860 | + |
1861 | + QTest::newRow("bookmark url") << true << 2 << url << QVariant(QUrl("http://test/url1")); |
1862 | + QTest::newRow("bookmark title") << true << 2 << title << QVariant(QStringLiteral("url1")); |
1863 | + QTest::newRow("bookmark icon") << true << 2 << icon << QVariant(QUrl("http://test/icon1")); |
1864 | + QTest::newRow("bookmark folder") << true << 2 << folder << QVariant(QStringLiteral("")); |
1865 | + QTest::newRow("bookmark type") << true << 2 << type << QVariant(QStringLiteral("bookmark")); |
1866 | + |
1867 | + QTest::newRow("collapsed folder url") << true << 3 << url << QVariant(); |
1868 | + QTest::newRow("collapsed folder title") << true << 3 << title << QVariant(); |
1869 | + QTest::newRow("collapsed folder icon") << true << 3 << icon << QVariant(); |
1870 | + QTest::newRow("collapsed folder created") << true << 3 << created << QVariant(); |
1871 | + QTest::newRow("collapsed folder name") << true << 3 << folder << QVariant(QStringLiteral("folder1")); |
1872 | + QTest::newRow("collapsed folder type") << true << 3 << type << QVariant(QStringLiteral("collapsedFolder")); |
1873 | + |
1874 | + QTest::newRow("empty folder url") << true << 5 << url << QVariant(); |
1875 | + QTest::newRow("empty folder title") << true << 5 << title << QVariant(); |
1876 | + QTest::newRow("empty folder icon") << true << 5 << icon << QVariant(); |
1877 | + QTest::newRow("empty folder created") << true << 5 << created << QVariant(); |
1878 | + QTest::newRow("empty folder name") << true << 5 << folder << QVariant(QStringLiteral("folder3")); |
1879 | + QTest::newRow("empty folder type") << true << 5 << type << QVariant(QStringLiteral("emptyFolder")); |
1880 | + |
1881 | + QTest::newRow("another collapsed folder name") << false << 5 << folder << QVariant(QStringLiteral("folder4")); |
1882 | + QTest::newRow("another collapsed folder type") << false << 5 << type << QVariant(QStringLiteral("collapsedFolder")); |
1883 | + } |
1884 | + |
1885 | + void test_modelData() { |
1886 | + QFETCH(bool, showEmptyFolders); |
1887 | + QFETCH(int, index); |
1888 | + QFETCH(int, role); |
1889 | + QFETCH(QVariant, value); |
1890 | + |
1891 | + populateBookmarksModel(); |
1892 | + model->setPrependHomepage(true); |
1893 | + model->setShowEmptyFolders(showEmptyFolders); |
1894 | + |
1895 | + QVariant actualValue = model->data(model->index(index), role); |
1896 | + if (!value.isValid()) { |
1897 | + QVERIFY(!actualValue.isValid()); |
1898 | + } else { |
1899 | + QCOMPARE(actualValue, value); |
1900 | + } |
1901 | + } |
1902 | + |
1903 | + void test_initialModelPopulation() { |
1904 | + populateBookmarksModel(); |
1905 | + model->setPrependHomepage(true); |
1906 | + model->setShowEmptyFolders(false); |
1907 | + model->setSourceModel(nullptr); |
1908 | + |
1909 | + QSignalSpy spy(model, SIGNAL(modelReset())); |
1910 | + model->setSourceModel(bookmarks); |
1911 | + QCOMPARE(spy.count(), 1); |
1912 | + QCOMPARE(model->rowCount(), 6); |
1913 | + } |
1914 | + |
1915 | + void test_expandAndCollapseFolders() { |
1916 | + populateBookmarksModel(); |
1917 | + model->setPrependHomepage(true); |
1918 | + model->setShowEmptyFolders(true); |
1919 | + |
1920 | + qRegisterMetaType<QVector<int>>(); |
1921 | + QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
1922 | + QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
1923 | + QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&))); |
1924 | + QCOMPARE(model->rowCount(), 7); |
1925 | + |
1926 | + // collapse the "All Bookmarks" folder |
1927 | + QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder")); |
1928 | + model->toggleFolderState(QStringLiteral("")); |
1929 | + QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder")); |
1930 | + QCOMPARE(model->rowCount(), 5); |
1931 | + QVERIFY(insertionSpy.isEmpty()); |
1932 | + QCOMPARE(removalSpy.count(), 1); |
1933 | + QCOMPARE(dataSpy.count(), 1); |
1934 | + QVariantList args = removalSpy.takeFirst(); |
1935 | + QCOMPARE(args.at(1).toInt(), 1); |
1936 | + QCOMPARE(args.at(2).toInt(), 2); |
1937 | + args = dataSpy.takeFirst(); |
1938 | + QCOMPARE(args.at(0).toModelIndex().row(), 0); |
1939 | + QCOMPARE(args.at(1).toModelIndex().row(), 0); |
1940 | + QVector<int> dataChanged = args.at(2).value<QVector<int>>(); |
1941 | + QCOMPARE(dataChanged.size(), 1); |
1942 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
1943 | + |
1944 | + // expand the "All Bookmarks" folder |
1945 | + model->toggleFolderState(QStringLiteral("")); |
1946 | + QCOMPARE(model->data(model->index(0), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder")); |
1947 | + QCOMPARE(model->rowCount(), 7); |
1948 | + QVERIFY(removalSpy.isEmpty()); |
1949 | + QCOMPARE(insertionSpy.count(), 1); |
1950 | + QCOMPARE(dataSpy.count(), 1); |
1951 | + args = insertionSpy.takeFirst(); |
1952 | + QCOMPARE(args.at(1).toInt(), 1); |
1953 | + QCOMPARE(args.at(2).toInt(), 2); |
1954 | + args = dataSpy.takeFirst(); |
1955 | + QCOMPARE(args.at(0).toModelIndex().row(), 0); |
1956 | + QCOMPARE(args.at(1).toModelIndex().row(), 0); |
1957 | + dataChanged = args.at(2).value<QVector<int>>(); |
1958 | + QCOMPARE(dataChanged.size(), 1); |
1959 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
1960 | + |
1961 | + // expand another folder |
1962 | + QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder")); |
1963 | + model->toggleFolderState(QStringLiteral("folder1")); |
1964 | + QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("expandedFolder")); |
1965 | + QCOMPARE(model->rowCount(), 8); |
1966 | + QVERIFY(removalSpy.isEmpty()); |
1967 | + QCOMPARE(insertionSpy.count(), 1); |
1968 | + QCOMPARE(dataSpy.count(), 1); |
1969 | + args = insertionSpy.takeFirst(); |
1970 | + QCOMPARE(args.at(1).toInt(), 4); |
1971 | + QCOMPARE(args.at(2).toInt(), 4); |
1972 | + args = dataSpy.takeFirst(); |
1973 | + QCOMPARE(args.at(0).toModelIndex().row(), 3); |
1974 | + QCOMPARE(args.at(1).toModelIndex().row(), 3); |
1975 | + dataChanged = args.at(2).value<QVector<int>>(); |
1976 | + QCOMPARE(dataChanged.size(), 1); |
1977 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
1978 | + |
1979 | + // toggle the state of an empty folder: nothing should happen |
1980 | + QCOMPARE(model->data(model->index(6), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder")); |
1981 | + model->toggleFolderState(QStringLiteral("folder3")); |
1982 | + QCOMPARE(model->data(model->index(6), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder")); |
1983 | + QCOMPARE(model->rowCount(), 8); |
1984 | + QVERIFY(removalSpy.isEmpty()); |
1985 | + QVERIFY(insertionSpy.isEmpty()); |
1986 | + QVERIFY(dataSpy.isEmpty()); |
1987 | + } |
1988 | + |
1989 | + void test_setShowEmptyFolders() |
1990 | + { |
1991 | + populateBookmarksModel(); |
1992 | + model->setPrependHomepage(true); |
1993 | + model->setShowEmptyFolders(true); |
1994 | + |
1995 | + QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
1996 | + QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
1997 | + QCOMPARE(model->rowCount(), 7); |
1998 | + QVERIFY(model->showEmptyFolders()); |
1999 | + |
2000 | + // hide empty folders, "folder3" gets hidden |
2001 | + model->setShowEmptyFolders(false); |
2002 | + QVERIFY(!model->showEmptyFolders()); |
2003 | + QCOMPARE(model->rowCount(), 6); |
2004 | + QVERIFY(insertionSpy.isEmpty()); |
2005 | + QCOMPARE(removalSpy.count(), 1); |
2006 | + QVariantList args = removalSpy.takeFirst(); |
2007 | + QCOMPARE(args.at(1).toInt(), 5); |
2008 | + QCOMPARE(args.at(2).toInt(), 5); |
2009 | + |
2010 | + // add a new empty folder, it should remain hidden |
2011 | + bookmarks->addFolder(QStringLiteral("folder5")); |
2012 | + QCOMPARE(model->rowCount(), 6); |
2013 | + QVERIFY(insertionSpy.isEmpty()); |
2014 | + QVERIFY(removalSpy.isEmpty()); |
2015 | + |
2016 | + // show empty folders, "folder3" and "folder5" should appear |
2017 | + model->setShowEmptyFolders(true); |
2018 | + QVERIFY(model->showEmptyFolders()); |
2019 | + QCOMPARE(model->rowCount(), 8); |
2020 | + QVERIFY(removalSpy.isEmpty()); |
2021 | + QCOMPARE(insertionSpy.count(), 2); |
2022 | + args = insertionSpy.takeFirst(); |
2023 | + QCOMPARE(args.at(1).toInt(), 5); |
2024 | + QCOMPARE(args.at(2).toInt(), 5); |
2025 | + args = insertionSpy.takeFirst(); |
2026 | + QCOMPARE(args.at(1).toInt(), 7); |
2027 | + QCOMPARE(args.at(2).toInt(), 7); |
2028 | + |
2029 | + // add a new empty folder, it should appear |
2030 | + bookmarks->addFolder(QStringLiteral("a new folder")); |
2031 | + QCOMPARE(model->rowCount(), 9); |
2032 | + QVERIFY(removalSpy.isEmpty()); |
2033 | + QCOMPARE(insertionSpy.count(), 1); |
2034 | + args = insertionSpy.takeFirst(); |
2035 | + QCOMPARE(args.at(1).toInt(), 3); |
2036 | + QCOMPARE(args.at(2).toInt(), 3); |
2037 | + } |
2038 | + |
2039 | + void test_setPrependHomepage() |
2040 | + { |
2041 | + model->setPrependHomepage(true); |
2042 | + model->setShowEmptyFolders(true); |
2043 | + |
2044 | + QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
2045 | + QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
2046 | + QCOMPARE(model->rowCount(), 2); |
2047 | + QVERIFY(model->prependHomepage()); |
2048 | + |
2049 | + // do not prepend the homepage bookmark, the "All Bookmarks" folder |
2050 | + // should remain visible though (because showEmptyFolders is true) |
2051 | + model->setPrependHomepage(false); |
2052 | + QVERIFY(!model->prependHomepage()); |
2053 | + QCOMPARE(model->rowCount(), 1); |
2054 | + QVERIFY(insertionSpy.isEmpty()); |
2055 | + QCOMPARE(removalSpy.count(), 1); |
2056 | + QVariantList args = removalSpy.takeFirst(); |
2057 | + QCOMPARE(args.at(1).toInt(), 1); |
2058 | + QCOMPARE(args.at(2).toInt(), 1); |
2059 | + |
2060 | + // hide empty folders, the model should become empty |
2061 | + model->setShowEmptyFolders(false); |
2062 | + QVERIFY(!model->showEmptyFolders()); |
2063 | + QCOMPARE(model->rowCount(), 0); |
2064 | + QVERIFY(insertionSpy.isEmpty()); |
2065 | + QCOMPARE(removalSpy.count(), 1); |
2066 | + args = removalSpy.takeFirst(); |
2067 | + QCOMPARE(args.at(1).toInt(), 0); |
2068 | + QCOMPARE(args.at(2).toInt(), 0); |
2069 | + |
2070 | + // add bookmark to "All Bookmarks" folder, which should become |
2071 | + // visible again (but collapsed) |
2072 | + bookmarks->add(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("url1"), |
2073 | + QUrl(QStringLiteral("http://test/icon1")), QStringLiteral("")); |
2074 | + QCOMPARE(model->rowCount(), 1); |
2075 | + QVERIFY(removalSpy.isEmpty()); |
2076 | + QCOMPARE(insertionSpy.count(), 1); |
2077 | + args = insertionSpy.takeFirst(); |
2078 | + QCOMPARE(args.at(1).toInt(), 0); |
2079 | + QCOMPARE(args.at(2).toInt(), 0); |
2080 | + |
2081 | + // expand the "All Bookmarks" folder |
2082 | + model->toggleFolderState(QStringLiteral("")); |
2083 | + QCOMPARE(model->rowCount(), 2); |
2084 | + QVERIFY(removalSpy.isEmpty()); |
2085 | + QCOMPARE(insertionSpy.count(), 1); |
2086 | + args = insertionSpy.takeFirst(); |
2087 | + QCOMPARE(args.at(1).toInt(), 1); |
2088 | + QCOMPARE(args.at(2).toInt(), 1); |
2089 | + |
2090 | + // prepend the homepage bookmark, it should be inserted again |
2091 | + model->setPrependHomepage(true); |
2092 | + QVERIFY(model->prependHomepage()); |
2093 | + QCOMPARE(model->rowCount(), 3); |
2094 | + QVERIFY(removalSpy.isEmpty()); |
2095 | + QCOMPARE(insertionSpy.count(), 1); |
2096 | + args = insertionSpy.takeFirst(); |
2097 | + QCOMPARE(args.at(1).toInt(), 1); |
2098 | + QCOMPARE(args.at(2).toInt(), 1); |
2099 | + } |
2100 | + |
2101 | + void test_removingBookmarks() |
2102 | + { |
2103 | + populateBookmarksModel(); |
2104 | + model->setPrependHomepage(true); |
2105 | + model->setShowEmptyFolders(true); |
2106 | + |
2107 | + qRegisterMetaType<QVector<int>>(); |
2108 | + QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
2109 | + QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
2110 | + QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&))); |
2111 | + QCOMPARE(model->rowCount(), 7); |
2112 | + |
2113 | + // remove last bookmark from "folder2", which becomes empty |
2114 | + bookmarks->remove(QUrl(QStringLiteral("http://test/url3"))); |
2115 | + QCOMPARE(model->rowCount(), 7); |
2116 | + QVERIFY(removalSpy.isEmpty()); |
2117 | + QVERIFY(insertionSpy.isEmpty()); |
2118 | + QCOMPARE(dataSpy.count(), 1); |
2119 | + QVariantList args = dataSpy.takeFirst(); |
2120 | + QCOMPARE(args.at(0).toModelIndex().row(), 4); |
2121 | + QCOMPARE(args.at(1).toModelIndex().row(), 4); |
2122 | + QVector<int> dataChanged = args.at(2).value<QVector<int>>(); |
2123 | + QCOMPARE(dataChanged.size(), 1); |
2124 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
2125 | + |
2126 | + // expand "folder1" |
2127 | + model->toggleFolderState(QStringLiteral("folder1")); |
2128 | + QCOMPARE(model->rowCount(), 8); |
2129 | + QVERIFY(removalSpy.isEmpty()); |
2130 | + QCOMPARE(insertionSpy.count(), 1); |
2131 | + QCOMPARE(dataSpy.count(), 1); |
2132 | + args = insertionSpy.takeFirst(); |
2133 | + QCOMPARE(args.at(1).toInt(), 4); |
2134 | + QCOMPARE(args.at(2).toInt(), 4); |
2135 | + args = dataSpy.takeFirst(); |
2136 | + QCOMPARE(args.at(0).toModelIndex().row(), 3); |
2137 | + QCOMPARE(args.at(1).toModelIndex().row(), 3); |
2138 | + dataChanged = args.at(2).value<QVector<int>>(); |
2139 | + QCOMPARE(dataChanged.size(), 1); |
2140 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
2141 | + |
2142 | + // remove last bookmark from "folder1" |
2143 | + bookmarks->remove(QUrl(QStringLiteral("http://test/url2"))); |
2144 | + QCOMPARE(model->rowCount(), 7); |
2145 | + QVERIFY(insertionSpy.isEmpty()); |
2146 | + QCOMPARE(removalSpy.count(), 1); |
2147 | + QCOMPARE(dataSpy.count(), 1); |
2148 | + args = removalSpy.takeFirst(); |
2149 | + QCOMPARE(args.at(1).toInt(), 4); |
2150 | + QCOMPARE(args.at(2).toInt(), 4); |
2151 | + args = dataSpy.takeFirst(); |
2152 | + QCOMPARE(args.at(0).toModelIndex().row(), 3); |
2153 | + QCOMPARE(args.at(1).toModelIndex().row(), 3); |
2154 | + dataChanged = args.at(2).value<QVector<int>>(); |
2155 | + QCOMPARE(dataChanged.size(), 1); |
2156 | + QVERIFY(dataChanged.contains((int) CollapsibleBookmarksModel::Type)); |
2157 | + |
2158 | + // add a bookmark to "folder1" |
2159 | + bookmarks->add(QUrl(QStringLiteral("http://test/url5")), QStringLiteral("url5"), |
2160 | + QUrl(QStringLiteral("http://test/icon5")), QStringLiteral("folder1")); |
2161 | + QCOMPARE(model->rowCount(), 7); |
2162 | + |
2163 | + // and expand it |
2164 | + model->toggleFolderState(QStringLiteral("folder1")); |
2165 | + QCOMPARE(model->rowCount(), 8); |
2166 | + |
2167 | + // hide empty folders |
2168 | + model->setShowEmptyFolders(false); |
2169 | + QCOMPARE(model->rowCount(), 6); |
2170 | + |
2171 | + // remove last bookmark from "folder1", which should be hidden |
2172 | + insertionSpy.clear(); |
2173 | + removalSpy.clear(); |
2174 | + dataSpy.clear(); |
2175 | + bookmarks->remove(QUrl(QStringLiteral("http://test/url5"))); |
2176 | + QCOMPARE(model->rowCount(), 4); |
2177 | + QVERIFY(insertionSpy.isEmpty()); |
2178 | + QCOMPARE(removalSpy.count(), 1); |
2179 | + QVERIFY(dataSpy.isEmpty()); |
2180 | + args = removalSpy.takeFirst(); |
2181 | + QCOMPARE(args.at(1).toInt(), 3); |
2182 | + QCOMPARE(args.at(2).toInt(), 4); |
2183 | + } |
2184 | + |
2185 | + void test_updatingBookmarks() |
2186 | + { |
2187 | + populateBookmarksModel(); |
2188 | + model->setPrependHomepage(true); |
2189 | + model->setShowEmptyFolders(true); |
2190 | + |
2191 | + qRegisterMetaType<QVector<int>>(); |
2192 | + QSignalSpy removalSpy(model, SIGNAL(rowsRemoved(const QModelIndex&, int, int))); |
2193 | + QSignalSpy insertionSpy(model, SIGNAL(rowsInserted(const QModelIndex&, int, int))); |
2194 | + QSignalSpy movedSpy(model, SIGNAL(rowsMoved(const QModelIndex&, int, int, const QModelIndex&, int))); |
2195 | + QSignalSpy dataSpy(model, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&))); |
2196 | + QCOMPARE(model->rowCount(), 7); |
2197 | + |
2198 | + // update a bookmark in a collapsed folder, nothing should happen |
2199 | + bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("new title 2"), QStringLiteral("folder1")); |
2200 | + QVERIFY(removalSpy.isEmpty()); |
2201 | + QVERIFY(insertionSpy.isEmpty()); |
2202 | + QVERIFY(movedSpy.isEmpty()); |
2203 | + QVERIFY(dataSpy.isEmpty()); |
2204 | + |
2205 | + // update a bookmark in an expanded folder |
2206 | + bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("")); |
2207 | + QVERIFY(removalSpy.isEmpty()); |
2208 | + QVERIFY(insertionSpy.isEmpty()); |
2209 | + QVERIFY(movedSpy.isEmpty()); |
2210 | + QCOMPARE(dataSpy.count(), 1); |
2211 | + QVariantList args = dataSpy.takeFirst(); |
2212 | + QCOMPARE(args.at(0).toModelIndex().row(), 2); |
2213 | + QCOMPARE(args.at(1).toModelIndex().row(), 2); |
2214 | + QVERIFY(args.at(2).value<QVector<int>>().isEmpty()); |
2215 | + QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Title).toString(), QStringLiteral("new title 1")); |
2216 | + |
2217 | + // move a bookmark from an expanded folder to a collapsed folder |
2218 | + bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("folder1")); |
2219 | + QCOMPARE(removalSpy.count(), 1); |
2220 | + QVERIFY(insertionSpy.isEmpty()); |
2221 | + QVERIFY(movedSpy.isEmpty()); |
2222 | + QVERIFY(dataSpy.isEmpty()); |
2223 | + args = removalSpy.takeFirst(); |
2224 | + QCOMPARE(args.at(1).toInt(), 2); |
2225 | + QCOMPARE(args.at(2).toInt(), 2); |
2226 | + |
2227 | + // move a bookmark from a collapsed folder to an expanded folder |
2228 | + bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("")); |
2229 | + QVERIFY(removalSpy.isEmpty()); |
2230 | + QCOMPARE(insertionSpy.count(), 1); |
2231 | + QVERIFY(movedSpy.isEmpty()); |
2232 | + QVERIFY(dataSpy.isEmpty()); |
2233 | + args = insertionSpy.takeFirst(); |
2234 | + QCOMPARE(args.at(1).toInt(), 2); |
2235 | + QCOMPARE(args.at(2).toInt(), 2); |
2236 | + |
2237 | + // move a bookmark from a collapsed folder to another collapsed folder, |
2238 | + // "folder1" becomes empty |
2239 | + bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("folder2")); |
2240 | + QVERIFY(removalSpy.isEmpty()); |
2241 | + QVERIFY(insertionSpy.isEmpty()); |
2242 | + QVERIFY(movedSpy.isEmpty()); |
2243 | + QCOMPARE(dataSpy.count(), 1); |
2244 | + args = dataSpy.takeFirst(); |
2245 | + QCOMPARE(args.at(0).toModelIndex().row(), 3); |
2246 | + QCOMPARE(args.at(1).toModelIndex().row(), 3); |
2247 | + QVector<int> roles = args.at(2).value<QVector<int>>(); |
2248 | + QCOMPARE(roles.count(), 1); |
2249 | + QVERIFY(roles.contains(CollapsibleBookmarksModel::Type)); |
2250 | + QCOMPARE(model->data(model->index(3), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("emptyFolder")); |
2251 | + |
2252 | + // expand "folder2" |
2253 | + model->toggleFolderState(QStringLiteral("folder2")); |
2254 | + insertionSpy.clear(); |
2255 | + dataSpy.clear(); |
2256 | + |
2257 | + // move a bookmark from an expanded folder to another expanded folder |
2258 | + bookmarks->update(QUrl(QStringLiteral("http://test/url1")), QStringLiteral("new title 1"), QStringLiteral("folder2")); |
2259 | + QVERIFY(removalSpy.isEmpty()); |
2260 | + QVERIFY(insertionSpy.isEmpty()); |
2261 | + QCOMPARE(movedSpy.count(), 1); |
2262 | + QCOMPARE(dataSpy.count(), 1); |
2263 | + args = movedSpy.takeFirst(); |
2264 | + QCOMPARE(args.at(1).toInt(), 2); |
2265 | + QCOMPARE(args.at(2).toInt(), 2); |
2266 | + QCOMPARE(args.at(4).toInt(), 7); |
2267 | + args = dataSpy.takeFirst(); |
2268 | + QCOMPARE(args.at(0).toModelIndex().row(), 6); |
2269 | + QCOMPARE(args.at(1).toModelIndex().row(), 6); |
2270 | + QVERIFY(args.at(2).value<QVector<int>>().isEmpty()); |
2271 | + |
2272 | + // move a bookmark from an expanded folder to a new folder |
2273 | + bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("a new folder")); |
2274 | + QCOMPARE(insertionSpy.count(), 1); |
2275 | + QCOMPARE(removalSpy.count(), 1); |
2276 | + QVERIFY(movedSpy.isEmpty()); |
2277 | + QVERIFY(dataSpy.isEmpty()); |
2278 | + args = insertionSpy.takeFirst(); |
2279 | + QCOMPARE(args.at(1).toInt(), 2); |
2280 | + QCOMPARE(args.at(2).toInt(), 2); |
2281 | + QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Type).toString(), QStringLiteral("collapsedFolder")); |
2282 | + QCOMPARE(model->data(model->index(2), CollapsibleBookmarksModel::Folder).toString(), QStringLiteral("a new folder")); |
2283 | + args = removalSpy.takeFirst(); |
2284 | + QCOMPARE(args.at(1).toInt(), 6); |
2285 | + QCOMPARE(args.at(2).toInt(), 6); |
2286 | + |
2287 | + // expand "a new folder" |
2288 | + model->toggleFolderState(QStringLiteral("a new folder")); |
2289 | + insertionSpy.clear(); |
2290 | + dataSpy.clear(); |
2291 | + |
2292 | + // do not show empty folders |
2293 | + model->setShowEmptyFolders(false); |
2294 | + removalSpy.clear(); |
2295 | + |
2296 | + // move the last bookmark from a folder to another folder, |
2297 | + // the source folder becomes empty and so should be hidden |
2298 | + bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("folder2")); |
2299 | + QVERIFY(insertionSpy.isEmpty()); |
2300 | + QCOMPARE(removalSpy.count(), 1); |
2301 | + QCOMPARE(movedSpy.count(), 1); |
2302 | + QCOMPARE(dataSpy.count(), 1); |
2303 | + args = movedSpy.takeFirst(); |
2304 | + QCOMPARE(args.at(1).toInt(), 3); |
2305 | + QCOMPARE(args.at(2).toInt(), 3); |
2306 | + QCOMPARE(args.at(4).toInt(), 6); |
2307 | + args = dataSpy.takeFirst(); |
2308 | + QCOMPARE(args.at(0).toModelIndex().row(), 5); |
2309 | + QCOMPARE(args.at(1).toModelIndex().row(), 5); |
2310 | + QVERIFY(args.at(2).value<QVector<int>>().isEmpty()); |
2311 | + args = removalSpy.takeFirst(); |
2312 | + QCOMPARE(args.at(1).toInt(), 2); |
2313 | + QCOMPARE(args.at(2).toInt(), 2); |
2314 | + |
2315 | + // move a bookmark from an expanded folder to another expanded folder |
2316 | + // that is positioned above in the model |
2317 | + bookmarks->update(QUrl(QStringLiteral("http://test/url2")), QStringLiteral("url2"), QStringLiteral("")); |
2318 | + QVERIFY(insertionSpy.isEmpty()); |
2319 | + QVERIFY(removalSpy.isEmpty()); |
2320 | + QCOMPARE(movedSpy.count(), 1); |
2321 | + QCOMPARE(dataSpy.count(), 1); |
2322 | + args = movedSpy.takeFirst(); |
2323 | + QCOMPARE(args.at(1).toInt(), 4); |
2324 | + QCOMPARE(args.at(2).toInt(), 4); |
2325 | + QCOMPARE(args.at(4).toInt(), 2); |
2326 | + args = dataSpy.takeFirst(); |
2327 | + QCOMPARE(args.at(0).toModelIndex().row(), 2); |
2328 | + QCOMPARE(args.at(1).toModelIndex().row(), 2); |
2329 | + QVERIFY(args.at(2).value<QVector<int>>().isEmpty()); |
2330 | + } |
2331 | +}; |
2332 | + |
2333 | +QTEST_MAIN(CollapsibleBookmarksModelTests) |
2334 | +#include "tst_CollapsibleBookmarksModelTests.moc" |
2335 | |
2336 | === modified file 'tests/unittests/qml/CMakeLists.txt' |
2337 | --- tests/unittests/qml/CMakeLists.txt 2015-10-06 09:48:38 +0000 |
2338 | +++ tests/unittests/qml/CMakeLists.txt 2015-12-02 14:15:47 +0000 |
2339 | @@ -20,6 +20,7 @@ |
2340 | ${webbrowser-app_SOURCE_DIR}/bookmarks-model.cpp |
2341 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folder-model.cpp |
2342 | ${webbrowser-app_SOURCE_DIR}/bookmarks-folderlist-model.cpp |
2343 | + ${webbrowser-app_SOURCE_DIR}/collapsible-bookmarks-model.cpp |
2344 | ${webbrowser-app_SOURCE_DIR}/file-operations.cpp |
2345 | ${webbrowser-app_SOURCE_DIR}/history-model.cpp |
2346 | ${webbrowser-app_SOURCE_DIR}/history-lastvisitdate-model.cpp |
2347 | |
2348 | === modified file 'tests/unittests/qml/tst_BookmarksView.qml' |
2349 | --- tests/unittests/qml/tst_BookmarksView.qml 2015-11-17 17:19:13 +0000 |
2350 | +++ tests/unittests/qml/tst_BookmarksView.qml 2015-12-02 14:15:47 +0000 |
2351 | @@ -92,22 +92,21 @@ |
2352 | |
2353 | function test_click_bookmark() { |
2354 | var items = getListItems(findChild(view, "bookmarksFolderListView"), |
2355 | - "bookmarkFolderDelegateLoader") |
2356 | + "bookmarkViewDelegateLoader") |
2357 | |
2358 | - clickItem(findChild(items[0], "urlDelegate_0")) |
2359 | + clickItem(items[1]) |
2360 | compare(bookmarkEntryClickedSpy.count, 1) |
2361 | compare(bookmarkEntryClickedSpy.signalArguments[0][0], view.homepageUrl) |
2362 | |
2363 | - clickItem(findChild(items[0], "urlDelegate_1")) |
2364 | + clickItem(items[2]) |
2365 | compare(bookmarkEntryClickedSpy.count, 2) |
2366 | compare(bookmarkEntryClickedSpy.signalArguments[1][0], "http://example.com") |
2367 | } |
2368 | |
2369 | function test_delete_bookmark() { |
2370 | var items = getListItems(findChild(view, "bookmarksFolderListView"), |
2371 | - "bookmarkFolderDelegateLoader") |
2372 | - var bookmark = findChild(items[0], "urlDelegate_1") |
2373 | - swipeToDeleteAndConfirm(bookmark) |
2374 | + "bookmarkViewDelegateLoader") |
2375 | + swipeToDeleteAndConfirm(items[2]) |
2376 | tryCompare(BookmarksModel, "count", 2) |
2377 | } |
2378 | } |
2379 | |
2380 | === modified file 'tests/unittests/qml/tst_QmlTests.cpp' |
2381 | --- tests/unittests/qml/tst_QmlTests.cpp 2015-10-22 15:07:26 +0000 |
2382 | +++ tests/unittests/qml/tst_QmlTests.cpp 2015-12-02 14:15:47 +0000 |
2383 | @@ -27,6 +27,7 @@ |
2384 | // local |
2385 | #include "bookmarks-model.h" |
2386 | #include "bookmarks-folderlist-model.h" |
2387 | +#include "collapsible-bookmarks-model.h" |
2388 | #include "favicon-fetcher.h" |
2389 | #include "file-operations.h" |
2390 | #include "history-model.h" |
2391 | @@ -192,6 +193,7 @@ |
2392 | qmlRegisterType<TabsModel>(browserUri, 0, 1, "TabsModel"); |
2393 | qmlRegisterSingletonType<BookmarksModel>(browserUri, 0, 1, "BookmarksModel", BookmarksModel_singleton_factory); |
2394 | qmlRegisterType<BookmarksFolderListModel>(browserUri, 0, 1, "BookmarksFolderListModel"); |
2395 | + qmlRegisterType<CollapsibleBookmarksModel>(browserUri, 0, 1, "CollapsibleBookmarksModel"); |
2396 | qmlRegisterSingletonType<HistoryModel>(browserUri, 0, 1, "HistoryModel", HistoryModel_singleton_factory); |
2397 | qmlRegisterType<HistoryTimeframeModel>(browserUri, 0, 1, "HistoryTimeframeModel"); |
2398 | qmlRegisterType<HistoryLastVisitDateListModel>(browserUri, 0, 1, "HistoryLastVisitDateListModel"); |
PASSED: Continuous integration, rev:1308 jenkins. qa.ubuntu. com/job/ webbrowser- app-ci/ 2498/ jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- vivid-touch/ 5551 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- amd64-ci/ 1252 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 1252 jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- armhf-ci/ 1252/artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ webbrowser- app-vivid- i386-ci/ 1252 jenkins. qa.ubuntu. com/job/ generic- deb-autopilot- runner- vivid-touch/ 4347 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 5565 jenkins. qa.ubuntu. com/job/ generic- mediumtests- builder- vivid-armhf/ 5565/artifact/ work/output/ *zip*/output. zip s-jenkins. ubuntu- ci:8080/ job/touch- flash-device/ 25719
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: s-jenkins. ubuntu- ci:8080/ job/webbrowser- app-ci/ 2498/rebuild
http://