QGIS API Documentation 3.27.0-Master (9c08adf5ef)
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
17#include "qgscolorscheme.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#include <QRegularExpression>
35
36QgsCompoundColorWidget::QgsCompoundColorWidget( QWidget *parent, const QColor &color, Layout widgetLayout )
37 : QgsPanelWidget( parent )
38{
39 setupUi( this );
40 connect( mHueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mHueRadio_toggled );
41 connect( mSaturationRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mSaturationRadio_toggled );
42 connect( mValueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mValueRadio_toggled );
43 connect( mRedRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mRedRadio_toggled );
44 connect( mGreenRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mGreenRadio_toggled );
45 connect( mBlueRadio, &QRadioButton::toggled, this, &QgsCompoundColorWidget::mBlueRadio_toggled );
46 connect( mAddColorToSchemeButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddColorToSchemeButton_clicked );
47 connect( mAddCustomColorButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mAddCustomColorButton_clicked );
48 connect( mSampleButton, &QPushButton::clicked, this, &QgsCompoundColorWidget::mSampleButton_clicked );
49 connect( mTabWidget, &QTabWidget::currentChanged, this, &QgsCompoundColorWidget::mTabWidget_currentChanged );
50 connect( mActionShowInButtons, &QAction::toggled, this, &QgsCompoundColorWidget::mActionShowInButtons_toggled );
51
52 if ( widgetLayout == LayoutVertical )
53 {
54 // shuffle stuff around
55 QVBoxLayout *newLayout = new QVBoxLayout();
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 const QgsSettings settings;
66
67 mSchemeList->header()->hide();
68 mSchemeList->setColumnWidth( 0, static_cast< int >( Qgis::UI_SCALE_FACTOR * fontMetrics().horizontalAdvance( 'X' ) * 6 ) );
69
70 //get schemes with ShowInColorDialog set
71 refreshSchemeComboBox();
72 const 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 const 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 const int currentTab = settings.value( QStringLiteral( "Windows/ColorDialog/activeTab" ), 0 ).toInt();
255 mTabWidget->setCurrentIndex( currentTab );
256
257 //setup connections
259 connect( mColorWheel, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
261 connect( mVerticalRamp, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
263 connect( mGreenSlider, &QgsColorWidget::colorChanged, this, &QgsCompoundColorWidget::setColor );
264 connect( mBlueSlider, &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 if ( !mDiscarded )
291 {
293 }
294}
295
297{
298 //all widgets should have the same color, so it shouldn't matter
299 //which we fetch it from
300 return mColorPreview->color();
301}
302
303void QgsCompoundColorWidget::setAllowOpacity( const bool allowOpacity )
304{
305 mAllowAlpha = allowOpacity;
306 mAlphaLabel->setVisible( allowOpacity );
307 mAlphaSlider->setVisible( allowOpacity );
308 if ( !allowOpacity )
309 {
310 mAlphaLayout->setContentsMargins( 0, 0, 0, 0 );
311 mAlphaLayout->setSpacing( 0 );
312 }
313}
314
315void QgsCompoundColorWidget::refreshSchemeComboBox()
316{
317 mSchemeComboBox->blockSignals( true );
318 mSchemeComboBox->clear();
319 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
320 QList<QgsColorScheme *>::const_iterator schemeIt = schemeList.constBegin();
321 for ( ; schemeIt != schemeList.constEnd(); ++schemeIt )
322 {
323 mSchemeComboBox->addItem( ( *schemeIt )->schemeName() );
324 }
325 mSchemeComboBox->blockSignals( false );
326}
327
328
330{
331 QgsSettings s;
332 const QString lastDir = s.value( QStringLiteral( "/UI/lastGplPaletteDir" ), QDir::homePath() ).toString();
333 const QString filePath = QFileDialog::getOpenFileName( parent, tr( "Select Palette File" ), lastDir, QStringLiteral( "GPL (*.gpl);;All files (*.*)" ) );
334 if ( parent )
335 parent->activateWindow();
336 if ( filePath.isEmpty() )
337 {
338 return nullptr;
339 }
340
341 //check if file exists
342 const QFileInfo fileInfo( filePath );
343 if ( !fileInfo.exists() || !fileInfo.isReadable() )
344 {
345 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Error, file does not exist or is not readable." ) );
346 return nullptr;
347 }
348
349 s.setValue( QStringLiteral( "/UI/lastGplPaletteDir" ), fileInfo.absolutePath() );
350 QFile file( filePath );
351
352 QgsNamedColorList importedColors;
353 bool ok = false;
354 QString paletteName;
355 importedColors = QgsSymbolLayerUtils::importColorsFromGpl( file, ok, paletteName );
356 if ( !ok )
357 {
358 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "Palette file is not readable." ) );
359 return nullptr;
360 }
361
362 if ( importedColors.length() == 0 )
363 {
364 //no imported colors
365 QMessageBox::critical( nullptr, tr( "Import Color Palette" ), tr( "No colors found in palette file." ) );
366 return nullptr;
367 }
368
369 //TODO - handle conflicting file names, name for new palette
370 QgsUserColorScheme *importedScheme = new QgsUserColorScheme( fileInfo.fileName() );
371 importedScheme->setName( paletteName );
372 importedScheme->setColors( importedColors );
373
375 return importedScheme;
376}
377
378void QgsCompoundColorWidget::importPalette()
379{
380 if ( importUserPaletteFromFile( this ) )
381 {
382 //refresh combobox
383 refreshSchemeComboBox();
384 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
385 }
386}
387
388
390{
391 if ( QMessageBox::question( parent, tr( "Remove Color Palette" ),
392 tr( "Are you sure you want to remove %1?" ).arg( scheme->schemeName() ),
393 QMessageBox::Yes | QMessageBox::No, QMessageBox::No ) != QMessageBox::Yes )
394 {
395 //user canceled
396 return false;
397 }
398
399 //remove palette and associated gpl file
400 if ( !scheme->erase() )
401 {
402 //something went wrong
403 return false;
404 }
405
406 //remove scheme from registry
408 return true;
409}
410
411void QgsCompoundColorWidget::removePalette()
412{
413 //get current scheme
414 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
415 int prevIndex = mSchemeComboBox->currentIndex();
416 if ( prevIndex >= schemeList.length() )
417 {
418 return;
419 }
420
421 //make user scheme is a user removable scheme
422 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( schemeList.at( prevIndex ) );
423 if ( !userScheme )
424 {
425 return;
426 }
427
428 if ( removeUserPalette( userScheme, this ) )
429 {
430 refreshSchemeComboBox();
431 prevIndex = std::max( std::min( prevIndex, mSchemeComboBox->count() - 1 ), 0 );
432 mSchemeComboBox->setCurrentIndex( prevIndex );
433 }
434}
435
437{
438 bool ok = false;
439 const QString name = QInputDialog::getText( parent, tr( "Create New Palette" ), tr( "Enter a name for the new palette:" ),
440 QLineEdit::Normal, tr( "New palette" ), &ok );
441
442 if ( !ok || name.isEmpty() )
443 {
444 //user canceled
445 return nullptr;
446 }
447
448 //generate file name for new palette
449 const QDir palettePath( gplFilePath() );
450 const thread_local QRegularExpression badChars( "[,^@={}\\[\\]~!?:&*\"|#%<>$\"'();`' /\\\\]" );
451 QString filename = name.simplified().toLower().replace( badChars, QStringLiteral( "_" ) );
452 if ( filename.isEmpty() )
453 {
454 filename = tr( "new_palette" );
455 }
456 QFileInfo destFileInfo( palettePath.filePath( filename + ".gpl" ) );
457 int fileNumber = 1;
458 while ( destFileInfo.exists() )
459 {
460 //try to generate a unique file name
461 destFileInfo = QFileInfo( palettePath.filePath( filename + QStringLiteral( "%1.gpl" ).arg( fileNumber ) ) );
462 fileNumber++;
463 }
464
465 QgsUserColorScheme *newScheme = new QgsUserColorScheme( destFileInfo.fileName() );
466 newScheme->setName( name );
467
469 return newScheme;
470}
471
472void QgsCompoundColorWidget::newPalette()
473{
474 if ( createNewUserPalette( this ) )
475 {
476 //refresh combobox and set new scheme as active
477 refreshSchemeComboBox();
478 mSchemeComboBox->setCurrentIndex( mSchemeComboBox->count() - 1 );
479 }
480}
481
482QString QgsCompoundColorWidget::gplFilePath()
483{
484 QString palettesDir = QgsApplication::qgisSettingsDirPath() + "palettes";
485
486 const QDir localDir;
487 if ( !localDir.mkpath( palettesDir ) )
488 {
489 return QString();
490 }
491
492 return palettesDir;
493}
494
495void QgsCompoundColorWidget::schemeIndexChanged( int index )
496{
497 //save changes to scheme
498 if ( mSchemeList->isDirty() )
499 {
500 mSchemeList->saveColorsToScheme();
501 }
502
503 //get schemes with ShowInColorDialog set
504 const QList<QgsColorScheme *> schemeList = QgsApplication::colorSchemeRegistry()->schemes( QgsColorScheme::ShowInColorDialog );
505 if ( index >= schemeList.length() )
506 {
507 return;
508 }
509
510 QgsColorScheme *scheme = schemeList.at( index );
511 mSchemeList->setScheme( scheme );
512
513 updateActionsForCurrentScheme();
514
515 //copy action defaults to disabled
516 mActionCopyColors->setEnabled( false );
517}
518
519void QgsCompoundColorWidget::listSelectionChanged( const QItemSelection &selected, const QItemSelection &deselected )
520{
521 Q_UNUSED( deselected )
522 mActionCopyColors->setEnabled( selected.length() > 0 );
523}
524
525void QgsCompoundColorWidget::mAddCustomColorButton_clicked()
526{
527 switch ( mLastCustomColorIndex )
528 {
529 case 0:
530 mSwatchButton1->setColor( mColorPreview->color() );
531 break;
532 case 1:
533 mSwatchButton2->setColor( mColorPreview->color() );
534 break;
535 case 2:
536 mSwatchButton3->setColor( mColorPreview->color() );
537 break;
538 case 3:
539 mSwatchButton4->setColor( mColorPreview->color() );
540 break;
541 case 4:
542 mSwatchButton5->setColor( mColorPreview->color() );
543 break;
544 case 5:
545 mSwatchButton6->setColor( mColorPreview->color() );
546 break;
547 case 6:
548 mSwatchButton7->setColor( mColorPreview->color() );
549 break;
550 case 7:
551 mSwatchButton8->setColor( mColorPreview->color() );
552 break;
553 case 8:
554 mSwatchButton9->setColor( mColorPreview->color() );
555 break;
556 case 9:
557 mSwatchButton10->setColor( mColorPreview->color() );
558 break;
559 case 10:
560 mSwatchButton11->setColor( mColorPreview->color() );
561 break;
562 case 11:
563 mSwatchButton12->setColor( mColorPreview->color() );
564 break;
565 case 12:
566 mSwatchButton13->setColor( mColorPreview->color() );
567 break;
568 case 13:
569 mSwatchButton14->setColor( mColorPreview->color() );
570 break;
571 case 14:
572 mSwatchButton15->setColor( mColorPreview->color() );
573 break;
574 case 15:
575 mSwatchButton16->setColor( mColorPreview->color() );
576 break;
577 }
578 mLastCustomColorIndex++;
579 if ( mLastCustomColorIndex >= 16 )
580 {
581 mLastCustomColorIndex = 0;
582 }
583}
584
585void QgsCompoundColorWidget::mSampleButton_clicked()
586{
587 //activate picker color
588 setCursor( QgsApplication::getThemeCursor( QgsApplication::Cursor::Sampler ) );
589 grabMouse();
590 grabKeyboard();
591 mPickingColor = true;
592 setMouseTracking( true );
593}
594
595void QgsCompoundColorWidget::mTabWidget_currentChanged( int index )
596{
597 //disable radio buttons if not using the first tab, as they have no meaning for other tabs
598 const bool enabled = index == 0;
599 mRedRadio->setEnabled( enabled );
600 mBlueRadio->setEnabled( enabled );
601 mGreenRadio->setEnabled( enabled );
602 mHueRadio->setEnabled( enabled );
603 mSaturationRadio->setEnabled( enabled );
604 mValueRadio->setEnabled( enabled );
605}
606
607void QgsCompoundColorWidget::mActionShowInButtons_toggled( bool state )
608{
609 QgsUserColorScheme *scheme = dynamic_cast< QgsUserColorScheme * >( mSchemeList->scheme() );
610 if ( scheme )
611 {
612 scheme->setShowSchemeInMenu( state );
613 }
614}
615
616QScreen *QgsCompoundColorWidget::findScreenAt( QPoint pos )
617{
618 const QList< QScreen * > screens = QGuiApplication::screens();
619 for ( QScreen *screen : screens )
620 {
621 if ( screen->geometry().contains( pos ) )
622 {
623 return screen;
624 }
625 }
626 return nullptr;
627}
628
629void 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
683void 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 const QColor snappedColor = sampleColor( eventPos );
700 mSamplePreview->setColor( snappedColor );
701 mColorPreview->setColor( snappedColor, true );
702}
703
704void QgsCompoundColorWidget::setColor( const QColor &color )
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 const QList<QgsColorWidget *> colorWidgets = this->findChildren<QgsColorWidget *>();
718 const auto constColorWidgets = colorWidgets;
719 for ( QgsColorWidget *widget : constColorWidgets )
720 {
721 if ( widget == mSamplePreview )
722 {
723 continue;
724 }
725 widget->blockSignals( true );
726 widget->setColor( fixedColor );
727 widget->blockSignals( false );
728 }
729 emit currentColorChanged( fixedColor );
730}
731
733{
734 mOldColorLabel->setVisible( color.isValid() );
735 mColorPreview->setColor2( color );
736}
737
739{
740 saveSettings();
741 QWidget::hideEvent( e );
742}
743
745{
746 if ( mPickingColor )
747 {
748 //don't show dialog if in color picker mode
749 e->accept();
750 return;
751 }
752
753 QWidget::mousePressEvent( e );
754}
755
756QColor QgsCompoundColorWidget::averageColor( const QImage &image ) const
757{
758 QRgb tmpRgb;
759 int colorCount = 0;
760 int sumRed = 0;
761 int sumBlue = 0;
762 int sumGreen = 0;
763 //scan through image and sum rgb components
764 for ( int heightIndex = 0; heightIndex < image.height(); ++heightIndex )
765 {
766 const QRgb *scanLine = reinterpret_cast< const QRgb * >( image.constScanLine( heightIndex ) );
767 for ( int widthIndex = 0; widthIndex < image.width(); ++widthIndex )
768 {
769 tmpRgb = scanLine[widthIndex];
770 sumRed += qRed( tmpRgb );
771 sumBlue += qBlue( tmpRgb );
772 sumGreen += qGreen( tmpRgb );
773 colorCount++;
774 }
775 }
776 //calculate average components as floats
777 const double avgRed = static_cast<double>( sumRed ) / ( 255.0 * colorCount );
778 const double avgGreen = static_cast<double>( sumGreen ) / ( 255.0 * colorCount );
779 const double avgBlue = static_cast<double>( sumBlue ) / ( 255.0 * colorCount );
780
781 //create a new color representing the average
782 return QColor::fromRgbF( avgRed, avgGreen, avgBlue );
783}
784
785QColor QgsCompoundColorWidget::sampleColor( QPoint point ) const
786{
787 const int sampleRadius = mSpinBoxRadius->value() - 1;
788 QScreen *screen = findScreenAt( point );
789 if ( ! screen )
790 {
791 return QColor();
792 }
793 const QPixmap snappedPixmap = screen->grabWindow( QApplication::desktop()->winId(),
794 point.x() - sampleRadius,
795 point.y() - sampleRadius,
796 1 + sampleRadius * 2,
797 1 + sampleRadius * 2 );
798 const QImage snappedImage = snappedPixmap.toImage();
799 //scan all pixels and take average color
800 return averageColor( snappedImage );
801}
802
804{
805 if ( mPickingColor )
806 {
807 //currently in color picker mode
808 //sample color under cursor update preview widget to give feedback to user
809 const QColor hoverColor = sampleColor( e->globalPos() );
810 mSamplePreview->setColor( hoverColor );
811
812 e->accept();
813 return;
814 }
815
816 QWidget::mouseMoveEvent( e );
817}
818
820{
821 if ( mPickingColor )
822 {
823 //end color picking operation by sampling the color under cursor
824 stopPicking( e->globalPos() );
825 e->accept();
826 return;
827 }
828
829 QWidget::mouseReleaseEvent( e );
830}
831
833{
834 if ( !mPickingColor )
835 {
836 //if not picking a color, use default tool button behavior
838 return;
839 }
840
841 //cancel picking, sampling the color if space was pressed
842 stopPicking( QCursor::pos(), e->key() == Qt::Key_Space );
843}
844
845void QgsCompoundColorWidget::mHueRadio_toggled( bool checked )
846{
847 if ( checked )
848 {
849 mColorBox->setComponent( QgsColorWidget::Hue );
850 mVerticalRamp->setComponent( QgsColorWidget::Hue );
851 }
852}
853
854void QgsCompoundColorWidget::mSaturationRadio_toggled( bool checked )
855{
856 if ( checked )
857 {
858 mColorBox->setComponent( QgsColorWidget::Saturation );
859 mVerticalRamp->setComponent( QgsColorWidget::Saturation );
860 }
861}
862
863void QgsCompoundColorWidget::mValueRadio_toggled( bool checked )
864{
865 if ( checked )
866 {
867 mColorBox->setComponent( QgsColorWidget::Value );
868 mVerticalRamp->setComponent( QgsColorWidget::Value );
869 }
870}
871
872void QgsCompoundColorWidget::mRedRadio_toggled( bool checked )
873{
874 if ( checked )
875 {
876 mColorBox->setComponent( QgsColorWidget::Red );
877 mVerticalRamp->setComponent( QgsColorWidget::Red );
878 }
879}
880
881void QgsCompoundColorWidget::mGreenRadio_toggled( bool checked )
882{
883 if ( checked )
884 {
885 mColorBox->setComponent( QgsColorWidget::Green );
886 mVerticalRamp->setComponent( QgsColorWidget::Green );
887 }
888}
889
890void QgsCompoundColorWidget::mBlueRadio_toggled( bool checked )
891{
892 if ( checked )
893 {
894 mColorBox->setComponent( QgsColorWidget::Blue );
895 mVerticalRamp->setComponent( QgsColorWidget::Blue );
896 }
897}
898
899void QgsCompoundColorWidget::mAddColorToSchemeButton_clicked()
900{
901 mSchemeList->addColor( mColorPreview->color(), QgsSymbolLayerUtils::colorToName( mColorPreview->color() ) );
902}
903
904void QgsCompoundColorWidget::updateActionsForCurrentScheme()
905{
906 QgsColorScheme *scheme = mSchemeList->scheme();
907
908 mActionImportColors->setEnabled( scheme->isEditable() );
909 mActionPasteColors->setEnabled( scheme->isEditable() );
910 mAddColorToSchemeButton->setEnabled( scheme->isEditable() );
911 mRemoveColorsFromSchemeButton->setEnabled( scheme->isEditable() );
912
913 QgsUserColorScheme *userScheme = dynamic_cast<QgsUserColorScheme *>( scheme );
914 mActionRemovePalette->setEnabled( static_cast< bool >( userScheme ) );
915 if ( userScheme )
916 {
917 mActionShowInButtons->setEnabled( true );
918 whileBlocking( mActionShowInButtons )->setChecked( userScheme->flags() & QgsColorScheme::ShowInColorButtonMenu );
919 }
920 else
921 {
922 whileBlocking( mActionShowInButtons )->setChecked( false );
923 mActionShowInButtons->setEnabled( false );
924 }
925}
static const double UI_SCALE_FACTOR
UI scaling factor.
Definition: qgis.h:2181
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:2330