File indexing completed on 2025-02-16 04:56:41
0001 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu> 0002 // SPDX-FileCopyrightText: 2022 Claudio Cambra <claudio.cambra@gmail.com> 0003 // SPDX-License-Identifier: GPL-2.0-or-later 0004 0005 #include "colorproxymodel.h" 0006 0007 #include <Akonadi/AgentInstance> 0008 #include <Akonadi/AgentManager> 0009 #include <Akonadi/AttributeFactory> 0010 #include <Akonadi/CollectionColorAttribute> 0011 #include <Akonadi/CollectionModifyJob> 0012 #include <Akonadi/CollectionUtils> 0013 #include <Akonadi/EntityDisplayAttribute> 0014 #include <KCalendarCore/Event> 0015 #include <KCalendarCore/Journal> 0016 #include <KCalendarCore/Todo> 0017 #include <KConfigGroup> 0018 #include <KContacts/Addressee> 0019 #include <KContacts/ContactGroup> 0020 #include <KLocalizedString> 0021 #include <KSharedConfig> 0022 #include <QFont> 0023 #include <QRandomGenerator> 0024 0025 namespace 0026 { 0027 static bool hasCompatibleMimeTypes(const Akonadi::Collection &collection) 0028 { 0029 static QStringList goodMimeTypes; 0030 0031 if (goodMimeTypes.isEmpty()) { 0032 goodMimeTypes << QStringLiteral("text/calendar") << KCalendarCore::Event::eventMimeType() << KCalendarCore::Todo::todoMimeType() 0033 << KContacts::Addressee::mimeType() << KContacts::ContactGroup::mimeType() << KCalendarCore::Journal::journalMimeType(); 0034 } 0035 0036 for (int i = 0; i < goodMimeTypes.count(); ++i) { 0037 if (collection.contentMimeTypes().contains(goodMimeTypes.at(i))) { 0038 return true; 0039 } 0040 } 0041 0042 return false; 0043 } 0044 } 0045 0046 ColorProxyModel::ColorProxyModel(QObject *parent) 0047 : QSortFilterProxyModel(parent) 0048 { 0049 // Needed to read colorattribute of collections for incidence colors 0050 Akonadi::AttributeFactory::registerAttribute<Akonadi::CollectionColorAttribute>(); 0051 } 0052 0053 QVariant ColorProxyModel::data(const QModelIndex &index, int role) const 0054 { 0055 if (!index.isValid()) { 0056 return {}; 0057 } 0058 0059 if (role == Qt::DecorationRole) { 0060 const Akonadi::Collection collection = Akonadi::CollectionUtils::fromIndex(index); 0061 0062 if (hasCompatibleMimeTypes(collection)) { 0063 if (collection.hasAttribute<Akonadi::EntityDisplayAttribute>() && !collection.attribute<Akonadi::EntityDisplayAttribute>()->iconName().isEmpty()) { 0064 return collection.attribute<Akonadi::EntityDisplayAttribute>()->iconName(); 0065 } 0066 } 0067 } else if (role == Qt::FontRole) { 0068 const Akonadi::Collection collection = Akonadi::CollectionUtils::fromIndex(index); 0069 if (!collection.contentMimeTypes().isEmpty() && collection.id() == m_standardCollectionId && collection.rights() & Akonadi::Collection::CanCreateItem) { 0070 auto font = qvariant_cast<QFont>(QSortFilterProxyModel::data(index, Qt::FontRole)); 0071 font.setBold(true); 0072 return font; 0073 } 0074 } else if (role == Qt::DisplayRole) { 0075 const Akonadi::Collection collection = Akonadi::CollectionUtils::fromIndex(index); 0076 const Akonadi::Collection::Id colId = collection.id(); 0077 const Akonadi::AgentInstance instance = Akonadi::AgentManager::self()->instance(collection.resource()); 0078 0079 if (!instance.isOnline() && !collection.isVirtual()) { 0080 return i18nc("@item this is the default calendar", "%1 (Offline)", collection.displayName()); 0081 } 0082 if (colId == m_standardCollectionId) { 0083 return i18nc("@item this is the default calendar", "%1 (Default)", collection.displayName()); 0084 } 0085 } else if (role == Qt::BackgroundRole) { 0086 auto color = getCollectionColor(Akonadi::CollectionUtils::fromIndex(index)); 0087 // Otherwise QML will get black 0088 if (color.isValid()) { 0089 return color; 0090 } else { 0091 return {}; 0092 } 0093 } else if (role == isResource) { 0094 return Akonadi::CollectionUtils::isResource(Akonadi::CollectionUtils::fromIndex(index)); 0095 } 0096 0097 return QSortFilterProxyModel::data(index, role); 0098 } 0099 0100 Qt::ItemFlags ColorProxyModel::flags(const QModelIndex &index) const 0101 { 0102 return Qt::ItemIsSelectable | QSortFilterProxyModel::flags(index); 0103 } 0104 0105 QHash<int, QByteArray> ColorProxyModel::roleNames() const 0106 { 0107 QHash<int, QByteArray> roleNames = QSortFilterProxyModel::roleNames(); 0108 roleNames[Qt::CheckStateRole] = "checkState"; 0109 roleNames[Qt::BackgroundRole] = "collectionColor"; 0110 roleNames[isResource] = "isResource"; 0111 return roleNames; 0112 } 0113 0114 QColor ColorProxyModel::getCollectionColor(Akonadi::Collection collection) const 0115 { 0116 const auto id = collection.id(); 0117 auto supportsMimeType = collection.contentMimeTypes().contains(QLatin1StringView("application/x-vnd.akonadi.calendar.event")) 0118 || collection.contentMimeTypes().contains(QLatin1StringView("application/x-vnd.akonadi.calendar.todo")) 0119 || collection.contentMimeTypes().contains(QLatin1StringView("application/x-vnd.akonadi.calendar.journal")) 0120 || collection.contentMimeTypes().contains(KContacts::Addressee::mimeType()) 0121 || collection.contentMimeTypes().contains(KContacts::ContactGroup::mimeType()); 0122 0123 if (!supportsMimeType) { 0124 return {}; 0125 } 0126 0127 if (colorCache.contains(id)) { 0128 return colorCache[id]; 0129 } 0130 0131 if (collection.hasAttribute<Akonadi::CollectionColorAttribute>()) { 0132 const auto colorAttr = collection.attribute<Akonadi::CollectionColorAttribute>(); 0133 if (colorAttr && colorAttr->color().isValid()) { 0134 colorCache[id] = colorAttr->color(); 0135 return colorAttr->color(); 0136 } 0137 } 0138 0139 KSharedConfig::Ptr config = KSharedConfig::openConfig(); 0140 KConfigGroup resourcesColorsConfig(config, QStringLiteral("Resources Colors")); 0141 const QStringList colorKeyList = resourcesColorsConfig.keyList(); 0142 0143 QColor color; 0144 for (const QString &key : colorKeyList) { 0145 if (key.toLongLong() == id) { 0146 color = resourcesColorsConfig.readEntry(key, QColor("blue")); 0147 } 0148 } 0149 0150 if (!color.isValid()) { 0151 color.setRgb(QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256), QRandomGenerator::global()->bounded(256)); 0152 colorCache[id] = color; 0153 } 0154 0155 auto colorAttr = collection.attribute<Akonadi::CollectionColorAttribute>(Akonadi::Collection::AddIfMissing); 0156 colorAttr->setColor(color); 0157 0158 auto modifyJob = new Akonadi::CollectionModifyJob(collection); 0159 connect(modifyJob, &Akonadi::CollectionModifyJob::result, this, [](KJob *job) { 0160 if (job->error()) { 0161 qWarning() << "Error occurred modifying collection color: " << job->errorString(); 0162 } 0163 }); 0164 0165 return color; 0166 } 0167 0168 QColor ColorProxyModel::color(Akonadi::Collection::Id collectionId) const 0169 { 0170 return colorCache[collectionId]; 0171 } 0172 0173 void ColorProxyModel::setColor(Akonadi::Collection::Id collectionId, const QColor &color) 0174 { 0175 colorCache[collectionId] = color; 0176 } 0177 0178 void ColorProxyModel::setStandardCollectionId(Akonadi::Collection::Id standardCollectionId) 0179 { 0180 m_standardCollectionId = standardCollectionId; 0181 } 0182 0183 #include "moc_colorproxymodel.cpp"