File indexing completed on 2024-04-21 08:47:43

0001 /* This file is part of FSView.
0002     SPDX-FileCopyrightText: 2002, 2003 Josef Weidendorfer <Josef.Weidendorfer@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-only
0005 */
0006 
0007 /*
0008  * FSView specialization of TreeMapItem class.
0009  */
0010 
0011 #include "inode.h"
0012 
0013 #include <kiconloader.h>
0014 #include <KLocalizedString>
0015 
0016 #include <QMimeDatabase>
0017 
0018 #include "fsview.h"
0019 #include "fsviewdebug.h"
0020 
0021 // Inode
0022 
0023 Inode::Inode()
0024 {
0025     _dirPeer = nullptr;
0026     _filePeer = nullptr;
0027     init(QString());
0028 }
0029 
0030 Inode::Inode(ScanDir *d, Inode *parent)
0031     : TreeMapItem(parent)
0032 {
0033     QString absPath;
0034     if (parent) {
0035         absPath = parent->path();
0036         if (!absPath.endsWith(QLatin1Char('/'))) {
0037             absPath += QLatin1Char('/');
0038         }
0039     }
0040     absPath += d->name();
0041 
0042     _dirPeer = d;
0043     _filePeer = nullptr;
0044 
0045     init(absPath);
0046 }
0047 
0048 Inode::Inode(ScanFile *f, Inode *parent)
0049     : TreeMapItem(parent)
0050 {
0051     QString absPath;
0052     if (parent) {
0053         absPath = parent->path() + QLatin1Char('/');
0054     }
0055     absPath += f->name();
0056 
0057     _dirPeer = nullptr;
0058     _filePeer = f;
0059 
0060     init(absPath);
0061 }
0062 
0063 Inode::~Inode()
0064 {
0065     if (0) qCDebug(FSVIEWLOG) << "~Inode [" << path()
0066                               << "]";
0067 
0068     /* reset Listener of old Peer */
0069     if (_dirPeer) {
0070         _dirPeer->setListener(nullptr);
0071     }
0072     if (_filePeer) {
0073         _filePeer->setListener(nullptr);
0074     }
0075 }
0076 
0077 void Inode::setPeer(ScanDir *d)
0078 {
0079     /* reset Listener of old Peer */
0080     if (_dirPeer) {
0081         _dirPeer->setListener(nullptr);
0082     }
0083     if (_filePeer) {
0084         _filePeer->setListener(nullptr);
0085     }
0086 
0087     _dirPeer = d;
0088     _filePeer = nullptr;
0089     init(d->name());
0090 }
0091 
0092 QString Inode::path() const
0093 {
0094     return _info.absoluteFilePath();
0095 }
0096 
0097 void Inode::init(const QString &path)
0098 {
0099     if (0) qCDebug(FSVIEWLOG) << "Inode::init [" << path
0100                               << "]";
0101 
0102     _info = QFileInfo(path);
0103 
0104     if (!FSView::getDirMetric(path, _sizeEstimation,
0105                               _fileCountEstimation,
0106                               _dirCountEstimation)) {
0107         _sizeEstimation = 0.0;
0108         _fileCountEstimation = 0;
0109         _dirCountEstimation = 0;
0110     }
0111 
0112     _mimeSet = false;
0113     _mimePixmapSet = false;
0114     _resortNeeded = false;
0115 
0116     clear();
0117 
0118     /* we want to get notifications about dir changes */
0119     if (_dirPeer) {
0120         _dirPeer->setListener(this);
0121     }
0122     if (_filePeer) {
0123         _filePeer->setListener(this);
0124     }
0125 
0126     if (_dirPeer && _dirPeer->scanFinished()) {
0127         scanFinished(_dirPeer);
0128     }
0129 }
0130 
0131 /* ScanListener interface */
0132 void Inode::sizeChanged(ScanDir *d)
0133 {
0134     if (0) qCDebug(FSVIEWLOG) << "Inode::sizeChanged [" << path() << "] in "
0135                               << d->name() << ": size " << d->size();
0136 
0137     _resortNeeded = true;
0138 }
0139 
0140 void Inode::scanFinished(ScanDir *d)
0141 {
0142     if (0) qCDebug(FSVIEWLOG) << "Inode::scanFinished [" << path() << "] in "
0143                               << d->name() << ": size " << d->size();
0144 
0145     _resortNeeded = true;
0146 
0147     /* no estimation any longer */
0148     _sizeEstimation = 0.0;
0149     _fileCountEstimation = 0;
0150     _dirCountEstimation = 0;
0151 
0152     // cache metrics if "important" (for "/usr" is dd==3)
0153     int dd = ((FSView *)widget())->pathDepth() + depth();
0154     int files = d->fileCount();
0155     int dirs = d->dirCount();
0156 
0157     if ((files < 500) && (dirs < 50)) {
0158         if (dd > 4 && (files < 50) && (dirs < 5)) {
0159             return;
0160         }
0161     }
0162 
0163     FSView::setDirMetric(path(), d->size(), files, dirs);
0164 }
0165 
0166 void Inode::destroyed(ScanDir *d)
0167 {
0168     if (_dirPeer == d) {
0169         _dirPeer = nullptr;
0170     }
0171 
0172     // remove children
0173     clear();
0174 }
0175 
0176 void Inode::destroyed(ScanFile *f)
0177 {
0178     if (_filePeer == f) {
0179         _filePeer = nullptr;
0180     }
0181 }
0182 
0183 TreeMapItemList *Inode::children()
0184 {
0185     if (!_dirPeer) {
0186         return nullptr;
0187     }
0188 
0189     if (!_children) {
0190         if (!_dirPeer->scanStarted()) {
0191             return nullptr;
0192         }
0193 
0194         _children = new TreeMapItemList;
0195 
0196         setSorting(-1);
0197 
0198         ScanFileVector &files = _dirPeer->files();
0199         if (files.count() > 0) {
0200             ScanFileVector::iterator it;
0201             for (it = files.begin(); it != files.end(); ++it) {
0202                 new Inode(&(*it), this);
0203             }
0204         }
0205 
0206         ScanDirVector &dirs = _dirPeer->dirs();
0207         if (dirs.count() > 0) {
0208             ScanDirVector::iterator it;
0209             for (it = dirs.begin(); it != dirs.end(); ++it) {
0210                 new Inode(&(*it), this);
0211             }
0212         }
0213 
0214         setSorting(-2);
0215         _resortNeeded = false;
0216     }
0217 
0218     if (_resortNeeded) {
0219         resort();
0220         _resortNeeded = false;
0221     }
0222 
0223     return _children;
0224 }
0225 
0226 double Inode::size() const
0227 {
0228     // sizes of files are always correct
0229     if (_filePeer) {
0230         return _filePeer->size();
0231     }
0232     if (!_dirPeer) {
0233         return 0;
0234     }
0235 
0236     double size = _dirPeer->size();
0237     return (_sizeEstimation > size) ? _sizeEstimation : size;
0238 }
0239 
0240 double Inode::value() const
0241 {
0242     return size();
0243 }
0244 
0245 unsigned int Inode::fileCount() const
0246 {
0247     unsigned int fileCount = 1;
0248 
0249     if (_dirPeer) {
0250         fileCount = _dirPeer->fileCount();
0251     }
0252 
0253     if (_fileCountEstimation > fileCount) {
0254         fileCount = _fileCountEstimation;
0255     }
0256 
0257     return fileCount;
0258 }
0259 
0260 unsigned int Inode::dirCount() const
0261 {
0262     unsigned int dirCount = 0;
0263 
0264     if (_dirPeer) {
0265         dirCount = _dirPeer->dirCount();
0266     }
0267 
0268     if (_dirCountEstimation > dirCount) {
0269         dirCount = _dirCountEstimation;
0270     }
0271 
0272     return dirCount;
0273 }
0274 
0275 QColor Inode::backColor() const
0276 {
0277     QString n;
0278     int id = 0;
0279 
0280     switch (((FSView *)widget())->colorMode()) {
0281     case FSView::Depth: {
0282         int d = ((FSView *)widget())->pathDepth() + depth();
0283         return QColor::fromHsv((100 * d) % 360, 192, 128);
0284     }
0285 
0286     case FSView::Name:   n = text(0); break;
0287     case FSView::Owner:  id = _info.ownerId(); break;
0288     case FSView::Group:  id = _info.groupId(); break;
0289     case FSView::Mime:   n = text(7); break;
0290 
0291     default:
0292         break;
0293     }
0294 
0295     if (id > 0) {
0296         n = QString::number(id);
0297     }
0298 
0299     if (n.isEmpty()) {
0300         return widget()->palette().button().color();
0301     }
0302 
0303     QByteArray tmpBuf = n.toLocal8Bit();
0304     const char *str = tmpBuf.data();
0305     int h = 0, s = 100;
0306     while (*str) {
0307         h = (h * 37 + s * (unsigned) * str) % 256;
0308         s = (s * 17 + h * (unsigned) * str) % 192;
0309         str++;
0310     }
0311     return QColor::fromHsv(h, 64 + s, 192);
0312 }
0313 
0314 QMimeType Inode::mimeType() const
0315 {
0316     if (!_mimeSet) {
0317         QMimeDatabase db;
0318         _mimeType = db.mimeTypeForUrl(QUrl::fromLocalFile(path()));
0319 
0320         _mimeSet = true;
0321     }
0322     return _mimeType;
0323 }
0324 
0325 QString Inode::text(int i) const
0326 {
0327     if (i == 0) {
0328         QString name;
0329         if (_dirPeer) {
0330             name = _dirPeer->name();
0331             if (!name.endsWith(QLatin1Char('/'))) {
0332                 name += QLatin1Char('/');
0333             }
0334         } else if (_filePeer) {
0335             name = _filePeer->name();
0336         }
0337 
0338         return name;
0339     }
0340     if (i == 1) {
0341         QString text = KIO::convertSize(static_cast<KIO::filesize_t>(size()+0.5));
0342         if (_sizeEstimation > 0) {
0343             text += QChar::fromLatin1('+');
0344         }
0345         return text;
0346     }
0347 
0348     if ((i == 2) || (i == 3)) {
0349         /* file/dir count makes no sense for files */
0350         if (_filePeer) {
0351             return QString();
0352         }
0353 
0354         QString text;
0355         unsigned int f = (i == 2) ? fileCount() : dirCount();
0356 
0357         if (f > 0) {
0358             while (f > 1000) {
0359                 text = QStringLiteral("%1 %2").arg(QString::number(f).right(3)).arg(text);
0360                 f /= 1000;
0361             }
0362             text = QStringLiteral("%1 %2").arg(QString::number(f)).arg(text);
0363             if (_fileCountEstimation > 0) {
0364                 text += '+';
0365             }
0366         }
0367         return text;
0368     }
0369 
0370     if (i == 4) {
0371         return _info.lastModified().toString();
0372     }
0373     if (i == 5) {
0374         return _info.owner();
0375     }
0376     if (i == 6) {
0377         return _info.group();
0378     }
0379     if (i == 7) {
0380         return mimeType().comment();
0381     }
0382     return QString();
0383 }
0384 
0385 QPixmap Inode::pixmap(int i) const
0386 {
0387     if (i != 0) {
0388         return QPixmap();
0389     }
0390 
0391     if (!_mimePixmapSet) {
0392         QUrl u = QUrl::fromLocalFile(path());
0393         const QIcon icon = QIcon::fromTheme(KIO::iconNameForUrl(u), QIcon::fromTheme(QStringLiteral("application-octet-stream")));
0394         _mimePixmap = icon.pixmap(KIconLoader::SizeSmall);
0395         _mimePixmapSet = true;
0396     }
0397     return _mimePixmap;
0398 }