]> cloud.milkyroute.net Git - dolphin.git/blob - src/panels/places/placespanel.cpp
Places Panel: Provide "Open in New Tab" entry
[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 <KConfigGroup>
27 #include <KDebug>
28 #include <KDirNotify>
29 #include <KIcon>
30 #include <KIO/Job>
31 #include <KIO/JobUiDelegate>
32 #include <KLocale>
33 #include <kitemviews/kitemlistcontainer.h>
34 #include <kitemviews/kitemlistcontroller.h>
35 #include <kitemviews/kitemlistselectionmanager.h>
36 #include <kitemviews/kstandarditem.h>
37 #include <kitemviews/kstandarditemlistview.h>
38 #include <KMenu>
39 #include <KMessageBox>
40 #include <KNotification>
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 m_model->setSortRole("group");
79
80 KStandardItemListView* view = new KStandardItemListView();
81 view->setWidgetCreator(new KItemListWidgetCreator<PlacesItemListWidget>());
82 view->setGroupHeaderCreator(new KItemListGroupHeaderCreator<PlacesItemListGroupHeader>());
83
84 m_controller = new KItemListController(m_model, view, this);
85 m_controller->setSelectionBehavior(KItemListController::SingleSelection);
86 connect(m_controller, SIGNAL(itemActivated(int)), this, SLOT(slotItemActivated(int)));
87 connect(m_controller, SIGNAL(itemMiddleClicked(int)), this, SLOT(slotItemMiddleClicked(int)));
88 connect(m_controller, SIGNAL(itemContextMenuRequested(int,QPointF)), this, SLOT(slotItemContextMenuRequested(int,QPointF)));
89 connect(m_controller, SIGNAL(viewContextMenuRequested(QPointF)), this, SLOT(slotViewContextMenuRequested(QPointF)));
90
91 KItemListContainer* container = new KItemListContainer(m_controller, this);
92 container->setEnabledFrame(false);
93
94 QVBoxLayout* layout = new QVBoxLayout(this);
95 layout->setMargin(0);
96 layout->addWidget(container);
97
98 selectClosestItem();
99 }
100
101 Panel::showEvent(event);
102 }
103
104 void PlacesPanel::slotItemActivated(int index)
105 {
106 const KUrl url = m_model->data(index).value("url").value<KUrl>();
107 if (!url.isEmpty()) {
108 emit placeActivated(url);
109 }
110 }
111
112 void PlacesPanel::slotItemMiddleClicked(int index)
113 {
114 const KUrl url = m_model->data(index).value("url").value<KUrl>();
115 if (!url.isEmpty()) {
116 emit placeMiddleClicked(url);
117 }
118 }
119
120 void PlacesPanel::slotItemContextMenuRequested(int index, const QPointF& pos)
121 {
122 const QHash<QByteArray, QVariant> data = m_model->data(index);
123 const QString label = data.value("text").toString();
124
125 KMenu menu(this);
126
127 QAction* emptyTrashAction = 0;
128 QAction* addAction = 0;
129 QAction* mainSeparator = 0;
130 QAction* editAction = 0;
131 QAction* tearDownAction = 0;
132 QAction* ejectAction = 0;
133
134 const bool isSystemItem = m_model->isSystemItem(index);
135 const bool isDevice = !data.value("udi").toString().isEmpty();
136 if (isDevice) {
137 ejectAction = m_model->ejectAction(index);
138 if (ejectAction) {
139 ejectAction->setParent(&menu);
140 menu.addAction(ejectAction);
141 }
142
143 tearDownAction = m_model->tearDownAction(index);
144 if (tearDownAction) {
145 tearDownAction->setParent(&menu);
146 menu.addAction(tearDownAction);
147 }
148
149 if (tearDownAction || ejectAction) {
150 mainSeparator = menu.addSeparator();
151 }
152 } else {
153 if (data.value("url").value<KUrl>() == KUrl("trash:/")) {
154 emptyTrashAction = menu.addAction(KIcon("trash-empty"), i18nc("@action:inmenu", "Empty Trash"));
155 KConfig trashConfig("trashrc", KConfig::SimpleConfig);
156 emptyTrashAction->setEnabled(!trashConfig.group("Status").readEntry("Empty", true));
157 menu.addSeparator();
158 }
159 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
160 mainSeparator = menu.addSeparator();
161 editAction = menu.addAction(KIcon("document-properties"), i18nc("@item:inmenu", "Edit '%1'...", label));
162 }
163
164 if (!addAction) {
165 addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
166 }
167
168 QAction* openInNewTabAction = menu.addAction(i18nc("@item:inmenu", "Open '%1' in New Tab", label));
169 openInNewTabAction->setIcon(KIcon("tab-new"));
170
171 QAction* removeAction = 0;
172 if (!isDevice && !isSystemItem) {
173 removeAction = menu.addAction(KIcon("edit-delete"), i18nc("@item:inmenu", "Remove '%1'", label));
174 }
175
176 QAction* hideAction = menu.addAction(i18nc("@item:inmenu", "Hide '%1'", label));
177 hideAction->setCheckable(true);
178 hideAction->setChecked(data.value("isHidden").toBool());
179
180 QAction* showAllAction = 0;
181 if (m_model->hiddenCount() > 0) {
182 if (!mainSeparator) {
183 mainSeparator = menu.addSeparator();
184 }
185 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
186 showAllAction->setCheckable(true);
187 showAllAction->setChecked(m_model->hiddenItemsShown());
188 }
189
190 menu.addSeparator();
191 foreach (QAction* action, customContextMenuActions()) {
192 menu.addAction(action);
193 }
194
195 QAction* action = menu.exec(pos.toPoint());
196 if (action) {
197 if (action == emptyTrashAction) {
198 emptyTrash();
199 } else if (action == addAction) {
200 addEntry();
201 } else if (action == editAction) {
202 editEntry(index);
203 } else if (action == removeAction) {
204 m_model->removeItem(index);
205 } else if (action == hideAction) {
206 m_model->setItemHidden(index, hideAction->isChecked());
207 } else if (action == openInNewTabAction) {
208 const KUrl url = m_model->item(index)->dataValue("url").value<KUrl>();
209 emit placeMiddleClicked(url);
210 } else if (action == showAllAction) {
211 m_model->setHiddenItemsShown(showAllAction->isChecked());
212 } else if (action == tearDownAction) {
213 } else if (action == ejectAction) {
214 }
215 }
216
217 selectClosestItem();
218 }
219
220 void PlacesPanel::slotViewContextMenuRequested(const QPointF& pos)
221 {
222 KMenu menu(this);
223
224 QAction* addAction = menu.addAction(KIcon("document-new"), i18nc("@item:inmenu", "Add Entry..."));
225
226 QAction* showAllAction = 0;
227 if (m_model->hiddenCount() > 0) {
228 showAllAction = menu.addAction(i18nc("@item:inmenu", "Show All Entries"));
229 showAllAction->setCheckable(true);
230 showAllAction->setChecked(m_model->hiddenItemsShown());
231 }
232
233 menu.addSeparator();
234 foreach (QAction* action, customContextMenuActions()) {
235 menu.addAction(action);
236 }
237
238 QAction* action = menu.exec(pos.toPoint());
239 if (action) {
240 if (action == addAction) {
241 addEntry();
242 } else if (action == showAllAction) {
243 m_model->setHiddenItemsShown(showAllAction->isChecked());
244 }
245 }
246
247 selectClosestItem();
248 }
249
250 void PlacesPanel::slotUrlsDropped(const KUrl& dest, QDropEvent* event, QWidget* parent)
251 {
252 Q_UNUSED(parent);
253 const QString error = DragAndDropHelper::dropUrls(KFileItem(), dest, event);
254 if (!error.isEmpty()) {
255 emit errorMessage(error);
256 }
257
258 }
259
260 void PlacesPanel::slotTrashUpdated(KJob* job)
261 {
262 if (job->error()) {
263 // TODO: Show error-string inside Dolphin, don't use job->ui->showErrorMessage().
264 }
265 org::kde::KDirNotify::emitFilesAdded("trash:/");
266 }
267
268 void PlacesPanel::emptyTrash()
269 {
270 const QString text = i18nc("@info", "Do you really want to empty the Trash? All items will be deleted.");
271 const bool del = KMessageBox::warningContinueCancel(window(),
272 text,
273 QString(),
274 KGuiItem(i18nc("@action:button", "Empty Trash"),
275 KIcon("user-trash"))
276 ) == KMessageBox::Continue;
277 if (del) {
278 QByteArray packedArgs;
279 QDataStream stream(&packedArgs, QIODevice::WriteOnly);
280 stream << int(1);
281 KIO::Job *job = KIO::special(KUrl("trash:/"), packedArgs);
282 KNotification::event("Trash: emptied", QString() , QPixmap() , 0, KNotification::DefaultEvent);
283 job->ui()->setWindow(parentWidget());
284 connect(job, SIGNAL(result(KJob*)), SLOT(slotTrashUpdated(KJob*)));
285 }
286 }
287
288 void PlacesPanel::addEntry()
289 {
290 const int index = m_controller->selectionManager()->currentItem();
291 const KUrl url = m_model->data(index).value("url").value<KUrl>();
292
293 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
294 dialog->setCaption(i18nc("@title:window", "Add Places Entry"));
295 dialog->setAllowGlobal(true);
296 dialog->setUrl(url);
297 if (dialog->exec() == QDialog::Accepted) {
298 KStandardItem* item = createStandardItemFromDialog(dialog);
299
300 // Insert the item as last item of the corresponding group.
301 int i = 0;
302 while (i < m_model->count() && m_model->item(i)->group() != item->group()) {
303 ++i;
304 }
305
306 bool inserted = false;
307 while (!inserted && i < m_model->count()) {
308 if (m_model->item(i)->group() != item->group()) {
309 m_model->insertItem(i, item);
310 inserted = true;
311 }
312 ++i;
313 }
314
315 if (!inserted) {
316 m_model->appendItem(item);
317 }
318 }
319
320 delete dialog;
321 }
322
323 void PlacesPanel::editEntry(int index)
324 {
325 QHash<QByteArray, QVariant> data = m_model->data(index);
326
327 QPointer<PlacesItemEditDialog> dialog = new PlacesItemEditDialog(this);
328 dialog->setCaption(i18nc("@title:window", "Edit Places Entry"));
329 dialog->setIcon(data.value("iconName").toString());
330 dialog->setText(data.value("text").toString());
331 dialog->setUrl(data.value("url").value<KUrl>());
332 dialog->setAllowGlobal(true);
333 if (dialog->exec() == QDialog::Accepted) {
334 KStandardItem* oldItem = m_model->item(index);
335 if (oldItem) {
336 KStandardItem* item = createStandardItemFromDialog(dialog);
337 // Although the user might have changed the URL of the item in a way
338 // that another group should be assigned, we still apply the old
339 // group to keep the same position for the item.
340 item->setGroup(oldItem->group());
341 m_model->replaceItem(index, item);
342 }
343 }
344
345 delete dialog;
346 }
347
348 void PlacesPanel::selectClosestItem()
349 {
350 const int index = m_model->closestItem(url());
351 KItemListSelectionManager* selectionManager = m_controller->selectionManager();
352 selectionManager->setCurrentItem(index);
353 selectionManager->clearSelection();
354 selectionManager->setSelected(index);
355 }
356
357 KStandardItem* PlacesPanel::createStandardItemFromDialog(PlacesItemEditDialog* dialog) const
358 {
359 Q_ASSERT(dialog);
360
361 const KUrl newUrl = dialog->url();
362 KStandardItem* item = new KStandardItem();
363 item->setIcon(KIcon(dialog->icon()));
364 item->setText(dialog->text());
365 item->setDataValue("url", newUrl);
366 item->setGroup(m_model->groupName(newUrl));
367
368 return item;
369 }
370
371 #include "placespanel.moc"