File indexing completed on 2024-05-19 04:28:57
0001 /* This file is part of the KDE project 0002 SPDX-FileCopyrightText: 2009 Vera Lukman <shicmap@gmail.com> 0003 SPDX-FileCopyrightText: 2011 Sven Langkamp <sven.langkamp@gmail.com> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only 0006 */ 0007 0008 #include <kis_debug.h> 0009 #include <QPoint> 0010 #include <QStringList> 0011 #include <QString> 0012 #include <QColor> 0013 #include <brushengine/kis_paintop_registry.h> 0014 #include <brushengine/kis_paintop_preset.h> 0015 #include <KoID.h> 0016 #include <kconfig.h> 0017 #include "kis_favorite_resource_manager.h" 0018 #include "kis_popup_palette.h" 0019 #include "kis_paintop_box.h" 0020 #include "KisViewManager.h" 0021 #include "KisResourceServerProvider.h" 0022 #include "kis_min_heap.h" 0023 #include "kis_config.h" 0024 #include "kis_config_notifier.h" 0025 #include <kis_paintop_preset.h> 0026 0027 0028 class KisFavoriteResourceManager::ColorDataList 0029 { 0030 public: 0031 static const int MAX_RECENT_COLOR = 12; 0032 0033 ColorDataList() { 0034 m_key = 0; 0035 } 0036 0037 ~ColorDataList() { 0038 qDeleteAll(m_guiList); 0039 } 0040 0041 int size() { 0042 return m_guiList.size(); 0043 } 0044 0045 int leastUsedGuiPos() { 0046 return findPos(m_priorityList.valueAt(0)); 0047 } 0048 0049 const KoColor& guiColor(int pos) { 0050 Q_ASSERT_X(pos < size(), "ColorDataList::guiColor", "index out of bounds"); 0051 Q_ASSERT_X(pos >= 0, "ColorDataList::guiColor", "negative index"); 0052 0053 return m_guiList.at(pos)->data; 0054 } 0055 0056 void append(const KoColor& data) { 0057 int pos = findPos(data); 0058 if (pos > -1) updateKey(pos); 0059 else appendNew(data); 0060 } 0061 0062 void appendNew(const KoColor& data) { 0063 if (size() >= ColorDataList::MAX_RECENT_COLOR) removeLeastUsed(); 0064 0065 PriorityNode<KoColor> * node; 0066 node = new PriorityNode <KoColor>(); 0067 node->data = data; 0068 node->key = m_key++; 0069 m_priorityList.append(node); 0070 0071 int pos = guiInsertPos(data); 0072 pos >= m_guiList.size() ? m_guiList.append(node) 0073 : m_guiList.insert(pos, node); 0074 node = 0; 0075 } 0076 0077 void removeLeastUsed() { 0078 Q_ASSERT_X(size() >= 0, "ColorDataList::removeLeastUsed", "index out of bounds"); 0079 if (size() <= 0) return; 0080 0081 int pos = findPos(m_priorityList.valueAt(0)); 0082 m_guiList.removeAt(pos); 0083 m_priorityList.remove(0); 0084 } 0085 0086 void clearHistory() { 0087 Q_ASSERT_X(size() >= 0, "ColorDataList::clearHistory", "index out of bounds"); 0088 if (size() <= 0 ) return; 0089 while (size() > 0){ 0090 removeLeastUsed(); 0091 } 0092 } 0093 0094 void updateKey(int guiPos) { 0095 if (m_guiList.at(guiPos)->key == m_key - 1) return; 0096 m_priorityList.changeKey(m_guiList.at(guiPos)->pos, m_key++); 0097 } 0098 0099 /*find position of the color on the gui list*/ 0100 int findPos(const KoColor& color) { 0101 0102 int low = 0, high = size(), mid = 0; 0103 while (low < high) { 0104 mid = (low + high) / 2; 0105 if (hsvComparison(color, m_guiList.at(mid)->data) == 0) return mid; 0106 else if (hsvComparison(color, m_guiList.at(mid)->data) < 0) high = mid; 0107 else low = mid + 1; 0108 } 0109 0110 return -1; 0111 } 0112 0113 0114 private: 0115 0116 int m_key; 0117 0118 int guiInsertPos(const KoColor& color) { 0119 int low = 0, high = size() - 1, mid = (low + high) / 2; 0120 while (low < high) { 0121 0122 hsvComparison(color, m_guiList[mid]->data) == -1 ? high = mid 0123 : low = mid + 1; 0124 mid = (low + high) / 2; 0125 } 0126 0127 if (m_guiList.size() > 0) { 0128 if (hsvComparison(color, m_guiList[mid]->data) == 1) ++mid; 0129 } 0130 return mid; 0131 } 0132 0133 /*compares c1 and c2 based on HSV. 0134 c1 < c2, returns -1 0135 c1 = c2, returns 0 0136 c1 > c2, returns 1 */ 0137 int hsvComparison(const KoColor& c1, const KoColor& c2) { 0138 QColor qc1 = c1.toQColor(); 0139 QColor qc2 = c2.toQColor(); 0140 0141 if (qc1.hue() < qc2.hue()) return -1; 0142 if (qc1.hue() > qc2.hue()) return 1; 0143 0144 // hue is the same, ok let's compare saturation 0145 if (qc1.saturation() < qc2.saturation()) return -1; 0146 if (qc1.saturation() > qc2.saturation()) return 1; 0147 0148 // oh, also saturation is same? 0149 if (qc1.value() < qc2.value()) return -1; 0150 if (qc1.value() > qc2.value()) return 1; 0151 0152 // user selected two similar colors 0153 return 0; 0154 } 0155 0156 KisMinHeap <KoColor, MAX_RECENT_COLOR> m_priorityList; 0157 QList <PriorityNode <KoColor>*> m_guiList; 0158 }; 0159 0160 0161 0162 KisFavoriteResourceManager::KisFavoriteResourceManager(KisPaintopBox *paintopBox) 0163 : m_paintopBox(paintopBox) 0164 { 0165 KisConfig cfg(true); 0166 m_maxPresets = cfg.favoritePresets(); 0167 m_colorList = new ColorDataList(); 0168 connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(configChanged())); 0169 KisPaintOpPresetResourceServer * rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); 0170 rServer->addObserver(this); 0171 } 0172 0173 KisFavoriteResourceManager::~KisFavoriteResourceManager() 0174 { 0175 KisPaintOpPresetResourceServer *rServer = KisResourceServerProvider::instance()->paintOpPresetServer(); 0176 rServer->removeObserver(this); 0177 delete m_colorList; 0178 } 0179 0180 void KisFavoriteResourceManager::unsetResourceServer() 0181 { 0182 // ... 0183 } 0184 QVector<QString> KisFavoriteResourceManager::favoritePresetNamesList() 0185 { 0186 init(); 0187 0188 QVector<QString> names; 0189 for (int i = 0; i < m_maxPresets; i++) { 0190 QModelIndex index = m_resourcesProxyModel->index(i, 0); 0191 if (index.isValid()) { 0192 QString name = m_resourcesProxyModel->data(index, Qt::UserRole + KisAbstractResourceModel::Name).toString(); 0193 names << name; 0194 } else { 0195 break; // no more valid indices 0196 } 0197 } 0198 0199 return names; 0200 } 0201 0202 QList<QImage> KisFavoriteResourceManager::favoritePresetImages() 0203 { 0204 init(); 0205 QList<QImage> images; 0206 for (int i = 0; i < m_maxPresets; i++) { 0207 QModelIndex index = m_resourcesProxyModel->index(i, 0); 0208 if (index.isValid()) { 0209 QVariant tmp = m_resourcesProxyModel->data(index, Qt::UserRole + KisAbstractResourceModel::Thumbnail); 0210 QImage image = tmp.value<QImage>(); 0211 images << image; 0212 } else { 0213 break; // no more valid indices 0214 } 0215 } 0216 return images; 0217 } 0218 0219 void KisFavoriteResourceManager::setCurrentTag(const KisTagSP tag) 0220 { 0221 m_currentTag = tag; 0222 m_resourcesProxyModel->setTagFilter(tag); 0223 KisConfig(false).writeEntry<QString>("favoritePresetsTag", tag->url()); 0224 updateFavoritePresets(); 0225 } 0226 0227 void KisFavoriteResourceManager::slotChangeActivePaintop(int pos) 0228 { 0229 //ENTER_FUNCTION() << ppVar(pos) << ppVar(numFavoritePresets()); 0230 if (pos < 0 || pos >= numFavoritePresets()) return; 0231 0232 QModelIndex index = m_resourcesProxyModel->index(pos, 0); 0233 KoResourceSP resource = m_resourcesProxyModel->resourceForIndex(index); 0234 0235 m_paintopBox->resourceSelected(resource); 0236 0237 emit hidePalettes(); 0238 } 0239 0240 int KisFavoriteResourceManager::numFavoritePresets() 0241 { 0242 init(); 0243 return favoritePresetNamesList().size(); 0244 } 0245 0246 //Recent Colors 0247 void KisFavoriteResourceManager::slotUpdateRecentColor(int pos) 0248 { 0249 // Do not update the key, the color might be selected but it is not used yet. So we are not supposed 0250 // to update the color priority when we select it. 0251 m_colorList->updateKey(pos); 0252 0253 emit setSelectedColor(pos); 0254 emit sigSetFGColor(m_colorList->guiColor(pos)); 0255 emit hidePalettes(); 0256 } 0257 0258 void KisFavoriteResourceManager::slotAddRecentColor(const KoColor& color) 0259 { 0260 m_colorList->append(color); 0261 int pos = m_colorList->findPos(color); 0262 emit setSelectedColor(pos); 0263 } 0264 0265 void KisFavoriteResourceManager::slotChangeFGColorSelector(KoColor c) 0266 { 0267 emit sigChangeFGColorSelector(c); 0268 } 0269 0270 void KisFavoriteResourceManager::removingResource(QSharedPointer<KisPaintOpPreset> /*resource*/) 0271 { 0272 updateFavoritePresets(); 0273 } 0274 0275 void KisFavoriteResourceManager::resourceAdded(QSharedPointer<KisPaintOpPreset> /*resource*/) 0276 { 0277 updateFavoritePresets(); 0278 } 0279 0280 void KisFavoriteResourceManager::resourceChanged(QSharedPointer<KisPaintOpPreset> /*resource*/) 0281 { 0282 updateFavoritePresets(); 0283 } 0284 0285 int KisFavoriteResourceManager::recentColorsTotal() 0286 { 0287 return m_colorList->size(); 0288 } 0289 0290 void KisFavoriteResourceManager::slotClearHistory() 0291 { 0292 m_colorList->clearHistory(); 0293 } 0294 0295 const KoColor& KisFavoriteResourceManager::recentColorAt(int pos) 0296 { 0297 return m_colorList->guiColor(pos); 0298 } 0299 0300 void KisFavoriteResourceManager::slotSetBGColor(const KoColor c) 0301 { 0302 m_bgColor = c; 0303 } 0304 0305 KoColor KisFavoriteResourceManager::bgColor() const 0306 { 0307 return m_bgColor; 0308 } 0309 0310 bool sortPresetByName(KisPaintOpPresetSP preset1, KisPaintOpPresetSP preset2) 0311 { 0312 return preset1->name() < preset2->name(); 0313 } 0314 0315 void KisFavoriteResourceManager::updateFavoritePresets() 0316 { 0317 emit updatePalettes(); 0318 } 0319 0320 void KisFavoriteResourceManager::configChanged() 0321 { 0322 KisConfig cfg(true); 0323 m_maxPresets = cfg.favoritePresets(); 0324 updateFavoritePresets(); 0325 } 0326 0327 void KisFavoriteResourceManager::presetsChanged() 0328 { 0329 emit updatePalettes(); 0330 } 0331 0332 void KisFavoriteResourceManager::init() 0333 { 0334 if (!m_initialized) { 0335 m_initialized = true; 0336 0337 m_tagModel = new KisTagModel(ResourceType::PaintOpPresets, this); 0338 m_resourcesProxyModel = new KisTagFilterResourceProxyModel(ResourceType::PaintOpPresets, this); 0339 0340 connect(m_resourcesProxyModel, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(presetsChanged())); 0341 connect(m_resourcesProxyModel, SIGNAL(rowsRemoved(QModelIndex, int, int)), this, SLOT(presetsChanged())); 0342 0343 m_resourceModel = new KisResourceModel(ResourceType::PaintOpPresets, this); 0344 0345 KisResourceServerProvider::instance()->paintOpPresetServer(); 0346 QString currentTag = KisConfig(true).readEntry<QString>("favoritePresetsTag", "★ My Favorites"); 0347 0348 KisTagModel tagModel(ResourceType::PaintOpPresets); 0349 KisTagSP currentTagSP = tagModel.tagForUrl(currentTag); 0350 if (!currentTagSP.isNull()) { 0351 m_currentTag = currentTagSP; 0352 } 0353 if (m_currentTag.isNull() && tagModel.rowCount() > 0) { 0354 // safety measure to have at least *some* tag chosen 0355 QModelIndex idx = tagModel.index(0, 0); 0356 currentTagSP = tagModel.tagForIndex(idx); 0357 if (currentTagSP && !m_currentTag) { 0358 m_currentTag = currentTagSP; 0359 } 0360 } 0361 m_resourcesProxyModel->setTagFilter(m_currentTag); 0362 m_resourcesProxyModel->sort(KisAbstractResourceModel::Name); 0363 0364 updateFavoritePresets(); 0365 } 0366 } 0367 0368