diff --git a/src/radialMap/widgetEvents.cpp b/src/radialMap/widgetEvents.cpp index 59bfa1a..f30d808 100644 --- a/src/radialMap/widgetEvents.cpp +++ b/src/radialMap/widgetEvents.cpp @@ -1,362 +1,383 @@ /*********************************************************************** * Copyright 2003-2004 Max Howell * Copyright 2008-2009 Martin Sandsmark * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ***********************************************************************/ #include "fileTree.h" #include "Config.h" #include "radialMap.h" //class Segment #include "widget.h" #include //::mouseMoveEvent() #include #include //::mousePressEvent() #include #include #include //::mousePressEvent() #include //::mousePressEvent() #include //::mousePressEvent() #include #include #include #include #include #include //QApplication::setOverrideCursor() #include #include #include //::resizeEvent() #include #include #include #include #include #include #include #include #include //::segmentAt() void RadialMap::Widget::resizeEvent(QResizeEvent*) { if (m_map.resize(rect())) m_timer.setSingleShot(true); m_timer.start(500); //will cause signature to rebuild for new size //always do these as they need to be initialised on creation m_offset.rx() = (width() - m_map.width()) / 2; m_offset.ry() = (height() - m_map.height()) / 2; } void RadialMap::Widget::paintEvent(QPaintEvent*) { QPainter paint; paint.begin(this); if (!m_map.isNull()) paint.drawPixmap(m_offset, m_map.pixmap()); else { paint.drawText(rect(), 0, i18nc("We messed up, the user needs to initiate a rescan.", "Internal representation is invalid,\nplease rescan.")); return; } //exploded labels if (!m_map.isNull() && !m_timer.isActive()) { if (Config::antialias) { paint.setRenderHint(QPainter::Antialiasing); //make lines appear on pixel boundaries paint.translate(0.5, 0.5); } paintExplodedLabels(paint); } } const RadialMap::Segment* RadialMap::Widget::segmentAt(QPoint &e) const { //determine which segment QPoint e is above e -= m_offset; if (!m_map.m_signature) return nullptr; if (e.x() <= m_map.width() && e.y() <= m_map.height()) { //transform to cartesian coords e.rx() -= m_map.width() / 2; //should be an int e.ry() = m_map.height() / 2 - e.y(); double length = hypot(e.x(), e.y()); if (length >= m_map.m_innerRadius) //not hovering over inner circle { uint depth = ((int)length - m_map.m_innerRadius) / m_map.m_ringBreadth; if (depth <= m_map.m_visibleDepth) //**** do earlier since you can //** check not outside of range { //vector calculation, reduces to simple trigonometry //cos angle = (aibi + ajbj) / albl //ai = x, bi=1, aj=y, bj=0 //cos angle = x / (length) uint a = (uint)(acos((double)e.x() / length) * 916.736); //916.7324722 = #radians in circle * 16 //acos only understands 0-180 degrees if (e.y() < 0) a = 5760 - a; for (Segment *segment : m_map.m_signature[depth]) { if (segment->intersects(a)) return segment; } } } else return m_rootSegment; //hovering over inner circle } return nullptr; } void RadialMap::Widget::mouseMoveEvent(QMouseEvent *e) { //set m_focus to what we hover over, update UI if it's a new segment Segment const * const oldFocus = m_focus; QPoint p = e->pos(); m_focus = segmentAt(p); //NOTE p is passed by non-const reference if (m_focus) { m_tooltip.move(e->globalX() + 20, e->globalY() + 20); if (m_focus != oldFocus) //if not same as last time { setCursor(Qt::PointingHandCursor); + QString string; + + if (isSummary()) { + if (strcmp("used", m_focus->file()->name8Bit()) == 0) { + string = i18nc("Tooltip of used space on the partition, %1 is path, %2 is size", "%1\nUsed: %2", + m_focus->file()->parent()->displayPath(), + m_focus->file()->humanReadableSize()); + } else if (strcmp("free", m_focus->file()->name8Bit()) == 0) { + string = i18nc("Tooltip of free space on the partition, %1 is path, %2 is size", "%1\nFree: %2", + m_focus->file()->parent()->displayPath(), + m_focus->file()->humanReadableSize()); + } else { + string = i18nc("Tooltip of file/folder, %1 is path, %2 is size", "%1\n%2", + m_focus->file()->displayPath(), + m_focus->file()->humanReadableSize()); + } + } else { + string = i18nc("Tooltip of file/folder, %1 is path, %2 is size", "%1\n%2", + m_focus->file()->displayPath(), + m_focus->file()->humanReadableSize()); + + if (m_focus->file()->isFolder()) { + int files = static_cast(m_focus->file())->children(); + const uint percent = uint((100 * files) / (double)m_tree->children()); + + string += QLatin1Char('\n'); + if (percent > 0) { + string += i18ncp("Tooltip of folder, %1 is number of files", + "%1 File (%2%)", "%1 Files (%2%)", files, percent); + } else { + string += i18ncp("Tooltip of folder, %1 is number of files", + "%1 File", "%1 Files", files); + } + } - QString string = m_focus->file()->displayPath() - + QLatin1Char('\n') - + m_focus->file()->humanReadableSize(); - - if (m_focus->file()->isFolder()) { - int files = static_cast(m_focus->file())->children(); - const uint percent = uint((100 * files) / (double)m_tree->children()); - string += QLatin1Char('\n'); - string += i18np("File: %1", "Files: %1", files); - - - if (percent > 0) string += QString(QLatin1String(" (%1%)")).arg(percent); - } - - const QUrl url = Widget::url(m_focus->file()); - if (m_focus == m_rootSegment && url != KIO::upUrl(url)) { - string += i18n("\nClick to go up to parent directory"); + const QUrl url = Widget::url(m_focus->file()); + if (m_focus == m_rootSegment && url != KIO::upUrl(url)) { + string += i18n("\nClick to go up to parent directory"); + } } // Calculate a semi-sane size for the tooltip QFontMetrics fontMetrics(font()); int tooltipWidth = 0; int tooltipHeight = 0; for (const QString &part : string.split(QLatin1Char('\n'))) { tooltipHeight += fontMetrics.height(); tooltipWidth = qMax(tooltipWidth, fontMetrics.width(part)); } // Limit it to the window size, probably should find something better tooltipWidth = qMin(tooltipWidth, window()->width()); tooltipWidth += 10; tooltipHeight += 10; m_tooltip.resize(tooltipWidth, tooltipHeight); m_tooltip.setText(string); m_tooltip.show(); emit mouseHover(m_focus->file()->displayPath()); update(); } } else if (oldFocus && oldFocus->file() != m_tree) { m_tooltip.hide(); unsetCursor(); update(); emit mouseHover(QString()); } } void RadialMap::Widget::enterEvent(QEvent *) { if (!m_focus) return; setCursor(Qt::PointingHandCursor); emit mouseHover(m_focus->file()->displayPath()); update(); } void RadialMap::Widget::leaveEvent(QEvent *) { m_tooltip.hide(); } void RadialMap::Widget::mousePressEvent(QMouseEvent *e) { if (!isEnabled()) return; //m_focus is set correctly (I've been strict, I assure you it is correct!) if (!m_focus || m_focus->isFake()) { return; } const QUrl url = Widget::url(m_focus->file()); const bool isDir = m_focus->file()->isFolder(); // Open file if (e->button() == Qt::MidButton || (e->button() == Qt::LeftButton && !isDir)) { new KRun(url, this, true); return; } if (e->button() == Qt::LeftButton) { if (m_focus->file() != m_tree) { emit activated(url); //activate first, this will cause UI to prepare itself createFromCache((Folder *)m_focus->file()); } else if (KIO::upUrl(url) != url) { emit giveMeTreeFor(KIO::upUrl(url)); } return; } if (e->button() != Qt::RightButton) { // Ignore other mouse buttons return; } // Actions in the right click menu QAction* openFileManager = nullptr; QAction* openTerminal = nullptr; QAction* centerMap = nullptr; QAction* openFile = nullptr; QAction* copyClipboard = nullptr; QAction* deleteItem = nullptr; QMenu popup; popup.setTitle(m_focus->file()->displayPath(m_tree)); if (isDir) { openFileManager = popup.addAction(QIcon::fromTheme(QLatin1String("system-file-manager")), i18n("Open &File Manager Here")); if (url.scheme() == QLatin1String("file")) { openTerminal = popup.addAction(QIcon::fromTheme(QLatin1String( "utilities-terminal" )), i18n("Open &Terminal Here")); } if (m_focus->file() != m_tree) { popup.addSeparator(); centerMap = popup.addAction(QIcon::fromTheme(QLatin1String( "zoom-in" )), i18n("&Center Map Here")); } } else { openFile = popup.addAction(QIcon::fromTheme(QLatin1String("document-open")), i18nc("Scan/open the path of the selected element", "&Open")); } popup.addSeparator(); copyClipboard = popup.addAction(QIcon::fromTheme(QLatin1String( "edit-copy" )), i18n("&Copy to clipboard")); if (m_focus->file() != m_tree) { popup.addSeparator(); deleteItem = popup.addAction(QIcon::fromTheme(QLatin1String( "edit-delete" )), i18n("&Delete")); } QAction* clicked = popup.exec(e->globalPos(), nullptr); if (openFileManager && clicked == openFileManager) { KRun::runUrl(url, QLatin1String("inode/directory"), this #if KIO_VERSION >= QT_VERSION_CHECK(5, 31, 0) , KRun::RunFlags() #endif ); } else if (openTerminal && clicked == openTerminal) { KToolInvocation::invokeTerminal(QString(),url.path()); } else if (centerMap && clicked == centerMap) { emit activated(url); //activate first, this will cause UI to prepare itself createFromCache((Folder *)m_focus->file()); } else if (openFile && clicked == openFile) { new KRun(url, this, true); } else if (clicked == copyClipboard) { QMimeData* mimedata = new QMimeData(); mimedata->setUrls(QList() << url); QApplication::clipboard()->setMimeData(mimedata , QClipboard::Clipboard); } else if (clicked == deleteItem && m_focus->file() != m_tree) { m_toBeDeleted = m_focus; const QUrl url = Widget::url(m_toBeDeleted->file()); const QString message = m_toBeDeleted->file()->isFolder() ? i18n("The folder at '%1' will be recursively and permanently deleted.", url.toString()) : i18n("'%1' will be permanently deleted.", url.toString()); const int userIntention = KMessageBox::warningContinueCancel( this, message, QString(), KGuiItem(i18n("&Delete"), QLatin1String("edit-delete"))); if (userIntention == KMessageBox::Continue) { KIO::Job *job = KIO::del(url); connect(job, &KJob::finished, this, &RadialMap::Widget::deleteJobFinished); QApplication::setOverrideCursor(Qt::BusyCursor); setEnabled(false); } } else { //ensure m_focus is set for new mouse position sendFakeMouseEvent(); } } void RadialMap::Widget::deleteJobFinished(KJob *job) { QApplication::restoreOverrideCursor(); setEnabled(true); if (!job->error() && m_toBeDeleted) { m_toBeDeleted->file()->parent()->remove(m_toBeDeleted->file()); delete m_toBeDeleted->file(); m_toBeDeleted = nullptr; m_focus = nullptr; m_map.make(m_tree, true); repaint(); } else KMessageBox::error(this, job->errorString(), i18n("Error while deleting")); } void RadialMap::Widget::dropEvent(QDropEvent *e) { QList uriList = KUrlMimeData::urlsFromMimeData(e->mimeData()); if (!uriList.isEmpty()) emit giveMeTreeFor(uriList.first()); } void RadialMap::Widget::dragEnterEvent(QDragEnterEvent *e) { QList uriList = KUrlMimeData::urlsFromMimeData(e->mimeData()); e->setAccepted(!uriList.isEmpty()); } void RadialMap::Widget::changeEvent(QEvent *e) { if (e->type() == QEvent::ApplicationPaletteChange || e->type() == QEvent::PaletteChange) m_map.paint(); } diff --git a/src/summaryWidget.cpp b/src/summaryWidget.cpp index 2b86558..8e86364 100644 --- a/src/summaryWidget.cpp +++ b/src/summaryWidget.cpp @@ -1,184 +1,181 @@ /*********************************************************************** * Copyright 2003-2004 Max Howell * Copyright 2008-2009 Martin Sandsmark * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ***********************************************************************/ #include "summaryWidget.h" #include "Config.h" #include "fileTree.h" #include "radialMap/radialMap.h" #include "radialMap/widget.h" #include #include #include #include #include #include #include #include #include namespace Filelight { struct Disk { QString mount; QString name; qint64 size; qint64 used; qint64 free; //NOTE used+avail != size (clustersize!) }; struct DiskList : QList { DiskList(); }; class MyRadialMap : public RadialMap::Widget { Q_OBJECT public: MyRadialMap(QWidget *parent) : RadialMap::Widget(parent, true) { } virtual void setCursor(const QCursor &c) { if (focusSegment() && focusSegment()->file()->decodedName() == QLatin1String( "Used" )) RadialMap::Widget::setCursor(c); else unsetCursor(); } void mousePressEvent(QMouseEvent *e) override { const RadialMap::Segment *segment = focusSegment(); //we will allow right clicks to the center circle if (segment == rootSegment() && e->button() == Qt::RightButton) RadialMap::Widget::mousePressEvent(e); //and clicks to the used segment else if (e->button() == Qt::LeftButton ) { const QRect rect(e->x() - 20, e->y() - 20, 40, 40); // KIconEffect::visualActivate(this, rect); TODO: Re-enable emit activated(url()); } } }; SummaryWidget::SummaryWidget(QWidget *parent) : QWidget(parent) { qApp->setOverrideCursor(Qt::WaitCursor); setLayout(new QGridLayout(this)); createDiskMaps(); qApp->restoreOverrideCursor(); } void SummaryWidget::createDiskMaps() { DiskList disks; - const QByteArray free = i18nc("Free space on the disks/partitions", "Free").toUtf8(); - const QByteArray used = i18nc("Used space on the disks/partitions", "Used").toUtf8(); - QString text; for (DiskList::ConstIterator it = disks.constBegin(), end = disks.constEnd(); it != end; ++it) { Disk const &disk = *it; if (disk.free == 0 && disk.used == 0) continue; QWidget *volume = new QWidget(this); QVBoxLayout *volumeLayout = new QVBoxLayout(volume); RadialMap::Widget *map = new MyRadialMap(this); QWidget *info = new QWidget(this); info->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); QHBoxLayout* horizontalLayout = new QHBoxLayout(info); // Create the text under the radialMap. if (disk.name.isEmpty()) { text = i18nc("Percent used disk space on the partition", "%1
%2% Used", disk.mount, disk.used*100/disk.size); } else { text = i18nc("Percent used disk space on the partition", "%1: %2
%3% Used", disk.name, disk.mount, disk.used*100/disk.size); } QLabel *label = new QLabel(text, this); label->setAlignment(Qt::AlignHCenter); horizontalLayout->addWidget(label); horizontalLayout->setAlignment(Qt::AlignCenter); volumeLayout->addWidget(map); volumeLayout->addWidget(info); // row (=n/2) column (0 or 1) qobject_cast(layout())->addWidget(volume, layout()->count()/2, layout()->count() % 2); Folder *tree = new Folder(disk.mount.toUtf8().constData()); - tree->append(free.constData(), disk.free); - tree->append(used.constData(), disk.used); + tree->append("free", disk.free); + tree->append("used", disk.used); map->create(tree); //must be done when visible connect(map, &RadialMap::Widget::activated, this, &SummaryWidget::activated); } } DiskList::DiskList() { static const QSet ignoredFsTypes = { "tmpfs", "squashfs" }; QStringList partitions; for (const QStorageInfo &storage : QStorageInfo::mountedVolumes()) { if (!storage.isReady() || ignoredFsTypes.contains(storage.fileSystemType())) { continue; } Disk disk; disk.mount = storage.rootPath(); disk.name = storage.name(); disk.size = storage.bytesTotal(); disk.free = storage.bytesFree(); disk.used = disk.size - disk.free; *this += disk; } } } #include "summaryWidget.moc"