File indexing completed on 2024-04-28 04:39:08
0001 /* 0002 SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "filtermodel.h" 0008 0009 #include <QIcon> 0010 0011 #include <KLocalizedString> 0012 0013 using namespace KDevelop; 0014 0015 FilterModel::FilterModel(QObject* parent) 0016 : QAbstractTableModel(parent) 0017 , m_ignoredLastInsert(false) 0018 { 0019 } 0020 0021 FilterModel::~FilterModel() 0022 { 0023 0024 } 0025 0026 SerializedFilters FilterModel::filters() const 0027 { 0028 return m_filters; 0029 } 0030 0031 void FilterModel::setFilters(const SerializedFilters& filters) 0032 { 0033 beginResetModel(); 0034 m_filters = filters; 0035 endResetModel(); 0036 } 0037 0038 void FilterModel::moveFilterUp(int row) 0039 { 0040 beginMoveRows(QModelIndex(), row, row, QModelIndex(), row - 1); 0041 using std::swap; 0042 swap(m_filters[row], m_filters[row - 1]); 0043 endMoveRows(); 0044 } 0045 0046 void FilterModel::moveFilterDown(int row) 0047 { 0048 beginMoveRows(QModelIndex(), row, row, QModelIndex(), row + 2); 0049 using std::swap; 0050 swap(m_filters[row], m_filters[row + 1]); 0051 endMoveRows(); 0052 } 0053 0054 int FilterModel::rowCount(const QModelIndex& parent) const 0055 { 0056 if (parent.isValid()) { 0057 return 0; 0058 } 0059 return m_filters.size(); 0060 } 0061 0062 int FilterModel::columnCount(const QModelIndex& /*parent*/) const 0063 { 0064 return NUM_COLUMNS; 0065 } 0066 0067 QVariant FilterModel::headerData(int section, Qt::Orientation orientation, int role) const 0068 { 0069 if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { 0070 return QVariant(); 0071 } 0072 0073 Q_ASSERT(section >= 0 && section < NUM_COLUMNS); 0074 0075 if (section == Pattern) { 0076 return i18nc("@title:column", "Pattern"); 0077 } else if (section == Targets) { 0078 return i18nc("@title:column", "Targets"); 0079 } else if (section == Inclusive) { 0080 return i18nc("@title:column", "Action"); 0081 } 0082 0083 return QVariant(); 0084 } 0085 0086 QVariant FilterModel::data(const QModelIndex& index, int role) const 0087 { 0088 if (!index.isValid()) { 0089 return QVariant(); 0090 } 0091 Q_ASSERT(!index.parent().isValid()); 0092 Q_ASSERT(index.row() >= 0 && index.row() < m_filters.size()); 0093 Q_ASSERT(index.column() >= 0 && index.column() < NUM_COLUMNS); 0094 0095 if (role != Qt::DisplayRole && role != Qt::DecorationRole 0096 && role != Qt::EditRole && role != Qt::ToolTipRole) 0097 { 0098 return QVariant(); 0099 } 0100 0101 const SerializedFilter& filter = m_filters.at(index.row()); 0102 const int column = index.column(); 0103 0104 if (column == Pattern) { 0105 if (role == Qt::DecorationRole) { 0106 return QVariant(); 0107 } else if (role == Qt::ToolTipRole) { 0108 return i18n( 0109 "The wildcard pattern defines whether a file or folder is included in a project or not.<br />" 0110 "The pattern is matched case-sensitively against the items relative path to the project root. " 0111 "The relative path starts with a forward slash, trailing slashes of folders are removed.<br />" 0112 "Patterns ending on <code>\"/\"</code> are implicitly considered to match against folders only.<br />" 0113 "Patterns which do not explicitly start with either <code>\"/\"</code> or <code>\"*\"</code> implicitly get <code>\"*/\"</code> prepended and thus match any item with a relative path ending on the given pattern." 0114 ); 0115 } 0116 return filter.pattern; 0117 } else if (column == Targets) { 0118 if (role == Qt::EditRole) { 0119 return static_cast<int>(filter.targets); 0120 } else if (role == Qt::ToolTipRole) { 0121 return i18n("The target defines what type of item the filter is matched against.<br />Filters either apply only to files, only to folders or to both."); 0122 } 0123 if (filter.targets & Filter::Files && filter.targets & Filter::Folders) { 0124 if (role == Qt::DecorationRole) { 0125 return QIcon::fromTheme(QStringLiteral("document-open")); 0126 } 0127 return i18nc("@item", "Files and Folders"); 0128 } else if (filter.targets & Filter::Folders) { 0129 if (role == Qt::DecorationRole) { 0130 return QIcon::fromTheme(QStringLiteral("folder")); 0131 } 0132 return i18nc("@item", "Folders"); 0133 } else { 0134 if (role == Qt::DecorationRole) { 0135 return QIcon::fromTheme(QStringLiteral("text-plain")); 0136 } 0137 return i18nc("@item", "Files"); 0138 } 0139 } else if (column == Inclusive) { 0140 if (role == Qt::EditRole) { 0141 return static_cast<int>(filter.type); 0142 } else if (role == Qt::ToolTipRole) { 0143 return i18n("Filters by default exclude items from the project. Inclusive patterns can be used to include items which where matched by previous exclusive patterns.<br />E.g. to only include files ending on <code>\".cpp\"</code> in your project, you could exclude all files via <code>\"*\"</code> and then apply an inclusive <code>\"*.cpp\"</code> pattern."); 0144 } 0145 if (filter.type == Filter::Inclusive) { 0146 if (role == Qt::DecorationRole) { 0147 return QIcon::fromTheme(QStringLiteral("list-add")); 0148 } 0149 return i18nc("@item", "Include"); 0150 } else { 0151 if (role == Qt::DecorationRole) { 0152 return QIcon::fromTheme(QStringLiteral("list-remove")); 0153 } 0154 return i18nc("@item", "Exclude"); 0155 } 0156 } 0157 0158 return QVariant(); 0159 } 0160 0161 bool FilterModel::setData(const QModelIndex& index, const QVariant& value, int role) 0162 { 0163 if (!index.isValid()) { 0164 return false; 0165 } 0166 Q_ASSERT(!index.parent().isValid()); 0167 Q_ASSERT(index.row() >= 0 && index.row() < m_filters.size()); 0168 Q_ASSERT(index.column() >= 0 && index.column() < NUM_COLUMNS); 0169 if (role != Qt::EditRole && role != Qt::DisplayRole) { 0170 return false; 0171 } 0172 SerializedFilter& filter = m_filters[index.row()]; 0173 const int column = index.column(); 0174 if (column == Pattern) { 0175 filter.pattern = value.toString(); 0176 } else if (column == Targets) { 0177 filter.targets = static_cast<Filter::Targets>(value.toInt()); 0178 } else if (column == Inclusive) { 0179 filter.type = static_cast<Filter::Type>(value.toInt()); 0180 } 0181 emit dataChanged(index, index); 0182 return true; 0183 } 0184 0185 Qt::DropActions FilterModel::supportedDropActions() const 0186 { 0187 return Qt::MoveAction; 0188 } 0189 0190 Qt::ItemFlags FilterModel::flags(const QModelIndex& index) const 0191 { 0192 Qt::ItemFlags baseFlags = QAbstractTableModel::flags(index); 0193 if (index.isValid() && !index.parent().isValid()) { 0194 return baseFlags | Qt::ItemIsEditable | Qt::ItemIsDragEnabled; 0195 } 0196 return baseFlags | Qt::ItemIsDropEnabled; 0197 } 0198 0199 bool FilterModel::insertRows(int row, int count, const QModelIndex& parent) 0200 { 0201 Q_ASSERT(!parent.isValid()); 0202 Q_ASSERT(count == 1); 0203 if (row == -1) { 0204 // after end of list and we cannot just append either as then the 0205 // later setData events will fails... 0206 m_ignoredLastInsert = true; 0207 return false; 0208 } 0209 m_ignoredLastInsert = false; 0210 Q_ASSERT(row >= 0 && row <= m_filters.size()); 0211 Q_ASSERT(row + count - 1 <= m_filters.size()); 0212 0213 beginInsertRows(parent, row, row + count - 1); 0214 for (int i = 0; i < count; ++i) { 0215 m_filters.insert(row + i, SerializedFilter()); 0216 } 0217 endInsertRows(); 0218 return true; 0219 } 0220 0221 bool FilterModel::removeRows(int row, int count, const QModelIndex& parent) 0222 { 0223 Q_ASSERT(!parent.isValid()); 0224 Q_ASSERT(count == 1); 0225 Q_ASSERT(row >= 0 && row < m_filters.size()); 0226 Q_ASSERT(row + count <= m_filters.size()); 0227 0228 if (m_ignoredLastInsert) { 0229 return false; 0230 } 0231 0232 beginRemoveRows(parent, row, row + count - 1); 0233 m_filters.remove(row, count); 0234 endRemoveRows(); 0235 0236 return true; 0237 } 0238 0239 QMap<int, QVariant> FilterModel::itemData(const QModelIndex& index) const 0240 { 0241 QMap<int, QVariant> ret; 0242 if (!index.isValid()) { 0243 return ret; 0244 } 0245 Q_ASSERT(!index.parent().isValid()); 0246 Q_ASSERT(index.row() >= 0 && index.row() < m_filters.size()); 0247 const SerializedFilter& filter = m_filters.at(index.row()); 0248 ret.insert(Qt::UserRole + Pattern, filter.pattern); 0249 ret.insert(Qt::UserRole + Inclusive, static_cast<int>(filter.type)); 0250 ret.insert(Qt::UserRole + Targets, static_cast<int>(filter.targets)); 0251 return ret; 0252 } 0253 0254 bool FilterModel::setItemData(const QModelIndex& index, const QMap< int, QVariant >& roles) 0255 { 0256 Q_ASSERT(index.isValid()); 0257 Q_ASSERT(!index.parent().isValid()); 0258 Q_ASSERT(index.row() >= 0 && index.row() < m_filters.size()); 0259 Q_ASSERT(roles.size() == 3); 0260 Q_ASSERT(roles.contains(Qt::UserRole + Pattern)); 0261 Q_ASSERT(roles.contains(Qt::UserRole + Inclusive)); 0262 Q_ASSERT(roles.contains(Qt::UserRole + Targets)); 0263 0264 if (m_ignoredLastInsert) { 0265 return false; 0266 } 0267 0268 SerializedFilter& filter = m_filters[index.row()]; 0269 filter.pattern = roles[Qt::UserRole + Pattern].toString(); 0270 filter.type = Filter::Type(roles[Qt::UserRole + Inclusive].toInt()); 0271 filter.targets = Filter::Targets(roles[Qt::UserRole + Targets].toInt()); 0272 return true; 0273 } 0274 0275 #include "moc_filtermodel.cpp"