File indexing completed on 2024-04-28 05:08:23
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 "models/filtermodel.h" 0031 #include "models/entrysortmodel.h" 0032 #include "models/models.h" 0033 #include "gui/countdelegate.h" 0034 0035 #include <KLocalizedString> 0036 0037 #include <QMenu> 0038 #include <QIcon> 0039 #include <QHeaderView> 0040 #include <QContextMenuEvent> 0041 0042 using namespace Tellico; 0043 using Tellico::FilterView; 0044 0045 FilterView::FilterView(QWidget* parent_) 0046 : GUI::TreeView(parent_), m_notSortedYet(true) { 0047 header()->setSectionResizeMode(QHeaderView::Stretch); 0048 setHeaderHidden(false); 0049 setSelectionMode(QAbstractItemView::ExtendedSelection); 0050 0051 connect(this, &QAbstractItemView::doubleClicked, 0052 this, &FilterView::slotDoubleClicked); 0053 0054 connect(header(), &QHeaderView::sortIndicatorChanged, 0055 this, &FilterView::slotSortingChanged); 0056 0057 FilterModel* filterModel = new FilterModel(this); 0058 EntrySortModel* sortModel = new EntrySortModel(this); 0059 sortModel->setSourceModel(filterModel); 0060 setModel(sortModel); 0061 setItemDelegate(new GUI::CountDelegate(this)); 0062 } 0063 0064 Tellico::FilterModel* FilterView::sourceModel() const { 0065 return static_cast<FilterModel*>(sortModel()->sourceModel()); 0066 } 0067 0068 void FilterView::addCollection(Tellico::Data::CollPtr coll_) { 0069 m_coll = coll_; 0070 sourceModel()->clear(); 0071 sourceModel()->addFilters(m_coll->filters()); 0072 } 0073 0074 void FilterView::addEntries(Tellico::Data::EntryList entries_) { 0075 invalidate(entries_); 0076 } 0077 0078 void FilterView::modifyEntries(Tellico::Data::EntryList entries_) { 0079 invalidate(entries_); 0080 } 0081 0082 void FilterView::removeEntries(Tellico::Data::EntryList entries_) { 0083 invalidate(entries_); 0084 } 0085 0086 void FilterView::slotReset() { 0087 sourceModel()->clear(); 0088 } 0089 0090 void FilterView::addFilter(Tellico::FilterPtr filter_) { 0091 Q_ASSERT(filter_); 0092 sourceModel()->addFilter(filter_); 0093 } 0094 0095 void FilterView::slotModifyFilter() { 0096 QModelIndex index = currentIndex(); 0097 // parent means a top-level item 0098 if(!index.isValid() || index.parent().isValid()) { 0099 return; 0100 } 0101 0102 QModelIndex realIndex = sortModel()->mapToSource(index); 0103 Kernel::self()->modifyFilter(sourceModel()->filter(realIndex)); 0104 } 0105 0106 void FilterView::slotDeleteFilter() { 0107 QModelIndex index = currentIndex(); 0108 // parent means a top-level item 0109 if(!index.isValid() || index.parent().isValid()) { 0110 return; 0111 } 0112 0113 QModelIndex realIndex = sortModel()->mapToSource(index); 0114 Kernel::self()->removeFilter(sourceModel()->filter(realIndex)); 0115 } 0116 0117 void FilterView::removeFilter(Tellico::FilterPtr filter_) { 0118 Q_ASSERT(filter_); 0119 sourceModel()->removeFilter(filter_); 0120 } 0121 0122 void FilterView::contextMenuEvent(QContextMenuEvent* event_) { 0123 QModelIndex index = indexAt(event_->pos()); 0124 if(!index.isValid()) { 0125 return; 0126 } 0127 0128 QMenu menu(this); 0129 // no parent means it's a top-level item 0130 if(!index.parent().isValid()) { 0131 menu.addAction(QIcon::fromTheme(QStringLiteral("view-filter")), 0132 i18n("Modify Filter"), this, &FilterView::slotModifyFilter); 0133 menu.addAction(QIcon::fromTheme(QStringLiteral("edit-delete")), 0134 i18n("Delete Filter"), this, &FilterView::slotDeleteFilter); 0135 } else { 0136 Controller::self()->plugEntryActions(&menu); 0137 } 0138 menu.exec(event_->globalPos()); 0139 } 0140 0141 void FilterView::selectionChanged(const QItemSelection& selected_, const QItemSelection& deselected_) { 0142 // DEBUG_BLOCK; 0143 // in Controller::slotUpdateFilter(), filterView->clearSelection() gets called when the filter is empty 0144 GUI::TreeView::selectionChanged(selected_, deselected_); 0145 FilterPtr filter; 0146 foreach(const QModelIndex& index, selectionModel()->selectedIndexes()) { 0147 QModelIndex realIndex = sortModel()->mapToSource(index); 0148 Data::EntryPtr entry = sourceModel()->entry(realIndex); 0149 if(!entry && !filter) { 0150 filter = sourceModel()->filter(realIndex); 0151 break; 0152 } 0153 } 0154 // emit the signal with a null filter if there is nothing selected, also clearing any active filter as well 0155 if(filter || selectionModel()->selectedIndexes().isEmpty()) { 0156 emit signalUpdateFilter(filter); 0157 } 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 }