1 /***************************************************************************
2 qgscolorscheme.cpp
3 -------------------
4 begin : July 2014
5 copyright : (C) 2014 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9 /***************************************************************************
10 * *
11 * This program 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 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
18 #include "qgscolorscheme.h"
19 #include "qgscolorschemeregistry.h"
20
21 #include "qgsproject.h"
22 #include "qgssymbollayerutils.h"
23 #include "qgsapplication.h"
24 #include "qgssettings.h"
25
26 #include <QDir>
27 #include <QRegularExpression>
28 #include <QTextStream>
29
setColors(const QgsNamedColorList & colors,const QString & context,const QColor & baseColor)30 bool QgsColorScheme::setColors( const QgsNamedColorList &colors, const QString &context, const QColor &baseColor )
31 {
32 //base implementation does nothing
33 Q_UNUSED( colors )
34 Q_UNUSED( context )
35 Q_UNUSED( baseColor )
36 return false;
37 }
38
39
40 //
41 // QgsRecentColorScheme
42 //
43
fetchColors(const QString & context,const QColor & baseColor)44 QgsNamedColorList QgsRecentColorScheme::fetchColors( const QString &context, const QColor &baseColor )
45 {
46 Q_UNUSED( context )
47 Q_UNUSED( baseColor )
48
49 //fetch recent colors
50 const QgsSettings settings;
51 const QList< QVariant > recentColorVariants = settings.value( QStringLiteral( "colors/recent" ) ).toList();
52
53 //generate list from recent colors
54 QgsNamedColorList colorList;
55 const auto constRecentColorVariants = recentColorVariants;
56 for ( const QVariant &color : constRecentColorVariants )
57 {
58 colorList.append( qMakePair( color.value<QColor>(), QgsSymbolLayerUtils::colorToName( color.value<QColor>() ) ) );
59 }
60 return colorList;
61 }
62
clone() const63 QgsRecentColorScheme *QgsRecentColorScheme::clone() const
64 {
65 return new QgsRecentColorScheme();
66 }
67
addRecentColor(const QColor & color)68 void QgsRecentColorScheme::addRecentColor( const QColor &color )
69 {
70 if ( !color.isValid() )
71 {
72 return;
73 }
74
75 //strip alpha from color
76 QColor opaqueColor = color;
77 opaqueColor.setAlpha( 255 );
78
79 QgsSettings settings;
80 QList< QVariant > recentColorVariants = settings.value( QStringLiteral( "colors/recent" ) ).toList();
81
82 //remove colors by name
83 for ( int colorIdx = recentColorVariants.length() - 1; colorIdx >= 0; --colorIdx )
84 {
85 if ( ( recentColorVariants.at( colorIdx ).value<QColor>() ).name() == opaqueColor.name() )
86 {
87 recentColorVariants.removeAt( colorIdx );
88 }
89 }
90
91 //add color
92 const QVariant colorVariant = QVariant( opaqueColor );
93 recentColorVariants.prepend( colorVariant );
94
95 //trim to 20 colors
96 while ( recentColorVariants.count() > 20 )
97 {
98 recentColorVariants.pop_back();
99 }
100
101 settings.setValue( QStringLiteral( "colors/recent" ), recentColorVariants );
102 }
103
lastUsedColor()104 QColor QgsRecentColorScheme::lastUsedColor()
105 {
106 //fetch recent colors
107 const QgsSettings settings;
108 const QList< QVariant > recentColorVariants = settings.value( QStringLiteral( "colors/recent" ) ).toList();
109
110 if ( recentColorVariants.isEmpty() )
111 return QColor();
112
113 return recentColorVariants.at( 0 ).value<QColor>();
114 }
115
fetchColors(const QString & context,const QColor & baseColor)116 QgsNamedColorList QgsCustomColorScheme::fetchColors( const QString &context, const QColor &baseColor )
117 {
118 Q_UNUSED( context )
119 Q_UNUSED( baseColor )
120
121 //fetch predefined custom colors
122 QgsNamedColorList colorList;
123 const QgsSettings settings;
124
125 //check if settings contains custom palette
126 if ( !settings.contains( QStringLiteral( "/colors/palettecolors" ) ) )
127 {
128 //no custom palette, return default colors
129 colorList.append( qMakePair( QColor( 0, 0, 0 ), QString() ) );
130 colorList.append( qMakePair( QColor( 255, 255, 255 ), QString() ) );
131 colorList.append( qMakePair( QColor( 166, 206, 227 ), QString() ) );
132 colorList.append( qMakePair( QColor( 31, 120, 180 ), QString() ) );
133 colorList.append( qMakePair( QColor( 178, 223, 138 ), QString() ) );
134 colorList.append( qMakePair( QColor( 51, 160, 44 ), QString() ) );
135 colorList.append( qMakePair( QColor( 251, 154, 153 ), QString() ) );
136 colorList.append( qMakePair( QColor( 227, 26, 28 ), QString() ) );
137 colorList.append( qMakePair( QColor( 253, 191, 111 ), QString() ) );
138 colorList.append( qMakePair( QColor( 255, 127, 0 ), QString() ) );
139
140 return colorList;
141 }
142
143 QList< QVariant > customColorVariants = settings.value( QStringLiteral( "colors/palettecolors" ) ).toList();
144 const QList< QVariant > customColorLabels = settings.value( QStringLiteral( "colors/palettelabels" ) ).toList();
145
146 //generate list from custom colors
147 int colorIndex = 0;
148 for ( QList< QVariant >::iterator it = customColorVariants.begin();
149 it != customColorVariants.end(); ++it )
150 {
151 const QColor color = ( *it ).value<QColor>();
152 QString label;
153 if ( customColorLabels.length() > colorIndex )
154 {
155 label = customColorLabels.at( colorIndex ).toString();
156 }
157
158 colorList.append( qMakePair( color, label ) );
159 colorIndex++;
160 }
161
162 return colorList;
163 }
164
setColors(const QgsNamedColorList & colors,const QString & context,const QColor & baseColor)165 bool QgsCustomColorScheme::setColors( const QgsNamedColorList &colors, const QString &context, const QColor &baseColor )
166 {
167 Q_UNUSED( context )
168 Q_UNUSED( baseColor )
169
170 // save colors to settings
171 QgsSettings settings;
172 QList< QVariant > customColors;
173 QList< QVariant > customColorLabels;
174
175 QgsNamedColorList::const_iterator colorIt = colors.constBegin();
176 for ( ; colorIt != colors.constEnd(); ++colorIt )
177 {
178 const QVariant color = ( *colorIt ).first;
179 const QVariant label = ( *colorIt ).second;
180 customColors.append( color );
181 customColorLabels.append( label );
182 }
183 settings.setValue( QStringLiteral( "colors/palettecolors" ), customColors );
184 settings.setValue( QStringLiteral( "colors/palettelabels" ), customColorLabels );
185 return true;
186 }
187
clone() const188 QgsCustomColorScheme *QgsCustomColorScheme::clone() const
189 {
190 return new QgsCustomColorScheme();
191 }
192
193
fetchColors(const QString & context,const QColor & baseColor)194 QgsNamedColorList QgsProjectColorScheme::fetchColors( const QString &context, const QColor &baseColor )
195 {
196 Q_UNUSED( context )
197 Q_UNUSED( baseColor )
198
199 QgsNamedColorList colorList;
200
201 QStringList colorStrings = QgsProject::instance()->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Colors" ) );
202 const QStringList colorLabels = QgsProject::instance()->readListEntry( QStringLiteral( "Palette" ), QStringLiteral( "/Labels" ) );
203
204 //generate list from custom colors
205 int colorIndex = 0;
206 for ( QStringList::iterator it = colorStrings.begin();
207 it != colorStrings.end(); ++it )
208 {
209 const QColor color = QgsSymbolLayerUtils::decodeColor( *it );
210 QString label;
211 if ( colorLabels.length() > colorIndex )
212 {
213 label = colorLabels.at( colorIndex );
214 }
215
216 colorList.append( qMakePair( color, label ) );
217 colorIndex++;
218 }
219
220 return colorList;
221 }
222
setColors(const QgsNamedColorList & colors,const QString & context,const QColor & baseColor)223 bool QgsProjectColorScheme::setColors( const QgsNamedColorList &colors, const QString &context, const QColor &baseColor )
224 {
225 Q_UNUSED( context )
226 Q_UNUSED( baseColor )
227 QgsProject::instance()->setProjectColors( colors );
228 return true;
229 }
230
clone() const231 QgsProjectColorScheme *QgsProjectColorScheme::clone() const
232 {
233 return new QgsProjectColorScheme();
234 }
235
236
237 //
238 // QgsGplColorScheme
239 //
240
fetchColors(const QString & context,const QColor & baseColor)241 QgsNamedColorList QgsGplColorScheme::fetchColors( const QString &context, const QColor &baseColor )
242 {
243 Q_UNUSED( context )
244 Q_UNUSED( baseColor )
245
246 const QString sourceFilePath = gplFilePath();
247 if ( sourceFilePath.isEmpty() )
248 {
249 QgsNamedColorList noColors;
250 return noColors;
251 }
252
253 bool ok;
254 QString name;
255 QFile sourceFile( sourceFilePath );
256 return QgsSymbolLayerUtils::importColorsFromGpl( sourceFile, ok, name );
257 }
258
setColors(const QgsNamedColorList & colors,const QString & context,const QColor & baseColor)259 bool QgsGplColorScheme::setColors( const QgsNamedColorList &colors, const QString &context, const QColor &baseColor )
260 {
261 Q_UNUSED( context )
262 Q_UNUSED( baseColor )
263
264 const QString destFilePath = gplFilePath();
265 if ( destFilePath.isEmpty() )
266 {
267 return false;
268 }
269
270 QFile destFile( destFilePath );
271 if ( QgsSymbolLayerUtils::saveColorsToGpl( destFile, schemeName(), colors ) )
272 {
273 if ( QgsApplication::colorSchemeRegistry()->randomStyleColorScheme() == this )
274 {
275 // force a re-generation of the random style color list, since the color list has changed
276 QgsApplication::colorSchemeRegistry()->setRandomStyleColorScheme( this );
277 }
278 return true;
279 }
280 else
281 {
282 return false;
283 }
284 }
285
286
287 //
288 // QgsUserColorScheme
289 //
290
QgsUserColorScheme(const QString & filename)291 QgsUserColorScheme::QgsUserColorScheme( const QString &filename )
292 : mFilename( filename )
293 {
294 QFile sourceFile( gplFilePath() );
295
296 //read in name
297 if ( sourceFile.open( QIODevice::ReadOnly ) )
298 {
299 QTextStream in( &sourceFile );
300
301 //find name line
302 QString line;
303 while ( !in.atEnd() && !line.startsWith( QLatin1String( "Name:" ) ) )
304 {
305 line = in.readLine();
306 }
307 if ( !in.atEnd() )
308 {
309 const QRegularExpression rx( "Name:\\s*(\\S.*)$" );
310 const QRegularExpressionMatch match = rx.match( line );
311 if ( match.hasMatch() )
312 {
313 mName = match.captured( 1 );
314 }
315 }
316 }
317 if ( mName.isEmpty() )
318 {
319 mName = mFilename;
320 }
321
322 // we consider this scheme writable if the user has permission, OR
323 // if it DOESN'T already exist (since new schemes are only created when
324 // first written to)
325 const QFileInfo sourceFileInfo( gplFilePath() );
326 mEditable = !sourceFileInfo.exists() || sourceFileInfo.isWritable();
327 }
328
schemeName() const329 QString QgsUserColorScheme::schemeName() const
330 {
331 return mName;
332 }
333
clone() const334 QgsUserColorScheme *QgsUserColorScheme::clone() const
335 {
336 return new QgsUserColorScheme( mFilename );
337 }
338
flags() const339 QgsColorScheme::SchemeFlags QgsUserColorScheme::flags() const
340 {
341 QgsColorScheme::SchemeFlags f = QgsGplColorScheme::flags();
342
343 const QgsSettings s;
344 const QStringList showInMenuSchemes = s.value( QStringLiteral( "/colors/showInMenuList" ) ).toStringList();
345
346 if ( showInMenuSchemes.contains( mName ) )
347 {
348 f |= QgsColorScheme::ShowInColorButtonMenu;
349 }
350
351 return f;
352 }
353
erase()354 bool QgsUserColorScheme::erase()
355 {
356 const QString filePath = gplFilePath();
357 if ( filePath.isEmpty() )
358 {
359 return false;
360 }
361
362 // if file does not exist, nothing to do on the disk, so we can consider erasing done
363 if ( ! QFile::exists( filePath ) )
364 {
365 return true;
366 }
367
368 //try to erase gpl file
369 return QFile::remove( filePath );
370 }
371
setShowSchemeInMenu(bool show)372 void QgsUserColorScheme::setShowSchemeInMenu( bool show )
373 {
374 QgsSettings s;
375 QStringList showInMenuSchemes = s.value( QStringLiteral( "/colors/showInMenuList" ) ).toStringList();
376
377 if ( show && !showInMenuSchemes.contains( mName ) )
378 {
379 showInMenuSchemes << mName;
380 }
381 else if ( !show && showInMenuSchemes.contains( mName ) )
382 {
383 showInMenuSchemes.removeAll( mName );
384 }
385
386 s.setValue( QStringLiteral( "/colors/showInMenuList" ), showInMenuSchemes );
387 }
388
gplFilePath()389 QString QgsUserColorScheme::gplFilePath()
390 {
391 const QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
392
393 const QDir localDir;
394 if ( !localDir.mkpath( palettesDir ) )
395 {
396 return QString();
397 }
398
399 return QDir( palettesDir ).filePath( mFilename );
400 }
401