File indexing completed on 2024-04-28 16:32:01
0001 /*************************************************************************** 0002 Copyright (C) 2005-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "filterview.h" 0026 #include "controller.h" 0027 #include "entry.h" 0028 #include "collection.h" 0029 #include "tellico_kernel.h" 0030 #include "../tellico_debug.h" 0031 #include "models/filtermodel.h" 0032 #include "models/entrysortmodel.h" 0033 #include "models/models.h" 0034 #include "gui/countdelegate.h" 0035 0036 #include <KLocalizedString> 0037 0038 #include <QMenu> 0039 #include <QIcon> 0040 #include <QHeaderView> 0041 #include <QContextMenuEvent> 0042 0043 using namespace Tellico; 0044 using Tellico::FilterView; 0045 0046 FilterView::FilterView(QWidget* parent_) 0047 : GUI::TreeView(parent_), m_notSortedYet(true) { 0048 header()->setSectionResizeMode(QHeaderView::Stretch); 0049 setHeaderHidden(false); 0050 setSelectionMode(QAbstractItemView::ExtendedSelection); 0051 0052 connect(this, &QAbstractItemView::doubleClicked, 0053 this, &FilterView::slotDoubleClicked); 0054 0055 connect(header(), &QHeaderView::sortIndicatorChanged, 0056 this, &FilterView::slotSortingChanged); 0057 0058 FilterModel* filterModel = new FilterModel(this); 0059 EntrySortModel* sortModel = new EntrySortModel(this); 0060 sortModel->setSourceModel(filterModel); 0061 setModel(sortModel); 0062 setItemDelegate(new GUI::CountDelegate(this)); 0063 } 0064 0065 Tellico::FilterModel* FilterView::sourceModel() const { 0066 return static_cast<FilterModel*>(sortModel()->sourceModel()); 0067 } 0068 0069 void FilterView::addCollection(Tellico::Data::CollPtr coll_) { 0070 m_coll = coll_; 0071 sourceModel()->clear(); 0072 sourceModel()->addFilters(m_coll->filters()); 0073 } 0074 0075 void FilterView::addEntries(Tellico::Data::EntryList entries_) { 0076 invalidate(entries_); 0077 } 0078 0079 void FilterView::modifyEntries(Tellico::Data::EntryList entries_) { 0080 invalidate(entries_); 0081 } 0082 0083 void FilterView::removeEntries(Tellico::Data::EntryList entries_) { 0084 invalidate(entries_); 0085 } 0086 0087 void FilterView::slotReset() { 0088 sourceModel()->clear(); 0089 } 0090 0091 void FilterView::addFilter(Tellico::FilterPtr filter_) { 0092 Q_ASSERT(filter_); 0093 sourceModel()->addFilter(filter_); 0094 } 0095 0096 void FilterView::slotModifyFilter() { 0097 QModelIndex index = currentIndex(); 0098 // parent means a top-level item 0099 if(!index.isValid() || index.parent().isValid()) { 0100 return; 0101 } 0102 0103 QModelIndex realIndex = sortModel()->mapToSource(index); 0104 Kernel::self()->modifyFilter(sourceModel()->filter(realIndex)); 0105 } 0106 0107 void FilterView::slotDeleteFilter() { 0108 QModelIndex index = currentIndex(); 0109 // parent means a top-level item 0110 if(!index.isValid() || index.parent().isValid()) { 0111 return; 0112 } 0113 0114 QModelIndex realIndex = sortModel()->mapToSource(index); 0115 Kernel::self()->removeFilter(sourceModel()->filter(realIndex)); 0116 } 0117 0118 void FilterView::removeFilter(Tellico::FilterPtr filter_) { 0119 Q_ASSERT(filter_); 0120 sourceModel()->removeFilter(filter_); 0121 } 0122 0123 void FilterView::contextMenuEvent(QContextMenuEvent* event_) { 0124 QModelIndex index = indexAt(event_->pos()); 0125 if(!index.isValid()) { 0126 return; 0127 } 0128 0129 QMenu menu(this); 0130 // no parent means it's a top-level item 0131 if(!index.parent().isValid()) { 0132 menu.addAction(QIcon::fromTheme(QStringLiteral("view-filter")), 0133 i18n("Modify Filter"), this, &FilterView::slotModifyFilter); 0134 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), 0135 i18n("Delete Filter"), this, &FilterView::slotDeleteFilter); 0136 } else { 0137 Controller::self()->plugEntryActions(&menu); 0138 } 0139 menu.exec(event_->globalPos()); 0140 } 0141 0142 void FilterView::selectionChanged(const QItemSelection& selected_, const QItemSelection& deselected_) { 0143 // DEBUG_BLOCK; 0144 // in Controller::slotUpdateFilter(), filterView->clearSelection() gets called when the filter is empty 0145 // which happens when only entries are selected here 0146 GUI::TreeView::selectionChanged(selected_, deselected_); 0147 FilterPtr filter; 0148 foreach(const QModelIndex& index, selectionModel()->selectedIndexes()) { 0149 QModelIndex realIndex = sortModel()->mapToSource(index); 0150 Data::EntryPtr entry = sourceModel()->entry(realIndex); 0151 if(!entry && !filter) { 0152 filter = sourceModel()->filter(realIndex); 0153 break; 0154 } 0155 } 0156 // emitting signal with a null filter is ok and accounts for clearing the selection 0157 emit signalUpdateFilter(filter); 0158 } 0159 0160 void FilterView::slotDoubleClicked(const QModelIndex& index_) { 0161 QModelIndex realIndex = sortModel()->mapToSource(index_); 0162 Data::EntryPtr entry = sourceModel()->entry(realIndex); 0163 if(entry) { 0164 Controller::self()->editEntry(entry); 0165 } else { 0166 FilterPtr filter = sourceModel()->filter(realIndex); 0167 if(filter) { 0168 Kernel::self()->modifyFilter(filter); 0169 } 0170 } 0171 } 0172 0173 // this gets called when header() is clicked, so cycle through 0174 void FilterView::slotSortingChanged(int col_, Qt::SortOrder order_) { 0175 Q_UNUSED(col_); 0176 if(order_ == Qt::AscendingOrder && !m_notSortedYet) { // cycle through after ascending 0177 if(sortModel()->sortRole() == RowCountRole) { 0178 sortModel()->setSortRole(Qt::DisplayRole); 0179 } else { 0180 sortModel()->setSortRole(RowCountRole); 0181 } 0182 } 0183 0184 updateHeader(); 0185 m_notSortedYet = false; 0186 } 0187 0188 void FilterView::updateHeader() { 0189 if(sortModel()->sortRole() == Qt::DisplayRole) { 0190 model()->setHeaderData(0, Qt::Horizontal, i18n("Filter")); 0191 } else { 0192 model()->setHeaderData(0, Qt::Horizontal, i18n("Filter (Sort by Count)")); 0193 } 0194 } 0195 0196 void FilterView::invalidate(Tellico::Data::EntryList entries_) { 0197 const int rows = model()->rowCount(); 0198 for(int row = 0; row < rows; ++row) { 0199 QModelIndex index = sourceModel()->index(row, 0); 0200 FilterPtr filter = sourceModel()->filter(index); 0201 if(!filter) { 0202 continue; 0203 } 0204 // two cases: if the filter used to match the entry and no longer does, then check the children indexes 0205 // if the filter matches now, check the actual match 0206 foreach(Data::EntryPtr entry, entries_) { 0207 if(sourceModel()->indexContainsEntry(index, entry) || filter->matches(entry)) { 0208 sourceModel()->invalidate(index); 0209 break; 0210 } 0211 } 0212 } 0213 }