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