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