File indexing completed on 2025-01-05 04:55:51
0001 /* 0002 ui/treewidget.cpp 0003 0004 This file is part of libkleopatra 0005 SPDX-FileCopyrightText: 2022 g10 Code GmbH 0006 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include <config-libkleo.h> 0012 0013 #include "treewidget.h" 0014 0015 #include <KConfigGroup> 0016 #include <KLocalizedString> 0017 #include <KSharedConfig> 0018 0019 #include <QContextMenuEvent> 0020 #include <QHeaderView> 0021 #include <QMenu> 0022 0023 using namespace Kleo; 0024 0025 class TreeWidget::Private 0026 { 0027 TreeWidget *q; 0028 0029 public: 0030 QMenu *mHeaderPopup = nullptr; 0031 QList<QAction *> mColumnActions; 0032 QString mStateGroupName; 0033 0034 Private(TreeWidget *qq) 0035 : q(qq) 0036 { 0037 } 0038 0039 ~Private() 0040 { 0041 saveColumnLayout(); 0042 } 0043 void saveColumnLayout(); 0044 }; 0045 0046 TreeWidget::TreeWidget(QWidget *parent) 0047 : QTreeWidget::QTreeWidget(parent) 0048 , d{new Private(this)} 0049 { 0050 header()->installEventFilter(this); 0051 } 0052 0053 TreeWidget::~TreeWidget() = default; 0054 0055 void TreeWidget::Private::saveColumnLayout() 0056 { 0057 if (mStateGroupName.isEmpty()) { 0058 return; 0059 } 0060 auto config = KConfigGroup(KSharedConfig::openStateConfig(), mStateGroupName); 0061 auto header = q->header(); 0062 0063 QVariantList columnVisibility; 0064 QVariantList columnOrder; 0065 QVariantList columnWidths; 0066 const int headerCount = header->count(); 0067 columnVisibility.reserve(headerCount); 0068 columnWidths.reserve(headerCount); 0069 columnOrder.reserve(headerCount); 0070 for (int i = 0; i < headerCount; ++i) { 0071 columnVisibility << QVariant(!q->isColumnHidden(i)); 0072 columnWidths << QVariant(header->sectionSize(i)); 0073 columnOrder << QVariant(header->visualIndex(i)); 0074 } 0075 0076 config.writeEntry("ColumnVisibility", columnVisibility); 0077 config.writeEntry("ColumnOrder", columnOrder); 0078 config.writeEntry("ColumnWidths", columnWidths); 0079 0080 config.writeEntry("SortAscending", (int)header->sortIndicatorOrder()); 0081 if (header->isSortIndicatorShown()) { 0082 config.writeEntry("SortColumn", header->sortIndicatorSection()); 0083 } else { 0084 config.writeEntry("SortColumn", -1); 0085 } 0086 } 0087 0088 bool TreeWidget::restoreColumnLayout(const QString &stateGroupName) 0089 { 0090 if (stateGroupName.isEmpty()) { 0091 return false; 0092 } 0093 d->mStateGroupName = stateGroupName; 0094 auto config = KConfigGroup(KSharedConfig::openStateConfig(), d->mStateGroupName); 0095 auto header = this->header(); 0096 0097 QVariantList columnVisibility = config.readEntry("ColumnVisibility", QVariantList()); 0098 QVariantList columnOrder = config.readEntry("ColumnOrder", QVariantList()); 0099 QVariantList columnWidths = config.readEntry("ColumnWidths", QVariantList()); 0100 0101 if (!columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty()) { 0102 for (int i = 0; i < header->count(); ++i) { 0103 if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) { 0104 // An additional column that was not around last time we saved. 0105 // We default to hidden. 0106 hideColumn(i); 0107 continue; 0108 } 0109 bool visible = columnVisibility[i].toBool(); 0110 int width = columnWidths[i].toInt(); 0111 int order = columnOrder[i].toInt(); 0112 0113 header->resizeSection(i, width ? width : 100); 0114 header->moveSection(header->visualIndex(i), order); 0115 0116 if (!visible) { 0117 hideColumn(i); 0118 } 0119 } 0120 } 0121 0122 int sortOrder = config.readEntry("SortAscending", (int)Qt::AscendingOrder); 0123 int sortColumn = config.readEntry("SortColumn", 0); 0124 if (sortColumn >= 0) { 0125 sortByColumn(sortColumn, (Qt::SortOrder)sortOrder); 0126 } 0127 return !columnVisibility.isEmpty() && !columnOrder.isEmpty() && !columnWidths.isEmpty(); 0128 } 0129 0130 bool TreeWidget::eventFilter(QObject *watched, QEvent *event) 0131 { 0132 Q_UNUSED(watched) 0133 if (event->type() == QEvent::ContextMenu) { 0134 auto e = static_cast<QContextMenuEvent *>(event); 0135 0136 if (!d->mHeaderPopup) { 0137 d->mHeaderPopup = new QMenu(this); 0138 d->mHeaderPopup->setTitle(i18nc("@title:menu", "View Columns")); 0139 for (int i = 0; i < model()->columnCount(); ++i) { 0140 QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString()); 0141 tmp->setData(QVariant(i)); 0142 tmp->setCheckable(true); 0143 d->mColumnActions << tmp; 0144 } 0145 0146 connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) { 0147 const int col = action->data().toInt(); 0148 if (action->isChecked()) { 0149 showColumn(col); 0150 } else { 0151 hideColumn(col); 0152 } 0153 0154 if (action->isChecked()) { 0155 Q_EMIT columnEnabled(col); 0156 } else { 0157 Q_EMIT columnDisabled(col); 0158 } 0159 }); 0160 } 0161 0162 for (QAction *action : std::as_const(d->mColumnActions)) { 0163 const int column = action->data().toInt(); 0164 action->setChecked(!isColumnHidden(column)); 0165 } 0166 0167 d->mHeaderPopup->popup(mapToGlobal(e->pos())); 0168 return true; 0169 } 0170 0171 return false; 0172 } 0173 0174 QModelIndex TreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) 0175 { 0176 // make column by column keyboard navigation with Left/Right possible by switching 0177 // the selection behavior to SelectItems before calling the parent class's moveCursor, 0178 // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows; 0179 // moreover, temporarily disable exanding of items to prevent expanding/collapsing 0180 // on MoveLeft/MoveRight 0181 if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) { 0182 return QTreeWidget::moveCursor(cursorAction, modifiers); 0183 } 0184 0185 const auto savedSelectionBehavior = selectionBehavior(); 0186 setSelectionBehavior(SelectItems); 0187 const auto savedItemsExpandable = itemsExpandable(); 0188 setItemsExpandable(false); 0189 0190 const auto result = QTreeWidget::moveCursor(cursorAction, modifiers); 0191 0192 setItemsExpandable(savedItemsExpandable); 0193 setSelectionBehavior(savedSelectionBehavior); 0194 0195 return result; 0196 } 0197 0198 #include "moc_treewidget.cpp"