File indexing completed on 2024-06-16 04:56:15
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 view/keytreeview.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include <config-kleopatra.h> 0011 0012 #include "keytreeview.h" 0013 #include "searchbar.h" 0014 0015 #include <Libkleo/KeyList> 0016 #include <Libkleo/KeyListModel> 0017 #include <Libkleo/KeyListSortFilterProxyModel> 0018 #include <Libkleo/KeyRearrangeColumnsProxyModel> 0019 #include <Libkleo/Predicates> 0020 #include <Libkleo/TreeView> 0021 0022 #include "utils/headerview.h" 0023 #include "utils/tags.h" 0024 0025 #include <Libkleo/KeyCache> 0026 #include <Libkleo/KeyFilter> 0027 #include <Libkleo/Stl_Util> 0028 0029 #include <gpgme++/key.h> 0030 0031 #include "kleopatra_debug.h" 0032 #include <QAction> 0033 #include <QContextMenuEvent> 0034 #include <QEvent> 0035 #include <QHeaderView> 0036 #include <QItemSelection> 0037 #include <QItemSelectionModel> 0038 #include <QLayout> 0039 #include <QList> 0040 #include <QMenu> 0041 #include <QTimer> 0042 0043 #include <KLocalizedString> 0044 #include <KSharedConfig> 0045 0046 static int tagsColumn; 0047 0048 using namespace Kleo; 0049 using namespace GpgME; 0050 0051 Q_DECLARE_METATYPE(GpgME::Key) 0052 0053 namespace 0054 { 0055 0056 class TreeViewInternal : public Kleo::TreeView 0057 { 0058 public: 0059 explicit TreeViewInternal(QWidget *parent = nullptr) 0060 : Kleo::TreeView{parent} 0061 { 0062 connect(this, &TreeView::columnEnabled, this, [this](int column) { 0063 if (column == tagsColumn) { 0064 Tags::enableTags(); 0065 } 0066 auto tv = qobject_cast<KeyTreeView *>(this->parent()); 0067 if (tv) { 0068 tv->resizeColumns(); 0069 } 0070 }); 0071 connect(this, &TreeView::columnDisabled, this, [this]() { 0072 auto tv = qobject_cast<KeyTreeView *>(this->parent()); 0073 if (tv) { 0074 tv->resizeColumns(); 0075 } 0076 }); 0077 } 0078 0079 QSize minimumSizeHint() const override 0080 { 0081 const QSize min = QTreeView::minimumSizeHint(); 0082 return QSize(min.width(), min.height() + 5 * fontMetrics().height()); 0083 } 0084 0085 protected: 0086 void focusInEvent(QFocusEvent *event) override 0087 { 0088 QTreeView::focusInEvent(event); 0089 // queue the invokation, so that it happens after the widget itself got focus 0090 QMetaObject::invokeMethod(this, &TreeViewInternal::forceAccessibleFocusEventForCurrentItem, Qt::QueuedConnection); 0091 } 0092 0093 private: 0094 void forceAccessibleFocusEventForCurrentItem() 0095 { 0096 // force Qt to send a focus event for the current item to accessibility 0097 // tools; otherwise, the user has no idea which item is selected when the 0098 // list gets keyboard input focus 0099 const auto current = currentIndex(); 0100 setCurrentIndex({}); 0101 setCurrentIndex(current); 0102 } 0103 0104 private: 0105 QMenu *mHeaderPopup = nullptr; 0106 0107 QList<QAction *> mColumnActions; 0108 }; 0109 0110 const KeyListModelInterface *keyListModel(const QTreeView &view) 0111 { 0112 const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(view.model()); 0113 Q_ASSERT(klmi); 0114 return klmi; 0115 } 0116 0117 } // anon namespace 0118 0119 KeyTreeView::KeyTreeView(QWidget *parent) 0120 : QWidget(parent) 0121 , m_proxy(new KeyListSortFilterProxyModel(this)) 0122 , m_additionalProxy(nullptr) 0123 , m_view(new TreeViewInternal(this)) 0124 , m_flatModel(nullptr) 0125 , m_hierarchicalModel(nullptr) 0126 , m_stringFilter() 0127 , m_keyFilter() 0128 , m_isHierarchical(true) 0129 { 0130 init(); 0131 } 0132 0133 KeyTreeView::KeyTreeView(const KeyTreeView &other) 0134 : QWidget(nullptr) 0135 , m_proxy(new KeyListSortFilterProxyModel(this)) 0136 , m_additionalProxy(other.m_additionalProxy ? other.m_additionalProxy->clone() : nullptr) 0137 , m_view(new TreeViewInternal(this)) 0138 , m_flatModel(other.m_flatModel) 0139 , m_hierarchicalModel(other.m_hierarchicalModel) 0140 , m_stringFilter(other.m_stringFilter) 0141 , m_keyFilter(other.m_keyFilter) 0142 , m_group(other.m_group) 0143 , m_isHierarchical(other.m_isHierarchical) 0144 { 0145 init(); 0146 setColumnSizes(other.columnSizes()); 0147 setSortColumn(other.sortColumn(), other.sortOrder()); 0148 } 0149 0150 KeyTreeView::KeyTreeView(const QString &text, 0151 const std::shared_ptr<KeyFilter> &kf, 0152 AbstractKeyListSortFilterProxyModel *proxy, 0153 QWidget *parent, 0154 const KConfigGroup &group) 0155 : QWidget(parent) 0156 , m_proxy(new KeyListSortFilterProxyModel(this)) 0157 , m_additionalProxy(proxy) 0158 , m_view(new TreeViewInternal(this)) 0159 , m_flatModel(nullptr) 0160 , m_hierarchicalModel(nullptr) 0161 , m_stringFilter(text) 0162 , m_keyFilter(kf) 0163 , m_group(group) 0164 , m_isHierarchical(true) 0165 , m_onceResized(false) 0166 { 0167 init(); 0168 } 0169 0170 void KeyTreeView::setColumnSizes(const std::vector<int> &sizes) 0171 { 0172 if (sizes.empty()) { 0173 return; 0174 } 0175 Q_ASSERT(m_view); 0176 Q_ASSERT(m_view->header()); 0177 Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header())); 0178 if (auto const hv = static_cast<HeaderView *>(m_view->header())) { 0179 hv->setSectionSizes(sizes); 0180 } 0181 } 0182 0183 void KeyTreeView::setSortColumn(int sortColumn, Qt::SortOrder sortOrder) 0184 { 0185 Q_ASSERT(m_view); 0186 m_view->sortByColumn(sortColumn, sortOrder); 0187 } 0188 0189 int KeyTreeView::sortColumn() const 0190 { 0191 Q_ASSERT(m_view); 0192 Q_ASSERT(m_view->header()); 0193 return m_view->header()->sortIndicatorSection(); 0194 } 0195 0196 Qt::SortOrder KeyTreeView::sortOrder() const 0197 { 0198 Q_ASSERT(m_view); 0199 Q_ASSERT(m_view->header()); 0200 return m_view->header()->sortIndicatorOrder(); 0201 } 0202 0203 std::vector<int> KeyTreeView::columnSizes() const 0204 { 0205 Q_ASSERT(m_view); 0206 Q_ASSERT(m_view->header()); 0207 Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header())); 0208 if (auto const hv = static_cast<HeaderView *>(m_view->header())) { 0209 return hv->sectionSizes(); 0210 } else { 0211 return std::vector<int>(); 0212 } 0213 } 0214 0215 void KeyTreeView::restoreLayout(const KConfigGroup &group) 0216 { 0217 if (!group.isValid() || !m_view->restoreColumnLayout(group.name())) { 0218 // if config is empty then use default settings 0219 // The numbers have to be in line with the order in 0220 // setsSourceColumns above 0221 m_view->hideColumn(5); 0222 0223 for (int i = 7; i < m_view->model()->columnCount(); ++i) { 0224 m_view->hideColumn(i); 0225 } 0226 if (KeyCache::instance()->initialized()) { 0227 QTimer::singleShot(0, this, &KeyTreeView::resizeColumns); 0228 } 0229 } else { 0230 m_onceResized = true; 0231 } 0232 if (!m_view->isColumnHidden(tagsColumn)) { 0233 Tags::enableTags(); 0234 } 0235 } 0236 0237 void KeyTreeView::init() 0238 { 0239 KDAB_SET_OBJECT_NAME(m_proxy); 0240 KDAB_SET_OBJECT_NAME(m_view); 0241 0242 if (m_group.isValid()) { 0243 // Reopen as non const 0244 KConfig *conf = m_group.config(); 0245 m_group = conf->group(m_group.name()); 0246 } 0247 0248 if (m_additionalProxy && m_additionalProxy->objectName().isEmpty()) { 0249 KDAB_SET_OBJECT_NAME(m_additionalProxy); 0250 } 0251 QLayout *layout = new QVBoxLayout(this); 0252 KDAB_SET_OBJECT_NAME(layout); 0253 layout->setContentsMargins(0, 0, 0, 0); 0254 layout->addWidget(m_view); 0255 0256 auto headerView = new HeaderView(Qt::Horizontal); 0257 KDAB_SET_OBJECT_NAME(headerView); 0258 headerView->installEventFilter(m_view); 0259 headerView->setSectionsMovable(true); 0260 m_view->setHeader(headerView); 0261 0262 m_view->setSelectionBehavior(QAbstractItemView::SelectRows); 0263 m_view->setSelectionMode(QAbstractItemView::ExtendedSelection); 0264 m_view->setAllColumnsShowFocus(false); 0265 m_view->setSortingEnabled(true); 0266 m_view->setAccessibleName(i18n("Certificates")); 0267 m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates")); 0268 // we show details on double-click 0269 m_view->setExpandsOnDoubleClick(false); 0270 0271 if (model()) { 0272 if (m_additionalProxy) { 0273 m_additionalProxy->setSourceModel(model()); 0274 } else { 0275 m_proxy->setSourceModel(model()); 0276 } 0277 } 0278 if (m_additionalProxy) { 0279 m_proxy->setSourceModel(m_additionalProxy); 0280 if (!m_additionalProxy->parent()) { 0281 m_additionalProxy->setParent(this); 0282 } 0283 } 0284 0285 m_proxy->setFilterRegularExpression(QRegularExpression::escape(m_stringFilter)); 0286 m_proxy->setKeyFilter(m_keyFilter); 0287 m_proxy->setSortCaseSensitivity(Qt::CaseInsensitive); 0288 0289 auto rearangingModel = new KeyRearrangeColumnsProxyModel(this); 0290 rearangingModel->setSourceModel(m_proxy); 0291 QList<int> columns = { 0292 KeyList::PrettyName, 0293 KeyList::PrettyEMail, 0294 KeyList::Validity, 0295 KeyList::ValidFrom, 0296 KeyList::ValidUntil, 0297 KeyList::TechnicalDetails, 0298 KeyList::KeyID, 0299 KeyList::Fingerprint, 0300 KeyList::OwnerTrust, 0301 KeyList::Origin, 0302 KeyList::LastUpdate, 0303 KeyList::Issuer, 0304 KeyList::SerialNumber, 0305 KeyList::Remarks, 0306 KeyList::Algorithm, 0307 KeyList::Keygrip, 0308 }; 0309 tagsColumn = columns.indexOf(KeyList::Remarks); 0310 rearangingModel->setSourceColumns(columns); 0311 m_view->setModel(rearangingModel); 0312 0313 /* Handle expansion state */ 0314 if (m_group.isValid()) { 0315 m_expandedKeys = m_group.readEntry("Expanded", QStringList()); 0316 } 0317 0318 connect(m_view, &QTreeView::expanded, this, [this](const QModelIndex &index) { 0319 if (!index.isValid()) { 0320 return; 0321 } 0322 const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>(); 0323 if (key.isNull()) { 0324 return; 0325 } 0326 const auto fpr = QString::fromLatin1(key.primaryFingerprint()); 0327 0328 if (m_expandedKeys.contains(fpr)) { 0329 return; 0330 } 0331 m_expandedKeys << fpr; 0332 if (m_group.isValid()) { 0333 m_group.writeEntry("Expanded", m_expandedKeys); 0334 } 0335 }); 0336 0337 connect(m_view, &QTreeView::collapsed, this, [this](const QModelIndex &index) { 0338 if (!index.isValid()) { 0339 return; 0340 } 0341 const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>(); 0342 if (key.isNull()) { 0343 return; 0344 } 0345 m_expandedKeys.removeAll(QString::fromLatin1(key.primaryFingerprint())); 0346 if (m_group.isValid()) { 0347 m_group.writeEntry("Expanded", m_expandedKeys); 0348 } 0349 }); 0350 0351 updateModelConnections(nullptr, model()); 0352 0353 resizeColumns(); 0354 0355 restoreLayout(m_group); 0356 } 0357 0358 void KeyTreeView::restoreExpandState() 0359 { 0360 if (!KeyCache::instance()->initialized()) { 0361 qCWarning(KLEOPATRA_LOG) << "Restore expand state before keycache available. Aborting."; 0362 return; 0363 } 0364 for (const auto &fpr : std::as_const(m_expandedKeys)) { 0365 const KeyListModelInterface *const km = keyListModel(*m_view); 0366 if (!km) { 0367 qCWarning(KLEOPATRA_LOG) << "invalid model"; 0368 return; 0369 } 0370 const auto key = KeyCache::instance()->findByFingerprint(fpr.toLatin1().constData()); 0371 if (key.isNull()) { 0372 qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in cache"; 0373 m_expandedKeys.removeAll(fpr); 0374 return; 0375 } 0376 const auto idx = km->index(key); 0377 if (!idx.isValid()) { 0378 qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in model"; 0379 m_expandedKeys.removeAll(fpr); 0380 return; 0381 } 0382 m_view->expand(idx); 0383 } 0384 } 0385 0386 void KeyTreeView::setUpTagKeys() 0387 { 0388 const auto tagKeys = Tags::tagKeys(); 0389 if (m_hierarchicalModel) { 0390 m_hierarchicalModel->setRemarkKeys(tagKeys); 0391 } 0392 if (m_flatModel) { 0393 m_flatModel->setRemarkKeys(tagKeys); 0394 } 0395 } 0396 0397 KeyTreeView::~KeyTreeView() = default; 0398 0399 static QAbstractProxyModel *find_last_proxy(QAbstractProxyModel *pm) 0400 { 0401 Q_ASSERT(pm); 0402 while (auto const sm = qobject_cast<QAbstractProxyModel *>(pm->sourceModel())) { 0403 pm = sm; 0404 } 0405 return pm; 0406 } 0407 0408 void KeyTreeView::updateModelConnections(AbstractKeyListModel *oldModel, AbstractKeyListModel *newModel) 0409 { 0410 if (oldModel == newModel) { 0411 return; 0412 } 0413 if (oldModel) { 0414 disconnect(oldModel, &QAbstractItemModel::modelAboutToBeReset, this, &KeyTreeView::saveStateBeforeModelChange); 0415 disconnect(oldModel, &QAbstractItemModel::modelReset, this, &KeyTreeView::restoreStateAfterModelChange); 0416 disconnect(oldModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &KeyTreeView::saveStateBeforeModelChange); 0417 disconnect(oldModel, &QAbstractItemModel::rowsInserted, this, &KeyTreeView::restoreStateAfterModelChange); 0418 disconnect(oldModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &KeyTreeView::saveStateBeforeModelChange); 0419 disconnect(oldModel, &QAbstractItemModel::rowsRemoved, this, &KeyTreeView::restoreStateAfterModelChange); 0420 } 0421 if (newModel) { 0422 connect(newModel, &QAbstractItemModel::modelAboutToBeReset, this, &KeyTreeView::saveStateBeforeModelChange); 0423 connect(newModel, &QAbstractItemModel::modelReset, this, &KeyTreeView::restoreStateAfterModelChange); 0424 connect(newModel, &QAbstractItemModel::rowsAboutToBeInserted, this, &KeyTreeView::saveStateBeforeModelChange); 0425 connect(newModel, &QAbstractItemModel::rowsInserted, this, &KeyTreeView::restoreStateAfterModelChange); 0426 connect(newModel, &QAbstractItemModel::rowsAboutToBeRemoved, this, &KeyTreeView::saveStateBeforeModelChange); 0427 connect(newModel, &QAbstractItemModel::rowsRemoved, this, &KeyTreeView::restoreStateAfterModelChange); 0428 } 0429 } 0430 0431 void KeyTreeView::setFlatModel(AbstractKeyListModel *model) 0432 { 0433 if (model == m_flatModel) { 0434 return; 0435 } 0436 auto oldModel = m_flatModel; 0437 m_flatModel = model; 0438 if (!m_isHierarchical) 0439 // TODO: this fails when called after setHierarchicalView( false )... 0440 { 0441 find_last_proxy(m_proxy)->setSourceModel(model); 0442 updateModelConnections(oldModel, model); 0443 } 0444 } 0445 0446 void KeyTreeView::setHierarchicalModel(AbstractKeyListModel *model) 0447 { 0448 if (model == m_hierarchicalModel) { 0449 return; 0450 } 0451 auto oldModel = m_hierarchicalModel; 0452 m_hierarchicalModel = model; 0453 if (m_isHierarchical) { 0454 find_last_proxy(m_proxy)->setSourceModel(model); 0455 updateModelConnections(oldModel, model); 0456 m_view->expandAll(); 0457 for (int column = 0; column < m_view->header()->count(); ++column) { 0458 m_view->header()->resizeSection(column, qMax(m_view->header()->sectionSize(column), m_view->header()->sectionSizeHint(column))); 0459 } 0460 } 0461 } 0462 0463 void KeyTreeView::setStringFilter(const QString &filter) 0464 { 0465 if (filter == m_stringFilter) { 0466 return; 0467 } 0468 m_stringFilter = filter; 0469 m_proxy->setFilterRegularExpression(QRegularExpression::escape(filter)); 0470 Q_EMIT stringFilterChanged(filter); 0471 } 0472 0473 void KeyTreeView::setKeyFilter(const std::shared_ptr<KeyFilter> &filter) 0474 { 0475 if (filter == m_keyFilter || (filter && m_keyFilter && filter->id() == m_keyFilter->id())) { 0476 return; 0477 } 0478 m_keyFilter = filter; 0479 m_proxy->setKeyFilter(filter); 0480 Q_EMIT keyFilterChanged(filter); 0481 } 0482 0483 namespace 0484 { 0485 QItemSelection itemSelectionFromKeys(const std::vector<Key> &keys, const QTreeView &view) 0486 { 0487 const QModelIndexList indexes = keyListModel(view)->indexes(keys); 0488 return std::accumulate(indexes.cbegin(), indexes.cend(), QItemSelection(), [](QItemSelection selection, const QModelIndex &index) { 0489 if (index.isValid()) { 0490 selection.merge(QItemSelection(index, index), QItemSelectionModel::Select); 0491 } 0492 return selection; 0493 }); 0494 } 0495 } 0496 0497 void KeyTreeView::selectKeys(const std::vector<Key> &keys) 0498 { 0499 m_view->selectionModel()->select(itemSelectionFromKeys(keys, *m_view), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); 0500 } 0501 0502 std::vector<Key> KeyTreeView::selectedKeys() const 0503 { 0504 return keyListModel(*m_view)->keys(m_view->selectionModel()->selectedRows()); 0505 } 0506 0507 void KeyTreeView::setHierarchicalView(bool on) 0508 { 0509 if (on == m_isHierarchical) { 0510 return; 0511 } 0512 if (on && !hierarchicalModel()) { 0513 qCWarning(KLEOPATRA_LOG) << "hierarchical view requested, but no hierarchical model set"; 0514 return; 0515 } 0516 if (!on && !flatModel()) { 0517 qCWarning(KLEOPATRA_LOG) << "flat view requested, but no flat model set"; 0518 return; 0519 } 0520 const std::vector<Key> selectedKeys = this->selectedKeys(); 0521 const Key currentKey = keyListModel(*m_view)->key(m_view->currentIndex()); 0522 0523 auto oldModel = model(); 0524 m_isHierarchical = on; 0525 find_last_proxy(m_proxy)->setSourceModel(model()); 0526 updateModelConnections(oldModel, model()); 0527 if (on) { 0528 m_view->expandAll(); 0529 } 0530 selectKeys(selectedKeys); 0531 if (!currentKey.isNull()) { 0532 const QModelIndex currentIndex = keyListModel(*m_view)->index(currentKey); 0533 if (currentIndex.isValid()) { 0534 m_view->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate); 0535 m_view->scrollTo(currentIndex); 0536 } 0537 } 0538 m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates")); 0539 Q_EMIT hierarchicalChanged(on); 0540 } 0541 0542 void KeyTreeView::setKeys(const std::vector<Key> &keys) 0543 { 0544 std::vector<Key> sorted = keys; 0545 _detail::sort_by_fpr(sorted); 0546 _detail::remove_duplicates_by_fpr(sorted); 0547 m_keys = sorted; 0548 if (m_flatModel) { 0549 m_flatModel->setKeys(sorted); 0550 } 0551 if (m_hierarchicalModel) { 0552 m_hierarchicalModel->setKeys(sorted); 0553 } 0554 } 0555 0556 void KeyTreeView::addKeysImpl(const std::vector<Key> &keys, bool select) 0557 { 0558 if (keys.empty()) { 0559 return; 0560 } 0561 if (m_keys.empty()) { 0562 setKeys(keys); 0563 return; 0564 } 0565 0566 std::vector<Key> sorted = keys; 0567 _detail::sort_by_fpr(sorted); 0568 _detail::remove_duplicates_by_fpr(sorted); 0569 0570 std::vector<Key> newKeys = _detail::union_by_fpr(sorted, m_keys); 0571 m_keys.swap(newKeys); 0572 0573 if (m_flatModel) { 0574 m_flatModel->addKeys(sorted); 0575 } 0576 if (m_hierarchicalModel) { 0577 m_hierarchicalModel->addKeys(sorted); 0578 } 0579 0580 if (select) { 0581 selectKeys(sorted); 0582 } 0583 } 0584 0585 void KeyTreeView::addKeysSelected(const std::vector<Key> &keys) 0586 { 0587 addKeysImpl(keys, true); 0588 } 0589 0590 void KeyTreeView::addKeysUnselected(const std::vector<Key> &keys) 0591 { 0592 addKeysImpl(keys, false); 0593 } 0594 0595 void KeyTreeView::removeKeys(const std::vector<Key> &keys) 0596 { 0597 if (keys.empty()) { 0598 return; 0599 } 0600 std::vector<Key> sorted = keys; 0601 _detail::sort_by_fpr(sorted); 0602 _detail::remove_duplicates_by_fpr(sorted); 0603 std::vector<Key> newKeys; 0604 newKeys.reserve(m_keys.size()); 0605 std::set_difference(m_keys.begin(), m_keys.end(), sorted.begin(), sorted.end(), std::back_inserter(newKeys), _detail::ByFingerprint<std::less>()); 0606 m_keys.swap(newKeys); 0607 0608 if (m_flatModel) { 0609 std::for_each(sorted.cbegin(), sorted.cend(), [this](const Key &key) { 0610 m_flatModel->removeKey(key); 0611 }); 0612 } 0613 if (m_hierarchicalModel) { 0614 std::for_each(sorted.cbegin(), sorted.cend(), [this](const Key &key) { 0615 m_hierarchicalModel->removeKey(key); 0616 }); 0617 } 0618 } 0619 0620 void KeyTreeView::disconnectSearchBar() 0621 { 0622 for (const auto &connection : m_connections) { 0623 disconnect(connection); 0624 } 0625 m_connections.clear(); 0626 } 0627 0628 bool KeyTreeView::connectSearchBar(const SearchBar *bar) 0629 { 0630 m_connections.reserve(4); 0631 m_connections.push_back(connect(this, &KeyTreeView::stringFilterChanged, bar, &SearchBar::setStringFilter)); 0632 m_connections.push_back(connect(bar, &SearchBar::stringFilterChanged, this, &KeyTreeView::setStringFilter)); 0633 m_connections.push_back(connect(this, &KeyTreeView::keyFilterChanged, bar, &SearchBar::setKeyFilter)); 0634 m_connections.push_back(connect(bar, &SearchBar::keyFilterChanged, this, &KeyTreeView::setKeyFilter)); 0635 0636 return std::all_of(m_connections.cbegin(), m_connections.cend(), [](const QMetaObject::Connection &conn) { 0637 return conn; 0638 }); 0639 } 0640 0641 void KeyTreeView::resizeColumns() 0642 { 0643 m_view->setColumnWidth(KeyList::PrettyName, 260); 0644 m_view->setColumnWidth(KeyList::PrettyEMail, 260); 0645 0646 for (int i = 2; i < m_view->model()->columnCount(); ++i) { 0647 m_view->resizeColumnToContents(i); 0648 } 0649 } 0650 0651 void KeyTreeView::saveStateBeforeModelChange() 0652 { 0653 m_currentKey = keyListModel(*m_view)->key(m_view->currentIndex()); 0654 m_selectedKeys = selectedKeys(); 0655 } 0656 0657 void KeyTreeView::restoreStateAfterModelChange() 0658 { 0659 restoreExpandState(); 0660 0661 selectKeys(m_selectedKeys); 0662 if (!m_currentKey.isNull()) { 0663 const QModelIndex currentIndex = keyListModel(*m_view)->index(m_currentKey); 0664 if (currentIndex.isValid()) { 0665 m_view->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate); 0666 m_view->scrollTo(currentIndex); 0667 } 0668 } 0669 0670 setUpTagKeys(); 0671 if (!m_onceResized) { 0672 m_onceResized = true; 0673 resizeColumns(); 0674 } 0675 } 0676 0677 #include "moc_keytreeview.cpp"