]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placespanel.cpp
Enable the "Empty Trash" action if items are deleted by non-KDE apps
[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 <QVBoxLayout>
47 #include <QShowEvent>
48
49 PlacesPanel::PlacesPanel(QWidget* parent) :
50 Panel(parent),
51 m_controller(0),
52 m_model(0)
53 {
54 }
55
56 PlacesPanel::~PlacesPanel()
57 {
58 }
59
60 bool PlacesPanel::urlChanged()
61 {
62 return true;
63 }
64
65 void PlacesPanel::showEvent(QShowEvent* event)
66 {
67 if (event->spontaneous()) {
68 Panel::showEvent(event);
69 return;
70 }
71
72 if (!m_controller) {
73 // Postpone the creating of the controller to the first show event.
74 // This assures that no performance and memory overhead is given when the folders panel is not
75 // used at all and stays invisible.
76 m_model = new PlacesItemModel(this);
77 m_model->setGroupedSorting(true);
78 connect(m_model, SIGNAL(errorMessage(QString)),
79 this, SIGNAL(errorMessage(QString)));
80
81 KStandardItemListView* view = new KStandardItemListView();
82 view->setWidgetCreator(new KItemListWidgetCreator<PlacesItemListWidget>());
83 view->setGroupHeaderCreator(new KItemListGroupHeaderCreator<PlacesItemListGroupHeader>());
84
85 m_controller = new KItemListController(m_model, view, this);
86 m_controller->setSelectionBehavior(KItemListController::SingleSelection);
87 connect(m_controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int)));
88 connect(m_controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
89 connect(m_controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF)));
90 connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF)));
91
92 KItemListContainer* container = new KItemListContainer(m_controller, this);
93 container->setEnabledFrame(false);
94
95 QVBoxLayout* layout = new QVBoxLayout(this);
96 layout->setMargin(0);
97 layout->addWidget(container);
98
99 selectClosestItem();
100 }
101
102 Panel::showEvent(event);
103 }
104
105 void PlacesPanel::slotItemActivated(int index)
106 {
107 const KUrl url = m_model->data(index).value("url").value<KUrl>();
108 if (!url.isEmpty()) {
109 emit placeActivated(PlacesItemModel::convertedUrl(url));
110 }
111 }
112
113 void PlacesPanel::slotItemMiddleClicked(int index)
114 {
115 const KUrl url = m_model->data(index).value("url").value<KUrl>();
116 if (!url.isEmpty()) {
117 emit placeMiddleClicked(PlacesItemModel::convertedUrl(url));
118 }
119 }
120
121 void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
122 {
123 PlacesItem* item = m_model->placesItem(index);
124 if (!item) {
125 return;
126 }
127
128 KMenu menu(this);
129
130 QAction* emptyTrashAction = 0;
131 QAction* addAction = 0;
132 QAction* mainSeparator = 0;
133 QAction* editAction = 0;
134 QAction* teardownAction = 0;
135 QAction* ejectAction = 0;
136
137 const QString label = item->text();
138
139 const bool isDevice = !item->udi().isEmpty();
140 if (isDevice) {
141 ejectAction = m_model->ejectAction(index);
142 if (ejectAction) {
143 ejectAction->setParent(&menu);
144 menu.addAction(ejectAction);
145 }
146
147 teardownAction = m_model->teardownAction(index);
148 if (teardownAction) {
149 teardownAction->setParent(&menu);
150 menu.addAction(teardownAction);
151 }
152
153 if (teardownAction || ejectAction) {
154 mainSeparator = menu.addSeparator();
155 }
156 } else {
157 if (item->url() == KUrl("trash:/")) {
158 emptyTrashAction = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
159 emptyTrashAction->setEnabled(item->icon() == "user-trash-full");
160 menu.addSeparator();
161 }
162 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
163 mainSeparator = menu.addSeparator();
164 editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit '%1'...", label));
165 }
166
167 if (!addAction) {
168 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
169 }
170
171 QAction* openInNewTabAction = menu.addAction(i18nc("@item:inmenu", "Open '%1' in New Tab", label));
172 openInNewTabAction->setIcon(KIcon("tab-new"));
173
174 QAction* removeAction = 0;
175 if (!isDevice && !item->isSystemItem()) {
176 removeAction = menu.addAction(KIcon("edit-delete"), i18nc("@item:inmenu", "Remove '%1'", label));
177 }
178
179 QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide '%1'", label));
180 hideAction->setCheckable(true);
181 hideAction->setChecked(item->isHidden());
182
183 QAction* showAllAction = 0;
184 if (m_model->hiddenCount() > 0) {
185 if (!mainSeparator) {
186 mainSeparator = menu.addSeparator();
187 }
188 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
189 showAllAction->setCheckable(true);
190 showAllAction->setChecked(m_model->hiddenItemsShown());
191 }
192
193 menu.addSeparator();
194 foreach (QAction* action, customContextMenuActions()) {
195 menu.addAction(action);
196 }
197
198 QAction* action = menu.exec(pos.toPoint());
199 if (action) {
200 if (action == emptyTrashAction) {
201 emptyTrash();
202 } else if (action == addAction) {
203 addEntry();
204 } else if (action == editAction) {
205 editEntry(index);
206 } else if (action == removeAction) {
207 m_model->removeItem(index);
208 } else if (action == hideAction) {
209 item->setHidden(hideAction->isChecked());
210 } else if (action == openInNewTabAction) {
211 const KUrl url = m_model->item(index)->dataValue("url").value<KUrl>();
212 emit placeMiddleClicked(url);
213 } else if (action == showAllAction) {
214 m_model->setHiddenItemsShown(showAllAction->isChecked());
215 } else if (action == teardownAction) {
216 m_model->requestTeardown(index);
217 } else if (action == ejectAction) {
218 m_model->requestEject(index);
219 }
220 }
221
222 selectClosestItem();
223 }
224
225 void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
226 {
227 KMenu menu(this);
228
229 QAction* addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
230
231 QAction* showAllAction = 0;
232 if (m_model->hiddenCount() > 0) {
233 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
234 showAllAction->setCheckable(true);
235 showAllAction->setChecked(m_model->hiddenItemsShown());
236 }
237
238 menu.addSeparator();
239 foreach (QAction* action, customContextMenuActions()) {
240 menu.addAction(action);
241 }
242
243 QAction* action = menu.exec(pos.toPoint());
244 if (action) {
245 if (action == addAction) {
246 addEntry();
247 } else if (action == showAllAction) {
248 m_model->setHiddenItemsShown(showAllAction->isChecked());
249 }
250 }
251
252 selectClosestItem();
253 }
254
255 void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
256 {
257 Q_UNUSED(parent);
258 const QString error = DragAndDropHelper::dropUrls(KFileItem(), dest, event);
259 if (!error.isEmpty()) {
260 emit errorMessage(error);
261 }
262
263 }
264
265 void PlacesPanel::slotTrashUpdated(KJob* job)
266 {
267 if (job->error()) {
268 emit errorMessage(job->errorString());
269 }
270 org::kde::KDirNotify::emitFilesAdded("trash:/");
271 }
272
273 void PlacesPanel::emptyTrash()
274 {
275 const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
276 const bool del = KMessageBox::warningContinueCancel(window(),
277 text,
278 QString(),
279 KGuiItem(i18nc("@action:button", "Empty Trash"),
280 KIcon("user-trash"))
281 ) == KMessageBox::Continue;
282 if (del) {
283 QByteArray packedArgs;
284 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
285 stream << int(1);
286 KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
287 KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
288 job->ui()->setWindow(parentWidget());
289 connect(job, SIGNAL(result(KJob*)), SLOT(slotTrashUpdated(KJob*)));
290 }
291 }
292
293 void PlacesPanel::addEntry()
294 {
295 const int index = m_controller->selectionManager()->currentItem();
296 const KUrl url = m_model->data(index).value("url").value<KUrl>();
297
298 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
299 dialog->setCaption(i18nc("@title:window", "Add Places Entry"));
300 dialog->setAllowGlobal(true);
301 dialog->setUrl(url);
302 if (dialog->exec() == QDialog::Accepted) {
303 PlacesItem* item = m_model->createPlacesItem(dialog->text(), dialog->url(), dialog->icon());
304
305 // Insert the item as last item of the corresponding group.
306 int i = 0;
307 while (i < m_model->count() && m_model->placesItem(i)->group() != item->group()) {
308 ++i;
309 }
310
311 bool inserted = false;
312 while (!inserted && i < m_model->count()) {
313 if (m_model->placesItem(i)->group() != item->group()) {
314 m_model->insertItem(i, item);
315 inserted = true;
316 }
317 ++i;
318 }
319
320 if (!inserted) {
321 m_model->appendItem(item);
322 }
323 }
324
325 delete dialog;
326 }
327
328 void PlacesPanel::editEntry(int index)
329 {
330 QHash<QByteArray, QVariant> data = m_model->data(index);
331
332 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
333 dialog->setCaption(i18nc("@title:window", "Edit Places Entry"));
334 dialog->setIcon(data.value("iconName").toString());
335 dialog->setText(data.value("text").toString());
336 dialog->setUrl(data.value("url").value<KUrl>());
337 dialog->setAllowGlobal(true);
338 if (dialog->exec() == QDialog::Accepted) {
339 PlacesItem* oldItem = m_model->placesItem(index);
340 if (oldItem) {
341 oldItem->setText(dialog->text());
342 oldItem->setUrl(dialog->url());
343 oldItem->setIcon(dialog->icon());
344 }
345 }
346
347 delete dialog;
348 }
349
350 void PlacesPanel::selectClosestItem()
351 {
352 const int index = m_model->closestItem(url());
353 KItemListSelectionManager* selectionManager = m_controller->selectionManager();
354 selectionManager->setCurrentItem(index);
355 selectionManager->clearSelection();
356 selectionManager->setSelected(index);
357 }
358
359 #include "placespanel.moc"