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"