Warning, file /utilities/krusader/app/DiskUsage/radialMap/widgetEvents.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2003-2004 Max Howell <max.howell@methylblue.com> 0003 SPDX-FileCopyrightText: 2004-2022 Krusader Krew <https://krusader.org> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "../../icon.h" 0009 #include "fileTree.h" 0010 #include "radialMap.h" //class Segment 0011 #include "widget.h" 0012 0013 #include <cmath> 0014 0015 // QtCore 0016 #include <QTimer> //::resizeEvent() 0017 // QtGui 0018 #include <QPaintEvent> 0019 #include <QPainter> 0020 // QtWidgets 0021 #include <QApplication> //QApplication::setOverrideCursor() 0022 #include <QMenu> 0023 0024 #include <KI18n/KLocalizedString> 0025 #include <KIO/DeleteJob> 0026 #include <KIO/JobUiDelegate> 0027 #include <KIOWidgets/KRun> 0028 #include <KWidgetsAddons/KMessageBox> 0029 #include <kio_version.h> 0030 #include <kservice_version.h> 0031 0032 #if KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0) 0033 #include <KIO/OpenUrlJob> 0034 #endif 0035 0036 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 83, 0) 0037 #include <KTerminalLauncherJob> 0038 #else 0039 #include <KToolInvocation> 0040 #endif 0041 0042 void RadialMap::Widget::resizeEvent(QResizeEvent *) 0043 { 0044 if (m_map.resize(rect())) { 0045 m_timer.setSingleShot(true); 0046 m_timer.start(500); // will cause signature to rebuild for new size 0047 } 0048 0049 // always do these as they need to be initialised on creation 0050 m_offset.rx() = (width() - m_map.width()) / 2; 0051 m_offset.ry() = (height() - m_map.height()) / 2; 0052 } 0053 0054 void RadialMap::Widget::paintEvent(QPaintEvent *) 0055 { 0056 // bltBit for some Qt setups will bitBlt _after_ the labels are painted. Which buggers things up! 0057 // shame as bitBlt is faster, possibly Qt bug? Should report the bug? - seems to be race condition 0058 // bitBlt( this, m_offset, &m_map ); 0059 0060 QPainter paint(this); 0061 0062 paint.drawPixmap(m_offset, m_map); 0063 0064 // vertical strips 0065 if (m_map.width() < width()) { 0066 paint.eraseRect(0, 0, m_offset.x(), height()); 0067 paint.eraseRect(m_map.width() + m_offset.x(), 0, m_offset.x() + 1, height()); 0068 } 0069 // horizontal strips 0070 if (m_map.height() < height()) { 0071 paint.eraseRect(0, 0, width(), m_offset.y()); 0072 paint.eraseRect(0, m_map.height() + m_offset.y(), width(), m_offset.y() + 1); 0073 } 0074 0075 // exploded labels 0076 if (!m_map.isNull() && !m_timer.isActive()) 0077 paintExplodedLabels(paint); 0078 } 0079 0080 const RadialMap::Segment *RadialMap::Widget::segmentAt(QPoint &e) const 0081 { 0082 // determine which segment QPoint e is above 0083 0084 e -= m_offset; 0085 0086 if (e.x() <= m_map.width() && e.y() <= m_map.height()) { 0087 // transform to cartesian coords 0088 e.rx() -= m_map.width() / 2; // should be an int 0089 e.ry() = m_map.height() / 2 - e.y(); 0090 0091 double length = std::hypot(e.x(), e.y()); 0092 0093 if (length >= m_map.m_innerRadius) { // not hovering over inner circle 0094 uint depth = ((int)length - m_map.m_innerRadius) / m_map.m_ringBreadth; 0095 0096 if (depth <= m_map.m_visibleDepth) { //**** do earlier since you can //** check not outside of range 0097 // vector calculation, reduces to simple trigonometry 0098 // cos angle = (aibi + ajbj) / albl 0099 // ai = x, bi=1, aj=y, bj=0 0100 // cos angle = x / (length) 0101 0102 auto a = (uint)(acos((double)e.x() / length) * 916.736); // 916.7324722 = #radians in circle * 16 0103 0104 // acos only understands 0-180 degrees 0105 if (e.y() < 0) 0106 a = 5760 - a; 0107 0108 #define ring (m_map.m_signature + depth) 0109 for (ConstIterator<Segment> it = ring->constIterator(); it != ring->end(); ++it) 0110 if ((*it)->intersects(a)) 0111 return *it; 0112 #undef ring 0113 } 0114 } else 0115 return m_rootSegment; // hovering over inner circle 0116 } 0117 0118 return nullptr; 0119 } 0120 0121 void RadialMap::Widget::mouseMoveEvent(QMouseEvent *e) 0122 { 0123 // set m_focus to what we hover over, update UI if it's a new segment 0124 0125 Segment const *const oldFocus = m_focus; 0126 QPoint p = e->pos(); 0127 0128 m_focus = segmentAt(p); // NOTE p is passed by non-const reference 0129 0130 if (m_focus && m_focus->file() != m_tree) { 0131 if (m_focus != oldFocus) { // if not same as last time 0132 setCursor(QCursor(Qt::PointingHandCursor)); 0133 m_tip.updateTip(m_focus->file(), m_tree); 0134 emit mouseHover(m_focus->file()->fullPath()); 0135 0136 // repaint required to update labels now before transparency is generated 0137 repaint(); 0138 } 0139 0140 // updates tooltip pseudo-transparent background 0141 m_tip.moveto(e->globalPos(), *this, (p.y() < 0)); 0142 } else if (oldFocus && oldFocus->file() != m_tree) { 0143 unsetCursor(); 0144 m_tip.hide(); 0145 update(); 0146 0147 emit mouseHover(QString()); 0148 } 0149 } 0150 0151 void RadialMap::Widget::mousePressEvent(QMouseEvent *e) 0152 { 0153 // m_tip is hidden already by event filter 0154 // m_focus is set correctly (I've been strict, I assure you it is correct!) 0155 0156 if (m_focus && !m_focus->isFake()) { 0157 const QUrl url = Widget::url(m_focus->file()); 0158 const bool isDir = m_focus->file()->isDir(); 0159 0160 if (e->button() == Qt::RightButton) { 0161 QMenu popup; 0162 popup.setTitle(m_focus->file()->fullPath(m_tree)); 0163 0164 QAction *actKonq = nullptr, *actKonsole = nullptr, *actViewMag = nullptr, *actFileOpen = nullptr, *actEditDel = nullptr; 0165 0166 if (isDir) { 0167 actKonq = popup.addAction(Icon("system-file-manager"), i18n("Open File Manager Here")); 0168 if (url.scheme() == "file") 0169 actKonsole = popup.addAction(Icon("utilities-terminal"), i18n("Open Terminal Here")); 0170 0171 if (m_focus->file() != m_tree) { 0172 popup.addSeparator(); 0173 actViewMag = popup.addAction(Icon("zoom-original"), i18n("&Center Map Here")); 0174 } 0175 } else 0176 actFileOpen = popup.addAction(Icon("document-open"), i18n("&Open")); 0177 0178 popup.addSeparator(); 0179 actEditDel = popup.addAction(Icon("edit-delete"), i18n("&Delete")); 0180 0181 QAction *result = popup.exec(e->globalPos()); 0182 if (result == nullptr) 0183 result = (QAction *)-1; // sanity 0184 0185 if (result == actKonq) { 0186 #if KIO_VERSION >= QT_VERSION_CHECK(5, 71, 0) 0187 // KJob jobs will delete themselves when they finish (see kjob.h for more info) 0188 auto *job = new KIO::OpenUrlJob(url, this); 0189 job->start(); 0190 #else 0191 // KRun::runCommand will show an error message if there was trouble 0192 KRun::runCommand(QString("kfmclient openURL '%1'").arg(url.url()), this); 0193 #endif 0194 } else if (result == actKonsole) { 0195 #if KSERVICE_VERSION >= QT_VERSION_CHECK(5, 83, 0) 0196 auto *job = new KTerminalLauncherJob(QString()); 0197 job->setWorkingDirectory(url.url()); 0198 job->start(); 0199 #elif KSERVICE_VERSION >= QT_VERSION_CHECK(5, 79, 0) 0200 KToolInvocation::invokeTerminal(QString(), QStringList(), url.url()); 0201 #else 0202 KToolInvocation::invokeTerminal(QString(), url.url()); 0203 #endif 0204 } else if (result == actViewMag || result == actFileOpen) { 0205 goto sectionTwo; 0206 } else if (result == actEditDel) { 0207 const QUrl url = Widget::url(m_focus->file()); 0208 const QString message = 0209 (m_focus->file()->isDir() 0210 ? i18n("<qt>The folder at <i>'%1'</i> will be <b>recursively</b> and <b>permanently</b> deleted.</qt>", url.toDisplayString()) 0211 : i18n("<qt><i>'%1'</i> will be <b>permanently</b> deleted.</qt>", url.toDisplayString())); 0212 const int userIntention = KMessageBox::warningContinueCancel(this, message, QString(), KStandardGuiItem::del()); 0213 0214 if (userIntention == KMessageBox::Continue) { 0215 KIO::Job *job = KIO::del(url); 0216 auto *ui = dynamic_cast<KIO::JobUiDelegate *>(job->uiDelegate()); 0217 ui->setWindow(this); 0218 connect(job, &KIO::Job::result, this, &Widget::deleteJobFinished); 0219 QApplication::setOverrideCursor(Qt::BusyCursor); 0220 } 0221 } else { 0222 // ensure m_focus is set for new mouse position 0223 sendFakeMouseEvent(); 0224 } 0225 0226 } else { 0227 sectionTwo: 0228 0229 const QRect rect(e->x() - 20, e->y() - 20, 40, 40); 0230 0231 m_tip.hide(); // user expects this 0232 0233 if (!isDir || e->button() == Qt::MidButton) { 0234 new KRun(url, this, true); // FIXME see above 0235 } else if (m_focus->file() != m_tree) { // is left mouse button 0236 emit activated(url); // activate first, this will cause UI to prepare itself 0237 if (m_focus) 0238 createFromCache(dynamic_cast<const Directory *>(m_focus->file())); 0239 } 0240 } 0241 } 0242 } 0243 0244 void RadialMap::Widget::deleteJobFinished(KJob *job) 0245 { 0246 QApplication::restoreOverrideCursor(); 0247 if (!job->error()) 0248 invalidate(); 0249 else 0250 job->uiDelegate()->showErrorMessage(); 0251 }