File indexing completed on 2024-05-05 17:56:54
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 "dulines.h" 0009 0010 #include "../FileSystem/krpermhandler.h" 0011 #include "../icon.h" 0012 #include "../krglobal.h" 0013 0014 // QtCore 0015 #include <QTimer> 0016 // QtGui 0017 #include <QFontMetrics> 0018 #include <QKeyEvent> 0019 #include <QMouseEvent> 0020 #include <QPainter> 0021 #include <QPen> 0022 #include <QPixmap> 0023 // QtWidgets 0024 #include <QApplication> 0025 #include <QHeaderView> 0026 #include <QItemDelegate> 0027 #include <QMenu> 0028 #include <QToolTip> 0029 0030 #include <KConfigCore/KSharedConfig> 0031 #include <KI18n/KLocalizedString> 0032 0033 #include "../compat.h" 0034 0035 class DULinesItemDelegate : public QItemDelegate 0036 { 0037 public: 0038 explicit DULinesItemDelegate(QObject *parent = nullptr) 0039 : QItemDelegate(parent) 0040 { 0041 } 0042 0043 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override 0044 { 0045 QItemDelegate::paint(painter, option, index); 0046 0047 QVariant value = index.data(Qt::UserRole); 0048 if (value.isValid()) { 0049 QString text = value.toString(); 0050 0051 value = index.data(Qt::DisplayRole); 0052 QString display; 0053 if (value.isValid()) 0054 display = value.toString(); 0055 0056 QSize iconSize; 0057 value = index.data(Qt::DecorationRole); 0058 if (value.isValid()) 0059 iconSize = qvariant_cast<QIcon>(value).actualSize(option.decorationSize); 0060 0061 painter->save(); 0062 painter->setClipRect(option.rect); 0063 0064 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; 0065 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) 0066 cg = QPalette::Inactive; 0067 if (option.state & QStyle::State_Selected) { 0068 painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); 0069 } else { 0070 painter->setPen(option.palette.color(cg, QPalette::Text)); 0071 } 0072 0073 QFont fnt = option.font; 0074 fnt.setItalic(true); 0075 painter->setFont(fnt); 0076 0077 QFontMetrics fm(fnt); 0078 QString renderedText = text; 0079 0080 int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin); 0081 int pos = 3 * textMargin + option.fontMetrics.horizontalAdvance(display) + iconSize.width(); 0082 0083 bool truncd = false; 0084 0085 QRect rct = option.rect; 0086 if (rct.width() > pos) { 0087 rct.setX(rct.x() + pos); 0088 0089 if (fm.horizontalAdvance(renderedText) > rct.width()) { 0090 truncd = true; 0091 0092 int points = fm.horizontalAdvance("..."); 0093 0094 while (!renderedText.isEmpty() && (fm.horizontalAdvance(renderedText) + points > rct.width())) 0095 renderedText.truncate(renderedText.length() - 1); 0096 0097 renderedText += "..."; 0098 } 0099 0100 painter->drawText(rct, Qt::AlignLeft, renderedText); 0101 } else 0102 truncd = true; 0103 0104 if (truncd) 0105 const_cast<QAbstractItemModel *>(index.model())->setData(index, QVariant(display + " " + text), Qt::ToolTipRole); 0106 else 0107 const_cast<QAbstractItemModel *>(index.model())->setData(index, QVariant(), Qt::ToolTipRole); 0108 0109 painter->restore(); 0110 } 0111 } 0112 }; 0113 0114 class DULinesItem : public QTreeWidgetItem 0115 { 0116 public: 0117 DULinesItem(File *fileItem, QTreeWidget *parent, const QString &label1, const QString &label2, const QString &label3) 0118 : QTreeWidgetItem(parent) 0119 , file(fileItem) 0120 { 0121 setText(0, label1); 0122 setText(1, label2); 0123 setText(2, label3); 0124 0125 setTextAlignment(1, Qt::AlignRight); 0126 } 0127 DULinesItem(File *fileItem, QTreeWidget *parent, QTreeWidgetItem *after, const QString &label1, const QString &label2, const QString &label3) 0128 : QTreeWidgetItem(parent, after) 0129 , file(fileItem) 0130 { 0131 setText(0, label1); 0132 setText(1, label2); 0133 setText(2, label3); 0134 0135 setTextAlignment(1, Qt::AlignRight); 0136 } 0137 0138 bool operator<(const QTreeWidgetItem &other) const override 0139 { 0140 int column = treeWidget() ? treeWidget()->sortColumn() : 0; 0141 0142 if (text(0) == "..") 0143 return true; 0144 0145 const auto *compWith = dynamic_cast<const DULinesItem *>(&other); 0146 if (compWith == nullptr) 0147 return false; 0148 0149 switch (column) { 0150 case 0: 0151 case 1: 0152 return file->size() > compWith->file->size(); 0153 default: 0154 return text(column) < other.text(column); 0155 } 0156 } 0157 0158 inline File *getFile() 0159 { 0160 return file; 0161 } 0162 0163 private: 0164 File *file; 0165 }; 0166 0167 DULines::DULines(DiskUsage *usage) 0168 : KrTreeWidget(usage) 0169 , diskUsage(usage) 0170 , refreshNeeded(false) 0171 , started(false) 0172 { 0173 setItemDelegate(itemDelegate = new DULinesItemDelegate()); 0174 0175 setAllColumnsShowFocus(true); 0176 setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0177 setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 0178 setIndentation(10); 0179 0180 int defaultSize = QFontMetrics(font()).horizontalAdvance("W"); 0181 0182 QStringList labels; 0183 labels << i18n("Line View"); 0184 labels << i18n("Percent"); 0185 labels << i18n("Name"); 0186 setHeaderLabels(labels); 0187 0188 header()->setSectionResizeMode(QHeaderView::Interactive); 0189 0190 KConfigGroup group(krConfig, diskUsage->getConfigGroup()); 0191 0192 showFileSize = group.readEntry("L Show File Size", true); 0193 0194 if (group.hasKey("L State")) 0195 header()->restoreState(group.readEntry("L State", QByteArray())); 0196 else { 0197 setColumnWidth(0, defaultSize * 20); 0198 setColumnWidth(1, defaultSize * 6); 0199 setColumnWidth(2, defaultSize * 20); 0200 } 0201 0202 setStretchingColumn(0); 0203 0204 header()->setSortIndicatorShown(true); 0205 sortItems(1, Qt::AscendingOrder); 0206 0207 connect(diskUsage, &DiskUsage::enteringDirectory, this, &DULines::slotDirChanged); 0208 connect(diskUsage, &DiskUsage::clearing, this, &DULines::clear); 0209 0210 connect(header(), &QHeaderView::sectionResized, this, &DULines::sectionResized); 0211 0212 connect(this, &DULines::itemRightClicked, this, &DULines::slotRightClicked); 0213 connect(diskUsage, &DiskUsage::changed, this, &DULines::slotChanged); 0214 connect(diskUsage, &DiskUsage::deleted, this, &DULines::slotDeleted); 0215 0216 started = true; 0217 } 0218 0219 DULines::~DULines() 0220 { 0221 KConfigGroup group(krConfig, diskUsage->getConfigGroup()); 0222 group.writeEntry("L State", header()->saveState()); 0223 0224 delete itemDelegate; 0225 } 0226 0227 bool DULines::event(QEvent *event) 0228 { 0229 switch (event->type()) { 0230 case QEvent::ToolTip: { 0231 auto *he = dynamic_cast<QHelpEvent *>(event); 0232 0233 if (viewport()) { 0234 QPoint pos = viewport()->mapFromGlobal(he->globalPos()); 0235 0236 QTreeWidgetItem *item = itemAt(pos); 0237 0238 int column = columnAt(pos.x()); 0239 0240 if (item && column == 1) { 0241 File *fileItem = (dynamic_cast<DULinesItem *>(item))->getFile(); 0242 QToolTip::showText(he->globalPos(), diskUsage->getToolTip(fileItem), this); 0243 return true; 0244 } 0245 } 0246 } break; 0247 default: 0248 break; 0249 } 0250 return KrTreeWidget::event(event); 0251 } 0252 0253 void DULines::slotDirChanged(Directory *dirEntry) 0254 { 0255 clear(); 0256 0257 QTreeWidgetItem *lastItem = nullptr; 0258 0259 if (!(dirEntry->parent() == nullptr)) { 0260 lastItem = new QTreeWidgetItem(this); 0261 lastItem->setText(0, ".."); 0262 lastItem->setIcon(0, Icon("go-up")); 0263 lastItem->setFlags(lastItem->flags() & (~Qt::ItemIsSelectable)); 0264 } 0265 0266 int maxPercent = -1; 0267 for (Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it) { 0268 File *item = *it; 0269 if (!item->isExcluded() && item->intPercent() > maxPercent) 0270 maxPercent = item->intPercent(); 0271 } 0272 0273 for (Iterator<File> it = dirEntry->iterator(); it != dirEntry->end(); ++it) { 0274 File *item = *it; 0275 0276 QString fileName = item->name(); 0277 0278 if (lastItem == nullptr) 0279 lastItem = new DULinesItem(item, this, "", item->percent() + " ", fileName); 0280 else 0281 lastItem = new DULinesItem(item, this, lastItem, "", item->percent() + " ", fileName); 0282 0283 if (item->isExcluded()) 0284 lastItem->setHidden(true); 0285 0286 int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; 0287 0288 lastItem->setIcon(2, diskUsage->getIcon(item->mime())); 0289 lastItem->setData(0, Qt::DecorationRole, createPixmap(item->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); 0290 0291 if (showFileSize) 0292 lastItem->setData(2, Qt::UserRole, " [" + KIO::convertSize(item->size()) + ']'); 0293 0294 QSize size = lastItem->sizeHint(0); 0295 size.setWidth(16); 0296 lastItem->setSizeHint(0, size); 0297 } 0298 0299 if (topLevelItemCount() > 0) { 0300 setCurrentItem(topLevelItem(0)); 0301 } 0302 } 0303 0304 QPixmap DULines::createPixmap(int percent, int maxPercent, int maxWidth) 0305 { 0306 if (percent < 0 || percent > maxPercent || maxWidth < 2 || maxPercent == 0) 0307 return QPixmap(); 0308 maxWidth -= 2; 0309 0310 int actualWidth = maxWidth * percent / maxPercent; 0311 if (actualWidth == 0) 0312 return QPixmap(); 0313 0314 QPen pen; 0315 pen.setColor(Qt::black); 0316 QPainter painter; 0317 0318 int size = QFontMetrics(font()).height() - 2; 0319 QRect rect(0, 0, actualWidth, size); 0320 QRect frameRect(0, 0, actualWidth - 1, size - 1); 0321 QPixmap pixmap(rect.width(), rect.height()); 0322 0323 painter.begin(&pixmap); 0324 painter.setPen(pen); 0325 0326 for (int i = 1; i < actualWidth - 1; i++) { 0327 int color = (511 * i / (maxWidth - 1)); 0328 if (color < 256) 0329 pen.setColor(QColor(255 - color, 255, 0)); 0330 else 0331 pen.setColor(QColor(color - 256, 511 - color, 0)); 0332 0333 painter.setPen(pen); 0334 painter.drawLine(i, 1, i, size - 1); 0335 } 0336 0337 pen.setColor(Qt::black); 0338 painter.setPen(pen); 0339 0340 if (actualWidth != 1) 0341 painter.drawRect(frameRect); 0342 else 0343 painter.drawLine(0, 0, 0, size); 0344 0345 painter.end(); 0346 pixmap.detach(); 0347 return pixmap; 0348 } 0349 0350 void DULines::resizeEvent(QResizeEvent *re) 0351 { 0352 KrTreeWidget::resizeEvent(re); 0353 0354 if (started && (re->oldSize() != re->size())) 0355 sectionResized(0); 0356 } 0357 0358 void DULines::sectionResized(int column) 0359 { 0360 if (topLevelItemCount() == 0 || column != 0) 0361 return; 0362 0363 Directory *currentDir = diskUsage->getCurrentDir(); 0364 if (currentDir == nullptr) 0365 return; 0366 0367 int maxPercent = -1; 0368 for (Iterator<File> it = currentDir->iterator(); it != currentDir->end(); ++it) { 0369 File *item = *it; 0370 0371 if (!item->isExcluded() && item->intPercent() > maxPercent) 0372 maxPercent = item->intPercent(); 0373 } 0374 0375 QTreeWidgetItemIterator it2(this); 0376 while (*it2) { 0377 QTreeWidgetItem *lvitem = *it2; 0378 if (lvitem->text(0) != "..") { 0379 auto *duItem = dynamic_cast<DULinesItem *>(lvitem); 0380 if (duItem) { 0381 int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; 0382 duItem->setData(0, Qt::DecorationRole, createPixmap(duItem->getFile()->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); 0383 QSize size = duItem->sizeHint(0); 0384 size.setWidth(16); 0385 duItem->setSizeHint(0, size); 0386 } 0387 } 0388 it2++; 0389 } 0390 } 0391 0392 bool DULines::doubleClicked(QTreeWidgetItem *item) 0393 { 0394 if (item) { 0395 if (item->text(0) != "..") { 0396 File *fileItem = (dynamic_cast<DULinesItem *>(item))->getFile(); 0397 if (fileItem->isDir()) 0398 diskUsage->changeDirectory(dynamic_cast<Directory *>(fileItem)); 0399 return true; 0400 } else { 0401 auto *upDir = const_cast<Directory *>(diskUsage->getCurrentDir()->parent()); 0402 0403 if (upDir) 0404 diskUsage->changeDirectory(upDir); 0405 return true; 0406 } 0407 } 0408 return false; 0409 } 0410 0411 void DULines::mouseDoubleClickEvent(QMouseEvent *e) 0412 { 0413 if (e || e->button() == Qt::LeftButton) { 0414 QPoint vp = viewport()->mapFromGlobal(e->globalPos()); 0415 QTreeWidgetItem *item = itemAt(vp); 0416 0417 if (doubleClicked(item)) 0418 return; 0419 } 0420 KrTreeWidget::mouseDoubleClickEvent(e); 0421 } 0422 0423 void DULines::keyPressEvent(QKeyEvent *e) 0424 { 0425 switch (e->key()) { 0426 case Qt::Key_Return: 0427 case Qt::Key_Enter: 0428 if (doubleClicked(currentItem())) 0429 return; 0430 break; 0431 case Qt::Key_Left: 0432 case Qt::Key_Right: 0433 case Qt::Key_Up: 0434 case Qt::Key_Down: 0435 if (e->modifiers() == Qt::ShiftModifier) { 0436 e->ignore(); 0437 return; 0438 } 0439 break; 0440 case Qt::Key_Delete: 0441 e->ignore(); 0442 return; 0443 } 0444 KrTreeWidget::keyPressEvent(e); 0445 } 0446 0447 void DULines::slotRightClicked(QTreeWidgetItem *item, const QPoint &pos) 0448 { 0449 File *file = nullptr; 0450 0451 if (item && item->text(0) != "..") 0452 file = (dynamic_cast<DULinesItem *>(item))->getFile(); 0453 0454 QMenu linesPopup; 0455 QAction *act = linesPopup.addAction(i18n("Show file sizes"), this, SLOT(slotShowFileSizes())); 0456 act->setChecked(showFileSize); 0457 0458 diskUsage->rightClickMenu(pos, file, &linesPopup, i18n("Lines")); 0459 } 0460 0461 void DULines::slotShowFileSizes() 0462 { 0463 showFileSize = !showFileSize; 0464 slotDirChanged(diskUsage->getCurrentDir()); 0465 } 0466 0467 File *DULines::getCurrentFile() 0468 { 0469 QTreeWidgetItem *item = currentItem(); 0470 0471 if (item == nullptr || item->text(0) == "..") 0472 return nullptr; 0473 0474 return (dynamic_cast<DULinesItem *>(item))->getFile(); 0475 } 0476 0477 void DULines::slotChanged(File *item) 0478 { 0479 QTreeWidgetItemIterator it(this); 0480 while (*it) { 0481 QTreeWidgetItem *lvitem = *it; 0482 it++; 0483 0484 if (lvitem->text(0) != "..") { 0485 auto *duItem = dynamic_cast<DULinesItem *>(lvitem); 0486 if (duItem->getFile() == item) { 0487 setSortingEnabled(false); 0488 duItem->setHidden(item->isExcluded()); 0489 duItem->setText(1, item->percent()); 0490 if (!refreshNeeded) { 0491 refreshNeeded = true; 0492 QTimer::singleShot(0, this, &DULines::slotRefresh); 0493 } 0494 break; 0495 } 0496 } 0497 } 0498 } 0499 0500 void DULines::slotDeleted(File *item) 0501 { 0502 QTreeWidgetItemIterator it(this); 0503 while (*it) { 0504 QTreeWidgetItem *lvitem = *it; 0505 it++; 0506 0507 if (lvitem->text(0) != "..") { 0508 auto *duItem = dynamic_cast<DULinesItem *>(lvitem); 0509 if (duItem->getFile() == item) { 0510 delete duItem; 0511 break; 0512 } 0513 } 0514 } 0515 } 0516 0517 void DULines::slotRefresh() 0518 { 0519 if (refreshNeeded) { 0520 refreshNeeded = false; 0521 setSortingEnabled(true); 0522 sortItems(1, Qt::AscendingOrder); 0523 } 0524 }