1/*
2 * kdfwidget.cpp
3 *
4 * Copyright (c) 1998-2001 Michael Kropfberger <michael.kropfberger@gmx.net>
5 * 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 */
21
22//
23// 1999-11-29 Espen Sand
24// Converted to QLayout and QListView + cleanups
25// 1999-12-05 Espen Sand
26// Usage bars should work again.
27//
28
29#include "kdfwidget.h"
30
31#include <stdlib.h>
32
33#include <QtCore/QAbstractEventDispatcher>
34#include <QtCore/QTimer>
35#include <QtCore/QFile>
36
37#include <QtGui/QLayout>
38#include <QtGui/QTreeView>
39#include <QtGui/QStandardItemModel>
40#include <QtGui/QStandardItem>
41#include <QtGui/QPainter>
42
43#include <kapplication.h>
44#include <kmessagebox.h>
45#include <kmenu.h>
46#include <ktoolinvocation.h>
47#include <kglobal.h>
48#include <kshell.h>
49#include <klocale.h>
50#include <kdebug.h>
51
52#include "optiondialog.h"
53
54//This aren't used here...
55//#define BAR_COLUMN 7
56//#define FULL_PERCENT 95.0
57
58#ifndef GUI_DEFINED
59static bool GUI;
60#define GUI_DEFINED
61#endif
62
63KDFWidget::KDFWidget( QWidget *parent, bool init )
64 : QWidget(parent), mOptionDialog(0), mPopup(0), mTimer(0)
65{
66 connect(&mDiskList , SIGNAL(readDFDone()),
67 this, SLOT (updateDFDone()) );
68 connect(&mDiskList , SIGNAL(criticallyFull(DiskEntry*)),
69 this, SLOT (criticallyFull(DiskEntry*)) );
70
71 m_columnList.append( Column( QLatin1String( "Icon" ), QLatin1String( "" ), 20, IconCol ));
72 m_columnList.append( Column( QLatin1String( "Device" ), i18nc("Device of the storage", "Device"), 100, DeviceCol ));
73 m_columnList.append( Column( QLatin1String( "Type" ), i18nc("Filesystem on storage", "Type"), 80, TypeCol ));
74 m_columnList.append( Column( QLatin1String( "Size" ), i18nc("Total size of the storage", "Size"), 80, SizeCol ));
75 m_columnList.append( Column( QLatin1String( "MountPoint" ), i18nc("Mount point of storage", "Mount Point"), 120, MountPointCol ));
76 m_columnList.append( Column( QLatin1String( "Free" ), i18nc("Free space in storage", "Free"), 80, FreeCol ));
77 m_columnList.append( Column( QLatin1String( "Full%" ), i18nc("Used storage space in %", "Full %"), 50, FullCol ));
78 m_columnList.append( Column( QLatin1String( "UsageBar" ), i18nc("Usage graphical bar", "Usage"), 200, UsageBarCol ));
79
80 GUI = !init;
81 if( GUI )
82 {
83 QVBoxLayout *topLayout = new QVBoxLayout( this );
84 topLayout->setSpacing( 0 );
85 topLayout->setMargin( 0 );
86
87 m_listModel = new QStandardItemModel( this );
88 m_sortModel = new KDFSortFilterProxyModel( this );
89 m_sortModel->setSourceModel( m_listModel );
90
91 m_listWidget = new QTreeView( this );
92 m_listWidget->setModel( m_sortModel );
93
94 m_itemDelegate = new KDFItemDelegate( m_listWidget );
95
96 m_listWidget->setItemDelegate( m_itemDelegate );
97 m_listWidget->setRootIsDecorated( false );
98 m_listWidget->setSortingEnabled( true );
99 m_listWidget->setContextMenuPolicy( Qt::CustomContextMenu );
100 m_listWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
101
102 topLayout->addWidget( m_listWidget );
103
104 connect( m_listWidget, SIGNAL(customContextMenuRequested(QPoint)),
105 this, SLOT(contextMenuRequested(QPoint)));
106
107 makeColumns();
108
109 mIsTopLevel = QLatin1String(parent->metaObject()->className()) == QLatin1String( "KDFTopLevel" ) ? true : false;
110 }
111
112 loadSettings();
113 if( init )
114 applySettings();
115}
116
117KDFWidget::~KDFWidget()
118{
119 delete m_listModel;
120 delete m_sortModel;
121 delete m_itemDelegate;
122 delete m_listWidget;
123}
124
125void KDFWidget::makeColumns( void )
126{
127
128 QStringList columns;
129 Q_FOREACH(const Column &c, m_columnList){
130 columns << c.columnName;
131 }
132 m_listModel->setHorizontalHeaderLabels( columns );
133
134}
135
136/******************************************************************/
137void KDFWidget::closeEvent(QCloseEvent *)
138{
139 applySettings();
140 kapp->quit();
141}
142
143
144void KDFWidget::settingsChanged( void )
145{
146 applySettings();
147 loadSettings();
148}
149
150
151/***************************************************************************
152 * writes the KConfig
153**/
154void KDFWidget::applySettings( void )
155{
156 KConfig m_config;
157 KConfigGroup config( &m_config, "KDiskFree" );
158
159 if( GUI )
160 {
161 Q_FOREACH(const Column &c, m_columnList){
162 if( !m_listWidget->isColumnHidden( c.number ) )
163 config.writeEntry( c.name, m_listWidget->columnWidth(c.number) );
164 }
165
166 config.writeEntry("SortColumn", m_sortModel->sortColumn());
167 config.writeEntry("SortOrder", (int)m_sortModel->sortOrder());
168
169 //Save the sort order of the QTreeView Header
170 QHeaderView * header = m_listWidget->header();
171 QList<int> sectionIndices;
172 for (int i = 0; i < header->count(); i++) {
173 sectionIndices.append(header->visualIndex(i));
174 }
175 config.writeEntry("HeaderSectionIndices", sectionIndices);
176 }
177 config.sync();
178 updateDF();
179}
180
181
182/***************************************************************************
183 * reads the KConfig
184**/
185void KDFWidget::loadSettings( void )
186{
187 mStd.updateConfiguration();
188
189 if(GUI)
190 {
191 KConfigGroup config(KGlobal::config(), "KDiskFree");
192 Q_FOREACH(const Column &c, m_columnList){
193 int width = config.readEntry( c.name, c.defaultWidth );
194 m_listWidget->setColumnWidth( c.number, width );
195 }
196
197 KConfigGroup config_visible(KGlobal::config(), "KDFConfig");
198 Q_FOREACH(const Column &c, m_columnList){
199 bool visible = config_visible.readEntry( c.name , true );
200 m_listWidget->setColumnHidden( c.number, !visible );
201 }
202
203 int sortColumn = config.readEntry("SortColumn",0);
204 int sortOrder = config.readEntry("SortOrder",(int)Qt::AscendingOrder);
205 m_listWidget->sortByColumn(sortColumn,Qt::SortOrder(sortOrder));
206
207 //Load the sort order of the QTreeView Header
208 //This can also be achieved by header->saveState() and header->restoreState(...),
209 //but this would not be "human-readable" any more...
210 QHeaderView * header = m_listWidget->header();
211 QList<int> sectionIndices;
212 sectionIndices = config.readEntry("HeaderSectionIndices",sectionIndices);
213 if (sectionIndices.count() == header->count()) {
214 for (int i = 0; i < header->count(); i++) {
215 int sectionIndex = sectionIndices.at(i);
216 int oldVisualIndex = header->visualIndex(sectionIndex);
217 header->moveSection(oldVisualIndex,i);
218 }
219 }
220
221 setUpdateFrequency( mStd.updateFrequency() );
222 updateDF();
223 }
224}
225
226
227/***************************************************************************
228 * pops up the SettingsBox if the settingsBtn is clicked
229**/
230void KDFWidget::settingsBtnClicked( void )
231{
232 if( mIsTopLevel == true )
233 {
234 if( mOptionDialog == 0 )
235 {
236 mOptionDialog = new COptionDialog( this );
237 if( mOptionDialog == 0 )
238 {
239 return;
240 }
241 connect( mOptionDialog, SIGNAL(valueChanged()),
242 this, SLOT(settingsChanged()) );
243 }
244 mOptionDialog->show();
245 }
246}
247
248
249/***************************************************************************
250 * resets the timer for automatic df-refreshes
251**/
252void KDFWidget::setUpdateFrequency( int frequency )
253{
254 //
255 // Kill current timer and restart it if the frequency is
256 // larger than zero.
257 //
258 QAbstractEventDispatcher::instance()->unregisterTimers(this);
259 if( frequency > 0 )
260 {
261 startTimer( frequency * 1000 );
262 }
263}
264
265/***************************************************************************
266 * Update (reread) all disk-dependencies
267**/
268void KDFWidget::timerEvent(QTimerEvent *)
269{
270 updateDF();
271}
272
273
274/***************************************************************************
275 * checks fstab & df
276**/
277void KDFWidget::updateDF( void )
278{
279 //
280 // We can only do this if the popupmenu is not present
281 //
282 if( mPopup == 0 )
283 {
284 readingDF = true;
285 mDiskList.readFSTAB();
286 mDiskList.readDF();
287 }
288}
289
290/***************************************************************************
291 * gets the signal when the diskList is complete and up to date
292**/
293void KDFWidget::updateDFDone( void ){
294 if (mPopup) //The popup menu is ont he screen... Don't touch the list view...
295 return;
296
297 //Clear the list items
298 m_listModel->removeRows(0,m_listModel->rowCount());
299
300 DisksConstIterator itr = mDiskList.disksConstIteratorBegin();
301 DisksConstIterator end = mDiskList.disksConstIteratorEnd();
302 for (; itr != end; ++itr)
303 {
304 DiskEntry * disk = *itr;
305
306 QString size,percent;
307 if( disk->kBSize() > 0 )
308 {
309 percent = KGlobal::locale()->formatNumber(disk->percentFull(), 1) + QLatin1Char( '%' );
310 size = disk->prettyKBSize();
311 }
312 else
313 {
314 percent = i18n("N/A");
315 size = i18n("N/A");
316 }
317
318 bool root = !disk->mountOptions().contains(QLatin1String( "user" ), Qt::CaseInsensitive);
319
320 QStandardItem * IconItem = new QStandardItem( generateIcon(disk->iconName(), root, disk->mounted() ), QLatin1String( "" ) );
321
322 QStandardItem * DeviceItem = new QStandardItem( disk->deviceName() );
323
324 QStandardItem * TypeItem = new QStandardItem( disk->fsType() );
325
326 QStandardItem * SizeItem = new QStandardItem( size );
327 SizeItem->setData( disk->kBSize(), Qt::UserRole );
328
329 QStandardItem * MountPointItem = new QStandardItem( disk->mountPoint() );
330
331 QStandardItem * FreeItem = new QStandardItem( disk->prettyKBAvail() );
332 FreeItem->setData( disk->kBAvail(), Qt::UserRole );
333
334 QStandardItem * FullItem = new QStandardItem( percent );
335 FullItem->setData( disk->percentFull() , Qt::UserRole );
336
337 QStandardItem * UsageBarItem = new QStandardItem( QLatin1String( "" ) );
338 UsageBarItem->setData( disk->percentFull(), Qt::UserRole );
339
340 m_listModel->appendRow( QList<QStandardItem*>() << IconItem << DeviceItem << TypeItem << SizeItem << MountPointItem <<
341 FreeItem << FullItem << UsageBarItem);
342 }
343
344 readingDF = false;
345
346 m_listModel->sort( DeviceCol );
347
348}
349
350QIcon KDFWidget::generateIcon( QString iconName, bool mode, bool mounted)
351{
352 QPixmap pix = SmallIcon(iconName);
353
354 QPainter painter(&pix);
355
356 if( mode )
357 painter.drawPixmap( QRect(0,6,10,10), SmallIcon(QLatin1String( "object-locked" )) );
358
359 if( mounted )
360 painter.drawPixmap( QRect(6,6,12,12) , SmallIcon(QLatin1String( "emblem-mounted" )) );
361
362 painter.end();
363 return QIcon(pix);
364
365}
366
367void KDFWidget::criticallyFull( DiskEntry *disk )
368{
369 if( mStd.popupIfFull() == true )
370 {
371 QString msg = i18n("Device [%1] on [%2] is critically full.",
372 disk->deviceName(), disk->mountPoint());
373 KMessageBox::sorry( this, msg, i18nc("Warning device getting critically full", "Warning"));
374 }
375}
376
377DiskEntry * KDFWidget::selectedDisk( QModelIndex index )
378{
379 if( !index.isValid() )
380 return 0;
381
382 QStandardItem * itemDevice = m_listModel->item( index.row() , DeviceCol );
383 QStandardItem * itemMountPoint = m_listModel->item( index.row() , MountPointCol );
384
385 DiskEntry * disk = new DiskEntry( itemDevice->text() );
386 disk->setMountPoint( itemMountPoint->text() );
387
388 int pos = mDiskList.find( disk );
389
390 delete disk;
391 return mDiskList.at(pos);
392
393}
394
395void KDFWidget::contextMenuRequested( const QPoint &p )
396{
397 if (mPopup) //The user may even be able to popup another menu while this open is active...
398 return;
399
400 QModelIndex index = m_listWidget->indexAt( p );
401
402 if( !index.isValid() )
403 {
404 QList<QModelIndex> selected = m_listWidget->selectionModel()->selectedIndexes();
405 if ( selected.size() > 0 )
406 index = selected.at(0);
407 else
408 return;
409 }
410
411 index = m_sortModel->mapToSource( index );
412
413 mDiskList.setUpdatesDisabled(true);
414 DiskEntry * disk = selectedDisk( index );
415
416 if( disk == 0 )
417 return;
418
419 mPopup = new KMenu( 0 );
420 mPopup->addTitle( disk->mountPoint() );
421 QAction *mountPointAction = mPopup->addAction( i18n("Mount Device") );
422 QAction *umountPointAction = mPopup->addAction( i18n("Unmount Device") );
423 mPopup->addSeparator();
424 QAction *openFileManagerAction = mPopup->addAction( i18n("Open in File Manager") );
425 mountPointAction->setEnabled( !disk->mounted() );
426 umountPointAction->setEnabled( disk->mounted() );
427 openFileManagerAction->setEnabled( disk->mounted() );
428 QAction *position = mPopup->exec( m_listWidget->mapToGlobal(p) );
429
430 bool openFileManager = false;
431 if( !position )
432 {
433 mDiskList.setUpdatesDisabled(false);
434 delete mPopup;
435 mPopup = 0;
436 return;
437 }
438 else if( position == mountPointAction || position == umountPointAction )
439 {
440 QStandardItem * SizeItem = m_listModel->item( index.row() , SizeCol );
441 SizeItem->setText( i18n("MOUNTING") );
442
443 QStandardItem * FreeItem = m_listModel->item( index.row() , FreeCol );
444 FreeItem->setText( i18n("MOUNTING") );
445
446 QStandardItem * IconItem = m_listModel->item( index.row() , IconCol );
447 IconItem->setIcon( SmallIcon(QLatin1String( "user-away" )) );
448
449 int val = disk->toggleMount();
450 if( val != 0 /*== false*/ )
451 {
452 KMessageBox::error( this, disk->lastSysError() );
453 }
454 else if ( ( mStd.openFileManager() == true)
455 && (position == mountPointAction) ) //only on mount
456 {
457 openFileManager = true;
458 }
459
460 //delete item;
461 mDiskList.deleteAllMountedAt(disk->mountPoint());
462 }
463 else if( position == openFileManagerAction )
464 {
465 openFileManager = true;
466 }
467
468 if( openFileManager == true )
469 {
470 kDebug() << "opening filemanager" ;
471 if( mStd.fileManager().isEmpty() == false )
472 {
473 QString cmd = mStd.fileManager();
474 int pos = cmd.indexOf(QLatin1String( "%m" ));
475 if( pos > 0 )
476 {
477 cmd = cmd.replace( pos, 2, KShell::quoteArg(disk->mountPoint()) ) + QLatin1String( " &" );
478 }
479 else
480 {
481 cmd += QLatin1Char( ' ' ) + KShell::quoteArg(disk->mountPoint()) + QLatin1String( " &" );
482 }
483 system( QFile::encodeName(cmd) );
484 }
485 }
486
487 //Update only here as showing of error message triggers event loop.
488 mDiskList.setUpdatesDisabled(false);
489 delete mPopup;
490 mPopup = 0;
491
492 if( position != openFileManagerAction ) // No need to update when just opening the fm.
493 {
494 updateDF();
495 }
496
497}
498
499void KDFWidget::invokeHelp()
500{
501 KToolInvocation::invokeHelp(QLatin1String( "" ), QLatin1String( "kcontrol/kdf" ));
502}
503
504#include "kdfwidget.moc"
505