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