You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

516 lines
21 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 CERN
  5. * Copyright (C) 2021 KiCad Developers, see AUTHORS.txt for contributors.
  6. * @author Jon Evans <jon@craftyjon.com>
  7. *
  8. * This program is free software: you can redistribute it and/or modify it
  9. * under the terms of the GNU General Public License as published by the
  10. * Free Software Foundation, either version 3 of the License, or (at your
  11. * option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful, but
  14. * WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  16. * General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License along
  19. * with this program. If not, see <http://www.gnu.org/licenses/>.
  20. */
  21. #include <lset.h>
  22. #include <project.h>
  23. #include <project/project_local_settings.h>
  24. #include <settings/json_settings_internals.h>
  25. #include <settings/parameters.h>
  26. const int projectLocalSettingsVersion = 4;
  27. PROJECT_LOCAL_SETTINGS::PROJECT_LOCAL_SETTINGS( PROJECT* aProject, const wxString& aFilename ) :
  28. JSON_SETTINGS( aFilename, SETTINGS_LOC::PROJECT, projectLocalSettingsVersion,
  29. /* aCreateIfMissing = */ true, /* aCreateIfDefault = */ false,
  30. /* aWriteFile = */ true ),
  31. m_ActiveLayer( UNDEFINED_LAYER ),
  32. m_ContrastModeDisplay( HIGH_CONTRAST_MODE::NORMAL ),
  33. m_NetColorMode( NET_COLOR_MODE::RATSNEST ),
  34. m_AutoTrackWidth( true ),
  35. m_ZoneDisplayMode( ZONE_DISPLAY_MODE::SHOW_FILLED ),
  36. m_TrackOpacity( 1.0 ),
  37. m_ViaOpacity( 1.0 ),
  38. m_PadOpacity( 1.0 ),
  39. m_ZoneOpacity( 0.6 ),
  40. m_ShapeOpacity( 1.0 ),
  41. m_ImageOpacity( 0.6 ),
  42. m_PcbSelectionFilter(),
  43. m_project( aProject )
  44. {
  45. // Keep old files around
  46. m_deleteLegacyAfterMigration = false;
  47. m_params.emplace_back( new PARAM_LAMBDA<std::string>( "board.visible_layers",
  48. [&]() -> std::string
  49. {
  50. return m_VisibleLayers.FmtHex();
  51. },
  52. [&]( const std::string& aString )
  53. {
  54. m_VisibleLayers.ParseHex( aString );
  55. },
  56. LSET::AllLayersMask().FmtHex() ) );
  57. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "board.visible_items",
  58. [&]() -> nlohmann::json
  59. {
  60. nlohmann::json ret = nlohmann::json::array();
  61. for( size_t i = 0; i < m_VisibleItems.size(); i++ )
  62. if( m_VisibleItems.test( i ) )
  63. ret.push_back( i );
  64. return ret;
  65. },
  66. [&]( const nlohmann::json& aVal )
  67. {
  68. if( !aVal.is_array() || aVal.empty() )
  69. {
  70. m_VisibleItems = GAL_SET::DefaultVisible();
  71. return;
  72. }
  73. m_VisibleItems.reset();
  74. for( const nlohmann::json& entry : aVal )
  75. {
  76. try
  77. {
  78. int i = entry.get<int>();
  79. m_VisibleItems.set( i );
  80. }
  81. catch( ... )
  82. {
  83. // Non-integer or out of range entry in the array; ignore
  84. }
  85. }
  86. },
  87. {} ) );
  88. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "board.selection_filter",
  89. [&]() -> nlohmann::json
  90. {
  91. nlohmann::json ret;
  92. ret["lockedItems"] = m_PcbSelectionFilter.lockedItems;
  93. ret["footprints"] = m_PcbSelectionFilter.footprints;
  94. ret["text"] = m_PcbSelectionFilter.text;
  95. ret["tracks"] = m_PcbSelectionFilter.tracks;
  96. ret["vias"] = m_PcbSelectionFilter.vias;
  97. ret["pads"] = m_PcbSelectionFilter.pads;
  98. ret["graphics"] = m_PcbSelectionFilter.graphics;
  99. ret["zones"] = m_PcbSelectionFilter.zones;
  100. ret["keepouts"] = m_PcbSelectionFilter.keepouts;
  101. ret["dimensions"] = m_PcbSelectionFilter.dimensions;
  102. ret["otherItems"] = m_PcbSelectionFilter.otherItems;
  103. return ret;
  104. },
  105. [&]( const nlohmann::json& aVal )
  106. {
  107. if( aVal.empty() || !aVal.is_object() )
  108. return;
  109. SetIfPresent( aVal, "lockedItems", m_PcbSelectionFilter.lockedItems );
  110. SetIfPresent( aVal, "footprints", m_PcbSelectionFilter.footprints );
  111. SetIfPresent( aVal, "text", m_PcbSelectionFilter.text );
  112. SetIfPresent( aVal, "tracks", m_PcbSelectionFilter.tracks );
  113. SetIfPresent( aVal, "vias", m_PcbSelectionFilter.vias );
  114. SetIfPresent( aVal, "pads", m_PcbSelectionFilter.pads );
  115. SetIfPresent( aVal, "graphics", m_PcbSelectionFilter.graphics );
  116. SetIfPresent( aVal, "zones", m_PcbSelectionFilter.zones );
  117. SetIfPresent( aVal, "keepouts", m_PcbSelectionFilter.keepouts );
  118. SetIfPresent( aVal, "dimensions", m_PcbSelectionFilter.dimensions );
  119. SetIfPresent( aVal, "otherItems", m_PcbSelectionFilter.otherItems );
  120. },
  121. {
  122. { "lockedItems", false },
  123. { "footprints", true },
  124. { "text", true },
  125. { "tracks", true },
  126. { "vias", true },
  127. { "pads", true },
  128. { "graphics", true },
  129. { "zones", true },
  130. { "keepouts", true },
  131. { "dimensions", true },
  132. { "otherItems", true }
  133. } ) );
  134. m_params.emplace_back( new PARAM_ENUM<PCB_LAYER_ID>( "board.active_layer",
  135. &m_ActiveLayer, F_Cu, PCBNEW_LAYER_ID_START, F_Fab ) );
  136. m_params.emplace_back( new PARAM<wxString>( "board.active_layer_preset",
  137. &m_ActiveLayerPreset, "" ) );
  138. m_params.emplace_back( new PARAM_ENUM<HIGH_CONTRAST_MODE>( "board.high_contrast_mode",
  139. &m_ContrastModeDisplay, HIGH_CONTRAST_MODE::NORMAL,
  140. HIGH_CONTRAST_MODE::NORMAL, HIGH_CONTRAST_MODE::HIDDEN ) );
  141. m_params.emplace_back( new PARAM<double>( "board.opacity.tracks", &m_TrackOpacity, 1.0 ) );
  142. m_params.emplace_back( new PARAM<double>( "board.opacity.vias", &m_ViaOpacity, 1.0 ) );
  143. m_params.emplace_back( new PARAM<double>( "board.opacity.pads", &m_PadOpacity, 1.0 ) );
  144. m_params.emplace_back( new PARAM<double>( "board.opacity.zones", &m_ZoneOpacity, 0.6 ) );
  145. m_params.emplace_back( new PARAM<double>( "board.opacity.images", &m_ImageOpacity, 0.6 ) );
  146. m_params.emplace_back( new PARAM<double>( "board.opacity.shapes", &m_ShapeOpacity, 1.0 ) );
  147. m_params.emplace_back( new PARAM_LIST<wxString>( "board.hidden_nets", &m_HiddenNets, {} ) );
  148. m_params.emplace_back( new PARAM_SET<wxString>( "board.hidden_netclasses",
  149. &m_HiddenNetclasses, {} ) );
  150. m_params.emplace_back( new PARAM_ENUM<NET_COLOR_MODE>( "board.net_color_mode",
  151. &m_NetColorMode, NET_COLOR_MODE::RATSNEST, NET_COLOR_MODE::OFF,
  152. NET_COLOR_MODE::ALL ) );
  153. m_params.emplace_back( new PARAM<bool>( "board.auto_track_width",
  154. &m_AutoTrackWidth, true ) );
  155. m_params.emplace_back( new PARAM_ENUM<ZONE_DISPLAY_MODE>( "board.zone_display_mode",
  156. &m_ZoneDisplayMode,
  157. ZONE_DISPLAY_MODE::SHOW_FILLED, ZONE_DISPLAY_MODE::SHOW_FILLED,
  158. ZONE_DISPLAY_MODE::SHOW_TRIANGULATION ) );
  159. m_params.emplace_back( new PARAM<wxString>( "git.repo_username",
  160. &m_GitRepoUsername, "" ) );
  161. m_params.emplace_back( new PARAM<wxString>( "git.repo_password",
  162. &m_GitRepoPassword, "" ) );
  163. m_params.emplace_back( new PARAM<wxString>( "git.repo_type",
  164. &m_GitRepoType, "" ) );
  165. m_params.emplace_back( new PARAM<wxString>( "git.ssh_key",
  166. &m_GitSSHKey, "" ) );
  167. m_params.emplace_back( new PARAM<wxString>( "net_inspector_panel.filter_text",
  168. &m_NetInspectorPanel.filter_text, "" ) );
  169. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.filter_by_net_name",
  170. &m_NetInspectorPanel.filter_by_net_name, true ) );
  171. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.filter_by_netclass",
  172. &m_NetInspectorPanel.filter_by_netclass, true ) );
  173. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.group_by_netclass",
  174. &m_NetInspectorPanel.group_by_netclass, false ) );
  175. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.group_by_constraint",
  176. &m_NetInspectorPanel.group_by_constraint, false ) );
  177. m_params.emplace_back( new PARAM_LIST<wxString>( "net_inspector_panel.custom_group_rules",
  178. &m_NetInspectorPanel.custom_group_rules,
  179. {} ) );
  180. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.show_zero_pad_nets",
  181. &m_NetInspectorPanel.show_zero_pad_nets, false ) );
  182. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.show_unconnected_nets",
  183. &m_NetInspectorPanel.show_unconnected_nets, false ) );
  184. m_params.emplace_back( new PARAM<int>( "net_inspector_panel.sorting_column",
  185. &m_NetInspectorPanel.sorting_column, -1 ) );
  186. m_params.emplace_back( new PARAM<bool>( "net_inspector_panel.sort_ascending",
  187. &m_NetInspectorPanel.sort_order_asc, true ) );
  188. m_params.emplace_back( new PARAM_LIST<int>( "net_inspector_panel.col_order",
  189. &m_NetInspectorPanel.col_order, {} ) );
  190. m_params.emplace_back( new PARAM_LIST<int>( "net_inspector_panel.col_widths",
  191. &m_NetInspectorPanel.col_widths, {} ) );
  192. m_params.emplace_back( new PARAM_LIST<bool>( "net_inspector_panel.col_hidden",
  193. &m_NetInspectorPanel.col_hidden, {} ) );
  194. m_params.emplace_back( new PARAM_LIST<wxString>( "net_inspector_panel.expanded_rows",
  195. &m_NetInspectorPanel.expanded_rows, {} ) );
  196. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "project.files",
  197. [&]() -> nlohmann::json
  198. {
  199. nlohmann::json ret = nlohmann::json::array();
  200. for( PROJECT_FILE_STATE& fileState : m_files )
  201. {
  202. nlohmann::json file;
  203. file["name"] = fileState.fileName;
  204. file["open"] = fileState.open;
  205. nlohmann::json window;
  206. window["maximized"] = fileState.window.maximized;
  207. window["size_x"] = fileState.window.size_x;
  208. window["size_y"] = fileState.window.size_y;
  209. window["pos_x"] = fileState.window.pos_x;
  210. window["pos_y"] = fileState.window.pos_y;
  211. window["display"] = fileState.window.display;
  212. file["window"] = window;
  213. ret.push_back( file );
  214. }
  215. return ret;
  216. },
  217. [&]( const nlohmann::json& aVal )
  218. {
  219. if( !aVal.is_array() || aVal.empty() )
  220. {
  221. return;
  222. }
  223. m_files.clear();
  224. for( const nlohmann::json& file : aVal )
  225. {
  226. PROJECT_FILE_STATE fileState;
  227. try
  228. {
  229. SetIfPresent( file, "name", fileState.fileName );
  230. SetIfPresent( file, "open", fileState.open );
  231. SetIfPresent( file, "window.size_x", fileState.window.size_x );
  232. SetIfPresent( file, "window.size_y", fileState.window.size_y );
  233. SetIfPresent( file, "window.pos_x", fileState.window.pos_x );
  234. SetIfPresent( file, "window.pos_y", fileState.window.pos_y );
  235. SetIfPresent( file, "window.maximized", fileState.window.maximized );
  236. SetIfPresent( file, "window.display", fileState.window.display );
  237. m_files.push_back( fileState );
  238. }
  239. catch( ... )
  240. {
  241. // Non-integer or out of range entry in the array; ignore
  242. }
  243. }
  244. },
  245. {
  246. } ) );
  247. m_params.emplace_back( new PARAM_LAMBDA<nlohmann::json>( "schematic.selection_filter",
  248. [&]() -> nlohmann::json
  249. {
  250. nlohmann::json ret;
  251. ret["lockedItems"] = m_SchSelectionFilter.lockedItems;
  252. ret["symbols"] = m_SchSelectionFilter.symbols;
  253. ret["text"] = m_SchSelectionFilter.text;
  254. ret["wires"] = m_SchSelectionFilter.wires;
  255. ret["labels"] = m_SchSelectionFilter.labels;
  256. ret["pins"] = m_SchSelectionFilter.pins;
  257. ret["graphics"] = m_SchSelectionFilter.graphics;
  258. ret["images"] = m_SchSelectionFilter.images;
  259. ret["otherItems"] = m_SchSelectionFilter.otherItems;
  260. return ret;
  261. },
  262. [&]( const nlohmann::json& aVal )
  263. {
  264. if( aVal.empty() || !aVal.is_object() )
  265. return;
  266. SetIfPresent( aVal, "lockedItems", m_SchSelectionFilter.lockedItems );
  267. SetIfPresent( aVal, "symbols", m_SchSelectionFilter.symbols );
  268. SetIfPresent( aVal, "text", m_SchSelectionFilter.text );
  269. SetIfPresent( aVal, "wires", m_SchSelectionFilter.wires );
  270. SetIfPresent( aVal, "labels", m_SchSelectionFilter.labels );
  271. SetIfPresent( aVal, "pins", m_SchSelectionFilter.pins );
  272. SetIfPresent( aVal, "graphics", m_SchSelectionFilter.graphics );
  273. SetIfPresent( aVal, "images", m_SchSelectionFilter.images );
  274. SetIfPresent( aVal, "otherItems", m_SchSelectionFilter.otherItems );
  275. },
  276. {
  277. { "lockedItems", false },
  278. { "symbols", true },
  279. { "text", true },
  280. { "wires", true },
  281. { "labels", true },
  282. { "pins", true },
  283. { "graphics", true },
  284. { "images", true },
  285. { "otherItems", true }
  286. } ) );
  287. registerMigration( 1, 2,
  288. [&]()
  289. {
  290. /**
  291. * Schema version 1 to 2:
  292. * LAYER_PADS and LAYER_ZONES added to visibility controls
  293. */
  294. std::string ptr( "board.visible_items" );
  295. if( Contains( ptr ) )
  296. {
  297. if( At( ptr ).is_array() )
  298. {
  299. At( ptr ).push_back( LAYER_PADS - GAL_LAYER_ID_START );
  300. At( ptr ).push_back( LAYER_ZONES - GAL_LAYER_ID_START );
  301. }
  302. else
  303. {
  304. At( "board" ).erase( "visible_items" );
  305. }
  306. }
  307. return true;
  308. } );
  309. registerMigration( 2, 3,
  310. [&]()
  311. {
  312. /**
  313. * Schema version 2 to 3:
  314. * Fix issue with object visibility not migrating from legacy, which required
  315. * remapping of GAL_LAYER_ID to match the legacy bitmask ordering.
  316. */
  317. /// Stores a mapping from old to new enum offset
  318. const std::map<int, int> offsets = {
  319. { 22, 34 }, // LAYER_PAD_HOLEWALLS
  320. { 23, 22 }, // LAYER_VIA_HOLES
  321. { 24, 35 }, // LAYER_VIA_HOLEWALLS
  322. { 25, 23 }, // LAYER_DRC_ERROR
  323. { 26, 36 }, // LAYER_DRC_WARNING
  324. { 27, 37 }, // LAYER_DRC_EXCLUSION
  325. { 28, 38 }, // LAYER_MARKER_SHADOWS
  326. { 29, 24 }, // LAYER_DRAWINGSHEET
  327. { 30, 25 }, // LAYER_GP_OVERLAY
  328. { 31, 26 }, // LAYER_SELECT_OVERLAY
  329. { 32, 27 }, // LAYER_PCB_BACKGROUND
  330. { 33, 28 }, // LAYER_CURSOR
  331. { 34, 29 }, // LAYER_AUX_ITEM
  332. { 35, 30 }, // LAYER_DRAW_BITMAPS
  333. { 39, 32 }, // LAYER_PADS
  334. { 40, 33 }, // LAYER_ZONES
  335. };
  336. std::string ptr( "board.visible_items" );
  337. if( Contains( ptr ) && At( ptr ).is_array() )
  338. {
  339. nlohmann::json visible = nlohmann::json::array();
  340. for( const nlohmann::json& val : At( ptr ) )
  341. {
  342. try
  343. {
  344. int layer = val.get<int>();
  345. if( offsets.count( layer ) )
  346. visible.push_back( offsets.at( layer ) );
  347. else
  348. visible.push_back( layer );
  349. }
  350. catch( ... )
  351. {
  352. // skip invalid value
  353. }
  354. }
  355. At( "board" )["visible_items"] = visible;
  356. }
  357. return true;
  358. } );
  359. registerMigration( 3, 4,
  360. [&]()
  361. {
  362. // Schema version 3 to 4: LAYER_SHAPES added to visibility controls
  363. std::string ptr( "board.visible_items" );
  364. if( Contains( ptr ) )
  365. {
  366. if( At( ptr ).is_array() )
  367. At( ptr ).push_back( LAYER_SHAPES - GAL_LAYER_ID_START );
  368. else
  369. At( "board" ).erase( "visible_items" );
  370. }
  371. return true;
  372. } );
  373. }
  374. bool PROJECT_LOCAL_SETTINGS::MigrateFromLegacy( wxConfigBase* aLegacyConfig )
  375. {
  376. /**
  377. * The normal legacy migration code won't be used for this because the only legacy
  378. * information stored here was stored in board files, so we do that migration when loading
  379. * the board.
  380. */
  381. return true;
  382. }
  383. bool PROJECT_LOCAL_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce )
  384. {
  385. wxASSERT( m_project );
  386. Set( "meta.filename", m_project->GetProjectName() + "." + FILEEXT::ProjectLocalSettingsFileExtension );
  387. return JSON_SETTINGS::SaveToFile( aDirectory, aForce );
  388. }
  389. bool PROJECT_LOCAL_SETTINGS::SaveAs( const wxString& aDirectory, const wxString& aFile )
  390. {
  391. Set( "meta.filename", aFile + "." + FILEEXT::ProjectLocalSettingsFileExtension );
  392. SetFilename( aFile );
  393. return JSON_SETTINGS::SaveToFile( aDirectory, true );
  394. }
  395. const PROJECT_FILE_STATE* PROJECT_LOCAL_SETTINGS::GetFileState( const wxString& aFileName )
  396. {
  397. auto it = std::find_if( m_files.begin(), m_files.end(),
  398. [&aFileName]( const PROJECT_FILE_STATE &a )
  399. {
  400. return a.fileName == aFileName;
  401. } );
  402. if( it != m_files.end() )
  403. {
  404. return &( *it );
  405. }
  406. return nullptr;
  407. }
  408. void PROJECT_LOCAL_SETTINGS::SaveFileState( const wxString& aFileName,
  409. const WINDOW_SETTINGS* aWindowCfg, bool aOpen )
  410. {
  411. auto it = std::find_if( m_files.begin(), m_files.end(),
  412. [&aFileName]( const PROJECT_FILE_STATE& a )
  413. {
  414. return a.fileName == aFileName;
  415. } );
  416. if( it == m_files.end() )
  417. {
  418. PROJECT_FILE_STATE fileState;
  419. fileState.fileName = aFileName;
  420. fileState.open = false;
  421. fileState.window.maximized = false;
  422. fileState.window.size_x = -1;
  423. fileState.window.size_y = -1;
  424. fileState.window.pos_x = -1;
  425. fileState.window.pos_y = -1;
  426. fileState.window.display = 0;
  427. m_files.push_back( fileState );
  428. it = m_files.end() - 1;
  429. }
  430. ( *it ).window = aWindowCfg->state;
  431. ( *it ).open = aOpen;
  432. }
  433. void PROJECT_LOCAL_SETTINGS::ClearFileState()
  434. {
  435. m_files.clear();
  436. }