File indexing completed on 2025-01-05 03:59:54

0001 /* ============================================================
0002  *
0003  * This file is a part of digiKam project
0004  * https://www.digikam.org
0005  *
0006  * Date        : 2007-08-31
0007  * Description : a widget to display free space for a mount-point.
0008  *
0009  * SPDX-FileCopyrightText: 2007-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
0010  *
0011  * SPDX-License-Identifier: GPL-2.0-or-later
0012  *
0013  * ============================================================ */
0014 
0015 #include "freespacewidget.h"
0016 
0017 // C++ includes
0018 
0019 #include <cmath>
0020 
0021 // Qt includes
0022 
0023 #include <QPainter>
0024 #include <QPixmap>
0025 #include <QPalette>
0026 #include <QColor>
0027 #include <QTimer>
0028 #include <QFont>
0029 #include <QBoxLayout>
0030 #include <QFontMetrics>
0031 #include <QUrl>
0032 #include <QIcon>
0033 #include <QStyle>
0034 #include <QStorageInfo>
0035 
0036 // KDE includes
0037 
0038 #include <klocalizedstring.h>
0039 
0040 // Local includes
0041 
0042 #include "digikam_debug.h"
0043 #include "freespacetooltip.h"
0044 #include "applicationsettings.h"
0045 #include "itempropertiestab.h"
0046 
0047 namespace Digikam
0048 {
0049 
0050 class Q_DECL_HIDDEN MountPointInfo
0051 {
0052 public:
0053 
0054     MountPointInfo()
0055       : isValid    (false),
0056         bytesSize  (0),
0057         bytesUsed  (0),
0058         bytesAvail (0)
0059     {
0060     }
0061 
0062     bool    isValid;
0063 
0064     qint64  bytesSize;
0065     qint64  bytesUsed;
0066     qint64  bytesAvail;
0067 
0068     QString mountPoint;
0069 };
0070 
0071 // ---------------------------------------------------------------------------------
0072 
0073 class Q_DECL_HIDDEN FreeSpaceWidget::Private
0074 {
0075 public:
0076 
0077     explicit Private()
0078       : isValid     (false),
0079         percentUsed (-1),
0080         dSizeBytes  (0),
0081         bytesSize   (0),
0082         bytesUsed   (0),
0083         bytesAvail  (0),
0084         timer       (nullptr),
0085         toolTip     (nullptr),
0086         mode        (FreeSpaceWidget::AlbumLibrary)
0087     {
0088     }
0089 
0090     bool                            isValid;
0091 
0092     int                             percentUsed;
0093 
0094     qint64                          dSizeBytes;
0095     qint64                          bytesSize;
0096     qint64                          bytesUsed;
0097     qint64                          bytesAvail;
0098 
0099     QStringList                     paths;
0100     QHash<QString, MountPointInfo>  infos;
0101 
0102     QTimer*                         timer;
0103 
0104     QPixmap                         iconPix;
0105 
0106     FreeSpaceToolTip*               toolTip;
0107 
0108     FreeSpaceWidget::FreeSpaceMode  mode;
0109 };
0110 
0111 FreeSpaceWidget::FreeSpaceWidget(QWidget* const parent, int width)
0112     : QWidget(parent),
0113       d      (new Private)
0114 {
0115     setAttribute(Qt::WA_DeleteOnClose);
0116     setFixedWidth(width);
0117     setMaximumHeight(fontMetrics().height() + 4);
0118     d->timer   = new QTimer(this);
0119     d->toolTip = new FreeSpaceToolTip(this);
0120 
0121     connect(d->timer, SIGNAL(timeout()),
0122             this, SLOT(slotTimeout()));
0123 }
0124 
0125 FreeSpaceWidget::~FreeSpaceWidget()
0126 {
0127     d->timer->stop();
0128     delete d->timer;
0129     delete d->toolTip;
0130     delete d;
0131 }
0132 
0133 void FreeSpaceWidget::setMode(FreeSpaceMode mode)
0134 {
0135     d->mode = mode;
0136 
0137     if (d->mode == FreeSpaceWidget::AlbumLibrary)
0138     {
0139         d->iconPix = QIcon::fromTheme(QLatin1String("folder-pictures")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize));
0140     }
0141     else
0142     {
0143         d->iconPix = QIcon::fromTheme(QLatin1String("camera-photo")).pixmap(style()->pixelMetric(QStyle::PM_SmallIconSize));
0144     }
0145 
0146     update();
0147 }
0148 
0149 void FreeSpaceWidget::setPath(const QString& path)
0150 {
0151     d->paths.clear();
0152     d->paths << path;
0153     refresh();
0154 }
0155 
0156 void FreeSpaceWidget::setPaths(const QStringList& paths)
0157 {
0158     d->paths = paths;
0159     refresh();
0160 }
0161 
0162 void FreeSpaceWidget::refresh()
0163 {
0164     d->timer->stop();
0165     slotTimeout();
0166     d->timer->start(10000);
0167 }
0168 
0169 void FreeSpaceWidget::addInformation(qint64 bytesSize,
0170                                      qint64 bytesUsed,
0171                                      qint64 bytesAvail,
0172                                      const QString& mountPoint)
0173 {
0174     MountPointInfo inf;
0175 
0176     inf.mountPoint = mountPoint;
0177     inf.bytesSize  = bytesSize;
0178     inf.bytesUsed  = bytesUsed;
0179     inf.bytesAvail = bytesAvail;
0180     inf.isValid    = (bytesSize > 0);
0181 
0182     d->infos[mountPoint] = inf;
0183 
0184     // update cumulative data
0185 
0186     d->bytesSize   = 0;
0187     d->bytesUsed   = 0;
0188     d->bytesAvail  = 0;
0189     d->isValid     = false;
0190     d->percentUsed = -1;
0191 
0192     Q_FOREACH (const MountPointInfo& info, d->infos)
0193     {
0194         if (info.isValid)
0195         {
0196             d->bytesSize  += info.bytesSize;
0197             d->bytesUsed  += info.bytesUsed;
0198             d->bytesAvail += info.bytesAvail;
0199             d->isValid     = true;
0200         }
0201     }
0202 
0203     if (bytesSize > 0)
0204     {
0205         d->percentUsed = lround(100.0 - (100.0 * bytesAvail / bytesSize));
0206     }
0207 
0208     updateToolTip();
0209     update();
0210 }
0211 
0212 void FreeSpaceWidget::setEstimatedDSizeBytes(qint64 dSize)
0213 {
0214     d->dSizeBytes = dSize;
0215 
0216     updateToolTip();
0217     update();
0218 }
0219 
0220 qint64 FreeSpaceWidget::estimatedDSizeBytes() const
0221 {
0222     return d->dSizeBytes;
0223 }
0224 
0225 bool FreeSpaceWidget::isValid() const
0226 {
0227     return d->isValid;
0228 }
0229 
0230 int FreeSpaceWidget::percentUsed() const
0231 {
0232     return d->percentUsed;
0233 }
0234 
0235 qint64 FreeSpaceWidget::bytesSize() const
0236 {
0237     return d->bytesSize;
0238 }
0239 
0240 qint64 FreeSpaceWidget::bytesUsed() const
0241 {
0242     return d->bytesUsed;
0243 }
0244 
0245 qint64 FreeSpaceWidget::bytesAvail() const
0246 {
0247     return d->bytesAvail;
0248 }
0249 
0250 qint64 FreeSpaceWidget::bytesAvail(const QString& path) const
0251 {
0252     int mountPointMatch = 0;
0253     MountPointInfo selectedInfo;
0254 
0255     Q_FOREACH (const MountPointInfo& info, d->infos)
0256     {
0257         if (info.isValid && !info.mountPoint.isEmpty() && path.startsWith(info.mountPoint))
0258         {
0259             int length = info.mountPoint.length();
0260 
0261             if (length > mountPointMatch)
0262             {
0263                 mountPointMatch = info.mountPoint.length();
0264                 selectedInfo    = info;
0265             }
0266         }
0267     }
0268 
0269     if (!mountPointMatch)
0270     {
0271         qCWarning(DIGIKAM_IMPORTUI_LOG) << "Did not identify a valid mount point for" << path;
0272 
0273         return -1;
0274     }
0275 
0276     return selectedInfo.bytesAvail;
0277 }
0278 
0279 void FreeSpaceWidget::paintEvent(QPaintEvent*)
0280 {
0281     QPainter p(this);
0282 
0283     p.setPen(palette().mid().color());
0284     p.drawRect(0, 0, width() - 1, height() - 1);
0285     p.drawPixmap(2, height() / 2 - d->iconPix.height() / 2,
0286                  d->iconPix, 0, 0, d->iconPix.width(), d->iconPix.height());
0287 
0288     if (isValid())
0289     {
0290         // We will compute the estimated % of space size used to download and process.
0291 
0292         qint64 eUsedBytes = d->dSizeBytes + d->bytesUsed;
0293         int peUsed        = (int)(100.0 * ((double)eUsedBytes / (double)d->bytesSize));
0294         int pClamp        = (peUsed > 100) ? 100 : peUsed;
0295         QColor barcol     = QColor(62, 255, 62);       // Smooth Green.
0296 
0297         if (peUsed > 80)
0298         {
0299             barcol = QColor(240, 255, 62);             // Smooth Yellow.
0300         }
0301 
0302         if (peUsed > 95)
0303         {
0304             barcol = QColor(255, 62, 62);              // Smooth Red.
0305         }
0306 
0307         p.setBrush(barcol);
0308         p.setPen(palette().light().color());
0309         QRect gRect(d->iconPix.height() + 3, 2,
0310                     (int)(((double)width() - 3.0 - d->iconPix.width() - 2.0) * (pClamp / 100.0)),
0311                     height() - 5);
0312         p.drawRect(gRect);
0313 
0314         QRect tRect(d->iconPix.height() + 3, 2, width() - 3 - d->iconPix.width() - 2, height() - 5);
0315         QString text        = QString::fromUtf8("%1%").arg(peUsed);
0316         QFontMetrics fontMt = p.fontMetrics();
0317 /*
0318         QRect fontRect      = fontMt.boundingRect(tRect.x(), tRect.y(),
0319                                                   tRect.width(), tRect.height(), 0, text);
0320 */
0321         p.setPen(Qt::black);
0322         p.drawText(tRect, Qt::AlignCenter, text);
0323     }
0324 }
0325 
0326 void FreeSpaceWidget::updateToolTip()
0327 {
0328     if (isValid())
0329     {
0330         QString value;
0331         QString header = i18nc("@title", "Camera Media");
0332 
0333         if (d->mode == FreeSpaceWidget::AlbumLibrary)
0334         {
0335             header = i18nc("@title", "Album Library");
0336         }
0337 
0338         DToolTipStyleSheet cnt(ApplicationSettings::instance()->getToolTipsFont());
0339         QString tip = cnt.tipHeader;
0340 
0341         tip        += cnt.headBeg + header + cnt.headEnd;
0342 
0343         if (d->dSizeBytes > 0)
0344         {
0345             tip += cnt.cellBeg + i18nc("@info Storage", "Capacity:") + cnt.cellMid;
0346             tip += ItemPropertiesTab::humanReadableBytesCount(d->bytesSize) + cnt.cellEnd;
0347 
0348             tip += cnt.cellBeg + i18nc("@info Storage", "Available:") + cnt.cellMid;
0349             tip += ItemPropertiesTab::humanReadableBytesCount(d->bytesAvail) + cnt.cellEnd;
0350 
0351             tip += cnt.cellBeg + i18nc("@info Storage", "Require:") + cnt.cellMid;
0352             tip += ItemPropertiesTab::humanReadableBytesCount(d->dSizeBytes) + cnt.cellEnd;
0353         }
0354         else
0355         {
0356             tip += cnt.cellBeg + i18nc("@info Storage", "Capacity:") + cnt.cellMid;
0357             tip += ItemPropertiesTab::humanReadableBytesCount(d->bytesSize) + cnt.cellEnd;
0358 
0359             tip += cnt.cellBeg + i18nc("@info Storage", "Available:") + cnt.cellMid;
0360             tip += ItemPropertiesTab::humanReadableBytesCount(d->bytesAvail) + cnt.cellEnd;
0361         }
0362 
0363         tip += cnt.tipFooter;
0364 
0365         d->toolTip->setToolTip(tip);
0366     }
0367     else
0368     {
0369         d->toolTip->setToolTip(QString());
0370     }
0371 }
0372 
0373 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
0374 
0375 void FreeSpaceWidget::enterEvent(QEnterEvent* e)
0376 
0377 #else
0378 
0379 void FreeSpaceWidget::enterEvent(QEvent* e)
0380 
0381 #endif
0382 
0383 {
0384     Q_UNUSED(e)
0385     d->toolTip->show();
0386 }
0387 
0388 void FreeSpaceWidget::leaveEvent(QEvent* e)
0389 {
0390     Q_UNUSED(e)
0391     d->toolTip->hide();
0392 }
0393 
0394 void FreeSpaceWidget::slotTimeout()
0395 {
0396     Q_FOREACH (const QString& path, d->paths)
0397     {
0398         QStorageInfo info(path);
0399 
0400         if (info.isValid())
0401         {
0402             addInformation((qint64)(info.bytesTotal()),
0403                            (qint64)(info.bytesTotal() - info.bytesAvailable()),
0404                            (qint64)(info.bytesAvailable()),
0405                            info.rootPath());
0406         }
0407     }
0408 }
0409 
0410 } // namespace Digikam
0411 
0412 #include "moc_freespacewidget.cpp"