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 {
66  if( !m_currentSettings )
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 void PANEL_COLOR_SETTINGS::OnLeftDownTheme( wxMouseEvent& event )
83 {
84  // Lazy rebuild of theme menu to catch any colour theme changes made in other panels
85  wxString sel = m_cbTheme->GetStringSelection();
86  createThemeList( sel );
87 
88  event.Skip();
89 }
90 
91 void PANEL_COLOR_SETTINGS::OnThemeChanged( wxCommandEvent& event )
92 {
93  int idx = m_cbTheme->GetSelection();
94 
95  if( idx == static_cast<int>( m_cbTheme->GetCount() ) - 2 )
96  {
97  // separator; re-select active theme
98  m_cbTheme->SetStringSelection( m_currentSettings->GetName() );
99  return;
100  }
101 
102  if( idx == (int)m_cbTheme->GetCount() - 1 )
103  {
104  // New Theme...
105 
106  if( !saveCurrentTheme( false ) )
107  return;
108 
109  MODULE_NAME_CHAR_VALIDATOR themeNameValidator;
110  wxTextEntryDialog dlg( this, _( "New theme name:" ), _( "Add Color Theme" ) );
111  dlg.SetTextValidator( themeNameValidator );
112 
113  if( dlg.ShowModal() != wxID_OK )
114  return;
115 
116  wxString themeName = dlg.GetValue();
117  wxFileName fn( themeName + wxT( ".json" ) );
119 
120  if( fn.Exists() )
121  {
122  wxMessageBox( _( "Theme already exists!" ) );
123  return;
124  }
125 
126  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
127  COLOR_SETTINGS* newSettings = settingsMgr.AddNewColorSettings( themeName );
128  newSettings->SetName( themeName );
129 
130  for( auto layer : m_validLayers )
131  newSettings->SetColor( layer, m_currentSettings->GetColor( layer ) );
132 
133  newSettings->SaveToFile( settingsMgr.GetPathForSettingsFile( newSettings ) );
134 
135  idx = m_cbTheme->Insert( themeName, idx - 1, static_cast<void*>( newSettings ) );
136  m_cbTheme->SetSelection( idx );
137 
138  m_optOverrideColors->SetValue( newSettings->GetOverrideSchItemColors() );
139 
140  *m_currentSettings = *newSettings;
142  }
143  else
144  {
145  COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( idx ) );
146 
147  if( selected->GetFilename() != m_currentSettings->GetFilename() )
148  {
149  if( !saveCurrentTheme( false ) )
150  return;
151 
152  m_optOverrideColors->SetValue( selected->GetOverrideSchItemColors() );
153 
154  *m_currentSettings = *selected;
156 
158 
159  for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
160  {
161  pair.second->SetSwatchBackground( background );
162  pair.second->SetSwatchColor( m_currentSettings->GetColor( pair.first ), false );
163  }
164  }
165  }
166 }
167 
168 
169 void PANEL_COLOR_SETTINGS::createThemeList( const wxString& aCurrent )
170 {
171  int width = 0;
172  int height = 0;
173 
174  m_cbTheme->GetTextExtent( _( "New Theme..." ), &width, &height );
175  int minwidth = width;
176 
177  m_cbTheme->Clear();
178 
179  for( COLOR_SETTINGS* settings : Pgm().GetSettingsManager().GetColorSettingsList() )
180  {
181  int pos = m_cbTheme->Append( settings->GetName(), static_cast<void*>( settings ) );
182 
183  if( settings->GetName() == aCurrent )
184  m_cbTheme->SetSelection( pos );
185 
186  m_cbTheme->GetTextExtent( settings->GetName(), &width, &height );
187  minwidth = std::max( minwidth, width );
188  }
189 
190  m_cbTheme->Append( wxT( "---" ) );
191  m_cbTheme->Append( _( "New Theme..." ) );
192 
193  m_cbTheme->SetMinSize( wxSize( minwidth + 50, -1 ) );
194 }
195 
196 
197 void PANEL_COLOR_SETTINGS::createSwatch( int aLayer, const wxString& aName )
198 {
199  wxStaticText* label = new wxStaticText( m_colorsListWindow, wxID_ANY, aName );
200 
201  // The previously selected theme can be deleted and cannot be selected.
202  // so select the default theme (first theme of the list)
203  if( m_cbTheme->GetSelection() < 0 )
204  {
205  m_cbTheme->SetSelection( 0 );
207  }
208 
209  void* clientData = m_cbTheme->GetClientData( m_cbTheme->GetSelection() );
210  COLOR_SETTINGS* selected = static_cast<COLOR_SETTINGS*>( clientData );
211 
212  int id = FIRST_BUTTON_ID + aLayer;
213  COLOR4D defaultColor = selected->GetDefaultColor( aLayer );
215  COLOR4D backgroundColor = m_currentSettings->GetColor( m_backgroundLayer );
216 
217  COLOR_SWATCH* swatch = new COLOR_SWATCH( m_colorsListWindow, color, id, backgroundColor,
218  defaultColor, true );
219  swatch->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOW ) );
220 
221  m_colorsGridSizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxLEFT, 5 );
222  m_colorsGridSizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL | wxALL, 3 );
223 
224  m_labels[aLayer] = label;
225  m_swatches[aLayer] = swatch;
226 
227  swatch->Bind( wxEVT_RIGHT_DOWN,
228  [&, aLayer]( wxMouseEvent& aEvent )
229  {
230  ShowColorContextMenu( aEvent, aLayer );
231  } );
232  swatch->Bind( COLOR_SWATCH_CHANGED, &PANEL_COLOR_SETTINGS::OnColorChanged, this );
233 }
234 
235 
236 void PANEL_COLOR_SETTINGS::ShowColorContextMenu( wxMouseEvent& aEvent, int aLayer )
237 {
238  auto selected =
239  static_cast<COLOR_SETTINGS*>( m_cbTheme->GetClientData( m_cbTheme->GetSelection() ) );
240 
241  COLOR4D current = m_currentSettings->GetColor( aLayer );
242  COLOR4D saved = selected->GetColor( aLayer );
243 
244  wxMenu menu;
245 
246  AddMenuItem( &menu, ID_COPY, _( "Copy color" ), KiBitmap( copy_xpm ) );
247 
248  if( m_copied != COLOR4D::UNSPECIFIED )
249  AddMenuItem( &menu, ID_PASTE, _( "Paste color" ), KiBitmap( paste_xpm ) );
250 
251  if( current != saved )
252  AddMenuItem( &menu, ID_REVERT, _( "Revert to saved color" ), KiBitmap( undo_xpm ) );
253 
254  menu.Bind( wxEVT_COMMAND_MENU_SELECTED,
255  [&]( wxCommandEvent& aCmd )
256  {
257  switch( aCmd.GetId() )
258  {
259  case ID_COPY:
260  m_copied = current;
261  break;
262 
263  case ID_PASTE:
264  updateColor( aLayer, m_copied );
265  break;
266 
267  case ID_REVERT:
268  updateColor( aLayer, saved );
269  break;
270 
271  default:
272  aCmd.Skip();
273  }
274  } );
275 
276  PopupMenu( &menu );
277 }
278 
279 
280 void PANEL_COLOR_SETTINGS::OnColorChanged( wxCommandEvent& aEvent )
281 {
282  COLOR_SWATCH* swatch = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
283  COLOR4D newColor = swatch->GetSwatchColor();
284  LAYER_NUM layer = static_cast<SCH_LAYER_ID>( swatch->GetId() - FIRST_BUTTON_ID );
285 
286  updateColor( layer, newColor );
287 }
288 
289 
290 void PANEL_COLOR_SETTINGS::updateColor( int aLayer, const KIGFX::COLOR4D& aColor )
291 {
292  if( m_currentSettings )
293  m_currentSettings->SetColor( aLayer, aColor );
294 
295  m_swatches[aLayer]->SetSwatchColor( aColor, false );
296 
297  if( m_currentSettings && aLayer == m_backgroundLayer )
298  {
300 
301  for( std::pair<int, COLOR_SWATCH*> pair : m_swatches )
302  pair.second->SetSwatchBackground( background );
303  }
304 
305  onColorChanged();
306 }
307 
308 
310 {
311  if( aValidate && !validateSave() )
312  return false;
313 
314  SETTINGS_MANAGER& settingsMgr = Pgm().GetSettingsManager();
315  COLOR_SETTINGS* selected = settingsMgr.GetColorSettings( m_currentSettings->GetFilename() );
316 
317  selected->SetOverrideSchItemColors( m_optOverrideColors->GetValue() );
318 
319  for( auto layer : m_validLayers )
320  selected->SetColor( layer, m_currentSettings->GetColor( layer ) );
321 
322  settingsMgr.SaveColorSettings( selected, m_colorNamespace );
323 
324  return true;
325 }
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104
KIGFX::COLOR4D GetSwatchColor() const
virtual bool saveCurrentTheme(bool aValidate)
void SetSwatchColor(KIGFX::COLOR4D aColor, bool sendEvent)
Set the current swatch color directly.
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:232
wxString GetFilename() const
Definition: json_settings.h:56
int color
Definition: DXF_plotter.cpp:61
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.
void ShowColorContextMenu(wxMouseEvent &aEvent, int aLayer)
std::string m_colorNamespace
A namespace that will be passed to SETTINGS_MANAGER::SaveColorSettings.
wxBitmap KiBitmap(BITMAP_DEF aBitmap)
Construct a wxBitmap from a memory record, held in a BITMAP_DEF.
Definition: bitmap.cpp:80
Class PANEL_COLOR_SETTINGS_BASE.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
void createThemeList(const wxString &aCurrent)
COLOR_SETTINGS * m_currentSettings
void SetOverrideSchItemColors(bool aFlag)
COLOR4D GetDefaultColor(int aLayer)
This class provides a custom wxValidator object for limiting the allowable characters when defining f...
Definition: validators.h:63
const BITMAP_OPAQUE copy_xpm[1]
Definition: copy.cpp:67
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 (normally .
int LAYER_NUM
Type LAYER_NUM can be replaced with int and removed.
void OnBtnOpenThemeFolderClicked(wxCommandEvent &event) override
const BITMAP_OPAQUE paste_xpm[1]
Definition: paste.cpp:69
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.
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:38
void SaveColorSettings(COLOR_SETTINGS *aSettings, const std::string &aNamespace="")
Safely saves a COLOR_SETTINGS to disk, preserving any changes outside the given namespace.
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:99