]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placespanel.cpp
Select the correct item in places-panel, when a directory is opened.
[dolphin.git] / src / panels / places / placespanel.cpp
1 /***************************************************************************
2 * Copyright (C) 2008-2012 by Peter Penz <peter.penz19@gmail.com> *
3 * *
4 * Based on KFilePlacesView 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 "placespanel.h"
25
26 #include <KDebug>
27 #include <KDirNotify>
28 #include <KIcon>
29 #include <KIO/Job>
30 #include <KIO/JobUiDelegate>
31 #include <KLocale>
32 #include <kitemviews/kitemlistcontainer.h>
33 #include <kitemviews/kitemlistcontroller.h>
34 #include <kitemviews/kitemlistselectionmanager.h>
35 #include <kitemviews/kstandarditem.h>
36 #include <kitemviews/kstandarditemlistview.h>
37 #include <KMenu>
38 #include <KMessageBox>
39 #include <KNotification>
40 #include "placesitem.h"
41 #include "placesitemeditdialog.h"
42 #include "placesitemlistgroupheader.h"
43 #include "placesitemlistwidget.h"
44 #include "placesitemmodel.h"
45 #include <views/draganddrophelper.h>
46 #include <QGraphicsSceneDragDropEvent>
47 #include <QVBoxLayout>
48 #include <QShowEvent>
49
50 PlacesPanel::PlacesPanel(QWidget* parent) :
51 Panel(parent),
52 m_controller(0),
53 m_model(0),
54 m_storageSetupFailedUrl(),
55 m_triggerStorageSetupButton()
56 {
57 }
58
59 PlacesPanel::~PlacesPanel()
60 {
61 }
62
63 bool PlacesPanel::urlChanged()
64 {
65 if (!url().isValid() || url().protocol().contains("search")) {
66 // Skip results shown by a search, as possible identical
67 // directory names are useless without parent-path information.
68 return false;
69 }
70
71 if (m_controller) {
72 selectClosestItem();
73 }
74
75 return true;
76 }
77
78 void PlacesPanel::showEvent(QShowEvent* event)
79 {
80 if (event->spontaneous()) {
81 Panel::showEvent(event);
82 return;
83 }
84
85 if (!m_controller) {
86 // Postpone the creating of the controller to the first show event.
87 // This assures that no performance and memory overhead is given when the folders panel is not
88 // used at all and stays invisible.
89 m_model = new PlacesItemModel(this);
90 m_model->setGroupedSorting(true);
91 connect(m_model, SIGNAL(errorMessage(QString)),
92 this, SIGNAL(errorMessage(QString)));
93
94 KStandardItemListView* view = new KStandardItemListView();
95 view->setWidgetCreator(new KItemListWidgetCreator<PlacesItemListWidget>());
96 view->setGroupHeaderCreator(new KItemListGroupHeaderCreator<PlacesItemListGroupHeader>());
97
98 m_controller = new KItemListController(m_model, view, this);
99 m_controller->setSelectionBehavior(KItemListController::SingleSelection);
100 m_controller->setSingleClickActivation(true);
101 connect(m_controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int)));
102 connect(m_controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
103 connect(m_controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF)));
104 connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF)));
105 connect(m_controller, SIGNAL(itemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotItemDropEvent(int,QGraphicsSceneDragDropEvent*)));
106 connect(m_controller, SIGNAL(aboveItemDropEvent(int,QGraphicsSceneDragDropEvent*)), this, SLOT(slotAboveItemDropEvent(int,QGraphicsSceneDragDropEvent*)));
107
108 KItemListContainer* container = new KItemListContainer(m_controller, this);
109 container->setEnabledFrame(false);
110
111 QVBoxLayout* layout = new QVBoxLayout(this);
112 layout->setMargin(0);
113 layout->addWidget(container);
114
115 selectClosestItem();
116 }
117
118 Panel::showEvent(event);
119 }
120
121 void PlacesPanel::slotItemActivated(int index)
122 {
123 triggerItem(index, Qt::LeftButton);
124 }
125
126 void PlacesPanel::slotItemMiddleClicked(int index)
127 {
128 triggerItem(index, Qt::MiddleButton);
129 }
130
131 void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
132 {
133 PlacesItem* item = m_model->placesItem(index);
134 if (!item) {
135 return;
136 }
137
138 KMenu menu(this);
139
140 QAction* emptyTrashAction = 0;
141 QAction* addAction = 0;
142 QAction* mainSeparator = 0;
143 QAction* editAction = 0;
144 QAction* teardownAction = 0;
145 QAction* ejectAction = 0;
146
147 const QString label = item->text();
148
149 const bool isDevice = !item->udi().isEmpty();
150 if (isDevice) {
151 ejectAction = m_model->ejectAction(index);
152 if (ejectAction) {
153 ejectAction->setParent(&menu);
154 menu.addAction(ejectAction);
155 }
156
157 teardownAction = m_model->teardownAction(index);
158 if (teardownAction) {
159 teardownAction->setParent(&menu);
160 menu.addAction(teardownAction);
161 }
162
163 if (teardownAction || ejectAction) {
164 mainSeparator = menu.addSeparator();
165 }
166 } else {
167 if (item->url() == KUrl("trash:/")) {
168 emptyTrashAction = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
169 emptyTrashAction->setEnabled(item->icon() == "user-trash-full");
170 menu.addSeparator();
171 }
172 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
173 mainSeparator = menu.addSeparator();
174 editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit '%1'...", label));
175 }
176
177 if (!addAction) {
178 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
179 }
180
181 QAction* openInNewTabAction = menu.addAction(i18nc("@item:inmenu", "Open '%1' in New Tab", label));
182 openInNewTabAction->setIcon(KIcon("tab-new"));
183
184 QAction* removeAction = 0;
185 if (!isDevice && !item->isSystemItem()) {
186 removeAction = menu.addAction(KIcon("edit-delete"), i18nc("@item:inmenu", "Remove '%1'", label));
187 }
188
189 QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide '%1'", label));
190 hideAction->setCheckable(true);
191 hideAction->setChecked(item->isHidden());
192
193 QAction* showAllAction = 0;
194 if (m_model->hiddenCount() > 0) {
195 if (!mainSeparator) {
196 mainSeparator = menu.addSeparator();
197 }
198 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
199 showAllAction->setCheckable(true);
200 showAllAction->setChecked(m_model->hiddenItemsShown());
201 }
202
203 menu.addSeparator();
204 foreach (QAction* action, customContextMenuActions()) {
205 menu.addAction(action);
206 }
207
208 QAction* action = menu.exec(pos.toPoint());
209 if (action) {
210 if (action == emptyTrashAction) {
211 emptyTrash();
212 } else if (action == addAction) {
213 addEntry();
214 } else if (action == editAction) {
215 editEntry(index);
216 } else if (action == removeAction) {
217 m_model->removeItem(index);
218 } else if (action == hideAction) {
219 item->setHidden(hideAction->isChecked());
220 } else if (action == openInNewTabAction) {
221 const KUrl url = m_model->item(index)->dataValue("url").value<KUrl>();
222 emit placeMiddleClicked(url);
223 } else if (action == showAllAction) {
224 m_model->setHiddenItemsShown(showAllAction->isChecked());
225 } else if (action == teardownAction) {
226 m_model->requestTeardown(index);
227 } else if (action == ejectAction) {
228 m_model->requestEject(index);
229 }
230 }
231
232 selectClosestItem();
233 }
234
235 void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
236 {
237 KMenu menu(this);
238
239 QAction* addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
240
241 QAction* showAllAction = 0;
242 if (m_model->hiddenCount() > 0) {
243 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
244 showAllAction->setCheckable(true);
245 showAllAction->setChecked(m_model->hiddenItemsShown());
246 }
247
248 menu.addSeparator();
249 foreach (QAction* action, customContextMenuActions()) {
250 menu.addAction(action);
251 }
252
253 QAction* action = menu.exec(pos.toPoint());
254 if (action) {
255 if (action == addAction) {
256 addEntry();
257 } else if (action == showAllAction) {
258 m_model->setHiddenItemsShown(showAllAction->isChecked());
259 }
260 }
261
262 selectClosestItem();
263 }
264
265 void PlacesPanel::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
266 {
267 if (index < 0) {
268 return;
269 }
270
271 KUrl destUrl = m_model->placesItem(index)->url();
272 QDropEvent dropEvent(event->pos().toPoint(),
273 event->possibleActions(),
274 event->mimeData(),
275 event->buttons(),
276 event->modifiers());
277
278 DragAndDropHelper::dropUrls(KFileItem(), destUrl, &dropEvent);
279 }
280
281 void PlacesPanel::slotAboveItemDropEvent(int index, QGraphicsSceneDragDropEvent* event)
282 {
283 m_model->dropMimeDataBefore(index, event->mimeData());
284 }
285
286 void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
287 {
288 Q_UNUSED(parent);
289 const QString error = DragAndDropHelper::dropUrls(KFileItem(), dest, event);
290 if (!error.isEmpty()) {
291 emit errorMessage(error);
292 }
293
294 }
295
296 void PlacesPanel::slotTrashUpdated(KJob* job)
297 {
298 if (job->error()) {
299 emit errorMessage(job->errorString());
300 }
301 org::kde::KDirNotify::emitFilesAdded("trash:/");
302 }
303
304 void PlacesPanel::slotStorageSetupDone(int index, bool success)
305 {
306 disconnect(m_model, SIGNAL(storageSetupDone(int,bool)),
307 this, SLOT(slotStorageSetupDone(int,bool)));
308
309 if (m_triggerStorageSetupButton == Qt::NoButton) {
310 return;
311 }
312
313 if (success) {
314 Q_ASSERT(!m_model->storageSetupNeeded(index));
315 triggerItem(index, m_triggerStorageSetupButton);
316 m_triggerStorageSetupButton = Qt::NoButton;
317 } else {
318 setUrl(m_storageSetupFailedUrl);
319 m_storageSetupFailedUrl = KUrl();
320 }
321 }
322
323 void PlacesPanel::emptyTrash()
324 {
325 const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
326 const bool del = KMessageBox::warningContinueCancel(window(),
327 text,
328 QString(),
329 KGuiItem(i18nc("@action:button", "Empty Trash"),
330 KIcon("user-trash"))
331 ) == KMessageBox::Continue;
332 if (del) {
333 QByteArray packedArgs;
334 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
335 stream << int(1);
336 KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
337 KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
338 job->ui()->setWindow(parentWidget());
339 connect(job, SIGNAL(result(KJob*)), SLOT(slotTrashUpdated(KJob*)));
340 }
341 }
342
343 void PlacesPanel::addEntry()
344 {
345 const int index = m_controller->selectionManager()->currentItem();
346 const KUrl url = m_model->data(index).value("url").value<KUrl>();
347
348 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
349 dialog->setCaption(i18nc("@title:window", "Add Places Entry"));
350 dialog->setAllowGlobal(true);
351 dialog->setUrl(url);
352 if (dialog->exec() == QDialog::Accepted) {
353 PlacesItem* item = m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon());
354 m_model->appendItemToGroup(item);
355 }
356
357 delete dialog;
358 }
359
360 void PlacesPanel::editEntry(int index)
361 {
362 QHash<QByteArray, QVariant> data = m_model->data(index);
363
364 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
365 dialog->setCaption(i18nc("@title:window", "Edit Places Entry"));
366 dialog->setIcon(data.value("iconName").toString());
367 dialog->setText(data.value("text").toString());
368 dialog->setUrl(data.value("url").value<KUrl>());
369 dialog->setAllowGlobal(true);
370 if (dialog->exec() == QDialog::Accepted) {
371 PlacesItem* oldItem = m_model->placesItem(index);
372 if (oldItem) {
373 oldItem->setText(dialog->text());
374 oldItem->setUrl(dialog->url());
375 oldItem->setIcon(dialog->icon());
376 }
377 }
378
379 delete dialog;
380 }
381
382 void PlacesPanel::selectClosestItem()
383 {
384 const int index = m_model->closestItem(url());
385 KItemListSelectionManager* selectionManager = m_controller->selectionManager();
386 selectionManager->setCurrentItem(index);
387 selectionManager->clearSelection();
388 selectionManager->setSelected(index);
389 }
390
391 void PlacesPanel::triggerItem(int index, Qt::MouseButton button)
392 {
393 const PlacesItem* item = m_model->placesItem(index);
394 if (!item) {
395 return;
396 }
397
398 if (m_model->storageSetupNeeded(index)) {
399 m_triggerStorageSetupButton = button;
400 m_storageSetupFailedUrl = url();
401
402 connect(m_model, SIGNAL(storageSetupDone(int,bool)),
403 this, SLOT(slotStorageSetupDone(int,bool)));
404
405 m_model->requestStorageSetup(index);
406 } else {
407 m_triggerStorageSetupButton = Qt::NoButton;
408
409 const KUrl url = m_model->data(index).value("url").value<KUrl>();
410 if (!url.isEmpty()) {
411 if (button == Qt::MiddleButton) {
412 emit placeMiddleClicked(PlacesItemModel::convertedUrl(url));
413 } else {
414 emit placeActivated(PlacesItemModel::convertedUrl(url));
415 }
416 }
417 }
418 }
419
420
421 #include "placespanel.moc"