QGIS API Documentation  2.8.2-Wien
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgscolorwidgets.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscolorwidgets.cpp - color selection widgets
3  ---------------------
4  begin : September 2014
5  copyright : (C) 2014 by Nyall Dawson
6  email : nyall dot dawson 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 
16 #include "qgscolorwidgets.h"
17 #include "qgsapplication.h"
18 #include "qgssymbollayerv2utils.h"
19 #include <QResizeEvent>
20 #include <QStyleOptionFrameV3>
21 #include <QPainter>
22 #include <QHBoxLayout>
23 #include <QSpinBox>
24 #include <QLineEdit>
25 #include <QFontMetrics>
26 #include <QToolButton>
27 #include <QMenu>
28 #include <QSettings>
29 #include <QDrag>
30 #include <cmath>
31 
32 
33 //
34 // QgsColorWidget
35 //
36 
37 QgsColorWidget::QgsColorWidget( QWidget* parent, const ColorComponent component )
38  : QWidget( parent )
39  , mCurrentColor( Qt::red )
40  , mComponent( component )
41  , mExplicitHue( 0 )
42 {
43  setAcceptDrops( true );
44 }
45 
47 {
48 
49 }
50 
52 {
53  return componentValue( mComponent );
54 }
55 
56 QPixmap QgsColorWidget::createDragIcon( const QColor &color )
57 {
58  //craft a pixmap for the drag icon
59  QPixmap pixmap( 50, 50 );
60  pixmap.fill( Qt::transparent );
61  QPainter painter;
62  painter.begin( &pixmap );
63  //start with a light gray background
64  painter.fillRect( QRect( 0, 0, 50, 50 ), QBrush( QColor( 200, 200, 200 ) ) );
65  //draw rect with white border, filled with current color
66  QColor pixmapColor = color;
67  pixmapColor.setAlpha( 255 );
68  painter.setBrush( QBrush( pixmapColor ) );
69  painter.setPen( QPen( Qt::white ) );
70  painter.drawRect( QRect( 1, 1, 47, 47 ) );
71  painter.end();
72  return pixmap;
73 }
74 
76 {
77  if ( !mCurrentColor.isValid() )
78  {
79  return -1;
80  }
81 
82  switch ( component )
83  {
85  return mCurrentColor.red();
87  return mCurrentColor.green();
89  return mCurrentColor.blue();
91  //hue is treated specially, to avoid -1 hues values from QColor for ambiguous hues
92  return hue();
94  return mCurrentColor.hsvSaturation();
96  return mCurrentColor.value();
98  return mCurrentColor.alpha();
99  default:
100  return -1;
101  }
102 }
103 
105 {
106  return componentRange( mComponent );
107 }
108 
110 {
111  if ( component == QgsColorWidget::Multiple )
112  {
113  //no component
114  return -1;
115  }
116 
117  if ( component == QgsColorWidget::Hue )
118  {
119  //hue ranges to 359
120  return 359;
121  }
122  else
123  {
124  //all other components range to 255
125  return 255;
126  }
127 }
128 
130 {
131  if ( mCurrentColor.hue() >= 0 )
132  {
133  return mCurrentColor.hue();
134  }
135  else
136  {
137  return mExplicitHue;
138  }
139 }
140 
141 void QgsColorWidget::alterColor( QColor& color, const QgsColorWidget::ColorComponent component, const int newValue ) const
142 {
143  int h, s, v, a;
144  color.getHsv( &h, &s, &v, &a );
145 
146  //clip value to sensible range
147  int clippedValue = qMin( qMax( 0, newValue ), componentRange( component ) );
148 
149  switch ( component )
150  {
151  case QgsColorWidget::Red:
152  color.setRed( clippedValue );
153  return;
155  color.setGreen( clippedValue );
156  return;
158  color.setBlue( clippedValue );
159  return;
160  case QgsColorWidget::Hue:
161  color.setHsv( clippedValue, s, v, a );
162  return;
164  color.setHsv( h, clippedValue, v, a );
165  return;
167  color.setHsv( h, s, clippedValue, a );
168  return;
170  color.setAlpha( clippedValue );
171  return;
172  default:
173  return;
174  }
175 }
176 
178 {
179  static QPixmap transpBkgrd;
180 
181  if ( transpBkgrd.isNull() )
182  transpBkgrd = QgsApplication::getThemePixmap( "/transp-background_8x8.png" );
183 
184  return transpBkgrd;
185 }
186 
187 void QgsColorWidget::dragEnterEvent( QDragEnterEvent *e )
188 {
189  //is dragged data valid color data?
190  bool hasAlpha;
191  QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
192 
193  if ( mimeColor.isValid() )
194  {
195  //if so, we accept the drag
196  e->acceptProposedAction();
197  }
198 }
199 
200 void QgsColorWidget::dropEvent( QDropEvent *e )
201 {
202  //is dropped data valid color data?
203  bool hasAlpha = false;
204  QColor mimeColor = QgsSymbolLayerV2Utils::colorFromMimeData( e->mimeData(), hasAlpha );
205 
206  if ( mimeColor.isValid() )
207  {
208  //accept drop and set new color
209  e->acceptProposedAction();
210 
211  if ( !hasAlpha )
212  {
213  //mime color has no explicit alpha component, so keep existing alpha
214  mimeColor.setAlpha( mCurrentColor.alpha() );
215  }
216 
217  setColor( mimeColor );
218  emit colorChanged( mCurrentColor );
219  }
220 
221  //could not get color from mime data
222 }
223 
224 QColor QgsColorWidget::color() const
225 {
226  return mCurrentColor;
227 }
228 
230 {
231  if ( component == mComponent )
232  {
233  return;
234  }
235 
237  update();
238 }
239 
240 void QgsColorWidget::setComponentValue( const int value )
241 {
243  {
244  return;
245  }
246 
247  //clip value to valid range
248  int valueClipped = qMin( value, componentRange() );
249  valueClipped = qMax( valueClipped, 0 );
250 
251  int r, g, b, a;
252  mCurrentColor.getRgb( &r, &g, &b, &a );
253  int h, s, v;
254  mCurrentColor.getHsv( &h, &s, &v );
255  //overwrite hue with explicit hue if required
256  h = hue();
257 
258  switch ( mComponent )
259  {
260  case QgsColorWidget::Red:
261  if ( r == valueClipped )
262  {
263  return;
264  }
265  mCurrentColor.setRed( valueClipped );
266  break;
268  if ( g == valueClipped )
269  {
270  return;
271  }
272  mCurrentColor.setGreen( valueClipped );
273  break;
275  if ( b == valueClipped )
276  {
277  return;
278  }
279  mCurrentColor.setBlue( valueClipped );
280  break;
281  case QgsColorWidget::Hue:
282  if ( h == valueClipped )
283  {
284  return;
285  }
286  mCurrentColor.setHsv( valueClipped, s, v, a );
287  break;
289  if ( s == valueClipped )
290  {
291  return;
292  }
293  mCurrentColor.setHsv( h, valueClipped, v, a );
294  break;
296  if ( v == valueClipped )
297  {
298  return;
299  }
300  mCurrentColor.setHsv( h, s, valueClipped, a );
301  break;
303  if ( a == valueClipped )
304  {
305  return;
306  }
307  mCurrentColor.setAlpha( valueClipped );
308  break;
309  default:
310  return;
311  }
312 
313  //update recorded hue
314  if ( mCurrentColor.hue() >= 0 )
315  {
316  mExplicitHue = mCurrentColor.hue();
317  }
318 
319  update();
320 }
321 
322 void QgsColorWidget::setColor( const QColor &color, const bool emitSignals )
323 {
324  if ( color == mCurrentColor )
325  {
326  return;
327  }
328 
330 
331  //update recorded hue
332  if ( color.hue() >= 0 )
333  {
334  mExplicitHue = color.hue();
335  }
336 
337  if ( emitSignals )
338  {
339  emit colorChanged( mCurrentColor );
340  }
341 
342  update();
343 }
344 
345 
346 //
347 // QgsColorWheel
348 //
349 
351  : QgsColorWidget( parent )
352  , mMargin( 4 )
353  , mWheelThickness( 18 )
354  , mClickedPart( QgsColorWheel::None )
355  , mWheelImage( 0 )
356  , mTriangleImage( 0 )
357  , mWidgetImage( 0 )
358  , mWheelDirty( true )
359  , mTriangleDirty( true )
360 {
361  //create wheel hue brush - only do this once
362  QConicalGradient wheelGradient = QConicalGradient( 0, 0, 0 );
363  int wheelStops = 20;
364  QColor gradColor = QColor::fromHsvF( 1.0, 1.0, 1.0 );
365  for ( int pos = 0; pos <= wheelStops; ++pos )
366  {
367  double relativePos = ( double )pos / wheelStops;
368  gradColor.setHsvF( relativePos, 1, 1 );
369  wheelGradient.setColorAt( relativePos, gradColor );
370  }
371  mWheelBrush = QBrush( wheelGradient );
372 }
373 
375 {
376  delete mWheelImage;
377  delete mTriangleImage;
378  delete mWidgetImage;
379 }
380 
381 void QgsColorWheel::paintEvent( QPaintEvent *event )
382 {
383  Q_UNUSED( event );
384  QPainter painter( this );
385 
386  //draw a frame
387  QStyleOptionFrameV3 option = QStyleOptionFrameV3();
388  option.initFrom( this );
389  option.state = this->hasFocus() ? QStyle::State_Active : QStyle::State_None;
390  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
391 
392  if ( !mWidgetImage || !mWheelImage || !mTriangleImage )
393  {
394  createImages( size() );
395  }
396 
397  //draw everything in an image
398  mWidgetImage->fill( Qt::transparent );
399  QPainter imagePainter( mWidgetImage );
400  imagePainter.setRenderHint( QPainter::Antialiasing );
401 
402  if ( mWheelDirty )
403  {
404  //need to redraw the wheel image
405  createWheel();
406  }
407 
408  //draw wheel centered on widget
409  QPointF center = QPointF( width() / 2.0, height() / 2.0 );
410  imagePainter.drawImage( QPointF( center.x() - ( mWheelImage->width() / 2.0 ), center.y() - ( mWheelImage->height() / 2.0 ) ), *mWheelImage );
411 
412  //draw hue marker
413  int h = hue();
414  double length = mWheelImage->width() / 2.0;
415  QLineF hueMarkerLine = QLineF( center.x(), center.y(), center.x() + length, center.y() );
416  hueMarkerLine.setAngle( h );
417  imagePainter.save();
418  //use sourceIn mode for nicer antialiasing
419  imagePainter.setCompositionMode( QPainter::CompositionMode_SourceIn );
420  QPen pen;
421  pen.setWidth( 2 );
422  //adapt pen color for hue
423  pen.setColor( h > 20 && h < 200 ? Qt::black : Qt::white );
424  imagePainter.setPen( pen );
425  imagePainter.drawLine( hueMarkerLine );
426  imagePainter.restore();
427 
428  //draw triangle
429  if ( mTriangleDirty )
430  {
431  createTriangle();
432  }
433  imagePainter.drawImage( QPointF( center.x() - ( mWheelImage->width() / 2.0 ), center.y() - ( mWheelImage->height() / 2.0 ) ), *mTriangleImage );
434 
435  //draw current color marker
436  double triangleRadius = length - mWheelThickness - 1;
437 
438  //adapted from equations at https://github.com/timjb/colortriangle/blob/master/colortriangle.js by Tim Baumann
439  double lightness = mCurrentColor.lightnessF();
440  double hueRadians = ( h * M_PI / 180.0 );
441  double hx = cos( hueRadians ) * triangleRadius;
442  double hy = -sin( hueRadians ) * triangleRadius;
443  double sx = -cos( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
444  double sy = -sin( -hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
445  double vx = -cos( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
446  double vy = sin( hueRadians + ( M_PI / 3.0 ) ) * triangleRadius;
447  double mx = ( sx + vx ) / 2.0;
448  double my = ( sy + vy ) / 2.0;
449 
450  double a = ( 1 - 2.0 * fabs( lightness - 0.5 ) ) * mCurrentColor.hslSaturationF();
451  double x = sx + ( vx - sx ) * lightness + ( hx - mx ) * a;
452  double y = sy + ( vy - sy ) * lightness + ( hy - my ) * a;
453 
454  //adapt pen color for lightness
455  pen.setColor( lightness > 0.7 ? Qt::black : Qt::white );
456  imagePainter.setPen( pen );
457  imagePainter.setBrush( Qt::NoBrush );
458  imagePainter.drawEllipse( QPointF( x + center.x(), y + center.y() ), 4.0, 4.0 );
459  imagePainter.end();
460 
461  //draw image onto widget
462  painter.drawImage( QPoint( 0, 0 ), *mWidgetImage );
463  painter.end();
464 }
465 
466 void QgsColorWheel::setColor( const QColor &color, const bool emitSignals )
467 {
468  if ( color.hue() >= 0 && color.hue() != hue() )
469  {
470  //hue has changed, need to redraw the triangle
471  mTriangleDirty = true;
472  }
473 
474  QgsColorWidget::setColor( color, emitSignals );
475 }
476 
477 void QgsColorWheel::createImages( const QSizeF size )
478 {
479  double wheelSize = qMin( size.width(), size.height() ) - mMargin * 2.0;
480  mWheelThickness = wheelSize / 15.0;
481 
482  //recreate cache images at correct size
483  delete mWheelImage;
484  mWheelImage = new QImage( wheelSize, wheelSize, QImage::Format_ARGB32 );
485  delete mTriangleImage;
486  mTriangleImage = new QImage( wheelSize, wheelSize, QImage::Format_ARGB32 );
487  delete mWidgetImage;
488  mWidgetImage = new QImage( size.width(), size.height(), QImage::Format_ARGB32 );
489 
490  //trigger a redraw for the images
491  mWheelDirty = true;
492  mTriangleDirty = true;
493 }
494 
495 void QgsColorWheel::resizeEvent( QResizeEvent *event )
496 {
497  //recreate images for new size
498  createImages( event->size() );
500 }
501 
502 void QgsColorWheel::setColorFromPos( const QPointF pos )
503 {
504  QPointF center = QPointF( width() / 2.0, height() / 2.0 );
505  //line from center to mouse position
506  QLineF line = QLineF( center.x(), center.y(), pos.x(), pos.y() );
507 
508  QColor newColor = QColor();
509 
510  int h, s, l, alpha;
511  mCurrentColor.getHsl( &h, &s, &l, &alpha );
512  //override hue with explicit hue, so we don't get -1 values from QColor for hue
513  h = hue();
514 
515  if ( mClickedPart == QgsColorWheel::Triangle )
516  {
517  //adapted from equations at https://github.com/timjb/colortriangle/blob/master/colortriangle.js by Tim Baumann
518 
519  //position of event relative to triangle center
520  double x = pos.x() - center.x();
521  double y = pos.y() - center.y();
522 
523  double eventAngleRadians = line.angle() * M_PI / 180.0;
524  double hueRadians = h * M_PI / 180.0;
525  double rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI );
526  double rad1 = fmod( rad0, (( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 );
527  double length = mWheelImage->width() / 2.0;
528  double triangleLength = length - mWheelThickness - 1;
529 
530  double a = 0.5 * triangleLength;
531  double b = tan( rad1 ) * a;
532  double r = sqrt( x * x + y * y );
533  double maxR = sqrt( a * a + b * b );
534 
535  if ( r > maxR )
536  {
537  double dx = tan( rad1 ) * r;
538  double rad2 = atan( dx / maxR );
539  rad2 = qMin( rad2, M_PI / 3.0 );
540  rad2 = qMax( rad2, -M_PI / 3.0 );
541  eventAngleRadians += rad2 - rad1;
542  rad0 = fmod( eventAngleRadians + 2.0 * M_PI - hueRadians, 2.0 * M_PI );
543  rad1 = fmod( rad0, (( 2.0 / 3.0 ) * M_PI ) ) - ( M_PI / 3.0 );
544  b = tan( rad1 ) * a;
545  r = sqrt( a * a + b * b );
546  }
547 
548  double triangleSideLength = sqrt( 3.0 ) * triangleLength;
549  double newL = (( -sin( rad0 ) * r ) / triangleSideLength ) + 0.5;
550  double widthShare = 1.0 - ( fabs( newL - 0.5 ) * 2.0 );
551  double newS = ((( cos( rad0 ) * r ) + ( triangleLength / 2.0 ) ) / ( 1.5 * triangleLength ) ) / widthShare;
552  s = qMin( qRound( qMax( 0.0, newS ) * 255.0 ), 255 );
553  l = qMin( qRound( qMax( 0.0, newL ) * 255.0 ), 255 );
554  newColor = QColor::fromHsl( h, s, l );
555  //explicitly set the hue again, so that it's exact
556  newColor.setHsv( h, newColor.hsvSaturation(), newColor.value(), alpha );
557  }
558  else if ( mClickedPart == QgsColorWheel::Wheel )
559  {
560  //use hue angle
561  s = mCurrentColor.hsvSaturation();
562  int v = mCurrentColor.value();
563  int newHue = line.angle();
564  newColor = QColor::fromHsv( newHue, s, v, alpha );
565  //hue has changed, need to redraw triangle
566  mTriangleDirty = true;
567  }
568 
569  if ( newColor.isValid() && newColor != mCurrentColor )
570  {
571  //color has changed
572  mCurrentColor = QColor( newColor );
573 
574  if ( mCurrentColor.hue() >= 0 )
575  {
576  //color has a valid hue, so update the QgsColorWidget's explicit hue
577  mExplicitHue = mCurrentColor.hue();
578  }
579 
580  update();
581  emit colorChanged( mCurrentColor );
582  }
583 }
584 
585 void QgsColorWheel::mouseMoveEvent( QMouseEvent *event )
586 {
587  setColorFromPos( event->posF() );
588 }
589 
590 void QgsColorWheel::mousePressEvent( QMouseEvent *event )
591 {
592  //calculate where the event occurred -- on the wheel or inside the triangle?
593 
594  //create a line from the widget's center to the event
595  QLineF line = QLineF( width() / 2.0, height() / 2.0, event->pos().x(), event->pos().y() );
596 
597  double innerLength = mWheelImage->width() / 2.0 - mWheelThickness;
598  if ( line.length() < innerLength )
599  {
600  mClickedPart = QgsColorWheel::Triangle;
601  }
602  else
603  {
604  mClickedPart = QgsColorWheel::Wheel;
605  }
606  setColorFromPos( event->posF() );
607 }
608 
609 void QgsColorWheel::mouseReleaseEvent( QMouseEvent *event )
610 {
611  Q_UNUSED( event );
612  mClickedPart = QgsColorWheel::None;
613 }
614 
615 void QgsColorWheel::createWheel()
616 {
617  if ( !mWheelImage )
618  {
619  return;
620  }
621 
622  int maxSize = qMin( mWheelImage->width(), mWheelImage->height() );
623  double wheelRadius = maxSize / 2.0;
624 
625  mWheelImage->fill( Qt::transparent );
626  QPainter p( mWheelImage );
627  p.setRenderHint( QPainter::Antialiasing );
628  p.setBrush( mWheelBrush );
629  p.setPen( Qt::NoPen );
630 
631  //draw hue wheel as a circle
632  p.translate( wheelRadius, wheelRadius );
633  p.drawEllipse( QPointF( 0.0, 0.0 ), wheelRadius, wheelRadius );
634 
635  //cut hole in center of circle to make a ring
636  p.setCompositionMode( QPainter::CompositionMode_DestinationOut );
637  p.setBrush( QBrush( Qt::black ) );
638  p.drawEllipse( QPointF( 0.0, 0.0 ), wheelRadius - mWheelThickness, wheelRadius - mWheelThickness );
639  p.end();
640 
641  mWheelDirty = false;
642 }
643 
644 void QgsColorWheel::createTriangle()
645 {
646  if ( !mWheelImage || !mTriangleImage )
647  {
648  return;
649  }
650 
651  QPointF center = QPointF( mWheelImage->width() / 2.0, mWheelImage->height() / 2.0 );
652  mTriangleImage->fill( Qt::transparent );
653 
654  QPainter imagePainter( mTriangleImage );
655  imagePainter.setRenderHint( QPainter::Antialiasing );
656 
657  int angle = hue();
658  double wheelRadius = mWheelImage->width() / 2.0;
659  double triangleRadius = wheelRadius - mWheelThickness - 1;
660 
661  //pure version of hue (at full saturation and value)
662  QColor pureColor = QColor::fromHsv( angle, 255, 255 );
663  //create copy of color but with 0 alpha
664  QColor alphaColor = QColor( pureColor );
665  alphaColor.setAlpha( 0 );
666 
667  //some rather ugly shortcuts to obtain corners and midpoints of triangle
668  QLineF line1 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() - triangleRadius * sin( M_PI / 3.0 ) );
669  QLineF line2 = QLineF( center.x(), center.y(), center.x() + triangleRadius, center.y() );
670  QLineF line3 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() + triangleRadius * sin( M_PI / 3.0 ) );
671  QLineF line4 = QLineF( center.x(), center.y(), center.x() - triangleRadius * cos( M_PI / 3.0 ), center.y() );
672  QLineF line5 = QLineF( center.x(), center.y(), ( line2.p2().x() + line1.p2().x() ) / 2.0, ( line2.p2().y() + line1.p2().y() ) / 2.0 );
673  line1.setAngle( line1.angle() + angle );
674  line2.setAngle( line2.angle() + angle );
675  line3.setAngle( line3.angle() + angle );
676  line4.setAngle( line4.angle() + angle );
677  line5.setAngle( line5.angle() + angle );
678  QPointF p1 = line1.p2();
679  QPointF p2 = line2.p2();
680  QPointF p3 = line3.p2();
681  QPointF p4 = line4.p2();
682  QPointF p5 = line5.p2();
683 
684  //inspired by Tim Baumann's work at https://github.com/timjb/colortriangle/blob/master/colortriangle.js
685  QLinearGradient colorGrad = QLinearGradient( p4.x(), p4.y(), p2.x(), p2.y() );
686  colorGrad.setColorAt( 0, alphaColor );
687  colorGrad.setColorAt( 1, pureColor );
688  QLinearGradient whiteGrad = QLinearGradient( p3.x(), p3.y(), p5.x(), p5.y() );
689  whiteGrad.setColorAt( 0, QColor( 255, 255, 255, 255 ) );
690  whiteGrad.setColorAt( 1, QColor( 255, 255, 255, 0 ) );
691 
692  QPolygonF triangle;
693  triangle << p2 << p1 << p3 << p2;
694  imagePainter.setPen( Qt::NoPen );
695  //start with a black triangle
696  imagePainter.setBrush( QBrush( Qt::black ) );
697  imagePainter.drawPolygon( triangle );
698  //draw a gradient from transparent to the pure color at the triangle's tip
699  imagePainter.setBrush( QBrush( colorGrad ) );
700  imagePainter.drawPolygon( triangle );
701  //draw a white gradient using additive composition mode
702  imagePainter.setCompositionMode( QPainter::CompositionMode_Plus );
703  imagePainter.setBrush( QBrush( whiteGrad ) );
704  imagePainter.drawPolygon( triangle );
705 
706  //above process results in some small artifacts on the edge of the triangle. Let's clear these up
707  //use source composition mode and draw an outline using a transparent pen
708  //this clears the edge pixels and leaves a nice smooth image
709  imagePainter.setCompositionMode( QPainter::CompositionMode_Source );
710  imagePainter.setBrush( Qt::NoBrush );
711  imagePainter.setPen( QPen( Qt::transparent ) );
712  imagePainter.drawPolygon( triangle );
713 
714  imagePainter.end();
715  mTriangleDirty = false;
716 }
717 
718 
719 
720 //
721 // QgsColorBox
722 //
723 
724 QgsColorBox::QgsColorBox( QWidget *parent, const ColorComponent component )
725  : QgsColorWidget( parent, component )
726  , mMargin( 2 )
727  , mBoxImage( 0 )
728  , mDirty( true )
729 {
730  setFocusPolicy( Qt::StrongFocus );
731  setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
732 
733  mBoxImage = new QImage( width() - mMargin * 2, height() - mMargin * 2, QImage::Format_RGB32 );
734 }
735 
737 {
738  delete mBoxImage;
739 }
740 
742 {
743  return QSize( 200, 200 );
744 }
745 
746 void QgsColorBox::paintEvent( QPaintEvent *event )
747 {
748  Q_UNUSED( event );
749  QPainter painter( this );
750 
751  QStyleOptionFrameV3 option;
752  option.initFrom( this );
753  option.state = hasFocus() ? QStyle::State_Active : QStyle::State_None;
754  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
755 
756  if ( mDirty )
757  {
758  createBox();
759  }
760 
761  //draw background image
762  painter.drawImage( QPoint( mMargin, mMargin ), *mBoxImage );
763 
764  //draw cross lines
765  double xPos = mMargin + ( width() - 2 * mMargin - 1 ) * ( double )xComponentValue() / ( double )valueRangeX();
766  double yPos = mMargin + ( height() - 2 * mMargin - 1 ) - ( height() - 2 * mMargin - 1 ) * ( double )yComponentValue() / ( double )valueRangeY();
767 
768  painter.setBrush( Qt::white );
769  painter.setPen( Qt::NoPen );
770 
771  painter.drawRect( xPos - 1, mMargin, 3, height() - 2 * mMargin - 1 );
772  painter.drawRect( mMargin, yPos - 1, width() - 2 * mMargin - 1, 3 );
773  painter.setPen( Qt::black );
774  painter.drawLine( xPos, mMargin, xPos, height() - mMargin - 1 );
775  painter.drawLine( mMargin, yPos, width() - mMargin - 1, yPos );
776 
777  painter.end();
778 }
779 
781 {
782  if ( component != mComponent )
783  {
784  //need to redraw
785  mDirty = true;
786  }
787  QgsColorWidget::setComponent( component );
788 }
789 
790 void QgsColorBox::setColor( const QColor &color, const bool emitSignals )
791 {
792  //check if we need to redraw the box image
793  if ( mComponent == QgsColorWidget::Red && mCurrentColor.red() != color.red() )
794  {
795  mDirty = true;
796  }
797  else if ( mComponent == QgsColorWidget::Green && mCurrentColor.green() != color.green() )
798  {
799  mDirty = true;
800  }
801  else if ( mComponent == QgsColorWidget::Blue && mCurrentColor.blue() != color.blue() )
802  {
803  mDirty = true;
804  }
805  else if ( mComponent == QgsColorWidget::Hue && color.hsvHue() >= 0 && hue() != color.hsvHue() )
806  {
807  mDirty = true;
808  }
809  else if ( mComponent == QgsColorWidget::Saturation && mCurrentColor.hsvSaturation() != color.hsvSaturation() )
810  {
811  mDirty = true;
812  }
813  else if ( mComponent == QgsColorWidget::Value && mCurrentColor.value() != color.value() )
814  {
815  mDirty = true;
816  }
817  QgsColorWidget::setColor( color, emitSignals );
818 }
819 
820 void QgsColorBox::resizeEvent( QResizeEvent *event )
821 {
822  mDirty = true;
823  delete mBoxImage;
824  mBoxImage = new QImage( event->size().width() - mMargin * 2, event->size().height() - mMargin * 2, QImage::Format_RGB32 );
826 }
827 
828 void QgsColorBox::mouseMoveEvent( QMouseEvent *event )
829 {
830  setColorFromPoint( event->pos() );
831 }
832 
833 void QgsColorBox::mousePressEvent( QMouseEvent *event )
834 {
835  setColorFromPoint( event->pos() );
836 }
837 
838 void QgsColorBox::createBox()
839 {
840  int maxValueX = mBoxImage->width();
841  int maxValueY = mBoxImage->height();
842 
843  //create a temporary color object
844  QColor currentColor = QColor( mCurrentColor );
845  int colorComponentValue;
846 
847  for ( int y = 0; y < maxValueY; ++y )
848  {
849  QRgb* scanLine = ( QRgb* )mBoxImage->scanLine( y );
850 
851  colorComponentValue = int( valueRangeY() - valueRangeY() * ( double( y ) / maxValueY ) );
852  alterColor( currentColor, yComponent(), colorComponentValue );
853  for ( int x = 0; x < maxValueX; ++x )
854  {
855  colorComponentValue = int( valueRangeX() * ( double( x ) / maxValueX ) );
856  alterColor( currentColor, xComponent(), colorComponentValue );
857  scanLine[x] = currentColor.rgb();
858  }
859  }
860  mDirty = false;
861 }
862 
863 int QgsColorBox::valueRangeX() const
864 {
865  return componentRange( xComponent() );
866 }
867 
868 int QgsColorBox::valueRangeY() const
869 {
870  return componentRange( yComponent() );
871 }
872 
873 QgsColorWidget::ColorComponent QgsColorBox::yComponent() const
874 {
875  switch ( mComponent )
876  {
877  case QgsColorWidget::Red:
878  return QgsColorWidget::Green;
881  return QgsColorWidget::Red;
882  case QgsColorWidget::Hue:
886  return QgsColorWidget::Hue;
887  default:
888  //should not occur
889  return QgsColorWidget::Red;
890  }
891 }
892 
893 int QgsColorBox::yComponentValue() const
894 {
895  return componentValue( yComponent() );
896 }
897 
898 QgsColorWidget::ColorComponent QgsColorBox::xComponent() const
899 {
900  switch ( mComponent )
901  {
902  case QgsColorWidget::Red:
904  return QgsColorWidget::Blue;
906  return QgsColorWidget::Green;
907  case QgsColorWidget::Hue:
909  return QgsColorWidget:: Value;
912  default:
913  //should not occur
914  return QgsColorWidget::Red;
915  }
916 }
917 
918 int QgsColorBox::xComponentValue() const
919 {
920  return componentValue( xComponent() );
921 }
922 
923 void QgsColorBox::setColorFromPoint( const QPoint &point )
924 {
925  int valX = valueRangeX() * ( point.x() - mMargin ) / ( width() - 2 * mMargin - 1 );
926  valX = qMin( qMax( valX, 0 ), valueRangeX() );
927 
928  int valY = valueRangeY() - valueRangeY() * ( point.y() - mMargin ) / ( height() - 2 * mMargin - 1 );
929  valY = qMin( qMax( valY, 0 ), valueRangeY() );
930 
931  QColor color = QColor( mCurrentColor );
932  alterColor( color, xComponent(), valX );
933  alterColor( color, yComponent(), valY );
934 
935  if ( color == mCurrentColor )
936  {
937  return;
938  }
939 
940  if ( color.hue() >= 0 )
941  {
942  mExplicitHue = color.hue();
943  }
944 
946  update();
947  emit colorChanged( color );
948 }
949 
950 
951 //
952 // QgsColorRampWidget
953 //
954 
956  const QgsColorWidget::ColorComponent component,
957  const Orientation orientation )
958  : QgsColorWidget( parent, component )
959  , mMargin( 4 )
960  , mShowFrame( false )
961 {
962  setFocusPolicy( Qt::StrongFocus );
963  setOrientation( orientation );
964 
965  //create triangle polygons
966  setMarkerSize( 5 );
967 }
968 
970 {
971 
972 }
973 
975 {
976  if ( mOrientation == QgsColorRampWidget::Horizontal )
977  {
978  //horizontal
979  return QSize( 200, 28 );
980  }
981  else
982  {
983  //vertical
984  return QSize( 18, 200 );
985  }
986 }
987 
988 void QgsColorRampWidget::paintEvent( QPaintEvent *event )
989 {
990  Q_UNUSED( event );
991  QPainter painter( this );
992 
993  if ( mShowFrame )
994  {
995  //draw frame
996  QStyleOptionFrameV3 option;
997  option.initFrom( this );
998  option.state = hasFocus() ? QStyle::State_KeyboardFocusChange : QStyle::State_None;
999  style()->drawPrimitive( QStyle::PE_Frame, &option, &painter );
1000  }
1001 
1002  if ( hasFocus() )
1003  {
1004  //draw focus rect
1005  QStyleOptionFocusRect option;
1006  option.initFrom( this );
1007  option.state = QStyle::State_KeyboardFocusChange;
1008  style()->drawPrimitive( QStyle::PE_FrameFocusRect, &option, &painter );
1009  }
1010 
1012  {
1013  int maxValue = ( mOrientation == QgsColorRampWidget::Horizontal ? width() : height() ) - 1 - 2 * mMargin;
1014  QColor color = QColor( mCurrentColor );
1015  color.setAlpha( 255 );
1016  QPen pen;
1017  pen.setWidth( 0 );
1018  painter.setPen( pen );
1019  painter.setBrush( Qt::NoBrush );
1020 
1021  //draw background ramp
1022  for ( int c = 0; c <= maxValue; ++c )
1023  {
1024  int colorVal = componentRange() * ( double )c / maxValue;
1025  //vertical sliders are reversed
1026  if ( mOrientation == QgsColorRampWidget::Vertical )
1027  {
1028  colorVal = componentRange() - colorVal;
1029  }
1030  alterColor( color, mComponent, colorVal );
1031  if ( color.hue() < 0 )
1032  {
1033  color.setHsv( hue(), color.saturation(), color.value() );
1034  }
1035  pen.setColor( color );
1036  painter.setPen( pen );
1037  if ( mOrientation == QgsColorRampWidget::Horizontal )
1038  {
1039  //horizontal
1040  painter.drawLine( c + mMargin, mMargin, c + mMargin, height() - mMargin - 1 );
1041  }
1042  else
1043  {
1044  //vertical
1045  painter.drawLine( mMargin, c + mMargin, width() - mMargin - 1, c + mMargin );
1046  }
1047  }
1048  }
1049  else if ( mComponent == QgsColorWidget::Alpha )
1050  {
1051  //alpha ramps are drawn differently
1052  //start with the checkboard pattern
1053  QBrush checkBrush = QBrush( transparentBackground() );
1054  painter.setBrush( checkBrush );
1055  painter.setPen( Qt::NoPen );
1056  painter.drawRect( mMargin, mMargin, width() - 2 * mMargin - 1, height() - 2 * mMargin - 1 );
1057  QLinearGradient colorGrad;
1058  if ( mOrientation == QgsColorRampWidget::Horizontal )
1059  {
1060  //horizontal
1061  colorGrad = QLinearGradient( mMargin, 0, width() - mMargin - 1, 0 );
1062  }
1063  else
1064  {
1065  //vertical
1066  colorGrad = QLinearGradient( 0, mMargin, 0, height() - mMargin - 1 );
1067  }
1068  QColor transparent = QColor( mCurrentColor );
1069  transparent.setAlpha( 0 );
1070  colorGrad.setColorAt( 0, transparent );
1071  QColor opaque = QColor( mCurrentColor );
1072  opaque.setAlpha( 255 );
1073  colorGrad.setColorAt( 1, opaque );
1074  QBrush colorBrush = QBrush( colorGrad );
1075  painter.setBrush( colorBrush );
1076  painter.drawRect( mMargin, mMargin, width() - 2 * mMargin - 1, height() - 2 * mMargin - 1 );
1077  }
1078 
1079  if ( mOrientation == QgsColorRampWidget::Horizontal )
1080  {
1081  //draw marker triangles for horizontal ramps
1082  painter.setRenderHint( QPainter::Antialiasing );
1083  painter.setBrush( QBrush( Qt::black ) );
1084  painter.setPen( Qt::NoPen );
1085  painter.translate( mMargin + ( width() - 2 * mMargin ) * ( double )componentValue() / componentRange(), mMargin - 1 );
1086  painter.drawPolygon( mTopTriangle );
1087  painter.translate( 0, height() - mMargin - 2 );
1088  painter.setBrush( QBrush( Qt::white ) );
1089  painter.drawPolygon( mBottomTriangle );
1090  painter.end();
1091  }
1092  else
1093  {
1094  //draw cross lines for vertical ramps
1095  int ypos = mMargin + ( height() - 2 * mMargin - 1 ) - ( height() - 2 * mMargin - 1 ) * ( double )componentValue() / componentRange();
1096  painter.setBrush( Qt::white );
1097  painter.setPen( Qt::NoPen );
1098  painter.drawRect( mMargin, ypos - 1, width() - 2 * mMargin - 1, 3 );
1099  painter.setPen( Qt::black );
1100  painter.drawLine( mMargin, ypos, width() - mMargin - 1, ypos );
1101  }
1102 }
1103 
1105 {
1106  mOrientation = orientation;
1107  if ( orientation == QgsColorRampWidget::Horizontal )
1108  {
1109  //horizontal
1110  setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed );
1111  }
1112  else
1113  {
1114  //vertical
1115  setSizePolicy( QSizePolicy::Fixed, QSizePolicy::MinimumExpanding );
1116  }
1117  updateGeometry();
1118 }
1119 
1121 {
1122  if ( margin == mMargin )
1123  {
1124  return;
1125  }
1126  mMargin = margin;
1127  update();
1128 }
1129 
1130 void QgsColorRampWidget::setShowFrame( const bool showFrame )
1131 {
1132  if ( showFrame == mShowFrame )
1133  {
1134  return;
1135  }
1136  mShowFrame = showFrame;
1137  update();
1138 }
1139 
1140 void QgsColorRampWidget::setMarkerSize( const int markerSize )
1141 {
1142  //create triangle polygons
1143  mTopTriangle << QPoint( -markerSize, 0 ) << QPoint( markerSize, 0 ) << QPoint( 0, markerSize );
1144  mBottomTriangle << QPoint( -markerSize, 0 ) << QPoint( markerSize, 0 ) << QPoint( 0, -markerSize );
1145  update();
1146 }
1147 
1148 void QgsColorRampWidget::mouseMoveEvent( QMouseEvent *event )
1149 {
1150  setColorFromPoint( event->posF() );
1151 }
1152 
1153 void QgsColorRampWidget::mousePressEvent( QMouseEvent *event )
1154 {
1155  setColorFromPoint( event->posF() );
1156 }
1157 
1158 void QgsColorRampWidget::keyPressEvent( QKeyEvent *event )
1159 {
1160  int oldValue = componentValue();
1161  if (( mOrientation == QgsColorRampWidget::Horizontal && ( event->key() == Qt::Key_Right || event->key() == Qt::Key_Up ) )
1162  || ( mOrientation == QgsColorRampWidget::Vertical && ( event->key() == Qt::Key_Left || event->key() == Qt::Key_Up ) ) )
1163  {
1165  }
1166  else if (( mOrientation == QgsColorRampWidget::Horizontal && ( event->key() == Qt::Key_Left || event->key() == Qt::Key_Down ) )
1167  || ( mOrientation == QgsColorRampWidget::Vertical && ( event->key() == Qt::Key_Right || event->key() == Qt::Key_Down ) ) )
1168  {
1170  }
1171  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_PageDown )
1172  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_PageUp ) )
1173  {
1175  }
1176  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_PageUp )
1177  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_PageDown ) )
1178  {
1180  }
1181  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_Home )
1182  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_End ) )
1183  {
1184  setComponentValue( 0 );
1185  }
1186  else if (( mOrientation == QgsColorRampWidget::Horizontal && event->key() == Qt::Key_End )
1187  || ( mOrientation == QgsColorRampWidget::Vertical && event->key() == Qt::Key_Home ) )
1188  {
1189  //set to maximum value
1191  }
1192  else
1193  {
1195  return;
1196  }
1197 
1198  if ( componentValue() != oldValue )
1199  {
1200  //value has changed
1201  emit colorChanged( mCurrentColor );
1202  emit valueChanged( componentValue() );
1203  }
1204 }
1205 
1206 void QgsColorRampWidget::setColorFromPoint( const QPointF &point )
1207 {
1208  int oldValue = componentValue();
1209  int val;
1210  if ( mOrientation == QgsColorRampWidget::Horizontal )
1211  {
1212  val = componentRange() * ( point.x() - mMargin ) / ( width() - 2 * mMargin );
1213  }
1214  else
1215  {
1216  val = componentRange() - componentRange() * ( point.y() - mMargin ) / ( height() - 2 * mMargin );
1217  }
1218  val = qMax( 0, qMin( val, componentRange() ) );
1219  setComponentValue( val );
1220 
1221  if ( componentValue() != oldValue )
1222  {
1223  //value has changed
1224  emit colorChanged( mCurrentColor );
1225  emit valueChanged( componentValue() );
1226  }
1227 }
1228 
1229 //
1230 // QgsColorSliderWidget
1231 //
1232 
1234  : QgsColorWidget( parent, component )
1235  , mRampWidget( 0 )
1236  , mSpinBox( 0 )
1237 {
1238  QHBoxLayout* hLayout = new QHBoxLayout();
1239  hLayout->setMargin( 0 );
1240  hLayout->setSpacing( 5 );
1241 
1242  mRampWidget = new QgsColorRampWidget( 0, component );
1243  mRampWidget->setColor( mCurrentColor );
1244  hLayout->addWidget( mRampWidget, 1 );
1245 
1246  mSpinBox = new QSpinBox();
1247  //set spinbox to a reasonable width
1248  int largestCharWidth = mSpinBox->fontMetrics().width( "888%" );
1249  mSpinBox->setMinimumWidth( largestCharWidth + 35 );
1250  mSpinBox->setMinimum( 0 );
1251  mSpinBox->setMaximum( convertRealToDisplay( componentRange() ) );
1252  mSpinBox->setValue( convertRealToDisplay( componentValue() ) );
1253  if ( component == QgsColorWidget::Hue )
1254  {
1255  //degrees suffix for hue
1256  mSpinBox->setSuffix( QChar( 176 ) );
1257  }
1258  else if ( component == QgsColorWidget::Saturation || component == QgsColorWidget::Value || component == QgsColorWidget::Alpha )
1259  {
1260  mSpinBox->setSuffix( tr( "%" ) );
1261  }
1262  hLayout->addWidget( mSpinBox );
1263  setLayout( hLayout );
1264 
1265  connect( mRampWidget, SIGNAL( valueChanged( int ) ), this, SLOT( rampChanged( int ) ) );
1266  connect( mRampWidget, SIGNAL( colorChanged( const QColor ) ), this, SLOT( rampColorChanged( const QColor ) ) );
1267  connect( mSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( spinChanged( int ) ) );
1268 }
1269 
1271 {
1272 }
1273 
1275 {
1276  QgsColorWidget::setComponent( component );
1277  mRampWidget->setComponent( component );
1278  mSpinBox->setMaximum( convertRealToDisplay( componentRange() ) );
1279  if ( component == QgsColorWidget::Hue )
1280  {
1281  //degrees suffix for hue
1282  mSpinBox->setSuffix( QChar( 176 ) );
1283  }
1284  else if ( component == QgsColorWidget::Saturation || component == QgsColorWidget::Value || component == QgsColorWidget::Alpha )
1285  {
1286  //saturation, value and alpha are in %
1287  mSpinBox->setSuffix( tr( "%" ) );
1288  }
1289  else
1290  {
1291  //clear suffix
1292  mSpinBox->setSuffix( QString() );
1293  }
1294 }
1295 
1297 {
1299  mRampWidget->blockSignals( true );
1300  mRampWidget->setComponentValue( value );
1301  mRampWidget->blockSignals( false );
1302  mSpinBox->blockSignals( true );
1303  mSpinBox->setValue( convertRealToDisplay( value ) );
1304  mSpinBox->blockSignals( false );
1305 }
1306 
1307 void QgsColorSliderWidget::setColor( const QColor &color, bool emitSignals )
1308 {
1309  QgsColorWidget::setColor( color, emitSignals );
1310  mRampWidget->setColor( color );
1311  mSpinBox->blockSignals( true );
1312  mSpinBox->setValue( convertRealToDisplay( componentValue() ) );
1313  mSpinBox->blockSignals( false );
1314 }
1315 
1316 void QgsColorSliderWidget::rampColorChanged( const QColor &color )
1317 {
1318  emit colorChanged( color );
1319 }
1320 
1321 void QgsColorSliderWidget::spinChanged( int value )
1322 {
1323  int convertedValue = convertDisplayToReal( value );
1324  QgsColorWidget::setComponentValue( convertedValue );
1325  mRampWidget->setComponentValue( convertedValue );
1326  emit colorChanged( mCurrentColor );
1327 }
1328 
1329 void QgsColorSliderWidget::rampChanged( int value )
1330 {
1331  mSpinBox->blockSignals( true );
1332  mSpinBox->setValue( convertRealToDisplay( value ) );
1333  mSpinBox->blockSignals( false );
1334 }
1335 
1336 
1337 int QgsColorSliderWidget::convertRealToDisplay( const int realValue ) const
1338 {
1339  //scale saturation, value or alpha to 0->100 range. This makes more sense for users
1340  //for whom "255" is a totally arbitrary value!
1342  {
1343  return qRound( 100.0 * realValue / 255.0 );
1344  }
1345 
1346  //leave all other values intact
1347  return realValue;
1348 }
1349 
1350 int QgsColorSliderWidget::convertDisplayToReal( const int displayValue ) const
1351 {
1352  //scale saturation, value or alpha from 0->100 range (see note in convertRealToDisplay)
1354  {
1355  return qRound( 255.0 * displayValue / 100.0 );
1356  }
1357 
1358  //leave all other values intact
1359  return displayValue;
1360 }
1361 
1362 //
1363 // QgsColorTextWidget
1364 //
1365 
1367  : QgsColorWidget( parent )
1368  , mLineEdit( 0 )
1369  , mMenuButton( 0 )
1370  , mFormat( QgsColorTextWidget::HexRgb )
1371 {
1372  QHBoxLayout* hLayout = new QHBoxLayout();
1373  hLayout->setMargin( 0 );
1374  hLayout->setSpacing( 0 );
1375 
1376  mLineEdit = new QLineEdit( 0 );
1377  hLayout->addWidget( mLineEdit );
1378 
1379  mMenuButton = new QToolButton( mLineEdit );
1380  mMenuButton->setIcon( QgsApplication::getThemeIcon( "/mIconDropDownMenu.svg" ) );
1381  mMenuButton->setCursor( Qt::ArrowCursor );
1382  mMenuButton->setFocusPolicy( Qt::NoFocus );
1383  mMenuButton->setStyleSheet( "QToolButton { border: none; padding: 0px; }" );
1384 
1385  setLayout( hLayout );
1386 
1387  int frameWidth = mLineEdit->style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
1388  mLineEdit->setStyleSheet( QString( "QLineEdit { padding-right: %1px; } " )
1389  .arg( mMenuButton->sizeHint().width() + frameWidth + 1 ) );
1390 
1391  connect( mLineEdit, SIGNAL( editingFinished() ), this, SLOT( textChanged() ) );
1392  connect( mMenuButton, SIGNAL( clicked() ), this, SLOT( showMenu() ) );
1393 
1394  //restore format setting
1395  QSettings settings;
1396  mFormat = ( ColorTextFormat )settings.value( "/ColorWidgets/textWidgetFormat", 0 ).toInt();
1397 
1398  updateText();
1399 }
1400 
1402 {
1403 
1404 }
1405 
1406 void QgsColorTextWidget::setColor( const QColor &color, const bool emitSignals )
1407 {
1408  QgsColorWidget::setColor( color, emitSignals );
1409  updateText();
1410 }
1411 
1412 void QgsColorTextWidget::resizeEvent( QResizeEvent * event )
1413 {
1414  Q_UNUSED( event );
1415  QSize sz = mMenuButton->sizeHint();
1416  int frameWidth = style()->pixelMetric( QStyle::PM_DefaultFrameWidth );
1417  mMenuButton->move( mLineEdit->rect().right() - frameWidth - sz.width(),
1418  ( mLineEdit->rect().bottom() + 1 - sz.height() ) / 2 );
1419 }
1420 
1421 void QgsColorTextWidget::updateText()
1422 {
1423  switch ( mFormat )
1424  {
1425  case HexRgb:
1426  mLineEdit->setText( mCurrentColor.name() );
1427  break;
1428  case HexRgbA:
1429  mLineEdit->setText( mCurrentColor.name() + QString( "%1" ).arg( mCurrentColor.alpha(), 2, 16, QChar( '0' ) ) );
1430  break;
1431  case Rgb:
1432  mLineEdit->setText( QString( tr( "rgb( %1, %2, %3 )" ) ).arg( mCurrentColor.red() ).arg( mCurrentColor.green() ).arg( mCurrentColor.blue() ) );
1433  break;
1434  case Rgba:
1435  mLineEdit->setText( QString( tr( "rgba( %1, %2, %3, %4 )" ) ).arg( mCurrentColor.red() ).arg( mCurrentColor.green() ).arg( mCurrentColor.blue() ).arg( QString::number( mCurrentColor.alphaF(), 'f', 2 ) ) );
1436  break;
1437  }
1438 }
1439 
1440 void QgsColorTextWidget::textChanged()
1441 {
1442  QString testString = mLineEdit->text();
1443  bool containsAlpha;
1444  QColor color = QgsSymbolLayerV2Utils::parseColorWithAlpha( testString, containsAlpha );
1445  if ( !color.isValid() )
1446  {
1447  //bad color string
1448  updateText();
1449  return;
1450  }
1451 
1452  //good color string
1453  if ( color != mCurrentColor )
1454  {
1455  //retain alpha if no explicit alpha set
1456  if ( !containsAlpha )
1457  {
1458  color.setAlpha( mCurrentColor.alpha() );
1459  }
1460  //color has changed
1461  mCurrentColor = color;
1462  emit colorChanged( mCurrentColor );
1463  }
1464  updateText();
1465 }
1466 
1467 void QgsColorTextWidget::showMenu()
1468 {
1469  QMenu colorContextMenu;
1470 
1471  QAction* hexRgbAction = new QAction( tr( "#RRGGBB" ), 0 );
1472  colorContextMenu.addAction( hexRgbAction );
1473  QAction* hexRgbaAction = new QAction( tr( "#RRGGBBAA" ), 0 );
1474  colorContextMenu.addAction( hexRgbaAction );
1475  QAction* rgbAction = new QAction( tr( "rgb( r, g, b )" ), 0 );
1476  colorContextMenu.addAction( rgbAction );
1477  QAction* rgbaAction = new QAction( tr( "rgba( r, g, b, a )" ), 0 );
1478  colorContextMenu.addAction( rgbaAction );
1479 
1480  QAction* selectedAction = colorContextMenu.exec( QCursor::pos() );
1481  if ( selectedAction == hexRgbAction )
1482  {
1483  mFormat = QgsColorTextWidget::HexRgb;
1484  }
1485  else if ( selectedAction == hexRgbaAction )
1486  {
1487  mFormat = QgsColorTextWidget::HexRgbA;
1488  }
1489  else if ( selectedAction == rgbAction )
1490  {
1491  mFormat = QgsColorTextWidget::Rgb;
1492  }
1493  else if ( selectedAction == rgbaAction )
1494  {
1495  mFormat = QgsColorTextWidget::Rgba;
1496  }
1497 
1498  //save format setting
1499  QSettings settings;
1500  settings.setValue( "/ColorWidgets/textWidgetFormat", ( int )mFormat );
1501 
1502  updateText();
1503 }
1504 
1505 
1506 //
1507 // QgsColorPreviewWidget
1508 //
1509 
1511  : QgsColorWidget( parent )
1512  , mColor2( QColor() )
1513 {
1514 
1515 }
1516 
1518 {
1519 
1520 }
1521 
1522 void QgsColorPreviewWidget::drawColor( const QColor &color, const QRect &rect, QPainter& painter )
1523 {
1524  painter.setPen( Qt::NoPen );
1525  //if color has an alpha, start with a checkboard pattern
1526  if ( color.alpha() < 255 )
1527  {
1528  QBrush checkBrush = QBrush( transparentBackground() );
1529  painter.setBrush( checkBrush );
1530  painter.drawRect( rect );
1531 
1532  //draw half of widget showing solid color, the other half showing color with alpha
1533 
1534  //ensure at least a 1px overlap to avoid artefacts
1535  QBrush colorBrush = QBrush( color );
1536  painter.setBrush( colorBrush );
1537  painter.drawRect( floor( rect.width() / 2.0 ) + rect.left(), rect.top(), rect.width() - floor( rect.width() / 2.0 ), rect.height() );
1538 
1539  QColor opaqueColor = QColor( color );
1540  opaqueColor.setAlpha( 255 );
1541  QBrush opaqueBrush = QBrush( opaqueColor );
1542  painter.setBrush( opaqueBrush );
1543  painter.drawRect( rect.left(), rect.top(), ceil( rect.width() / 2.0 ), rect.height() );
1544  }
1545  else
1546  {
1547  //no alpha component, just draw a solid rectangle
1548  QBrush brush = QBrush( color );
1549  painter.setBrush( brush );
1550  painter.drawRect( rect );
1551  }
1552 }
1553 
1554 void QgsColorPreviewWidget::paintEvent( QPaintEvent *event )
1555 {
1556  Q_UNUSED( event );
1557  QPainter painter( this );
1558 
1559  if ( mColor2.isValid() )
1560  {
1561  //drawing with two color sections
1562  int verticalSplit = qRound( height() / 2.0 );
1563  drawColor( mCurrentColor, QRect( 0, 0, width(), verticalSplit ), painter );
1564  drawColor( mColor2, QRect( 0, verticalSplit, width(), height() - verticalSplit ), painter );
1565  }
1566  else if ( mCurrentColor.isValid() )
1567  {
1568  drawColor( mCurrentColor, QRect( 0, 0, width(), height() ), painter );
1569  }
1570 
1571  painter.end();
1572 }
1573 
1574 void QgsColorPreviewWidget::setColor2( const QColor &color )
1575 {
1576  if ( color == mColor2 )
1577  {
1578  return;
1579  }
1580  mColor2 = color;
1581  update();
1582 }
1583 
1585 {
1586  if ( e->button() == Qt::LeftButton )
1587  {
1588  mDragStartPosition = e->pos();
1589  }
1591 }
1592 
1594 {
1595  if (( e->pos() - mDragStartPosition ).manhattanLength() >= QApplication::startDragDistance() )
1596  {
1597  //mouse moved, so a drag. nothing to do here
1599  return;
1600  }
1601 
1602  //work out which color was clicked
1603  QColor clickedColor = mCurrentColor;
1604  if ( mColor2.isValid() )
1605  {
1606  //two color sections, check if dragged color was the second color
1607  int verticalSplit = qRound( height() / 2.0 );
1608  if ( mDragStartPosition.y() >= verticalSplit )
1609  {
1610  clickedColor = mColor2;
1611  }
1612  }
1613  emit colorChanged( clickedColor );
1614 
1615 }
1616 
1618 {
1619  //handle dragging colors from button
1620 
1621  if ( !( e->buttons() & Qt::LeftButton ) )
1622  {
1623  //left button not depressed, so not a drag
1625  return;
1626  }
1627 
1628  if (( e->pos() - mDragStartPosition ).manhattanLength() < QApplication::startDragDistance() )
1629  {
1630  //mouse not moved, so not a drag
1632  return;
1633  }
1634 
1635  //user is dragging color
1636 
1637  //work out which color is being dragged
1638  QColor dragColor = mCurrentColor;
1639  if ( mColor2.isValid() )
1640  {
1641  //two color sections, check if dragged color was the second color
1642  int verticalSplit = qRound( height() / 2.0 );
1643  if ( mDragStartPosition.y() >= verticalSplit )
1644  {
1645  dragColor = mColor2;
1646  }
1647  }
1648 
1649  QDrag *drag = new QDrag( this );
1650  drag->setMimeData( QgsSymbolLayerV2Utils::colorToMimeData( dragColor ) );
1651  drag->setPixmap( createDragIcon( dragColor ) );
1652  drag->exec( Qt::CopyAction );
1653 }