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