QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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, 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  for ( QScreen *screen : QGuiApplication::screens() )
620  {
621  if ( screen->geometry().contains( pos ) )
622  {
623  return screen;
624  }
625  }
626  return nullptr;
627 }
628 
629 void QgsCompoundColorWidget::saveSettings()
630 {
631  //save changes to scheme
632  if ( mSchemeList->isDirty() )
633  {
634  mSchemeList->saveColorsToScheme();
635  }
636 
637  QgsSettings settings;
638 
639  //record active component
640  int activeRadio = 0;
641  if ( mHueRadio->isChecked() )
642  activeRadio = 0;
643  if ( mSaturationRadio->isChecked() )
644  activeRadio = 1;
645  if ( mValueRadio->isChecked() )
646  activeRadio = 2;
647  if ( mRedRadio->isChecked() )
648  activeRadio = 3;
649  if ( mGreenRadio->isChecked() )
650  activeRadio = 4;
651  if ( mBlueRadio->isChecked() )
652  activeRadio = 5;
653  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeComponent" ), activeRadio );
654 
655  //record current scheme
656  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeScheme" ), mSchemeComboBox->currentIndex() );
657 
658  //record current tab
659  settings.setValue( QStringLiteral( "Windows/ColorDialog/activeTab" ), mTabWidget->currentIndex() );
660 
661  //record custom colors
662  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor1" ), QVariant( mSwatchButton1->color() ) );
663  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor2" ), QVariant( mSwatchButton2->color() ) );
664  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor3" ), QVariant( mSwatchButton3->color() ) );
665  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor4" ), QVariant( mSwatchButton4->color() ) );
666  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor5" ), QVariant( mSwatchButton5->color() ) );
667  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor6" ), QVariant( mSwatchButton6->color() ) );
668  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor7" ), QVariant( mSwatchButton7->color() ) );
669  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor8" ), QVariant( mSwatchButton8->color() ) );
670  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor9" ), QVariant( mSwatchButton9->color() ) );
671  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor10" ), QVariant( mSwatchButton10->color() ) );
672  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor11" ), QVariant( mSwatchButton11->color() ) );
673  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor12" ), QVariant( mSwatchButton12->color() ) );
674  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor13" ), QVariant( mSwatchButton13->color() ) );
675  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor14" ), QVariant( mSwatchButton14->color() ) );
676  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor15" ), QVariant( mSwatchButton15->color() ) );
677  settings.setValue( QStringLiteral( "Windows/ColorDialog/customColor16" ), QVariant( mSwatchButton16->color() ) );
678 
679  //sample radius
680  settings.setValue( QStringLiteral( "Windows/ColorDialog/sampleRadius" ), mSpinBoxRadius->value() );
681 }
682 
683 void QgsCompoundColorWidget::stopPicking( QPoint eventPos, const bool takeSample )
684 {
685  //release mouse and keyboard, and reset cursor
686  releaseMouse();
687  releaseKeyboard();
688  unsetCursor();
689  setMouseTracking( false );
690  mPickingColor = false;
691 
692  if ( !takeSample )
693  {
694  //not sampling color, nothing more to do
695  return;
696  }
697 
698  //grab snapshot of pixel under mouse cursor
699  QColor snappedColor = sampleColor( eventPos );
700  mSamplePreview->setColor( snappedColor );
701  mColorPreview->setColor( snappedColor, true );
702 }
703 
705 {
706  if ( !color.isValid() )
707  {
708  return;
709  }
710 
711  QColor fixedColor = QColor( color );
712  if ( !mAllowAlpha )
713  {
714  //opacity disallowed, so don't permit transparent colors
715  fixedColor.setAlpha( 255 );
716  }
717  QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
718  Q_FOREACH ( QgsColorWidget *widget, colorWidgets )
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 
732 {
733  mOldColorLabel->setVisible( color.isValid() );
734  mColorPreview->setColor2( color );
735 }
736 
738 {
739  if ( mPickingColor )
740  {
741  //don't show dialog if in color picker mode
742  e->accept();
743  return;
744  }
745 
746  QWidget::mousePressEvent( e );
747 }
748 
749 QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
750 {
751  QRgb tmpRgb;
752  int colorCount = 0;
753  int sumRed = 0;
754  int sumBlue = 0;
755  int sumGreen = 0;
756  //scan through image and sum rgb components
757  for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
758  {
759  QRgb *scanLine = ( QRgb * )image.constScanLine( heightIndex );
760  for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
761  {
762  tmpRgb = scanLine[widthIndex];
763  sumRed += qRed( tmpRgb );
764  sumBlue += qBlue( tmpRgb );
765  sumGreen += qGreen( tmpRgb );
766  colorCount++;
767  }
768  }
769  //calculate average components as floats
770  double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
771  double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
772  double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
773 
774  //create a new color representing the average
775  return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
776 }
777 
778 QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
779 {
780  int sampleRadius = mSpinBoxRadius->value() - 1;
781  QScreen *screen = findScreenAt( point );
782  if ( ! screen )
783  {
784  return QColor();
785  }
786  QPixmap snappedPixmap = screen->grabWindow( QApplication::desktop()->winId(),
787  point.x() - sampleRadius,
788  point.y() - sampleRadius,
789  1 + sampleRadius * 2,
790  1 + sampleRadius * 2 );
791  QImage snappedImage = snappedPixmap.toImage();
792  //scan all pixels and take average color
793  return averageColor( snappedImage );
794 }
795 
797 {
798  if ( mPickingColor )
799  {
800  //currently in color picker mode
801  //sample color under cursor update preview widget to give feedback to user
802  QColor hoverColor = sampleColor( e->globalPos() );
803  mSamplePreview->setColor( hoverColor );
804 
805  e->accept();
806  return;
807  }
808 
809  QWidget::mouseMoveEvent( e );
810 }
811 
813 {
814  if ( mPickingColor )
815  {
816  //end color picking operation by sampling the color under cursor
817  stopPicking( e->globalPos() );
818  e->accept();
819  return;
820  }
821 
822  QWidget::mouseReleaseEvent( e );
823 }
824 
826 {
827  if ( !mPickingColor )
828  {
829  //if not picking a color, use default tool button behavior
831  return;
832  }
833 
834  //cancel picking, sampling the color if space was pressed
835  stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
836 }
837 
838 void QgsCompoundColorWidget::mHueRadio_toggled( bool checked )
839 {
840  if ( checked )
841  {
842  mColorBox->setComponent( QgsColorWidget::Hue );
843  mVerticalRamp->setComponent( QgsColorWidget::Hue );
844  }
845 }
846 
847 void QgsCompoundColorWidget::mSaturationRadio_toggled( bool checked )
848 {
849  if ( checked )
850  {
851  mColorBox->setComponent( QgsColorWidget::Saturation );
852  mVerticalRamp->setComponent( QgsColorWidget::Saturation );
853  }
854 }
855 
856 void QgsCompoundColorWidget::mValueRadio_toggled( bool checked )
857 {
858  if ( checked )
859  {
860  mColorBox->setComponent( QgsColorWidget::Value );
861  mVerticalRamp->setComponent( QgsColorWidget::Value );
862  }
863 }
864 
865 void QgsCompoundColorWidget::mRedRadio_toggled( bool checked )
866 {
867  if ( checked )
868  {
869  mColorBox->setComponent( QgsColorWidget::Red );
870  mVerticalRamp->setComponent( QgsColorWidget::Red );
871  }
872 }
873 
874 void QgsCompoundColorWidget::mGreenRadio_toggled( bool checked )
875 {
876  if ( checked )
877  {
878  mColorBox->setComponent( QgsColorWidget::Green );
879  mVerticalRamp->setComponent( QgsColorWidget::Green );
880  }
881 }
882 
883 void QgsCompoundColorWidget::mBlueRadio_toggled( bool checked )
884 {
885  if ( checked )
886  {
887  mColorBox->setComponent( QgsColorWidget::Blue );
888  mVerticalRamp->setComponent( QgsColorWidget::Blue );
889  }
890 }
891 
892 void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
893 {
894  mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
895 }
896 
897 void QgsCompoundColorWidget::updateActionsForCurrentScheme()
898 {
899  QgsColorScheme *scheme = mSchemeList->scheme();
900 
901  mActionImportColors->setEnabled( scheme->isEditable() );
902  mActionPasteColors->setEnabled( scheme->isEditable() );
903  mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
904  mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
905 
906  QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
907  mActionRemovePalette->setEnabled( static_cast< bool >( userScheme ) );
908  if ( userScheme )
909  {
910  mActionShowInButtons->setEnabled( true );
911  whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
912  }
913  else
914  {
915  whileBlocking( mActionShowInButtons )->setChecked( false );
916  mActionShowInButtons->setEnabled( false );
917  }
918 }
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.
QColor color() const
Returns the current color for the 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:152
This class is a composition of two QSettings instances:
Definition: qgssettings.h:58
virtual void setColor(const QColor &color, bool emitSignals=false)
Sets the color for the widget.
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.
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
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.
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.
virtual bool isEditable() const
Returns whether the color scheme is editable.
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
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.
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:225
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
QList< QgsColorScheme * > schemes() const
Returns all color schemes in the registry.
Use a narrower, vertically stacked layout.
static QgsUserColorScheme * createNewUserPalette(QWidget *parent)
Triggers a user prompt for creating a new user color scheme.