]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placesitemmodel.cpp
Places Panel: Fix implementation issues when hiding items
[dolphin.git] / src / panels / places / placesitemmodel.cpp
1 /***************************************************************************
2 * Copyright (C) 2012 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * Based on KFilePlacesModel from kdelibs: *
5 * Copyright (C) 2007 Kevin Ottens <ervin@kde.org> *
6 * Copyright (C) 2007 David Faure <faure@kde.org> *
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 * This program is distributed in the hope that it will be useful, *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU General Public License *
19 * along with this program; if not, write to the *
20 * Free Software Foundation, Inc., *
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
22 ***************************************************************************/
23
24 #include "placesitemmodel.h"
25
26 #ifdef HAVE_NEPOMUK
27 #include <Nepomuk/ResourceManager>
28 #include <Nepomuk/Query/ComparisonTerm>
29 #include <Nepomuk/Query/LiteralTerm>
30 #include <Nepomuk/Query/Query>
31 #include <Nepomuk/Query/ResourceTypeTerm>
32 #include <Nepomuk/Vocabulary/NFO>
33 #include <Nepomuk/Vocabulary/NIE>
34 #endif
35
36 #include <KBookmark>
37 #include <KBookmarkGroup>
38 #include <KBookmarkManager>
39 #include <KComponentData>
40 #include <KDebug>
41 #include <KIcon>
42 #include <kitemviews/kstandarditem.h>
43 #include <KLocale>
44 #include <KStandardDirs>
45 #include <KUser>
46 #include <QDate>
47
48 PlacesItemModel::PlacesItemModel(QObject* parent) :
49 KStandardItemModel(parent),
50 m_nepomukRunning(false),
51 m_hiddenItemsShown(false),
52 m_availableDevices(),
53 m_bookmarkManager(0),
54 m_systemBookmarks(),
55 m_systemBookmarksIndexes(),
56 m_hiddenItems()
57 {
58 #ifdef HAVE_NEPOMUK
59 m_nepomukRunning = (Nepomuk::ResourceManager::instance()->initialized());
60 #endif
61 const QString file = KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
62 m_bookmarkManager = KBookmarkManager::managerForFile(file, "kfilePlaces");
63
64 createSystemBookmarks();
65 loadBookmarks();
66 }
67
68 PlacesItemModel::~PlacesItemModel()
69 {
70 qDeleteAll(m_hiddenItems);
71 m_hiddenItems.clear();
72 }
73
74 int PlacesItemModel::hiddenCount() const
75 {
76 int itemCount = 0;
77 foreach (const KStandardItem* item, m_hiddenItems) {
78 if (item) {
79 ++itemCount;
80 }
81 }
82
83 return itemCount;
84 }
85
86 void PlacesItemModel::setItemHidden(int index, bool hide)
87 {
88 if (index >= 0 && index < count()) {
89 KStandardItem* shownItem = this->item(index);
90 shownItem->setDataValue("isHidden", hide);
91 if (!m_hiddenItemsShown && hide) {
92 const int newIndex = hiddenIndex(index);
93 KStandardItem* hiddenItem = new KStandardItem(*shownItem);
94 removeItem(index);
95 m_hiddenItems.insert(newIndex, hiddenItem);
96 }
97 #ifdef PLACESITEMMODEL_DEBUG
98 kDebug() << "Changed hide-state from" << index << "to" << hide;
99 showModelState();
100 #endif
101 }
102 }
103
104 bool PlacesItemModel::isItemHidden(int index) const
105 {
106 return (index >= 0 && index < count()) ? m_hiddenItems[index] != 0 : false;
107 }
108
109 void PlacesItemModel::setHiddenItemsShown(bool show)
110 {
111 if (m_hiddenItemsShown != show) {
112 m_hiddenItemsShown = show;
113 }
114 }
115
116 bool PlacesItemModel::hiddenItemsShown() const
117 {
118 return m_hiddenItemsShown;
119 }
120
121 bool PlacesItemModel::isSystemItem(int index) const
122 {
123 if (index >= 0 && index < count()) {
124 const KUrl url = data(index).value("url").value<KUrl>();
125 return m_systemBookmarksIndexes.contains(url);
126 }
127 return false;
128 }
129
130 int PlacesItemModel::closestItem(const KUrl& url) const
131 {
132 int foundIndex = -1;
133 int maxLength = 0;
134
135 for (int i = 0; i < count(); ++i) {
136 const KUrl itemUrl = data(i).value("url").value<KUrl>();
137 if (itemUrl.isParentOf(url)) {
138 const int length = itemUrl.prettyUrl().length();
139 if (length > maxLength) {
140 foundIndex = i;
141 maxLength = length;
142 }
143 }
144 }
145
146 return foundIndex;
147 }
148
149 QString PlacesItemModel::groupName(const KUrl &url) const
150 {
151 const QString protocol = url.protocol();
152
153 if (protocol.contains(QLatin1String("search"))) {
154 return searchForGroupName();
155 }
156
157 if (protocol == QLatin1String("timeline")) {
158 return recentlyAccessedGroupName();
159 }
160
161 return placesGroupName();
162 }
163
164 QAction* PlacesItemModel::ejectAction(int index) const
165 {
166 Q_UNUSED(index);
167 return 0;
168 }
169
170 QAction* PlacesItemModel::tearDownAction(int index) const
171 {
172 Q_UNUSED(index);
173 return 0;
174 }
175
176 void PlacesItemModel::onItemInserted(int index)
177 {
178 if (index == count() - 1) {
179 m_hiddenItems.append(0);
180 } else {
181 m_hiddenItems.insert(hiddenIndex(index), 0);
182 }
183 }
184
185 void PlacesItemModel::onItemRemoved(int index)
186 {
187 const int removeIndex = hiddenIndex(index);
188 Q_ASSERT(!m_hiddenItems[removeIndex]);
189 m_hiddenItems.removeAt(removeIndex);
190 #ifdef PLACESITEMMODEL_DEBUG
191 kDebug() << "Removed item" << index;
192 showModelState();
193 #endif
194 }
195
196 void PlacesItemModel::loadBookmarks()
197 {
198 KBookmarkGroup root = m_bookmarkManager->root();
199 KBookmark bookmark = root.first();
200 QSet<QString> devices = m_availableDevices;
201
202 QSet<KUrl> missingSystemBookmarks;
203 foreach (const SystemBookmarkData& data, m_systemBookmarks) {
204 missingSystemBookmarks.insert(data.url);
205 }
206
207 while (!bookmark.isNull()) {
208 const QString udi = bookmark.metaDataItem("UDI");
209 const KUrl url = bookmark.url();
210 const QString appName = bookmark.metaDataItem("OnlyInApp");
211 const bool deviceAvailable = devices.remove(udi);
212
213 const bool allowedHere = (appName.isEmpty() || appName == KGlobal::mainComponent().componentName())
214 && (m_nepomukRunning || url.protocol() != QLatin1String("timeline"));
215
216 if ((udi.isEmpty() && allowedHere) || deviceAvailable) {
217 KStandardItem* item = new KStandardItem();
218 item->setIcon(KIcon(bookmark.icon()));
219 item->setDataValue("address", bookmark.address());
220 item->setDataValue("url", url);
221
222 if (missingSystemBookmarks.contains(url)) {
223 missingSystemBookmarks.remove(url);
224 // Apply the translated text to the system bookmarks, otherwise an outdated
225 // translation might be shown.
226 const int index = m_systemBookmarksIndexes.value(url);
227 item->setText(m_systemBookmarks[index].text);
228
229 // The system bookmarks don't contain "real" queries stored as URLs, so
230 // they must be translated first.
231 item->setDataValue("url", translatedSystemBookmarkUrl(url));
232 } else {
233 item->setText(bookmark.text());
234 }
235
236 if (deviceAvailable) {
237 item->setDataValue("udi", udi);
238 item->setGroup(i18nc("@item", "Devices"));
239 } else {
240 item->setGroup(i18nc("@item", "Places"));
241 }
242
243 if (bookmark.metaDataItem("IsHidden") == QLatin1String("true")) {
244 m_hiddenItems.append(item);
245 } else {
246 appendItem(item);
247 }
248 }
249
250 bookmark = root.next(bookmark);
251 }
252
253 if (!missingSystemBookmarks.isEmpty()) {
254 foreach (const SystemBookmarkData& data, m_systemBookmarks) {
255 if (missingSystemBookmarks.contains(data.url)) {
256 KStandardItem* item = new KStandardItem();
257 item->setIcon(KIcon(data.icon));
258 item->setText(data.text);
259 item->setDataValue("url", translatedSystemBookmarkUrl(data.url));
260 item->setGroup(data.group);
261 appendItem(item);
262 }
263 }
264 }
265
266 #ifdef PLACESITEMMODEL_DEBUG
267 kDebug() << "Loaded bookmarks";
268 showModelState();
269 #endif
270 }
271
272 void PlacesItemModel::createSystemBookmarks()
273 {
274 Q_ASSERT(m_systemBookmarks.isEmpty());
275 Q_ASSERT(m_systemBookmarksIndexes.isEmpty());
276
277 const QString placesGroup = placesGroupName();
278 const QString recentlyAccessedGroup = recentlyAccessedGroupName();
279 const QString searchForGroup = searchForGroupName();
280 const QString timeLineIcon = "package_utility_time"; // TODO: Ask the Oxygen team to create
281 // a custom icon for the timeline-protocol
282
283 m_systemBookmarks.append(SystemBookmarkData(KUrl(KUser().homeDir()),
284 "user-home",
285 i18nc("@item", "Home"),
286 placesGroup));
287 m_systemBookmarks.append(SystemBookmarkData(KUrl("remote:/"),
288 "network-workgroup",
289 i18nc("@item", "Network"),
290 placesGroup));
291 m_systemBookmarks.append(SystemBookmarkData(KUrl("/"),
292 "folder-red",
293 i18nc("@item", "Root"),
294 placesGroup));
295 m_systemBookmarks.append(SystemBookmarkData(KUrl("trash:/"),
296 "user-trash",
297 i18nc("@item", "Trash"),
298 placesGroup));
299
300 if (m_nepomukRunning) {
301 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/today"),
302 timeLineIcon,
303 i18nc("@item Recently Accessed", "Today"),
304 recentlyAccessedGroup));
305 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/yesterday"),
306 timeLineIcon,
307 i18nc("@item Recently Accessed", "Yesterday"),
308 recentlyAccessedGroup));
309 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/thismonth"),
310 timeLineIcon,
311 i18nc("@item Recently Accessed", "This Month"),
312 recentlyAccessedGroup));
313 m_systemBookmarks.append(SystemBookmarkData(KUrl("timeline:/lastmonth"),
314 timeLineIcon,
315 i18nc("@item Recently Accessed", "Last Month"),
316 recentlyAccessedGroup));
317 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/documents"),
318 "folder-txt",
319 i18nc("@item Commonly Accessed", "Documents"),
320 searchForGroup));
321 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/images"),
322 "folder-image",
323 i18nc("@item Commonly Accessed", "Images"),
324 searchForGroup));
325 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/audio"),
326 "folder-sound",
327 i18nc("@item Commonly Accessed", "Audio"),
328 searchForGroup));
329 m_systemBookmarks.append(SystemBookmarkData(KUrl("search:/videos"),
330 "folder-video",
331 i18nc("@item Commonly Accessed", "Videos"),
332 searchForGroup));
333 }
334
335 for (int i = 0; i < m_systemBookmarks.count(); ++i) {
336 const KUrl url = translatedSystemBookmarkUrl(m_systemBookmarks[i].url);
337 m_systemBookmarksIndexes.insert(url, i);
338 }
339 }
340
341 int PlacesItemModel::hiddenIndex(int index) const
342 {
343 int hiddenIndex = 0;
344 int visibleItemIndex = 0;
345 while (hiddenIndex < m_hiddenItems.count()) {
346 if (!m_hiddenItems[hiddenIndex]) {
347 if (visibleItemIndex == index) {
348 break;
349 }
350 ++visibleItemIndex;
351 }
352 ++hiddenIndex;
353 }
354
355 return hiddenIndex >= m_hiddenItems.count() ? -1 : hiddenIndex;
356 }
357
358 QString PlacesItemModel::placesGroupName()
359 {
360 return i18nc("@item", "Places");
361 }
362
363 QString PlacesItemModel::recentlyAccessedGroupName()
364 {
365 return i18nc("@item", "Recently Accessed");
366 }
367
368 QString PlacesItemModel::searchForGroupName()
369 {
370 return i18nc("@item", "Search For");
371 }
372
373 KUrl PlacesItemModel::translatedSystemBookmarkUrl(const KUrl& url)
374 {
375 KUrl translatedUrl = url;
376 if (url.protocol() == QLatin1String("timeline")) {
377 translatedUrl = createTimelineUrl(url);
378 } else if (url.protocol() == QLatin1String("search")) {
379 translatedUrl = createSearchUrl(url);
380 }
381
382 return translatedUrl;
383 }
384
385 KUrl PlacesItemModel::createTimelineUrl(const KUrl& url)
386 {
387 // TODO: Clarify with the Nepomuk-team whether it makes sense
388 // provide default-timeline-URLs like 'yesterday', 'this month'
389 // and 'last month'.
390 KUrl timelineUrl;
391
392 const QString path = url.pathOrUrl();
393 if (path.endsWith("yesterday")) {
394 const QDate date = QDate::currentDate().addDays(-1);
395 const int year = date.year();
396 const int month = date.month();
397 const int day = date.day();
398 timelineUrl = "timeline:/" + timelineDateString(year, month) +
399 '/' + timelineDateString(year, month, day);
400 } else if (path.endsWith("thismonth")) {
401 const QDate date = QDate::currentDate();
402 timelineUrl = "timeline:/" + timelineDateString(date.year(), date.month());
403 } else if (path.endsWith("lastmonth")) {
404 const QDate date = QDate::currentDate().addMonths(-1);
405 timelineUrl = "timeline:/" + timelineDateString(date.year(), date.month());
406 } else {
407 Q_ASSERT(path.endsWith("today"));
408 timelineUrl= url;
409 }
410
411 return timelineUrl;
412 }
413
414 QString PlacesItemModel::timelineDateString(int year, int month, int day)
415 {
416 QString date = QString::number(year) + '-';
417 if (month < 10) {
418 date += '0';
419 }
420 date += QString::number(month);
421
422 if (day >= 1) {
423 date += '-';
424 if (day < 10) {
425 date += '0';
426 }
427 date += QString::number(day);
428 }
429
430 return date;
431 }
432
433 KUrl PlacesItemModel::createSearchUrl(const KUrl& url)
434 {
435 KUrl searchUrl;
436
437 #ifdef HAVE_NEPOMUK
438 const QString path = url.pathOrUrl();
439 if (path.endsWith("documents")) {
440 searchUrl = searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Document()));
441 } else if (path.endsWith("images")) {
442 searchUrl = searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Image()));
443 } else if (path.endsWith("audio")) {
444 searchUrl = searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
445 Nepomuk::Query::LiteralTerm("audio")));
446 } else if (path.endsWith("videos")) {
447 searchUrl = searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
448 Nepomuk::Query::LiteralTerm("video")));
449 } else {
450 Q_ASSERT(false);
451 }
452 #else
453 Q_UNUSED(url);
454 #endif
455
456 return searchUrl;
457 }
458
459 #ifdef HAVE_NEPOMUK
460 KUrl PlacesItemModel::searchUrlForTerm(const Nepomuk::Query::Term& term)
461 {
462 const Nepomuk::Query::Query query(term);
463 return query.toSearchUrl();
464 }
465 #endif
466
467 #ifdef PLACESITEMMODEL_DEBUG
468 void PlacesItemModel::showModelState()
469 {
470 kDebug() << "hidden-index model-index text";
471 int j = 0;
472 for (int i = 0; i < m_hiddenItems.count(); ++i) {
473 if (m_hiddenItems[i]) {
474 kDebug() << i << "(Hidden) " << " " << m_hiddenItems[i]->dataValue("text").toString();
475 } else {
476 kDebug() << i << " " << j << " " << item(j)->dataValue("text").toString();
477 ++j;
478 }
479 }
480 }
481 #endif
482
483 #include "placesitemmodel.moc"