~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/desktop/applets/trash/trash.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright 2007 by Marco Martin <notmart@gmail.com>                    *
 
3
 *                                                                         *
 
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.                                   *
 
8
 *                                                                         *
 
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.                          *
 
13
 *                                                                         *
 
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
 ***************************************************************************/
 
19
 
 
20
#include "trash.h"
 
21
 
 
22
//QT
 
23
#include <QGraphicsSceneDragDropEvent>
 
24
#include <QGraphicsLinearLayout>
 
25
 
 
26
//KDE
 
27
#include <KCModuleProxy>
 
28
#include <KConfigDialog>
 
29
#include <KDebug>
 
30
#include <KLocale>
 
31
#include <KIconLoader>
 
32
#include <KRun>
 
33
#include <KSharedConfig>
 
34
#include <KMessageBox>
 
35
#include <KUrl>
 
36
#include <KProcess>
 
37
#include <KStandardDirs>
 
38
#include <KNotification>
 
39
#include <KGlobalSettings>
 
40
 
 
41
#include <kfileplacesmodel.h>
 
42
 
 
43
#include <KIO/CopyJob>
 
44
#include <KIO/JobUiDelegate>
 
45
 
 
46
//Plasma
 
47
#include <Plasma/IconWidget>
 
48
#include <Plasma/Containment>
 
49
#include <Plasma/ToolTipManager>
 
50
 
 
51
 
 
52
 
 
53
//Solid
 
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>
 
61
 
 
62
 
 
63
Trash::Trash(QObject *parent, const QVariantList &args)
 
64
    : Plasma::Applet(parent, args),
 
65
      m_icon(0),
 
66
      m_dirLister(0),
 
67
      emptyTrash(0),
 
68
      m_count(0),
 
69
      m_showText(false),
 
70
      m_places(0),
 
71
      m_proxy(0),
 
72
      m_emptyProcess(0)
 
73
{
 
74
    setHasConfigurationInterface(true);
 
75
    setAspectRatioMode(Plasma::ConstrainedSquare);
 
76
 
 
77
    m_icon = new Plasma::IconWidget(KIcon("user-trash"),QString(),this);
 
78
    m_icon->setNumDisplayLines(2);
 
79
    m_icon->setDrawBackground(true);
 
80
    setBackgroundHints(NoBackground);
 
81
 
 
82
    resize(m_icon->sizeFromIconSize(IconSize(KIconLoader::Desktop)));
 
83
    createMenu();
 
84
}
 
85
 
 
86
Trash::~Trash()
 
87
{
 
88
    delete m_dirLister;
 
89
}
 
90
 
 
91
void Trash::init()
 
92
{
 
93
    registerAsDragHandle(m_icon);
 
94
 
 
95
    QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
 
96
    layout->setContentsMargins(0, 0, 0, 0);
 
97
    layout->setSpacing(0);
 
98
    layout->addItem(m_icon);
 
99
 
 
100
    setAcceptDrops(true);
 
101
    installSceneEventFilter(m_icon);
 
102
 
 
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 & ) ) );
 
110
 
 
111
    m_dirLister->openUrl(KUrl("trash:/"));
 
112
 
 
113
    connect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
 
114
    
 
115
    connect(KGlobalSettings::self(), SIGNAL(iconChanged(int)),
 
116
        this, SLOT(iconSizeChanged(int)));
 
117
}
 
118
 
 
119
void Trash::createConfigurationInterface(KConfigDialog *parent)
 
120
{
 
121
    m_proxy = new KCModuleProxy("kcmtrash");
 
122
 
 
123
    parent->addPage(m_proxy, i18n("Trash"), icon());
 
124
    connect(parent, SIGNAL(okClicked()), this, SLOT(slotApplyConfig()));
 
125
 
 
126
    m_proxy->load();
 
127
}
 
128
 
 
129
void Trash::createMenu()
 
130
{
 
131
    QAction* open = new QAction(SmallIcon("document-open"), i18n("&Open"), this);
 
132
    actions.append(open);
 
133
    connect(open, SIGNAL(triggered(bool)), this , SLOT(slotOpen()));
 
134
 
 
135
    emptyTrash = new QAction(SmallIcon("trash-empty"), i18n("&Empty Trashcan"), this);
 
136
    actions.append(emptyTrash);
 
137
    connect(emptyTrash, SIGNAL(triggered(bool)), this, SLOT(slotEmpty()));
 
138
 
 
139
    m_menu.addTitle(i18n("Trash"));
 
140
    m_menu.addAction(open);
 
141
    m_menu.addAction(emptyTrash);
 
142
 
 
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);
 
147
 
 
148
    connect(&m_menu, SIGNAL(aboutToHide()), m_icon, SLOT(setUnpressed()));
 
149
}
 
150
 
 
151
void Trash::popup()
 
152
{
 
153
    if (m_menu.isVisible()) {
 
154
        m_menu.hide();
 
155
        return;
 
156
    }
 
157
    m_menu.popup(popupPosition(m_menu.sizeHint()));
 
158
    m_icon->setPressed();
 
159
}
 
160
 
 
161
void Trash::constraintsEvent(Plasma::Constraints constraints)
 
162
{
 
163
    if (constraints & Plasma::FormFactorConstraint) {
 
164
        disconnect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
 
165
        disconnect(m_icon, SIGNAL(clicked()), this, SLOT(slotOpen()));
 
166
 
 
167
        if (formFactor() == Plasma::Planar ||
 
168
            formFactor() == Plasma::MediaCenter) {
 
169
 
 
170
            connect(m_icon, SIGNAL(activated()), this, SLOT(slotOpen()));
 
171
 
 
172
            m_icon->setText(i18n("Trash"));
 
173
            m_icon->setInfoText(i18np("One item", "%1 items", m_count));
 
174
            m_showText = true;
 
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));
 
178
        } else {
 
179
            //in a panel the icon always behaves like a button
 
180
            connect(m_icon, SIGNAL(clicked()), this, SLOT(slotOpen()));
 
181
 
 
182
            m_icon->setText(0);
 
183
            m_icon->setInfoText(0);
 
184
            m_showText = false;
 
185
            m_icon->setDrawBackground(false);
 
186
 
 
187
            setMinimumSize(m_icon->sizeFromIconSize(IconSize(KIconLoader::Small)));
 
188
        }
 
189
        updateIcon();
 
190
    }
 
191
}
 
192
 
 
193
void Trash::slotOpen()
 
194
{
 
195
    emit releaseVisualFocus();
 
196
    KRun::runUrl(KUrl("trash:/"), "inode/directory", 0);
 
197
}
 
198
 
 
199
void Trash::slotEmpty()
 
200
{
 
201
    if (m_emptyProcess) {
 
202
        return;
 
203
    }
 
204
 
 
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,
 
209
                                                        text,
 
210
                                                        QString(),
 
211
                                                        KGuiItem(i18nc("@action:button", "Empty Trash"),
 
212
                                                                  KIcon("user-trash"))
 
213
                                                        ) == KMessageBox::Continue;
 
214
 
 
215
    if (del) {
 
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();
 
226
    }
 
227
}
 
228
 
 
229
void Trash::emptyFinished(int exitCode, QProcess::ExitStatus exitStatus)
 
230
{
 
231
    Q_UNUSED(exitCode)
 
232
    Q_UNUSED(exitStatus)
 
233
 
 
234
    KNotification::event("Trash: emptied", QString() , QPixmap() , 0l, KNotification::DefaultEvent );
 
235
    
 
236
    //TODO: check the exit status and let the user know if it fails
 
237
    delete m_emptyProcess;
 
238
    m_emptyProcess = 0;
 
239
    emptyTrash->setEnabled(false);
 
240
    emptyTrash->setText(i18n("&Empty Trashcan"));
 
241
}
 
242
 
 
243
void Trash::updateIcon()
 
244
{
 
245
    Plasma::ToolTipContent data;
 
246
    data.setMainText(i18n("Trash"));
 
247
 
 
248
    if (m_count > 0) {
 
249
        m_icon->setIcon("user-trash-full");
 
250
 
 
251
        data.setSubText(i18np("One item", "%1 items", m_count));
 
252
        if (m_showText) {
 
253
            m_icon->setInfoText(i18np("One item", "%1 items", m_count));
 
254
        }
 
255
    } else {
 
256
        m_icon->setIcon("user-trash");
 
257
 
 
258
        data.setSubText(i18nc("The trash is empty. This is not an action, but a state", "Empty"));
 
259
        if (m_showText) {
 
260
            m_icon->setInfoText(i18nc("The trash is empty. This is not an action, but a state", "Empty"));
 
261
        }
 
262
    }
 
263
 
 
264
    m_icon->update();
 
265
 
 
266
    data.setImage(m_icon->icon().pixmap(IconSize(KIconLoader::Desktop)));
 
267
 
 
268
    if (!m_showText) {
 
269
        Plasma::ToolTipManager::self()->setContent(this, data);
 
270
    } else {
 
271
        Plasma::ToolTipManager::self()->clearContent(this);
 
272
    }
 
273
 
 
274
    emptyTrash->setEnabled(m_count > 0);
 
275
}
 
276
 
 
277
void Trash::slotClear()
 
278
{
 
279
    m_count = 0;
 
280
    updateIcon();
 
281
}
 
282
 
 
283
void Trash::slotCompleted()
 
284
{
 
285
    m_count = m_dirLister->items(KDirLister::AllItems).count();
 
286
    updateIcon();
 
287
}
 
288
 
 
289
void Trash::slotDeleteItem(const KFileItem &)
 
290
{
 
291
    m_count--;
 
292
    updateIcon();
 
293
}
 
294
 
 
295
void Trash::slotApplyConfig()
 
296
{
 
297
    m_proxy->save();
 
298
}
 
299
 
 
300
QList<QAction*> Trash::contextualActions()
 
301
{
 
302
    return actions;
 
303
}
 
304
 
 
305
void Trash::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
 
306
{
 
307
    if (KUrl::List::canDecode(event->mimeData())) {
 
308
        const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
 
309
        if (urls.count() == 1) {
 
310
            if (!m_places) {
 
311
                m_places = new KFilePlacesModel(this);
 
312
            }
 
313
 
 
314
            KUrl url = urls.at(0);
 
315
 
 
316
            const Solid::Predicate predicate(Solid::DeviceInterface::StorageAccess, "filePath", url.path());
 
317
 
 
318
            //query for mounted devices
 
319
            const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString());
 
320
 
 
321
            //seek for an item in the places (e.g. Dolphin sidebar)
 
322
            const QModelIndex index = m_places->closestItem(url);
 
323
 
 
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");
 
328
            }
 
329
        }
 
330
    }
 
331
}
 
332
 
 
333
void Trash::dragLeaveEvent(QGraphicsSceneDragDropEvent *)
 
334
{
 
335
    updateIcon();
 
336
}
 
337
 
 
338
void Trash::dropEvent(QGraphicsSceneDragDropEvent *event)
 
339
{
 
340
    if (KUrl::List::canDecode(event->mimeData())) {
 
341
        const KUrl::List urls = KUrl::List::fromMimeData(event->mimeData());
 
342
        if (!urls.isEmpty()) {
 
343
            event->accept();
 
344
 
 
345
            //some special operation was done instead of simply deleting a file
 
346
            bool specialOperation = false;
 
347
 
 
348
            if (!m_places) {
 
349
                m_places = new KFilePlacesModel(this);
 
350
            }
 
351
 
 
352
            foreach (const KUrl& url, urls) {
 
353
                const Solid::Predicate predicate(Solid::DeviceInterface::StorageAccess, "filePath", url.path());
 
354
 
 
355
                //query for mounted devices
 
356
                const QList<Solid::Device> devList = Solid::Device::listFromQuery(predicate, QString());
 
357
 
 
358
 
 
359
                //seek for an item in the places (e.g. Dolphin sidebar)
 
360
                const QModelIndex index = m_places->closestItem(url);
 
361
 
 
362
                if (!devList.isEmpty()) {
 
363
                    //Assuming a mountpoint has a single device
 
364
                    Solid::Device device = devList.first();
 
365
 
 
366
                    if (device.is<Solid::OpticalDisc>()) {
 
367
                        device.parent().as<Solid::OpticalDrive>()->eject();
 
368
                    } else {
 
369
                        device.as<Solid::StorageAccess>()->teardown();
 
370
                    }
 
371
 
 
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;
 
377
                }
 
378
            }
 
379
 
 
380
            if (specialOperation) {
 
381
                updateIcon();
 
382
 
 
383
            //finally, try to trash a file
 
384
            }else{
 
385
                KIO::Job* job = KIO::trash(urls);
 
386
                job->ui()->setWindow(0);
 
387
                job->ui()->setAutoErrorHandlingEnabled(true);
 
388
            }
 
389
        }
 
390
    }
 
391
}
 
392
 
 
393
QSizeF Trash::sizeHint(Qt::SizeHint which, const QSizeF & constraint) const
 
394
{
 
395
    if (which == Qt::PreferredSize) {
 
396
        int iconSize;
 
397
 
 
398
        switch (formFactor()) {
 
399
            case Plasma::Planar:
 
400
            case Plasma::MediaCenter:
 
401
                iconSize = IconSize(KIconLoader::Desktop);
 
402
                break;
 
403
 
 
404
            case Plasma::Horizontal:
 
405
            case Plasma::Vertical:
 
406
                iconSize = IconSize(KIconLoader::Panel);
 
407
                break;
 
408
        }
 
409
 
 
410
        return QSizeF(iconSize, iconSize);
 
411
    }
 
412
 
 
413
    return Plasma::Applet::sizeHint(which, constraint);
 
414
}
 
415
 
 
416
void Trash::iconSizeChanged(int group)
 
417
{
 
418
    if (group == KIconLoader::Desktop || group == KIconLoader::Panel) {
 
419
        updateGeometry();
 
420
    }
 
421
}
 
422
 
 
423
#include "trash.moc"
 
424