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