File indexing completed on 2024-05-05 17:56:53
0001 /* 0002 SPDX-FileCopyrightText: 2004 Csaba Karai <krusader@users.sourceforge.net> 0003 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "diskusage.h" 0009 0010 // QtCore 0011 #include <QDebug> 0012 #include <QEvent> 0013 #include <QHash> 0014 #include <QMimeDatabase> 0015 #include <QMimeType> 0016 #include <QPointer> 0017 // QtGui 0018 #include <QCursor> 0019 #include <QKeyEvent> 0020 #include <QPixmap> 0021 #include <QPixmapCache> 0022 #include <QResizeEvent> 0023 // QtWidgets 0024 #include <QApplication> 0025 #include <QFrame> 0026 #include <QGridLayout> 0027 #include <QGroupBox> 0028 #include <QLabel> 0029 #include <QLayout> 0030 #include <QMenu> 0031 #include <QPushButton> 0032 0033 #include <KConfigCore/KSharedConfig> 0034 #include <KI18n/KLocalizedString> 0035 #include <KIO/DeleteJob> 0036 #include <KIO/Job> 0037 #include <KWidgetsAddons/KMessageBox> 0038 #include <KWidgetsAddons/KStandardGuiItem> 0039 #include <utility> 0040 0041 #include "../FileSystem/fileitem.h" 0042 #include "../FileSystem/filesystemprovider.h" 0043 #include "../FileSystem/krpermhandler.h" 0044 #include "../Panel/krpanel.h" 0045 #include "../Panel/panelfunc.h" 0046 #include "../compat.h" 0047 #include "../defaults.h" 0048 #include "../filelisticon.h" 0049 #include "../krglobal.h" 0050 #include "dufilelight.h" 0051 #include "dulines.h" 0052 #include "dulistview.h" 0053 #include "filelightParts/Config.h" 0054 0055 // these are the values that will exist in the menu 0056 #define DELETE_ID 90 0057 #define EXCLUDE_ID 91 0058 #define PARENT_DIR_ID 92 0059 #define NEW_SEARCH_ID 93 0060 #define REFRESH_ID 94 0061 #define STEP_INTO_ID 95 0062 #define INCLUDE_ALL_ID 96 0063 #define VIEW_POPUP_ID 97 0064 #define LINES_VIEW_ID 98 0065 #define DETAILED_VIEW_ID 99 0066 #define FILELIGHT_VIEW_ID 100 0067 #define NEXT_VIEW_ID 101 0068 #define PREVIOUS_VIEW_ID 102 0069 #define ADDITIONAL_POPUP_ID 103 0070 0071 #define MAX_FILENUM 100 0072 0073 LoaderWidget::LoaderWidget(QWidget *parent) 0074 : QScrollArea(parent) 0075 , cancelled(false) 0076 { 0077 QPalette palette = viewport()->palette(); 0078 palette.setColor(viewport()->backgroundRole(), Qt::white); 0079 viewport()->setPalette(palette); 0080 0081 widget = new QWidget(parent); 0082 0083 auto *loaderLayout = new QGridLayout(widget); 0084 loaderLayout->setSpacing(0); 0085 loaderLayout->setContentsMargins(0, 0, 0, 0); 0086 0087 QFrame *loaderBox = new QFrame(widget); 0088 loaderBox->setFrameShape(QFrame::Box); 0089 loaderBox->setFrameShadow(QFrame::Sunken); 0090 loaderBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0091 loaderBox->setFrameStyle(QFrame::Panel + QFrame::Raised); 0092 loaderBox->setLineWidth(2); 0093 0094 auto *synchGrid = new QGridLayout(loaderBox); 0095 synchGrid->setSpacing(6); 0096 synchGrid->setContentsMargins(11, 11, 11, 11); 0097 0098 QLabel *titleLabel = new QLabel(i18n("Loading Usage Information"), loaderBox); 0099 titleLabel->setAlignment(Qt::AlignHCenter); 0100 synchGrid->addWidget(titleLabel, 0, 0, 1, 2); 0101 0102 QLabel *filesLabel = new QLabel(i18n("Files:"), loaderBox); 0103 filesLabel->setFrameShape(QLabel::StyledPanel); 0104 filesLabel->setFrameShadow(QLabel::Sunken); 0105 synchGrid->addWidget(filesLabel, 1, 0); 0106 0107 QLabel *directoriesLabel = new QLabel(i18n("Folders:"), loaderBox); 0108 directoriesLabel->setFrameShape(QLabel::StyledPanel); 0109 directoriesLabel->setFrameShadow(QLabel::Sunken); 0110 synchGrid->addWidget(directoriesLabel, 2, 0); 0111 0112 QLabel *totalSizeLabel = new QLabel(i18n("Total Size:"), loaderBox); 0113 totalSizeLabel->setFrameShape(QLabel::StyledPanel); 0114 totalSizeLabel->setFrameShadow(QLabel::Sunken); 0115 synchGrid->addWidget(totalSizeLabel, 3, 0); 0116 0117 files = new QLabel(loaderBox); 0118 files->setFrameShape(QLabel::StyledPanel); 0119 files->setFrameShadow(QLabel::Sunken); 0120 files->setAlignment(Qt::AlignRight); 0121 synchGrid->addWidget(files, 1, 1); 0122 0123 directories = new QLabel(loaderBox); 0124 directories->setFrameShape(QLabel::StyledPanel); 0125 directories->setFrameShadow(QLabel::Sunken); 0126 directories->setAlignment(Qt::AlignRight); 0127 synchGrid->addWidget(directories, 2, 1); 0128 0129 totalSize = new QLabel(loaderBox); 0130 totalSize->setFrameShape(QLabel::StyledPanel); 0131 totalSize->setFrameShadow(QLabel::Sunken); 0132 totalSize->setAlignment(Qt::AlignRight); 0133 synchGrid->addWidget(totalSize, 3, 1); 0134 0135 int width; 0136 searchedDirectory = new KSqueezedTextLabel(loaderBox); 0137 searchedDirectory->setFrameShape(QLabel::StyledPanel); 0138 searchedDirectory->setFrameShadow(QLabel::Sunken); 0139 searchedDirectory->setMinimumWidth(width = QFontMetrics(searchedDirectory->font()).horizontalAdvance("W") * 30); 0140 searchedDirectory->setMaximumWidth(width); 0141 synchGrid->addWidget(searchedDirectory, 4, 0, 1, 2); 0142 0143 QFrame *line = new QFrame(loaderBox); 0144 line->setFrameStyle(QFrame::HLine | QFrame::Sunken); 0145 synchGrid->addWidget(line, 5, 0, 1, 2); 0146 0147 QWidget *hboxWidget = new QWidget(loaderBox); 0148 auto *hbox = new QHBoxLayout(hboxWidget); 0149 0150 auto *spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); 0151 hbox->addItem(spacer); 0152 auto *cancelButton = new QPushButton(hboxWidget); 0153 KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel); 0154 hbox->addWidget(cancelButton); 0155 0156 synchGrid->addWidget(hboxWidget, 6, 1); 0157 0158 loaderLayout->addWidget(loaderBox, 0, 0); 0159 0160 setWidget(widget); 0161 setAlignment(Qt::AlignCenter); 0162 0163 connect(cancelButton, &QPushButton::clicked, this, &LoaderWidget::slotCancelled); 0164 } 0165 0166 void LoaderWidget::init() 0167 { 0168 cancelled = false; 0169 } 0170 0171 void LoaderWidget::setCurrentURL(const QUrl &url) 0172 { 0173 searchedDirectory->setText(FileSystem::ensureTrailingSlash(url).toDisplayString(QUrl::PreferLocalFile)); 0174 } 0175 0176 void LoaderWidget::setValues(int fileNum, int dirNum, KIO::filesize_t total) 0177 { 0178 files->setText(QString("%1").arg(fileNum)); 0179 directories->setText(QString("%1").arg(dirNum)); 0180 totalSize->setText(QString("%1").arg(KrPermHandler::parseSize(total).trimmed())); 0181 } 0182 0183 void LoaderWidget::slotCancelled() 0184 { 0185 cancelled = true; 0186 } 0187 0188 DiskUsage::DiskUsage(QString confGroup, QWidget *parent) 0189 : QStackedWidget(parent) 0190 , currentDirectory(nullptr) 0191 , root(nullptr) 0192 , configGroup(std::move(confGroup)) 0193 , loading(false) 0194 , abortLoading(false) 0195 , clearAfterAbort(false) 0196 , deleting(false) 0197 , searchFileSystem(nullptr) 0198 { 0199 listView = new DUListView(this); 0200 lineView = new DULines(this); 0201 filelightView = new DUFilelight(this); 0202 loaderView = new LoaderWidget(this); 0203 0204 addWidget(listView); 0205 addWidget(lineView); 0206 addWidget(filelightView); 0207 addWidget(loaderView); 0208 0209 setView(VIEW_LINES); 0210 0211 Filelight::Config::read(); 0212 0213 connect(&loadingTimer, &QTimer::timeout, this, &DiskUsage::slotLoadDirectory); 0214 } 0215 0216 DiskUsage::~DiskUsage() 0217 { 0218 if (listView) // don't remove these lines. The module will crash at exit if removed 0219 delete listView; 0220 if (lineView) 0221 delete lineView; 0222 if (filelightView) 0223 delete filelightView; 0224 0225 if (root) 0226 delete root; 0227 0228 QHashIterator<File *, Properties *> lit(propertyMap); 0229 while (lit.hasNext()) 0230 delete lit.next().value(); 0231 } 0232 0233 void DiskUsage::load(const QUrl &baseDir) 0234 { 0235 fileNum = dirNum = 0; 0236 currentSize = 0; 0237 0238 emit status(i18n("Loading the disk usage information...")); 0239 0240 clear(); 0241 0242 baseURL = baseDir.adjusted(QUrl::StripTrailingSlash); 0243 0244 root = new Directory(baseURL.fileName(), baseDir.toDisplayString(QUrl::PreferLocalFile)); 0245 0246 directoryStack.clear(); 0247 parentStack.clear(); 0248 0249 directoryStack.push(""); 0250 parentStack.push(root); 0251 0252 if (searchFileSystem) { 0253 delete searchFileSystem; 0254 searchFileSystem = nullptr; 0255 } 0256 searchFileSystem = FileSystemProvider::instance().getFilesystem(baseDir); 0257 if (searchFileSystem == nullptr) { 0258 qWarning() << "could not get filesystem for directory=" << baseDir; 0259 loading = abortLoading = clearAfterAbort = false; 0260 emit loadFinished(false); 0261 return; 0262 } 0263 0264 currentFileItem = nullptr; 0265 0266 if (!loading) { 0267 viewBeforeLoad = activeView; 0268 setView(VIEW_LOADER); 0269 } 0270 0271 loading = true; 0272 0273 loaderView->init(); 0274 loaderView->setCurrentURL(baseURL); 0275 loaderView->setValues(fileNum, dirNum, currentSize); 0276 0277 loadingTimer.setSingleShot(true); 0278 loadingTimer.start(0); 0279 } 0280 0281 void DiskUsage::slotLoadDirectory() 0282 { 0283 if ((currentFileItem == nullptr && directoryStack.isEmpty()) || loaderView->wasCancelled() || abortLoading) { 0284 if (searchFileSystem) 0285 delete searchFileSystem; 0286 0287 searchFileSystem = nullptr; 0288 currentFileItem = nullptr; 0289 0290 setView(viewBeforeLoad); 0291 0292 if (clearAfterAbort) 0293 clear(); 0294 else { 0295 calculateSizes(); 0296 changeDirectory(root); 0297 } 0298 0299 emit loadFinished(!(loaderView->wasCancelled() || abortLoading)); 0300 0301 loading = abortLoading = clearAfterAbort = false; 0302 } else if (loading) { 0303 for (int counter = 0; counter != MAX_FILENUM; counter++) { 0304 if (currentFileItem == nullptr) { 0305 if (directoryStack.isEmpty()) 0306 break; 0307 0308 dirToCheck = directoryStack.pop(); 0309 currentParent = parentStack.pop(); 0310 0311 contentMap.insert(dirToCheck, currentParent); 0312 0313 QUrl url = baseURL; 0314 0315 if (!dirToCheck.isEmpty()) { 0316 url = url.adjusted(QUrl::StripTrailingSlash); 0317 url.setPath(url.path() + '/' + (dirToCheck)); 0318 } 0319 0320 #ifdef BSD 0321 if (url.isLocalFile() && url.path().left(7) == "/procfs") 0322 break; 0323 #else 0324 if (url.isLocalFile() && url.path().left(5) == "/proc") 0325 break; 0326 #endif 0327 0328 loaderView->setCurrentURL(url); 0329 0330 if (!searchFileSystem->scanDir(url)) 0331 break; 0332 fileItems = searchFileSystem->fileItems(); 0333 0334 dirNum++; 0335 0336 currentFileItem = fileItems.isEmpty() ? nullptr : fileItems.takeFirst(); 0337 } else { 0338 fileNum++; 0339 File *newItem = nullptr; 0340 0341 QString mime = currentFileItem->getMime(); // fast == not using mimetype magic 0342 0343 if (currentFileItem->isDir() && !currentFileItem->isSymLink()) { 0344 newItem = new Directory(currentParent, 0345 currentFileItem->getName(), 0346 dirToCheck, 0347 currentFileItem->getSize(), 0348 currentFileItem->getMode(), 0349 currentFileItem->getOwner(), 0350 currentFileItem->getGroup(), 0351 currentFileItem->getPerm(), 0352 currentFileItem->getModificationTime(), 0353 currentFileItem->isSymLink(), 0354 mime); 0355 directoryStack.push((dirToCheck.isEmpty() ? "" : dirToCheck + '/') + currentFileItem->getName()); 0356 parentStack.push(dynamic_cast<Directory *>(newItem)); 0357 } else { 0358 newItem = new File(currentParent, 0359 currentFileItem->getName(), 0360 dirToCheck, 0361 currentFileItem->getSize(), 0362 currentFileItem->getMode(), 0363 currentFileItem->getOwner(), 0364 currentFileItem->getGroup(), 0365 currentFileItem->getPerm(), 0366 currentFileItem->getModificationTime(), 0367 currentFileItem->isSymLink(), 0368 mime); 0369 currentSize += currentFileItem->getSize(); 0370 } 0371 currentParent->append(newItem); 0372 0373 currentFileItem = fileItems.isEmpty() ? nullptr : fileItems.takeFirst(); 0374 } 0375 } 0376 0377 loaderView->setValues(fileNum, dirNum, currentSize); 0378 loadingTimer.setSingleShot(true); 0379 loadingTimer.start(0); 0380 } 0381 } 0382 0383 void DiskUsage::stopLoad() 0384 { 0385 abortLoading = true; 0386 } 0387 0388 void DiskUsage::close() 0389 { 0390 if (loading) { 0391 abortLoading = true; 0392 clearAfterAbort = true; 0393 } 0394 } 0395 0396 void DiskUsage::dirUp() 0397 { 0398 if (currentDirectory != nullptr) { 0399 if (currentDirectory->parent() != nullptr) 0400 changeDirectory(const_cast<Directory *>(currentDirectory->parent())); 0401 else { 0402 QUrl up = KIO::upUrl(baseURL); 0403 0404 if (KMessageBox::questionYesNo(this, 0405 i18n("Stepping into the parent folder requires " 0406 "loading the content of the \"%1\" URL. Do you wish " 0407 "to continue?", 0408 up.toDisplayString(QUrl::PreferLocalFile)), 0409 i18n("Krusader::DiskUsage"), 0410 KStandardGuiItem::cont(), 0411 KStandardGuiItem::cancel(), 0412 "DiskUsageLoadParentDir") 0413 == KMessageBox::Yes) 0414 load(up); 0415 } 0416 } 0417 } 0418 0419 Directory *DiskUsage::getDirectory(QString dir) 0420 { 0421 while (dir.endsWith('/')) 0422 dir.truncate(dir.length() - 1); 0423 0424 if (dir.isEmpty()) 0425 return root; 0426 0427 if (contentMap.find(dir) == contentMap.end()) 0428 return nullptr; 0429 return contentMap[dir]; 0430 } 0431 0432 File *DiskUsage::getFile(const QString &path) 0433 { 0434 if (path.isEmpty()) 0435 return root; 0436 0437 QString dir = path; 0438 0439 int ndx = path.lastIndexOf('/'); 0440 QString file = path.mid(ndx + 1); 0441 0442 if (ndx == -1) 0443 dir = ""; 0444 else 0445 dir.truncate(ndx); 0446 0447 Directory *dirEntry = getDirectory(dir); 0448 if (dirEntry == nullptr) 0449 return nullptr; 0450 0451 for (Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it) 0452 if ((*it)->name() == file) 0453 return *it; 0454 0455 return nullptr; 0456 } 0457 0458 void DiskUsage::clear() 0459 { 0460 baseURL = QUrl(); 0461 emit clearing(); 0462 0463 QHashIterator<File *, Properties *> lit(propertyMap); 0464 while (lit.hasNext()) 0465 delete lit.next().value(); 0466 0467 propertyMap.clear(); 0468 contentMap.clear(); 0469 if (root) 0470 delete root; 0471 root = currentDirectory = nullptr; 0472 } 0473 0474 int DiskUsage::calculateSizes(Directory *dirEntry, bool emitSig, int depth) 0475 { 0476 int changeNr = 0; 0477 0478 if (dirEntry == nullptr) 0479 dirEntry = root; 0480 0481 KIO::filesize_t own = 0, total = 0; 0482 0483 for (Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it) { 0484 File *item = *it; 0485 0486 if (!item->isExcluded()) { 0487 if (item->isDir()) 0488 changeNr += calculateSizes(dynamic_cast<Directory *>(item), emitSig, depth + 1); 0489 else 0490 own += item->size(); 0491 0492 total += item->size(); 0493 } 0494 } 0495 0496 KIO::filesize_t oldOwn = dirEntry->ownSize(), oldTotal = dirEntry->size(); 0497 dirEntry->setSizes(total, own); 0498 0499 if (dirEntry == currentDirectory) 0500 currentSize = total; 0501 0502 if (emitSig && (own != oldOwn || total != oldTotal)) { 0503 emit changed(dirEntry); 0504 changeNr++; 0505 } 0506 0507 if (depth == 0 && changeNr != 0) 0508 emit changeFinished(); 0509 return changeNr; 0510 } 0511 0512 int DiskUsage::exclude(File *file, bool calcPercents, int depth) 0513 { 0514 int changeNr = 0; 0515 0516 if (!file->isExcluded()) { 0517 file->exclude(true); 0518 emit changed(file); 0519 changeNr++; 0520 0521 if (file->isDir()) { 0522 auto *dir = dynamic_cast<Directory *>(file); 0523 for (Iterator<File> it = dir->iterator(); it != dir->end(); ++it) 0524 changeNr += exclude(*it, false, depth + 1); 0525 } 0526 } 0527 0528 if (calcPercents) { 0529 calculateSizes(root, true); 0530 calculatePercents(true); 0531 createStatus(); 0532 } 0533 0534 if (depth == 0 && changeNr != 0) 0535 emit changeFinished(); 0536 0537 return changeNr; 0538 } 0539 0540 int DiskUsage::include(Directory *dir, int depth) 0541 { 0542 int changeNr = 0; 0543 0544 if (dir == nullptr) 0545 return 0; 0546 0547 for (Iterator<File> it = dir->iterator(); it != dir->end(); ++it) { 0548 File *item = *it; 0549 0550 if (item->isDir()) 0551 changeNr += include(dynamic_cast<Directory *>(item), depth + 1); 0552 0553 if (item->isExcluded()) { 0554 item->exclude(false); 0555 emit changed(item); 0556 changeNr++; 0557 } 0558 } 0559 0560 if (depth == 0 && changeNr != 0) 0561 emit changeFinished(); 0562 0563 return changeNr; 0564 } 0565 0566 void DiskUsage::includeAll() 0567 { 0568 include(root); 0569 calculateSizes(root, true); 0570 calculatePercents(true); 0571 createStatus(); 0572 } 0573 0574 int DiskUsage::del(File *file, bool calcPercents, int depth) 0575 { 0576 int deleteNr = 0; 0577 0578 if (file == root) 0579 return 0; 0580 0581 KConfigGroup gg(krConfig, "General"); 0582 bool trash = gg.readEntry("Move To Trash", _MoveToTrash); 0583 QUrl url = QUrl::fromLocalFile(file->fullPath()); 0584 0585 if (calcPercents) { 0586 // now ask the user if he want to delete: 0587 KConfigGroup ga(krConfig, "Advanced"); 0588 if (ga.readEntry("Confirm Delete", _ConfirmDelete)) { 0589 QString s; 0590 KGuiItem b; 0591 if (trash && url.isLocalFile()) { 0592 s = i18nc("singularOnly", "Do you really want to move this item to the trash?"); 0593 b = KGuiItem(i18n("&Trash")); 0594 } else { 0595 s = i18nc("singularOnly", "Do you really want to delete this item?"); 0596 b = KStandardGuiItem::del(); 0597 } 0598 0599 QStringList name; 0600 name.append(file->fullPath()); 0601 // show message 0602 // note: i'm using continue and not yes/no because the yes/no has cancel as default button 0603 if (KMessageBox::warningContinueCancelList(krMainWindow, s, name, i18n("Warning"), b) != KMessageBox::Continue) 0604 return 0; 0605 } 0606 0607 emit status(i18n("Deleting %1...", file->name())); 0608 } 0609 0610 if (file == currentDirectory) 0611 dirUp(); 0612 0613 if (file->isDir()) { 0614 auto *dir = dynamic_cast<Directory *>(file); 0615 0616 Iterator<File> it; 0617 while ((it = dir->iterator()) != dir->end()) 0618 deleteNr += del(*it, false, depth + 1); 0619 0620 QString path; 0621 for (const Directory *d = dynamic_cast<Directory *>(file); d != root && d && d->parent() != nullptr; d = d->parent()) { 0622 if (!path.isEmpty()) 0623 path = '/' + path; 0624 0625 path = d->name() + path; 0626 } 0627 0628 contentMap.remove(path); 0629 } 0630 0631 emit deleted(file); 0632 deleteNr++; 0633 0634 KIO::Job *job; 0635 0636 if (trash) { 0637 job = KIO::trash(url); 0638 } else { 0639 job = KIO::del(QUrl::fromLocalFile(file->fullPath()), KIO::HideProgressInfo); 0640 } 0641 0642 deleting = true; // during qApp->processEvent strange things can occur 0643 grabMouse(); // that's why we disable the mouse and keyboard events 0644 grabKeyboard(); 0645 0646 job->exec(); 0647 delete job; 0648 0649 releaseMouse(); 0650 releaseKeyboard(); 0651 deleting = false; 0652 0653 const_cast<Directory *>(file->parent())->remove(file); 0654 delete file; 0655 0656 if (depth == 0) 0657 createStatus(); 0658 0659 if (calcPercents) { 0660 calculateSizes(root, true); 0661 calculatePercents(true); 0662 createStatus(); 0663 emit enteringDirectory(currentDirectory); 0664 } 0665 0666 if (depth == 0 && deleteNr != 0) 0667 emit deleteFinished(); 0668 0669 return deleteNr; 0670 } 0671 0672 void *DiskUsage::getProperty(File *item, const QString &key) 0673 { 0674 QHash<File *, Properties *>::iterator itr = propertyMap.find(item); 0675 if (itr == propertyMap.end()) 0676 return nullptr; 0677 0678 QHash<QString, void *>::iterator it = (*itr)->find(key); 0679 if (it == (*itr)->end()) 0680 return nullptr; 0681 0682 return it.value(); 0683 } 0684 0685 void DiskUsage::addProperty(File *item, const QString &key, void *prop) 0686 { 0687 Properties *props; 0688 QHash<File *, Properties *>::iterator itr = propertyMap.find(item); 0689 if (itr == propertyMap.end()) { 0690 props = new Properties(); 0691 propertyMap.insert(item, props); 0692 } else 0693 props = *itr; 0694 0695 props->insert(key, prop); 0696 } 0697 0698 void DiskUsage::removeProperty(File *item, const QString &key) 0699 { 0700 QHash<File *, Properties *>::iterator itr = propertyMap.find(item); 0701 if (itr == propertyMap.end()) 0702 return; 0703 (*itr)->remove(key); 0704 if ((*itr)->count() == 0) 0705 propertyMap.remove(item); 0706 } 0707 0708 void DiskUsage::createStatus() 0709 { 0710 Directory *dirEntry = currentDirectory; 0711 0712 if (dirEntry == nullptr) 0713 return; 0714 0715 QUrl url = baseURL; 0716 if (dirEntry != root) { 0717 url = url.adjusted(QUrl::StripTrailingSlash); 0718 url.setPath(url.path() + '/' + (dirEntry->directory())); 0719 } 0720 0721 emit status(i18n("Current folder:%1, Total size:%2, Own size:%3", 0722 url.toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash), 0723 ' ' + KrPermHandler::parseSize(dirEntry->size()), 0724 ' ' + KrPermHandler::parseSize(dirEntry->ownSize()))); 0725 } 0726 0727 void DiskUsage::changeDirectory(Directory *dir) 0728 { 0729 currentDirectory = dir; 0730 0731 currentSize = dir->size(); 0732 calculatePercents(true, dir); 0733 0734 createStatus(); 0735 emit enteringDirectory(dir); 0736 } 0737 0738 Directory *DiskUsage::getCurrentDir() 0739 { 0740 return currentDirectory; 0741 } 0742 0743 void DiskUsage::rightClickMenu(const QPoint &pos, File *fileItem, QMenu *addPopup, const QString &addPopupName) 0744 { 0745 QMenu popup(this); 0746 0747 popup.setTitle(i18n("Disk Usage")); 0748 0749 QHash<void *, int> actionHash; 0750 0751 if (fileItem != nullptr) { 0752 QAction *actDelete = popup.addAction(i18n("Delete")); 0753 actionHash[actDelete] = DELETE_ID; 0754 actDelete->setShortcut(Qt::Key_Delete); 0755 QAction *actExclude = popup.addAction(i18n("Exclude")); 0756 actionHash[actExclude] = EXCLUDE_ID; 0757 actExclude->setShortcut(Qt::CTRL + Qt::Key_E); 0758 popup.addSeparator(); 0759 } 0760 0761 QAction *myAct = popup.addAction(i18n("Up one folder")); 0762 actionHash[myAct] = PARENT_DIR_ID; 0763 myAct->setShortcut(Qt::SHIFT + Qt::Key_Up); 0764 0765 myAct = popup.addAction(i18n("New search")); 0766 actionHash[myAct] = NEW_SEARCH_ID; 0767 myAct->setShortcut(Qt::CTRL + Qt::Key_N); 0768 0769 myAct = popup.addAction(i18n("Refresh")); 0770 actionHash[myAct] = REFRESH_ID; 0771 myAct->setShortcut(Qt::CTRL + Qt::Key_R); 0772 0773 myAct = popup.addAction(i18n("Include all")); 0774 actionHash[myAct] = INCLUDE_ALL_ID; 0775 myAct->setShortcut(Qt::CTRL + Qt::Key_I); 0776 0777 myAct = popup.addAction(i18n("Step into")); 0778 actionHash[myAct] = STEP_INTO_ID; 0779 myAct->setShortcut(Qt::SHIFT + Qt::Key_Down); 0780 0781 popup.addSeparator(); 0782 0783 if (addPopup != nullptr) { 0784 QAction *menu = popup.addMenu(addPopup); 0785 menu->setText(addPopupName); 0786 } 0787 0788 QMenu viewPopup; 0789 0790 myAct = viewPopup.addAction(i18n("Lines")); 0791 actionHash[myAct] = LINES_VIEW_ID; 0792 myAct->setShortcut(Qt::CTRL + Qt::Key_L); 0793 0794 myAct = viewPopup.addAction(i18n("Detailed")); 0795 actionHash[myAct] = DETAILED_VIEW_ID; 0796 myAct->setShortcut(Qt::CTRL + Qt::Key_D); 0797 0798 myAct = viewPopup.addAction(i18n("Filelight")); 0799 actionHash[myAct] = FILELIGHT_VIEW_ID; 0800 myAct->setShortcut(Qt::CTRL + Qt::Key_F); 0801 0802 viewPopup.addSeparator(); 0803 0804 myAct = viewPopup.addAction(i18n("Next")); 0805 actionHash[myAct] = NEXT_VIEW_ID; 0806 myAct->setShortcut(Qt::SHIFT + Qt::Key_Right); 0807 0808 myAct = viewPopup.addAction(i18n("Previous")); 0809 actionHash[myAct] = PREVIOUS_VIEW_ID; 0810 myAct->setShortcut(Qt::SHIFT + Qt::Key_Left); 0811 0812 QAction *menu = popup.addMenu(&viewPopup); 0813 menu->setText(i18n("View")); 0814 0815 QAction *res = popup.exec(pos); 0816 0817 if (actionHash.contains(res)) 0818 executeAction(actionHash[res], fileItem); 0819 } 0820 0821 void DiskUsage::executeAction(int action, File *fileItem) 0822 { 0823 // check out the user's option 0824 switch (action) { 0825 case DELETE_ID: 0826 if (fileItem) 0827 del(fileItem); 0828 break; 0829 case EXCLUDE_ID: 0830 if (fileItem) 0831 exclude(fileItem); 0832 break; 0833 case PARENT_DIR_ID: 0834 dirUp(); 0835 break; 0836 case NEW_SEARCH_ID: 0837 emit newSearch(); 0838 break; 0839 case REFRESH_ID: 0840 load(baseURL); 0841 break; 0842 case INCLUDE_ALL_ID: 0843 includeAll(); 0844 break; 0845 case STEP_INTO_ID: { 0846 QString uri; 0847 if (fileItem && fileItem->isDir()) 0848 uri = fileItem->fullPath(); 0849 else 0850 uri = currentDirectory->fullPath(); 0851 ACTIVE_FUNC->openUrl(QUrl::fromLocalFile(uri)); 0852 } break; 0853 case LINES_VIEW_ID: 0854 setView(VIEW_LINES); 0855 break; 0856 case DETAILED_VIEW_ID: 0857 setView(VIEW_DETAILED); 0858 break; 0859 case FILELIGHT_VIEW_ID: 0860 setView(VIEW_FILELIGHT); 0861 break; 0862 case NEXT_VIEW_ID: 0863 setView((activeView + 1) % 3); 0864 break; 0865 case PREVIOUS_VIEW_ID: 0866 setView((activeView + 2) % 3); 0867 break; 0868 } 0869 // currentWidget()->setFocus(); 0870 } 0871 0872 void DiskUsage::keyPressEvent(QKeyEvent *e) 0873 { 0874 if (activeView != VIEW_LOADER) { 0875 switch (e->key()) { 0876 case Qt::Key_E: 0877 if (e->modifiers() == Qt::ControlModifier) { 0878 executeAction(EXCLUDE_ID, getCurrentFile()); 0879 return; 0880 } 0881 break; 0882 case Qt::Key_D: 0883 if (e->modifiers() == Qt::ControlModifier) { 0884 executeAction(DETAILED_VIEW_ID); 0885 return; 0886 } 0887 break; 0888 case Qt::Key_F: 0889 if (e->modifiers() == Qt::ControlModifier) { 0890 executeAction(FILELIGHT_VIEW_ID); 0891 return; 0892 } 0893 break; 0894 case Qt::Key_I: 0895 if (e->modifiers() == Qt::ControlModifier) { 0896 executeAction(INCLUDE_ALL_ID); 0897 return; 0898 } 0899 break; 0900 case Qt::Key_L: 0901 if (e->modifiers() == Qt::ControlModifier) { 0902 executeAction(LINES_VIEW_ID); 0903 return; 0904 } 0905 break; 0906 case Qt::Key_N: 0907 if (e->modifiers() == Qt::ControlModifier) { 0908 executeAction(NEW_SEARCH_ID); 0909 return; 0910 } 0911 break; 0912 case Qt::Key_R: 0913 if (e->modifiers() == Qt::ControlModifier) { 0914 executeAction(REFRESH_ID); 0915 return; 0916 } 0917 break; 0918 case Qt::Key_Up: 0919 if (e->modifiers() == Qt::ShiftModifier) { 0920 executeAction(PARENT_DIR_ID); 0921 return; 0922 } 0923 break; 0924 case Qt::Key_Down: 0925 if (e->modifiers() == Qt::ShiftModifier) { 0926 executeAction(STEP_INTO_ID); 0927 return; 0928 } 0929 break; 0930 case Qt::Key_Left: 0931 if (e->modifiers() == Qt::ShiftModifier) { 0932 executeAction(PREVIOUS_VIEW_ID); 0933 return; 0934 } 0935 break; 0936 case Qt::Key_Right: 0937 if (e->modifiers() == Qt::ShiftModifier) { 0938 executeAction(NEXT_VIEW_ID); 0939 return; 0940 } 0941 break; 0942 case Qt::Key_Delete: 0943 if (!e->modifiers()) { 0944 executeAction(DELETE_ID, getCurrentFile()); 0945 return; 0946 } 0947 break; 0948 case Qt::Key_Plus: 0949 if (activeView == VIEW_FILELIGHT) { 0950 filelightView->zoomIn(); 0951 return; 0952 } 0953 break; 0954 case Qt::Key_Minus: 0955 if (activeView == VIEW_FILELIGHT) { 0956 filelightView->zoomOut(); 0957 return; 0958 } 0959 break; 0960 } 0961 } 0962 QStackedWidget::keyPressEvent(e); 0963 } 0964 0965 QPixmap DiskUsage::getIcon(const QString &mime) 0966 { 0967 QPixmap icon; 0968 0969 if (!QPixmapCache::find(mime, &icon)) { 0970 // get the icon. 0971 if (mime == "Broken Link !") // FIXME: this doesn't work anymore - the reported mimetype for a broken link is now "unknown" 0972 icon = FileListIcon("file-broken").pixmap(); 0973 else { 0974 QMimeDatabase db; 0975 QMimeType mt = db.mimeTypeForName(mime); 0976 if (mt.isValid()) 0977 icon = FileListIcon(mt.iconName()).pixmap(); 0978 else 0979 icon = FileListIcon("file-broken").pixmap(); 0980 } 0981 0982 // insert it into the cache 0983 QPixmapCache::insert(mime, icon); 0984 } 0985 return icon; 0986 } 0987 0988 int DiskUsage::calculatePercents(bool emitSig, Directory *dirEntry, int depth) 0989 { 0990 int changeNr = 0; 0991 0992 if (dirEntry == nullptr) 0993 dirEntry = root; 0994 0995 for (Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it) { 0996 File *item = *it; 0997 0998 if (!item->isExcluded()) { 0999 int newPerc; 1000 1001 if (dirEntry->size() == 0 && item->size() == 0) 1002 newPerc = 0; 1003 else if (dirEntry->size() == 0) 1004 newPerc = -1; 1005 else 1006 newPerc = (int)((double)item->size() / (double)currentSize * 10000. + 0.5); 1007 1008 int oldPerc = item->intPercent(); 1009 item->setPercent(newPerc); 1010 1011 if (emitSig && newPerc != oldPerc) { 1012 emit changed(item); 1013 changeNr++; 1014 } 1015 1016 if (item->isDir()) 1017 changeNr += calculatePercents(emitSig, dynamic_cast<Directory *>(item), depth + 1); 1018 } 1019 } 1020 1021 if (depth == 0 && changeNr != 0) 1022 emit changeFinished(); 1023 return changeNr; 1024 } 1025 1026 QString DiskUsage::getToolTip(File *item) 1027 { 1028 QMimeDatabase db; 1029 QMimeType mt = db.mimeTypeForName(item->mime()); 1030 QString mime; 1031 if (mt.isValid()) 1032 mime = mt.comment(); 1033 1034 time_t tma = item->time(); 1035 struct tm *t = localtime((time_t *)&tma); 1036 QDateTime tmp(QDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday), QTime(t->tm_hour, t->tm_min)); 1037 QString date = QLocale().toString(tmp, QLocale::ShortFormat); 1038 1039 QString str = "<qt><h5><table><tr><td>" + i18n("Name:") + "</td><td>" + item->name() + "</td></tr>" + "<tr><td>" + i18n("Type:") + "</td><td>" + mime 1040 + "</td></tr>" + "<tr><td>" + i18n("Size:") + "</td><td>" + KrPermHandler::parseSize(item->size()) + "</td></tr>"; 1041 1042 if (item->isDir()) 1043 str += "<tr><td>" + i18n("Own size:") + "</td><td>" + KrPermHandler::parseSize(item->ownSize()) + "</td></tr>"; 1044 1045 str += "<tr><td>" + i18n("Last modified:") + "</td><td>" + date + "</td></tr>" + "<tr><td>" + i18n("Permissions:") + "</td><td>" + item->perm() 1046 + "</td></tr>" + "<tr><td>" + i18n("Owner:") + "</td><td>" + item->owner() + " - " + item->group() + "</td></tr>" + "</table></h5></qt>"; 1047 str.replace(' ', " "); 1048 return str; 1049 } 1050 1051 void DiskUsage::setView(int view) 1052 { 1053 switch (view) { 1054 case VIEW_LINES: 1055 setCurrentWidget(lineView); 1056 break; 1057 case VIEW_DETAILED: 1058 setCurrentWidget(listView); 1059 break; 1060 case VIEW_FILELIGHT: 1061 setCurrentWidget(filelightView); 1062 break; 1063 case VIEW_LOADER: 1064 setCurrentWidget(loaderView); 1065 break; 1066 } 1067 1068 // currentWidget()->setFocus(); 1069 emit viewChanged(activeView = view); 1070 } 1071 1072 File *DiskUsage::getCurrentFile() 1073 { 1074 File *file = nullptr; 1075 1076 switch (activeView) { 1077 case VIEW_LINES: 1078 file = lineView->getCurrentFile(); 1079 break; 1080 case VIEW_DETAILED: 1081 file = listView->getCurrentFile(); 1082 break; 1083 case VIEW_FILELIGHT: 1084 file = filelightView->getCurrentFile(); 1085 break; 1086 } 1087 1088 return file; 1089 } 1090 1091 bool DiskUsage::event(QEvent *e) 1092 { 1093 if (deleting) { // if we are deleting, disable the mouse and 1094 switch (e->type()) { // keyboard events 1095 case QEvent::MouseButtonPress: 1096 case QEvent::MouseButtonRelease: 1097 case QEvent::MouseButtonDblClick: 1098 case QEvent::MouseMove: 1099 case QEvent::KeyPress: 1100 case QEvent::KeyRelease: 1101 return true; 1102 default: 1103 break; 1104 } 1105 } 1106 1107 if (e->type() == QEvent::ShortcutOverride) { 1108 auto *ke = dynamic_cast<QKeyEvent *>(e); 1109 1110 if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::KeypadModifier) { 1111 switch (ke->key()) { 1112 case Qt::Key_Delete: 1113 case Qt::Key_Plus: 1114 case Qt::Key_Minus: 1115 ke->accept(); 1116 break; 1117 } 1118 } else if (ke->modifiers() == Qt::ShiftModifier) { 1119 switch (ke->key()) { 1120 case Qt::Key_Left: 1121 case Qt::Key_Right: 1122 case Qt::Key_Up: 1123 case Qt::Key_Down: 1124 ke->accept(); 1125 break; 1126 } 1127 } else if (ke->modifiers() & Qt::ControlModifier) { 1128 switch (ke->key()) { 1129 case Qt::Key_D: 1130 case Qt::Key_E: 1131 case Qt::Key_F: 1132 case Qt::Key_I: 1133 case Qt::Key_L: 1134 case Qt::Key_N: 1135 case Qt::Key_R: 1136 ke->accept(); 1137 break; 1138 } 1139 } 1140 } 1141 return QStackedWidget::event(e); 1142 }