KiCad PCB EDA Suite
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 <jon@craftyjon.com>
5  * Copyright (C) 2020 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>
23 #include <launch_ext.h>
25 #include <menus_helpers.h>
26 #include <panel_color_settings.h>
27 #include <pgm_base.h>
31 #include <validators.h>
32 #include <widgets/color_swatch.h>
33 
34 
35 // Button ID starting point
36 constexpr int FIRST_BUTTON_ID = 1800;
37 
38 
40  PANEL_COLOR_SETTINGS_BASE( aParent ),
41  m_currentSettings( nullptr ),
42  m_swatches(),
43  m_copied( COLOR4D::UNSPECIFIED ),
44  m_validLayers(),
45  m_backgroundLayer( LAYER_PCB_BACKGROUND ),
46  m_colorNamespace()
47 {
48 #ifdef __APPLE__
49  m_btnOpenFolder->SetLabel( _( "Reveal Themes in Finder" ) );
50 
51  // Simple border is too dark on OSX
52  m_colorsListWindow->SetWindowStyle( wxBORDER_SUNKEN|wxVSCROLL );
53 #endif
54 }
55 
56 
58 {
60  LaunchExternal( dir );
61 }
62 
63 
65 {
67  return;
68 
69  for( const std::pair<const int, COLOR_SWATCH*>& pair : m_swatches )
70  {
71  int layer = pair.first;
72  COLOR_SWATCH* button = pair.second;
73 
74  COLOR4D defaultColor = m_currentSettings->GetDefaultColor( layer );
75 
76  m_currentSettings->SetColor( layer, defaultColor );
77  button->SetSwatchColor( defaultColor, false );
78  }
79 }
80 
81 
82 bool PANEL_COLOR_SETTINGS::Show( bool show )
83 {
84  if( show )
85  {
86  // In case changes have been made to the current theme in another panel:
87  int idx = m_cbTheme->GetSelection();
88  COLOR_SETTINGS* settings = static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( idx ) );
89 
90  if( settings )
91  *m_currentSettings = *settings;
92 
95  }
96 
97  return PANEL_COLOR_SETTINGS_BASE::Show( show );
98 }
99 
100 
101 void PANEL_COLOR_SETTINGS::OnLeftDownTheme( wxMouseEvent& event )
102 {
103  // Lazy rebuild of theme menu to catch any colour theme changes made in other panels
104  wxString sel = m_cbTheme->GetStringSelection();
105  createThemeList( sel );
106 
107  event.Skip();
108 }
109 
110 
111 void PANEL_COLOR_SETTINGS::OnThemeChanged( wxCommandEvent& event )
112 {
113  int idx = m_cbTheme->GetSelection();
114 
115  if( idx == static_cast<int>( m_cbTheme->GetCount() ) - 2 )
116  {
117  // separator; re-select active theme
118  m_cbTheme->SetStringSelection( m_currentSettings->GetName() );
119  return;
120  }
121 
122  if( idx == (int)m_cbTheme->GetCount() - 1 )
123  {
124  // New Theme...
125 
126  if( !saveCurrentTheme( false ) )
127  return;
128 
129  FOOTPRINT_NAME_VALIDATOR themeNameValidator;
130  wxTextEntryDialog dlg( this, _( "New theme name:" ), _( "Add Color Theme" ) );
131  dlg.SetTextValidator( themeNameValidator );
132 
133  if( dlg.ShowModal() != wxID_OK )
134  return;
135 
136  wxString themeName = dlg.GetValue();
137  wxFileName fn( themeName + wxT( ".json" ) );
139 
140  if( fn.Exists() )
141  {
142  wxMessageBox( _( "Theme already exists!" ) );
143  return;
144  }
145 
146  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
147  COLOR_SETTINGS* newSettings = settingsMgr.AddNewColorSettings( themeName );
148  newSettings->SetName( themeName );
149  newSettings->SetReadOnly( false );
150 
151  for( auto layer : m_validLayers )
152  newSettings->SetColor( layer, m_currentSettings->GetColor( layer ) );
153 
154  newSettings->SaveToFile( settingsMgr.GetPathForSettingsFile( newSettings ) );
155 
156  idx = m_cbTheme->Insert( themeName, idx - 1, static_cast<void*>( newSettings ) );
157  m_cbTheme->SetSelection( idx );
158 
159  m_optOverrideColors->SetValue( newSettings->GetOverrideSchItemColors() );
160 
161  *m_currentSettings = *newSettings;
162  updateSwatches();
164  }
165  else
166  {
167  COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( idx ) );
168 
169  if( selected->GetFilename() != m_currentSettings->GetFilename() )
170  {
171  if( !saveCurrentTheme( false ) )
172  return;
173 
174  m_optOverrideColors->SetValue( selected->GetOverrideSchItemColors() );
175 
176  *m_currentSettings = *selected;
178  updateSwatches();
179  }
180  }
181 }
182 
183 
185 {
186  bool isReadOnly = m_currentSettings->IsReadOnly();
188 
189  for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
190  {
191  pair.second->SetSwatchBackground( background );
192  pair.second->SetSwatchColor( m_currentSettings->GetColor( pair.first ), false );
193  pair.second->SetReadOnly( isReadOnly );
194  }
195 }
196 
197 
198 void PANEL_COLOR_SETTINGS::createThemeList( const wxString& aCurrent )
199 {
200  int width = 0;
201  int height = 0;
202 
203  m_cbTheme->GetTextExtent( _( "New Theme..." ), &width, &height );
204  int minwidth = width;
205 
206  m_cbTheme->Clear();
207 
208  for( COLOR_SETTINGS* settings : Pgm().GetSettingsManager().GetColorSettingsList() )
209  {
210  wxString name = settings->GetName();
211 
212  if( settings->IsReadOnly() )
213  name += wxS( " " ) + _( "(read-only)" );
214 
215  int pos = m_cbTheme->Append( name, static_cast<void*>( settings ) );
216 
217  if( settings->GetFilename() == aCurrent )
218  m_cbTheme->SetSelection( pos );
219 
220  m_cbTheme->GetTextExtent( name, &width, &height );
221  minwidth = std::max( minwidth, width );
222  }
223 
224  m_cbTheme->Append( wxT( "---" ) );
225  m_cbTheme->Append( _( "New Theme..." ) );
226 
227  m_cbTheme->SetMinSize( wxSize( minwidth + 50, -1 ) );
228 }
229 
230 
231 void PANEL_COLOR_SETTINGS::createSwatch( int aLayer, const wxString& aName )
232 {
233  wxStaticText* label = new wxStaticText( m_colorsListWindow, wxID_ANY, aName );
234 
235  // The previously selected theme can be deleted and cannot be selected.
236  // so select the default theme (first theme of the list)
237  if( m_cbTheme->GetSelection() < 0 )
238  {
239  m_cbTheme->SetSelection( 0 );
241  }
242 
243  void* clientData = m_cbTheme->GetClientData( m_cbTheme->GetSelection() );
244  COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( clientData );
245 
246  int id = FIRST_BUTTON_ID + aLayer;
247  COLOR4D defaultColor = selected->GetDefaultColor( aLayer );
249  COLOR4D backgroundColor = m_currentSettings->GetColor( m_backgroundLayer );
250 
251  COLOR_SWATCH* swatch = new COLOR_SWATCH( m_colorsListWindow, color, id, backgroundColor,
252  defaultColor, SWATCH_MEDIUM );
253  swatch->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
254 
255  m_colorsGridSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxLEFT, 5 );
256  m_colorsGridSizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3 );
257 
258  m_labels[aLayer] = label;
259  m_swatches[aLayer] = swatch;
260 
261  swatch->Bind( wxEVT_RIGHT_DOWN,
262  [&, aLayer]( wxMouseEvent& aEvent )
263  {
264  ShowColorContextMenu( aEvent, aLayer );
265  } );
266 
267  swatch->Bind( COLOR_SWATCH_CHANGED, &PANEL_COLOR_SETTINGS::OnColorChanged, this );
268 }
269 
270 
271 void PANEL_COLOR_SETTINGS::ShowColorContextMenu( wxMouseEvent& aEvent, int aLayer )
272 {
273  auto selected =
274  static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( m_cbTheme->GetSelection() ) );
275 
276  COLOR4D current = m_currentSettings->GetColor( aLayer );
277  COLOR4D saved = selected->GetColor( aLayer );
278  bool readOnly = m_currentSettings->IsReadOnly();
279 
280  wxMenu menu;
281 
282  AddMenuItem( &menu, ID_COPY, _( "Copy color" ), KiBitmap( copy_xpm ) );
283 
284  if( !readOnly && m_copied != COLOR4D::UNSPECIFIED )
285  AddMenuItem( &menu, ID_PASTE, _( "Paste color" ), KiBitmap( paste_xpm ) );
286 
287  if( !readOnly && current != saved )
288  AddMenuItem( &menu, ID_REVERT, _( "Revert to saved color" ), KiBitmap( undo_xpm ) );
289 
290  menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
291  [&]( wxCommandEvent& aCmd )
292  {
293  switch( aCmd.GetId() )
294  {
295  case ID_COPY:
296  m_copied = current;
297  break;
298 
299  case ID_PASTE:
300  updateColor( aLayer, m_copied );
301  break;
302 
303  case ID_REVERT:
304  updateColor( aLayer, saved );
305  break;
306 
307  default:
308  aCmd.Skip();
309  }
310  } );
311 
312  PopupMenu( &menu );
313 }
314 
315 
316 void PANEL_COLOR_SETTINGS::OnColorChanged( wxCommandEvent& aEvent )
317 {
318  COLOR_SWATCH* swatch = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
319  COLOR4D newColor = swatch->GetSwatchColor();
320  LAYER_NUM layer = static_cast<SCH_LAYER_ID>( swatch->GetId() - FIRST_BUTTON_ID );
321 
322  updateColor( layer, newColor );
323 }
324 
325 
326 void PANEL_COLOR_SETTINGS::updateColor( int aLayer, const KIGFX::COLOR4D& aColor )
327 {
328  if( m_currentSettings )
329  m_currentSettings->SetColor( aLayer, aColor );
330 
331  // Colors must be persisted when edited because multiple PANEL_COLOR_SETTINGS could be
332  // referring to the same theme.
333  saveCurrentTheme( false );
334 
335  m_swatches[aLayer]->SetSwatchColor( aColor, false );
336 
337  if( m_currentSettings && aLayer == m_backgroundLayer )
338  {
340 
341  for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
342  pair.second->SetSwatchBackground( background );
343  }
344 
345  onColorChanged();
346 }
347 
348 
350 {
352  return true;
353 
354  if( aValidate && !validateSave() )
355  return false;
356 
357  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
358  COLOR_SETTINGS* selected = settingsMgr.GetColorSettings( m_currentSettings->GetFilename() );
359 
360  selected->SetOverrideSchItemColors( m_optOverrideColors->GetValue() );
361 
362  for( auto layer : m_validLayers )
363  selected->SetColor( layer, m_currentSettings->GetColor( layer ) );
364 
365  settingsMgr.SaveColorSettings( selected, m_colorNamespace );
366 
367  return true;
368 }
void SetSwatchColor(KIGFX::COLOR4D aColor, bool aSendEvent)
Set the current swatch color directly.
KIGFX::COLOR4D GetSwatchColor() const
virtual bool saveCurrentTheme(bool aValidate)
virtual void ResetPanel() override
Reset the contents of this panel.
wxMenuItem * AddMenuItem(wxMenu *aMenu, int aId, const wxString &aText, const wxBitmap &aImage, wxItemKind aType=wxITEM_NORMAL)
Function AddMenuItem is an inline helper function to create and insert a menu item with an icon into ...
Definition: bitmap.cpp:234
void SetReadOnly(bool aReadOnly)
Definition: json_settings.h:75
wxString GetFilename() const
Definition: json_settings.h:64
int color
Definition: DXF_plotter.cpp:60
std::map< int, wxStaticText * > m_labels
virtual void onNewThemeSelected()
Event fired when a new theme is selected that can be overridden in children.
std::map< int, COLOR_SWATCH * > m_swatches
void updateColor(int aLayer, const KIGFX::COLOR4D &aColor)
const wxString & GetName() const
COLOR_SETTINGS * AddNewColorSettings(const wxString &aFilename)
Registers a new color settings object with the given filename.
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:102
void ShowColorContextMenu(wxMouseEvent &aEvent, int aLayer)
std::string m_colorNamespace
A namespace that will be passed to SETTINGS_MANAGER::SaveColorSettings.
This class provides a custom wxValidator object for limiting the allowable characters when defining f...
Definition: validators.h:63
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:82
Class PANEL_COLOR_SETTINGS_BASE.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
bool Show(bool show) override
void createThemeList(const wxString &aCurrent)
COLOR_SETTINGS * m_currentSettings
void SetOverrideSchItemColors(bool aFlag)
COLOR4D GetDefaultColor(int aLayer)
const BITMAP_OPAQUE copy_xpm[1]
Definition: copy.cpp:62
void SetName(const wxString &aName)
constexpr int FIRST_BUTTON_ID
virtual void onColorChanged()
Event fired when the user changes any color.
PANEL_COLOR_SETTINGS(wxWindow *aParent)
SETTINGS_MANAGER * GetSettingsManager()
std::vector< int > m_validLayers
A list of layer IDs that are valid for the current color settings dialog.
static wxString GetColorSettingsPath()
Returns the path where color scheme files are stored; creating it if missing (normally .
int LAYER_NUM
This can be replaced with int and removed.
void OnBtnOpenThemeFolderClicked(wxCommandEvent &event) override
const BITMAP_OPAQUE paste_xpm[1]
Definition: paste.cpp:77
COLOR_SETTINGS * GetColorSettings(const wxString &aName="user")
Retrieves a color settings object that applications can read colors from.
COLOR4D GetColor(int aLayer) const
see class PGM_BASE
Board layer functions and definitions.
const char * name
Definition: DXF_plotter.cpp:59
void OnLeftDownTheme(wxMouseEvent &event) override
#define _(s)
Definition: 3d_actions.cpp:33
Class representing a simple color swatch, of the kind used to set layer colors.
Definition: color_swatch.h:57
void SaveColorSettings(COLOR_SETTINGS *aSettings, const std::string &aNamespace="")
Safely saves a COLOR_SETTINGS to disk, preserving any changes outside the given namespace.
bool IsReadOnly() const
Definition: json_settings.h:74
const BITMAP_OPAQUE undo_xpm[1]
Definition: undo.cpp:74
Color settings are a bit different than most of the settings objects in that there can be more than o...
bool GetOverrideSchItemColors() const
void LaunchExternal(const wxString &aPath)
Launches the given file or folder in the host OS.
Definition: launch_ext.cpp:26
void createSwatch(int aLayer, const wxString &aName)
void OnThemeChanged(wxCommandEvent &aEvent) override
Custom text control validator definitions.
virtual bool validateSave(bool aQuiet=false)
Performs a pre-save validation of the current color theme.
wxString GetPathForSettingsFile(JSON_SETTINGS *aSettings)
Returns the path a given settings file should be loaded from / stored to.
void SetColor(int aLayer, COLOR4D aColor)
void OnColorChanged(wxCommandEvent &aEvent)
COLOR4D is the color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:100