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