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 <class_draw_panel_gal.h>
22 #include <common.h>
23 #include <layer_ids.h>
24 #include <pgm_base.h>
25 #include <settings/app_settings.h>
26 #include <settings/json_settings_internals.h>
27 #include <settings/common_settings.h>
28 #include <settings/parameters.h>
29 #include <base_units.h>
30 
31 ///! Update the schema version whenever a migration is required
32 const int appSettingsSchemaVersion = 0;
33 
34 
APP_SETTINGS_BASE(const std::string & aFilename,int aSchemaVersion)35 APP_SETTINGS_BASE::APP_SETTINGS_BASE( const std::string& aFilename, int aSchemaVersion ) :
36         JSON_SETTINGS( aFilename, SETTINGS_LOC::USER, aSchemaVersion ),
37         m_CrossProbing(),
38         m_FindReplace(),
39         m_Graphics(),
40         m_ColorPicker(),
41         m_LibTree(),
42         m_Printing(),
43         m_System(),
44         m_Window(),
45         m_appSettingsSchemaVersion( aSchemaVersion )
46 {
47     // Make Coverity happy:
48     m_LibTree.column_width = 360;
49     m_Graphics.canvas_type = EDA_DRAW_PANEL_GAL::GAL_FALLBACK;
50 
51     // Build parameters list:
52     m_params.emplace_back( new PARAM<int>( "find_replace.flags", &m_FindReplace.flags, 1 ) );
53 
54     m_params.emplace_back( new PARAM<wxString>( "find_replace.find_string",
55             &m_FindReplace.find_string, "" ) );
56 
57     m_params.emplace_back( new PARAM_LIST<wxString>( "find_replace.find_history",
58             &m_FindReplace.find_history, {} ) );
59 
60     m_params.emplace_back( new PARAM<wxString>( "find_replace.replace_string",
61             &m_FindReplace.replace_string, "" ) );
62 
63     m_params.emplace_back( new PARAM_LIST<wxString>( "find_replace.replace_history",
64             &m_FindReplace.replace_history, {} ) );
65 
66     m_params.emplace_back( new PARAM<int>( "graphics.canvas_type",
67             &m_Graphics.canvas_type, EDA_DRAW_PANEL_GAL::GAL_FALLBACK ) );
68 
69     m_params.emplace_back( new PARAM<float>(
70             "graphics.highlight_factor", &m_Graphics.highlight_factor, 0.5f, 0.0, 1.0f ) );
71 
72     m_params.emplace_back( new PARAM<float>(
73             "graphics.select_factor", &m_Graphics.select_factor, 0.75f, 0.0, 1.0f ) );
74 
75     m_params.emplace_back( new PARAM<float>(
76             "graphics.high_contrast_factor", &m_Graphics.high_contrast_factor, 0.35f, 0.0, 1.0f ) );
77 
78     m_params.emplace_back( new PARAM<int>( "color_picker.default_tab",
79             &m_ColorPicker.default_tab, 0 ) );
80 
81     m_params.emplace_back( new PARAM<int>( "lib_tree.column_width",
82             &m_LibTree.column_width, 360 ) );
83 
84     // Now that we allow hiding/showing of the tree control, it's never terribly useful to
85     // decrease the width to nothing, and wxWidgets appears to have some bugs where it sets it
86     // way too narrow.
87     if( m_LibTree.column_width < 360 )
88         m_LibTree.column_width = 360;
89 
90     m_params.emplace_back( new PARAM<bool>( "printing.background",
91             &m_Printing.background, false ) );
92 
93     m_params.emplace_back( new PARAM<bool>( "printing.monochrome",
94             &m_Printing.monochrome, true ) );
95 
96     m_params.emplace_back( new PARAM<double>( "printing.scale",
97             &m_Printing.scale, 1.0 ) );
98 
99     m_params.emplace_back( new PARAM<bool>( "printing.use_theme",
100             &m_Printing.use_theme, false ) );
101 
102     m_params.emplace_back( new PARAM<wxString>( "printing.color_theme",
103             &m_Printing.color_theme, "" ) );
104 
105     m_params.emplace_back( new PARAM<bool>( "printing.title_block",
106             &m_Printing.title_block, false ) );
107 
108     m_params.emplace_back( new PARAM_LIST<int>( "printing.layers",
109             &m_Printing.layers, {} ) );
110 
111     m_params.emplace_back( new PARAM<bool>( "system.first_run_shown",
112             &m_System.first_run_shown, false ) );
113 
114     m_params.emplace_back( new PARAM<int>( "system.max_undo_items",
115             &m_System.max_undo_items, 0 ) );
116 
117 
118     m_params.emplace_back( new PARAM_LIST<wxString>( "system.file_history",
119             &m_System.file_history, {} ) );
120 
121     m_params.emplace_back( new PARAM<int>( "system.units",
122             &m_System.units, static_cast<int>( EDA_UNITS::MILLIMETRES ) ) );
123 
124     m_params.emplace_back( new PARAM<int>( "system.last_metric_units",
125             &m_System.last_metric_units, static_cast<int>( EDA_UNITS::MILLIMETRES ) ) );
126 
127     m_params.emplace_back( new PARAM<int>( "system.last_imperial_units",
128             &m_System.last_imperial_units, static_cast<int>( EDA_UNITS::INCHES ) ) );
129 
130     m_params.emplace_back( new PARAM<wxString>( "appearance.color_theme",
131             &m_ColorTheme, "_builtin_default" ) );
132 
133     addParamsForWindow( &m_Window, "window" );
134 
135     m_params.emplace_back( new PARAM<bool>( "cross_probing.center_on_items",
136             &m_CrossProbing.center_on_items, true ) );
137 
138     m_params.emplace_back( new PARAM<bool>( "cross_probing.zoom_to_fit",
139             &m_CrossProbing.zoom_to_fit, true ) );
140 
141     m_params.emplace_back( new PARAM<bool>( "cross_probing.auto_highlight",
142             &m_CrossProbing.auto_highlight, true ) );
143 }
144 
145 
MigrateFromLegacy(wxConfigBase * aCfg)146 bool APP_SETTINGS_BASE::MigrateFromLegacy( wxConfigBase* aCfg )
147 {
148     bool ret = true;
149 
150     const std::string f = getLegacyFrameName();
151 
152     ret &= fromLegacyString(   aCfg, "LastFindString",      "find_replace.find_string" );
153     ret &= fromLegacyString(   aCfg, "LastReplaceString",   "find_replace.replace_string" );
154 
155     migrateFindReplace( aCfg );
156 
157     ret &= fromLegacy<int>(    aCfg, "canvas_type",         "graphics.canvas_type" );
158 
159     ret &= fromLegacy<int>(    aCfg, "P22LIB_TREE_MODEL_ADAPTERSelectorColumnWidth",
160                                                             "lib_tree.column_width" );
161 
162     ret &= fromLegacy<bool>(   aCfg, "PrintMonochrome",     "printing.monochrome" );
163     ret &= fromLegacy<double>( aCfg, "PrintScale",          "printing.scale" );
164     ret &= fromLegacy<bool>(   aCfg, "PrintPageFrame",      "printing.title_block" );
165 
166     {
167         nlohmann::json js = nlohmann::json::array();
168         wxString       key;
169         bool           val = false;
170 
171         for( unsigned i = 0; i < PCB_LAYER_ID_COUNT; ++i )
172         {
173             key.Printf( wxT( "PlotLayer_%d" ), i );
174 
175             if( aCfg->Read( key, &val ) && val )
176                 js.push_back( i );
177         }
178 
179         Set( "printing.layers", js );
180     }
181 
182     ret &= fromLegacy<bool>(   aCfg, f + "FirstRunShown",       "system.first_run_shown" );
183     ret &= fromLegacy<int>(    aCfg, f + "DevelMaxUndoItems",   "system.max_undo_items" );
184     ret &= fromLegacy<int>(    aCfg, f + "Units",               "system.units" );
185 
186     {
187         int            max_history_size = Pgm().GetCommonSettings()->m_System.file_history_size;
188         wxString       file, key;
189         nlohmann::json js = nlohmann::json::array();
190 
191         for( int i = 1; i <= max_history_size; i++ )
192         {
193             key.Printf( "file%d", i );
194             file = aCfg->Read( key, wxEmptyString );
195 
196             if( !file.IsEmpty() )
197                 js.push_back( file.ToStdString() );
198         }
199 
200         Set( "system.file_history", js );
201     }
202 
203     ret &= migrateWindowConfig( aCfg, f, "window" );
204 
205     return ret;
206 }
207 
208 
migrateFindReplace(wxConfigBase * aCfg)209 void APP_SETTINGS_BASE::migrateFindReplace( wxConfigBase* aCfg )
210 {
211     const int find_replace_history_size = 10;
212     nlohmann::json find_history         = nlohmann::json::array();
213     nlohmann::json replace_history      = nlohmann::json::array();
214     wxString tmp, find_key, replace_key;
215 
216     for( int i = 0; i < find_replace_history_size; ++i )
217     {
218         find_key.Printf( "FindStringHistoryList%d", i );
219         replace_key.Printf( "ReplaceStringHistoryList%d", i );
220 
221         if( aCfg->Read( find_key, &tmp ) )
222             find_history.push_back( tmp.ToStdString() );
223 
224         if( aCfg->Read( replace_key, &tmp ) )
225             replace_history.push_back( tmp.ToStdString() );
226     }
227 
228     Set( "find_replace.find_history", find_history );
229     Set( "find_replace.replace_history", replace_history );
230 }
231 
232 
migrateWindowConfig(wxConfigBase * aCfg,const std::string & aFrame,const std::string & aJsonPath)233 bool APP_SETTINGS_BASE::migrateWindowConfig( wxConfigBase* aCfg, const std::string& aFrame,
234                                              const std::string& aJsonPath )
235 {
236     bool ret = true;
237 
238     const std::string frameGDO = aFrame + "GalDisplayOptions";
239     const std::string cursorPath = aJsonPath + ".cursor";
240     const std::string gridPath = aJsonPath + ".grid";
241 
242     ret &= fromLegacy<bool>( aCfg, aFrame + "Maximized",            aJsonPath + ".maximized" );
243     ret &= fromLegacyString( aCfg, aFrame + "MostRecentlyUsedPath", aJsonPath + ".mru_path" );
244     ret &= fromLegacy<int>(  aCfg, aFrame + "Size_x",               aJsonPath + ".size_x" );
245     ret &= fromLegacy<int>(  aCfg, aFrame + "Size_y",               aJsonPath + ".size_y" );
246     ret &= fromLegacyString( aCfg, aFrame + "Perspective",          aJsonPath + ".perspective" );
247     ret &= fromLegacy<int>(  aCfg, aFrame + "Pos_x",                aJsonPath + ".pos_x" );
248     ret &= fromLegacy<int>(  aCfg, aFrame + "Pos_y",                aJsonPath + ".pos_y" );
249 
250     ret &= fromLegacy<bool>( aCfg, frameGDO + "ForceDisplayCursor", cursorPath + ".always_show_cursor" );
251     ret &= fromLegacy<bool>( aCfg, frameGDO + "CursorFullscreen",   cursorPath + ".fullscreen_cursor" );
252 
253     ret &= fromLegacy<int>(  aCfg, aFrame + "_LastGridSize",        gridPath + ".last_size" );
254 
255     ret &= fromLegacy<int>(  aCfg, aFrame + "FastGrid1",            gridPath + ".fast_grid_1" );
256     ret &= fromLegacy<int>(  aCfg, aFrame + "FastGrid2",            gridPath + ".fast_grid_2" );
257 
258     ret &= fromLegacy<bool>(   aCfg, frameGDO + "GridAxesEnabled",  gridPath + ".axes_enabled" );
259     ret &= fromLegacy<double>( aCfg, frameGDO + "GridLineWidth",    gridPath + ".line_width" );
260     ret &= fromLegacy<double>( aCfg, frameGDO + "GridMaxDensity",   gridPath + ".min_spacing" );
261     ret &= fromLegacy<bool>(   aCfg, frameGDO + "ShowGrid",         gridPath + ".show" );
262     ret &= fromLegacy<int>(    aCfg, frameGDO + "GridStyle",        gridPath + ".style" );
263     ret &= fromLegacyColor(    aCfg, frameGDO + "GridColor",        gridPath + ".color" );
264 
265     return ret;
266 }
267 
268 
addParamsForWindow(WINDOW_SETTINGS * aWindow,const std::string & aJsonPath)269 void APP_SETTINGS_BASE::addParamsForWindow( WINDOW_SETTINGS* aWindow, const std::string& aJsonPath )
270 {
271     m_params.emplace_back( new PARAM<bool>( aJsonPath + ".maximized",
272             &aWindow->state.maximized, false ) );
273 
274     m_params.emplace_back( new PARAM<wxString>( aJsonPath + ".mru_path",
275             &aWindow->mru_path, "" ) );
276 
277     m_params.emplace_back( new PARAM<int>( aJsonPath + ".size_x", &aWindow->state.size_x, 0 ) );
278 
279     m_params.emplace_back( new PARAM<int>( aJsonPath + ".size_y", &aWindow->state.size_y, 0 ) );
280 
281     m_params.emplace_back( new PARAM<wxString>( aJsonPath + ".perspective",
282             &aWindow->perspective, "" ) );
283 
284     m_params.emplace_back( new PARAM<int>( aJsonPath + ".pos_x", &aWindow->state.pos_x, 0 ) );
285 
286     m_params.emplace_back( new PARAM<int>( aJsonPath + ".pos_y", &aWindow->state.pos_y, 0 ) );
287 
288     m_params.emplace_back( new PARAM<unsigned int>( aJsonPath + ".display", &aWindow->state.display, 0 ) );
289 
290     m_params.emplace_back( new PARAM_LIST<double>( aJsonPath + ".zoom_factors",
291             &aWindow->zoom_factors, {} ) );
292 
293     m_params.emplace_back( new PARAM<bool>( aJsonPath + ".grid.axes_enabled",
294             &aWindow->grid.axes_enabled, false ) );
295 
296     m_params.emplace_back( new PARAM_LIST<wxString>( aJsonPath + ".grid.sizes",
297             &aWindow->grid.sizes, DefaultGridSizeList() ) );
298 
299     int defaultGridIdx;
300 
301     if( m_filename == "eeschema" || m_filename == "symbol_editor" || m_filename == "pl_editor" )
302         defaultGridIdx = 1;
303     else
304         defaultGridIdx = 4;
305 
306     m_params.emplace_back( new PARAM<int>( aJsonPath + ".grid.last_size",
307             &aWindow->grid.last_size_idx, defaultGridIdx ) );
308 
309     m_params.emplace_back( new PARAM<int>( aJsonPath + ".grid.fast_grid_1",
310             &aWindow->grid.fast_grid_1, defaultGridIdx ) );
311 
312     m_params.emplace_back( new PARAM<int>( aJsonPath + ".grid.fast_grid_2",
313             &aWindow->grid.fast_grid_2, defaultGridIdx + 1 ) );
314 
315     // for grid user, use a default value compatible with eeschema and pcbnew (10 mils)
316     m_params.emplace_back( new PARAM<wxString>( aJsonPath + ".grid.user_grid_x",
317             &aWindow->grid.user_grid_x, "10 mil" ) );
318     m_params.emplace_back( new PARAM<wxString>( aJsonPath + ".grid.user_grid_y",
319             &aWindow->grid.user_grid_y, "10 mil" ) );
320 
321     m_params.emplace_back( new PARAM<double>( aJsonPath + ".grid.line_width",
322             &aWindow->grid.line_width, 1.0 ) );
323 
324     m_params.emplace_back( new PARAM<double>( aJsonPath + ".grid.min_spacing",
325             &aWindow->grid.min_spacing, 10 ) );
326 
327     m_params.emplace_back( new PARAM<bool>( aJsonPath + ".grid.show",
328             &aWindow->grid.show, true ) );
329 
330     m_params.emplace_back( new PARAM<int>( aJsonPath + ".grid.style",
331             &aWindow->grid.style, 0 ) );
332 
333     m_params.emplace_back( new PARAM<int>( aJsonPath + ".grid.snap",
334             &aWindow->grid.snap, 0 ) );
335 
336     m_params.emplace_back( new PARAM<bool>( aJsonPath + ".cursor.always_show_cursor",
337             &aWindow->cursor.always_show_cursor, true ) );
338 
339     m_params.emplace_back( new PARAM<bool>( aJsonPath + ".cursor.fullscreen_cursor",
340             &aWindow->cursor.fullscreen_cursor, false ) );
341 }
342 
343 
DefaultGridSizeList() const344 const std::vector<wxString> APP_SETTINGS_BASE::DefaultGridSizeList() const
345 {
346     return { "1000 mil",
347              "500 mil",
348              "250 mil",
349              "200 mil",
350              "100 mil",
351              "50 mil",
352              "25 mil",
353              "20 mil",
354              "10 mil",
355              "5 mil",
356              "2 mil",
357              "1 mil",
358              "5.0 mm",
359              "2.5 mm",
360              "1.0 mm",
361              "0.5 mm",
362              "0.25 mm",
363              "0.2 mm",
364              "0.1 mm",
365              "0.05 mm",
366              "0.025 mm",
367              "0.01 mm" };
368 }
369