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