File indexing completed on 2024-05-19 05:08:30

0001 /*
0002     SPDX-FileCopyrightText: 2019 Thomas Baumgart <tbaumgart@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "columnselector.h"
0007 
0008 // ----------------------------------------------------------------------------
0009 // QT Includes
0010 
0011 #include <QList>
0012 #include <QHeaderView>
0013 #include <QMenu>
0014 #include <QPoint>
0015 #include <QDebug>
0016 
0017 // ----------------------------------------------------------------------------
0018 // KDE Includes
0019 
0020 #include <KLocalizedString>
0021 #include <KSharedConfig>
0022 #include <KConfigGroup>
0023 
0024 // ----------------------------------------------------------------------------
0025 // Project Includes
0026 
0027 #include "mymoneyenums.h"
0028 
0029 /// @todo add feature to group multiple column selectors for the same view
0030 /// in different instances by group name.
0031 
0032 class ColumnSelectorPrivate
0033 {
0034     Q_DECLARE_PUBLIC(ColumnSelector)
0035 
0036 public:
0037     ColumnSelectorPrivate(ColumnSelector* qq)
0038         : q_ptr(qq)
0039         , treeView(nullptr)
0040         , tableView(nullptr)
0041         , headerView(nullptr)
0042         , model(nullptr)
0043         , storageOffset(0)
0044         , isInit(false)
0045         , columnSelectionEnabled(true)
0046     {
0047     }
0048 
0049     void setColumnHidden(int col, bool state)
0050     {
0051         if (treeView) {
0052             treeView->setColumnHidden(col, state);
0053         }
0054         else if (tableView) {
0055             tableView->setColumnHidden(col, state);
0056         }
0057     }
0058 
0059     bool isColumnHidden(int col)
0060     {
0061         if (treeView) {
0062             return treeView->isColumnHidden(col);
0063         }
0064         else if (tableView) {
0065             return tableView->isColumnHidden(col);
0066         }
0067         return false;
0068     }
0069 
0070     void init(const QString& _configGroupName)
0071     {
0072         Q_Q(ColumnSelector);
0073 
0074         configGroupName = _configGroupName;
0075 
0076         if ((treeView || tableView) && model) {
0077             const auto maxColumn = model->columnCount();
0078             QList<int> visibleColumns;
0079 
0080             if (!configGroupName.isEmpty()) {
0081                 // restore the headers when we have a group name
0082                 const auto grp = KSharedConfig::openConfig()->group(configGroupName);
0083                 const auto columnNames = grp.readEntry("HeaderState", QByteArray());
0084                 headerView->restoreState(columnNames);
0085 
0086                 visibleColumns = grp.readEntry<int>("ColumnsSelection", QList<int>());
0087                 // add the storage offset during loading operation
0088                 for (auto &column : visibleColumns) {
0089                     if (applyStorageOffsetColumns.contains(column + storageOffset)) {
0090                         column += storageOffset;
0091                     }
0092                 }
0093             }
0094 
0095             if (visibleColumns.isEmpty()) {
0096                 // in case no column was marked as visible so far, all should
0097                 for (int col = 0; col < maxColumn; ++col) {
0098                     visibleColumns += col;
0099                 }
0100             }
0101 
0102             // now setup the initial visibility of the columns
0103             for (int col = 0; col < maxColumn; ++col) {
0104                 if (alwaysHiddenColumns.contains(col)) {
0105                     setColumnHidden(col, true);
0106                 } else if (alwaysVisibleColumns.contains(col) || visibleColumns.contains(col)) {
0107                     setColumnHidden(col, false);
0108                 } else {
0109                     setColumnHidden(col, true);
0110                 }
0111             }
0112 
0113             // allow context menu to be opened on tree header for columns selection
0114             headerView->setContextMenuPolicy(Qt::CustomContextMenu);
0115 
0116             if (!isInit) {
0117                 q->connect(headerView, &QWidget::customContextMenuRequested, q, &ColumnSelector::slotColumnsMenu);
0118                 q->connect(headerView, &QHeaderView::sectionResized, q, &ColumnSelector::slotUpdateHeaderState);
0119                 q->connect(headerView, &QHeaderView::sectionMoved, q, &ColumnSelector::slotUpdateHeaderState);
0120                 isInit = true;
0121             }
0122 
0123         } else if (!(treeView || tableView)) {
0124             qDebug() << "WARNING: You must not create a ColumnSelector without a view";
0125         }
0126     }
0127 
0128     ColumnSelector*       q_ptr;
0129 
0130     QTreeView*            treeView;
0131     QTableView*           tableView;
0132     QHeaderView*          headerView;
0133     QAbstractItemModel*   model;
0134 
0135     QVector<int>          selectableColumns;
0136     QVector<int>          alwaysHiddenColumns;
0137     QVector<int>          alwaysVisibleColumns;
0138     QVector<int>          applyStorageOffsetColumns;
0139     QString               configGroupName;
0140 
0141     int                   storageOffset;
0142     bool                  isInit;
0143     bool columnSelectionEnabled;
0144 };
0145 
0146 
0147 ColumnSelector::ColumnSelector(QTableView* view, const QString& configGroupName, int offset, const QVector<int>& columns)
0148     : d_ptr(new ColumnSelectorPrivate(this))
0149 {
0150     Q_D(ColumnSelector);
0151     d->tableView = view;
0152     d->headerView = view->horizontalHeader();
0153     d->model = view->model();
0154     d->storageOffset = offset;
0155     d->applyStorageOffsetColumns = columns;
0156     d->init(configGroupName);
0157 }
0158 
0159 ColumnSelector::ColumnSelector(QTreeView* view, const QString& configGroupName, int offset, const QVector<int>& columns)
0160     : d_ptr(new ColumnSelectorPrivate(this))
0161 {
0162     Q_D(ColumnSelector);
0163     d->treeView = view;
0164     d->headerView = view->header();
0165     d->model = view->model();
0166     d->storageOffset = offset;
0167     d->applyStorageOffsetColumns = columns;
0168     d->init(configGroupName);
0169 }
0170 
0171 ColumnSelector::~ColumnSelector()
0172 {
0173     Q_D(ColumnSelector);
0174     delete d;
0175 }
0176 
0177 void ColumnSelector::slotUpdateHeaderState()
0178 {
0179     Q_D(ColumnSelector);
0180     if (!d->configGroupName.isEmpty()) {
0181         auto grp = KSharedConfig::openConfig()->group(d->configGroupName);
0182         grp.writeEntry("HeaderState", d->headerView->saveState());
0183         grp.sync();
0184     }
0185 }
0186 
0187 void ColumnSelector::slotColumnsMenu(const QPoint)
0188 {
0189     Q_D(ColumnSelector);
0190 
0191     if (!d->columnSelectionEnabled) {
0192         return;
0193     }
0194 
0195     // create actions menu
0196     QList<QAction *> actions;
0197     const auto maxColumn = d->headerView->count();
0198 
0199     for (int col = 0; col < maxColumn; ++col) {
0200         if (d->alwaysHiddenColumns.contains(col))
0201             continue;
0202         if (d->alwaysVisibleColumns.contains(col))
0203             continue;
0204         if (!d->selectableColumns.isEmpty()) {
0205             if (!d->selectableColumns.contains(col)) {
0206                 continue;
0207             }
0208         }
0209         auto a = new QAction(nullptr);
0210         a->setObjectName(QString::number(col));
0211         a->setText(d->model->headerData(col, Qt::Horizontal, eMyMoney::Model::LongDisplayRole).toString());
0212         a->setCheckable(true);
0213         a->setChecked(!d->isColumnHidden(col));
0214         actions.append(a);
0215     }
0216 
0217     // if there are any actions present menu
0218     if (!actions.isEmpty()) {
0219         QMenu menu(i18n("Displayed columns"));
0220         menu.addActions(actions);
0221 
0222         // execute menu and get result
0223         const auto retAction = menu.exec(QCursor::pos());
0224         if (retAction) {
0225             d->setColumnHidden(retAction->objectName().toInt(), !retAction->isChecked());
0226 
0227             QList<int> visibleColumns;
0228 
0229             for (int col = 0; col < maxColumn; ++col) {
0230                 if (d->alwaysHiddenColumns.contains(col))
0231                     continue;
0232                 // in case a column is always visible, we
0233                 // add it to the list for backward compatibility
0234                 if (!d->alwaysVisibleColumns.contains(col)) {
0235                     if (!d->selectableColumns.isEmpty() && !d->selectableColumns.contains(col))
0236                         continue;
0237                 }
0238                 if (!d->isColumnHidden(col)) {
0239                     visibleColumns.append(col);
0240                 }
0241             }
0242             if (!d->configGroupName.isEmpty()) {
0243                 auto grp = KSharedConfig::openConfig()->group(d->configGroupName);
0244                 // subtract offset during storage operation
0245                 for (auto &column : visibleColumns) {
0246                     if (d->applyStorageOffsetColumns.contains(column)) {
0247                         column -= d->storageOffset;
0248                     }
0249                 }
0250                 grp.writeEntry("ColumnsSelection", visibleColumns);
0251             }
0252 
0253             // do this as last statement as it contains the sync of the grp
0254             slotUpdateHeaderState();
0255             Q_EMIT columnsChanged();
0256         }
0257     }
0258 }
0259 
0260 void ColumnSelector::setAlwaysHidden(QVector<int> columns)
0261 {
0262     Q_D(ColumnSelector);
0263 
0264     d->alwaysHiddenColumns = columns;
0265     for (int i = 0; i < columns.count(); ++i) {
0266         auto col = columns.at(i);
0267         d->setColumnHidden(col, true);
0268         d->alwaysVisibleColumns.removeAll(col);
0269     }
0270 }
0271 
0272 void ColumnSelector::setAlwaysVisible(QVector<int> columns)
0273 {
0274     Q_D(ColumnSelector);
0275 
0276     d->alwaysVisibleColumns = columns;
0277     for (int i = 0; i < columns.count(); ++i) {
0278         auto col = columns.at(i);
0279         d->setColumnHidden(col, false);
0280         d->alwaysHiddenColumns.removeAll(col);
0281     }
0282 }
0283 
0284 void ColumnSelector::setSelectable(QVector<int> columns)
0285 {
0286     Q_D(ColumnSelector);
0287 
0288     d->selectableColumns = columns;
0289 }
0290 
0291 void ColumnSelector::setModel(QAbstractItemModel* model)
0292 {
0293     Q_D(ColumnSelector);
0294     d->model = model;
0295     d->init(d->configGroupName);
0296 }
0297 
0298 QVector<int> ColumnSelector::columns() const
0299 {
0300     Q_D(const ColumnSelector);
0301 
0302     QVector<int>    columns;
0303     const auto maxColumn = d->headerView->count();
0304 
0305     for (int col = 0; col < maxColumn; ++col) {
0306         columns.append(col);
0307     }
0308     return columns;
0309 }
0310 
0311 const QString& ColumnSelector::configGroupName() const
0312 {
0313     Q_D(const ColumnSelector);
0314     return d->configGroupName;
0315 }
0316 
0317 void ColumnSelector::setColumnSelectionEnabled()
0318 {
0319     Q_D(ColumnSelector);
0320     d->columnSelectionEnabled = true;
0321 }
0322 
0323 void ColumnSelector::setColumnSelectionDisabled()
0324 {
0325     Q_D(ColumnSelector);
0326     d->columnSelectionEnabled = false;
0327 }