Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsconditionalstyle.cpp
3 : : ---------------------
4 : : begin : August 2015
5 : : copyright : (C) 2015 by Nathan Woodrow
6 : : email : woodrow dot nathan at gmail dot com
7 : : ***************************************************************************
8 : : * *
9 : : * This program is free software; you can redistribute it and/or modify *
10 : : * it under the terms of the GNU General Public License as published by *
11 : : * the Free Software Foundation; either version 2 of the License, or *
12 : : * (at your option) any later version. *
13 : : * *
14 : : ***************************************************************************/
15 : : #include <QPainter>
16 : :
17 : : #include "qgsconditionalstyle.h"
18 : : #include "qgsexpression.h"
19 : : #include "qgsfontutils.h"
20 : : #include "qgssymbollayerutils.h"
21 : : #include "qgsmarkersymbollayer.h"
22 : :
23 : 78 : QgsConditionalLayerStyles::QgsConditionalLayerStyles( QObject *parent )
24 : 78 : : QObject( parent )
25 : 234 : {}
26 : :
27 : 0 : QgsConditionalStyles QgsConditionalLayerStyles::rowStyles() const
28 : : {
29 : 0 : return mRowStyles;
30 : : }
31 : :
32 : 0 : void QgsConditionalLayerStyles::setRowStyles( const QgsConditionalStyles &styles )
33 : : {
34 : 0 : if ( styles == mRowStyles )
35 : 0 : return;
36 : :
37 : 0 : mRowStyles = styles;
38 : 0 : emit changed();
39 : 0 : }
40 : :
41 : 0 : void QgsConditionalLayerStyles::setFieldStyles( const QString &fieldName, const QList<QgsConditionalStyle> &styles )
42 : : {
43 : 0 : if ( mFieldStyles.value( fieldName ) == styles )
44 : 0 : return;
45 : :
46 : 0 : mFieldStyles.insert( fieldName, styles );
47 : 0 : emit changed();
48 : 0 : }
49 : :
50 : 0 : QList<QgsConditionalStyle> QgsConditionalLayerStyles::fieldStyles( const QString &fieldName ) const
51 : : {
52 : 0 : return mFieldStyles.value( fieldName );
53 : : }
54 : :
55 : 0 : bool QgsConditionalLayerStyles::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
56 : : {
57 : 0 : QDomElement stylesel = doc.createElement( QStringLiteral( "conditionalstyles" ) );
58 : 0 : QDomElement rowel = doc.createElement( QStringLiteral( "rowstyles" ) );
59 : 0 : const auto constMRowStyles = mRowStyles;
60 : 0 : for ( const QgsConditionalStyle &style : constMRowStyles )
61 : : {
62 : 0 : style.writeXml( rowel, doc, context );
63 : : }
64 : :
65 : 0 : stylesel.appendChild( rowel );
66 : :
67 : 0 : QDomElement fieldsel = doc.createElement( QStringLiteral( "fieldstyles" ) );
68 : 0 : QHash<QString, QgsConditionalStyles>::const_iterator it = mFieldStyles.constBegin();
69 : 0 : for ( ; it != mFieldStyles.constEnd(); ++it )
70 : : {
71 : 0 : QDomElement fieldel = doc.createElement( QStringLiteral( "fieldstyle" ) );
72 : 0 : fieldel.setAttribute( QStringLiteral( "fieldname" ), it.key() );
73 : 0 : QgsConditionalStyles styles = it.value();
74 : 0 : const auto constStyles = styles;
75 : 0 : for ( const QgsConditionalStyle &style : constStyles )
76 : : {
77 : 0 : style.writeXml( fieldel, doc, context );
78 : : }
79 : 0 : fieldsel.appendChild( fieldel );
80 : 0 : }
81 : :
82 : 0 : stylesel.appendChild( fieldsel );
83 : :
84 : 0 : node.appendChild( stylesel );
85 : : return true;
86 : 0 : }
87 : :
88 : 0 : bool QgsConditionalLayerStyles::readXml( const QDomNode &node, const QgsReadWriteContext &context )
89 : : {
90 : 0 : QDomElement condel = node.firstChildElement( QStringLiteral( "conditionalstyles" ) );
91 : 0 : mRowStyles.clear();
92 : 0 : mFieldStyles.clear();
93 : 0 : QDomElement rowstylesel = condel.firstChildElement( QStringLiteral( "rowstyles" ) );
94 : 0 : QDomNodeList nodelist = rowstylesel.toElement().elementsByTagName( QStringLiteral( "style" ) );
95 : 0 : for ( int i = 0; i < nodelist.count(); i++ )
96 : : {
97 : 0 : QDomElement styleElm = nodelist.at( i ).toElement();
98 : 0 : QgsConditionalStyle style = QgsConditionalStyle();
99 : 0 : style.readXml( styleElm, context );
100 : 0 : mRowStyles.append( style );
101 : 0 : }
102 : :
103 : 0 : QDomElement fieldstylesel = condel.firstChildElement( QStringLiteral( "fieldstyles" ) );
104 : 0 : nodelist = fieldstylesel.toElement().elementsByTagName( QStringLiteral( "fieldstyle" ) );
105 : 0 : QList<QgsConditionalStyle> styles;
106 : 0 : for ( int i = 0; i < nodelist.count(); i++ )
107 : : {
108 : 0 : styles.clear();
109 : 0 : QDomElement fieldel = nodelist.at( i ).toElement();
110 : 0 : QString fieldName = fieldel.attribute( QStringLiteral( "fieldname" ) );
111 : 0 : QDomNodeList stylenodelist = fieldel.toElement().elementsByTagName( QStringLiteral( "style" ) );
112 : 0 : styles.reserve( stylenodelist.count() );
113 : 0 : for ( int j = 0; j < stylenodelist.count(); j++ )
114 : : {
115 : 0 : QDomElement styleElm = stylenodelist.at( j ).toElement();
116 : 0 : QgsConditionalStyle style = QgsConditionalStyle();
117 : 0 : style.readXml( styleElm, context );
118 : 0 : styles.append( style );
119 : 0 : }
120 : 0 : mFieldStyles.insert( fieldName, styles );
121 : 0 : }
122 : :
123 : : return true;
124 : 0 : }
125 : :
126 : 0 : QgsConditionalStyle::QgsConditionalStyle()
127 : 0 : {}
128 : :
129 : 0 : QgsConditionalStyle::QgsConditionalStyle( const QString &rule )
130 : : {
131 : 0 : setRule( rule );
132 : 0 : }
133 : :
134 : 0 : QgsConditionalStyle::QgsConditionalStyle( const QgsConditionalStyle &other )
135 : 0 : : mValid( other.mValid )
136 : 0 : , mName( other.mName )
137 : 0 : , mRule( other.mRule )
138 : 0 : , mFont( other.mFont )
139 : 0 : , mBackColor( other.mBackColor )
140 : 0 : , mTextColor( other.mTextColor )
141 : 0 : , mIcon( other.mIcon )
142 : : {
143 : 0 : if ( other.mSymbol )
144 : 0 : mSymbol.reset( other.mSymbol->clone() );
145 : 0 : }
146 : :
147 : 0 : QgsConditionalStyle &QgsConditionalStyle::operator=( const QgsConditionalStyle &other )
148 : : {
149 : 0 : mValid = other.mValid;
150 : 0 : mRule = other.mRule;
151 : 0 : mFont = other.mFont;
152 : 0 : mBackColor = other.mBackColor;
153 : 0 : mTextColor = other.mTextColor;
154 : 0 : mIcon = other.mIcon;
155 : 0 : mName = other.mName;
156 : 0 : if ( other.mSymbol )
157 : : {
158 : 0 : mSymbol.reset( other.mSymbol->clone() );
159 : 0 : }
160 : : else
161 : : {
162 : 0 : mSymbol.reset();
163 : : }
164 : 0 : return ( *this );
165 : : }
166 : :
167 : 0 : QString QgsConditionalStyle::displayText() const
168 : : {
169 : 0 : if ( name().isEmpty() )
170 : 0 : return rule();
171 : : else
172 : 0 : return QStringLiteral( "%1 \n%2" ).arg( name(), rule() );
173 : 0 : }
174 : :
175 : 0 : void QgsConditionalStyle::setSymbol( QgsSymbol *value )
176 : : {
177 : 0 : mValid = true;
178 : 0 : if ( value )
179 : : {
180 : 0 : mSymbol.reset( value->clone() );
181 : 0 : mIcon = QgsSymbolLayerUtils::symbolPreviewPixmap( mSymbol.get(), QSize( 16, 16 ) );
182 : 0 : }
183 : : else
184 : : {
185 : 0 : mSymbol.reset();
186 : : }
187 : 0 : }
188 : :
189 : 0 : bool QgsConditionalStyle::matches( const QVariant &value, QgsExpressionContext &context ) const
190 : : {
191 : 0 : QgsExpression exp( mRule );
192 : 0 : context.lastScope()->addVariable( QgsExpressionContextScope::StaticVariable( QStringLiteral( "value" ), value, true ) );
193 : 0 : return exp.evaluate( &context ).toBool();
194 : 0 : }
195 : :
196 : 0 : QPixmap QgsConditionalStyle::renderPreview( const QSize &size ) const
197 : : {
198 : 0 : QPixmap pixmap( size.isValid() ? size.width() : 64, size.isValid() ? size.height() : 32 );
199 : 0 : pixmap.fill( Qt::transparent );
200 : :
201 : 0 : QPainter painter( &pixmap );
202 : :
203 : 0 : if ( validBackgroundColor() )
204 : 0 : painter.setBrush( mBackColor );
205 : :
206 : 0 : QRect rect = QRect( 0, 0, pixmap.width(), pixmap.height() );
207 : 0 : painter.setPen( Qt::NoPen );
208 : 0 : painter.drawRect( rect );
209 : 0 : const QPixmap symbolIcon = icon();
210 : 0 : if ( !symbolIcon.isNull() )
211 : : {
212 : 0 : painter.drawPixmap( ( pixmap.width() / 3 - symbolIcon.width() ) / 2, ( pixmap.height() - symbolIcon.height() ) / 2, symbolIcon );
213 : 0 : }
214 : :
215 : 0 : if ( validTextColor() )
216 : 0 : painter.setPen( mTextColor );
217 : : else
218 : 0 : painter.setPen( Qt::black );
219 : :
220 : 0 : painter.setRenderHint( QPainter::Antialiasing );
221 : 0 : painter.setFont( font() );
222 : 0 : rect = QRect( pixmap.width() / 3, 0, 2 * pixmap.width() / 3, pixmap.height() );
223 : 0 : painter.drawText( rect, Qt::AlignCenter, QStringLiteral( "abc\n123" ) );
224 : 0 : painter.end();
225 : 0 : return pixmap;
226 : 0 : }
227 : :
228 : 0 : bool QgsConditionalStyle::validBackgroundColor() const
229 : : {
230 : 0 : return ( backgroundColor().isValid() && backgroundColor().alpha() != 0 );
231 : : }
232 : :
233 : 0 : bool QgsConditionalStyle::validTextColor() const
234 : : {
235 : 0 : return ( textColor().isValid() && textColor().alpha() != 0 );
236 : : }
237 : :
238 : 0 : QList<QgsConditionalStyle> QgsConditionalStyle::matchingConditionalStyles( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
239 : : {
240 : 0 : QList<QgsConditionalStyle> matchingstyles;
241 : 0 : const auto constStyles = styles;
242 : 0 : for ( const QgsConditionalStyle &style : constStyles )
243 : : {
244 : 0 : if ( style.matches( value, context ) )
245 : 0 : matchingstyles.append( style );
246 : : }
247 : 0 : return matchingstyles;
248 : 0 : }
249 : :
250 : 0 : QgsConditionalStyle QgsConditionalStyle::matchingConditionalStyle( const QList<QgsConditionalStyle> &styles, const QVariant &value, QgsExpressionContext &context )
251 : : {
252 : 0 : const auto constStyles = styles;
253 : 0 : for ( const QgsConditionalStyle &style : constStyles )
254 : : {
255 : 0 : if ( style.matches( value, context ) )
256 : 0 : return style;
257 : : }
258 : 0 : return QgsConditionalStyle();
259 : 0 : }
260 : :
261 : 0 : QgsConditionalStyle QgsConditionalStyle::compressStyles( const QList<QgsConditionalStyle> &styles )
262 : : {
263 : 0 : QgsConditionalStyle style;
264 : 0 : for ( const QgsConditionalStyle &s : styles )
265 : : {
266 : 0 : if ( !s.isValid() )
267 : 0 : continue;
268 : :
269 : 0 : style.setFont( s.font() );
270 : 0 : if ( s.backgroundColor().isValid() && s.backgroundColor().alpha() != 0 )
271 : 0 : style.setBackgroundColor( s.backgroundColor() );
272 : 0 : if ( s.textColor().isValid() && s.textColor().alpha() != 0 )
273 : 0 : style.setTextColor( s.textColor() );
274 : 0 : if ( auto *lSymbol = s.symbol() )
275 : 0 : style.setSymbol( lSymbol );
276 : : }
277 : 0 : return style;
278 : 0 : }
279 : :
280 : 0 : bool QgsConditionalStyle::writeXml( QDomNode &node, QDomDocument &doc, const QgsReadWriteContext &context ) const
281 : : {
282 : 0 : QDomElement stylesel = doc.createElement( QStringLiteral( "style" ) );
283 : 0 : stylesel.setAttribute( QStringLiteral( "rule" ), mRule );
284 : 0 : stylesel.setAttribute( QStringLiteral( "name" ), mName );
285 : 0 : if ( mBackColor.isValid() )
286 : : {
287 : 0 : stylesel.setAttribute( QStringLiteral( "background_color" ), mBackColor.name() );
288 : 0 : stylesel.setAttribute( QStringLiteral( "background_color_alpha" ), mBackColor.alpha() );
289 : 0 : }
290 : 0 : if ( mTextColor.isValid() )
291 : : {
292 : 0 : stylesel.setAttribute( QStringLiteral( "text_color" ), mTextColor.name() );
293 : 0 : stylesel.setAttribute( QStringLiteral( "text_color_alpha" ), mTextColor.alpha() );
294 : 0 : }
295 : 0 : QDomElement labelFontElem = QgsFontUtils::toXmlElement( mFont, doc, QStringLiteral( "font" ) );
296 : 0 : stylesel.appendChild( labelFontElem );
297 : 0 : if ( mSymbol )
298 : 0 : {
299 : 0 : QDomElement symbolElm = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "icon" ), mSymbol.get(), doc, context );
300 : 0 : stylesel.appendChild( symbolElm );
301 : 0 : }
302 : 0 : node.appendChild( stylesel );
303 : : return true;
304 : 0 : }
305 : :
306 : 0 : bool QgsConditionalStyle::operator==( const QgsConditionalStyle &other ) const
307 : : {
308 : 0 : return mValid == other.mValid
309 : 0 : && mName == other.mName
310 : 0 : && mRule == other.mRule
311 : 0 : && mFont == other.mFont
312 : 0 : && mBackColor == other.mBackColor
313 : 0 : && mTextColor == other.mTextColor
314 : 0 : && static_cast< bool >( mSymbol ) == static_cast< bool >( other.mSymbol )
315 : 0 : && ( ! mSymbol || QgsSymbolLayerUtils::symbolProperties( mSymbol.get() ) == QgsSymbolLayerUtils::symbolProperties( other.mSymbol.get() ) );
316 : 0 : }
317 : :
318 : 0 : bool QgsConditionalStyle::operator!=( const QgsConditionalStyle &other ) const
319 : : {
320 : 0 : return !( *this == other );
321 : : }
322 : :
323 : 0 : bool QgsConditionalStyle::readXml( const QDomNode &node, const QgsReadWriteContext &context )
324 : : {
325 : 0 : QDomElement styleElm = node.toElement();
326 : 0 : setRule( styleElm.attribute( QStringLiteral( "rule" ) ) );
327 : 0 : setName( styleElm.attribute( QStringLiteral( "name" ) ) );
328 : 0 : if ( styleElm.hasAttribute( QStringLiteral( "background_color" ) ) )
329 : : {
330 : 0 : QColor bColor = QColor( styleElm.attribute( QStringLiteral( "background_color" ) ) );
331 : 0 : if ( styleElm.hasAttribute( QStringLiteral( "background_color_alpha" ) ) )
332 : : {
333 : 0 : bColor.setAlpha( styleElm.attribute( QStringLiteral( "background_color_alpha" ) ).toInt() );
334 : 0 : }
335 : 0 : if ( bColor.alpha() == 0 )
336 : 0 : setBackgroundColor( QColor() );
337 : : else
338 : 0 : setBackgroundColor( bColor );
339 : 0 : }
340 : : else
341 : : {
342 : 0 : setBackgroundColor( QColor() );
343 : : }
344 : 0 : if ( styleElm.hasAttribute( QStringLiteral( "text_color" ) ) )
345 : : {
346 : 0 : QColor tColor = QColor( styleElm.attribute( QStringLiteral( "text_color" ) ) );
347 : 0 : if ( styleElm.hasAttribute( QStringLiteral( "text_color_alpha" ) ) )
348 : : {
349 : 0 : tColor.setAlpha( styleElm.attribute( QStringLiteral( "text_color_alpha" ) ).toInt() );
350 : 0 : }
351 : 0 : if ( tColor.alpha() == 0 )
352 : 0 : setTextColor( QColor() );
353 : : else
354 : 0 : setTextColor( tColor );
355 : 0 : }
356 : : else
357 : : {
358 : 0 : setTextColor( QColor() );
359 : : }
360 : 0 : QgsFontUtils::setFromXmlChildNode( mFont, styleElm, QStringLiteral( "font" ) );
361 : 0 : QDomElement symbolElm = styleElm.firstChildElement( QStringLiteral( "symbol" ) );
362 : 0 : if ( !symbolElm.isNull() )
363 : : {
364 : 0 : QgsSymbol *symbol = QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( symbolElm, context );
365 : 0 : setSymbol( symbol );
366 : 0 : }
367 : : return true;
368 : 0 : }
369 : :
|