1 /*
2 Drawpile - a collaborative drawing program.
3
4 Copyright (C) 2007-2019 Calle Laakkonen
5
6 Drawpile is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Drawpile is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Drawpile. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "palette.h"
21 #include "../libshared/util/paths.h"
22
23 #include <QDebug>
24 #include <QVariant>
25 #include <QColor>
26 #include <QFile>
27 #include <QFileInfo>
28 #include <QTextStream>
29 #include <QRegularExpression>
30
Palette(QObject * parent)31 Palette::Palette(QObject *parent) : Palette(QString(), QString(), false, parent) { }
Palette(const QString & name,QObject * parent)32 Palette::Palette(const QString &name, QObject *parent) : Palette(name, QString(), false, parent) { }
33
Palette(const QString & name,const QString & filename,bool readonly,QObject * parent)34 Palette::Palette(const QString& name, const QString& filename, bool readonly, QObject *parent)
35 : QObject(parent), _name(name), _oldname(name), _filename(filename), _columns(8), _modified(false), _readonly(readonly), _writeprotect(false)
36 {
37 }
38
39 /**
40 * Load a palette from a GIMP palette file.
41 *
42 * The file format is:
43 *
44 * GIMP Palette
45 * *HEADER FIELDS*
46 * # one or more comment
47 * r g b name
48 * ...
49 *
50 * @param filename palette file name
51 * @param writeprotected is the source file read only
52 */
fromFile(const QFileInfo & file,bool readonly,QObject * parent)53 Palette *Palette::fromFile(const QFileInfo& file, bool readonly, QObject *parent)
54 {
55 QFile palfile(file.absoluteFilePath());
56 if (!palfile.open(QIODevice::ReadOnly | QIODevice::Text))
57 return nullptr;
58
59 QTextStream in(&palfile);
60 if(in.readLine() != "GIMP Palette")
61 return nullptr;
62
63 Palette *pal = new Palette(file.baseName(), file.absoluteFilePath(), !file.isWritable() | readonly, parent);
64
65 const QRegularExpression colorRe("^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*(.+)?$");
66
67 do {
68 QString line = in.readLine().trimmed();
69 if(line.isEmpty() || line.at(0) == '#') {
70 // ignore comments and empty lines
71
72 } else if(line.startsWith("Name:")) {
73 pal->_name = line.mid(5).trimmed();
74
75 } else if(line.startsWith("Columns:")) {
76 bool ok;
77 int cols = line.mid(9).trimmed().toInt(&ok);
78 if(ok && cols>0)
79 pal->_columns = cols;
80
81 } else {
82 QRegularExpressionMatch m = colorRe.match(line);
83 if(m.hasMatch()) {
84 pal->_colors.append(PaletteColor(
85 QColor(
86 m.captured(1).toInt(),
87 m.captured(2).toInt(),
88 m.captured(3).toInt()
89 ),
90 m.captured(4)
91 )
92 );
93
94 } else {
95 qWarning() << "unhandled line" << line << "in" << file.fileName();
96 }
97 }
98 } while(!in.atEnd());
99
100 // Palettes loaded from file are write-protected by default
101 pal->_writeprotect = true;
102
103 return pal;
104 }
105
copy(const Palette * pal,const QString & newname,QObject * parent)106 Palette *Palette::copy(const Palette *pal, const QString &newname, QObject *parent)
107 {
108 Q_ASSERT(pal);
109 Palette *p = new Palette(newname, parent);
110 p->_columns = pal->_columns;
111 p->_colors = pal->_colors;
112 p->_modified = true;
113 return p;
114 }
115
116 /**
117 * @param filename palette file name
118 */
save(const QString & filename)119 bool Palette::save(const QString& filename)
120 {
121 return exportPalette(filename);
122 }
123
exportPalette(const QString & filename,QString * errorString)124 bool Palette::exportPalette(const QString &filename, QString *errorString)
125 {
126 QFile data(filename);
127 if (data.open(QFile::WriteOnly | QFile::Truncate)) {
128 QTextStream out(&data);
129 out << "GIMP Palette\n";
130 out << "Name: " << _name << "\n";
131 out << "Columns: " << _columns << "\n";
132 out << "#\n";
133 for(const PaletteColor &c : _colors) {
134 out << c.color.red() << ' ' << c.color.green() << ' ' << c.color.blue() << '\t' << c.name << '\n';
135 }
136 return true;
137
138 } else {
139 if(errorString)
140 *errorString = data.errorString();
141 qWarning() << filename << data.errorString();
142 return false;
143 }
144 }
145
save()146 bool Palette::save()
147 {
148 if(_readonly)
149 return false;
150
151 QString oldpath;
152 if(_name != _oldname) {
153 // Name has changed: we need to delete the old palette
154 oldpath = _filename;
155 _filename = QString();
156 }
157
158 if(_filename.isEmpty()) {
159 // No filename set? Create it from the palette name
160 _filename = utils::paths::writablePath("palettes/", _name + ".gpl");
161 }
162
163 bool ok = save(_filename);
164 if(ok) {
165 _oldname = _name;
166 _modified = false;
167
168 if(!oldpath.isEmpty() && oldpath != _filename)
169 QFile(oldpath).remove();
170 }
171
172 return ok;
173 }
174
deleteFile()175 bool Palette::deleteFile()
176 {
177 if(_filename.isEmpty() || _readonly)
178 return false;
179 return QFile(_filename).remove();
180 }
181
182 /**
183 * Change the palette name.
184 * The filename is set as the name + extension ".gpl"
185 * @param name new palette name
186 */
setName(const QString & name)187 void Palette::setName(const QString& name)
188 {
189 if(_name != name) {
190 _name = name;
191 _modified = true;
192 emit nameChanged();
193 }
194 }
195
setColumns(int columns)196 void Palette::setColumns(int columns)
197 {
198 if(_columns != columns) {
199 _columns = columns;
200 _modified = true;
201 emit columnsChanged();
202 }
203 }
204
205 /**
206 * Size of the palette is increased by one. The new color is
207 * inserted before the index. If index == count(), the color is
208 * added to the end of the palette.
209 * @param index color index
210 * @param color color
211 * @pre 0 <= index <= count()
212 */
setColor(int index,const PaletteColor & color)213 void Palette::setColor(int index, const PaletteColor& color)
214 {
215 if(_readonly)
216 return;
217
218 _colors[index] = color;
219 _modified = true;
220 emit colorsChanged();
221 }
222
insertColor(int index,const QColor & color,const QString & name)223 void Palette::insertColor(int index, const QColor& color, const QString &name)
224 {
225 if(_readonly)
226 return;
227
228 _colors.insert(index, PaletteColor(color, name));
229 _modified = true;
230 emit colorsChanged();
231 }
232
appendColor(const QColor & color,const QString & name)233 void Palette::appendColor(const QColor &color, const QString &name)
234 {
235 if(_readonly)
236 return;
237
238 _colors.append(PaletteColor(color, name));
239 _modified = true;
240 emit colorsChanged();
241 }
242
243 /**
244 * Size of the palette is decreased by one.
245 * @param index color index
246 * @pre 0 <= index < count()
247 */
removeColor(int index)248 void Palette::removeColor(int index)
249 {
250 if(_readonly)
251 return;
252
253 _colors.removeAt(index);
254 _modified = true;
255 emit colorsChanged();
256 }
257
258