1 /***************************************************************************
2 * Copyright (C) 2008-2012 by Peter Penz <peter.penz19@gmail.com> *
4 * Based on KFilePlacesModel from kdelibs: *
5 * Copyright (C) 2007 Kevin Ottens <ervin@kde.org> *
6 * Copyright (C) 2007 David Faure <faure@kde.org> *
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. *
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. *
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 ***************************************************************************/
24 #include "placespanel.h"
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>
37 #include <KBookmarkGroup>
38 #include <KBookmarkManager>
39 #include <KComponentData>
43 #include <kitemviews/kitemlistcontainer.h>
44 #include <kitemviews/kitemlistcontroller.h>
45 #include <kitemviews/kstandarditem.h>
46 #include <kitemviews/kstandarditemlistview.h>
47 #include <kitemviews/kstandarditemmodel.h>
48 #include <KStandardDirs>
50 #include "placesitemlistgroupheader.h"
51 #include <views/draganddrophelper.h>
53 #include <QVBoxLayout>
56 PlacesPanel::PlacesPanel(QWidget
* parent
) :
58 m_nepomukRunning(false),
64 m_defaultBookmarksIndexes()
68 PlacesPanel::~PlacesPanel()
72 bool PlacesPanel::urlChanged()
77 void PlacesPanel::showEvent(QShowEvent
* event
)
79 if (event
->spontaneous()) {
80 Panel::showEvent(event
);
85 // Postpone the creating of the controller to the first show event.
86 // This assures that no performance and memory overhead is given when the folders panel is not
87 // used at all and stays invisible.
89 m_nepomukRunning
= (Nepomuk::ResourceManager::instance()->initialized());
91 createDefaultBookmarks();
93 const QString file
= KStandardDirs::locateLocal("data", "kfileplaces/bookmarks.xml");
94 m_bookmarkManager
= KBookmarkManager::managerForFile(file
, "kfilePlaces");
95 m_model
= new KStandardItemModel(this);
96 m_model
->setGroupedSorting(true);
97 m_model
->setSortRole("group");
100 KStandardItemListView
* view
= new KStandardItemListView();
101 view
->setGroupHeaderCreator(new KItemListGroupHeaderCreator
<PlacesItemListGroupHeader
>());
103 m_controller
= new KItemListController(m_model
, view
, this);
104 m_controller
->setSelectionBehavior(KItemListController::SingleSelection
);
105 connect(m_controller
, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int)));
106 connect(m_controller
, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
107 connect(m_controller
, SIGNAL(itemContextMenuRequested(int,QPointF
)), this, SLOT(slotItemContextMenuRequested(int,QPointF
)));
108 connect(m_controller
, SIGNAL(viewContextMenuRequested(QPointF
)), this, SLOT(slotViewContextMenuRequested(QPointF
)));
110 KItemListContainer
* container
= new KItemListContainer(m_controller
, this);
111 container
->setEnabledFrame(false);
113 QVBoxLayout
* layout
= new QVBoxLayout(this);
114 layout
->setMargin(0);
115 layout
->addWidget(container
);
118 Panel::showEvent(event
);
121 void PlacesPanel::slotItemActivated(int index
)
123 const KUrl url
= urlForIndex(index
);
124 if (!url
.isEmpty()) {
125 emit
placeActivated(url
);
129 void PlacesPanel::slotItemMiddleClicked(int index
)
131 const KUrl url
= urlForIndex(index
);
132 if (!url
.isEmpty()) {
133 emit
placeMiddleClicked(url
);
137 void PlacesPanel::slotItemContextMenuRequested(int index
, const QPointF
& pos
)
143 void PlacesPanel::slotViewContextMenuRequested(const QPointF
& pos
)
148 void PlacesPanel::slotUrlsDropped(const KUrl
& dest
, QDropEvent
* event
, QWidget
* parent
)
151 DragAndDropHelper::dropUrls(KFileItem(), dest
, event
);
154 void PlacesPanel::createDefaultBookmarks()
156 Q_ASSERT(m_defaultBookmarks
.isEmpty());
157 Q_ASSERT(m_defaultBookmarksIndexes
.isEmpty());
159 const QString placesGroup
= i18nc("@item", "Places");
160 const QString recentlyAccessedGroup
= i18nc("@item", "Recently Accessed");
161 const QString searchForGroup
= i18nc("@item", "Search For");
162 const QString timeLineIcon
= "package_utility_time"; // TODO: Ask the Oxygen team to create
163 // a custom icon for the timeline-protocol
165 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl(KUser().homeDir()),
167 i18nc("@item", "Home"),
169 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("remote:/"),
171 i18nc("@item", "Network"),
173 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("/"),
175 i18nc("@item", "Root"),
177 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("trash:/"),
179 i18nc("@item", "Trash"),
182 if (m_nepomukRunning
) {
183 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("timeline:/today"),
185 i18nc("@item Recently Accessed", "Today"),
186 recentlyAccessedGroup
));
187 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("timeline:/yesterday"),
189 i18nc("@item Recently Accessed", "Yesterday"),
190 recentlyAccessedGroup
));
191 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("timeline:/thismonth"),
193 i18nc("@item Recently Accessed", "This Month"),
194 recentlyAccessedGroup
));
195 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("timeline:/lastmonth"),
197 i18nc("@item Recently Accessed", "Last Month"),
198 recentlyAccessedGroup
));
199 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("search:/documents"),
201 i18nc("@item Commonly Accessed", "Documents"),
203 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("search:/images"),
205 i18nc("@item Commonly Accessed", "Images"),
207 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("search:/music"),
209 i18nc("@item Commonly Accessed", "Music"),
211 m_defaultBookmarks
.append(DefaultBookmarkData(KUrl("search:/videos"),
213 i18nc("@item Commonly Accessed", "Videos"),
217 for (int i
= 0; i
< m_defaultBookmarks
.count(); ++i
) {
218 m_defaultBookmarksIndexes
.insert(m_defaultBookmarks
[i
].url
, i
);
222 void PlacesPanel::loadBookmarks()
224 KBookmarkGroup root
= m_bookmarkManager
->root();
225 KBookmark bookmark
= root
.first();
226 QSet
<QString
> devices
= m_availableDevices
;
228 QSet
<KUrl
> missingDefaultBookmarks
;
229 foreach (const DefaultBookmarkData
& data
, m_defaultBookmarks
) {
230 missingDefaultBookmarks
.insert(data
.url
);
233 while (!bookmark
.isNull()) {
234 const QString udi
= bookmark
.metaDataItem("UDI");
235 const KUrl url
= bookmark
.url();
236 const QString appName
= bookmark
.metaDataItem("OnlyInApp");
237 const bool deviceAvailable
= devices
.remove(udi
);
239 const bool allowedHere
= (appName
.isEmpty() || appName
== KGlobal::mainComponent().componentName())
240 && (m_nepomukRunning
|| url
.protocol() != QLatin1String("timeline"));
242 if ((udi
.isEmpty() && allowedHere
) || deviceAvailable
) {
243 KStandardItem
* item
= new KStandardItem();
244 item
->setIcon(KIcon(bookmark
.icon()));
245 item
->setDataValue("address", bookmark
.address());
246 item
->setDataValue("url", url
);
248 if (missingDefaultBookmarks
.contains(url
)) {
249 missingDefaultBookmarks
.remove(url
);
250 // Always apply the translated text to the default bookmarks, otherwise an outdated
251 // translation might be shown.
252 const int index
= m_defaultBookmarksIndexes
.value(url
);
253 item
->setText(m_defaultBookmarks
[index
].text
);
255 item
->setText(bookmark
.text());
258 if (deviceAvailable
) {
259 item
->setDataValue("udi", udi
);
260 item
->setGroup(i18nc("@item", "Devices"));
262 item
->setGroup(i18nc("@item", "Places"));
265 m_model
->appendItem(item
);
268 bookmark
= root
.next(bookmark
);
271 if (!missingDefaultBookmarks
.isEmpty()) {
272 foreach (const DefaultBookmarkData
& data
, m_defaultBookmarks
) {
273 if (missingDefaultBookmarks
.contains(data
.url
)) {
274 KStandardItem
* item
= new KStandardItem();
275 item
->setIcon(KIcon(data
.icon
));
276 item
->setText(data
.text
);
277 item
->setDataValue("url", data
.url
);
278 item
->setGroup(data
.group
);
279 m_model
->appendItem(item
);
285 KUrl
PlacesPanel::urlForIndex(int index
) const
287 const KStandardItem
* item
= m_model
->item(index
);
292 KUrl url
= item
->dataValue("url").value
<KUrl
>();
293 if (url
.protocol() == QLatin1String("timeline")) {
294 url
= createTimelineUrl(url
);
295 } else if (url
.protocol() == QLatin1String("search")) {
296 url
= createSearchUrl(url
);
302 KUrl
PlacesPanel::createTimelineUrl(const KUrl
& url
)
304 // TODO: Clarify with the Nepomuk-team whether it makes sense
305 // provide default-timeline-URLs like 'yesterday', 'this month'
309 const QString path
= url
.pathOrUrl();
310 if (path
.endsWith("yesterday")) {
311 const QDate date
= QDate::currentDate().addDays(-1);
312 const int year
= date
.year();
313 const int month
= date
.month();
314 const int day
= date
.day();
315 timelineUrl
= "timeline:/" + timelineDateString(year
, month
) +
316 '/' + timelineDateString(year
, month
, day
);
317 } else if (path
.endsWith("thismonth")) {
318 const QDate date
= QDate::currentDate();
319 timelineUrl
= "timeline:/" + timelineDateString(date
.year(), date
.month());
320 } else if (path
.endsWith("lastmonth")) {
321 const QDate date
= QDate::currentDate().addMonths(-1);
322 timelineUrl
= "timeline:/" + timelineDateString(date
.year(), date
.month());
324 Q_ASSERT(path
.endsWith("today"));
331 QString
PlacesPanel::timelineDateString(int year
, int month
, int day
)
333 QString date
= QString::number(year
) + '-';
337 date
+= QString::number(month
);
344 date
+= QString::number(day
);
350 KUrl
PlacesPanel::createSearchUrl(const KUrl
& url
)
355 const QString path
= url
.pathOrUrl();
356 if (path
.endsWith("documents")) {
357 searchUrl
= searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Document()));
358 } else if (path
.endsWith("images")) {
359 searchUrl
= searchUrlForTerm(Nepomuk::Query::ResourceTypeTerm(Nepomuk::Vocabulary::NFO::Image()));
360 } else if (path
.endsWith("music")) {
361 searchUrl
= searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
362 Nepomuk::Query::LiteralTerm("audio")));
363 } else if (path
.endsWith("videos")) {
364 searchUrl
= searchUrlForTerm(Nepomuk::Query::ComparisonTerm(Nepomuk::Vocabulary::NIE::mimeType(),
365 Nepomuk::Query::LiteralTerm("video")));
375 KUrl
PlacesPanel::searchUrlForTerm(const Nepomuk::Query::Term
& term
)
377 const Nepomuk::Query::Query
query(term
);
378 return query
.toSearchUrl();
382 #include "placespanel.moc"