QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgscompoundcolorwidget.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscompoundcolorwidget.cpp
3  --------------------------
4  begin : April 2016
5  copyright : (C) 2016 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 "qgscompoundcolorwidget.h"
17 #include "qgscolorscheme.h"
18 #include "qgscolorschemeregistry.h"
19 #include "qgssymbollayerutils.h"
20 #include "qgsapplication.h"
21 #include "qgssettings.h"
22 
23 #include <QHeaderView>
24 #include <QPushButton>
25 #include <QMenu>
26 #include <QToolButton>
27 #include <QFileDialog>
28 #include <QMessageBox>
29 #include <QDesktopWidget>
30 #include <QMouseEvent>
31 #include <QScreen>
32 #include <QInputDialog>
33 #include <QVBoxLayout>
34 
35 QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
36  : QgsPanelWidget( parent )
37 {
38  setupUi( this );
39  connect( mHueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mHueRadio_toggled );
40  connect( mSaturationRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mSaturationRadio_toggled );
41  connect( mValueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mValueRadio_toggled );
42  connect( mRedRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mRedRadio_toggled );
43  connect( mGreenRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mGreenRadio_toggled );
44  connect( mBlueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mBlueRadio_toggled );
45  connect( mAddColorToSchemeButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddColorToSchemeButton_clicked );
46  connect( mAddCustomColorButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddCustomColorButton_clicked );
47  connect( mSampleButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mSampleButton_clicked );
48  connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsCompoundColorWidget::mTabWidget_currentChanged );
49  connect( mActionShowInButtons, &QAction::toggled, this, &QgsCompoundColorWidget::mActionShowInButtons_toggled );
50 
51  if ( widgetLayout == LayoutVertical )
52  {
53  // shuffle stuff around
54  QVBoxLayout *newLayout = new QVBoxLayout();
55  newLayout->setMargin( 0 );
56  newLayout->setContentsMargins( 0, 0, 0, 0 );
57  newLayout->addWidget( mTabWidget );
58  newLayout->addWidget( mSlidersWidget );
59  newLayout->addWidget( mPreviewWidget );
60  newLayout->addWidget( mSwatchesWidget );
61  delete layout();
62  setLayout( newLayout );
63  }
64 
65  QgsSettings settings;
66 
67  mSchemeList->header()->hide();
68  mSchemeList->setColumnWidth( 0, static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().width( 'X' ) * 6 ) );
69 
70  //get schemes with ShowInColorDialog set
71  refreshSchemeComboBox();
72  QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
73 
74  //choose a reasonable starting scheme
75  int activeScheme = settings.value( QStringLiteral( "Windows/ColorDialog/activeScheme" ), 0 ).toInt();
76  activeScheme = activeScheme >= mSchemeComboBox->count() ? 0 : activeScheme;
77 
78  mSchemeList->setScheme( schemeList.at( activeScheme ) );
79 
80  mSchemeComboBox->setCurrentIndex( activeScheme );
81  updateActionsForCurrentScheme();
82 
83  //listen out for selection changes in list, so we can enable/disable the copy colors option
84  connect( mSchemeList->selectionModel(), &QItemSelectionModel::selectionChanged, this, &QgsCompoundColorWidget::listSelectionChanged );
85  //copy action defaults to disabled
86  mActionCopyColors->setEnabled( false );
87 
88  connect( mActionCopyColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::copyColors );
89  connect( mActionPasteColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::pasteColors );
90  connect( mActionExportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showExportColorsDialog );
91  connect( mActionImportColors, &QAction::triggered, mSchemeList, &QgsColorSchemeList::showImportColorsDialog );
92  connect( mActionImportPalette, &QAction::triggered, this, &QgsCompoundColorWidget::importPalette );
93  connect( mActionRemovePalette, &QAction::triggered, this, &QgsCompoundColorWidget::removePalette );
94  connect( mActionNewPalette, &QAction::triggered, this, &QgsCompoundColorWidget::newPalette );
95  connect( mRemoveColorsFromSchemeButton, &QAbstractButton::clicked, mSchemeList, &QgsColorSchemeList::removeSelection );
96 
97  QMenu *schemeMenu = new QMenu( mSchemeToolButton );
98  schemeMenu->addAction( mActionCopyColors );
99  schemeMenu->addAction( mActionPasteColors );
100  schemeMenu->addSeparator();
101  schemeMenu->addAction( mActionImportColors );
102  schemeMenu->addAction( mActionExportColors );
103  schemeMenu->addSeparator();
104  schemeMenu->addAction( mActionNewPalette );
105  schemeMenu->addAction( mActionImportPalette );
106  schemeMenu->addAction( mActionRemovePalette );
107  schemeMenu->addAction( mActionShowInButtons );
108  mSchemeToolButton->setMenu( schemeMenu );
109 
110  connect( mSchemeComboBox, static_cast<void ( QComboBox::* )( int )>( &QComboBox::currentIndexChanged ), this, &QgsCompoundColorWidget::schemeIndexChanged );
112 
113  mOldColorLabel->hide();
114 
115  mVerticalRamp->setOrientation( QgsColorRampWidget::Vertical );
116  mVerticalRamp->setInteriorMargin( 2 );
117  mVerticalRamp->setShowFrame( true );
118 
119  mRedSlider->setComponent( QgsColorWidget::Red );
120  mGreenSlider->setComponent( QgsColorWidget::Green );
121  mBlueSlider->setComponent( QgsColorWidget::Blue );
122  mHueSlider->setComponent( QgsColorWidget::Hue );
123  mSaturationSlider->setComponent( QgsColorWidget::Saturation );
124  mValueSlider->setComponent( QgsColorWidget::Value );
125  mAlphaSlider->setComponent( QgsColorWidget::Alpha );
126 
127  mSwatchButton1->setShowMenu( false );
128  mSwatchButton1->setBehavior( QgsColorButton::SignalOnly );
129  mSwatchButton2->setShowMenu( false );
130  mSwatchButton2->setBehavior( QgsColorButton::SignalOnly );
131  mSwatchButton3->setShowMenu( false );
132  mSwatchButton3->setBehavior( QgsColorButton::SignalOnly );
133  mSwatchButton4->setShowMenu( false );
134  mSwatchButton4->setBehavior( QgsColorButton::SignalOnly );
135  mSwatchButton5->setShowMenu( false );
136  mSwatchButton5->setBehavior( QgsColorButton::SignalOnly );
137  mSwatchButton6->setShowMenu( false );
138  mSwatchButton6->setBehavior( QgsColorButton::SignalOnly );
139  mSwatchButton7->setShowMenu( false );
140  mSwatchButton7->setBehavior( QgsColorButton::SignalOnly );
141  mSwatchButton8->setShowMenu( false );
142  mSwatchButton8->setBehavior( QgsColorButton::SignalOnly );
143  mSwatchButton9->setShowMenu( false );
144  mSwatchButton9->setBehavior( QgsColorButton::SignalOnly );
145  mSwatchButton10->setShowMenu( false );
146  mSwatchButton10->setBehavior( QgsColorButton::SignalOnly );
147  mSwatchButton11->setShowMenu( false );
148  mSwatchButton11->setBehavior( QgsColorButton::SignalOnly );
149  mSwatchButton12->setShowMenu( false );
150  mSwatchButton12->setBehavior( QgsColorButton::SignalOnly );
151  mSwatchButton13->setShowMenu( false );
152  mSwatchButton13->setBehavior( QgsColorButton::SignalOnly );
153  mSwatchButton14->setShowMenu( false );
154  mSwatchButton14->setBehavior( QgsColorButton::SignalOnly );
155  mSwatchButton15->setShowMenu( false );
156  mSwatchButton15->setBehavior( QgsColorButton::SignalOnly );
157  mSwatchButton16->setShowMenu( false );
158  mSwatchButton16->setBehavior( QgsColorButton::SignalOnly );
159  //restore custom colors
160  mSwatchButton1->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( QColor() ) ).value<QColor>() );
161  mSwatchButton2->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( QColor() ) ).value<QColor>() );
162  mSwatchButton3->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( QColor() ) ).value<QColor>() );
163  mSwatchButton4->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( QColor() ) ).value<QColor>() );
164  mSwatchButton5->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( QColor() ) ).value<QColor>() );
165  mSwatchButton6->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( QColor() ) ).value<QColor>() );
166  mSwatchButton7->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( QColor() ) ).value<QColor>() );
167  mSwatchButton8->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( QColor() ) ).value<QColor>() );
168  mSwatchButton9->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( QColor() ) ).value<QColor>() );
169  mSwatchButton10->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( QColor() ) ).value<QColor>() );
170  mSwatchButton11->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( QColor() ) ).value<QColor>() );
171  mSwatchButton12->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( QColor() ) ).value<QColor>() );
172  mSwatchButton13->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( QColor() ) ).value<QColor>() );
173  mSwatchButton14->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( QColor() ) ).value<QColor>() );
174  mSwatchButton15->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( QColor() ) ).value<QColor>() );
175  mSwatchButton16->setColor( settings.value( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( QColor() ) ).value<QColor>() );
176 
177  //restore sample radius
178  mSpinBoxRadius->setValue( settings.value( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), 1 ).toInt() );
179  mSamplePreview->setColor( QColor() );
180 
181  // hidpi friendly sizes
182  const int swatchWidth = static_cast< int >( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.9 * mSwatchButton1->fontMetrics().height(), 38.0 ) ) );
183  const int swatchHeight = static_cast< int >( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.5 * mSwatchButton1->fontMetrics().height(), 30.0 ) ) );
184  mSwatchButton1->setMinimumSize( swatchWidth, swatchHeight );
185  mSwatchButton1->setMaximumSize( swatchWidth, swatchHeight );
186  mSwatchButton2->setMinimumSize( swatchWidth, swatchHeight );
187  mSwatchButton2->setMaximumSize( swatchWidth, swatchHeight );
188  mSwatchButton3->setMinimumSize( swatchWidth, swatchHeight );
189  mSwatchButton3->setMaximumSize( swatchWidth, swatchHeight );
190  mSwatchButton4->setMinimumSize( swatchWidth, swatchHeight );
191  mSwatchButton4->setMaximumSize( swatchWidth, swatchHeight );
192  mSwatchButton5->setMinimumSize( swatchWidth, swatchHeight );
193  mSwatchButton5->setMaximumSize( swatchWidth, swatchHeight );
194  mSwatchButton6->setMinimumSize( swatchWidth, swatchHeight );
195  mSwatchButton6->setMaximumSize( swatchWidth, swatchHeight );
196  mSwatchButton7->setMinimumSize( swatchWidth, swatchHeight );
197  mSwatchButton7->setMaximumSize( swatchWidth, swatchHeight );
198  mSwatchButton8->setMinimumSize( swatchWidth, swatchHeight );
199  mSwatchButton8->setMaximumSize( swatchWidth, swatchHeight );
200  mSwatchButton9->setMinimumSize( swatchWidth, swatchHeight );
201  mSwatchButton9->setMaximumSize( swatchWidth, swatchHeight );
202  mSwatchButton10->setMinimumSize( swatchWidth, swatchHeight );
203  mSwatchButton10->setMaximumSize( swatchWidth, swatchHeight );
204  mSwatchButton11->setMinimumSize( swatchWidth, swatchHeight );
205  mSwatchButton11->setMaximumSize( swatchWidth, swatchHeight );
206  mSwatchButton12->setMinimumSize( swatchWidth, swatchHeight );
207  mSwatchButton12->setMaximumSize( swatchWidth, swatchHeight );
208  mSwatchButton13->setMinimumSize( swatchWidth, swatchHeight );
209  mSwatchButton13->setMaximumSize( swatchWidth, swatchHeight );
210  mSwatchButton14->setMinimumSize( swatchWidth, swatchHeight );
211  mSwatchButton14->setMaximumSize( swatchWidth, swatchHeight );
212  mSwatchButton15->setMinimumSize( swatchWidth, swatchHeight );
213  mSwatchButton15->setMaximumSize( swatchWidth, swatchHeight );
214  mSwatchButton16->setMinimumSize( swatchWidth, swatchHeight );
215  mSwatchButton16->setMaximumSize( swatchWidth, swatchHeight );
216  const int previewHeight = static_cast< int >( std::round( std::max( Qgis::UI_SCALE_FACTOR * 2.0 * mSwatchButton1->fontMetrics().height(), 40.0 ) ) );
217  mColorPreview->setMinimumSize( 0, previewHeight );
218  mPreviewWidget->setMaximumHeight( previewHeight * 2 );
219  const int swatchAddSize = static_cast< int >( std::round( std::max( Qgis::UI_SCALE_FACTOR * 1.4 * mSwatchButton1->fontMetrics().height(), 28.0 ) ) );
220  mAddCustomColorButton->setMinimumWidth( swatchAddSize );
221  mAddCustomColorButton->setMaximumWidth( swatchAddSize );
222 
223  const int iconSize = QgsGuiUtils::scaleIconSize( 16 );
224  mTabWidget->setIconSize( QSize( iconSize, iconSize ) );
225 
226  if ( color.isValid() )
227  {
228  setColor( color );
229  }
230 
231  //restore active component radio button
232  int activeRadio = settings.value( QStringLiteral( "Windows/ColorDialog/activeComponent" ), 2 ).toInt();
233  switch ( activeRadio )
234  {
235  case 0:
236  mHueRadio->setChecked( true );
237  break;
238  case 1:
239  mSaturationRadio->setChecked( true );
240  break;
241  case 2:
242  mValueRadio->setChecked( true );
243  break;
244  case 3:
245  mRedRadio->setChecked( true );
246  break;
247  case 4:
248  mGreenRadio->setChecked( true );
249  break;
250  case 5:
251  mBlueRadio->setChecked( true );
252  break;
253  }
254  int currentTab = settings.value( QStringLiteral( "Windows/ColorDialog/activeTab" ), 0 ).toInt();
255  mTabWidget->setCurrentIndex( currentTab );
256 
257  //setup connections
258  connect( mColorBox, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
259  connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
260  connect( mColorText, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
261  connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
262  connect( mRedSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
263  connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
264  connect( mBlueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
265  connect( mHueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
266  connect( mValueSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
267  connect( mSaturationSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
268  connect( mAlphaSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
269  connect( mColorPreview, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
270  connect( mSwatchButton1, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
271  connect( mSwatchButton2, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
272  connect( mSwatchButton3, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
273  connect( mSwatchButton4, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
274  connect( mSwatchButton5, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
275  connect( mSwatchButton6, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
276  connect( mSwatchButton7, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
277  connect( mSwatchButton8, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
278  connect( mSwatchButton9, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
279  connect( mSwatchButton10, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
280  connect( mSwatchButton11, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
281  connect( mSwatchButton12, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
282  connect( mSwatchButton13, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
283  connect( mSwatchButton14, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
284  connect( mSwatchButton15, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
285  connect( mSwatchButton16, &QgsColorButton::colorClicked, this, &QgsCompoundColorWidget::setColor );
286 }
287 
289 {
290  saveSettings();
291  if ( !mDiscarded )
292  {
294  }
295 }
296 
298 {
299  //all widgets should have the same color, so it shouldn't matter
300  //which we fetch it from
301  return mColorPreview->color();
302 }
303 
304 void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
305 {
306  mAllowAlpha = allowOpacity;
307  mAlphaLabel->setVisible( allowOpacity );
308  mAlphaSlider->setVisible( allowOpacity );
309  if ( !allowOpacity )
310  {
311  mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
312  mAlphaLayout->setSpacing( 0 );
313  }
314 }
315 
316 void QgsCompoundColorWidget::refreshSchemeComboBox()
317 {
318  mSchemeComboBox->blockSignals( true );
319  mSchemeComboBox->clear();
320  QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
321  QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
322  for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
323  {
324  mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
325  }
326  mSchemeComboBox->blockSignals( false );
327 }
328 
329 
331 {
332  QgsSettings s;
333  QString lastDir = s.value( QStringLiteral( "/UI/lastGplPaletteDir" ), QDir::homePath() ).toString();
334  QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, QStringLiteral( "GPL (*.gpl);;All files (*.*)" ) );
335  if ( parent )
336  parent->activateWindow();
337  if ( filePath.isEmpty() )
338  {
339  return nullptr;
340  }
341 
342  //check if file exists
343  QFileInfo fileInfo( filePath );
344  if ( !fileInfo.exists() || !fileInfo.isReadable() )
345  {
346  QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
347  return nullptr;
348  }
349 
350  s.setValue( QStringLiteral( "/UI/lastGplPaletteDir" ), fileInfo.absolutePath() );
351  QFile file( filePath );
352 
353  QgsNamedColorList importedColors;
354  bool ok = false;
355  QString paletteName;
356  importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
357  if ( !ok )
358  {
359  QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
360  return nullptr;
361  }
362 
363  if ( importedColors.length() == 0 )
364  {
365  //no imported colors
366  QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
367  return nullptr;
368  }
369 
370  //TODO - handle conflicting file names, name for new palette
371  QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
372  importedScheme->setName( paletteName );
373  importedScheme->setColors( importedColors );
374 
376  return importedScheme;
377 }
378 
379 void QgsCompoundColorWidget::importPalette()
380 {
381  if ( importUserPaletteFromFile( this ) )
382  {
383  //refresh combobox
384  refreshSchemeComboBox();
385  mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
386  }
387 }
388 
389 
391 {
392  if ( QMessageBox::question( parent, tr( "Remove Color Palette" ),
393  QString( tr( "Are you sure you want to remove %1?" ) ).arg( scheme->schemeName() ),
394  QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
395  {
396  //user canceled
397  return false;
398  }
399 
400  //remove palette and associated gpl file
401  if ( !scheme->erase() )
402  {
403  //something went wrong
404  return false;
405  }
406 
407  //remove scheme from registry
409  return true;
410 }
411 
412 void QgsCompoundColorWidget::removePalette()
413 {
414  //get current scheme
415  QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
416  int prevIndex = mSchemeComboBox->currentIndex();
417  if ( prevIndex >= schemeList.length() )
418  {
419  return;
420  }
421 
422  //make user scheme is a user removable scheme
423  QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( schemeList.at( prevIndex ) );
424  if ( !userScheme )
425  {
426  return;
427  }
428 
429  if ( removeUserPalette( userScheme, this ) )
430  {
431  refreshSchemeComboBox();
432  prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 );
433  mSchemeComboBox->setCurrentIndex( prevIndex );
434  }
435 }
436 
438 {
439  bool ok = false;
440  QString name = QInputDialog::getText( parent, tr( "Create New Palette" ), tr( "Enter a name for the new palette:" ),
441  QLineEdit::Normal, tr( "New palette" ), &ok );
442 
443  if ( !ok || name.isEmpty() )
444  {
445  //user canceled
446  return nullptr;
447  }
448 
449 //generate file name for new palette
450  QDir palettePath( gplFilePath() );
451  QRegExp badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
452  QString filename = name.simplified().toLower().replace( badChars, QStringLiteral( "_" ) );
453  if ( filename.isEmpty() )
454  {
455  filename = tr( "new_palette" );
456  }
457  QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
458  int fileNumber = 1;
459  while ( destFileInfo.exists() )
460  {
461  //try to generate a unique file name
462  destFileInfo = QFileInfo( palettePath.filePath( filename + QStringLiteral( "%1.gpl" ).arg( fileNumber ) ) );
463  fileNumber++;
464  }
465 
466  QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
467  newScheme->setName( name );
468 
470  return newScheme;
471 }
472 
473 void QgsCompoundColorWidget::newPalette()
474 {
475  if ( createNewUserPalette( this ) )
476  {
477  //refresh combobox and set new scheme as active
478  refreshSchemeComboBox();
479  mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
480  }
481 }
482 
483 QString QgsCompoundColorWidget::gplFilePath()
484 {
485  QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
486 
487  QDir localDir;
488  if ( !localDir.mkpath( palettesDir ) )
489  {
490  return QString();
491  }
492 
493  return palettesDir;
494 }
495 
496 void QgsCompoundColorWidget::schemeIndexChanged( int index )
497 {
498  //save changes to scheme
499  if ( mSchemeList->isDirty() )
500  {
501  mSchemeList->saveColorsToScheme();
502  }
503 
504  //get schemes with ShowInColorDialog set
505  QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
506  if ( index >= schemeList.length() )
507  {
508  return;
509  }
510 
511  QgsColorScheme *scheme = schemeList.at( index );
512  mSchemeList->setScheme( scheme );
513 
514  updateActionsForCurrentScheme();
515 
516  //copy action defaults to disabled
517  mActionCopyColors->setEnabled( false );
518 }
519 
520 void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
521 {
522  Q_UNUSED( deselected )
523  mActionCopyColors->setEnabled( selected.length() > 0 );
524 }
525 
526 void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
527 {
528  switch ( mLastCustomColorIndex )
529  {
530  case 0:
531  mSwatchButton1->setColor( mColorPreview->color() );
532  break;
533  case 1:
534  mSwatchButton2->setColor( mColorPreview->color() );
535  break;
536  case 2:
537  mSwatchButton3->setColor( mColorPreview->color() );
538  break;
539  case 3:
540  mSwatchButton4->setColor( mColorPreview->color() );
541  break;
542  case 4:
543  mSwatchButton5->setColor( mColorPreview->color() );
544  break;
545  case 5:
546  mSwatchButton6->setColor( mColorPreview->color() );
547  break;
548  case 6:
549  mSwatchButton7->setColor( mColorPreview->color() );
550  break;
551  case 7:
552  mSwatchButton8->setColor( mColorPreview->color() );
553  break;
554  case 8:
555  mSwatchButton9->setColor( mColorPreview->color() );
556  break;
557  case 9:
558  mSwatchButton10->setColor( mColorPreview->color() );
559  break;
560  case 10:
561  mSwatchButton11->setColor( mColorPreview->color() );
562  break;
563  case 11:
564  mSwatchButton12->setColor( mColorPreview->color() );
565  break;
566  case 12:
567  mSwatchButton13->setColor( mColorPreview->color() );
568  break;
569  case 13:
570  mSwatchButton14->setColor( mColorPreview->color() );
571  break;
572  case 14:
573  mSwatchButton15->setColor( mColorPreview->color() );
574  break;
575  case 15:
576  mSwatchButton16->setColor( mColorPreview->color() );
577  break;
578  }
579  mLastCustomColorIndex++;
580  if ( mLastCustomColorIndex >= 16 )
581  {
582  mLastCustomColorIndex = 0;
583  }
584 }
585 
586 void QgsCompoundColorWidget::mSampleButton_clicked()
587 {
588  //activate picker color
589  setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Sampler ) );
590  grabMouse();
591  grabKeyboard();
592  mPickingColor = true;
593  setMouseTracking( true );
594 }
595 
596 void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
597 {
598  //disable radio buttons if not using the first tab, as they have no meaning for other tabs
599  bool enabled = index == 0;
600  mRedRadio->setEnabled( enabled );
601  mBlueRadio->setEnabled( enabled );
602  mGreenRadio->setEnabled( enabled );
603  mHueRadio->setEnabled( enabled );
604  mSaturationRadio->setEnabled( enabled );
605  mValueRadio->setEnabled( enabled );
606 }
607 
608 void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
609 {
610  QgsUserColorScheme *scheme = dynamic_cast< QgsUserColorScheme * >( mSchemeList->scheme() );
611  if ( scheme )
612  {
613  scheme->setShowSchemeInMenu( state );
614  }
615 }
616 
617 QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
618 {
619  const QList< QScreen * > screens = QGuiApplication::screens();
620  for ( QScreen *screen : screens )
621  {
622  if ( screen->geometry().contains( pos ) )
623  {
624  return screen;
625  }
626  }
627  return nullptr;
628 }
629 
630 void QgsCompoundColorWidget::saveSettings()
631 {
632  //save changes to scheme
633  if ( mSchemeList->isDirty() )
634  {
635  mSchemeList->saveColorsToScheme();
636  }
637 
638  QgsSettings settings;
639 
640  //record active component
641  int activeRadio = 0;
642  if ( mHueRadio->isChecked() )
643  activeRadio = 0;
644  if ( mSaturationRadio->isChecked() )
645  activeRadio = 1;
646  if ( mValueRadio->isChecked() )
647  activeRadio = 2;
648  if ( mRedRadio->isChecked() )
649  activeRadio = 3;
650  if ( mGreenRadio->isChecked() )
651  activeRadio = 4;
652  if ( mBlueRadio->isChecked() )
653  activeRadio = 5;
654  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeComponent" ), activeRadio );
655 
656  //record current scheme
657  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeScheme" ), mSchemeComboBox->currentIndex() );
658 
659  //record current tab
660  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeTab" ), mTabWidget->currentIndex() );
661 
662  //record custom colors
663  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( mSwatchButton1->color() ) );
664  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( mSwatchButton2->color() ) );
665  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( mSwatchButton3->color() ) );
666  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( mSwatchButton4->color() ) );
667  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( mSwatchButton5->color() ) );
668  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( mSwatchButton6->color() ) );
669  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( mSwatchButton7->color() ) );
670  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( mSwatchButton8->color() ) );
671  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( mSwatchButton9->color() ) );
672  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( mSwatchButton10->color() ) );
673  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( mSwatchButton11->color() ) );
674  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( mSwatchButton12->color() ) );
675  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( mSwatchButton13->color() ) );
676  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( mSwatchButton14->color() ) );
677  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( mSwatchButton15->color() ) );
678  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( mSwatchButton16->color() ) );
679 
680  //sample radius
681  settings.setValue( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), mSpinBoxRadius->value() );
682 }
683 
684 void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
685 {
686  //release mouse and keyboard, and reset cursor
687  releaseMouse();
688  releaseKeyboard();
689  unsetCursor();
690  setMouseTracking( false );
691  mPickingColor = false;
692 
693  if ( !takeSample )
694  {
695  //not sampling color, nothing more to do
696  return;
697  }
698 
699  //grab snapshot of pixel under mouse cursor
700  QColor snappedColor = sampleColor( eventPos );
701  mSamplePreview->setColor( snappedColor );
702  mColorPreview->setColor( snappedColor, true );
703 }
704 
706 {
707  if ( !color.isValid() )
708  {
709  return;
710  }
711 
712  QColor fixedColor = QColor( color );
713  if ( !mAllowAlpha )
714  {
715  //opacity disallowed, so don't permit transparent colors
716  fixedColor.setAlpha( 255 );
717  }
718  QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
719  const auto constColorWidgets = colorWidgets;
720  for ( QgsColorWidget *widget : constColorWidgets )
721  {
722  if ( widget == mSamplePreview )
723  {
724  continue;
725  }
726  widget->blockSignals( true );
727  widget->setColor( fixedColor );
728  widget->blockSignals( false );
729  }
730  emit currentColorChanged( fixedColor );
731 }
732 
734 {
735  mOldColorLabel->setVisible( color.isValid() );
736  mColorPreview->setColor2( color );
737 }
738 
740 {
741  if ( mPickingColor )
742  {
743  //don't show dialog if in color picker mode
744  e->accept();
745  return;
746  }
747 
748  QWidget::mousePressEvent( e );
749 }
750 
751 QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
752 {
753  QRgb tmpRgb;
754  int colorCount = 0;
755  int sumRed = 0;
756  int sumBlue = 0;
757  int sumGreen = 0;
758  //scan through image and sum rgb components
759  for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
760  {
761  const QRgb *scanLine = reinterpret_cast< const QRgb * >( image.constScanLine( heightIndex ) );
762  for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
763  {
764  tmpRgb = scanLine[widthIndex];
765  sumRed += qRed( tmpRgb );
766  sumBlue += qBlue( tmpRgb );
767  sumGreen += qGreen( tmpRgb );
768  colorCount++;
769  }
770  }
771  //calculate average components as floats
772  double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
773  double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
774  double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
775 
776  //create a new color representing the average
777  return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
778 }
779 
780 QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
781 {
782  int sampleRadius = mSpinBoxRadius->value() - 1;
783  QScreen *screen = findScreenAt( point );
784  if ( ! screen )
785  {
786  return QColor();
787  }
788  QPixmap snappedPixmap = screen->grabWindow( QApplication::desktop()->winId(),
789  point.x() - sampleRadius,
790  point.y() - sampleRadius,
791  1 + sampleRadius * 2,
792  1 + sampleRadius * 2 );
793  QImage snappedImage = snappedPixmap.toImage();
794  //scan all pixels and take average color
795  return averageColor( snappedImage );
796 }
797 
799 {
800  if ( mPickingColor )
801  {
802  //currently in color picker mode
803  //sample color under cursor update preview widget to give feedback to user
804  QColor hoverColor = sampleColor( e->globalPos() );
805  mSamplePreview->setColor( hoverColor );
806 
807  e->accept();
808  return;
809  }
810 
811  QWidget::mouseMoveEvent( e );
812 }
813 
815 {
816  if ( mPickingColor )
817  {
818  //end color picking operation by sampling the color under cursor
819  stopPicking( e->globalPos() );
820  e->accept();
821  return;
822  }
823 
824  QWidget::mouseReleaseEvent( e );
825 }
826 
828 {
829  if ( !mPickingColor )
830  {
831  //if not picking a color, use default tool button behavior
833  return;
834  }
835 
836  //cancel picking, sampling the color if space was pressed
837  stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
838 }
839 
840 void QgsCompoundColorWidget::mHueRadio_toggled( bool checked )
841 {
842  if ( checked )
843  {
844  mColorBox->setComponent( QgsColorWidget::Hue );
845  mVerticalRamp->setComponent( QgsColorWidget::Hue );
846  }
847 }
848 
849 void QgsCompoundColorWidget::mSaturationRadio_toggled( bool checked )
850 {
851  if ( checked )
852  {
853  mColorBox->setComponent( QgsColorWidget::Saturation );
854  mVerticalRamp->setComponent( QgsColorWidget::Saturation );
855  }
856 }
857 
858 void QgsCompoundColorWidget::mValueRadio_toggled( bool checked )
859 {
860  if ( checked )
861  {
862  mColorBox->setComponent( QgsColorWidget::Value );
863  mVerticalRamp->setComponent( QgsColorWidget::Value );
864  }
865 }
866 
867 void QgsCompoundColorWidget::mRedRadio_toggled( bool checked )
868 {
869  if ( checked )
870  {
871  mColorBox->setComponent( QgsColorWidget::Red );
872  mVerticalRamp->setComponent( QgsColorWidget::Red );
873  }
874 }
875 
876 void QgsCompoundColorWidget::mGreenRadio_toggled( bool checked )
877 {
878  if ( checked )
879  {
880  mColorBox->setComponent( QgsColorWidget::Green );
881  mVerticalRamp->setComponent( QgsColorWidget::Green );
882  }
883 }
884 
885 void QgsCompoundColorWidget::mBlueRadio_toggled( bool checked )
886 {
887  if ( checked )
888  {
889  mColorBox->setComponent( QgsColorWidget::Blue );
890  mVerticalRamp->setComponent( QgsColorWidget::Blue );
891  }
892 }
893 
894 void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
895 {
896  mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
897 }
898 
899 void QgsCompoundColorWidget::updateActionsForCurrentScheme()
900 {
901  QgsColorScheme *scheme = mSchemeList->scheme();
902 
903  mActionImportColors->setEnabled( scheme->isEditable() );
904  mActionPasteColors->setEnabled( scheme->isEditable() );
905  mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
906  mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
907 
908  QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
909  mActionRemovePalette->setEnabled( static_cast< bool >( userScheme ) );
910  if ( userScheme )
911  {
912  mActionShowInButtons->setEnabled( true );
913  whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
914  }
915  else
916  {
917  whileBlocking( mActionShowInButtons )->setChecked( false );
918  mActionShowInButtons->setEnabled( false );
919  }
920 }
A color scheme which stores its colors in a gpl palette file within the "palettes" subfolder off the ...
void setName(const QString &name)
Sets the name for the scheme.
A base class for interactive color widgets.
void colorSelected(const QColor &color)
Emitted when a color is selected from the list.
Emit colorClicked signal only, no dialog.
Value component of color (based on HSV model)
bool erase()
Erases the associated gpl palette file from the users "palettes" folder.
static QString qgisSettingsDirPath()
Returns the path to the settings directory in user&#39;s home dir.
void removeSelection()
Removes any selected colors from the list.
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:139
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
void mouseMoveEvent(QMouseEvent *e) override
int scaleIconSize(int standardSize)
Scales an icon size to compensate for display pixel density, making the icon size hi-dpi friendly...
Abstract base class for color schemes.
bool setColors(const QgsNamedColorList &colors, const QString &context=QString(), const QColor &baseColor=QColor()) override
Sets the colors for the scheme.
void colorClicked(const QColor &color)
Emitted when the button is clicked, if the button&#39;s behavior is set to SignalOnly.
static QgsUserColorScheme * importUserPaletteFromFile(QWidget *parent)
Triggers a user prompt for importing a new color scheme from an existing GPL file.
QgsColorScheme::SchemeFlags flags() const override
Returns the current flags for the color scheme.
Base class for any widget that can be shown as a inline panel.
void pasteColors()
Pastes colors from clipboard to the list.
Show scheme in color button drop-down menu.
bool removeColorScheme(QgsColorScheme *scheme)
Removes all matching color schemes from the registry.
void mousePressEvent(QMouseEvent *e) override
Green component of color.
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
void setColor(const QColor &color)
Sets the current color for the dialog.
void showExportColorsDialog()
Displays a file picker dialog allowing users to export colors from the list into a file...
Red component of color.
static QgsNamedColorList importColorsFromGpl(QFile &file, bool &ok, QString &name)
Imports colors from a gpl GIMP palette file.
void setShowSchemeInMenu(bool show)
Sets whether a this scheme should be shown in color button menus.
static bool removeUserPalette(QgsUserColorScheme *scheme, QWidget *parent)
Triggers a user prompt for removing an existing user color scheme.
void showImportColorsDialog()
Displays a file picker dialog allowing users to import colors into the list from a file...
Alpha component (opacity) of color.
static QString colorToName(const QColor &color)
Returns a friendly display name for a color.
QList< QPair< QColor, QString > > QgsNamedColorList
List of colors paired with a friendly display name identifying the color.
Blue component of color.
Hue component of color (based on HSV model)
void keyPressEvent(QKeyEvent *e) override
QSize iconSize(bool dockableToolbar)
Returns the user-preferred size of a window&#39;s toolbar icons.
void addColorScheme(QgsColorScheme *scheme)
Adds a color scheme to the registry.
static void addRecentColor(const QColor &color)
Adds a color to the list of recent colors.
void setAllowOpacity(bool allowOpacity)
Sets whether opacity modification (transparency) is permitted for the color dialog.
void keyPressEvent(QKeyEvent *event) override
Overridden key press event to handle the esc event on the widget.
Show scheme in color picker dialog.
void setPreviousColor(const QColor &color)
Sets the color to show in an optional "previous color" section.
QColor color() const
Returns the current color for the dialog.
virtual bool isEditable() const
Returns whether the color scheme is editable.
static QgsColorSchemeRegistry * colorSchemeRegistry()
Returns the application&#39;s color scheme registry, used for managing color schemes. ...
QgsSignalBlocker< Object > whileBlocking(Object *object)
Temporarily blocks signals from a QObject while calling a single method from the object.
Definition: qgis.h:212
Saturation component of color (based on HSV model)
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
QString schemeName() const override
Gets the name for the color scheme.
void copyColors()
Copies colors from the list to the clipboard.
QgsCompoundColorWidget(QWidget *parent=nullptr, const QColor &color=QColor(), Layout layout=LayoutDefault)
Constructor for QgsCompoundColorWidget.
void currentColorChanged(const QColor &color)
Emitted when the dialog&#39;s color changes.
void colorChanged(const QColor &color)
Emitted when the widget&#39;s color changes.
static QCursor getThemeCursor(Cursor cursor)
Helper to get a theme cursor.
void mouseReleaseEvent(QMouseEvent *e) override
Use a narrower, vertically stacked layout.
static QgsUserColorScheme * createNewUserPalette(QWidget *parent)
Triggers a user prompt for creating a new user color scheme.