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