1
/***************************************************************************
2
* Copyright 2007 by Marco Martin <notmart@gmail.com> *
4
* This program is free software; you can redistribute it and/or modify *
5
* it under the terms of the GNU General Public License as published by *
6
* the Free Software Foundation; either version 2 of the License, or *
7
* (at your option) any later version. *
9
* This program is distributed in the hope that it will be useful, *
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12
* GNU General Public License for more details. *
14
* You should have received a copy of the GNU General Public License *
15
* along with this program; if not, write to the *
16
* Free Software Foundation, Inc., *
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA . *
18
***************************************************************************/
23
#include <QGraphicsSceneDragDropEvent>
24
#include <QGraphicsLinearLayout>
27
#include <KCModuleProxy>
28
#include <KConfigDialog>
31
#include <KIconLoader>
33
#include <KSharedConfig>
34
#include <KMessageBox>
37
#include <KStandardDirs>
38
#include <KNotification>
39
#include <KGlobalSettings>
41
#include <kfileplacesmodel.h>
43
#include <KIO/CopyJob>
44
#include <KIO/JobUiDelegate>
47
#include <Plasma/IconWidget>
48
#include <Plasma/Containment>
49
#include <Plasma/ToolTipManager>
54
#include <solid/devicenotifier.h>
55
#include <solid/device.h>
56
#include <solid/deviceinterface.h>
57
#include <solid/predicate.h>
58
#include <solid/storageaccess.h>
59
#include <solid/opticaldrive.h>
60
#include <solid/opticaldisc.h>
63
Trash::Trash(QObject *parent, const QVariantList &args)
64
: Plasma::Applet(parent, args),
74
setHasConfigurationInterface(true);
75
setAspectRatioMode(Plasma::ConstrainedSquare);
77
m_icon = new Plasma::IconWidget(KIcon("user-trash"),QString(),this);
78
m_icon->setNumDisplayLines(2);
79
m_icon->setDrawBackground(true);
80
setBackgroundHints(NoBackground);
82
resize(m_icon->sizeFromIconSize(IconSize(KIconLoader::Desktop)));
93
registerAsDragHandle(m_icon);
95
QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
96
layout->setContentsMargins(0, 0, 0, 0);
97
layout->setSpacing(0);
98
layout->addItem(m_icon);
100
setAcceptDrops(true);
101
installSceneEventFilter(m_icon);
103
m_dirLister = new KDirLister();
104
connect( m_dirLister, SIGNAL( clear() ),
105
this, SLOT( slotClear() ) );
106
connect( m_dirLister, SIGNAL( completed() ),
107
this, SLOT( slotCompleted() ) );
108
connect( m_dirLister, SIGNAL( deleteItem( const KFileItem & ) ),
109
this, SLOT( slotDeleteItem( const KFileItem & ) ) );
111
m_dirLister->openUrl(KUrl("trash:/"));
113
connect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
115
connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
116
this, SLOT(iconSizeChanged(int)));
119
void Trash::createConfigurationInterface(KConfigDialog *parent)
121
m_proxy = new KCModuleProxy("kcmtrash");
123
parent->addPage(m_proxy, i18n("Trash"), icon());
124
connect(parent, SIGNAL(okClicked()), this, SLOT(slotApplyConfig()));
129
void Trash::createMenu()
131
QAction* open = new QAction(SmallIcon("document-open"), i18n("&Open"), this);
132
actions.append(open);
133
connect(open, SIGNAL(triggered(bool)), this , SLOT(slotOpen()));
135
emptyTrash = new QAction(SmallIcon("trash-empty"), i18n("&Empty Trashcan"), this);
136
actions.append(emptyTrash);
137
connect(emptyTrash, SIGNAL(triggered(bool)), this, SLOT(slotEmpty()));
139
m_menu.addTitle(i18n("Trash"));
140
m_menu.addAction(open);
141
m_menu.addAction(emptyTrash);
143
//add the menu as an action icon
144
QAction* menu = new QAction(SmallIcon("arrow-up-double"),i18n("&Menu"), this);
145
connect(menu, SIGNAL(triggered(bool)), this , SLOT(popup()));
146
m_icon->addIconAction(menu);
148
connect(&m_menu, SIGNAL(aboutToHide()), m_icon, SLOT(setUnpressed()));
153
if (m_menu.isVisible()) {
157
m_menu.popup(popupPosition(m_menu.sizeHint()));
158
m_icon->setPressed();
161
void Trash::constraintsEvent(Plasma::Constraints constraints)
163
if (constraints & Plasma::FormFactorConstraint) {
164
disconnect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
165
disconnect(m_icon, SIGNAL(clicked()), this, SLOT(slotOpen()));
167
if (formFactor() == Plasma::Planar ||
168
formFactor() == Plasma::MediaCenter) {
170
connect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
172
m_icon->setText(i18n("Trash"));
173
m_icon->setInfoText(i18np("One item", "%1 items", m_count));
175
m_icon->setDrawBackground(true);
176
//Adding an arbitrary width to make room for a larger count of items
177
setMinimumSize(m_icon->sizeFromIconSize(IconSize(KIconLoader::Desktop))+=QSizeF(20,0));
179
//in a panel the icon always behaves like a button
180
connect(m_icon, SIGNAL(clicked()), this, SLOT(slotOpen()));
183
m_icon->setInfoText(0);
185
m_icon->setDrawBackground(false);
187
setMinimumSize(m_icon->sizeFromIconSize(IconSize(KIconLoader::Small)));
193
void Trash::slotOpen()
195
emit releaseVisualFocus();
196
KRun::runUrl(KUrl("trash:/"), "inode/directory", 0);
199
void Trash::slotEmpty()
201
if (m_emptyProcess) {
205
emit releaseVisualFocus();
206
const QString text(i18nc("@info", "Do you really want to empty the trash? All items will be deleted."));
207
//FIXME: should use KMessageBox::createKMessageBox so that it doesn't block the whole app!
208
const bool del = KMessageBox::warningContinueCancel(&m_menu,
211
KGuiItem(i18nc("@action:button", "Empty Trash"),
213
) == KMessageBox::Continue;
216
// We can't use KonqOperations here. To avoid duplicating its code (small, though),
217
// we can simply call ktrash.
218
//KonqOperations::emptyTrash(&m_menu);
219
emptyTrash->setEnabled(false);
220
emptyTrash->setText(i18n("Emptying Trashcan..."));
221
m_emptyProcess = new KProcess(this);
222
connect(m_emptyProcess, SIGNAL(finished(int,QProcess::ExitStatus)),
223
this, SLOT(emptyFinished(int,QProcess::ExitStatus)));
224
(*m_emptyProcess) << KStandardDirs::findExe("ktrash") << "--empty";
225
m_emptyProcess->start();
229
void Trash::emptyFinished(int exitCode, QProcess::ExitStatus exitStatus)
234
KNotification::event("Trash: emptied", QString() , QPixmap() , 0l, KNotification::DefaultEvent );
236
//TODO: check the exit status and let the user know if it fails
237
delete m_emptyProcess;
239
emptyTrash->setEnabled(false);
240
emptyTrash->setText(i18n("&Empty Trashcan"));
243
void Trash::updateIcon()
245
Plasma::ToolTipContent data;
246
data.setMainText(i18n("Trash"));
249
m_icon->setIcon("user-trash-full");
251
data.setSubText(i18np("One item", "%1 items", m_count));
253
m_icon->setInfoText(i18np("One item", "%1 items", m_count));
256
m_icon->setIcon("user-trash");
258
data.setSubText(i18nc("The trash is empty. This is not an action, but a state", "Empty"));
260
m_icon->setInfoText(i18nc("The trash is empty. This is not an action, but a state", "Empty"));
266
data.setImage(m_icon->icon().pixmap(IconSize(KIconLoader::Desktop)));
269
Plasma::ToolTipManager::self()->setContent(this, data);
271
Plasma::ToolTipManager::self()->clearContent(this);
274
emptyTrash->setEnabled(m_count > 0);
277
void Trash::slotClear()
283
void Trash::slotCompleted()
285
m_count = m_dirLister->items(KDirLister::AllItems).count();
289
void Trash::slotDeleteItem(const KFileItem &)
295
void Trash::slotApplyConfig()
300
QList<QAction*> Trash::contextualActions()
305
void Trash::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
307
if (KUrl::List::canDecode(event->mimeData())) {
308
const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
309
if (urls.count() == 1) {
311
m_places = new KFilePlacesModel(this);
314
KUrl url = urls.at(0);
316
const Solid::Predicate predicate(Solid::DeviceInterface::StorageAccess, "filePath", url.path());
318
//query for mounted devices
319
const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString());
321
//seek for an item in the places (e.g. Dolphin sidebar)
322
const QModelIndex index = m_places->closestItem(url);
324
if (!devList.isEmpty()) {
325
m_icon->setIcon("arrow-up-double");
326
} else if (m_places->bookmarkForIndex(index).url() == url) {
327
m_icon->setIcon("edit-delete");
333
void Trash::dragLeaveEvent(QGraphicsSceneDragDropEvent *)
338
void Trash::dropEvent(QGraphicsSceneDragDropEvent *event)
340
if (KUrl::List::canDecode(event->mimeData())) {
341
const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
342
if (!urls.isEmpty()) {
345
//some special operation was done instead of simply deleting a file
346
bool specialOperation = false;
349
m_places = new KFilePlacesModel(this);
352
foreach (const KUrl& url, urls) {
353
const Solid::Predicate predicate(Solid::DeviceInterface::StorageAccess, "filePath", url.path());
355
//query for mounted devices
356
const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString());
359
//seek for an item in the places (e.g. Dolphin sidebar)
360
const QModelIndex index = m_places->closestItem(url);
362
if (!devList.isEmpty()) {
363
//Assuming a mountpoint has a single device
364
Solid::Device device = devList.first();
366
if (device.is<Solid::OpticalDisc>()) {
367
device.parent().as<Solid::OpticalDrive>()->eject();
369
device.as<Solid::StorageAccess>()->teardown();
372
specialOperation = true;
373
//hide if there is exactly that item in the places model
374
} else if (m_places->bookmarkForIndex(index).url() == url) {
375
m_places->removePlace(index);
376
specialOperation = true;
380
if (specialOperation) {
383
//finally, try to trash a file
385
KIO::Job* job = KIO::trash(urls);
386
job->ui()->setWindow(0);
387
job->ui()->setAutoErrorHandlingEnabled(true);
393
QSizeF Trash::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
395
if (which == Qt::PreferredSize) {
398
switch (formFactor()) {
400
case Plasma::MediaCenter:
401
iconSize = IconSize(KIconLoader::Desktop);
404
case Plasma::Horizontal:
405
case Plasma::Vertical:
406
iconSize = IconSize(KIconLoader::Panel);
410
return QSizeF(iconSize, iconSize);
413
return Plasma::Applet::sizeHint(which, constraint);
416
void Trash::iconSizeChanged(int group)
418
if (group == KIconLoader::Desktop || group == KIconLoader::Panel) {