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