File indexing completed on 2024-04-28 04:57:34

0001 /* This file is part of the KDE project
0002 
0003    Copyright (C) 2008 Javier Goday <jgoday@gmail.com>
0004 
0005    This program is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU General Public
0007    License as published by the Free Software Foundation; either
0008    version 2 of the License, or (at your option) any later version.
0009 */
0010 
0011 #include "rangetreewidget.h"
0012 #include "settings.h"
0013 
0014 #include <QDebug>
0015 
0016 #include <QApplication>
0017 #include <QFont>
0018 #include <QHeaderView>
0019 #include <QLinearGradient>
0020 #include <QList>
0021 #include <QPainter>
0022 #include <QPalette>
0023 #include <QStandardItem>
0024 #include <QStandardItemModel>
0025 #include <QUrl>
0026 #include <QVariant>
0027 
0028 class RangeTreeWidget::Range
0029 {
0030 public:
0031     Range()
0032     {
0033     }
0034 
0035     bool check(const QVariant &data) const
0036     {
0037         if (data.type() == QVariant::String) {
0038             return (QString::compare(data.toString(), min.toString()) == 0);
0039         } else if (data.type() == QVariant::Int || data.type() == QVariant::Double) {
0040             if (data.toDouble() >= min.toDouble() && (data.toDouble() <= max.toDouble() || max.toDouble() < 0)) {
0041                 // the last range ends with -1
0042                 return true;
0043             }
0044         }
0045 
0046         return false;
0047     }
0048 
0049     int id;
0050     QVariant min;
0051     QVariant max;
0052     QString title;
0053 };
0054 
0055 RangeSortFilterProxyModel::RangeSortFilterProxyModel(QObject *parent)
0056     : QSortFilterProxyModel(parent)
0057 {
0058 }
0059 
0060 RangeSortFilterProxyModel::~RangeSortFilterProxyModel()
0061 {
0062 }
0063 
0064 bool RangeSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
0065 {
0066     // if the row is a range row, we include in the filter always
0067     if (source_parent.isValid()) {
0068         return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
0069     }
0070 
0071     return true;
0072 }
0073 
0074 RangeTreeWidget::RangeTreeWidget(QWidget *parent)
0075     : QTreeView(parent)
0076     , m_data()
0077     , m_ranges()
0078     , m_rangeDelegate(nullptr)
0079 {
0080     setDragEnabled(false);
0081     setAlternatingRowColors(true);
0082     setEditTriggers(QAbstractItemView::NoEditTriggers);
0083     header()->setSectionsMovable(false);
0084 
0085     // initialize the standard item model of the tree
0086     m_model = new QStandardItemModel(this);
0087     m_proxyModel = new RangeSortFilterProxyModel(this);
0088 
0089     m_proxyModel->setSourceModel(m_model);
0090     setModel(m_proxyModel);
0091 
0092     // delegate for the range title
0093     auto *delegate = new RangeTreeWidgetItemDelegate(this);
0094     setItemDelegate(delegate);
0095 }
0096 
0097 RangeTreeWidget::~RangeTreeWidget()
0098 {
0099     QList<int> list;
0100     for (int i = 0; i < 5; i++) {
0101         list.append(columnWidth(i));
0102     }
0103     Settings::setHistoryColumnWidths(list);
0104     Settings::self()->save();
0105     clear();
0106 
0107     delete m_rangeDelegate;
0108 }
0109 
0110 int RangeTreeWidget::addRange(const QVariant &min, const QVariant &max, const QString &title)
0111 {
0112     int row = m_data.size();
0113 
0114     Range range;
0115     range.min = min;
0116     range.max = max;
0117     range.title = title;
0118     range.id = row;
0119     m_ranges << range;
0120 
0121     m_data[row] = new QStandardItem(title);
0122     m_model->insertRow(row, m_data[row]);
0123     setFirstColumnSpanned(row, QModelIndex(), true);
0124     // openPersistentEditor(model()->index(row, 0, QModelIndex()));
0125 
0126     // expand the first row
0127     if (row == 0) {
0128         setExpanded(model()->index(row, 0, QModelIndex()), true);
0129     }
0130 
0131     return row;
0132 }
0133 
0134 void RangeTreeWidget::clear()
0135 {
0136     m_model->clear();
0137     m_data.clear();
0138     m_ranges.clear();
0139 }
0140 
0141 void RangeTreeWidget::add(const QVariant &data, const QString &column)
0142 {
0143     QVariantList list;
0144     list << QVariant(column);
0145 
0146     add(data, list);
0147 }
0148 
0149 void RangeTreeWidget::add(const QVariant &data, const QVariantList &columns)
0150 {
0151     QStandardItem *parent = getRange(data);
0152 
0153     QList<QStandardItem *> list;
0154     foreach (const QVariant &item, columns) {
0155         list << new QStandardItem(item.toString());
0156     }
0157 
0158     parent->appendRow(list);
0159     // TODO: need to find a better way to update rangetitlewidget count from the QStandardItem children count
0160     // closePersistentEditor(parent->index());
0161     // openPersistentEditor(parent->index());
0162 }
0163 
0164 void RangeTreeWidget::addLabel(const QString &title)
0165 {
0166     int index = header()->count();
0167     m_model->setColumnCount(index + 1);
0168     m_model->setHeaderData(index, Qt::Horizontal, title);
0169 }
0170 
0171 void RangeTreeWidget::setLabels(const QStringList &labels)
0172 {
0173     m_model->setColumnCount(labels.size());
0174 
0175     for (int i = 0; i < labels.size(); i++) {
0176         m_model->setHeaderData(i, Qt::Horizontal, labels.at(i));
0177     }
0178 }
0179 
0180 void RangeTreeWidget::setRangeDelegate(RangeDelegate *delegate)
0181 {
0182     delete m_rangeDelegate;
0183     m_rangeDelegate = delegate;
0184 }
0185 
0186 QList<QVariantList> RangeTreeWidget::data()
0187 {
0188     QList<QVariantList> list;
0189     foreach (const Range &range, m_ranges) {
0190         QStandardItem *parent = m_model->itemFromIndex(model()->index(range.id, 0, QModelIndex()));
0191 
0192         for (int y = 0; y < parent->rowCount(); y++) {
0193             QVariantList items;
0194             for (int x = 0; x < header()->count(); x++) {
0195                 QStandardItem *item = parent->child(y, x);
0196                 items << item->data(Qt::DisplayRole);
0197             }
0198             list << items;
0199         }
0200     }
0201 
0202     return list;
0203 }
0204 
0205 QStandardItem *RangeTreeWidget::currentItem(int column)
0206 {
0207     QStandardItem *item = nullptr;
0208     if (column >= 0) {
0209         item =
0210             m_model->itemFromIndex(m_model->index(m_proxyModel->mapToSource(currentIndex()).row(), column, m_proxyModel->mapToSource(currentIndex()).parent()));
0211     } else {
0212         item = m_model->itemFromIndex(m_proxyModel->mapToSource(currentIndex()));
0213     }
0214     return item;
0215 }
0216 
0217 QStandardItem *RangeTreeWidget::item(const QModelIndex &index, int column)
0218 {
0219     return m_model->item(m_proxyModel->mapToSource(index).row(), column);
0220 }
0221 
0222 void RangeTreeWidget::removeRow(int row, const QModelIndex &parent)
0223 {
0224     m_model->removeRow(row, parent);
0225 }
0226 
0227 void RangeTreeWidget::setFilterRegExp(const QString &text)
0228 {
0229     m_proxyModel->setFilterRegularExpression(text);
0230 }
0231 
0232 QStandardItem *RangeTreeWidget::getRange(const QVariant &data)
0233 {
0234     QVariant rangeData = data;
0235     if (m_rangeDelegate) {
0236         rangeData = m_rangeDelegate->getRangeData(data);
0237     }
0238 
0239     foreach (const Range &range, m_ranges) {
0240         if (range.check(rangeData)) {
0241             return m_data[range.id];
0242         }
0243     }
0244 
0245     if (m_rangeDelegate) {
0246         int id = addRange(rangeData, rangeData, rangeData.toString());
0247         return m_data[id];
0248     } else {
0249         // if no range found, return the last one
0250         return m_data[m_data.size() - 1];
0251     }
0252 }
0253 
0254 RangeDelegate::RangeDelegate(QObject *parent)
0255     : QObject(parent)
0256 {
0257 }
0258 
0259 RangeDelegate::~RangeDelegate()
0260 {
0261 }
0262 
0263 HostRangeDelegate::HostRangeDelegate(QObject *parent)
0264     : RangeDelegate(parent)
0265 {
0266 }
0267 
0268 HostRangeDelegate::~HostRangeDelegate()
0269 {
0270 }
0271 
0272 QVariant HostRangeDelegate::getRangeData(const QVariant &data)
0273 {
0274     return QUrl(data.toString()).host();
0275 }
0276 
0277 RangeTreeWidgetItemDelegate::RangeTreeWidgetItemDelegate(QAbstractItemView *parent)
0278     : QStyledItemDelegate(parent)
0279 {
0280 }
0281 
0282 void RangeTreeWidgetItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
0283 {
0284     if (index.parent().isValid()) {
0285         QStyledItemDelegate::paint(painter, option, index);
0286     } else if (index.isValid()) {
0287         QStyleOptionViewItem opt(option);
0288         QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
0289         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
0290 
0291         const auto *model = static_cast<const QSortFilterProxyModel *>(index.model());
0292         const auto *s_model = static_cast<const QStandardItemModel *>(model->sourceModel());
0293         QStandardItem *item = s_model->itemFromIndex(model->mapToSource(index));
0294         // draw the range title
0295         painter->save();
0296         QFont font;
0297         font.setBold(true);
0298         painter->setFont(font);
0299         painter->drawText(option.rect.left() + 10,
0300                           option.rect.top() + 5,
0301                           option.rect.width() - 20,
0302                           15,
0303                           Qt::AlignLeft,
0304                           item->data(Qt::DisplayRole).toString() + " (" + QString::number(model->rowCount(index)) + ')');
0305         painter->restore();
0306 
0307         // Draw the line under the title
0308         QColor color = option.palette.color(QPalette::Text);
0309         if (option.state & QStyle::State_Selected) {
0310             color = option.palette.color(QPalette::HighlightedText);
0311         }
0312 
0313         QRect lineRect(option.rect.left() + 10, option.rect.bottom() - 2, 500, 1);
0314 
0315         QLinearGradient gradient(option.rect.left() + 10, option.rect.top(), 500, option.rect.height());
0316         gradient.setColorAt(0, color);
0317         gradient.setColorAt(1, Qt::transparent);
0318 
0319         painter->fillRect(lineRect, gradient);
0320     }
0321 }
0322 
0323 QSize RangeTreeWidgetItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0324 {
0325     Q_UNUSED(option)
0326     Q_UNUSED(index)
0327 
0328     return QSize(0, 30);
0329 }
0330 
0331 #include "moc_rangetreewidget.cpp"