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) 2021 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 <layer_ids.h>
22 #include <pgm_base.h>
23 #include <settings/color_settings.h>
24 #include <settings/json_settings_internals.h>
25 #include <settings/parameters.h>
26 #include <settings/settings_manager.h>
27 #include <wx/log.h>
28
29 #include "builtin_color_themes.h"
30
31
32 ///! Update the schema version whenever a migration is required
33 const int colorsSchemaVersion = 3;
34
35
COLOR_SETTINGS(const wxString & aFilename,bool aAbsolutePath)36 COLOR_SETTINGS::COLOR_SETTINGS( const wxString& aFilename, bool aAbsolutePath ) :
37 JSON_SETTINGS( std::move( aFilename ), SETTINGS_LOC::COLORS, colorsSchemaVersion ),
38 m_overrideSchItemColors( false ),
39 m_useBoardStackupColors( true )
40 {
41 if( aAbsolutePath )
42 SetLocation( SETTINGS_LOC::NONE );
43
44 m_params.emplace_back( new PARAM<wxString>( "meta.name", &m_displayName, "KiCad Default" ) );
45
46 std::vector<COLOR4D> default_palette = {
47 CSS_COLOR( 200, 52, 52, 1 ),
48 CSS_COLOR( 127, 200, 127, 1 ),
49 CSS_COLOR( 206, 125, 44, 1 ),
50 CSS_COLOR( 79, 203, 203, 1 ),
51 CSS_COLOR( 219, 98, 139, 1 ),
52 CSS_COLOR( 167, 165, 198, 1 ),
53 CSS_COLOR( 40, 204, 217, 1 ),
54 CSS_COLOR( 232, 178, 167, 1 ),
55 CSS_COLOR( 242, 237, 161, 1 ),
56 CSS_COLOR( 141, 203, 129, 1 ),
57 CSS_COLOR( 237, 124, 51, 1 ),
58 CSS_COLOR( 91, 195, 235, 1 ),
59 CSS_COLOR( 247, 111, 142, 1 ),
60 CSS_COLOR( 77, 127, 196, 1 )
61 };
62
63 // TODO(JE) in actual usage, how long does the default palette need to be?
64 m_params.emplace_back( new PARAM_LIST<COLOR4D>( "palette", &m_Palette, default_palette ) );
65
66 m_params.emplace_back( new PARAM<bool>( "schematic.override_item_colors",
67 &m_overrideSchItemColors, false ) );
68
69 m_params.emplace_back( new PARAM<bool>( "3d_viewer.use_board_stackup_colors",
70 &m_useBoardStackupColors, true ) );
71
72 #define CLR( x, y ) \
73 wxASSERT( s_defaultTheme.count( y ) ); \
74 m_params.emplace_back( new COLOR_MAP_PARAM( x, y, s_defaultTheme.at( y ), &m_colors ) );
75
76 CLR( "schematic.anchor", LAYER_SCHEMATIC_ANCHOR );
77 CLR( "schematic.aux_items", LAYER_SCHEMATIC_AUX_ITEMS );
78 CLR( "schematic.background", LAYER_SCHEMATIC_BACKGROUND );
79 CLR( "schematic.brightened", LAYER_BRIGHTENED );
80 CLR( "schematic.bus", LAYER_BUS );
81 CLR( "schematic.bus_junction", LAYER_BUS_JUNCTION );
82 CLR( "schematic.component_body", LAYER_DEVICE_BACKGROUND );
83 CLR( "schematic.component_outline", LAYER_DEVICE );
84 CLR( "schematic.cursor", LAYER_SCHEMATIC_CURSOR );
85 CLR( "schematic.erc_error", LAYER_ERC_ERR );
86 CLR( "schematic.erc_warning", LAYER_ERC_WARN );
87 CLR( "schematic.fields", LAYER_FIELDS );
88 CLR( "schematic.grid", LAYER_SCHEMATIC_GRID );
89 CLR( "schematic.grid_axes", LAYER_SCHEMATIC_GRID_AXES );
90 CLR( "schematic.hidden", LAYER_HIDDEN );
91 CLR( "schematic.junction", LAYER_JUNCTION );
92 CLR( "schematic.label_global", LAYER_GLOBLABEL );
93 CLR( "schematic.label_hier", LAYER_HIERLABEL );
94 CLR( "schematic.label_local", LAYER_LOCLABEL );
95 CLR( "schematic.no_connect", LAYER_NOCONNECT );
96 CLR( "schematic.note", LAYER_NOTES );
97 CLR( "schematic.pin", LAYER_PIN );
98 CLR( "schematic.pin_name", LAYER_PINNAM );
99 CLR( "schematic.pin_number", LAYER_PINNUM );
100 CLR( "schematic.reference", LAYER_REFERENCEPART );
101 // Macs look better with a lighter shadow
102 #ifdef __WXMAC__
103 CLR( "schematic.shadow", LAYER_SELECTION_SHADOWS );
104 #else
105 CLR( "schematic.shadow", LAYER_SELECTION_SHADOWS );
106 #endif
107 CLR( "schematic.sheet", LAYER_SHEET );
108 CLR( "schematic.sheet_background", LAYER_SHEET_BACKGROUND );
109 CLR( "schematic.sheet_filename", LAYER_SHEETFILENAME );
110 CLR( "schematic.sheet_fields", LAYER_SHEETFIELDS );
111 CLR( "schematic.sheet_label", LAYER_SHEETLABEL );
112 CLR( "schematic.sheet_name", LAYER_SHEETNAME );
113 CLR( "schematic.value", LAYER_VALUEPART );
114 CLR( "schematic.wire", LAYER_WIRE );
115 CLR( "schematic.worksheet", LAYER_SCHEMATIC_DRAWINGSHEET );
116
117 CLR( "gerbview.axes", LAYER_GERBVIEW_AXES );
118 CLR( "gerbview.background", LAYER_GERBVIEW_BACKGROUND );
119 CLR( "gerbview.dcodes", LAYER_DCODES );
120 CLR( "gerbview.grid", LAYER_GERBVIEW_GRID );
121 CLR( "gerbview.negative_objects", LAYER_NEGATIVE_OBJECTS );
122 CLR( "gerbview.worksheet", LAYER_GERBVIEW_DRAWINGSHEET );
123
124 for( int i = 0, id = GERBVIEW_LAYER_ID_START;
125 id < GERBER_DRAWLAYERS_COUNT + GERBVIEW_LAYER_ID_START; ++i, ++id )
126 {
127 m_params.emplace_back( new COLOR_MAP_PARAM( "gerbview.layers." + std::to_string( i ), id,
128 default_palette[ i % default_palette.size() ],
129 &m_colors ) );
130 }
131
132 CLR( "board.anchor", LAYER_ANCHOR );
133 CLR( "board.aux_items", LAYER_AUX_ITEMS );
134 CLR( "board.background", LAYER_PCB_BACKGROUND );
135 CLR( "board.cursor", LAYER_CURSOR );
136 CLR( "board.drc_error", LAYER_DRC_ERROR );
137 CLR( "board.drc_warning", LAYER_DRC_WARNING );
138 CLR( "board.drc_exclusion", LAYER_DRC_EXCLUSION );
139 CLR( "board.footprint_text_invisible", LAYER_MOD_TEXT_INVISIBLE );
140 CLR( "board.grid", LAYER_GRID );
141 CLR( "board.grid_axes", LAYER_GRID_AXES );
142 CLR( "board.no_connect", LAYER_NO_CONNECTS );
143 CLR( "board.pad_plated_hole", LAYER_PAD_PLATEDHOLES );
144 CLR( "board.pad_through_hole", LAYER_PADS_TH );
145 CLR( "board.plated_hole", LAYER_NON_PLATEDHOLES );
146 CLR( "board.ratsnest", LAYER_RATSNEST );
147 CLR( "board.via_blind_buried", LAYER_VIA_BBLIND );
148 CLR( "board.via_hole", LAYER_VIA_HOLES );
149 CLR( "board.via_micro", LAYER_VIA_MICROVIA );
150 CLR( "board.via_through", LAYER_VIA_THROUGH );
151 CLR( "board.worksheet", LAYER_DRAWINGSHEET );
152
153 CLR( "board.copper.f", F_Cu );
154 CLR( "board.copper.in1", In1_Cu );
155 CLR( "board.copper.in2", In2_Cu );
156 CLR( "board.copper.in3", In3_Cu );
157 CLR( "board.copper.in4", In4_Cu );
158 CLR( "board.copper.in5", In5_Cu );
159 CLR( "board.copper.in6", In6_Cu );
160 CLR( "board.copper.in7", In7_Cu );
161 CLR( "board.copper.in8", In8_Cu );
162 CLR( "board.copper.in9", In9_Cu );
163 CLR( "board.copper.in10", In10_Cu );
164 CLR( "board.copper.in11", In11_Cu );
165 CLR( "board.copper.in12", In12_Cu );
166 CLR( "board.copper.in13", In13_Cu );
167 CLR( "board.copper.in14", In14_Cu );
168 CLR( "board.copper.in15", In15_Cu );
169 CLR( "board.copper.in16", In16_Cu );
170 CLR( "board.copper.in17", In17_Cu );
171 CLR( "board.copper.in18", In18_Cu );
172 CLR( "board.copper.in19", In19_Cu );
173 CLR( "board.copper.in20", In20_Cu );
174 CLR( "board.copper.in21", In21_Cu );
175 CLR( "board.copper.in22", In22_Cu );
176 CLR( "board.copper.in23", In23_Cu );
177 CLR( "board.copper.in24", In24_Cu );
178 CLR( "board.copper.in25", In25_Cu );
179 CLR( "board.copper.in26", In26_Cu );
180 CLR( "board.copper.in27", In27_Cu );
181 CLR( "board.copper.in28", In28_Cu );
182 CLR( "board.copper.in29", In29_Cu );
183 CLR( "board.copper.in30", In30_Cu );
184 CLR( "board.copper.b", B_Cu );
185
186 CLR( "board.b_adhes", B_Adhes );
187 CLR( "board.f_adhes", F_Adhes );
188 CLR( "board.b_paste", B_Paste );
189 CLR( "board.f_paste", F_Paste );
190 CLR( "board.b_silks", B_SilkS );
191 CLR( "board.f_silks", F_SilkS );
192 CLR( "board.b_mask", B_Mask );
193 CLR( "board.f_mask", F_Mask );
194 CLR( "board.dwgs_user", Dwgs_User );
195 CLR( "board.cmts_user", Cmts_User );
196 CLR( "board.eco1_user", Eco1_User );
197 CLR( "board.eco2_user", Eco2_User );
198 CLR( "board.edge_cuts", Edge_Cuts );
199 CLR( "board.margin", Margin );
200 CLR( "board.b_crtyd", B_CrtYd );
201 CLR( "board.f_crtyd", F_CrtYd );
202 CLR( "board.b_fab", B_Fab );
203 CLR( "board.f_fab", F_Fab );
204 CLR( "board.user_1", User_1 );
205 CLR( "board.user_2", User_2 );
206 CLR( "board.user_3", User_3 );
207 CLR( "board.user_4", User_4 );
208 CLR( "board.user_5", User_5 );
209 CLR( "board.user_6", User_6 );
210 CLR( "board.user_7", User_7 );
211 CLR( "board.user_8", User_8 );
212 CLR( "board.user_9", User_9 );
213
214 // Colors for 3D viewer, which are used as defaults unless overridden by the board
215 CLR( "3d_viewer.background_bottom", LAYER_3D_BACKGROUND_BOTTOM );
216 CLR( "3d_viewer.background_top", LAYER_3D_BACKGROUND_TOP );
217 CLR( "3d_viewer.board", LAYER_3D_BOARD );
218 CLR( "3d_viewer.copper", LAYER_3D_COPPER );
219 CLR( "3d_viewer.silkscreen_bottom", LAYER_3D_SILKSCREEN_BOTTOM );
220 CLR( "3d_viewer.silkscreen_top", LAYER_3D_SILKSCREEN_TOP );
221 CLR( "3d_viewer.soldermask_bottom", LAYER_3D_SOLDERMASK_BOTTOM );
222 CLR( "3d_viewer.soldermask_top", LAYER_3D_SOLDERMASK_TOP );
223 CLR( "3d_viewer.solderpaste", LAYER_3D_SOLDERPASTE );
224
225 registerMigration( 0, 1, std::bind( &COLOR_SETTINGS::migrateSchema0to1, this ) );
226
227 registerMigration( 1, 2,
228 [&]()
229 {
230 // Fix LAYER_VIA_HOLES color - before version 2, this setting had no effect
231 nlohmann::json::json_pointer ptr( "/board/via_hole");
232
233 ( *m_internals )[ptr] = COLOR4D( 0.5, 0.4, 0, 0.8 ).ToWxString( wxC2S_CSS_SYNTAX );
234
235 return true;
236 } );
237
238 registerMigration( 2, 3,
239 [&]()
240 {
241 // We don't support opacity in some 3D colors but some versions of 5.99 let
242 // you set it.
243
244 for( std::string path : { "3d_viewer.background_top",
245 "3d_viewer.background_bottom",
246 "3d_viewer.copper",
247 "3d_viewer.silkscreen_top",
248 "3d_viewer.silkscreen_bottom",
249 "3d_viewer.solderpaste" } )
250 {
251 if( OPT<COLOR4D> optval = Get<COLOR4D>( path ) )
252 Set( path, optval->WithAlpha( 1.0 ) );
253 }
254
255 return true;
256 } );
257 }
258
259
COLOR_SETTINGS(const COLOR_SETTINGS & aOther)260 COLOR_SETTINGS::COLOR_SETTINGS( const COLOR_SETTINGS& aOther ) :
261 JSON_SETTINGS( aOther.m_filename, SETTINGS_LOC::COLORS, colorsSchemaVersion )
262 {
263 initFromOther( aOther );
264 }
265
266
operator =(const COLOR_SETTINGS & aOther)267 COLOR_SETTINGS& COLOR_SETTINGS::operator=( const COLOR_SETTINGS &aOther )
268 {
269 m_filename = aOther.m_filename;
270
271 initFromOther( aOther );
272
273 return *this;
274 }
275
276
initFromOther(const COLOR_SETTINGS & aOther)277 void COLOR_SETTINGS::initFromOther( const COLOR_SETTINGS& aOther )
278 {
279 m_displayName = aOther.m_displayName;
280 m_overrideSchItemColors = aOther.m_overrideSchItemColors;
281 m_useBoardStackupColors = aOther.m_useBoardStackupColors;
282 m_colors = aOther.m_colors;
283 m_defaultColors = aOther.m_defaultColors;
284 m_writeFile = aOther.m_writeFile;
285
286 // Ensure default colors are present
287 for( PARAM_BASE* param : aOther.m_params )
288 {
289 if( COLOR_MAP_PARAM* cmp = dynamic_cast<COLOR_MAP_PARAM*>( param ) )
290 m_defaultColors[cmp->GetKey()] = cmp->GetDefault();
291 }
292 }
293
294
MigrateFromLegacy(wxConfigBase * aCfg)295 bool COLOR_SETTINGS::MigrateFromLegacy( wxConfigBase* aCfg )
296 {
297 return false;
298 }
299
300
migrateSchema0to1()301 bool COLOR_SETTINGS::migrateSchema0to1()
302 {
303 /**
304 * Schema version 0 to 1:
305 *
306 * - Footprint editor settings are split out into a new file called "ThemeName (Footprints)"
307 * - fpedit namespace is removed from the schema
308 */
309
310 if( !m_manager )
311 {
312 wxLogTrace( traceSettings, "Error: COLOR_SETTINGS migration cannot run unmanaged!" );
313 return false;
314 }
315
316 if( !Contains( "fpedit" ) )
317 {
318 wxLogTrace( traceSettings, "migrateSchema0to1: %s doesn't have fpedit settings; skipping.",
319 m_filename );
320 return true;
321 }
322
323 wxString filename = m_filename + wxT( "_footprints" );
324
325 COLOR_SETTINGS* fpsettings = m_manager->AddNewColorSettings( filename );
326
327 // Start out with a clone
328 fpsettings->m_internals->CloneFrom( *m_internals );
329
330 // Footprint editor now just looks at the "board" namespace
331 fpsettings->Set( "board", fpsettings->At( "fpedit" ) );
332
333 fpsettings->Internals()->erase( "fpedit" );
334 fpsettings->Load();
335 fpsettings->SetName( fpsettings->GetName() + wxS( " " ) + _( "(Footprints)" ) );
336 m_manager->Save( fpsettings );
337
338 // Now we can get rid of our own copy
339 m_internals->erase( "fpedit" );
340
341 return true;
342 }
343
344
GetColor(int aLayer) const345 COLOR4D COLOR_SETTINGS::GetColor( int aLayer ) const
346 {
347 if( m_colors.count( aLayer ) )
348 return m_colors.at( aLayer );
349
350 return COLOR4D::UNSPECIFIED;
351 }
352
353
GetDefaultColor(int aLayer)354 COLOR4D COLOR_SETTINGS::GetDefaultColor( int aLayer )
355 {
356 if( !m_defaultColors.count( aLayer ) )
357 {
358 COLOR_MAP_PARAM* p = nullptr;
359
360 for( PARAM_BASE* param : m_params )
361 {
362 COLOR_MAP_PARAM* cmp = dynamic_cast<COLOR_MAP_PARAM*>( param );
363
364 if( cmp && cmp->GetKey() == aLayer )
365 p = cmp;
366 }
367
368 if( p )
369 m_defaultColors[aLayer] = p->GetDefault();
370 else
371 m_defaultColors[aLayer] = COLOR4D::UNSPECIFIED;
372 }
373
374 return m_defaultColors.at( aLayer );
375 }
376
377
SetColor(int aLayer,const COLOR4D & aColor)378 void COLOR_SETTINGS::SetColor( int aLayer, const COLOR4D& aColor )
379 {
380 m_colors[ aLayer ] = aColor;
381 }
382
383
CreateBuiltinColorSettings()384 std::vector<COLOR_SETTINGS*> COLOR_SETTINGS::CreateBuiltinColorSettings()
385 {
386 COLOR_SETTINGS* defaultTheme = new COLOR_SETTINGS( wxT( "_builtin_default" ) );
387 defaultTheme->SetName( _( "KiCad Default" ) );
388 defaultTheme->m_writeFile = false;
389 defaultTheme->Load(); // We can just get the colors out of the param defaults for this one
390
391 COLOR_SETTINGS* classicTheme = new COLOR_SETTINGS( wxT( "_builtin_classic" ) );
392 classicTheme->SetName( _( "KiCad Classic" ) );
393 classicTheme->m_writeFile = false;
394
395 for( PARAM_BASE* param : classicTheme->m_params )
396 delete param;
397
398 classicTheme->m_params.clear(); // Disable load/store
399
400 for( const std::pair<int, COLOR4D> entry : s_classicTheme )
401 classicTheme->m_colors[entry.first] = entry.second;
402
403 std::vector<COLOR_SETTINGS*> ret;
404
405 ret.push_back( defaultTheme );
406 ret.push_back( classicTheme );
407
408 return ret;
409 }
410