1/* This file is part of the KDE Project
2 Copyright (c) 2006 Lukas Tinkl <ltinkl@suse.cz>
3 Copyright (c) 2008 Lubos Lunak <l.lunak@suse.cz>
4 Copyright (c) 2009 Ivo Anjo <knuckles@gmail.com>
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "freespacenotifier.h"
21
22#include <QtCore/QDir>
23#include <QtCore/QFile>
24#include <QtGui/QLabel>
25#include <QtGui/QSpinBox>
26
27#include <QtDBus/QtDBus>
28
29#include <KDebug>
30#include <KLocale>
31#include <KRun>
32#include <KConfigDialog>
33#include <KDiskFreeSpaceInfo>
34
35#include "settings.h"
36#include "ui_freespacenotifier_prefs_base.h"
37
38FreeSpaceNotifier::FreeSpaceNotifier( QObject* parent )
39 : QObject( parent )
40 , lastAvailTimer( NULL )
41 , notification( NULL )
42 , lastAvail( -1 )
43{
44 // If we are running, notifications are enabled
45 FreeSpaceNotifierSettings::setEnableNotification( true );
46
47 connect( &timer, SIGNAL(timeout()), SLOT(checkFreeDiskSpace()) );
48 timer.start( 1000 * 60 /* 1 minute */ );
49}
50
51FreeSpaceNotifier::~FreeSpaceNotifier()
52{
53 // The notification is automatically destroyed when it goes away, so we only need to do this if
54 // it is still being shown
55 if ( notification ) notification->deref();
56}
57
58void FreeSpaceNotifier::checkFreeDiskSpace()
59{
60 if ( notification || !FreeSpaceNotifierSettings::enableNotification() )
61 return;
62 KDiskFreeSpaceInfo fsInfo = KDiskFreeSpaceInfo::freeSpaceInfo( QDir::homePath() );
63 if ( fsInfo.isValid() )
64 {
65 int limit = FreeSpaceNotifierSettings::minimumSpace(); // MiB
66 int availpct = int( 100 * fsInfo.available() / fsInfo.size() );
67 qint64 avail = fsInfo.available() / ( 1024 * 1024 ); // to MiB
68 bool warn = false;
69 if( avail < limit ) // avail disk space dropped under a limit
70 {
71 if( lastAvail < 0 ) // always warn the first time
72 {
73 lastAvail = avail;
74 warn = true;
75 }
76 else if( avail > lastAvail ) // the user freed some space
77 lastAvail = avail; // so warn if it goes low again
78 else if( avail < lastAvail / 2 ) // available dropped to a half of previous one, warn again
79 {
80 warn = true;
81 lastAvail = avail;
82 }
83 // do not change lastAvail otherwise, to handle free space slowly going down
84 }
85 if ( warn )
86 {
87 notification = new KNotification( "freespacenotif", 0, KNotification::Persistent );
88
89 notification->setText( i18nc( "Warns the user that the system is running low on space on his home folder, indicating the percentage and absolute MiB size remaining, and asks if the user wants to do something about it", "You are running low on disk space on your home folder (currently %2%, %1 MiB free).\nWould you like to run a file manager to free some disk space?", avail, availpct ) );
90 notification->setActions( QStringList() << i18nc( "Opens a file manager like dolphin", "Open File Manager" ) << i18nc( "Closes the notification", "Do Nothing" ) << i18nc( "Allows the user to configure the warning notification being shown", "Configure Warning" ) );
91 //notification->setPixmap( ... ); // TODO: Maybe add a picture here?
92
93 connect( notification, SIGNAL(action1Activated()), SLOT(openFileManager()) );
94 connect( notification, SIGNAL(action2Activated()), SLOT(cleanupNotification()) );
95 connect( notification, SIGNAL(action3Activated()), SLOT(showConfiguration()) );
96 connect( notification, SIGNAL(closed()), SLOT(cleanupNotification()) );
97
98 notification->setComponentData( KComponentData( "freespacenotifier" ) );
99 notification->sendEvent();
100 }
101 }
102}
103
104void FreeSpaceNotifier::openFileManager()
105{
106 cleanupNotification();
107 new KRun( KUrl( QDir::homePath() ), 0 );
108}
109
110void FreeSpaceNotifier::showConfiguration()
111{
112 cleanupNotification();
113
114 if ( KConfigDialog::showDialog( "settings" ) ) {
115 return;
116 }
117
118 KConfigDialog *dialog = new KConfigDialog( 0, "settings", FreeSpaceNotifierSettings::self() );
119 QWidget *generalSettingsDlg = new QWidget();
120
121 Ui::freespacenotifier_prefs_base preferences;
122 preferences.setupUi( generalSettingsDlg );
123
124 dialog->addPage( generalSettingsDlg, i18nc( "The settings dialog main page name, as in 'general settings'", "General" ), "system-run" );
125 connect( dialog, SIGNAL(finished()), this, SLOT(configDialogClosed()) );
126 dialog->setAttribute( Qt::WA_DeleteOnClose );
127 dialog->show();
128}
129
130void FreeSpaceNotifier::cleanupNotification()
131{
132 notification = NULL;
133
134 // warn again if constantly below limit for too long
135 if( lastAvailTimer == NULL )
136 {
137 lastAvailTimer = new QTimer( this );
138 connect( lastAvailTimer, SIGNAL(timeout()), SLOT(resetLastAvailable()) );
139 }
140 lastAvailTimer->start( 1000 * 60 * 60 /* 1 hour*/ );
141}
142
143void FreeSpaceNotifier::resetLastAvailable()
144{
145 lastAvail = -1;
146 lastAvailTimer->deleteLater();
147 lastAvailTimer = NULL;
148}
149
150void FreeSpaceNotifier::configDialogClosed()
151{
152 if ( !FreeSpaceNotifierSettings::enableNotification() )
153 disableFSNotifier();
154}
155
156/* The idea here is to disable ourselves by telling kded to stop autostarting us, and
157 * to kill the current running instance.
158 */
159void FreeSpaceNotifier::disableFSNotifier()
160{
161 QDBusInterface iface( "org.kde.kded", "/kded", "org.kde.kded" );
162 if ( dbusError( iface ) ) return;
163
164 // Disable current module autoload
165 iface.call( "setModuleAutoloading", "freespacenotifier", false );
166 if ( dbusError( iface ) ) return;
167
168 // Unload current module
169 iface.call( "unloadModule", "freespacenotifier" );
170 if ( dbusError( iface ) ) return;
171}
172
173bool FreeSpaceNotifier::dbusError( QDBusInterface &iface )
174{
175 QDBusError err = iface.lastError();
176 if ( err.isValid() )
177 {
178 kError() << "Failed to perform operation on kded [" << err.name() << "]:" << err.message();
179 return true;
180 }
181 return false;
182}
183