File indexing completed on 2024-04-28 17:02:21

0001 /*
0002    This file is part of Massif Visualizer
0003 
0004    Copyright 2010 Milian Wolff <mail@milianw.de>
0005 
0006    This library is free software; you can redistribute it and/or
0007    modify it under the terms of the GNU Lesser General Public
0008    License as published by the Free Software Foundation; either
0009    version 2.1 of the License, or (at your option) version 3, or any
0010    later version accepted by the membership of KDE e.V. (or its
0011    successor approved by the membership of KDE e.V.), which shall
0012    act as a proxy defined in Section 6 of version 3 of the license.
0013 
0014    This library is distributed in the hope that it will be useful,
0015    but WITHOUT ANY WARRANTY; without even the implied warranty of
0016    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0017    Lesser General Public License for more details.
0018 
0019    You should have received a copy of the GNU Lesser General Public
0020    License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0021 */
0022 
0023 #include "datatreemodel.h"
0024 
0025 #include "massifdata/filedata.h"
0026 #include "massifdata/snapshotitem.h"
0027 #include "massifdata/treeleafitem.h"
0028 #include "massifdata/util.h"
0029 
0030 #include <KLocalizedString>
0031 
0032 #include <QtGui/QBrush>
0033 
0034 using namespace Massif;
0035 
0036 DataTreeModel::DataTreeModel(QObject* parent)
0037     : QAbstractItemModel(parent), m_data(0)
0038 {
0039 }
0040 
0041 DataTreeModel::~DataTreeModel()
0042 {
0043 }
0044 
0045 void DataTreeModel::setSource(const FileData* data)
0046 {
0047     if (m_data) {
0048         beginRemoveRows(QModelIndex(), 0, rowCount() - 1);
0049         m_data = 0;
0050         m_nodeToRow.clear();
0051         m_heapRootToSnapshot.clear();
0052         endRemoveRows();
0053     }
0054     if (data) {
0055         beginInsertRows(QModelIndex(), 0, data->snapshots().size() - 1);
0056         m_data = data;
0057         int row = 0;
0058         foreach(const SnapshotItem* item, m_data->snapshots()) {
0059             if (item->heapTree()) {
0060                 mapNodeToRow(item->heapTree(), row++);
0061                 m_heapRootToSnapshot[item->heapTree()] = item;
0062             } else {
0063                 row++;
0064             }
0065         }
0066         endInsertRows();
0067     }
0068 }
0069 
0070 void DataTreeModel::mapNodeToRow(const TreeLeafItem* node, const int row)
0071 {
0072     m_nodeToRow[node] = row;
0073     int r = 0;
0074     foreach(const TreeLeafItem* child, node->children()) {
0075         mapNodeToRow(child, r++);
0076     }
0077 }
0078 
0079 QVariant DataTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
0080 {
0081     Q_UNUSED(section)
0082     Q_UNUSED(orientation)
0083     Q_UNUSED(role)
0084     return i18n("Snapshots");
0085 }
0086 
0087 QVariant DataTreeModel::data(const QModelIndex& index, int role) const
0088 {
0089     // FIXME kdchart queries (-1, -1) for empty models
0090     if ( index.row() == -1 || index.column() == -1 ) {
0091 //         qWarning() << "DataTreeModel::data: FIXME fix kdchart views to not query model data for invalid indices!";
0092         return QVariant();
0093     }
0094 
0095     Q_ASSERT(index.row() >= 0 && index.row() < rowCount(index.parent()));
0096     Q_ASSERT(index.column() >= 0 && index.column() < columnCount(index.parent()));
0097     Q_ASSERT(m_data);
0098 
0099 
0100     // custom background for peak snapshot
0101     if ( role == Qt::BackgroundRole ) {
0102 //         double maxValue = 1;
0103         const double maxValue = m_data->peak()->cost();
0104         double currentValue = 0;
0105         if ( !index.parent().isValid() && m_data->peak() ) {
0106 //             maxValue = m_data->peak()->memHeap();
0107             const SnapshotItem* snapshot = m_data->snapshots().at(index.row());
0108             currentValue = snapshot->cost();
0109         } else if (index.parent().isValid()) {
0110             Q_ASSERT(index.internalPointer());
0111             const TreeLeafItem* node = static_cast<TreeLeafItem*>(index.internalPointer());
0112             currentValue = node->cost();
0113             Q_ASSERT(node->parent());
0114             /*
0115             TreeLeafItem* parent = node->parent();
0116             while (parent->parent()) {
0117                 parent = parent->parent();
0118             }
0119             maxValue = parent->cost();
0120             // normalize
0121             maxValue -= parent->children().last()->cost();
0122             currentValue -= parent->children().last()->cost();
0123             */
0124         }
0125         if (currentValue > 0) {
0126             const double ratio = (currentValue / maxValue);
0127             QColor c = QColor::fromHsv(120 - ratio * 120, 255, 255, (-((ratio-1) * (ratio-1))) * 120 + 120);
0128 //             QColor c = QColor::fromHsv(120 - ratio * 120, 255, 255, 120);
0129             return QBrush(c);
0130         } else {
0131             return QVariant();
0132         }
0133     }
0134 
0135     if (role == ModelItemRole) {
0136         return QVariant::fromValue(itemForIndex(index));
0137     }
0138 
0139     if (role != Qt::DisplayRole && role != Qt::ToolTipRole && role != RawLabelRole && role != TreeItemRole)
0140     {
0141         return QVariant();
0142     }
0143 
0144     if (!index.parent().isValid()) {
0145         const SnapshotItem* snapshot = m_data->snapshots().at(index.row());
0146         if (role == Qt::ToolTipRole) {
0147             if (snapshot == m_data->peak()) {
0148                 return i18n("Peak snapshot: heap cost of %1", prettyCost(snapshot->cost()));
0149             } else {
0150                 return i18n("Snapshot #%1: heap cost of %2", snapshot->number(), prettyCost(snapshot->cost()));
0151             }
0152         } else if (role == RawLabelRole) {
0153             return i18nc("%1: snapshot number", "Snapshot #%1", snapshot->number());
0154         } else if (role == TreeItemRole) {
0155             return QVariant::fromValue<const TreeLeafItem*>(0);
0156         }
0157         const QString costStr = prettyCost(snapshot->cost());
0158         if (snapshot == m_data->peak()) {
0159             return i18nc("%1: cost, %2: snapshot number",
0160                          "%1: Snapshot #%2 (peak)", costStr,  snapshot->number());
0161         } else {
0162             return i18nc("%1: cost, %2: snapshot number",
0163                          "%1: Snapshot #%2", costStr, snapshot->number());
0164         }
0165     } else {
0166         Q_ASSERT(index.internalPointer());
0167         const TreeLeafItem* item = static_cast<const TreeLeafItem*>(index.internalPointer());
0168         if (role == Qt::ToolTipRole) {
0169             return tooltipForTreeLeaf(item, snapshotForTreeLeaf(item), item->label());
0170         } else if (role == RawLabelRole) {
0171             return item->label();
0172         } else if (role == TreeItemRole) {
0173             return QVariant::fromValue<const TreeLeafItem*>(item);
0174         }
0175         return i18nc("%1: cost, %2: snapshot label (i.e. func name etc.)", "%1: %2",
0176                      prettyCost(item->cost()), QString::fromLatin1(prettyLabel(item->label())));
0177     }
0178     return QVariant();
0179 }
0180 
0181 int DataTreeModel::columnCount(const QModelIndex& parent) const
0182 {
0183     Q_UNUSED(parent)
0184     return 1;
0185 }
0186 
0187 int DataTreeModel::rowCount(const QModelIndex& parent) const
0188 {
0189     if (!m_data || (parent.isValid() && parent.column() != 0)) {
0190         return 0;
0191     }
0192 
0193     if (parent.isValid()) {
0194         if (!parent.internalPointer()) {
0195             // snapshot without detailed heaptree
0196             return 0;
0197         }
0198         return static_cast<const TreeLeafItem*>(parent.internalPointer())->children().size();
0199     } else {
0200         return m_data->snapshots().size();
0201     }
0202 }
0203 
0204 QModelIndex DataTreeModel::index(int row, int column, const QModelIndex& parent) const
0205 {
0206     if (row < 0 || row >= rowCount(parent) || column < 0 || column >= columnCount(parent)) {
0207         // invalid
0208         return QModelIndex();
0209     }
0210 
0211     if (parent.isValid()) {
0212         if (parent.column() == 0) {
0213             Q_ASSERT(parent.internalPointer());
0214             // parent is a tree leaf item
0215             return createIndex(row, column, static_cast<void*>(static_cast<const TreeLeafItem*>(parent.internalPointer())->children().at(row)));
0216         } else {
0217             return QModelIndex();
0218         }
0219     } else {
0220         return createIndex(row, column, static_cast<void*>(m_data->snapshots().at(row)->heapTree()));
0221     }
0222 }
0223 
0224 QModelIndex DataTreeModel::parent(const QModelIndex& child) const
0225 {
0226     if (child.internalPointer()) {
0227         const TreeLeafItem* item = static_cast<const TreeLeafItem*>(child.internalPointer());
0228         if (item->parent()) {
0229             int row = m_nodeToRow.value(item->parent(), -1);
0230             Q_ASSERT(row != -1);
0231             // somewhere in the detailed heap tree
0232             return createIndex(row, 0, static_cast<void*>(item->parent()));
0233         } else {
0234             // snapshot item with heap tree
0235             return QModelIndex();
0236         }
0237     } else {
0238         // snapshot item without detailed heap tree
0239         return QModelIndex();
0240     }
0241 }
0242 
0243 QModelIndex DataTreeModel::indexForSnapshot(const SnapshotItem* snapshot) const
0244 {
0245     int idx = m_data->snapshots().indexOf(const_cast<SnapshotItem*>(snapshot));
0246     if ( idx == -1 ) {
0247         return QModelIndex();
0248     }
0249     return index(idx, 0);
0250 }
0251 
0252 QModelIndex DataTreeModel::indexForTreeLeaf(const TreeLeafItem* node) const
0253 {
0254     if (!m_data) {
0255         return QModelIndex();
0256     }
0257     int row = m_nodeToRow.value(node, -1);
0258     if (row == -1) {
0259         return QModelIndex();
0260     }
0261     return createIndex(row, 0, const_cast<void*>(static_cast<const void*>(node)));
0262 }
0263 
0264 ModelItem DataTreeModel::itemForIndex(const QModelIndex& idx) const
0265 {
0266     if (!m_data || !idx.isValid() || idx.row() >= m_data->snapshots().count()) {
0267         return ModelItem(0, 0);
0268     }
0269     if (idx.parent().isValid()) {
0270         Q_ASSERT(idx.internalPointer());
0271         return ModelItem(static_cast<const TreeLeafItem*>(idx.internalPointer()), 0);
0272     } else {
0273         return ModelItem(0, m_data->snapshots().at(idx.row()));
0274     }
0275 }
0276 
0277 QModelIndex DataTreeModel::indexForItem(const ModelItem& item) const
0278 {
0279     if (!item.first && !item.second) {
0280         return QModelIndex();
0281     }
0282     Q_ASSERT((item.first && !item.second) || (!item.first && item.second));
0283     if (item.first) {
0284         return indexForTreeLeaf(item.first);
0285     } else {
0286         return indexForSnapshot(item.second);
0287     }
0288 }
0289 
0290 const SnapshotItem* DataTreeModel::snapshotForTreeLeaf(const TreeLeafItem* node) const
0291 {
0292     while (node->parent()) {
0293         node = node->parent();
0294     }
0295     return m_heapRootToSnapshot.value(node);
0296 }