diff --git a/plugins/fsview/fsview.cpp b/plugins/fsview/fsview.cpp index 324858365..fe29c0bf1 100644 --- a/plugins/fsview/fsview.cpp +++ b/plugins/fsview/fsview.cpp @@ -1,593 +1,592 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * FSView specialization of TreeMap classes. */ #include "fsview.h" #include #include #include #include #include #include #include #include #include #include #include #include "fsviewdebug.h" // FSView QMap FSView::_dirMetric; FSView::FSView(Inode *base, QWidget *parent) : TreeMapWidget(base, parent) { setFieldType(0, i18n("Name")); setFieldType(1, i18n("Size")); setFieldType(2, i18n("File Count")); setFieldType(3, i18n("Directory Count")); setFieldType(4, i18n("Last Modified")); setFieldType(5, i18n("Owner")); setFieldType(6, i18n("Group")); setFieldType(7, i18n("Mime Type")); // defaults setVisibleWidth(4, true); setSplitMode(TreeMapItem::Rows); setFieldForced(0, true); // show directory names setFieldForced(1, true); // show directory sizes setSelectionMode(TreeMapWidget::Extended); _colorMode = Depth; _pathDepth = 0; _allowRefresh = true; _progressPhase = 0; _chunkData1 = 0; _chunkData2 = 0; _chunkData3 = 0; _chunkSize1 = 0; _chunkSize2 = 0; _chunkSize3 = 0; _progressSize = 0; _progress = 0; _dirsFinished = 0; _lastDir = nullptr; _config = new KConfig(QStringLiteral("fsviewrc")); // restore TreeMap visualization options of last execution KConfigGroup tmconfig(_config, "TreeMap"); restoreOptions(&tmconfig); QString str = tmconfig.readEntry("ColorMode"); if (!str.isEmpty()) { setColorMode(str); } if (_dirMetric.count() == 0) { // restore metric cache KConfigGroup cconfig(_config, "MetricCache"); int ccount = cconfig.readEntry("Count", 0); int i, f, d; double s; QString str; for (i = 1; i <= ccount; i++) { str = QStringLiteral("Dir%1").arg(i); if (!cconfig.hasKey(str)) { continue; } str = cconfig.readPathEntry(str, QString()); s = cconfig.readEntry(QStringLiteral("Size%1").arg(i), 0.0); f = cconfig.readEntry(QStringLiteral("Files%1").arg(i), 0); d = cconfig.readEntry(QStringLiteral("Dirs%1").arg(i), 0); if (s == 0.0 || f == 0 || d == 0) { continue; } setDirMetric(str, s, f, d); } } _sm.setListener(this); } FSView::~FSView() { delete _config; } void FSView::stop() { _sm.stopScan(); } void FSView::setPath(const QString &p) { Inode *b = (Inode *)base(); if (!b) { return; } //qCDebug(FSVIEWLOG) << "FSView::setPath " << p; // stop any previous updating stop(); QFileInfo fi(p); _path = fi.absoluteFilePath(); if (!fi.isDir()) { _path = fi.absolutePath(); } _path = QDir::cleanPath(_path); _pathDepth = _path.count('/'); QUrl u = QUrl::fromLocalFile(_path); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("list"), QUrl(), u)) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, u.toDisplayString()); KMessageBox::sorry(this, msg); } ScanDir *d = _sm.setTop(_path); b->setPeer(d); setWindowTitle(QStringLiteral("%1 - FSView").arg(_path)); requestUpdate(b); } QList FSView::selectedUrls() { QList urls; foreach (TreeMapItem *i, selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); urls.append(u); } return urls; } bool FSView::getDirMetric(const QString &k, double &s, unsigned int &f, unsigned int &d) { QMap::iterator it; it = _dirMetric.find(k); if (it == _dirMetric.end()) { return false; } s = (*it).size; f = (*it).fileCount; d = (*it).dirCount; if (0) { qCDebug(FSVIEWLOG) << "getDirMetric " << k; } if (0) { qCDebug(FSVIEWLOG) << " - got size " << s << ", files " << f; } return true; } void FSView::setDirMetric(const QString &k, double s, unsigned int f, unsigned int d) { if (0) qCDebug(FSVIEWLOG) << "setDirMetric '" << k << "': size " << s << ", files " << f << ", dirs " << d << endl; _dirMetric.insert(k, MetricEntry(s, f, d)); } void FSView::requestUpdate(Inode *i) { if (0) qCDebug(FSVIEWLOG) << "FSView::requestUpdate(" << i->path() << ")" << endl; ScanDir *peer = i->dirPeer(); if (!peer) { return; } peer->clear(); i->clear(); if (!_sm.scanRunning()) { QTimer::singleShot(0, this, SLOT(doUpdate())); QTimer::singleShot(100, this, SLOT(doRedraw())); /* start new progress chunk */ _progressPhase = 1; _chunkData1 += 3; _chunkData2 = _chunkData1 + 1; _chunkData3 = _chunkData1 + 2; _chunkSize1 = 0; _chunkSize2 = 0; _chunkSize3 = 0; peer->setData(_chunkData1); _progressSize = 0; _progress = 0; _dirsFinished = 0; _lastDir = nullptr; emit started(); } _sm.startScan(peer); } void FSView::scanFinished(ScanDir *d) { /* if finished directory was from last progress chunk, increment */ int data = d->data(); switch (_progressPhase) { case 1: if (data == _chunkData1) { _chunkSize1--; } break; case 2: if (data == _chunkData1) { _progress++; } if (data == _chunkData2) { _chunkSize2--; } break; case 3: if ((data == _chunkData1) || (data == _chunkData2)) { _progress++; } if (data == _chunkData3) { _chunkSize3--; } break; case 4: if ((data == _chunkData1) || (data == _chunkData2) || (data == _chunkData3)) { _progress++; } break; default: break; } _lastDir = d; _dirsFinished++; if (0) qCDebug(FSVIEWLOG) << "FSFiew::scanFinished: " << d->path() << ", Data " << data << ", Progress " << _progress << "/" << _progressSize << endl; } void FSView::selected(TreeMapItem *i) { setPath(((Inode *)i)->path()); } void FSView::contextMenu(TreeMapItem *i, const QPoint &p) { QMenu popup; QMenu *spopup = new QMenu(i18n("Go To")); QMenu *dpopup = new QMenu(i18n("Stop at Depth")); QMenu *apopup = new QMenu(i18n("Stop at Area")); QMenu *fpopup = new QMenu(i18n("Stop at Name")); // choosing from the selection menu will give a selectionChanged() signal addSelectionItems(spopup, 901, i); popup.addMenu(spopup); QAction *actionGoUp = popup.addAction(i18n("Go Up")); popup.addSeparator(); QAction *actionStopRefresh = popup.addAction(i18n("Stop Refresh")); actionStopRefresh->setEnabled(_sm.scanRunning()); QAction *actionRefresh = popup.addAction(i18n("Refresh")); actionRefresh->setEnabled(!_sm.scanRunning()); QAction *actionRefreshSelected = nullptr; if (i) { actionRefreshSelected = popup.addAction(i18n("Refresh '%1'", i->text(0))); } popup.addSeparator(); addDepthStopItems(dpopup, 1001, i); popup.addMenu(dpopup); addAreaStopItems(apopup, 1101, i); popup.addMenu(apopup); addFieldStopItems(fpopup, 1201, i); popup.addMenu(fpopup); popup.addSeparator(); QMenu *cpopup = new QMenu(i18n("Color Mode")); addColorItems(cpopup, 1401); popup.addMenu(cpopup); QMenu *vpopup = new QMenu(i18n("Visualization")); addVisualizationItems(vpopup, 1301); popup.addMenu(vpopup); _allowRefresh = false; QAction *action = popup.exec(mapToGlobal(p)); _allowRefresh = true; if (!action) { return; } if (action == actionGoUp) { Inode *i = (Inode *) base(); if (i) { setPath(i->path() + QLatin1String("/..")); } } else if (action == actionStopRefresh) { stop(); } else if (action == actionRefreshSelected) { //((Inode*)i)->refresh(); requestUpdate((Inode *)i); } else if (action == actionRefresh) { Inode *i = (Inode *) base(); if (i) { requestUpdate(i); } } } void FSView::saveMetric(KConfigGroup *g) { QMap::iterator it; int c = 1; for (it = _dirMetric.begin(); it != _dirMetric.end(); ++it) { g->writePathEntry(QStringLiteral("Dir%1").arg(c), it.key()); g->writeEntry(QStringLiteral("Size%1").arg(c), (*it).size); g->writeEntry(QStringLiteral("Files%1").arg(c), (*it).fileCount); g->writeEntry(QStringLiteral("Dirs%1").arg(c), (*it).dirCount); c++; } g->writeEntry("Count", c - 1); } void FSView::setColorMode(FSView::ColorMode cm) { if (_colorMode == cm) { return; } _colorMode = cm; redraw(); } bool FSView::setColorMode(const QString &mode) { if (mode == QLatin1String("None")) { setColorMode(None); } else if (mode == QLatin1String("Depth")) { setColorMode(Depth); } else if (mode == QLatin1String("Name")) { setColorMode(Name); } else if (mode == QLatin1String("Owner")) { setColorMode(Owner); } else if (mode == QLatin1String("Group")) { setColorMode(Group); } else if (mode == QLatin1String("Mime")) { setColorMode(Mime); } else { return false; } return true; } QString FSView::colorModeString() const { QString mode; switch (_colorMode) { case None: mode = QStringLiteral("None"); break; case Depth: mode = QStringLiteral("Depth"); break; case Name: mode = QStringLiteral("Name"); break; case Owner: mode = QStringLiteral("Owner"); break; case Group: mode = QStringLiteral("Group"); break; case Mime: mode = QStringLiteral("Mime"); break; default: mode = QStringLiteral("Unknown"); break; } return mode; } void FSView::addColorItems(QMenu *popup, int id) { _colorID = id; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(colorActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &FSView::colorActivated); addPopupItem(popup, i18n("None"), colorMode() == None, id++); addPopupItem(popup, i18n("Depth"), colorMode() == Depth, id++); addPopupItem(popup, i18n("Name"), colorMode() == Name, id++); addPopupItem(popup, i18n("Owner"), colorMode() == Owner, id++); addPopupItem(popup, i18n("Group"), colorMode() == Group, id++); addPopupItem(popup, i18n("Mime Type"), colorMode() == Mime, id++); } void FSView::colorActivated(QAction *a) { const int id = a->data().toInt(); if (id == _colorID) { setColorMode(None); } else if (id == _colorID + 1) { setColorMode(Depth); } else if (id == _colorID + 2) { setColorMode(Name); } else if (id == _colorID + 3) { setColorMode(Owner); } else if (id == _colorID + 4) { setColorMode(Group); } else if (id == _colorID + 5) { setColorMode(Mime); } } void FSView::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape && !_pressed && (selection().size() > 0)) { // For consistency with Dolphin, deselect all on Escape if we're not dragging. TreeMapItem *changed = selection().commonParent(); if (changed) { clearSelection(changed); } } else { TreeMapWidget::keyPressEvent(e); } } void FSView::saveFSOptions() { KConfigGroup tmconfig(_config, "TreeMap"); saveOptions(&tmconfig); tmconfig.writeEntry("ColorMode", colorModeString()); KConfigGroup gconfig(_config, "General"); gconfig.writeEntry("Path", _path); KConfigGroup cconfig(_config, "MetricCache"); saveMetric(&cconfig); } void FSView::quit() { saveFSOptions(); qApp->quit(); } void FSView::doRedraw() { // we update progress every 1/4 second, and redraw every second static int redrawCounter = 0; bool redo = _sm.scanRunning(); if (!redo) { redrawCounter = 0; } if ((_progress > 0) && (_progressSize > 0) && _lastDir) { int percent = _progress * 100 / _progressSize; if (0) qCDebug(FSVIEWLOG) << "FSView::progress " << _progress << "/" << _progressSize << "= " << percent << "%, " << _dirsFinished << " dirs read, in " << _lastDir->path() << endl; emit progress(percent, _dirsFinished, _lastDir->path()); } if (_allowRefresh && ((redrawCounter % 4) == 0)) { if (0) { qCDebug(FSVIEWLOG) << "doRedraw " << _sm.scanLength(); } redraw(); } else { redo = true; } if (redo) { QTimer::singleShot(500, this, SLOT(doRedraw())); redrawCounter++; } } void FSView::doUpdate() { for (int i = 0; i < 5; i++) { switch (_progressPhase) { case 1: _chunkSize1 += _sm.scan(_chunkData1); if (_chunkSize1 > 100) { _progressPhase = 2; /* Go to maximally 33% by scaling with 3 */ _progressSize = 3 * _chunkSize1; if (1) { qCDebug(FSVIEWLOG) << "Phase 2: CSize " << _chunkSize1; } } break; case 2: /* progress phase 2 */ _chunkSize2 += _sm.scan(_chunkData2); /* switch to Phase 3 if we reach 80 % of Phase 2 */ if (_progress * 3 > _progressSize * 8 / 10) { _progressPhase = 3; /* Goal: Keep percentage equal from phase 2 to 3 */ double percent = (double)_progress / _progressSize; /* We scale by factor 2/3 afterwards */ percent = percent * 3 / 2; int todo = _chunkSize2 + (_progressSize / 3 - _progress); _progressSize = (int)((double)todo / (1.0 - percent)); _progress = _progressSize - todo; /* Go to maximally 66% by scaling with 1.5 */ _progressSize = _progressSize * 3 / 2; if (1) qCDebug(FSVIEWLOG) << "Phase 3: CSize " << _chunkSize2 << ", Todo " << todo << ", Progress " << _progress << "/" << _progressSize << endl; } break; case 3: /* progress phase 3 */ _chunkSize3 += _sm.scan(_chunkData3); /* switch to Phase 4 if we reach 80 % of Phase 3 */ if (_progress * 3 / 2 > _progressSize * 8 / 10) { _progressPhase = 4; /* Goal: Keep percentage equal from phase 2 to 3 */ double percent = (double)_progress / _progressSize; int todo = _chunkSize3 + (_progressSize * 2 / 3 - _progress); _progressSize = (int)((double)todo / (1.0 - percent) + .5); _progress = _progressSize - todo; if (1) qCDebug(FSVIEWLOG) << "Phase 4: CSize " << _chunkSize3 << ", Todo " << todo << ", Progress " << _progress << "/" << _progressSize << endl; } default: _sm.scan(-1); break; } } if (_sm.scanRunning()) { QTimer::singleShot(0, this, SLOT(doUpdate())); } else { emit completed(_dirsFinished); } } diff --git a/plugins/fsview/fsview_part.cpp b/plugins/fsview/fsview_part.cpp index 7f4f5382d..30c58dc3b 100644 --- a/plugins/fsview/fsview_part.cpp +++ b/plugins/fsview/fsview_part.cpp @@ -1,549 +1,534 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer Some file management code taken from the Dolphin file manager (C) 2006-2009, by Peter Penz KCachegrind 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The KPart embedding the FSView widget */ #include "fsview_part.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsviewdebug.h" K_PLUGIN_FACTORY(FSViewPartFactory, registerPlugin();) // FSJob, for progress FSJob::FSJob(FSView *v) : KIO::Job() { _view = v; - QObject::connect(v, SIGNAL(progress(int,int,QString)), - this, SLOT(progressSlot(int,int,QString))); + connect(v, &FSView::progress, this, &FSJob::progressSlot); } void FSJob::kill(bool /*quietly*/) { _view->stop(); Job::kill(); } void FSJob::progressSlot(int percent, int dirs, const QString &cDir) { if (percent < 100) { emitPercent(percent, 100); slotInfoMessage(this, i18np("Read 1 folder, in %2", "Read %1 folders, in %2", dirs, cDir), QString()); } else { slotInfoMessage(this, i18np("1 folder", "%1 folders", dirs), QString()); } } // FSViewPart FSViewPart::FSViewPart(QWidget *parentWidget, QObject *parent, const QList & /* args */) : KParts::ReadOnlyPart(parent) { KAboutData aboutData(QStringLiteral("fsview"), i18n("FSView"), QStringLiteral("0.1"), i18n("Filesystem Viewer"), KAboutLicense::GPL, i18n("(c) 2002, Josef Weidendorfer")); setComponentData(aboutData); _view = new FSView(new Inode(), parentWidget); _view->setWhatsThis(i18n("

This is the FSView plugin, a graphical " "browsing mode showing filesystem utilization " "by using a tree map visualization.

" "

Note that in this mode, automatic updating " "when filesystem changes are made " "is intentionally not done.

" "

For details on usage and options available, " "see the online help under " "menu 'Help/FSView Manual'.

")); _view->show(); setWidget(_view); _ext = new FSViewBrowserExtension(this); _job = nullptr; _areaMenu = new KActionMenu(i18n("Stop at Area"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_areadir"), _areaMenu); _depthMenu = new KActionMenu(i18n("Stop at Depth"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_depthdir"), _depthMenu); _visMenu = new KActionMenu(i18n("Visualization"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_visdir"), _visMenu); _colorMenu = new KActionMenu(i18n("Color Mode"), actionCollection()); actionCollection()->addAction(QStringLiteral("treemap_colordir"), _colorMenu); QAction *action; action = actionCollection()->addAction(QStringLiteral("help_fsview")); action->setText(i18n("&FSView Manual")); action->setIcon(QIcon::fromTheme(QStringLiteral("fsview"))); action->setToolTip(i18n("Show FSView manual")); action->setWhatsThis(i18n("Opens the help browser with the " "FSView documentation")); - connect(action, SIGNAL(triggered()), this, SLOT(showHelp())); + connect(action, &QAction::triggered, this, &FSViewPart::showHelp); - QObject::connect(_visMenu->menu(), SIGNAL(aboutToShow()), - SLOT(slotShowVisMenu())); - QObject::connect(_areaMenu->menu(), SIGNAL(aboutToShow()), - SLOT(slotShowAreaMenu())); - QObject::connect(_depthMenu->menu(), SIGNAL(aboutToShow()), - SLOT(slotShowDepthMenu())); - QObject::connect(_colorMenu->menu(), SIGNAL(aboutToShow()), - SLOT(slotShowColorMenu())); + connect(_visMenu->menu(), &QMenu::aboutToShow,this, &FSViewPart::slotShowVisMenu); + connect(_areaMenu->menu(), &QMenu::aboutToShow, this, &FSViewPart::slotShowAreaMenu); + connect(_depthMenu->menu(), &QMenu::aboutToShow, this, &FSViewPart::slotShowDepthMenu); + connect(_colorMenu->menu(), &QMenu::aboutToShow, this, &FSViewPart::slotShowColorMenu); // Both of these click signals are connected. Whether a single or // double click activates an item is checked against the current // style setting when the click happens. - QObject::connect(_view, SIGNAL(clicked(TreeMapItem*)), - _ext, SLOT(itemSingleClicked(TreeMapItem*))); - QObject::connect(_view, SIGNAL(doubleClicked(TreeMapItem*)), - _ext, SLOT(itemDoubleClicked(TreeMapItem*))); - - QObject::connect(_view, SIGNAL(returnPressed(TreeMapItem*)), - _ext, SLOT(selected(TreeMapItem*))); - QObject::connect(_view, SIGNAL(selectionChanged()), - this, SLOT(updateActions())); - QObject::connect(_view, - SIGNAL(contextMenuRequested(TreeMapItem*,QPoint)), - this, - SLOT(contextMenu(TreeMapItem*,QPoint))); - - QObject::connect(_view, SIGNAL(started()), this, SLOT(startedSlot())); - QObject::connect(_view, SIGNAL(completed(int)), - this, SLOT(completedSlot(int))); + connect(_view, &FSView::clicked, _ext, &FSViewBrowserExtension::itemSingleClicked); + connect(_view, &FSView::doubleClicked, _ext, &FSViewBrowserExtension::itemDoubleClicked); + + connect(_view, &TreeMapWidget::returnPressed, _ext, &FSViewBrowserExtension::selected); + connect(_view, QOverload<>::of(&TreeMapWidget::selectionChanged), this, &FSViewPart::updateActions); + connect(_view, &TreeMapWidget::contextMenuRequested, this, &FSViewPart::contextMenu); + + connect(_view, &FSView::started, this, &FSViewPart::startedSlot); + connect(_view, &FSView::completed, this, &FSViewPart::completedSlot); // Create common file management actions - this is necessary in KDE4 // as these common actions are no longer automatically part of KParts. // Much of this is taken from Dolphin. // FIXME: Renaming didn't even seem to work in KDE3! Implement (non-inline) renaming // functionality. //QAction* renameAction = m_actionCollection->addAction("rename"); //rename->setText(i18nc("@action:inmenu Edit", "Rename...")); //rename->setShortcut(Qt::Key_F2); QAction *moveToTrashAction = actionCollection()->addAction(QStringLiteral("move_to_trash")); moveToTrashAction->setText(i18nc("@action:inmenu File", "Move to Trash")); moveToTrashAction->setIcon(QIcon::fromTheme(QStringLiteral("user-trash"))); actionCollection()->setDefaultShortcut(moveToTrashAction, QKeySequence(QKeySequence::Delete)); - connect(moveToTrashAction, SIGNAL(triggered()), _ext, SLOT(trash())); + connect(moveToTrashAction, &QAction::triggered, _ext, &FSViewBrowserExtension::trash); QAction *deleteAction = actionCollection()->addAction(QStringLiteral("delete")); deleteAction->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete"))); deleteAction->setText(i18nc("@action:inmenu File", "Delete")); actionCollection()->setDefaultShortcut(deleteAction, QKeySequence(Qt::SHIFT | Qt::Key_Delete)); - connect(deleteAction, SIGNAL(triggered()), _ext, SLOT(del())); + connect(deleteAction, &QAction::triggered, _ext, &FSViewBrowserExtension::del); QAction *editMimeTypeAction = actionCollection()->addAction(QStringLiteral("editMimeType")); editMimeTypeAction->setText(i18nc("@action:inmenu Edit", "&Edit File Type...")); - connect(editMimeTypeAction, SIGNAL(triggered()), _ext, SLOT(editMimeType())); + connect(editMimeTypeAction, &QAction::triggered, _ext, &FSViewBrowserExtension::editMimeType); QAction *propertiesAction = actionCollection()->addAction(QStringLiteral("properties")); propertiesAction->setText(i18nc("@action:inmenu File", "Properties")); propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); propertiesAction->setShortcut(Qt::ALT | Qt::Key_Return); - connect(propertiesAction, SIGNAL(triggered()), SLOT(slotProperties())); + connect(propertiesAction, &QAction::triggered, this, &FSViewPart::slotProperties); QTimer::singleShot(1, this, SLOT(showInfo())); updateActions(); setXMLFile(QStringLiteral("fsview_part.rc")); } FSViewPart::~FSViewPart() { qCDebug(FSVIEWLOG); delete _job; _view->saveFSOptions(); } void FSViewPart::showInfo() { QString info; info = i18n("FSView intentionally does not support automatic updates " "when changes are made to files or directories, " "currently visible in FSView, from the outside.\n" "For details, see the 'Help/FSView Manual'."); KMessageBox::information(_view, info, QString(), QStringLiteral("ShowFSViewInfo")); } void FSViewPart::showHelp() { KToolInvocation::startServiceByDesktopName(QStringLiteral("khelpcenter"), QStringLiteral("help:/konqueror/index.html#fsview")); } void FSViewPart::startedSlot() { _job = new FSJob(_view); _job->setUiDelegate(new KIO::JobUiDelegate()); emit started(_job); } void FSViewPart::completedSlot(int dirs) { if (_job) { _job->progressSlot(100, dirs, QString()); delete _job; _job = nullptr; } KConfigGroup cconfig = _view->config()->group("MetricCache"); _view->saveMetric(&cconfig); emit completed(); } void FSViewPart::slotShowVisMenu() { _visMenu->menu()->clear(); _view->addVisualizationItems(_visMenu->menu(), 1301); } void FSViewPart::slotShowAreaMenu() { _areaMenu->menu()->clear(); _view->addAreaStopItems(_areaMenu->menu(), 1001, nullptr); } void FSViewPart::slotShowDepthMenu() { _depthMenu->menu()->clear(); _view->addDepthStopItems(_depthMenu->menu(), 1501, nullptr); } void FSViewPart::slotShowColorMenu() { _colorMenu->menu()->clear(); _view->addColorItems(_colorMenu->menu(), 1401); } bool FSViewPart::openFile() // never called since openUrl is reimplemented { qCDebug(FSVIEWLOG) << localFilePath(); _view->setPath(localFilePath()); return true; } bool FSViewPart::openUrl(const QUrl &url) { qCDebug(FSVIEWLOG) << url.path(); if (!url.isValid()) { return false; } if (!url.isLocalFile()) { return false; } setUrl(url); emit setWindowCaption(this->url().toDisplayString(QUrl::PreferLocalFile)); _view->setPath(this->url().path()); return true; } bool FSViewPart::closeUrl() { qCDebug(FSVIEWLOG); _view->stop(); return true; } void FSViewPart::setNonStandardActionEnabled(const char *actionName, bool enabled) { QAction *action = actionCollection()->action(actionName); action->setEnabled(enabled); } void FSViewPart::updateActions() { int canDel = 0, canCopy = 0, canMove = 0; QList urls; foreach (TreeMapItem *i, _view->selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); urls.append(u); canCopy++; if (KProtocolManager::supportsDeleting(u)) { canDel++; } if (KProtocolManager::supportsMoving(u)) { canMove++; } } // Standard KBrowserExtension actions. emit _ext->enableAction("copy", canCopy > 0); emit _ext->enableAction("cut", canMove > 0); // Custom actions. //setNonStandardActionEnabled("rename", canMove > 0 ); // FIXME setNonStandardActionEnabled("move_to_trash", (canDel > 0 && canMove > 0)); setNonStandardActionEnabled("delete", canDel > 0); setNonStandardActionEnabled("editMimeType", _view->selection().count() == 1); setNonStandardActionEnabled("properties", _view->selection().count() == 1); emit _ext->selectionInfo(urls); if (canCopy > 0) { stateChanged(QStringLiteral("has_selection")); } else { stateChanged(QStringLiteral("has_no_selection")); } qCDebug(FSVIEWLOG) << "deletable" << canDel; } void FSViewPart::contextMenu(TreeMapItem * /*item*/, const QPoint &p) { int canDel = 0, canCopy = 0, canMove = 0; KFileItemList items; foreach (TreeMapItem *i, _view->selection()) { QUrl u = QUrl::fromLocalFile(((Inode *)i)->path()); QString mimetype = ((Inode *)i)->mimeType().name(); const QFileInfo &info = ((Inode *)i)->fileInfo(); mode_t mode = info.isFile() ? S_IFREG : info.isDir() ? S_IFDIR : info.isSymLink() ? S_IFLNK : (mode_t) - 1; items.append(KFileItem(u, mimetype, mode)); canCopy++; if (KProtocolManager::supportsDeleting(u)) { canDel++; } if (KProtocolManager::supportsMoving(u)) { canMove++; } } QList editActions; KParts::BrowserExtension::ActionGroupMap actionGroups; KParts::BrowserExtension::PopupFlags flags = KParts::BrowserExtension::ShowUrlOperations | KParts::BrowserExtension::ShowProperties; bool addTrash = (canMove > 0); bool addDel = false; if (canDel == 0) { flags |= KParts::BrowserExtension::NoDeletion; } else { if (!url().isLocalFile()) { addDel = true; } else if (QApplication::keyboardModifiers() & Qt::ShiftModifier) { addTrash = false; addDel = true; } else { KSharedConfig::Ptr globalConfig = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::IncludeGlobals); KConfigGroup configGroup(globalConfig, "KDE"); addDel = configGroup.readEntry("ShowDeleteCommand", false); } } if (addTrash) { editActions.append(actionCollection()->action(QStringLiteral("move_to_trash"))); } if (addDel) { editActions.append(actionCollection()->action(QStringLiteral("delete"))); } // FIXME: rename is currently unavailable. Requires popup renaming. // if (canMove) // editActions.append(actionCollection()->action("rename")); actionGroups.insert(QStringLiteral("editactions"), editActions); if (items.count() > 0) emit _ext->popupMenu(_view->mapToGlobal(p), items, KParts::OpenUrlArguments(), KParts::BrowserArguments(), flags, actionGroups); } void FSViewPart::slotProperties() { QList urlList; if (view()) { urlList = view()->selectedUrls(); } if (!urlList.isEmpty()) { KPropertiesDialog::showDialog(urlList.first(), view()); } } // FSViewBrowserExtension FSViewBrowserExtension::FSViewBrowserExtension(FSViewPart *viewPart) : KParts::BrowserExtension(viewPart) { _view = viewPart->view(); } FSViewBrowserExtension::~FSViewBrowserExtension() {} void FSViewBrowserExtension::del() { const QList urls = _view->selectedUrls(); KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(_view); if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job *job = KIO::del(urls); KJobWidgets::setWindow(job, _view); job->uiDelegate()->setAutoErrorHandlingEnabled(true); - connect(job, SIGNAL(result(KJob*)), - this, SLOT(refresh())); + connect(job, &KJob::result, this, &FSViewBrowserExtension::refresh); } } void FSViewBrowserExtension::trash() { bool deleteNotTrash = ((QGuiApplication::keyboardModifiers() & Qt::ShiftModifier) != 0); if (deleteNotTrash) { del(); } else { KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(_view); const QList urls = _view->selectedUrls(); if (uiDelegate.askDeleteConfirmation(urls, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job *job = KIO::trash(urls); KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, urls, QUrl("trash:/"), job); KJobWidgets::setWindow(job, _view); job->uiDelegate()->setAutoErrorHandlingEnabled(true); - connect(job, SIGNAL(result(KJob*)), - this, SLOT(refresh())); + connect(job, &KJob::result, this, &FSViewBrowserExtension::refresh); } } } void FSViewBrowserExtension::copySelection(bool move) { QMimeData *data = new QMimeData; data->setUrls(_view->selectedUrls()); KIO::setClipboardDataCut(data, move); QApplication::clipboard()->setMimeData(data); } void FSViewBrowserExtension::editMimeType() { Inode *i = (Inode *) _view->selection().first(); if (i) { KMimeTypeEditor::editMimeType(i->mimeType().name(), _view); } } // refresh treemap at end of KIO jobs void FSViewBrowserExtension::refresh() { // only need to refresh common ancestor for all selected items TreeMapItem *commonParent = _view->selection().commonParent(); if (!commonParent) { return; } /* if commonParent is a file, update parent directory */ if (!((Inode *)commonParent)->isDir()) { commonParent = commonParent->parent(); if (!commonParent) { return; } } qCDebug(FSVIEWLOG) << "refreshing" << ((Inode *)commonParent)->path() << endl; _view->requestUpdate((Inode *)commonParent); } void FSViewBrowserExtension::itemSingleClicked(TreeMapItem *i) { if (_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { selected(i); } } void FSViewBrowserExtension::itemDoubleClicked(TreeMapItem *i) { if (!_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { selected(i); } } void FSViewBrowserExtension::selected(TreeMapItem *i) { if (!i) { return; } QUrl url = QUrl::fromLocalFile(((Inode *)i)->path()); emit openUrlRequest(url); } #include "fsview_part.moc" diff --git a/plugins/fsview/fsview_part.h b/plugins/fsview/fsview_part.h index 61e2df853..a3d5ccb1b 100644 --- a/plugins/fsview/fsview_part.h +++ b/plugins/fsview/fsview_part.h @@ -1,134 +1,136 @@ /* This file is part of FSView. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * The KPart embedding the FSView widget */ #ifndef FSVIEW_PART_H #define FSVIEW_PART_H #include #include #include #include "fsview.h" class KActionMenu; class FSViewPart; class FSViewBrowserExtension : public KParts::BrowserExtension { Q_OBJECT public: explicit FSViewBrowserExtension(FSViewPart *viewPart); ~FSViewBrowserExtension() override; -protected slots: +public slots: void selected(TreeMapItem *); + + void itemSingleClicked(TreeMapItem *i); + void itemDoubleClicked(TreeMapItem *i); + + void trash(); + void del(); + void editMimeType(); + +protected slots: void refresh(); void copy() { copySelection(false); } void cut() { copySelection(true); } - void trash(); - void del(); - void editMimeType(); - - void itemSingleClicked(TreeMapItem *i); - void itemDoubleClicked(TreeMapItem *i); - private: void copySelection(bool move); FSView *_view; }; class FSJob: public KIO::Job { Q_OBJECT public: explicit FSJob(FSView *); virtual void kill(bool quietly = true); public slots: void progressSlot(int percent, int dirs, const QString &lastDir); private: FSView *_view; }; class FSViewPart : public KParts::ReadOnlyPart { Q_OBJECT Q_PROPERTY(bool supportsUndo READ supportsUndo) public: FSViewPart(QWidget *parentWidget, QObject *parent, const QList &args); ~FSViewPart() override; bool supportsUndo() const { return false; } FSView *view() const { return _view; } public slots: void updateActions(); void contextMenu(TreeMapItem *, const QPoint &); void showInfo(); void showHelp(); void startedSlot(); void completedSlot(int dirs); void slotShowVisMenu(); void slotShowAreaMenu(); void slotShowDepthMenu(); void slotShowColorMenu(); void slotProperties(); protected: /** * This must be implemented by each part */ bool openFile() override; bool openUrl(const QUrl &url) override; bool closeUrl() override; private: FSView *_view; FSJob *_job; FSViewBrowserExtension *_ext; KActionMenu *_visMenu, *_areaMenu, *_depthMenu, *_colorMenu; void setNonStandardActionEnabled(const char *actionName, bool enabled); }; #endif // FSVIEW_PART_H diff --git a/plugins/fsview/main.cpp b/plugins/fsview/main.cpp index 562a943b3..2639645c4 100644 --- a/plugins/fsview/main.cpp +++ b/plugins/fsview/main.cpp @@ -1,56 +1,53 @@ /***************************************************** * FSView, a simple TreeMap application * * (C) 2002, Josef Weidendorfer */ #include #include #include "fsview.h" #include #include #include #include #include #include #include int main(int argc, char *argv[]) { + QApplication app(argc, argv); // KDE compliant startup KAboutData aboutData(QStringLiteral("fsview"), i18n("FSView"), QStringLiteral("0.1"), i18n("Filesystem Viewer"), KAboutLicense::GPL, i18n("(c) 2002, Josef Weidendorfer")); - QApplication app(argc, argv); QCommandLineParser parser; KAboutData::setApplicationData(aboutData); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("+[folder]"), i18n("View filesystem starting from this folder"))); KConfigGroup gconfig(KSharedConfig::openConfig(), "General"); QString path = gconfig.readPathEntry("Path", QStringLiteral(".")); if (parser.positionalArguments().count() > 0) { path = parser.positionalArguments().at(0); } // TreeMap Widget as toplevel window FSView w(new Inode()); - QObject::connect(&w, SIGNAL(clicked(TreeMapItem*)), - &w, SLOT(selected(TreeMapItem*))); - QObject::connect(&w, SIGNAL(returnPressed(TreeMapItem*)), - &w, SLOT(selected(TreeMapItem*))); - QObject::connect(&w, SIGNAL(contextMenuRequested(TreeMapItem*,QPoint)), - &w, SLOT(contextMenu(TreeMapItem*,QPoint))); + QObject::connect(&w, &TreeMapWidget::clicked, &w, &FSView::selected); + QObject::connect(&w, &TreeMapWidget::returnPressed, &w, &FSView::selected); + QObject::connect(&w, &TreeMapWidget::contextMenuRequested, &w, &FSView::contextMenu); w.setPath(path); w.show(); return app.exec(); } diff --git a/plugins/fsview/treemap.cpp b/plugins/fsview/treemap.cpp index f1b21e22f..a1486c11d 100644 --- a/plugins/fsview/treemap.cpp +++ b/plugins/fsview/treemap.cpp @@ -1,3647 +1,3641 @@ /* This file is part of KCachegrind. Copyright (C) 2002, 2003 Josef Weidendorfer KCachegrind 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, version 2. 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; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* * A Widget for visualizing hierarchical metrics as areas. * The API is similar to QListView. */ #include "treemap.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "fsviewdebug.h" // set this to 1 to enable debug output #define DEBUG_DRAWING 0 #define MAX_FIELD 12 // // StoredDrawParams // StoredDrawParams::StoredDrawParams() { _selected = false; _current = false; _shaded = true; _rotated = false; _drawFrame = true; _backColor = Qt::white; // field array has size 0 } StoredDrawParams::StoredDrawParams(const QColor &c, bool selected, bool current) { _backColor = c; _selected = selected; _current = current; _shaded = true; _rotated = false; _drawFrame = true; // field array has size 0 } QString StoredDrawParams::text(int f) const { if ((f < 0) || (f >= _field.size())) { return QString(); } return _field[f].text; } QPixmap StoredDrawParams::pixmap(int f) const { if ((f < 0) || (f >= _field.size())) { return QPixmap(); } return _field[f].pix; } DrawParams::Position StoredDrawParams::position(int f) const { if ((f < 0) || (f >= _field.size())) { return Default; } return _field[f].pos; } int StoredDrawParams::maxLines(int f) const { if ((f < 0) || (f >= _field.size())) { return 0; } return _field[f].maxLines; } const QFont &StoredDrawParams::font() const { static QFont *f = nullptr; if (!f) { f = new QFont(QApplication::font()); } return *f; } void StoredDrawParams::ensureField(int f) { if (f < 0 || f >= MAX_FIELD) { return; } if (_field.size() < f + 1) { int oldSize = _field.size(); _field.resize(f + 1); while (oldSize < f + 1) { _field[oldSize].pos = Default; _field[oldSize].maxLines = 0; oldSize++; } } } void StoredDrawParams::setField(int f, const QString &t, const QPixmap &pm, Position p, int maxLines) { if (f < 0 || f >= MAX_FIELD) { return; } ensureField(f); _field[f].text = t; _field[f].pix = pm; _field[f].pos = p; _field[f].maxLines = maxLines; } void StoredDrawParams::setText(int f, const QString &t) { if (f < 0 || f >= MAX_FIELD) { return; } ensureField(f); _field[f].text = t; } void StoredDrawParams::setPixmap(int f, const QPixmap &pm) { if (f < 0 || f >= MAX_FIELD) { return; } ensureField(f); _field[f].pix = pm; } void StoredDrawParams::setPosition(int f, Position p) { if (f < 0 || f >= MAX_FIELD) { return; } ensureField(f); _field[f].pos = p; } void StoredDrawParams::setMaxLines(int f, int m) { if (f < 0 || f >= MAX_FIELD) { return; } ensureField(f); _field[f].maxLines = m; } // // RectDrawing // RectDrawing::RectDrawing(const QRect &r) { _fm = nullptr; _dp = nullptr; setRect(r); } RectDrawing::~RectDrawing() { delete _fm; delete _dp; } DrawParams *RectDrawing::drawParams() { if (!_dp) { _dp = new StoredDrawParams(); } return _dp; } void RectDrawing::setDrawParams(DrawParams *dp) { delete _dp; _dp = dp; } void RectDrawing::setRect(const QRect &r) { _rect = r; _usedTopLeft = 0; _usedTopCenter = 0; _usedTopRight = 0; _usedBottomLeft = 0; _usedBottomCenter = 0; _usedBottomRight = 0; _fontHeight = 0; } QRect RectDrawing::remainingRect(DrawParams *dp) { if (!dp) { dp = drawParams(); } if ((_usedTopLeft > 0) || (_usedTopCenter > 0) || (_usedTopRight > 0)) { if (dp->rotated()) { _rect.setLeft(_rect.left() + _fontHeight); } else { _rect.setTop(_rect.top() + _fontHeight); } } if ((_usedBottomLeft > 0) || (_usedBottomCenter > 0) || (_usedBottomRight > 0)) { if (dp->rotated()) { _rect.setRight(_rect.right() - _fontHeight); } else { _rect.setBottom(_rect.bottom() - _fontHeight); } } return _rect; } void RectDrawing::drawBack(QPainter *p, DrawParams *dp) { if (!dp) { dp = drawParams(); } if (_rect.width() <= 0 || _rect.height() <= 0) { return; } QRect r = _rect; QColor normal = dp->backColor(); if (dp->selected()) { normal = normal.lighter(); } bool isCurrent = dp->current(); if (dp->drawFrame() || isCurrent) { // 3D raised/sunken frame effect... QColor high = normal.lighter(); QColor low = normal.darker(); p->setPen(isCurrent ? low : high); p->drawLine(r.left(), r.top(), r.right(), r.top()); p->drawLine(r.left(), r.top(), r.left(), r.bottom()); p->setPen(isCurrent ? high : low); p->drawLine(r.right(), r.top(), r.right(), r.bottom()); p->drawLine(r.left(), r.bottom(), r.right(), r.bottom()); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); } if (r.width() <= 0 || r.height() <= 0) { return; } if (dp->shaded() && (r.width() > 0 && r.height() > 0)) { // adjustment for drawRect semantic in Qt4: decrement height/width r.setRect(r.x(), r.y(), r.width() - 1, r.height() - 1); // some shading bool goDark = qGray(normal.rgb()) > 128; int rBase, gBase, bBase; normal.getRgb(&rBase, &gBase, &bBase); p->setBrush(Qt::NoBrush); // shade parameters: int d = 7; float factor = 0.1, forth = 0.7, back1 = 0.9, toBack2 = .7, back2 = 0.97; // coefficient corrections because of rectangle size int s = r.width(); if (s > r.height()) { s = r.height(); } if (s < 100) { forth -= .3 * (100 - s) / 100; back1 -= .2 * (100 - s) / 100; back2 -= .02 * (100 - s) / 100; } // maximal color difference int rDiff = goDark ? -rBase / d : (255 - rBase) / d; int gDiff = goDark ? -gBase / d : (255 - gBase) / d; int bDiff = goDark ? -bBase / d : (255 - bBase) / d; QColor shadeColor; while (factor < .95 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb((int)(rBase + factor * rDiff + .5), (int)(gBase + factor * gDiff + .5), (int)(bBase + factor * bDiff + .5)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = 1.0 - ((1.0 - factor) * forth); } // and back (1st half) while (factor > toBack2 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb((int)(rBase + factor * rDiff + .5), (int)(gBase + factor * gDiff + .5), (int)(bBase + factor * bDiff + .5)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = 1.0 - ((1.0 - factor) / back1); } // and back (2nd half) while (factor > .01 && (r.width() >= 0 && r.height() >= 0)) { shadeColor.setRgb((int)(rBase + factor * rDiff + .5), (int)(gBase + factor * gDiff + .5), (int)(bBase + factor * bDiff + .5)); p->setPen(shadeColor); p->drawRect(r); r.setRect(r.x() + 1, r.y() + 1, r.width() - 2, r.height() - 2); factor = factor * back2; } normal = shadeColor; // for filling, width and height has to be incremented again r.setRect(r.x(), r.y(), r.width() + 1, r.height() + 1); } // fill inside p->fillRect(r, normal); } /* Helper for drawField * Find a line break position in a string, given a font and maximum width * * Returns the actually used width, and sets */ static int findBreak(int &breakPos, QString text, QFontMetrics *fm, int maxWidth) { int usedWidth; // does full text fit? breakPos = text.length(); usedWidth = fm->horizontalAdvance(text); if (usedWidth < maxWidth) { return usedWidth; } // now lower breakPos until best position is found. // first by binary search, resulting in a position a little bit too large int bottomPos = 0; while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) { int halfPos = (bottomPos + breakPos) / 2; int halfWidth = fm->horizontalAdvance(text, halfPos); if (halfWidth < maxWidth) { bottomPos = halfPos; } else { breakPos = halfPos; usedWidth = halfWidth; } } // final position by taking break boundaries into account. // possible break boundaries are changing char categories but not middle of "Aa" QChar::Category lastCat, cat; int pos = breakPos; lastCat = text[pos - 1].category(); // at minimum 2 chars before break while (pos > 2) { pos--; cat = text[pos - 1].category(); if (cat == lastCat) { continue; } // "Aa" has not a possible break inbetween if ((cat == QChar::Letter_Uppercase) && (lastCat == QChar::Letter_Lowercase)) { lastCat = cat; continue; } lastCat = cat; breakPos = pos; usedWidth = fm->horizontalAdvance(text, breakPos); if (usedWidth < maxWidth) { break; } } return usedWidth; } /* Helper for drawField * Find last line break position in a string from backwards, * given a font and maximum width * * Returns the actually used width, and sets */ static int findBreakBackwards(int &breakPos, QString text, QFontMetrics *fm, int maxWidth) { int usedWidth; // does full text fit? breakPos = 0; usedWidth = fm->horizontalAdvance(text); if (usedWidth < maxWidth) { return usedWidth; } // now raise breakPos until best position is found. // first by binary search, resulting in a position a little bit too small int topPos = text.length(); while (qAbs(maxWidth - usedWidth) > 3 * fm->maxWidth()) { int halfPos = (breakPos + topPos) / 2; int halfWidth = fm->horizontalAdvance(text.mid(halfPos)); if (halfWidth < maxWidth) { breakPos = halfPos; usedWidth = halfWidth; } else { topPos = halfPos; } } // final position by taking break boundaries into account. // possible break boundaries are changing char categories but not middle of "Aa" QChar::Category lastCat, cat; int pos = breakPos; lastCat = text[pos].category(); // at minimum 2 chars before break while (pos < text.length() - 2) { pos++; cat = text[pos].category(); if (cat == lastCat) { continue; } // "Aa" has not a possible break inbetween if ((lastCat == QChar::Letter_Uppercase) && (cat == QChar::Letter_Lowercase)) { lastCat = cat; continue; } lastCat = cat; breakPos = pos; usedWidth = fm->horizontalAdvance(text.mid(breakPos)); if (usedWidth < maxWidth) { break; } } return usedWidth; } bool RectDrawing::drawField(QPainter *p, int f, DrawParams *dp) { if (!dp) { dp = drawParams(); } if (!_fm) { _fm = new QFontMetrics(dp->font()); _fontHeight = _fm->height(); } QRect r = _rect; if (0) qCDebug(FSVIEWLOG) << "Rect " << r.x() << "/" << r.y() << " - " << r.width() << "x" << r.height() << endl; int h = _fontHeight; bool rotate = dp->rotated(); int width = (rotate ? r.height() : r.width()) - 4; int height = (rotate ? r.width() : r.height()); int lines = height / h; // stop if there is no space available if (lines < 1) { return false; } // calculate free space in first line () int pos = dp->position(f); if (pos == DrawParams::Default) { switch (f % 4) { case 0: pos = DrawParams::TopLeft; break; case 1: pos = DrawParams::TopRight; break; case 2: pos = DrawParams::BottomRight; break; case 3: pos = DrawParams::BottomLeft; break; } } int unused = 0; bool isBottom = false; bool isCenter = false; bool isRight = false; int *used = nullptr; switch (pos) { case DrawParams::TopLeft: used = &_usedTopLeft; if (_usedTopLeft == 0) { if (_usedTopCenter) { unused = (width - _usedTopCenter) / 2; } else { unused = width - _usedTopRight; } } break; case DrawParams::TopCenter: isCenter = true; used = &_usedTopCenter; if (_usedTopCenter == 0) { if (_usedTopLeft > _usedTopRight) { unused = width - 2 * _usedTopLeft; } else { unused = width - 2 * _usedTopRight; } } break; case DrawParams::TopRight: isRight = true; used = &_usedTopRight; if (_usedTopRight == 0) { if (_usedTopCenter) { unused = (width - _usedTopCenter) / 2; } else { unused = width - _usedTopLeft; } } break; case DrawParams::BottomLeft: isBottom = true; used = &_usedBottomLeft; if (_usedBottomLeft == 0) { if (_usedBottomCenter) { unused = (width - _usedBottomCenter) / 2; } else { unused = width - _usedBottomRight; } } break; case DrawParams::BottomCenter: isCenter = true; isBottom = true; used = &_usedBottomCenter; if (_usedBottomCenter == 0) { if (_usedBottomLeft > _usedBottomRight) { unused = width - 2 * _usedBottomLeft; } else { unused = width - 2 * _usedBottomRight; } } break; case DrawParams::BottomRight: isRight = true; isBottom = true; used = &_usedBottomRight; if (_usedBottomRight == 0) { if (_usedBottomCenter) { unused = (width - _usedBottomCenter) / 2; } else { unused = width - _usedBottomLeft; } } break; } if (isBottom) { if ((_usedTopLeft > 0) || (_usedTopCenter > 0) || (_usedTopRight > 0)) { lines--; } } else if (!isBottom) { if ((_usedBottomLeft > 0) || (_usedBottomCenter > 0) || (_usedBottomRight > 0)) { lines--; } } if (lines < 1) { return false; } int y = isBottom ? height - h : 0; if (unused < 0) { unused = 0; } if (unused == 0) { // no space available in last line at this position y = isBottom ? (y - h) : (y + h); lines--; if (lines < 1) { return false; } // new line: reset used space if (isBottom) { _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; } else { _usedTopLeft = _usedTopCenter = _usedTopRight = 0; } unused = width; } // stop as soon as possible when there is no space for "..." static int dotW = 0; if (!dotW) { dotW = _fm->horizontalAdvance(QStringLiteral("...")); } if (width < dotW) { return false; } // get text and pixmap now, only if we need to, because it is possible // that they are calculated on demand (and this can take some time) QString name = dp->text(f); if (name.isEmpty()) { return 0; } QPixmap pix = dp->pixmap(f); // check if pixmap can be drawn int pixW = pix.width(); int pixH = pix.height(); int pixY = 0; bool pixDrawn = true; if (pixW > 0) { pixW += 2; // X distance from pix if ((width < pixW + dotW) || (height < pixH)) { // do not draw pixW = 0; } else { pixDrawn = false; } } // width of text and pixmap to be drawn int w = pixW + _fm->horizontalAdvance(name); if (0) qCDebug(FSVIEWLOG) << " For '" << name << "': Unused " << unused << ", StrW " << w << ", Width " << width << endl; // if we have limited space at 1st line: // use it only if whole name does fit in last line... if ((unused < width) && (w > unused)) { y = isBottom ? (y - h) : (y + h); lines--; if (lines < 1) { return false; } // new line: reset used space if (isBottom) { _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; } else { _usedTopLeft = _usedTopCenter = _usedTopRight = 0; } } p->save(); p->setPen((qGray(dp->backColor().rgb()) > 100) ? Qt::black : Qt::white); p->setFont(dp->font()); if (rotate) { //p->translate(r.x()+2, r.y()+r.height()); p->translate(r.x(), r.y() + r.height() - 2); p->rotate(270); } else { p->translate(r.x() + 2, r.y()); } // adjust available lines according to maxLines int max = dp->maxLines(f); if ((max > 0) && (lines > max)) { lines = max; } /* loop over name parts to break up string depending on available width. * every char category change is supposed a possible break, * with the exception Uppercase=>Lowercase. * It is good enough for numbers, Symbols... * * If the text is to be written at the bottom, we start with the * end of the string (so everything is reverted) */ QString remaining; int origLines = lines; while (lines > 0) { // more than one line: search for line break if (w > width && lines > 1) { int breakPos; if (!isBottom) { w = pixW + findBreak(breakPos, name, _fm, width - pixW); remaining = name.mid(breakPos); // remove space on break point if (name[breakPos - 1].category() == QChar::Separator_Space) { name = name.left(breakPos - 1); } else { name = name.left(breakPos); } } else { // bottom w = pixW + findBreakBackwards(breakPos, name, _fm, width - pixW); remaining = name.left(breakPos); // remove space on break point if (name[breakPos].category() == QChar::Separator_Space) { name = name.mid(breakPos + 1); } else { name = name.mid(breakPos); } } } else { remaining = QString(); } /* truncate and add ... if needed */ if (w > width) { name = _fm->elidedText(name, Qt::ElideRight, width - pixW); w = _fm->horizontalAdvance(name) + pixW; } int x = 0; if (isCenter) { x = (width - w) / 2; } else if (isRight) { x = width - w; } if (!pixDrawn) { pixY = y + (h - pixH) / 2; // default: center vertically if (pixH > h) { pixY = isBottom ? y - (pixH - h) : y; } p->drawPixmap(x, pixY, pix); // for distance to next text pixY = isBottom ? (pixY - h - 2) : (pixY + pixH + 2); pixDrawn = true; } if (0) qCDebug(FSVIEWLOG) << " Drawing '" << name << "' at " << x + pixW << "/" << y << endl; p->drawText(x + pixW, y, width - pixW, h, Qt::AlignLeft, name); y = isBottom ? (y - h) : (y + h); lines--; if (remaining.isEmpty()) { break; } name = remaining; w = pixW + _fm->horizontalAdvance(name); } // make sure the pix stays visible if (pixDrawn && (pixY > 0)) { if (isBottom && (pixY < y)) { y = pixY; } if (!isBottom && (pixY > y)) { y = pixY; } } if (origLines > lines) { // if only 1 line written, do not reset _used* vars if (lines - origLines > 1) { if (isBottom) { _usedBottomLeft = _usedBottomCenter = _usedBottomRight = 0; } else { _usedTopLeft = _usedTopCenter = _usedTopRight = 0; } } // take back one line y = isBottom ? (y + h) : (y - h); if (used) { *used = w; } } // update free space if (!isBottom) { if (rotate) { _rect.setRect(r.x() + y, r.y(), r.width() - y, r.height()); } else { _rect.setRect(r.x(), r.y() + y, r.width(), r.height() - y); } } else { if (rotate) { _rect.setRect(r.x(), r.y(), y + h, r.height()); } else { _rect.setRect(r.x(), r.y(), r.width(), y + h); } } p->restore(); return true; } // // TreeMapItemList // TreeMapItem *TreeMapItemList::commonParent() { if (isEmpty()) { return nullptr; } TreeMapItem *parent = first(); for (int i = 1; parent && i < size(); i++) { parent = parent->commonParent(at(i)); } return parent; } class TreeMapItemLessThan { public: bool operator()(const TreeMapItem *i1, const TreeMapItem *i2) const { TreeMapItem *p = i1->parent(); // should not happen if (!p) { return false; } bool ascending; bool result; int textNo = p->sorting(&ascending); if (textNo < 0) { result = i1->value() < i2->value(); } else { result = i1->text(textNo) < i2->text(textNo); } return ascending ? result : !result; } }; TreeMapItemLessThan treeMapItemLessThan; // TreeMapItem TreeMapItem::TreeMapItem(TreeMapItem *parent, double value) { _value = value; _parent = parent; _sum = 0; _children = nullptr; _widget = nullptr; _index = -1; _depth = -1; // not set _unused_self = 0; if (_parent) { // take sorting from parent _sortTextNo = _parent->sorting(&_sortAscending); _parent->addItem(this); } else { _sortAscending = false; _sortTextNo = -1; // default: no sorting } } TreeMapItem::TreeMapItem(TreeMapItem *parent, double value, const QString &text1, const QString &text2, const QString &text3, const QString &text4) { _value = value; _parent = parent; // this resizes the text vector only if needed if (!text4.isEmpty()) { setText(3, text4); } if (!text3.isEmpty()) { setText(2, text3); } if (!text2.isEmpty()) { setText(1, text2); } setText(0, text1); _sum = 0; _children = nullptr; _widget = nullptr; _index = -1; _depth = -1; // not set _unused_self = 0; if (_parent) { _parent->addItem(this); } } TreeMapItem::~TreeMapItem() { if (_children) { qDeleteAll(*_children); delete _children; _children = nullptr; } // finally, notify widget about deletion if (_widget) { _widget->deletingItem(this); } } void TreeMapItem::setParent(TreeMapItem *p) { _parent = p; if (p) { _widget = p->_widget; } } bool TreeMapItem::isChildOf(TreeMapItem *item) { if (!item) { return false; } TreeMapItem *i = this; while (i) { if (item == i) { return true; } i = i->_parent; } return false; } TreeMapItem *TreeMapItem::commonParent(TreeMapItem *item) { while (item && !isChildOf(item)) { item = item->parent(); } return item; } void TreeMapItem::redraw() { if (_widget) { _widget->redraw(this); } } void TreeMapItem::clear() { if (_children) { // delete selected items below this item from selection if (_widget) { _widget->clearSelection(this); } qDeleteAll(*_children); delete _children; _children = nullptr; } } // invalidates current children and forces redraw // this is only useful when children are created on demand in items() void TreeMapItem::refresh() { clear(); redraw(); } QStringList TreeMapItem::path(int textNo) const { QStringList list(text(textNo)); TreeMapItem *i = _parent; while (i) { QString text = i->text(textNo); if (!text.isEmpty()) { list.prepend(i->text(textNo)); } i = i->_parent; } return list; } int TreeMapItem::depth() const { if (_depth > 0) { return _depth; } if (_parent) { return _parent->depth() + 1; } return 1; } bool TreeMapItem::initialized() { if (!_children) { _children = new TreeMapItemList; return false; } return true; } void TreeMapItem::addItem(TreeMapItem *i) { if (!i) { return; } if (!_children) { _children = new TreeMapItemList; } i->setParent(this); _children->append(i); // preserve insertion order if (sorting(nullptr) != -1) { std::sort(_children->begin(), _children->end(), treeMapItemLessThan); } } // default implementations of virtual functions double TreeMapItem::value() const { return _value; } double TreeMapItem::sum() const { return _sum; } DrawParams::Position TreeMapItem::position(int f) const { Position p = StoredDrawParams::position(f); if (_widget && (p == Default)) { p = _widget->fieldPosition(f); } return p; } // use widget font const QFont &TreeMapItem::font() const { return _widget->currentFont(); } bool TreeMapItem::isMarked(int) const { return false; } int TreeMapItem::borderWidth() const { if (_widget) { return _widget->borderWidth(); } return 2; } int TreeMapItem::sorting(bool *ascending) const { if (ascending) { *ascending = _sortAscending; } return _sortTextNo; } // do *not* set sorting recursively void TreeMapItem::setSorting(int textNo, bool ascending) { if (_sortTextNo == textNo) { if (_sortAscending == ascending) { return; } if (textNo == -1) { // when no sorting is done, order change does not do anything _sortAscending = ascending; return; } } _sortAscending = ascending; _sortTextNo = textNo; if (_children && _sortTextNo != -1) { std::sort(_children->begin(), _children->end(), treeMapItemLessThan); } } void TreeMapItem::resort(bool recursive) { if (!_children) { return; } if (_sortTextNo != -1) { std::sort(_children->begin(), _children->end(), treeMapItemLessThan); } if (recursive) foreach (TreeMapItem *i, *_children) { i->resort(recursive); } } TreeMapItem::SplitMode TreeMapItem::splitMode() const { if (_widget) { return _widget->splitMode(); } return Best; } int TreeMapItem::rtti() const { return 0; } TreeMapItemList *TreeMapItem::children() { if (!_children) { _children = new TreeMapItemList; } return _children; } void TreeMapItem::clearItemRect() { _rect = QRect(); clearFreeRects(); } void TreeMapItem::clearFreeRects() { _freeRects.clear(); } void TreeMapItem::addFreeRect(const QRect &r) { // do not add invalid rects if ((r.width() < 1) || (r.height() < 1)) { return; } if (0) qCDebug(FSVIEWLOG) << "addFree(" << path(0).join(QStringLiteral("/")) << ", " << r.x() << "/" << r.y() << "-" << r.width() << "x" << r.height() << ")" << endl; if (_freeRects.isEmpty()) { _freeRects.append(r); return; } // join rect with last rect if possible // this saves memory and does not make the tooltip flicker QRect &last = _freeRects.last(); bool replaced = false; if ((last.left() == r.left()) && (last.width() == r.width())) { if ((last.bottom() + 1 == r.top()) || (r.bottom() + 1 == last.top())) { last |= r; replaced = true; } } else if ((last.top() == r.top()) && (last.height() == r.height())) { if ((last.right() + 1 == r.left()) || (r.right() + 1 == last.left())) { last |= r; replaced = true; } } if (!replaced) { _freeRects.append(r); return; } if (0) qCDebug(FSVIEWLOG) << " united with last to (" << last.x() << "/" << last.y() << "-" << last.width() << "x" << last.height() << ")" << endl; } // TreeMapWidget TreeMapWidget::TreeMapWidget(TreeMapItem *base, QWidget *parent) : QWidget(parent) { _base = base; _base->setWidget(this); _font = font(); _fontHeight = fontMetrics().height(); // default behaviour _selectionMode = Single; _splitMode = TreeMapItem::AlwaysBest; _visibleWidth = 2; _reuseSpace = false; _skipIncorrectBorder = false; _drawSeparators = false; _allowRotation = true; _borderWidth = 2; _shading = true; // beautiful is default! _maxSelectDepth = -1; // unlimited _maxDrawingDepth = -1; // unlimited _minimalArea = -1; // unlimited _markNo = 0; for (int i = 0; i < 4; i++) { _drawFrame[i] = true; _transparent[i] = false; } // _stopAtText will be unset on resizing (per default) // _textVisible will be true on resizing (per default) // _forceText will be false on resizing (per default) // start state: _selection is an empty list _current = nullptr; _oldCurrent = nullptr; _pressed = nullptr; _lastOver = nullptr; _needsRefresh = _base; setAttribute(Qt::WA_NoSystemBackground, true); setFocusPolicy(Qt::StrongFocus); } TreeMapWidget::~TreeMapWidget() { delete _base; } const QFont &TreeMapWidget::currentFont() const { return _font; } void TreeMapWidget::setSplitMode(TreeMapItem::SplitMode m) { if (_splitMode == m) { return; } _splitMode = m; redraw(); } TreeMapItem::SplitMode TreeMapWidget::splitMode() const { return _splitMode; } bool TreeMapWidget::setSplitMode(const QString &mode) { if (mode == QLatin1String("Bisection")) { setSplitMode(TreeMapItem::Bisection); } else if (mode == QLatin1String("Columns")) { setSplitMode(TreeMapItem::Columns); } else if (mode == QLatin1String("Rows")) { setSplitMode(TreeMapItem::Rows); } else if (mode == QLatin1String("AlwaysBest")) { setSplitMode(TreeMapItem::AlwaysBest); } else if (mode == QLatin1String("Best")) { setSplitMode(TreeMapItem::Best); } else if (mode == QLatin1String("HAlternate")) { setSplitMode(TreeMapItem::HAlternate); } else if (mode == QLatin1String("VAlternate")) { setSplitMode(TreeMapItem::VAlternate); } else if (mode == QLatin1String("Horizontal")) { setSplitMode(TreeMapItem::Horizontal); } else if (mode == QLatin1String("Vertical")) { setSplitMode(TreeMapItem::Vertical); } else { return false; } return true; } QString TreeMapWidget::splitModeString() const { QString mode; switch (splitMode()) { case TreeMapItem::Bisection: mode = QStringLiteral("Bisection"); break; case TreeMapItem::Columns: mode = QStringLiteral("Columns"); break; case TreeMapItem::Rows: mode = QStringLiteral("Rows"); break; case TreeMapItem::AlwaysBest: mode = QStringLiteral("AlwaysBest"); break; case TreeMapItem::Best: mode = QStringLiteral("Best"); break; case TreeMapItem::HAlternate: mode = QStringLiteral("HAlternate"); break; case TreeMapItem::VAlternate: mode = QStringLiteral("VAlternate"); break; case TreeMapItem::Horizontal: mode = QStringLiteral("Horizontal"); break; case TreeMapItem::Vertical: mode = QStringLiteral("Vertical"); break; default: mode = QStringLiteral("Unknown"); break; } return mode; } void TreeMapWidget::setShadingEnabled(bool s) { if (_shading == s) { return; } _shading = s; redraw(); } void TreeMapWidget::drawFrame(int d, bool b) { if ((d < 0) || (d >= 4) || (_drawFrame[d] == b)) { return; } _drawFrame[d] = b; redraw(); } void TreeMapWidget::setTransparent(int d, bool b) { if ((d < 0) || (d >= 4) || (_transparent[d] == b)) { return; } _transparent[d] = b; redraw(); } void TreeMapWidget::setAllowRotation(bool enable) { if (_allowRotation == enable) { return; } _allowRotation = enable; redraw(); } void TreeMapWidget::setVisibleWidth(int width, bool reuseSpace) { if (_visibleWidth == width && _reuseSpace == reuseSpace) { return; } _visibleWidth = width; _reuseSpace = reuseSpace; redraw(); } void TreeMapWidget::setSkipIncorrectBorder(bool enable) { if (_skipIncorrectBorder == enable) { return; } _skipIncorrectBorder = enable; redraw(); } void TreeMapWidget::setBorderWidth(int w) { if (_borderWidth == w) { return; } _borderWidth = w; redraw(); } void TreeMapWidget::setMaxDrawingDepth(int d) { if (_maxDrawingDepth == d) { return; } _maxDrawingDepth = d; redraw(); } QString TreeMapWidget::defaultFieldType(int f) const { return i18n("Text %1", f + 1); } QString TreeMapWidget::defaultFieldStop(int) const { return QString(); } bool TreeMapWidget::defaultFieldVisible(int f) const { return (f < 2); } bool TreeMapWidget::defaultFieldForced(int) const { return false; } DrawParams::Position TreeMapWidget::defaultFieldPosition(int f) const { switch (f % 4) { case 0: return DrawParams::TopLeft; case 1: return DrawParams::TopRight; case 2: return DrawParams::BottomRight; case 3: return DrawParams::BottomLeft; default: break; } return DrawParams::TopLeft; } bool TreeMapWidget::resizeAttr(int size) { if (size < 0 || size >= MAX_FIELD) { return false; } if (size > _attr.size()) { int oldSize = _attr.size(); _attr.resize(size); while (oldSize < size) { _attr[oldSize].type = defaultFieldType(oldSize); _attr[oldSize].stop = defaultFieldStop(oldSize); _attr[oldSize].visible = defaultFieldVisible(oldSize); _attr[oldSize].forced = defaultFieldForced(oldSize); _attr[oldSize].pos = defaultFieldPosition(oldSize); oldSize++; } } return true; } void TreeMapWidget::setFieldType(int f, const QString &type) { if ((_attr.size() < f + 1) && (type == defaultFieldType(f))) { return; } if (resizeAttr(f + 1)) { _attr[f].type = type; } // no need to redraw: the type string is not visible in the TreeMap } QString TreeMapWidget::fieldType(int f) const { if (f < 0 || _attr.size() < f + 1) { return defaultFieldType(f); } return _attr[f].type; } void TreeMapWidget::setFieldStop(int f, const QString &stop) { if ((_attr.size() < f + 1) && (stop == defaultFieldStop(f))) { return; } if (resizeAttr(f + 1)) { _attr[f].stop = stop; redraw(); } } QString TreeMapWidget::fieldStop(int f) const { if (f < 0 || _attr.size() < f + 1) { return defaultFieldStop(f); } return _attr[f].stop; } void TreeMapWidget::setFieldVisible(int f, bool enable) { if ((_attr.size() < f + 1) && (enable == defaultFieldVisible(f))) { return; } if (resizeAttr(f + 1)) { _attr[f].visible = enable; redraw(); } } bool TreeMapWidget::fieldVisible(int f) const { if (f < 0 || _attr.size() < f + 1) { return defaultFieldVisible(f); } return _attr[f].visible; } void TreeMapWidget::setFieldForced(int f, bool enable) { if ((_attr.size() < f + 1) && (enable == defaultFieldForced(f))) { return; } if (resizeAttr(f + 1)) { _attr[f].forced = enable; if (_attr[f].visible) { redraw(); } } } bool TreeMapWidget::fieldForced(int f) const { if (f < 0 || _attr.size() < f + 1) { return defaultFieldForced(f); } return _attr[f].forced; } void TreeMapWidget::setFieldPosition(int f, TreeMapItem::Position pos) { if ((_attr.size() < f + 1) && (pos == defaultFieldPosition(f))) { return; } if (resizeAttr(f + 1)) { _attr[f].pos = pos; if (_attr[f].visible) { redraw(); } } } DrawParams::Position TreeMapWidget::fieldPosition(int f) const { if (f < 0 || _attr.size() < f + 1) { return defaultFieldPosition(f); } return _attr[f].pos; } void TreeMapWidget::setFieldPosition(int f, const QString &pos) { if (pos == QLatin1String("TopLeft")) { setFieldPosition(f, DrawParams::TopLeft); } else if (pos == QLatin1String("TopCenter")) { setFieldPosition(f, DrawParams::TopCenter); } else if (pos == QLatin1String("TopRight")) { setFieldPosition(f, DrawParams::TopRight); } else if (pos == QLatin1String("BottomLeft")) { setFieldPosition(f, DrawParams::BottomLeft); } else if (pos == QLatin1String("BottomCenter")) { setFieldPosition(f, DrawParams::BottomCenter); } else if (pos == QLatin1String("BottomRight")) { setFieldPosition(f, DrawParams::BottomRight); } else if (pos == QLatin1String("Default")) { setFieldPosition(f, DrawParams::Default); } } QString TreeMapWidget::fieldPositionString(int f) const { TreeMapItem::Position pos = fieldPosition(f); if (pos == DrawParams::TopLeft) { return QStringLiteral("TopLeft"); } if (pos == DrawParams::TopCenter) { return QStringLiteral("TopCenter"); } if (pos == DrawParams::TopRight) { return QStringLiteral("TopRight"); } if (pos == DrawParams::BottomLeft) { return QStringLiteral("BottomLeft"); } if (pos == DrawParams::BottomCenter) { return QStringLiteral("BottomCenter"); } if (pos == DrawParams::BottomRight) { return QStringLiteral("BottomRight"); } if (pos == DrawParams::Default) { return QStringLiteral("Default"); } return QStringLiteral("unknown"); } void TreeMapWidget::setMinimalArea(int area) { if (_minimalArea == area) { return; } _minimalArea = area; redraw(); } void TreeMapWidget::deletingItem(TreeMapItem *i) { // remove any references to the item to be deleted _selection.removeAll(i); _tmpSelection.removeAll(i); if (_current == i) { _current = nullptr; } if (_oldCurrent == i) { _oldCurrent = nullptr; } if (_pressed == i) { _pressed = nullptr; } if (_lastOver == i) { _lastOver = nullptr; } // do not redraw a deleted item if (_needsRefresh == i) { // we can safely redraw the parent, as deleting order is // from child to parent; i.e. i->parent() is existing. _needsRefresh = i->parent(); } } QString TreeMapWidget::tipString(TreeMapItem *i) const { QString tip, itemTip; while (i) { if (!i->text(0).isEmpty()) { itemTip = i->text(0); if (!i->text(1).isEmpty()) { itemTip += " (" + i->text(1) + ')'; } if (!tip.isEmpty()) { tip += '\n'; } tip += itemTip; } i = i->parent(); } return tip; } TreeMapItem *TreeMapWidget::item(int x, int y) const { if (!rect().contains(x, y)) { return nullptr; } if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << "item(" << x << "," << y << "):"; } TreeMapItem *p = _base; TreeMapItem *i; while (1) { TreeMapItemList *list = p->children(); i = nullptr; if (list) { int idx; for (idx = 0; idx < list->size(); idx++) { i = list->at(idx); if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " Checking " << i->path(0).join(QStringLiteral("/")) << " (" << i->itemRect().x() << "/" << i->itemRect().y() << "-" << i->itemRect().width() << "x" << i->itemRect().height() << ")" << endl; if (i->itemRect().contains(x, y)) { if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << " .. Got. Index " << idx; } p->setIndex(idx); break; } } if (idx == list->size()) { i = nullptr; // not contained in child } } if (!i) { static TreeMapItem *last = nullptr; if (p != last) { last = p; if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << "item(" << x << "," << y << "): Got " << p->path(0).join(QStringLiteral("/")) << " (Size " << p->itemRect().width() << "x" << p->itemRect().height() << ", Val " << p->value() << ")" << endl; } return p; } p = i; } return nullptr; } TreeMapItem *TreeMapWidget::possibleSelection(TreeMapItem *i) const { if (i) { if (_maxSelectDepth >= 0) { int depth = i->depth(); while (i && depth > _maxSelectDepth) { i = i->parent(); depth--; } } } return i; } TreeMapItem *TreeMapWidget::visibleItem(TreeMapItem *i) const { if (i) { /* Must have a visible area */ while (i && ((i->itemRect().width() < 1) || (i->itemRect().height() < 1))) { TreeMapItem *p = i->parent(); if (!p) { break; } int idx = p->children()->indexOf(i); idx--; if (idx < 0) { i = p; } else { i = p->children()->at(idx); } } } return i; } void TreeMapWidget::setSelected(TreeMapItem *item, bool selected) { if (!item) { return; } item = possibleSelection(item); setCurrent(item); TreeMapItem *changed = setTmpSelected(item, selected); if (!changed) { return; } _selection = _tmpSelection; if (_selectionMode == Single) { emit selectionChanged(item); } emit selectionChanged(); redraw(changed); if (0) qCDebug(FSVIEWLOG) << (selected ? "S" : "Des") << "elected Item " << (item ? item->path(0).join(QLatin1String("")) : QStringLiteral("(null)")) << " (depth " << (item ? item->depth() : -1) << ")" << endl; } void TreeMapWidget::setMarked(int markNo, bool redrawWidget) { // if there is no marking, return if ((_markNo == 0) && (markNo == 0)) { return; } _markNo = markNo; if (!clearSelection() && redrawWidget) { redraw(); } } /* Returns all items which appear only in one of the given lists */ TreeMapItemList TreeMapWidget::diff(TreeMapItemList &l1, TreeMapItemList &l2) { TreeMapItemList l; foreach (TreeMapItem *i, l1) if (!l2.contains(i)) { l.append(i); } foreach (TreeMapItem *i, l2) if (!l1.contains(i)) { l.append(i); } return l; } /* Only modifies _tmpSelection. * Returns 0 when no change happened, otherwise the TreeMapItem that has * to be redrawn for all changes. */ TreeMapItem *TreeMapWidget::setTmpSelected(TreeMapItem *item, bool selected) { if (!item) { return nullptr; } if (_selectionMode == NoSelection) { return nullptr; } TreeMapItemList old = _tmpSelection; if (_selectionMode == Single) { _tmpSelection.clear(); if (selected) { _tmpSelection.append(item); } } else { if (selected) { // first remove any selection which is parent or child of foreach (TreeMapItem *i, _tmpSelection) if (i->isChildOf(item) || item->isChildOf(i)) { _tmpSelection.removeAll(i); } _tmpSelection.append(item); } else { _tmpSelection.removeAll(item); } } return diff(old, _tmpSelection).commonParent(); } bool TreeMapWidget::clearSelection(TreeMapItem *parent) { TreeMapItemList old = _selection; // remove any selection which is child of foreach (TreeMapItem *i, _selection) if (i->isChildOf(parent)) { _selection.removeAll(i); } TreeMapItem *changed = diff(old, _selection).commonParent(); if (changed) { _tmpSelection = _selection; changed->redraw(); emit selectionChanged(); } return (changed != nullptr); } bool TreeMapWidget::isSelected(TreeMapItem *i) const { if (!i) { return false; } return _selection.contains(i); } bool TreeMapWidget::isTmpSelected(TreeMapItem *i) { if (!i) { return false; } return _tmpSelection.contains(i); } void TreeMapWidget::setCurrent(TreeMapItem *i, bool kbd) { TreeMapItem *old = _current; _current = i; if (_markNo > 0) { // remove mark _markNo = 0; if (1) qCDebug(FSVIEWLOG) << "setCurrent(" << i->path(0).join(QStringLiteral("/")) << ") - mark removed" << endl; // always complete redraw needed to remove mark redraw(); if (old == _current) { return; } } else { if (old == _current) { return; } if (old) { old->redraw(); } if (i) { i->redraw(); } } //qCDebug(FSVIEWLOG) << "Current Item " << (i ? i->path().ascii() : "(null)"); emit currentChanged(i, kbd); } void TreeMapWidget::setRangeSelection(TreeMapItem *i1, TreeMapItem *i2, bool selected) { i1 = possibleSelection(i1); i2 = possibleSelection(i2); setCurrent(i2); TreeMapItem *changed = setTmpRangeSelection(i1, i2, selected); if (!changed) { return; } _selection = _tmpSelection; if (_selectionMode == Single) { emit selectionChanged(i2); } emit selectionChanged(); redraw(changed); } TreeMapItem *TreeMapWidget::setTmpRangeSelection(TreeMapItem *i1, TreeMapItem *i2, bool selected) { if ((i1 == nullptr) && (i2 == nullptr)) { return nullptr; } if ((i1 == nullptr) || i1->isChildOf(i2)) { return setTmpSelected(i2, selected); } if ((i2 == nullptr) || i2->isChildOf(i1)) { return setTmpSelected(i1, selected); } TreeMapItem *changed = setTmpSelected(i1, selected); TreeMapItem *changed2 = setTmpSelected(i2, selected); if (changed2) { changed = changed2->commonParent(changed); } TreeMapItem *commonParent = i1; while (commonParent && !i2->isChildOf(commonParent)) { i1 = commonParent; commonParent = commonParent->parent(); } if (!commonParent) { return changed; } while (i2 && i2->parent() != commonParent) { i2 = i2->parent(); } if (!i2) { return changed; } TreeMapItemList *list = commonParent->children(); if (!list) { return changed; } bool between = false; foreach (TreeMapItem *i, *list) { if (between) { if (i == i1 || i == i2) { break; } changed2 = setTmpSelected(i, selected); if (changed2) { changed = changed2->commonParent(changed); } } else if (i == i1 || i == i2) { between = true; } } return changed; } void TreeMapWidget::contextMenuEvent(QContextMenuEvent *e) { //qCDebug(FSVIEWLOG) << "TreeMapWidget::contextMenuEvent"; if (receivers(SIGNAL(contextMenuRequested(TreeMapItem*,QPoint)))) { e->accept(); } if (e->reason() == QContextMenuEvent::Keyboard) { QRect r = (_current) ? _current->itemRect() : _base->itemRect(); QPoint p = QPoint(r.left() + r.width() / 2, r.top() + r.height() / 2); emit contextMenuRequested(_current, p); } else { TreeMapItem *i = item(e->x(), e->y()); emit contextMenuRequested(i, e->pos()); } } void TreeMapWidget::mousePressEvent(QMouseEvent *e) { //qCDebug(FSVIEWLOG) << "TreeMapWidget::mousePressEvent"; _oldCurrent = _current; TreeMapItem *i = item(e->x(), e->y()); _pressed = i; _inShiftDrag = e->modifiers().testFlag(Qt::ShiftModifier); _inControlDrag = e->modifiers().testFlag(Qt::ControlModifier); _lastOver = _pressed; TreeMapItem *changed = nullptr; TreeMapItem *item = possibleSelection(_pressed); switch (_selectionMode) { case Single: changed = setTmpSelected(item, true); break; case Multi: changed = setTmpSelected(item, !isTmpSelected(item)); break; case Extended: if (_inControlDrag) { changed = setTmpSelected(item, !isTmpSelected(item)); } else if (_inShiftDrag) { TreeMapItem *sCurrent = possibleSelection(_current); changed = setTmpRangeSelection(sCurrent, item, !isTmpSelected(item)); } else { _selectionMode = Single; changed = setTmpSelected(item, true); _selectionMode = Extended; } break; default: break; } // item under mouse always selected on right button press if (e->button() == Qt::RightButton) { TreeMapItem *changed2 = setTmpSelected(item, true); if (changed2) { changed = changed2->commonParent(changed); } } setCurrent(_pressed); if (changed) { redraw(changed); } if (e->button() == Qt::RightButton) { // emit selection change if (!(_tmpSelection == _selection)) { _selection = _tmpSelection; if (_selectionMode == Single) { emit selectionChanged(_lastOver); } emit selectionChanged(); } _pressed = nullptr; _lastOver = nullptr; emit rightButtonPressed(i, e->pos()); } } void TreeMapWidget::mouseMoveEvent(QMouseEvent *e) { //qCDebug(FSVIEWLOG) << "TreeMapWidget::mouseMoveEvent"; if (!_pressed) { return; } TreeMapItem *over = item(e->x(), e->y()); if (_lastOver == over) { return; } setCurrent(over); if (over == nullptr) { _lastOver = nullptr; return; } TreeMapItem *changed = nullptr; TreeMapItem *item = possibleSelection(over); switch (_selectionMode) { case Single: changed = setTmpSelected(item, true); break; case Multi: changed = setTmpSelected(item, !isTmpSelected(item)); break; case Extended: if (_inControlDrag) { changed = setTmpSelected(item, !isTmpSelected(item)); } else { TreeMapItem *sLast = possibleSelection(_lastOver); changed = setTmpRangeSelection(sLast, item, true); } break; default: break; } _lastOver = over; if (changed) { redraw(changed); } } void TreeMapWidget::mouseReleaseEvent(QMouseEvent *) { //qCDebug(FSVIEWLOG) << "TreeMapWidget::mouseReleaseEvent"; if (!_pressed) { return; } if (!_lastOver) { // take back setCurrent(_oldCurrent); TreeMapItem *changed = diff(_tmpSelection, _selection).commonParent(); _tmpSelection = _selection; if (changed) { redraw(changed); } } else { if (!(_tmpSelection == _selection)) { _selection = _tmpSelection; if (_selectionMode == Single) { emit selectionChanged(_lastOver); } emit selectionChanged(); } if (!_inControlDrag && !_inShiftDrag && (_pressed == _lastOver)) { emit clicked(_lastOver); } } _pressed = nullptr; _lastOver = nullptr; } void TreeMapWidget::mouseDoubleClickEvent(QMouseEvent *e) { TreeMapItem *over = item(e->x(), e->y()); emit doubleClicked(over); } /* returns -1 if nothing visible found */ int nextVisible(TreeMapItem *i) { TreeMapItem *p = i->parent(); if (!p || p->itemRect().isEmpty()) { return -1; } int idx = p->children()->indexOf(i); if (idx < 0) { return -1; } while (idx < p->children()->count() - 1) { idx++; QRect r = p->children()->at(idx)->itemRect(); if (r.width() > 1 && r.height() > 1) { return idx; } } return -1; } /* returns -1 if nothing visible found */ int prevVisible(TreeMapItem *i) { TreeMapItem *p = i->parent(); if (!p || p->itemRect().isEmpty()) { return -1; } int idx = p->children()->indexOf(i); if (idx < 0) { return -1; } while (idx > 0) { idx--; QRect r = p->children()->at(idx)->itemRect(); if (r.width() > 1 && r.height() > 1) { return idx; } } return -1; } void TreeMapWidget::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape && _pressed) { // take back if (_oldCurrent != _lastOver) { setCurrent(_oldCurrent); } if (!(_tmpSelection == _selection)) { TreeMapItem *changed = diff(_tmpSelection, _selection).commonParent(); _tmpSelection = _selection; if (changed) { redraw(changed); } } _pressed = nullptr; _lastOver = nullptr; } if ((e->key() == Qt::Key_Space) || (e->key() == Qt::Key_Return)) { switch (_selectionMode) { case NoSelection: break; case Single: setSelected(_current, true); break; case Multi: setSelected(_current, !isSelected(_current)); break; case Extended: if (e->modifiers().testFlag(Qt::ControlModifier) || e->modifiers().testFlag(Qt::ShiftModifier)) { setSelected(_current, !isSelected(_current)); } else { _selectionMode = Single; setSelected(_current, true); _selectionMode = Extended; } } if (_current && (e->key() == Qt::Key_Return)) { emit returnPressed(_current); } return; } if (!_current) { if (e->key() == Qt::Key_Down) { setCurrent(_base, true); } return; } TreeMapItem *old = _current, *newItem; TreeMapItem *p = _current->parent(); bool goBack; if (_current->sorting(&goBack) == -1) { // noSorting goBack = false; } if ((e->key() == Qt::Key_Backspace) || (e->key() == Qt::Key_Up)) { newItem = visibleItem(p); setCurrent(newItem, true); } else if (e->key() == Qt::Key_Left) { int newIdx = goBack ? nextVisible(_current) : prevVisible(_current); if (p && newIdx >= 0) { p->setIndex(newIdx); setCurrent(p->children()->at(newIdx), true); } } else if (e->key() == Qt::Key_Right) { int newIdx = goBack ? prevVisible(_current) : nextVisible(_current); if (p && newIdx >= 0) { p->setIndex(newIdx); setCurrent(p->children()->at(newIdx), true); } } else if (e->key() == Qt::Key_Down) { if (_current->children() && _current->children()->count() > 0) { int newIdx = _current->index(); if (newIdx < 0) { newIdx = goBack ? (_current->children()->count() - 1) : 0; } if (newIdx >= _current->children()->count()) { newIdx = _current->children()->count() - 1; } newItem = visibleItem(_current->children()->at(newIdx)); setCurrent(newItem, true); } } if (old == _current) { return; } if (!e->modifiers().testFlag(Qt::ControlModifier)) { return; } if (!e->modifiers().testFlag(Qt::ShiftModifier)) { return; } switch (_selectionMode) { case NoSelection: break; case Single: setSelected(_current, true); break; case Multi: setSelected(_current, !isSelected(_current)); break; case Extended: if (e->modifiers().testFlag(Qt::ControlModifier)) { setSelected(_current, !isSelected(_current)); } else { setSelected(_current, isSelected(old)); } } } void TreeMapWidget::fontChange(const QFont &) { redraw(); } // react on tooltip events bool TreeMapWidget::event(QEvent *event) { if (event->type() == QEvent::ToolTip) { QHelpEvent *helpEvent = static_cast(event); TreeMapItem *i = item(helpEvent->pos().x(), helpEvent->pos().y()); bool hasTip = false; if (i) { const QList &rList = i->freeRects(); foreach (const QRect &r, rList) { if (r.contains(helpEvent->pos())) { hasTip = true; break; } } } if (hasTip) { QToolTip::showText(helpEvent->globalPos(), tipString(i)); } else { QToolTip::hideText(); } } return QWidget::event(event); } void TreeMapWidget::paintEvent(QPaintEvent *) { drawTreeMap(); } // Updates screen from shadow buffer, // but redraws before if needed void TreeMapWidget::drawTreeMap() { // no need to draw if hidden if (!isVisible()) { return; } if (_pixmap.size() != size()) { _needsRefresh = _base; } if (_needsRefresh) { if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << "Redrawing " << _needsRefresh->path(0).join(QStringLiteral("/")); } if (_needsRefresh == _base) { // redraw whole widget _pixmap = QPixmap(size()); _pixmap.fill(palette().color(backgroundRole())); } QPainter p(&_pixmap); if (_needsRefresh == _base) { p.setPen(Qt::black); p.drawRect(QRect(2, 2, QWidget::width() - 5, QWidget::height() - 5)); _base->setItemRect(QRect(3, 3, QWidget::width() - 6, QWidget::height() - 6)); } else { // only subitem if (!_needsRefresh->itemRect().isValid()) { return; } } // reset cached font object; it could have been changed _font = font(); _fontHeight = fontMetrics().height(); drawItems(&p, _needsRefresh); _needsRefresh = nullptr; } QStylePainter p(this); p.drawPixmap(0, 0, width(), height(), _pixmap); if (hasFocus()) { QStyleOptionFocusRect opt; opt.rect = rect(); opt.palette = palette(); opt.state = QStyle::State_None; p.drawPrimitive(QStyle::PE_FrameFocusRect, opt); } } void TreeMapWidget::redraw(TreeMapItem *i) { if (!i) { return; } if (!_needsRefresh) { _needsRefresh = i; } else { if (!i->isChildOf(_needsRefresh)) { _needsRefresh = _needsRefresh->commonParent(i); } } if (isVisible()) { // delayed drawing if we have multiple redraw requests update(); } } void TreeMapWidget::drawItem(QPainter *p, TreeMapItem *item) { bool isSelected = false; if (_markNo > 0) { for (TreeMapItem *i = item; i; i = i->parent()) { if (i->isMarked(_markNo)) { isSelected = true; break; } } } else { foreach (TreeMapItem *i, _tmpSelection) { if (item->isChildOf(i)) { isSelected = true; break; } } } bool isCurrent = _current && item->isChildOf(_current); int dd = item->depth(); if (isTransparent(dd)) { return; } RectDrawing d(item->itemRect()); item->setSelected(isSelected); item->setCurrent(isCurrent); item->setShaded(_shading); item->drawFrame(drawFrame(dd)); d.drawBack(p, item); } bool TreeMapWidget::horizontal(TreeMapItem *i, const QRect &r) { switch (i->splitMode()) { case TreeMapItem::HAlternate: return (i->depth() % 2) == 1; case TreeMapItem::VAlternate: return (i->depth() % 2) == 0; case TreeMapItem::Horizontal: return true; case TreeMapItem::Vertical: return false; default: return r.width() > r.height(); } return false; } /** * Draw TreeMapItems recursive, starting from item */ void TreeMapWidget::drawItems(QPainter *p, TreeMapItem *item) { if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << "+drawItems(" << item->path(0).join(QStringLiteral("/")) << ", " << item->itemRect().x() << "/" << item->itemRect().y() << "-" << item->itemRect().width() << "x" << item->itemRect().height() << "), Val " << item->value() << ", Sum " << item->sum() << endl; drawItem(p, item); item->clearFreeRects(); QRect origRect = item->itemRect(); int bw = item->borderWidth(); QRect r = QRect(origRect.x() + bw, origRect.y() + bw, origRect.width() - 2 * bw, origRect.height() - 2 * bw); TreeMapItemList *list = item->children(); bool stopDrawing = false; // only subdivide if there are children if (!list || list->count() == 0) { stopDrawing = true; } // only subdivide if there is enough space if (!stopDrawing && (r.width() <= 0 || r.height() <= 0)) { stopDrawing = true; } // stop drawing if maximum depth is reached if (!stopDrawing && (_maxDrawingDepth >= 0 && item->depth() >= _maxDrawingDepth)) { stopDrawing = true; } // stop drawing if stopAtText is reached if (!stopDrawing) for (int no = 0; no < _attr.size(); no++) { QString stopAt = fieldStop(no); if (!stopAt.isEmpty() && (item->text(no) == stopAt)) { stopDrawing = true; break; } } // area size is checked later... #if 0 // stop drawing if minimal area size is reached if (!stopDrawing && (_minimalArea > 0) && (r.width() * r.height() < _minimalArea)) { stopDrawing = true; } #endif if (stopDrawing) { if (list) { // invalidate rects foreach (TreeMapItem *i, *list) { i->clearItemRect(); } } // tooltip appears on whole item rect item->addFreeRect(item->itemRect()); // if we have space for text... if ((r.height() < _fontHeight) || (r.width() < _fontHeight)) { return; } RectDrawing d(r); item->setRotated(_allowRotation && (r.height() > r.width())); for (int no = 0; no < _attr.size(); no++) { if (!fieldVisible(no)) { continue; } d.drawField(p, no, item); } r = d.remainingRect(item); if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << "-drawItems(" << item->path(0).join(QStringLiteral("/")) << ")"; } return; } double user_sum, child_sum, self; // user supplied sum user_sum = item->sum(); // own sum child_sum = 0; foreach (TreeMapItem *i, *list) { child_sum += i->value(); if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " child: " << i->text(0) << ", value " << i->value() << endl; } QRect orig = r; // if we have space for text... if ((r.height() >= _fontHeight) && (r.width() >= _fontHeight)) { RectDrawing d(r); item->setRotated(_allowRotation && (r.height() > r.width())); for (int no = 0; no < _attr.size(); no++) { if (!fieldVisible(no)) { continue; } if (!fieldForced(no)) { continue; } d.drawField(p, no, item); } r = d.remainingRect(item); } if (orig.x() == r.x()) { // Strings on top item->addFreeRect(QRect(orig.x(), orig.y(), orig.width(), orig.height() - r.height())); } else { // Strings on the left item->addFreeRect(QRect(orig.x(), orig.y(), orig.width() - r.width(), orig.height())); } if (user_sum == 0) { // user did not supply any sum user_sum = child_sum; self = 0; } else { self = user_sum - child_sum; if (user_sum < child_sum) { //qCDebug(FSVIEWLOG) << "TreeMWidget " << // item->path() << ": User sum " << user_sum << " < Child Items sum " << child_sum << endl; // invalid user supplied sum: ignore and use calculate sum user_sum = child_sum; self = 0.0; } else { // Try to put the border waste in self // percent of wasted space on border... float borderArea = origRect.width() * origRect.height(); borderArea = (borderArea - r.width() * r.height()) / borderArea; unsigned borderValue = (unsigned)(borderArea * user_sum); if (borderValue > self) { if (_skipIncorrectBorder) { r = origRect; // should add my self to nested self and set my self =0 } else { self = 0.0; } } else { self -= borderValue; } user_sum = child_sum + self; } } bool rotate = (_allowRotation && (r.height() > r.width())); int self_length = (int)(((rotate) ? r.width() : r.height()) * self / user_sum + .5); if (self_length > 0) { // take space for self cost QRect sr = r; if (rotate) { sr.setWidth(self_length); r.setRect(r.x() + sr.width(), r.y(), r.width() - sr.width(), r.height()); } else { sr.setHeight(self_length); r.setRect(r.x(), r.y() + sr.height(), r.width(), r.height() - sr.height()); } // set selfRect (not occupied by children) for tooltip item->addFreeRect(sr); if (0) qCDebug(FSVIEWLOG) << "Item " << item->path(0).join(QStringLiteral("/")) << ": SelfR " << sr.x() << "/" << sr.y() << "-" << sr.width() << "/" << sr.height() << ", self " << self << "/" << user_sum << endl; if ((sr.height() >= _fontHeight) && (sr.width() >= _fontHeight)) { RectDrawing d(sr); item->setRotated(_allowRotation && (r.height() > r.width())); for (int no = 0; no < _attr.size(); no++) { if (!fieldVisible(no)) { continue; } if (fieldForced(no)) { continue; } d.drawField(p, no, item); } } user_sum -= self; } bool goBack; if (item->sorting(&goBack) == -1) { // noSorting goBack = false; } int idx = goBack ? (list->size() - 1) : 0; if (item->splitMode() == TreeMapItem::Columns) { int len = list->count(); bool drawDetails = true; while (len > 0 && user_sum > 0) { int firstIdx = idx; double valSum = 0; int lenLeft = len; int columns = (int)(sqrt((double)len * r.width() / r.height()) + .5); if (columns == 0) { columns = 1; //should never be needed } while (lenLeft > 0 && ((double)valSum * (len - lenLeft) < (double)len * user_sum / columns / columns)) { valSum += list->at(idx)->value(); if (goBack) { --idx; } else { ++idx; } lenLeft--; } // we always split horizontally int nextPos = (int)((double)r.width() * valSum / user_sum); QRect firstRect = QRect(r.x(), r.y(), nextPos, r.height()); if (nextPos < _visibleWidth) { if (item->sorting(nullptr) == -1) { // fill current rect with hash pattern drawFill(item, p, firstRect); } else { // fill rest with hash pattern drawFill(item, p, r, list, firstIdx, len, goBack); break; } } else { drawDetails = drawItemArray(p, item, firstRect, valSum, list, firstIdx, len - lenLeft, goBack); } r.setRect(r.x() + nextPos, r.y(), r.width() - nextPos, r.height()); user_sum -= valSum; len = lenLeft; if (!drawDetails) { if (item->sorting(nullptr) == -1) { drawDetails = true; } else { drawFill(item, p, r, list, idx, len, goBack); break; } } } } else if (item->splitMode() == TreeMapItem::Rows) { int len = list->count(); bool drawDetails = true; while (len > 0 && user_sum > 0) { int firstIdx = idx; double valSum = 0; int lenLeft = len; int rows = (int)(sqrt((double)len * r.height() / r.width()) + .5); if (rows == 0) { rows = 1; //should never be needed } while (lenLeft > 0 && ((double)valSum * (len - lenLeft) < (double)len * user_sum / rows / rows)) { valSum += list->at(idx)->value(); if (goBack) { --idx; } else { ++idx; } lenLeft--; } // we always split horizontally int nextPos = (int)((double)r.height() * valSum / user_sum); QRect firstRect = QRect(r.x(), r.y(), r.width(), nextPos); if (nextPos < _visibleWidth) { if (item->sorting(nullptr) == -1) { drawFill(item, p, firstRect); } else { drawFill(item, p, r, list, firstIdx, len, goBack); break; } } else { drawDetails = drawItemArray(p, item, firstRect, valSum, list, firstIdx, len - lenLeft, goBack); } r.setRect(r.x(), r.y() + nextPos, r.width(), r.height() - nextPos); user_sum -= valSum; len = lenLeft; if (!drawDetails) { if (item->sorting(nullptr) == -1) { drawDetails = true; } else { drawFill(item, p, r, list, idx, len, goBack); break; } } } } else { drawItemArray(p, item, r, user_sum, list, idx, list->count(), goBack); } if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << "-drawItems(" << item->path(0).join(QStringLiteral("/")) << ")"; } } // fills area with a pattern if to small to draw children void TreeMapWidget::drawFill(TreeMapItem *i, QPainter *p, const QRect &r) { p->setBrush(Qt::Dense4Pattern); p->setPen(Qt::NoPen); p->drawRect(QRect(r.x(), r.y(), r.width() - 1, r.height() - 1)); i->addFreeRect(r); } // fills area with a pattern if to small to draw children void TreeMapWidget::drawFill(TreeMapItem *i, QPainter *p, const QRect &r, TreeMapItemList *list, int idx, int len, bool goBack) { if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " +drawFill(" << r.x() << "/" << r.y() << "-" << r.width() << "x" << r.height() << ", len " << len << ")" << endl; p->setBrush(Qt::Dense4Pattern); p->setPen(Qt::NoPen); p->drawRect(QRect(r.x(), r.y(), r.width() - 1, r.height() - 1)); i->addFreeRect(r); // reset rects while (len > 0 && (i = list->value(idx))) { if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << " Reset Rect " << i->path(0).join(QStringLiteral("/")); } i->clearItemRect(); if (goBack) { --idx; } else { ++idx; } len--; } if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " -drawFill(" << r.x() << "/" << r.y() << "-" << r.width() << "x" << r.height() << ", len " << len << ")" << endl; } // returns false if rect gets to small bool TreeMapWidget::drawItemArray(QPainter *p, TreeMapItem *item, const QRect &r, double user_sum, TreeMapItemList *list, int idx, int len, bool goBack) { if (user_sum == 0) { return false; } static bool b2t = true; // stop recursive bisection for small rectangles if (((r.height() < _visibleWidth) && (r.width() < _visibleWidth)) || ((_minimalArea > 0) && (r.width() * r.height() < _minimalArea))) { drawFill(item, p, r, list, idx, len, goBack); return false; } if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " +drawItemArray(" << item->path(0).join(QStringLiteral("/")) << ", " << r.x() << "/" << r.y() << "-" << r.width() << "x" << r.height() << ")" << endl; if (len > 2 && (item->splitMode() == TreeMapItem::Bisection)) { int firstIdx = idx; double valSum = 0; int lenLeft = len; //while (lenLeft>0 && valSum len / 2) { valSum += list->at(idx)->value(); if (goBack) { --idx; } else { ++idx; } lenLeft--; } // draw first half... bool drawOn; QRect secondRect; if (r.width() > r.height()) { int halfPos = (int)((double)r.width() * valSum / user_sum); QRect firstRect = QRect(r.x(), r.y(), halfPos, r.height()); drawOn = drawItemArray(p, item, firstRect, valSum, list, firstIdx, len - lenLeft, goBack); secondRect.setRect(r.x() + halfPos, r.y(), r.width() - halfPos, r.height()); } else { int halfPos = (int)((double)r.height() * valSum / user_sum); QRect firstRect = QRect(r.x(), r.y(), r.width(), halfPos); drawOn = drawItemArray(p, item, firstRect, valSum, list, firstIdx, len - lenLeft, goBack); secondRect.setRect(r.x(), r.y() + halfPos, r.width(), r.height() - halfPos); } // if no sorting, do not stop drawing if (item->sorting(nullptr) == -1) { drawOn = true; } // second half if (drawOn) drawOn = drawItemArray(p, item, secondRect, user_sum - valSum, list, idx, lenLeft, goBack); else { drawFill(item, p, secondRect, list, idx, len, goBack); } if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " -drawItemArray(" << item->path(0).join(QStringLiteral("/")) << ")" << endl; return drawOn; } bool hor = horizontal(item, r); TreeMapItem *i; QRect fullRect = r; while (len > 0) { i = list->at(idx); if (user_sum <= 0) { if (DEBUG_DRAWING) { qCDebug(FSVIEWLOG) << "drawItemArray: Reset " << i->path(0).join(QStringLiteral("/")); } i->clearItemRect(); if (goBack) { --idx; } else { ++idx; } len--; continue; } // stop drawing for small rectangles if (((fullRect.height() < _visibleWidth) && (fullRect.width() < _visibleWidth)) || ((_minimalArea > 0) && (fullRect.width() * fullRect.height() < _minimalArea))) { drawFill(item, p, fullRect, list, idx, len, goBack); if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " -drawItemArray(" << item->path(0).join(QStringLiteral("/")) << "): Stop" << endl; return false; } if (i->splitMode() == TreeMapItem::AlwaysBest) { hor = fullRect.width() > fullRect.height(); } int lastPos = hor ? fullRect.width() : fullRect.height(); double val = i->value(); int nextPos = (user_sum <= 0.0) ? 0 : (int)(lastPos * val / user_sum + .5); if (nextPos > lastPos) { nextPos = lastPos; } if ((item->sorting(nullptr) != -1) && (nextPos < _visibleWidth)) { drawFill(item, p, fullRect, list, idx, len, goBack); if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " -drawItemArray(" << item->path(0).join(QStringLiteral("/")) << "): Stop" << endl; return false; } QRect currRect = fullRect; if (hor) { currRect.setWidth(nextPos); } else { if (b2t) { currRect.setRect(fullRect.x(), fullRect.bottom() - nextPos + 1, fullRect.width(), nextPos); } else { currRect.setHeight(nextPos); } } // do not draw very small rectangles: if (nextPos >= _visibleWidth) { i->setItemRect(currRect); drawItems(p, i); } else { i->clearItemRect(); drawFill(item, p, currRect); } // draw Separator if (_drawSeparators && (nextPos < lastPos)) { p->setPen(Qt::black); if (hor) { if (fullRect.top() <= fullRect.bottom()) { p->drawLine(fullRect.x() + nextPos, fullRect.top(), fullRect.x() + nextPos, fullRect.bottom()); } } else { if (fullRect.left() <= fullRect.right()) { p->drawLine(fullRect.left(), fullRect.y() + nextPos, fullRect.right(), fullRect.y() + nextPos); } } nextPos++; } if (hor) fullRect.setRect(fullRect.x() + nextPos, fullRect.y(), lastPos - nextPos, fullRect.height()); else { if (b2t) fullRect.setRect(fullRect.x(), fullRect.y(), fullRect.width(), lastPos - nextPos); else fullRect.setRect(fullRect.x(), fullRect.y() + nextPos, fullRect.width(), lastPos - nextPos); } user_sum -= val; if (goBack) { --idx; } else { ++idx; } len--; } if (DEBUG_DRAWING) qCDebug(FSVIEWLOG) << " -drawItemArray(" << item->path(0).join(QStringLiteral("/")) << "): Continue" << endl; return true; } /*---------------------------------------------------------------- * Popup menus for option setting */ void TreeMapWidget::splitActivated(QAction *a) { int id = a->data().toInt(); if (id == _splitID) { setSplitMode(TreeMapItem::Bisection); } else if (id == _splitID + 1) { setSplitMode(TreeMapItem::Columns); } else if (id == _splitID + 2) { setSplitMode(TreeMapItem::Rows); } else if (id == _splitID + 3) { setSplitMode(TreeMapItem::AlwaysBest); } else if (id == _splitID + 4) { setSplitMode(TreeMapItem::Best); } else if (id == _splitID + 5) { setSplitMode(TreeMapItem::VAlternate); } else if (id == _splitID + 6) { setSplitMode(TreeMapItem::HAlternate); } else if (id == _splitID + 7) { setSplitMode(TreeMapItem::Horizontal); } else if (id == _splitID + 8) { setSplitMode(TreeMapItem::Vertical); } } void TreeMapWidget::addSplitDirectionItems(QMenu *popup, int id) { _splitID = id; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(splitActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::splitActivated); addPopupItem(popup, i18n("Recursive Bisection"), splitMode() == TreeMapItem::Bisection, id++); addPopupItem(popup, i18n("Columns"), splitMode() == TreeMapItem::Columns, id++); addPopupItem(popup, i18n("Rows"), splitMode() == TreeMapItem::Rows, id++); addPopupItem(popup, i18n("Always Best"), splitMode() == TreeMapItem::AlwaysBest, id++); addPopupItem(popup, i18n("Best"), splitMode() == TreeMapItem::Best, id++); addPopupItem(popup, i18n("Alternate (V)"), splitMode() == TreeMapItem::VAlternate, id++); addPopupItem(popup, i18n("Alternate (H)"), splitMode() == TreeMapItem::HAlternate, id++); addPopupItem(popup, i18n("Horizontal"), splitMode() == TreeMapItem::Horizontal, id++); addPopupItem(popup, i18n("Vertical"), splitMode() == TreeMapItem::Vertical, id++); } void TreeMapWidget::visualizationActivated(QAction *a) { int id = a->data().toInt(); if (id == _visID + 2) { setSkipIncorrectBorder(!skipIncorrectBorder()); } else if (id == _visID + 3) { setBorderWidth(0); } else if (id == _visID + 4) { setBorderWidth(1); } else if (id == _visID + 5) { setBorderWidth(2); } else if (id == _visID + 6) { setBorderWidth(3); } else if (id == _visID + 10) { setAllowRotation(!allowRotation()); } else if (id == _visID + 11) { setShadingEnabled(!isShadingEnabled()); } else if (id < _visID + 19 || id > _visID + 100) { return; } id -= 20 + _visID; int f = id / 10; if ((id % 10) == 1) { setFieldVisible(f, !fieldVisible(f)); } else if ((id % 10) == 2) { setFieldForced(f, !fieldForced(f)); } else if ((id % 10) == 3) { setFieldPosition(f, DrawParams::TopLeft); } else if ((id % 10) == 4) { setFieldPosition(f, DrawParams::TopCenter); } else if ((id % 10) == 5) { setFieldPosition(f, DrawParams::TopRight); } else if ((id % 10) == 6) { setFieldPosition(f, DrawParams::BottomLeft); } else if ((id % 10) == 7) { setFieldPosition(f, DrawParams::BottomCenter); } else if ((id % 10) == 8) { setFieldPosition(f, DrawParams::BottomRight); } } void TreeMapWidget::addVisualizationItems(QMenu *popup, int id) { _visID = id; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(visualizationActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::visualizationActivated); QMenu *spopup = new QMenu(i18n("Nesting")); addSplitDirectionItems(spopup, id + 100); popup->addMenu(spopup); QMenu *bpopup = new QMenu(i18n("Border")); popup->addMenu(bpopup); addPopupItem(bpopup, i18n("Correct Borders Only"), skipIncorrectBorder(), id + 2); bpopup->addSeparator(); for (int i = 0; i < 4; ++i) { addPopupItem(bpopup, i18n("Width %1", i), borderWidth() == i, id + i + 3); } addPopupItem(popup, i18n("Allow Rotation"), allowRotation(), id + 10); addPopupItem(popup, i18n("Shading"), isShadingEnabled(), id + 11); if (_attr.size() == 0) { return; } popup->addSeparator(); id += 20; for (int f = 0; f < _attr.size(); f++, id += 10) { QMenu *tpopup = new QMenu(_attr[f].type); popup->addMenu(tpopup); addPopupItem(tpopup, i18n("Visible"), _attr[f].visible, id + 1); addPopupItem(tpopup, i18n("Take Space From Children"), _attr[f].forced, id + 2, _attr[f].visible); tpopup->addSeparator(); addPopupItem(tpopup, i18n("Top Left"), _attr[f].pos == DrawParams::TopLeft, id + 3, _attr[f].visible); addPopupItem(tpopup, i18n("Top Center"), _attr[f].pos == DrawParams::TopCenter, id + 4, _attr[f].visible); addPopupItem(tpopup, i18n("Top Right"), _attr[f].pos == DrawParams::TopRight, id + 5, _attr[f].visible); addPopupItem(tpopup, i18n("Bottom Left"), _attr[f].pos == DrawParams::BottomLeft, id + 6, _attr[f].visible); addPopupItem(tpopup, i18n("Bottom Center"), _attr[f].pos == DrawParams::BottomCenter, id + 7, _attr[f].visible); addPopupItem(tpopup, i18n("Bottom Right"), _attr[f].pos == DrawParams::BottomRight, id + 8, _attr[f].visible); } } void TreeMapWidget::selectionActivated(QAction *a) { int id = a->data().toInt(); TreeMapItem *i = _menuItem; id -= _selectionID; while (id > 0 && i) { i = i->parent(); id--; } if (i) { setSelected(i, true); } } void TreeMapWidget::addSelectionItems(QMenu *popup, int id, TreeMapItem *i) { if (!i) { return; } _selectionID = id; _menuItem = i; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(selectionActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::selectionActivated); while (i) { QString name = i->text(0); if (name.isEmpty()) { break; } addPopupItem(popup, i->text(0), false, id++); i = i->parent(); } } void TreeMapWidget::fieldStopActivated(QAction *a) { int id = a->data().toInt(); if (id == _fieldStopID) { setFieldStop(0, QString()); } else { TreeMapItem *i = _menuItem; id -= _fieldStopID + 1; while (id > 0 && i) { i = i->parent(); id--; } if (i) { setFieldStop(0, i->text(0)); } } } void TreeMapWidget::addFieldStopItems(QMenu *popup, int id, TreeMapItem *i) { _fieldStopID = id; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(fieldStopActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::fieldStopActivated); addPopupItem(popup, i18n("No %1 Limit", fieldType(0)), fieldStop(0).isEmpty(), id); _menuItem = i; bool foundFieldStop = false; if (i) { popup->addSeparator(); while (i) { id++; QString name = i->text(0); if (name.isEmpty()) { break; } bool bChecked = fieldStop(0) == i->text(0); addPopupItem(popup, i->text(0), bChecked, id); if (bChecked) { foundFieldStop = true; } i = i->parent(); } } if (!foundFieldStop && !fieldStop(0).isEmpty()) { popup->addSeparator(); addPopupItem(popup, fieldStop(0), true, id + 1); } } void TreeMapWidget::areaStopActivated(QAction *a) { const int id = a->data().toInt(); if (id == _areaStopID) { setMinimalArea(-1); } else if (id == _areaStopID + 1) { int area = _menuItem ? (_menuItem->width() * _menuItem->height()) : -1; setMinimalArea(area); } else if (id == _areaStopID + 2) { setMinimalArea(100); } else if (id == _areaStopID + 3) { setMinimalArea(400); } else if (id == _areaStopID + 4) { setMinimalArea(1000); } else if (id == _areaStopID + 5) { setMinimalArea(minimalArea() * 2); } else if (id == _areaStopID + 6) { setMinimalArea(minimalArea() / 2); } } void TreeMapWidget::addAreaStopItems(QMenu *popup, int id, TreeMapItem *i) { _areaStopID = id; _menuItem = i; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(areaStopActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::areaStopActivated); bool foundArea = false; addPopupItem(popup, i18n("No Area Limit"), minimalArea() == -1, id); if (i) { int area = i->width() * i->height(); popup->addSeparator(); addPopupItem(popup, i18n("Area of '%1' (%2)", i->text(0), area), area == minimalArea(), id + 1); if (area == minimalArea()) { foundArea = true; } } popup->addSeparator(); int area = 100, count; for (count = 0; count < 3; count++) { addPopupItem(popup, i18np("1 Pixel", "%1 Pixels", area), area == minimalArea(), id + 2 + count); if (area == minimalArea()) { foundArea = true; } area = (area == 100) ? 400 : (area == 400) ? 1000 : 4000; } if (minimalArea() > 0) { popup->addSeparator(); if (!foundArea) { addPopupItem(popup, i18np("1 Pixel", "%1 Pixels", minimalArea()), true, id + 10); } addPopupItem(popup, i18n("Double Area Limit (to %1)", minimalArea() * 2), false, id + 5); addPopupItem(popup, i18n("Halve Area Limit (to %1)", minimalArea() / 2), false, id + 6); } } void TreeMapWidget::depthStopActivated(QAction *a) { const int id = a->data().toInt(); if (id == _depthStopID) { setMaxDrawingDepth(-1); } else if (id == _depthStopID + 1) { int d = _menuItem ? _menuItem->depth() : -1; setMaxDrawingDepth(d); } else if (id == _depthStopID + 2) { setMaxDrawingDepth(maxDrawingDepth() - 1); } else if (id == _depthStopID + 3) { setMaxDrawingDepth(maxDrawingDepth() + 1); } else if (id == _depthStopID + 4) { setMaxDrawingDepth(2); } else if (id == _depthStopID + 5) { setMaxDrawingDepth(4); } else if (id == _depthStopID + 6) { setMaxDrawingDepth(6); } } void TreeMapWidget::addDepthStopItems(QMenu *popup, int id, TreeMapItem *i) { _depthStopID = id; _menuItem = i; - connect(popup, SIGNAL(triggered(QAction*)), - this, SLOT(depthStopActivated(QAction*))); + connect(popup, &QMenu::triggered, this, &TreeMapWidget::depthStopActivated); bool foundDepth = false; addPopupItem(popup, i18n("No Depth Limit"), maxDrawingDepth() == -1, id); if (i) { int d = i->depth(); popup->addSeparator(); addPopupItem(popup, i18n("Depth of '%1' (%2)", i->text(0), d), d == maxDrawingDepth(), id + 1); if (d == maxDrawingDepth()) { foundDepth = true; } } popup->addSeparator(); int depth = 2, count; for (count = 0; count < 3; count++) { addPopupItem(popup, i18n("Depth %1", depth), depth == maxDrawingDepth(), id + 4 + count); if (depth == maxDrawingDepth()) { foundDepth = true; } depth = (depth == 2) ? 4 : 6; } if (maxDrawingDepth() > 1) { popup->addSeparator(); if (!foundDepth) { addPopupItem(popup, i18n("Depth %1", maxDrawingDepth()), true, id + 10); } addPopupItem(popup, i18n("Decrement (to %1)", maxDrawingDepth() - 1), false, id + 2); addPopupItem(popup, i18n("Increment (to %1)", maxDrawingDepth() + 1), false, id + 3); } } /*---------------------------------------------------------------- * Option saving/restoring */ void TreeMapWidget::saveOptions(KConfigGroup *config, const QString &prefix) { config->writeEntry(prefix + "Nesting", splitModeString()); config->writeEntry(prefix + "AllowRotation", allowRotation()); config->writeEntry(prefix + "ShadingEnabled", isShadingEnabled()); config->writeEntry(prefix + "OnlyCorrectBorder", skipIncorrectBorder()); config->writeEntry(prefix + "BorderWidth", borderWidth()); config->writeEntry(prefix + "MaxDepth", maxDrawingDepth()); config->writeEntry(prefix + "MinimalArea", minimalArea()); int f, fCount = _attr.size(); config->writeEntry(prefix + "FieldCount", fCount); for (f = 0; f < fCount; f++) { config->writeEntry(QString(prefix + "FieldVisible%1").arg(f), _attr[f].visible); config->writeEntry(QString(prefix + "FieldForced%1").arg(f), _attr[f].forced); config->writeEntry(QString(prefix + "FieldStop%1").arg(f), _attr[f].stop); config->writeEntry(QString(prefix + "FieldPosition%1").arg(f), fieldPositionString(f)); } } void TreeMapWidget::restoreOptions(KConfigGroup *config, const QString &prefix) { bool enabled; int num; QString str; str = config->readEntry(prefix + "Nesting", QString()); if (!str.isEmpty()) { setSplitMode(str); } if (config->hasKey(prefix + "AllowRotation")) { enabled = config->readEntry(prefix + "AllowRotation", true); setAllowRotation(enabled); } if (config->hasKey(prefix + "ShadingEnabled")) { enabled = config->readEntry(prefix + "ShadingEnabled", true); setShadingEnabled(enabled); } if (config->hasKey(prefix + "OnlyCorrectBorder")) { enabled = config->readEntry(prefix + "OnlyCorrectBorder", false); setSkipIncorrectBorder(enabled); } num = config->readEntry(prefix + "BorderWidth", -2); if (num != -2) { setBorderWidth(num); } num = config->readEntry(prefix + "MaxDepth", -2); if (num != -2) { setMaxDrawingDepth(num); } num = config->readEntry(prefix + "MinimalArea", -2); if (num != -2) { setMinimalArea(num); } num = config->readEntry(prefix + "FieldCount", -2); if (num <= 0 || num > MAX_FIELD) { return; } int f; for (f = 0; f < num; f++) { str = QString(prefix + "FieldVisible%1").arg(f); if (config->hasKey(str)) { setFieldVisible(f, config->readEntry(str, false)); } str = QString(prefix + "FieldForced%1").arg(f); if (config->hasKey(str)) { setFieldForced(f, config->readEntry(str, false)); } str = config->readEntry(QString(prefix + "FieldStop%1").arg(f), QString()); setFieldStop(f, str); str = config->readEntry(QString(prefix + "FieldPosition%1").arg(f), QString()); if (!str.isEmpty()) { setFieldPosition(f, str); } } } void TreeMapWidget::addPopupItem(QMenu *popup, const QString &text, bool bChecked, int id, bool bEnabled) { QAction *a; a = popup->addAction(text); a->setCheckable(true); a->setChecked(bChecked); a->setData(id); a->setEnabled(bEnabled); }