File indexing completed on 2022-11-29 20:24:04

0001 /*
0002     SPDX-FileCopyrightText: 1998-2001 Michael Kropfberger <michael.kropfberger@gmx.net>
0003     SPDX-FileCopyrightText: 2009 Dario Andres Rodriguez <andresbajotierra@gmail.com>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 //
0009 // 1999-11-29 Espen Sand
0010 // Converted to QLayout and QListView + cleanups
0011 // 1999-12-05 Espen Sand
0012 // Usage bars should work again.
0013 //
0014 
0015 #include "kdfwidget.h"
0016 
0017 #include "kdfprivate_debug.h"
0018 #include "optiondialog.h"
0019 
0020 #include <KMessageBox>
0021 #include <KShell>
0022 #include <KHelpClient>
0023 #include <KConfigGroup>
0024 
0025 #include <QAbstractEventDispatcher>
0026 #include <QTimer>
0027 #include <QLayout>
0028 #include <QTreeView>
0029 #include <QStandardItemModel>
0030 #include <QStandardItem>
0031 #include <QPainter>
0032 #include <QMenu>
0033 #include <QProcess>
0034 #include <QDesktopServices>
0035 #include <QUrl>
0036 
0037 //This aren't used here...
0038 //#define BAR_COLUMN   7
0039 //#define FULL_PERCENT 95.0
0040 
0041 #ifndef GUI_DEFINED
0042 static bool GUI;
0043 #define GUI_DEFINED
0044 #endif
0045 
0046 KDFWidget::KDFWidget( QWidget *parent, bool init )
0047         : QWidget(parent)
0048 {
0049     connect(&mDiskList , &DiskList::readDFDone,
0050             this, &KDFWidget::updateDFDone );
0051     connect(&mDiskList , &DiskList::criticallyFull,
0052             this, &KDFWidget::criticallyFull );
0053 
0054     m_columnList.append( Column( QStringLiteral( "Icon" ), QLatin1String( "" ), 20, IconCol ));
0055     m_columnList.append( Column( QStringLiteral( "Device" ), i18nc("Device of the storage", "Device"), 100, DeviceCol ));
0056     m_columnList.append( Column( QStringLiteral( "Type" ), i18nc("Filesystem on storage", "Type"), 80, TypeCol ));
0057     m_columnList.append( Column( QStringLiteral( "Size" ), i18nc("Total size of the storage", "Size"), 80, SizeCol ));
0058     m_columnList.append( Column( QStringLiteral( "MountPoint" ), i18nc("Mount point of storage", "Mount Point"), 120, MountPointCol ));
0059     m_columnList.append( Column( QStringLiteral( "Free" ), i18nc("Free space in storage", "Free"), 80, FreeCol ));
0060     m_columnList.append( Column( QStringLiteral( "Full%" ), i18nc("Used storage space in %", "Full %"), 50, FullCol ));
0061     m_columnList.append( Column( QStringLiteral( "UsageBar" ), i18nc("Usage graphical bar", "Usage"), 200, UsageBarCol ));
0062 
0063     GUI = !init;
0064     if( GUI )
0065     {
0066         QVBoxLayout *topLayout = new QVBoxLayout( this );
0067         topLayout->setSpacing( 0 );
0068         topLayout->setContentsMargins(0, 0, 0, 0);
0069 
0070         m_listModel = new QStandardItemModel( this );
0071         m_sortModel = new KDFSortFilterProxyModel( this );
0072         m_sortModel->setSourceModel( m_listModel );
0073 
0074         m_listWidget = new QTreeView( this );
0075         m_listWidget->setModel( m_sortModel );
0076 
0077         m_itemDelegate = new KDFItemDelegate( m_listWidget );
0078 
0079         m_listWidget->setItemDelegate( m_itemDelegate );
0080         m_listWidget->setRootIsDecorated( false );
0081         m_listWidget->setSortingEnabled( true );
0082         m_listWidget->setContextMenuPolicy( Qt::CustomContextMenu );
0083         m_listWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
0084 
0085         topLayout->addWidget( m_listWidget );
0086 
0087         connect( m_listWidget, &QWidget::customContextMenuRequested,
0088                  this, &KDFWidget::contextMenuRequested);
0089 
0090         makeColumns();
0091 
0092         mIsTopLevel = QLatin1String(parent->metaObject()->className()) == QLatin1String( "KDFTopLevel" ) ? true : false;
0093     }
0094 
0095     loadSettings();
0096     if( init )
0097         applySettings();
0098 }
0099 
0100 KDFWidget::~KDFWidget()
0101 {
0102     delete m_listModel;
0103     delete m_sortModel;
0104     delete m_itemDelegate;
0105     delete m_listWidget;
0106 }
0107 
0108 void KDFWidget::makeColumns( void )
0109 {
0110 
0111     QStringList columns;
0112     for (const Column &c : std::as_const(m_columnList)){
0113         columns << c.columnName;
0114     }
0115     m_listModel->setHorizontalHeaderLabels( columns );
0116 
0117 }
0118 
0119 /******************************************************************/
0120 void KDFWidget::closeEvent(QCloseEvent *)
0121 {
0122     applySettings();
0123     qApp->quit();
0124 }
0125 
0126 
0127 void KDFWidget::settingsChanged( void )
0128 {
0129     applySettings();
0130     loadSettings();
0131 }
0132 
0133 
0134 /***************************************************************************
0135   * writes the KConfig
0136 **/
0137 void KDFWidget::applySettings( void )
0138 {
0139     KConfig m_config;
0140     KConfigGroup config( &m_config, "KDiskFree" );
0141 
0142     if( GUI )
0143     {
0144         for (const Column &c : std::as_const(m_columnList)){
0145             if( !m_listWidget->isColumnHidden( c.number ) )
0146                 config.writeEntry( c.name, m_listWidget->columnWidth(c.number) );
0147         }
0148 
0149         config.writeEntry("SortColumn", m_sortModel->sortColumn());
0150         config.writeEntry("SortOrder", (int)m_sortModel->sortOrder());
0151 
0152         //Save the sort order of the QTreeView Header
0153         QHeaderView * header = m_listWidget->header();
0154         QList<int> sectionIndices;
0155         for (int i = 0; i < header->count(); i++) {
0156             sectionIndices.append(header->visualIndex(i));
0157         }
0158         config.writeEntry("HeaderSectionIndices", sectionIndices);
0159     }
0160     config.sync();
0161     updateDF();
0162 }
0163 
0164 
0165 /***************************************************************************
0166   * reads the KConfig
0167 **/
0168 void KDFWidget::loadSettings( void )
0169 {
0170     mStd.updateConfiguration();
0171 
0172     if(GUI)
0173     {
0174         KConfigGroup config(KSharedConfig::openConfig(), "KDiskFree");
0175         for (const Column &c : std::as_const(m_columnList)){
0176             int width = config.readEntry( c.name, c.defaultWidth );
0177             m_listWidget->setColumnWidth( c.number, width );
0178         }
0179 
0180         KConfigGroup config_visible(KSharedConfig::openConfig(), "KDFConfig");
0181         for (const Column &c : std::as_const(m_columnList)){
0182             bool visible = config_visible.readEntry( c.name , true );
0183             m_listWidget->setColumnHidden( c.number, !visible );
0184         }
0185 
0186         int sortColumn = config.readEntry("SortColumn",0);
0187         int sortOrder = config.readEntry("SortOrder",(int)Qt::AscendingOrder);
0188         m_listWidget->sortByColumn(sortColumn,Qt::SortOrder(sortOrder));
0189 
0190         //Load the sort order of the QTreeView Header
0191         //This can also be achieved by header->saveState() and header->restoreState(...),
0192         //but this would not be "human-readable" any more...
0193         QHeaderView * header = m_listWidget->header();
0194         QList<int> sectionIndices;
0195         sectionIndices = config.readEntry("HeaderSectionIndices",sectionIndices);
0196         if (sectionIndices.count() == header->count()) {
0197             for (int i = 0; i < header->count(); i++) {
0198                 int sectionIndex = sectionIndices.at(i);
0199                 int oldVisualIndex = header->visualIndex(sectionIndex);
0200                 header->moveSection(oldVisualIndex,i);
0201             }
0202         }
0203 
0204         setUpdateFrequency( mStd.updateFrequency() );
0205         updateDF();
0206     }
0207 }
0208 
0209 
0210 /***************************************************************************
0211   * pops up the SettingsBox if the settingsBtn is clicked
0212 **/
0213 void KDFWidget::settingsBtnClicked( void )
0214 {
0215     if( mIsTopLevel )
0216     {
0217         if( mOptionDialog == nullptr )
0218         {
0219             mOptionDialog = new COptionDialog( this );
0220             if( mOptionDialog == nullptr )
0221             {
0222                 return;
0223             }
0224             connect( mOptionDialog, &COptionDialog::valueChanged,
0225                      this, &KDFWidget::settingsChanged );
0226         }
0227         mOptionDialog->show();
0228     }
0229 }
0230 
0231 
0232 /***************************************************************************
0233   * resets the timer for automatic df-refreshes
0234 **/
0235 void KDFWidget::setUpdateFrequency( int frequency )
0236 {
0237     //
0238     // Kill current timer and restart it if the frequency is
0239     // larger than zero.
0240     //
0241     QAbstractEventDispatcher::instance()->unregisterTimers(this);
0242     if( frequency > 0 )
0243     {
0244         startTimer( frequency * 1000 );
0245     }
0246 }
0247 
0248 /***************************************************************************
0249   * Update (reread) all disk-dependencies
0250 **/
0251 void KDFWidget::timerEvent(QTimerEvent *)
0252 {
0253     updateDF();
0254 }
0255 
0256 
0257 /***************************************************************************
0258   * checks fstab & df
0259 **/
0260 void KDFWidget::updateDF( void )
0261 {
0262     //
0263     // We can only do this if the popupmenu is not present
0264     //
0265     if( mPopup == nullptr )
0266     {
0267         readingDF = true;
0268         mDiskList.readFSTAB();
0269         mDiskList.readDF();
0270     }
0271 }
0272 
0273 /***************************************************************************
0274   * gets the signal when the diskList is complete and up to date
0275 **/
0276 void KDFWidget::updateDFDone( void ){
0277     if (mPopup) //The popup menu is ont he screen... Don't touch the list view...
0278         return;
0279 
0280     //Clear the list items
0281     m_listModel->removeRows(0,m_listModel->rowCount());
0282 
0283     DisksConstIterator itr = mDiskList.disksConstIteratorBegin();
0284     DisksConstIterator end = mDiskList.disksConstIteratorEnd();
0285     for (; itr != end; ++itr)
0286     {
0287         DiskEntry * disk = *itr;
0288 
0289         QString size,percent;
0290         if( disk->kBSize() > 0 )
0291         {
0292             percent = i18nc("Disk percentage", "%1%", QString::number(disk->percentFull()));
0293             size = disk->prettyKBSize();
0294         }
0295         else
0296         {
0297             percent = i18n("N/A");
0298             size = i18n("N/A");
0299         }
0300 
0301         bool root = !disk->mountOptions().contains(QLatin1String( "user" ), Qt::CaseInsensitive);
0302 
0303         QStandardItem * IconItem = new QStandardItem( generateIcon(disk->iconName(), root, disk->mounted() ), QLatin1String( "" ) );
0304 
0305         QStandardItem * DeviceItem = new QStandardItem( disk->deviceName() );
0306 
0307         QStandardItem * TypeItem = new QStandardItem( disk->fsType() );
0308 
0309         QStandardItem * SizeItem = new QStandardItem( size );
0310         SizeItem->setData( disk->kBSize(), Qt::UserRole );
0311 
0312         QStandardItem * MountPointItem = new QStandardItem( disk->mountPoint() );
0313 
0314         QStandardItem * FreeItem = new QStandardItem( disk->prettyKBAvail() );
0315         FreeItem->setData( disk->kBAvail(), Qt::UserRole );
0316 
0317         QStandardItem * FullItem = new QStandardItem( percent );
0318         FullItem->setData( disk->percentFull() , Qt::UserRole );
0319 
0320         QStandardItem * UsageBarItem = new QStandardItem( QLatin1String( "" ) );
0321         UsageBarItem->setData( disk->percentFull(), Qt::UserRole );
0322 
0323         m_listModel->appendRow( QList<QStandardItem*>() << IconItem <<  DeviceItem << TypeItem << SizeItem << MountPointItem <<
0324           FreeItem << FullItem << UsageBarItem);
0325     }
0326 
0327     readingDF = false;
0328 
0329     m_listModel->sort( DeviceCol );
0330 
0331 }
0332 
0333 QIcon KDFWidget::generateIcon( const QString &iconName, bool mode, bool mounted)
0334 {
0335     const int smallIcon = qApp->style()->pixelMetric(QStyle::PM_SmallIconSize);
0336     QPixmap pix = QIcon::fromTheme( iconName ).pixmap(smallIcon);
0337 
0338     QPainter painter(&pix);
0339 
0340     if( mode )
0341         painter.drawPixmap( QRect(0,6,10,10), QIcon::fromTheme(QStringLiteral( "object-locked" )).pixmap(smallIcon) );
0342 
0343     if( mounted )
0344         painter.drawPixmap( QRect(6,6,12,12) , QIcon::fromTheme(QStringLiteral( "emblem-mounted" )).pixmap(smallIcon) );
0345 
0346     painter.end();
0347     return QIcon(pix);
0348 
0349 }
0350 
0351 void KDFWidget::criticallyFull( DiskEntry *disk )
0352 {
0353     if( mStd.popupIfFull() )
0354     {
0355         QString msg = i18n("Device [%1] on [%2] is critically full.",
0356                            disk->deviceName(), disk->mountPoint());
0357         KMessageBox::error( this, msg, i18nc("Warning device getting critically full", "Warning"));
0358     }
0359 }
0360 
0361 DiskEntry * KDFWidget::selectedDisk( const QModelIndex &index )
0362 {
0363     if( !index.isValid() )
0364         return nullptr;
0365 
0366     QStandardItem * itemDevice = m_listModel->item( index.row() , DeviceCol );
0367     QStandardItem * itemMountPoint = m_listModel->item( index.row() , MountPointCol );
0368 
0369     DiskEntry * disk = new DiskEntry( itemDevice->text() );
0370     disk->setMountPoint( itemMountPoint->text() );
0371 
0372     int pos = mDiskList.find( disk );
0373 
0374     delete disk;
0375     return mDiskList.at(pos);
0376 
0377 }
0378 
0379 void KDFWidget::contextMenuRequested( const QPoint &p )
0380 {
0381     if (mPopup) //The user may even be able to popup another menu while this open is active...
0382         return;
0383 
0384     QModelIndex index = m_listWidget->indexAt( p );
0385 
0386     if( !index.isValid() )
0387     {
0388         QList<QModelIndex> selected = m_listWidget->selectionModel()->selectedIndexes();
0389         if ( selected.size() > 0 )
0390             index = selected.at(0);
0391         else
0392             return;
0393     }
0394 
0395     index = m_sortModel->mapToSource( index );
0396 
0397     mDiskList.setUpdatesDisabled(true);
0398     DiskEntry * disk = selectedDisk( index );
0399 
0400     if( disk == nullptr )
0401         return;
0402 
0403     mPopup = new QMenu( nullptr );
0404     mPopup->setTitle( disk->mountPoint() );
0405     QAction *mountPointAction = mPopup->addAction( i18n("Mount Device") );
0406     QAction *umountPointAction = mPopup->addAction( i18n("Unmount Device") );
0407     mPopup->addSeparator();
0408     QAction *openFileManagerAction = mPopup->addAction( i18n("Open in File Manager") );
0409     mountPointAction->setEnabled( !disk->mounted() );
0410     umountPointAction->setEnabled( disk->mounted() );
0411     openFileManagerAction->setEnabled( disk->mounted() );
0412     QAction *position = mPopup->exec( m_listWidget->mapToGlobal(p) );
0413 
0414     bool openFileManager = false;
0415     if( !position )
0416     {
0417         mDiskList.setUpdatesDisabled(false);
0418         delete mPopup;
0419         mPopup = nullptr;
0420         return;
0421     }
0422     else if( position == mountPointAction || position == umountPointAction )
0423     {
0424         QStandardItem * SizeItem = m_listModel->item( index.row() , SizeCol );
0425         SizeItem->setText( i18n("MOUNTING") );
0426 
0427         QStandardItem * FreeItem = m_listModel->item( index.row() , FreeCol );
0428         FreeItem->setText( i18n("MOUNTING") );
0429 
0430         QStandardItem * IconItem = m_listModel->item( index.row() , IconCol );
0431         IconItem->setIcon( QIcon::fromTheme(QStringLiteral( "user-away" )) );
0432 
0433         int val = disk->toggleMount();
0434         if( val != 0 /*== false*/ )
0435         {
0436             KMessageBox::error( this, disk->lastSysError() );
0437         }
0438         else if ( mStd.openFileManager()
0439                   && (position == mountPointAction) ) //only on mount
0440         {
0441             openFileManager = true;
0442         }
0443 
0444         //delete item;
0445         mDiskList.deleteAllMountedAt(disk->mountPoint());
0446     }
0447     else if( position == openFileManagerAction )
0448     {
0449         openFileManager = true;
0450     }
0451 
0452     if( openFileManager )
0453     {
0454         qCDebug(KDF) << "opening filemanager";
0455         if( mStd.useSystemFileManager() )
0456         {
0457             QDesktopServices::openUrl( QUrl::fromLocalFile( disk->mountPoint() ) );
0458         }
0459         else if( !mStd.fileManager().isEmpty() )
0460         {
0461             QString cmd = mStd.fileManager();
0462             int pos = cmd.indexOf(QLatin1String( "%m" ));
0463             if( pos > 0 )
0464             {
0465                 cmd.replace(pos, 2, KShell::quoteArg(disk->mountPoint()));
0466             }
0467             else
0468             {
0469                 cmd += QLatin1Char( ' ' ) + KShell::quoteArg(disk->mountPoint());
0470             }
0471             QStringList argList = cmd.split(QLatin1Char( ' ' ), Qt::SkipEmptyParts);
0472             cmd = argList.takeFirst();
0473             QProcess::startDetached(cmd, argList);
0474         }
0475     }
0476 
0477     //Update only here as showing of error message triggers event loop.
0478     mDiskList.setUpdatesDisabled(false);
0479     delete mPopup;
0480     mPopup = nullptr;
0481 
0482     if( position != openFileManagerAction ) // No need to update when just opening the fm.
0483     {
0484         updateDF();
0485     }
0486 
0487 }
0488 
0489 void KDFWidget::invokeHelp()
0490 {
0491     KHelpClient::invokeHelp(QLatin1String( "" ), QStringLiteral( "kcontrol/kdf" ));
0492 }
0493