KiCad PCB EDA Suite
Loading...
Searching...
No Matches
panel_color_settings.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2020 Jon Evans <[email protected]>
5 * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <bitmaps.h>
22#include <launch_ext.h>
23#include <layer_ids.h>
25#include <pgm_base.h>
29#include <validators.h>
30#include <widgets/ui_common.h>
32#include <widgets/wx_panel.h>
33
34#include <wx/msgdlg.h>
35#include <wx/menu.h>
36#include <wx/textdlg.h>
37
38// Button ID starting point
39constexpr int FIRST_BUTTON_ID = 1800;
40
41
44 m_currentSettings( nullptr ),
45 m_swatches(),
46 m_copied( COLOR4D::UNSPECIFIED ),
47 m_validLayers(),
48 m_backgroundLayer( LAYER_PCB_BACKGROUND ),
49 m_colorNamespace()
50{
51#ifdef __APPLE__
52 m_btnOpenFolder->SetLabel( _( "Reveal Themes in Finder" ) );
53
54 // Simple border is too dark on OSX
55 m_colorsListWindow->SetWindowStyle( wxBORDER_SUNKEN|wxVSCROLL );
56#endif
57
58 m_cbTheme->SetMinSize( FromDIP( m_cbTheme->GetMinSize() ) );
59 m_colorsListWindow->SetMinSize( FromDIP( m_colorsListWindow->GetMinSize() ) );
60 m_colorsGridSizer->SetMinSize( FromDIP( m_colorsGridSizer->GetMinSize() ) );
61 m_panel1->SetBorders( false, false, true, false );
62}
63
64
66{
68 LaunchExternal( dir );
69}
70
71
73{
75 return;
76
77 for( const std::pair<const int, COLOR_SWATCH*>& pair : m_swatches )
78 {
79 int layer = pair.first;
80 COLOR_SWATCH* button = pair.second;
81
82 COLOR4D defaultColor = m_currentSettings->GetDefaultColor( layer );
83
84 m_currentSettings->SetColor( layer, defaultColor );
85 button->SetSwatchColor( defaultColor, false );
86 }
87}
88
89
91{
92 if( show )
93 {
94 // In case changes have been made to the current theme in another panel:
95 int idx = m_cbTheme->GetSelection();
96 COLOR_SETTINGS* settings = static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( idx ) );
97
98 if( settings )
99 *m_currentSettings = *settings;
100
103 }
104
105 return PANEL_COLOR_SETTINGS_BASE::Show( show );
106}
107
108
109void PANEL_COLOR_SETTINGS::OnLeftDownTheme( wxMouseEvent& event )
110{
111 // Lazy rebuild of theme menu to catch any colour theme changes made in other panels
113
114 event.Skip();
115}
116
117
118void PANEL_COLOR_SETTINGS::OnThemeChanged( wxCommandEvent& event )
119{
120 int idx = m_cbTheme->GetSelection();
121
122 if( idx == static_cast<int>( m_cbTheme->GetCount() ) - 2 )
123 {
124 m_cbTheme->SetStringSelection( GetSettingsDropdownName( m_currentSettings ) );
125 return;
126 }
127
128 if( idx == (int)m_cbTheme->GetCount() - 1 )
129 {
130 // New Theme...
131
132 if( !saveCurrentTheme( false ) )
133 return;
134
135 FOOTPRINT_NAME_VALIDATOR themeNameValidator;
136 wxTextEntryDialog dlg( wxGetTopLevelParent( this ), _( "New theme name:" ),
137 _( "Add Color Theme" ) );
138 dlg.SetTextValidator( themeNameValidator );
139
140 if( dlg.ShowModal() != wxID_OK )
141 return;
142
143 wxString themeName = dlg.GetValue();
144 wxFileName fn( themeName + wxT( ".json" ) );
146
147 if( fn.Exists() )
148 {
149 wxMessageBox( _( "Theme already exists!" ) );
150 return;
151 }
152
153 SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
154 COLOR_SETTINGS* newSettings = settingsMgr.AddNewColorSettings( themeName );
155 newSettings->SetName( themeName );
156 newSettings->SetReadOnly( false );
157
158 for( int layer : m_validLayers )
159 newSettings->SetColor( layer, m_currentSettings->GetColor( layer ) );
160
161 newSettings->SaveToFile( settingsMgr.GetPathForSettingsFile( newSettings ) );
162
163 idx = m_cbTheme->Insert( themeName, idx - 1, static_cast<void*>( newSettings ) );
164 m_cbTheme->SetSelection( idx );
165
166 m_optOverrideColors->SetValue( newSettings->GetOverrideSchItemColors() );
167 m_optOverrideColors->Enable( !newSettings->IsReadOnly() );
168
169 *m_currentSettings = *newSettings;
172 }
173 else
174 {
175 COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( idx ) );
176
177 if( selected->GetFilename() != m_currentSettings->GetFilename() )
178 {
179 if( !saveCurrentTheme( false ) )
180 return;
181
182 m_optOverrideColors->SetValue( selected->GetOverrideSchItemColors() );
183 m_optOverrideColors->Enable( !selected->IsReadOnly() );
184
185 *m_currentSettings = *selected;
188 }
189 }
190}
191
192
194{
195 if( m_swatches.empty() )
196 {
198 }
199 else
200 {
201 bool isReadOnly = m_currentSettings->IsReadOnly();
203
204 for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
205 {
206 pair.second->SetSwatchBackground( background );
207 pair.second->SetSwatchColor( m_currentSettings->GetColor( pair.first ), false );
208 pair.second->SetReadOnly( isReadOnly );
209 }
210 }
211}
212
213
214void PANEL_COLOR_SETTINGS::createThemeList( const wxString& aCurrent )
215{
216 int width = 0;
217 int height = 0;
218
219 m_cbTheme->GetTextExtent( _( "New Theme..." ), &width, &height );
220 int minwidth = width;
221
222 m_cbTheme->Clear();
223
224 for( COLOR_SETTINGS* settings : Pgm().GetSettingsManager().GetColorSettingsList() )
225 {
226 wxString name = GetSettingsDropdownName( settings );
227
228 int pos = m_cbTheme->Append( name, static_cast<void*>( settings ) );
229
230 if( settings->GetFilename() == aCurrent )
231 m_cbTheme->SetSelection( pos );
232
233 m_cbTheme->GetTextExtent( name, &width, &height );
234 minwidth = std::max( minwidth, width );
235 }
236
237 m_cbTheme->Append( wxT( "---" ) );
238 m_cbTheme->Append( _( "New Theme..." ) );
239
240 m_cbTheme->SetMinSize( wxSize( minwidth + 50, -1 ) );
241}
242
243
244void PANEL_COLOR_SETTINGS::createSwatch( int aLayer, const wxString& aName )
245{
246 wxStaticText* label = new wxStaticText( m_colorsListWindow, wxID_ANY, aName );
247
248 // The previously selected theme can be deleted and cannot be selected.
249 // so select the default theme (first theme of the list)
250 if( m_cbTheme->GetSelection() < 0 )
251 {
252 m_cbTheme->SetSelection( 0 );
254 }
255
256 void* clientData = m_cbTheme->GetClientData( m_cbTheme->GetSelection() );
257 COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( clientData );
258
259 int id = FIRST_BUTTON_ID + aLayer;
260 COLOR4D defaultColor = selected->GetDefaultColor( aLayer );
263
264 COLOR_SWATCH* swatch = new COLOR_SWATCH( m_colorsListWindow, color, id, backgroundColor,
265 defaultColor, SWATCH_MEDIUM );
266 swatch->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
267
268 // Display the swatches in the left column and the layer name in the right column
269 // The right column is sometimes (depending on the translation) truncated for long texts.
270 // We cannot allow swatches to be truncated or not shown
271 m_colorsGridSizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3 );
272 m_colorsGridSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxLEFT, 5 );
273
274 m_labels[aLayer] = label;
275 m_swatches[aLayer] = swatch;
276
277 swatch->Bind( wxEVT_RIGHT_DOWN,
278 [&, aLayer]( wxMouseEvent& aEvent )
279 {
280 ShowColorContextMenu( aEvent, aLayer );
281 } );
282
283 swatch->Bind( COLOR_SWATCH_CHANGED, &PANEL_COLOR_SETTINGS::OnColorChanged, this );
284}
285
286
287void PANEL_COLOR_SETTINGS::ShowColorContextMenu( wxMouseEvent& aEvent, int aLayer )
288{
289 auto selected =
290 static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( m_cbTheme->GetSelection() ) );
291
292 wxCHECK_RET( selected, wxT( "Invalid color theme selected" ) );
293
294 COLOR4D current = m_currentSettings->GetColor( aLayer );
295 COLOR4D saved = selected->GetColor( aLayer );
296 bool readOnly = m_currentSettings->IsReadOnly();
297
298 wxMenu menu;
299
300 KIUI::AddMenuItem( &menu, ID_COPY, _( "Copy color" ), KiBitmap( BITMAPS::copy ) );
301
302 if( !readOnly && m_copied != COLOR4D::UNSPECIFIED )
303 KIUI::AddMenuItem( &menu, ID_PASTE, _( "Paste color" ), KiBitmap( BITMAPS::paste ) );
304
305 if( !readOnly && current != saved )
306 KIUI::AddMenuItem( &menu, ID_REVERT, _( "Revert to saved color" ), KiBitmap( BITMAPS::undo ) );
307
308 menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
309 [&]( wxCommandEvent& aCmd )
310 {
311 switch( aCmd.GetId() )
312 {
313 case ID_COPY:
314 m_copied = current;
315 break;
316
317 case ID_PASTE:
318 updateColor( aLayer, m_copied );
319 break;
320
321 case ID_REVERT:
322 updateColor( aLayer, saved );
323 break;
324
325 default:
326 aCmd.Skip();
327 }
328 } );
329
330 PopupMenu( &menu );
331}
332
333
334void PANEL_COLOR_SETTINGS::OnColorChanged( wxCommandEvent& aEvent )
335{
336 COLOR_SWATCH* swatch = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
337 COLOR4D newColor = swatch->GetSwatchColor();
338 int layer = static_cast<SCH_LAYER_ID>( swatch->GetId() - FIRST_BUTTON_ID );
339
340 updateColor( layer, newColor );
341}
342
343
344void PANEL_COLOR_SETTINGS::updateColor( int aLayer, const KIGFX::COLOR4D& aColor )
345{
347 m_currentSettings->SetColor( aLayer, aColor );
348
349 // Colors must be persisted when edited because multiple PANEL_COLOR_SETTINGS could be
350 // referring to the same theme.
351 saveCurrentTheme( false );
352
353 m_swatches[aLayer]->SetSwatchColor( aColor, false );
354
355 if( m_currentSettings && aLayer == m_backgroundLayer )
356 {
358
359 for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
360 pair.second->SetSwatchBackground( background );
361 }
362
364}
365
366
368{
370 return true;
371
372 if( aValidate && !validateSave() )
373 return false;
374
375 SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
376 COLOR_SETTINGS* selected = settingsMgr.GetColorSettings( m_currentSettings->GetFilename() );
377
378 selected->SetOverrideSchItemColors( m_optOverrideColors->GetValue() );
379
380 for( int layer : m_validLayers )
381 selected->SetColor( layer, m_currentSettings->GetColor( layer ) );
382
383 settingsMgr.SaveColorSettings( selected, m_colorNamespace );
384
385 return true;
386}
387
388
390{
391 wxString name = aSettings->GetName();
392
393 if( aSettings->IsReadOnly() )
394 name += wxS( " " ) + _( "(read-only)" );
395
396 return name;
397}
int color
Definition: DXF_plotter.cpp:58
const char * name
Definition: DXF_plotter.cpp:57
wxBitmap KiBitmap(BITMAPS aBitmap, int aHeightTag)
Construct a wxBitmap from an image identifier Returns the image from the active theme if the image ha...
Definition: bitmap.cpp:104
Color settings are a bit different than most of the settings objects in that there can be more than o...
void SetName(const wxString &aName)
void SetColor(int aLayer, const COLOR4D &aColor)
bool GetOverrideSchItemColors() const
COLOR4D GetColor(int aLayer) const
COLOR4D GetDefaultColor(int aLayer)
void SetOverrideSchItemColors(bool aFlag)
const wxString & GetName() const
A simple color swatch of the kind used to set layer colors.
Definition: color_swatch.h:57
void SetSwatchColor(const KIGFX::COLOR4D &aColor, bool aSendEvent)
Set the current swatch color directly.
KIGFX::COLOR4D GetSwatchColor() const
This class provides a custom wxValidator object for limiting the allowable characters when defining f...
Definition: validators.h:60
bool IsReadOnly() const
Definition: json_settings.h:91
void SetReadOnly(bool aReadOnly)
Definition: json_settings.h:92
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
Calls Store() and then writes the contents of the JSON document to a file.
wxString GetFilename() const
Definition: json_settings.h:80
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:104
Class PANEL_COLOR_SETTINGS_BASE.
void createThemeList(const wxString &aCurrent)
Builds the theme listbox and sets the selection to the current theme.
void createSwatch(int aLayer, const wxString &aName)
std::vector< int > m_validLayers
A list of layer IDs that are valid for the current color settings dialog.
void OnThemeChanged(wxCommandEvent &aEvent) override
virtual bool saveCurrentTheme(bool aValidate)
std::string m_colorNamespace
A namespace that will be passed to SETTINGS_MANAGER::SaveColorSettings.
virtual void ResetPanel() override
Reset the contents of this panel.
void OnBtnOpenThemeFolderClicked(wxCommandEvent &event) override
void OnColorChanged(wxCommandEvent &aEvent)
std::map< int, wxStaticText * > m_labels
virtual void onNewThemeSelected()
Event fired when a new theme is selected that can be overridden in children.
wxString GetSettingsDropdownName(COLOR_SETTINGS *aSettings)
Retrieves the drop down name to be displayed for a color setting.
void updateColor(int aLayer, const KIGFX::COLOR4D &aColor)
COLOR_SETTINGS * m_currentSettings
PANEL_COLOR_SETTINGS(wxWindow *aParent)
void ShowColorContextMenu(wxMouseEvent &aEvent, int aLayer)
void OnLeftDownTheme(wxMouseEvent &event) override
bool Show(bool show) override
virtual void createSwatches()=0
virtual void onColorChanged()
Event fired when the user changes any color.
virtual bool validateSave(bool aQuiet=false)
Performs a pre-save validation of the current color theme.
std::map< int, COLOR_SWATCH * > m_swatches
virtual SETTINGS_MANAGER & GetSettingsManager() const
Definition: pgm_base.h:142
wxString GetPathForSettingsFile(JSON_SETTINGS *aSettings)
Returns the path a given settings file should be loaded from / stored to.
COLOR_SETTINGS * GetColorSettings(const wxString &aName="user")
Retrieves a color settings object that applications can read colors from.
void SaveColorSettings(COLOR_SETTINGS *aSettings, const std::string &aNamespace="")
Safely saves a COLOR_SETTINGS to disk, preserving any changes outside the given namespace.
static wxString GetColorSettingsPath()
Returns the path where color scheme files are stored; creating it if missing (normally .
COLOR_SETTINGS * AddNewColorSettings(const wxString &aFilename)
Registers a new color settings object with the given filename.
void SetBorders(bool aLeft, bool aRight, bool aTop, bool aBottom)
Definition: wx_panel.h:39
@ SWATCH_MEDIUM
Definition: color_swatch.h:41
#define _(s)
bool LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
Definition: launch_ext.cpp:25
@ LAYER_PCB_BACKGROUND
PCB background color.
Definition: layer_ids.h:222
SCH_LAYER_ID
Eeschema drawing layers.
Definition: layer_ids.h:354
KICOMMON_API wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmapBundle &aImage, wxItemKind aType=wxITEM_NORMAL)
Create and insert a menu item with an icon into aMenu.
Definition: ui_common.cpp:372
constexpr int FIRST_BUTTON_ID
SETTINGS_MANAGER * GetSettingsManager()
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1060
see class PGM_BASE
Functions to provide common constants and other functions to assist in making a consistent UI.
Custom text control validator definitions.