1 /** -*- mode: c++ ; c-basic-offset: 2 -*-
2  *
3  *  @file FilterTagMap.h
4  *
5  *  Copyright 2017 Sebastien Fourey
6  *
7  *  This file is part of G'MIC-Qt, a generic plug-in for raster graphics
8  *  editors, offering hundreds of filters thanks to the underlying G'MIC
9  *  image processing framework.
10  *
11  *  gmic_qt is free software: you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation, either version 3 of the License, or
14  *  (at your option) any later version.
15  *
16  *  gmic_qt is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with gmic_qt.  If not, see <http://www.gnu.org/licenses/>.
23  *
24  */
25 #include "FilterTagMap.h"
26 #include <QBuffer>
27 #include <QByteArray>
28 #include <QDataStream>
29 #include <QDebug>
30 #include <QFile>
31 #include <QJsonArray>
32 #include <QJsonDocument>
33 #include <QJsonObject>
34 #include <QJsonValue>
35 #include <QList>
36 #include "Common.h"
37 #include "Globals.h"
38 #include "GmicQt.h"
39 #include "Logger.h"
40 #include "Utils.h"
41 
42 namespace GmicQt
43 {
44 
45 QMap<QString, TagColorSet> FiltersTagMap::_hashesToColors;
46 
filterTags(const QString & hash)47 TagColorSet FiltersTagMap::filterTags(const QString & hash)
48 {
49   auto it = _hashesToColors.find(hash);
50   if (it == _hashesToColors.end()) {
51     return TagColorSet::Empty;
52   }
53   return it.value();
54 }
55 
setFilterTags(const QString & hash,const TagColorSet & colors)56 void FiltersTagMap::setFilterTags(const QString & hash, const TagColorSet & colors)
57 {
58   if (colors.isEmpty()) {
59     _hashesToColors.remove(hash);
60     return;
61   }
62   _hashesToColors[hash] = colors;
63 }
64 
load()65 void FiltersTagMap::load()
66 {
67   _hashesToColors.clear();
68   QString jsonFilename = QString("%1%2").arg(gmicConfigPath(true), FILTERS_TAGS_FILENAME);
69   QFile jsonFile(jsonFilename);
70   if (!jsonFile.exists()) {
71     return;
72   }
73   if (jsonFile.open(QFile::ReadOnly)) {
74 #ifdef _GMIC_QT_DEBUG_
75     QJsonDocument jsonDoc;
76     QByteArray allFile = jsonFile.readAll();
77     if (allFile.startsWith("{")) {
78       jsonDoc = QJsonDocument::fromJson(allFile);
79     } else {
80       jsonDoc = QJsonDocument::fromJson(qUncompress(allFile));
81     }
82 #else
83     QJsonDocument jsonDoc = QJsonDocument::fromJson(qUncompress(jsonFile.readAll()));
84 #endif
85     if (jsonDoc.isNull()) {
86       Logger::warning(QString("Cannot parse ") + jsonFilename);
87       Logger::warning("Fiter tags are lost!");
88     } else {
89       if (!jsonDoc.isObject()) {
90         Logger::error(QString("JSON file format is not correct (") + jsonFilename + ")");
91       } else {
92         QJsonObject documentObject = jsonDoc.object();
93         for (QJsonObject::const_iterator it = documentObject.constBegin(); //
94              it != documentObject.constEnd();                              //
95              ++it) {
96           _hashesToColors[it.key()] = TagColorSet(it.value().toInt());
97         }
98       }
99     }
100   } else {
101     Logger::error("Cannot open " + jsonFilename);
102     Logger::error("Tags cannot be restored");
103   }
104 }
105 
save()106 void FiltersTagMap::save()
107 {
108   QJsonObject documentObject;
109   auto it = _hashesToColors.begin();
110   while (it != _hashesToColors.end()) {
111     documentObject.insert(it.key(), QJsonValue(int(it.value().mask())));
112     ++it;
113   }
114   QJsonDocument jsonDoc(documentObject);
115   QString jsonFilename = QString("%1%2").arg(gmicConfigPath(true), FILTERS_TAGS_FILENAME);
116   if (QFile::exists(jsonFilename)) {
117     QString bakFilename = QString("%1%2").arg(gmicConfigPath(false), FILTERS_TAGS_FILENAME ".bak");
118     QFile::remove(bakFilename);
119     QFile::copy(jsonFilename, bakFilename);
120   }
121 
122 #ifdef _GMIC_QT_DEBUG_
123   const bool ok = safelyWrite(jsonDoc.toJson(), jsonFilename);
124 #else
125   const bool ok = safelyWrite(qCompress(jsonDoc.toJson(QJsonDocument::Compact)), jsonFilename);
126 #endif
127   if (!ok) {
128     Logger::error("Cannot write " + jsonFilename);
129     Logger::error("Parameters cannot be saved");
130   }
131 }
132 
usedColors(int * count)133 TagColorSet FiltersTagMap::usedColors(int * count)
134 {
135   TagColorSet all;
136   auto it = _hashesToColors.cbegin();
137   if (count) {
138     memset(count, 0, sizeof(int) * int(TagColor::Count));
139     while (it != _hashesToColors.cend()) {
140       TagColorSet colors = it.value();
141       for (TagColor color : colors) {
142         ++count[int(color)];
143       }
144       all |= colors;
145       ++it;
146     }
147   } else {
148     while (it != _hashesToColors.cend()) {
149       all |= it.value();
150       ++it;
151     }
152   }
153   return all;
154 }
155 
removeAllTags(TagColor color)156 void FiltersTagMap::removeAllTags(TagColor color)
157 {
158   QList<QString> toBeRemoved;
159   auto it = _hashesToColors.begin();
160   while (it != _hashesToColors.end()) {
161     it.value() -= color;
162     if (it.value().isEmpty()) {
163       toBeRemoved.push_back(it.key());
164     }
165     ++it;
166   }
167   for (const QString & hash : toBeRemoved) {
168     _hashesToColors.remove(hash);
169   }
170 }
171 
clearFilterTag(const QString & hash,TagColor color)172 void FiltersTagMap::clearFilterTag(const QString & hash, TagColor color)
173 {
174   auto it = _hashesToColors.find(hash);
175   if (it == _hashesToColors.end()) {
176     return;
177   }
178   it.value() -= color;
179   if (it.value().isEmpty()) {
180     _hashesToColors.erase(it);
181   }
182 }
183 
setFilterTag(const QString & hash,TagColor color)184 void FiltersTagMap::setFilterTag(const QString & hash, TagColor color)
185 {
186   _hashesToColors[hash] += color;
187 }
188 
toggleFilterTag(const QString & hash,TagColor color)189 void FiltersTagMap::toggleFilterTag(const QString & hash, TagColor color)
190 {
191   _hashesToColors[hash].toggle(color);
192 }
193 
remove(const QString & hash)194 void FiltersTagMap::remove(const QString & hash)
195 {
196   _hashesToColors.remove(hash);
197 }
198 } // namespace GmicQt
199