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"