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.

3498 lines
119 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  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 The 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. #include <widgets/appearance_controls.h>
  21. #include <bitmaps.h>
  22. #include <board.h>
  23. #include <board_design_settings.h>
  24. #include <pad.h>
  25. #include <pcb_track.h>
  26. #include <eda_list_dialog.h>
  27. #include <string_utils.h>
  28. #include <footprint_edit_frame.h>
  29. #include <confirm.h>
  30. #include <pcb_display_options.h>
  31. #include <pcb_edit_frame.h>
  32. #include <pcb_painter.h>
  33. #include <pcbnew_settings.h>
  34. #include <project.h>
  35. #include <project/project_local_settings.h>
  36. #include <settings/color_settings.h>
  37. #include <settings/settings_manager.h>
  38. #include <tool/tool_manager.h>
  39. #include <tools/pcb_actions.h>
  40. #include <widgets/bitmap_button.h>
  41. #include <widgets/bitmap_toggle.h>
  42. #include <widgets/wx_collapsible_pane.h>
  43. #include <widgets/color_swatch.h>
  44. #include <widgets/grid_bitmap_toggle.h>
  45. #include <widgets/grid_color_swatch_helpers.h>
  46. #include <widgets/grid_text_helpers.h>
  47. #include <widgets/indicator_icon.h>
  48. #include <widgets/wx_infobar.h>
  49. #include <widgets/wx_grid.h>
  50. #include <dialogs/eda_view_switcher.h>
  51. #include <wx/checkbox.h>
  52. #include <wx/hyperlink.h>
  53. #include <wx/msgdlg.h>
  54. #include <wx/radiobut.h>
  55. #include <wx/sizer.h>
  56. #include <wx/slider.h>
  57. #include <wx/statline.h>
  58. #include <wx/textdlg.h>
  59. #include <wx/bmpbuttn.h> // needed on wxMSW for OnSetFocus()
  60. #include <core/profile.h>
  61. NET_GRID_TABLE::NET_GRID_TABLE( PCB_BASE_FRAME* aFrame, wxColor aBackgroundColor ) :
  62. wxGridTableBase(),
  63. m_frame( aFrame )
  64. {
  65. m_defaultAttr = new wxGridCellAttr;
  66. m_defaultAttr->SetBackgroundColour( aBackgroundColor );
  67. m_labelAttr = new wxGridCellAttr;
  68. m_labelAttr->SetRenderer( new GRID_CELL_ESCAPED_TEXT_RENDERER );
  69. m_labelAttr->SetBackgroundColour( aBackgroundColor );
  70. }
  71. NET_GRID_TABLE::~NET_GRID_TABLE()
  72. {
  73. m_defaultAttr->DecRef();
  74. m_labelAttr->DecRef();
  75. }
  76. wxGridCellAttr* NET_GRID_TABLE::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind )
  77. {
  78. wxGridCellAttr* attr = nullptr;
  79. switch( aCol )
  80. {
  81. case COL_COLOR: attr = m_defaultAttr; break;
  82. case COL_VISIBILITY: attr = m_defaultAttr; break;
  83. case COL_LABEL: attr = m_labelAttr; break;
  84. default: wxFAIL;
  85. }
  86. if( attr )
  87. attr->IncRef();
  88. return attr;
  89. }
  90. wxString NET_GRID_TABLE::GetValue( int aRow, int aCol )
  91. {
  92. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  93. switch( aCol )
  94. {
  95. case COL_COLOR: return m_nets[aRow].color.ToCSSString();
  96. case COL_VISIBILITY: return m_nets[aRow].visible ? wxT( "1" ) : wxT( "0" );
  97. case COL_LABEL: return m_nets[aRow].name;
  98. default: return wxEmptyString;
  99. }
  100. }
  101. void NET_GRID_TABLE::SetValue( int aRow, int aCol, const wxString& aValue )
  102. {
  103. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  104. NET_GRID_ENTRY& net = m_nets[aRow];
  105. switch( aCol )
  106. {
  107. case COL_COLOR:
  108. net.color.SetFromWxString( aValue );
  109. updateNetColor( net );
  110. break;
  111. case COL_VISIBILITY:
  112. net.visible = ( aValue != wxT( "0" ) );
  113. updateNetVisibility( net );
  114. break;
  115. case COL_LABEL:
  116. net.name = aValue;
  117. break;
  118. default:
  119. break;
  120. }
  121. }
  122. wxString NET_GRID_TABLE::GetTypeName( int aRow, int aCol )
  123. {
  124. switch( aCol )
  125. {
  126. case COL_COLOR: return wxT( "COLOR4D" );
  127. case COL_VISIBILITY: return wxGRID_VALUE_BOOL;
  128. case COL_LABEL: return wxGRID_VALUE_STRING;
  129. default: return wxGRID_VALUE_STRING;
  130. }
  131. }
  132. bool NET_GRID_TABLE::GetValueAsBool( int aRow, int aCol )
  133. {
  134. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  135. wxASSERT( aCol == COL_VISIBILITY );
  136. return m_nets[aRow].visible;
  137. }
  138. void NET_GRID_TABLE::SetValueAsBool( int aRow, int aCol, bool aValue )
  139. {
  140. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  141. wxASSERT( aCol == COL_VISIBILITY );
  142. m_nets[aRow].visible = aValue;
  143. updateNetVisibility( m_nets[aRow] );
  144. }
  145. void* NET_GRID_TABLE::GetValueAsCustom( int aRow, int aCol, const wxString& aTypeName )
  146. {
  147. wxASSERT( aCol == COL_COLOR );
  148. wxASSERT( aTypeName == wxT( "COLOR4D" ) );
  149. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  150. return ColorToVoid( m_nets[aRow].color );
  151. }
  152. void NET_GRID_TABLE::SetValueAsCustom( int aRow, int aCol, const wxString& aTypeName, void* aValue )
  153. {
  154. wxASSERT( aCol == COL_COLOR );
  155. wxASSERT( aTypeName == wxT( "COLOR4D" ) );
  156. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  157. m_nets[aRow].color = VoidToColor( aValue );
  158. updateNetColor( m_nets[aRow] );
  159. }
  160. NET_GRID_ENTRY& NET_GRID_TABLE::GetEntry( int aRow )
  161. {
  162. wxASSERT( static_cast<size_t>( aRow ) < m_nets.size() );
  163. return m_nets[aRow];
  164. }
  165. int NET_GRID_TABLE::GetRowByNetcode( int aCode ) const
  166. {
  167. auto it = std::find_if( m_nets.cbegin(), m_nets.cend(),
  168. [aCode]( const NET_GRID_ENTRY& aEntry )
  169. {
  170. return aEntry.code == aCode;
  171. } );
  172. if( it == m_nets.cend() )
  173. return -1;
  174. return std::distance( m_nets.cbegin(), it );
  175. }
  176. void NET_GRID_TABLE::Rebuild()
  177. {
  178. BOARD* board = m_frame->GetBoard();
  179. const NETNAMES_MAP& nets = board->GetNetInfo().NetsByName();
  180. KIGFX::PCB_RENDER_SETTINGS* rs = static_cast<KIGFX::PCB_RENDER_SETTINGS*>(
  181. m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings() );
  182. std::set<int>& hiddenNets = rs->GetHiddenNets();
  183. std::map<int, KIGFX::COLOR4D>& netColors = rs->GetNetColorMap();
  184. int deleted = m_nets.size();
  185. m_nets.clear();
  186. if( GetView() )
  187. {
  188. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_DELETED, 0, deleted );
  189. GetView()->ProcessTableMessage( msg );
  190. }
  191. for( const std::pair<const wxString, NETINFO_ITEM*>& pair : nets )
  192. {
  193. int netCode = pair.second->GetNetCode();
  194. if( netCode > 0 && !pair.first.StartsWith( wxT( "unconnected-(" ) ) )
  195. {
  196. COLOR4D color = netColors.count( netCode ) ? netColors.at( netCode ) :
  197. COLOR4D::UNSPECIFIED;
  198. bool visible = hiddenNets.count( netCode ) == 0;
  199. m_nets.emplace_back( NET_GRID_ENTRY( netCode, pair.first, color, visible ) );
  200. }
  201. }
  202. // TODO(JE) move to ::Compare so we can re-sort easily
  203. std::sort( m_nets.begin(), m_nets.end(),
  204. []( const NET_GRID_ENTRY& a, const NET_GRID_ENTRY& b )
  205. {
  206. return a.name < b.name;
  207. } );
  208. if( GetView() )
  209. {
  210. wxGridTableMessage msg( this, wxGRIDTABLE_NOTIFY_ROWS_APPENDED, m_nets.size() );
  211. GetView()->ProcessTableMessage( msg );
  212. }
  213. }
  214. void NET_GRID_TABLE::ShowAllNets()
  215. {
  216. for( NET_GRID_ENTRY& net : m_nets )
  217. {
  218. net.visible = true;
  219. updateNetVisibility( net );
  220. }
  221. if( GetView() )
  222. GetView()->ForceRefresh();
  223. }
  224. void NET_GRID_TABLE::HideOtherNets( const NET_GRID_ENTRY& aNet )
  225. {
  226. for( NET_GRID_ENTRY& net : m_nets )
  227. {
  228. net.visible = ( net.code == aNet.code );
  229. updateNetVisibility( net );
  230. }
  231. if( GetView() )
  232. GetView()->ForceRefresh();
  233. }
  234. void NET_GRID_TABLE::updateNetVisibility( const NET_GRID_ENTRY& aNet )
  235. {
  236. const TOOL_ACTION& action = aNet.visible ? PCB_ACTIONS::showNetInRatsnest
  237. : PCB_ACTIONS::hideNetInRatsnest;
  238. m_frame->GetToolManager()->RunAction( action, aNet.code );
  239. }
  240. void NET_GRID_TABLE::updateNetColor( const NET_GRID_ENTRY& aNet )
  241. {
  242. KIGFX::RENDER_SETTINGS* rs = m_frame->GetCanvas()->GetView()->GetPainter()->GetSettings();
  243. KIGFX::PCB_RENDER_SETTINGS* renderSettings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( rs );
  244. std::map<int, KIGFX::COLOR4D>& netColors = renderSettings->GetNetColorMap();
  245. if( aNet.color != COLOR4D::UNSPECIFIED )
  246. netColors[aNet.code] = aNet.color;
  247. else
  248. netColors.erase( aNet.code );
  249. m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
  250. m_frame->GetCanvas()->RedrawRatsnest();
  251. m_frame->GetCanvas()->Refresh();
  252. }
  253. /// Template for object appearance settings
  254. const APPEARANCE_CONTROLS::APPEARANCE_SETTING APPEARANCE_CONTROLS::s_objectSettings[] = {
  255. #define RR APPEARANCE_CONTROLS::APPEARANCE_SETTING // Render Row abbreviation to reduce source width
  256. // text id tooltip opacity slider visibility checkbox
  257. RR( _HKI( "Tracks" ), LAYER_TRACKS, _HKI( "Show tracks" ), true ),
  258. RR( _HKI( "Vias" ), LAYER_VIAS, _HKI( "Show all vias" ), true ),
  259. RR( _HKI( "Pads" ), LAYER_PADS, _HKI( "Show all pads" ), true ),
  260. RR( _HKI( "Zones" ), LAYER_ZONES, _HKI( "Show copper zones" ), true ),
  261. RR( _HKI( "Filled Shapes" ), LAYER_SHAPES, _HKI( "Opacity of filled shapes" ), true, false ),
  262. RR( _HKI( "Images" ), LAYER_DRAW_BITMAPS, _HKI( "Show user images" ), true ),
  263. RR(),
  264. RR( _HKI( "Footprints Front" ), LAYER_FOOTPRINTS_FR, _HKI( "Show footprints that are on board's front" ) ),
  265. RR( _HKI( "Footprints Back" ), LAYER_FOOTPRINTS_BK, _HKI( "Show footprints that are on board's back" ) ),
  266. RR( _HKI( "Values" ), LAYER_FP_VALUES, _HKI( "Show footprint values" ) ),
  267. RR( _HKI( "References" ), LAYER_FP_REFERENCES, _HKI( "Show footprint references" ) ),
  268. RR( _HKI( "Footprint Text" ), LAYER_FP_TEXT, _HKI( "Show all footprint text" ) ),
  269. RR(),
  270. RR(),
  271. RR( _HKI( "Ratsnest" ), LAYER_RATSNEST, _HKI( "Show unconnected nets as a ratsnest") ),
  272. RR( _HKI( "DRC Warnings" ), LAYER_DRC_WARNING, _HKI( "DRC violations with a Warning severity" ) ),
  273. RR( _HKI( "DRC Errors" ), LAYER_DRC_ERROR, _HKI( "DRC violations with an Error severity" ) ),
  274. RR( _HKI( "DRC Exclusions" ), LAYER_DRC_EXCLUSION, _HKI( "DRC violations which have been individually excluded" ) ),
  275. RR( _HKI( "Anchors" ), LAYER_ANCHOR, _HKI( "Show footprint and text origins as a cross" ) ),
  276. RR( _HKI( "Locked Item Shadow" ), LAYER_LOCKED_ITEM_SHADOW, _HKI( "Show a shadow marker on locked items" ) ),
  277. RR( _HKI( "Conflict Footprint Shadow" ), LAYER_CONFLICTS_SHADOW, _HKI( "Show a shadow marker on conflicting footprints" ) ),
  278. RR( _HKI( "Drawing Sheet" ), LAYER_DRAWINGSHEET, _HKI( "Show drawing sheet borders and title block" ) ),
  279. RR( _HKI( "Grid" ), LAYER_GRID, _HKI( "Show the (x,y) grid dots" ) )
  280. };
  281. /// These GAL layers are shown in the Objects tab in the footprint editor
  282. static std::set<int> s_allowedInFpEditor =
  283. {
  284. LAYER_TRACKS,
  285. LAYER_VIAS,
  286. LAYER_PADS,
  287. LAYER_ZONES,
  288. LAYER_SHAPES,
  289. LAYER_FP_VALUES,
  290. LAYER_FP_REFERENCES,
  291. LAYER_FP_TEXT,
  292. LAYER_DRAW_BITMAPS,
  293. LAYER_GRID
  294. };
  295. // These are the built-in layer presets that cannot be deleted
  296. LAYER_PRESET APPEARANCE_CONTROLS::presetNoLayers( _HKI( "No Layers" ), LSET(), false );
  297. LAYER_PRESET APPEARANCE_CONTROLS::presetAllLayers( _HKI( "All Layers" ),
  298. LSET::AllLayersMask(), false );
  299. LAYER_PRESET APPEARANCE_CONTROLS::presetAllCopper( _HKI( "All Copper Layers" ),
  300. LSET::AllCuMask().set( Edge_Cuts ), false );
  301. LAYER_PRESET APPEARANCE_CONTROLS::presetInnerCopper( _HKI( "Inner Copper Layers" ),
  302. LSET::InternalCuMask().set( Edge_Cuts ), false );
  303. LAYER_PRESET APPEARANCE_CONTROLS::presetFront( _HKI( "Front Layers" ),
  304. LSET::FrontMask().set( Edge_Cuts ), false );
  305. LAYER_PRESET APPEARANCE_CONTROLS::presetFrontAssembly( _HKI( "Front Assembly View" ),
  306. LSET::FrontAssembly().set( Edge_Cuts ), GAL_SET::DefaultVisible(), F_SilkS, false );
  307. LAYER_PRESET APPEARANCE_CONTROLS::presetBack( _HKI( "Back Layers" ),
  308. LSET::BackMask().set( Edge_Cuts ), true );
  309. LAYER_PRESET APPEARANCE_CONTROLS::presetBackAssembly( _HKI( "Back Assembly View" ),
  310. LSET::BackAssembly().set( Edge_Cuts ), GAL_SET::DefaultVisible(), B_SilkS, true );
  311. // this one is only used to store the object visibility settings of the last used
  312. // built-in layer preset
  313. LAYER_PRESET APPEARANCE_CONTROLS::m_lastBuiltinPreset;
  314. APPEARANCE_CONTROLS::APPEARANCE_CONTROLS( PCB_BASE_FRAME* aParent, wxWindow* aFocusOwner,
  315. bool aFpEditorMode ) :
  316. APPEARANCE_CONTROLS_BASE( aParent ),
  317. m_frame( aParent ),
  318. m_focusOwner( aFocusOwner ),
  319. m_board( nullptr ),
  320. m_isFpEditor( aFpEditorMode ),
  321. m_currentPreset( nullptr ),
  322. m_lastSelectedUserPreset( nullptr ),
  323. m_layerContextMenu( nullptr ),
  324. m_togglingNetclassRatsnestVisibility( false )
  325. {
  326. // Correct the min size from wxformbuilder not using fromdip
  327. SetMinSize( FromDIP( GetMinSize() ) );
  328. int screenHeight = wxSystemSettings::GetMetric( wxSYS_SCREEN_Y );
  329. m_iconProvider = new ROW_ICON_PROVIDER( KIUI::c_IndicatorSizeDIP, this );
  330. m_pointSize = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT ).GetPointSize();
  331. m_layerPanelColour = m_panelLayers->GetBackgroundColour().ChangeLightness( 110 );
  332. SetBorders( true, false, false, false );
  333. m_layersOuterSizer = new wxBoxSizer( wxVERTICAL );
  334. m_windowLayers->SetSizer( m_layersOuterSizer );
  335. m_windowLayers->SetScrollRate( 0, 5 );
  336. m_windowLayers->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this );
  337. m_objectsOuterSizer = new wxBoxSizer( wxVERTICAL );
  338. m_windowObjects->SetSizer( m_objectsOuterSizer );
  339. m_windowObjects->SetScrollRate( 0, 5 );
  340. m_windowObjects->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this );
  341. wxFont infoFont = KIUI::GetInfoFont( this );
  342. m_staticTextNets->SetFont( infoFont );
  343. m_staticTextNetClasses->SetFont( infoFont );
  344. m_panelLayers->SetFont( infoFont );
  345. m_windowLayers->SetFont( infoFont );
  346. m_windowObjects->SetFont( infoFont );
  347. m_presetsLabel->SetFont( infoFont );
  348. m_viewportsLabel->SetFont( infoFont );
  349. m_cbLayerPresets->SetToolTip( wxString::Format( _( "Save and restore layer visibility combinations.\n"
  350. "Use %s+Tab to activate selector.\n"
  351. "Successive Tabs while holding %s down will "
  352. "cycle through presets in the popup." ),
  353. KeyNameFromKeyCode( PRESET_SWITCH_KEY ),
  354. KeyNameFromKeyCode( PRESET_SWITCH_KEY ) ) );
  355. m_cbViewports->SetToolTip( wxString::Format( _( "Save and restore view location and zoom.\n"
  356. "Use %s+Tab to activate selector.\n"
  357. "Successive Tabs while holding %s down will "
  358. "cycle through viewports in the popup." ),
  359. KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ),
  360. KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ) ) );
  361. createControls();
  362. m_btnNetInspector->SetBitmap( KiBitmapBundle( BITMAPS::list_nets_16 ) );
  363. m_btnNetInspector->SetPadding( 2 );
  364. m_btnConfigureNetClasses->SetBitmap( KiBitmapBundle( BITMAPS::options_generic_16 ) );
  365. m_btnConfigureNetClasses->SetPadding( 2 );
  366. m_txtNetFilter->SetHint( _( "Filter nets" ) );
  367. if( screenHeight <= 900 && m_pointSize >= FromDIP( KIUI::c_IndicatorSizeDIP ) )
  368. m_pointSize = m_pointSize * 8 / 10;
  369. wxFont font = m_notebook->GetFont();
  370. #ifdef __WXMAC__
  371. font.SetPointSize( m_pointSize );
  372. m_notebook->SetFont( font );
  373. #endif
  374. auto setHighContrastMode =
  375. [&]( HIGH_CONTRAST_MODE aMode )
  376. {
  377. PCB_DISPLAY_OPTIONS opts = m_frame->GetDisplayOptions();
  378. opts.m_ContrastModeDisplay = aMode;
  379. m_frame->SetDisplayOptions( opts );
  380. passOnFocus();
  381. };
  382. m_rbHighContrastNormal->Bind( wxEVT_RADIOBUTTON,
  383. [=]( wxCommandEvent& aEvent )
  384. {
  385. setHighContrastMode( HIGH_CONTRAST_MODE::NORMAL );
  386. } );
  387. m_rbHighContrastDim->Bind( wxEVT_RADIOBUTTON,
  388. [=]( wxCommandEvent& aEvent )
  389. {
  390. setHighContrastMode( HIGH_CONTRAST_MODE::DIMMED );
  391. } );
  392. m_rbHighContrastOff->Bind( wxEVT_RADIOBUTTON,
  393. [=]( wxCommandEvent& aEvent )
  394. {
  395. setHighContrastMode( HIGH_CONTRAST_MODE::HIDDEN );
  396. } );
  397. m_cbLayerPresets->Bind( wxEVT_CHOICE, &APPEARANCE_CONTROLS::onLayerPresetChanged, this );
  398. m_btnNetInspector->Bind( wxEVT_BUTTON,
  399. [&]( wxCommandEvent& aEvent )
  400. {
  401. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::showNetInspector );
  402. } );
  403. m_btnConfigureNetClasses->Bind( wxEVT_BUTTON,
  404. [&]( wxCommandEvent& aEvent )
  405. {
  406. // This panel should only be visible in the PCB_EDIT_FRAME anyway
  407. if( PCB_EDIT_FRAME* editframe = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
  408. editframe->ShowBoardSetupDialog( _( "Net Classes" ) );
  409. passOnFocus();
  410. } );
  411. m_cbFlipBoard->SetValue( m_frame->GetCanvas()->GetView()->IsMirroredX() );
  412. m_cbFlipBoard->Bind( wxEVT_CHECKBOX,
  413. [&]( wxCommandEvent& aEvent )
  414. {
  415. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::flipBoard );
  416. syncLayerPresetSelection();
  417. } );
  418. m_toggleGridRenderer = new GRID_BITMAP_TOGGLE_RENDERER(
  419. KiBitmapBundle( BITMAPS::visibility ), KiBitmapBundle( BITMAPS::visibility_off ) );
  420. m_netsGrid->RegisterDataType( wxT( "bool" ), m_toggleGridRenderer, new wxGridCellBoolEditor );
  421. m_netsGrid->RegisterDataType( wxT( "COLOR4D" ),
  422. new GRID_CELL_COLOR_RENDERER( m_frame, SWATCH_SMALL ),
  423. new GRID_CELL_COLOR_SELECTOR( m_frame, m_netsGrid ) );
  424. m_netsTable = new NET_GRID_TABLE( m_frame, m_panelNets->GetBackgroundColour() );
  425. m_netsGrid->SetTable( m_netsTable, true );
  426. m_netsGrid->SetColLabelSize( 0 );
  427. m_netsGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
  428. m_netsGrid->SetSelectionForeground( m_netsGrid->GetDefaultCellTextColour() );
  429. m_netsGrid->SetSelectionBackground( m_panelNets->GetBackgroundColour() );
  430. const int cellPadding = 6;
  431. #ifdef __WXMAC__
  432. const int rowHeightPadding = 5;
  433. #else
  434. const int rowHeightPadding = 3;
  435. #endif
  436. wxSize size = ConvertDialogToPixels( SWATCH_SIZE_SMALL_DU );
  437. m_netsGrid->SetColSize( NET_GRID_TABLE::COL_COLOR, size.x + cellPadding );
  438. size = KiBitmapBundle( BITMAPS::visibility ).GetPreferredBitmapSizeFor( this );
  439. m_netsGrid->SetColSize( NET_GRID_TABLE::COL_VISIBILITY, size.x + cellPadding );
  440. m_netsGrid->SetDefaultCellFont( font );
  441. m_netsGrid->SetDefaultRowSize( font.GetPixelSize().y + rowHeightPadding );
  442. m_netsGrid->GetGridWindow()->Bind( wxEVT_MOTION, &APPEARANCE_CONTROLS::OnNetGridMouseEvent,
  443. this );
  444. // To handle middle click on color swatches
  445. m_netsGrid->GetGridWindow()->Bind( wxEVT_MIDDLE_UP, &APPEARANCE_CONTROLS::OnNetGridMouseEvent,
  446. this );
  447. m_netsGrid->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT );
  448. m_netclassScrolledWindow->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_DEFAULT );
  449. if( m_isFpEditor )
  450. m_notebook->RemovePage( 2 );
  451. PCBNEW_SETTINGS* settings = m_frame->GetPcbNewSettings();
  452. if( settings->m_AuiPanels.appearance_expand_layer_display )
  453. m_paneLayerDisplayOptions->Expand();
  454. if( settings->m_AuiPanels.appearance_expand_net_display )
  455. m_paneNetDisplayOptions->Expand();
  456. loadDefaultLayerPresets();
  457. rebuildLayerPresetsWidget();
  458. rebuildObjects();
  459. OnBoardChanged();
  460. // Grid visibility is loaded and set to the GAL before we are constructed
  461. SetObjectVisible( LAYER_GRID, m_frame->IsGridVisible() );
  462. Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::OnLayerContextMenu, this,
  463. ID_CHANGE_COLOR, ID_LAST_VALUE );
  464. m_frame->Bind( EDA_LANG_CHANGED, &APPEARANCE_CONTROLS::OnLanguageChanged, this );
  465. }
  466. APPEARANCE_CONTROLS::~APPEARANCE_CONTROLS()
  467. {
  468. m_frame->Unbind( EDA_LANG_CHANGED, &APPEARANCE_CONTROLS::OnLanguageChanged, this );
  469. delete m_iconProvider;
  470. }
  471. void APPEARANCE_CONTROLS::createControls()
  472. {
  473. int hotkey;
  474. wxString msg;
  475. wxFont infoFont = KIUI::GetInfoFont( this );
  476. // Create layer display options
  477. m_paneLayerDisplayOptions = new WX_COLLAPSIBLE_PANE( m_panelLayers, wxID_ANY,
  478. _( "Layer Display Options" ) );
  479. m_paneLayerDisplayOptions->Collapse();
  480. m_paneLayerDisplayOptions->SetBackgroundColour( m_notebook->GetThemeBackgroundColour() );
  481. wxWindow* layerDisplayPane = m_paneLayerDisplayOptions->GetPane();
  482. wxBoxSizer* layerDisplayOptionsSizer;
  483. layerDisplayOptionsSizer = new wxBoxSizer( wxVERTICAL );
  484. hotkey = PCB_ACTIONS::highContrastModeCycle.GetHotKey();
  485. if( hotkey )
  486. msg = wxString::Format( _( "Inactive layers (%s):" ), KeyNameFromKeyCode( hotkey ) );
  487. else
  488. msg = _( "Inactive layers:" );
  489. m_inactiveLayersLabel = new wxStaticText( layerDisplayPane, wxID_ANY, msg );
  490. m_inactiveLayersLabel->SetFont( infoFont );
  491. m_inactiveLayersLabel->Wrap( -1 );
  492. layerDisplayOptionsSizer->Add( m_inactiveLayersLabel, 0, wxEXPAND | wxBOTTOM, 2 );
  493. wxBoxSizer* contrastModeSizer;
  494. contrastModeSizer = new wxBoxSizer( wxHORIZONTAL );
  495. m_rbHighContrastNormal = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Normal" ),
  496. wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
  497. m_rbHighContrastNormal->SetFont( infoFont );
  498. m_rbHighContrastNormal->SetValue( true );
  499. m_rbHighContrastNormal->SetToolTip( _( "Inactive layers will be shown in full color" ) );
  500. contrastModeSizer->Add( m_rbHighContrastNormal, 0, wxRIGHT, 5 );
  501. contrastModeSizer->AddStretchSpacer();
  502. m_rbHighContrastDim = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Dim" ) );
  503. m_rbHighContrastDim->SetFont( infoFont );
  504. m_rbHighContrastDim->SetToolTip( _( "Inactive layers will be dimmed" ) );
  505. contrastModeSizer->Add( m_rbHighContrastDim, 0, wxRIGHT, 5 );
  506. contrastModeSizer->AddStretchSpacer();
  507. m_rbHighContrastOff = new wxRadioButton( layerDisplayPane, wxID_ANY, _( "Hide" ) );
  508. m_rbHighContrastOff->SetFont( infoFont );
  509. m_rbHighContrastOff->SetToolTip( _( "Inactive layers will be hidden" ) );
  510. contrastModeSizer->Add( m_rbHighContrastOff, 0, 0, 5 );
  511. contrastModeSizer->AddStretchSpacer();
  512. layerDisplayOptionsSizer->Add( contrastModeSizer, 0, wxEXPAND, 5 );
  513. m_layerDisplaySeparator = new wxStaticLine( layerDisplayPane, wxID_ANY, wxDefaultPosition,
  514. wxDefaultSize, wxLI_HORIZONTAL );
  515. layerDisplayOptionsSizer->Add( m_layerDisplaySeparator, 0, wxEXPAND | wxBOTTOM, 3 );
  516. m_cbFlipBoard = new wxCheckBox( layerDisplayPane, wxID_ANY, _( "Flip board view" ) );
  517. m_cbFlipBoard->SetFont( infoFont );
  518. layerDisplayOptionsSizer->Add( m_cbFlipBoard, 0, wxTOP | wxBOTTOM, 5 );
  519. layerDisplayPane->SetSizer( layerDisplayOptionsSizer );
  520. layerDisplayPane->Layout();
  521. layerDisplayOptionsSizer->Fit( layerDisplayPane );
  522. m_panelLayersSizer->Add( m_paneLayerDisplayOptions, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 5 );
  523. m_paneLayerDisplayOptions->Bind( WX_COLLAPSIBLE_PANE_CHANGED,
  524. [&]( wxCommandEvent& aEvent )
  525. {
  526. Freeze();
  527. m_panelLayers->Fit();
  528. m_sizerOuter->Layout();
  529. Thaw();
  530. } );
  531. // Create net display options
  532. m_paneNetDisplayOptions = new WX_COLLAPSIBLE_PANE( m_panelNetsAndClasses, wxID_ANY,
  533. _( "Net Display Options" ) );
  534. m_paneNetDisplayOptions->Collapse();
  535. m_paneNetDisplayOptions->SetBackgroundColour( m_notebook->GetThemeBackgroundColour() );
  536. wxWindow* netDisplayPane = m_paneNetDisplayOptions->GetPane();
  537. wxBoxSizer* netDisplayOptionsSizer = new wxBoxSizer( wxVERTICAL );
  538. //// Net color mode
  539. hotkey = PCB_ACTIONS::netColorModeCycle.GetHotKey();
  540. if( hotkey )
  541. msg = wxString::Format( _( "Net colors (%s):" ), KeyNameFromKeyCode( hotkey ) );
  542. else
  543. msg = _( "Net colors:" );
  544. m_txtNetDisplayTitle = new wxStaticText( netDisplayPane, wxID_ANY, msg );
  545. m_txtNetDisplayTitle->SetFont( infoFont );
  546. m_txtNetDisplayTitle->Wrap( -1 );
  547. m_txtNetDisplayTitle->SetToolTip( _( "Choose when to show net and netclass colors" ) );
  548. netDisplayOptionsSizer->Add( m_txtNetDisplayTitle, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2 );
  549. wxBoxSizer* netColorSizer = new wxBoxSizer( wxHORIZONTAL );
  550. m_rbNetColorAll = new wxRadioButton( netDisplayPane, wxID_ANY, _( "All" ), wxDefaultPosition,
  551. wxDefaultSize, wxRB_GROUP );
  552. m_rbNetColorAll->SetFont( infoFont );
  553. m_rbNetColorAll->SetToolTip( _( "Net and netclass colors are shown on all copper items" ) );
  554. netColorSizer->Add( m_rbNetColorAll, 0, wxRIGHT, 5 );
  555. netColorSizer->AddStretchSpacer();
  556. m_rbNetColorRatsnest = new wxRadioButton( netDisplayPane, wxID_ANY, _( "Ratsnest" ) );
  557. m_rbNetColorRatsnest->SetFont( infoFont );
  558. m_rbNetColorRatsnest->SetValue( true );
  559. m_rbNetColorRatsnest->SetToolTip( _( "Net and netclass colors are shown on the ratsnest only" ) );
  560. netColorSizer->Add( m_rbNetColorRatsnest, 0, wxRIGHT, 5 );
  561. netColorSizer->AddStretchSpacer();
  562. m_rbNetColorOff = new wxRadioButton( netDisplayPane, wxID_ANY, _( "None" ) );
  563. m_rbNetColorOff->SetFont( infoFont );
  564. m_rbNetColorOff->SetToolTip( _( "Net and netclass colors are not shown" ) );
  565. netColorSizer->Add( m_rbNetColorOff, 0, 0, 5 );
  566. netDisplayOptionsSizer->Add( netColorSizer, 0, wxEXPAND | wxBOTTOM, 5 );
  567. //// Ratsnest display
  568. hotkey = PCB_ACTIONS::ratsnestModeCycle.GetHotKey();
  569. if( hotkey )
  570. msg = wxString::Format( _( "Ratsnest display (%s):" ), KeyNameFromKeyCode( hotkey ) );
  571. else
  572. msg = _( "Ratsnest display:" );
  573. m_txtRatsnestVisibility = new wxStaticText( netDisplayPane, wxID_ANY, msg );
  574. m_txtRatsnestVisibility->SetFont( infoFont );
  575. m_txtRatsnestVisibility->Wrap( -1 );
  576. m_txtRatsnestVisibility->SetToolTip( _( "Choose which ratsnest lines to display" ) );
  577. netDisplayOptionsSizer->Add( m_txtRatsnestVisibility, 0, wxEXPAND | wxBOTTOM | wxLEFT, 2 );
  578. wxBoxSizer* ratsnestDisplayModeSizer = new wxBoxSizer( wxHORIZONTAL );
  579. m_rbRatsnestAllLayers = new wxRadioButton( netDisplayPane, wxID_ANY, _( "All" ),
  580. wxDefaultPosition, wxDefaultSize, wxRB_GROUP );
  581. m_rbRatsnestAllLayers->SetFont( infoFont );
  582. m_rbRatsnestAllLayers->SetValue( true );
  583. m_rbRatsnestAllLayers->SetToolTip( _( "Show ratsnest lines to items on all layers" ) );
  584. ratsnestDisplayModeSizer->Add( m_rbRatsnestAllLayers, 0, wxRIGHT, 5 );
  585. ratsnestDisplayModeSizer->AddStretchSpacer();
  586. m_rbRatsnestVisLayers = new wxRadioButton( netDisplayPane, wxID_ANY, _( "Visible layers" ) );
  587. m_rbRatsnestVisLayers->SetFont( infoFont );
  588. m_rbRatsnestVisLayers->SetToolTip( _( "Show ratsnest lines to items on visible layers" ) );
  589. ratsnestDisplayModeSizer->Add( m_rbRatsnestVisLayers, 0, wxRIGHT, 5 );
  590. ratsnestDisplayModeSizer->AddStretchSpacer();
  591. m_rbRatsnestNone = new wxRadioButton( netDisplayPane, wxID_ANY, _( "None" ) );
  592. m_rbRatsnestNone->SetFont( infoFont );
  593. m_rbRatsnestNone->SetToolTip( _( "Hide all ratsnest lines" ) );
  594. ratsnestDisplayModeSizer->Add( m_rbRatsnestNone, 0, 0, 5 );
  595. netDisplayOptionsSizer->Add( ratsnestDisplayModeSizer, 0, wxEXPAND | wxBOTTOM, 5 );
  596. ////
  597. netDisplayPane->SetSizer( netDisplayOptionsSizer );
  598. netDisplayPane->Layout();
  599. netDisplayOptionsSizer->Fit( netDisplayPane );
  600. m_netsTabOuterSizer->Add( m_paneNetDisplayOptions, 0, wxEXPAND | wxTOP, 5 );
  601. m_paneNetDisplayOptions->Bind( WX_COLLAPSIBLE_PANE_CHANGED,
  602. [&]( wxCommandEvent& aEvent )
  603. {
  604. Freeze();
  605. m_panelNetsAndClasses->Fit();
  606. m_sizerOuter->Layout();
  607. passOnFocus();
  608. Thaw();
  609. } );
  610. m_rbNetColorAll->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this );
  611. m_rbNetColorOff->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this );
  612. m_rbNetColorRatsnest->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onNetColorMode, this );
  613. m_rbRatsnestAllLayers->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this );
  614. m_rbRatsnestVisLayers->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this );
  615. m_rbRatsnestNone->Bind( wxEVT_RADIOBUTTON, &APPEARANCE_CONTROLS::onRatsnestMode, this );
  616. }
  617. wxSize APPEARANCE_CONTROLS::GetBestSize() const
  618. {
  619. DPI_SCALING_COMMON dpi( nullptr, m_frame );
  620. wxSize size( 220 * dpi.GetScaleFactor(), 480 * dpi.GetScaleFactor() );
  621. return size;
  622. }
  623. bool APPEARANCE_CONTROLS::IsLayerOptionsExpanded()
  624. {
  625. return m_paneLayerDisplayOptions->IsExpanded();
  626. }
  627. bool APPEARANCE_CONTROLS::IsNetOptionsExpanded()
  628. {
  629. return m_paneNetDisplayOptions->IsExpanded();
  630. }
  631. void APPEARANCE_CONTROLS::OnNotebookPageChanged( wxNotebookEvent& aEvent )
  632. {
  633. // Work around wxMac issue where the notebook pages are blank
  634. #ifdef __WXMAC__
  635. int page = aEvent.GetSelection();
  636. if( page >= 0 )
  637. m_notebook->ChangeSelection( static_cast<unsigned>( page ) );
  638. #endif
  639. #ifndef __WXMSW__
  640. // Because wxWidgets is broken and will send click events to children of the collapsible
  641. // panes even if they are collapsed without this
  642. Freeze();
  643. m_panelLayers->Fit();
  644. m_panelNetsAndClasses->Fit();
  645. m_sizerOuter->Layout();
  646. Thaw();
  647. #endif
  648. Bind( wxEVT_IDLE, &APPEARANCE_CONTROLS::idleFocusHandler, this );
  649. }
  650. void APPEARANCE_CONTROLS::idleFocusHandler( wxIdleEvent& aEvent )
  651. {
  652. passOnFocus();
  653. Unbind( wxEVT_IDLE, &APPEARANCE_CONTROLS::idleFocusHandler, this );
  654. }
  655. void APPEARANCE_CONTROLS::OnSetFocus( wxFocusEvent& aEvent )
  656. {
  657. #ifdef __WXMSW__
  658. // In wxMSW, buttons won't process events unless they have focus, so we'll let it take the
  659. // focus and give it back to the parent in the button event handler.
  660. if( wxBitmapButton* btn = dynamic_cast<wxBitmapButton*>( aEvent.GetEventObject() ) )
  661. {
  662. wxCommandEvent evt( wxEVT_BUTTON );
  663. wxPostEvent( btn, evt );
  664. }
  665. #endif
  666. passOnFocus();
  667. aEvent.Skip();
  668. }
  669. void APPEARANCE_CONTROLS::OnSize( wxSizeEvent& aEvent )
  670. {
  671. aEvent.Skip();
  672. }
  673. void APPEARANCE_CONTROLS::OnNetGridClick( wxGridEvent& event )
  674. {
  675. int row = event.GetRow();
  676. int col = event.GetCol();
  677. switch( col )
  678. {
  679. case NET_GRID_TABLE::COL_VISIBILITY:
  680. m_netsTable->SetValueAsBool( row, col, !m_netsTable->GetValueAsBool( row, col ) );
  681. m_netsGrid->ForceRefresh();
  682. break;
  683. default:
  684. break;
  685. }
  686. }
  687. void APPEARANCE_CONTROLS::OnNetGridDoubleClick( wxGridEvent& event )
  688. {
  689. int row = event.GetRow();
  690. int col = event.GetCol();
  691. switch( col )
  692. {
  693. case NET_GRID_TABLE::COL_COLOR:
  694. m_netsGrid->GetCellEditor( row, col )->BeginEdit( row, col, m_netsGrid );
  695. break;
  696. default:
  697. break;
  698. }
  699. }
  700. void APPEARANCE_CONTROLS::OnNetGridRightClick( wxGridEvent& event )
  701. {
  702. m_netsGrid->SelectRow( event.GetRow() );
  703. wxString netName = UnescapeString( m_netsGrid->GetCellValue( event.GetRow(),
  704. NET_GRID_TABLE::COL_LABEL ) );
  705. wxMenu menu;
  706. menu.Append( new wxMenuItem( &menu, ID_SET_NET_COLOR, _( "Set Net Color" ), wxEmptyString,
  707. wxITEM_NORMAL ) );
  708. menu.Append( new wxMenuItem( &menu, ID_CLEAR_NET_COLOR, _( "Clear Net Color" ), wxEmptyString,
  709. wxITEM_NORMAL ) );
  710. menu.AppendSeparator();
  711. menu.Append( new wxMenuItem( &menu, ID_HIGHLIGHT_NET,
  712. wxString::Format( _( "Highlight %s" ), netName ), wxEmptyString,
  713. wxITEM_NORMAL ) );
  714. menu.Append( new wxMenuItem( &menu, ID_SELECT_NET,
  715. wxString::Format( _( "Select Tracks and Vias in %s" ), netName ),
  716. wxEmptyString, wxITEM_NORMAL ) );
  717. menu.Append( new wxMenuItem( &menu, ID_DESELECT_NET,
  718. wxString::Format( _( "Unselect Tracks and Vias in %s" ), netName ),
  719. wxEmptyString, wxITEM_NORMAL ) );
  720. menu.AppendSeparator();
  721. menu.Append( new wxMenuItem( &menu, ID_SHOW_ALL_NETS, _( "Show All Nets" ), wxEmptyString,
  722. wxITEM_NORMAL ) );
  723. menu.Append( new wxMenuItem( &menu, ID_HIDE_OTHER_NETS, _( "Hide All Other Nets" ),
  724. wxEmptyString, wxITEM_NORMAL ) );
  725. menu.Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::onNetContextMenu, this );
  726. PopupMenu( &menu );
  727. }
  728. void APPEARANCE_CONTROLS::OnNetGridMouseEvent( wxMouseEvent& aEvent )
  729. {
  730. wxPoint pos = m_netsGrid->CalcUnscrolledPosition( aEvent.GetPosition() );
  731. wxGridCellCoords cell = m_netsGrid->XYToCell( pos );
  732. if( aEvent.Moving() || aEvent.Entering() )
  733. {
  734. aEvent.Skip();
  735. if( !cell )
  736. {
  737. m_netsGrid->GetGridWindow()->UnsetToolTip();
  738. return;
  739. }
  740. if( cell == m_hoveredCell )
  741. return;
  742. m_hoveredCell = cell;
  743. NET_GRID_ENTRY& net = m_netsTable->GetEntry( cell.GetRow() );
  744. wxString name = net.name;
  745. wxString showOrHide = net.visible ? _( "Click to hide ratsnest for %s" )
  746. : _( "Click to show ratsnest for %s" );
  747. wxString tip;
  748. if( cell.GetCol() == NET_GRID_TABLE::COL_VISIBILITY )
  749. {
  750. tip.Printf( showOrHide, name );
  751. }
  752. else if( cell.GetCol() == NET_GRID_TABLE::COL_COLOR )
  753. {
  754. tip = _( "Double click (or middle click) to change color; "
  755. "right click for more actions" );
  756. }
  757. m_netsGrid->GetGridWindow()->SetToolTip( tip );
  758. }
  759. else if( aEvent.Leaving() )
  760. {
  761. m_netsGrid->UnsetToolTip();
  762. aEvent.Skip();
  763. }
  764. else if( aEvent.Dragging() )
  765. {
  766. // not allowed
  767. CallAfter( [this]()
  768. {
  769. m_netsGrid->ClearSelection();
  770. } );
  771. }
  772. else if( aEvent.ButtonUp( wxMOUSE_BTN_MIDDLE ) && !!cell )
  773. {
  774. int row = cell.GetRow();
  775. int col = cell.GetCol();
  776. if(col == NET_GRID_TABLE::COL_COLOR )
  777. m_netsGrid->GetCellEditor( row, col )->BeginEdit( row, col, m_netsGrid );
  778. aEvent.Skip();
  779. }
  780. else
  781. {
  782. aEvent.Skip();
  783. }
  784. }
  785. void APPEARANCE_CONTROLS::OnLanguageChanged( wxCommandEvent& aEvent )
  786. {
  787. m_notebook->SetPageText( 0, _( "Layers" ) );
  788. m_notebook->SetPageText( 1, _( "Objects" ) );
  789. if( m_notebook->GetPageCount() >= 3 )
  790. m_notebook->SetPageText( 2, _( "Nets" ) );
  791. Freeze();
  792. rebuildLayers();
  793. rebuildLayerContextMenu();
  794. rebuildLayerPresetsWidget();
  795. rebuildViewportsWidget();
  796. rebuildObjects();
  797. rebuildNets();
  798. syncColorsAndVisibility();
  799. syncObjectSettings();
  800. syncLayerPresetSelection();
  801. UpdateDisplayOptions();
  802. Thaw();
  803. Refresh();
  804. aEvent.Skip();
  805. }
  806. void APPEARANCE_CONTROLS::OnBoardChanged()
  807. {
  808. Freeze();
  809. rebuildLayers();
  810. rebuildLayerContextMenu();
  811. syncColorsAndVisibility();
  812. syncObjectSettings();
  813. rebuildNets();
  814. rebuildLayerPresetsWidget();
  815. syncLayerPresetSelection();
  816. rebuildViewportsWidget();
  817. UpdateDisplayOptions();
  818. m_board = m_frame->GetBoard();
  819. if( m_board )
  820. m_board->AddListener( this );
  821. Thaw();
  822. Refresh();
  823. }
  824. void APPEARANCE_CONTROLS::OnBoardNetSettingsChanged( BOARD& aBoard )
  825. {
  826. handleBoardItemsChanged();
  827. }
  828. void APPEARANCE_CONTROLS::OnNetVisibilityChanged( int aNetCode, bool aVisibility )
  829. {
  830. if( m_togglingNetclassRatsnestVisibility )
  831. return;
  832. int row = m_netsTable->GetRowByNetcode( aNetCode );
  833. if( row >= 0 )
  834. {
  835. m_netsTable->SetValueAsBool( row, NET_GRID_TABLE::COL_VISIBILITY, aVisibility );
  836. m_netsGrid->ForceRefresh();
  837. }
  838. }
  839. bool APPEARANCE_CONTROLS::doesBoardItemNeedRebuild( BOARD_ITEM* aBoardItem )
  840. {
  841. return aBoardItem->Type() == PCB_NETINFO_T;
  842. }
  843. bool APPEARANCE_CONTROLS::doesBoardItemNeedRebuild( std::vector<BOARD_ITEM*>& aBoardItems )
  844. {
  845. bool rebuild = std::any_of( aBoardItems.begin(), aBoardItems.end(),
  846. []( const BOARD_ITEM* a )
  847. {
  848. return a->Type() == PCB_NETINFO_T;
  849. } );
  850. return rebuild;
  851. }
  852. void APPEARANCE_CONTROLS::OnBoardItemAdded( BOARD& aBoard, BOARD_ITEM* aItem )
  853. {
  854. if( doesBoardItemNeedRebuild( aItem ) )
  855. handleBoardItemsChanged();
  856. }
  857. void APPEARANCE_CONTROLS::OnBoardItemsAdded( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems )
  858. {
  859. if( doesBoardItemNeedRebuild( aItems ) )
  860. handleBoardItemsChanged();
  861. }
  862. void APPEARANCE_CONTROLS::OnBoardItemRemoved( BOARD& aBoard, BOARD_ITEM* aItem )
  863. {
  864. if( doesBoardItemNeedRebuild( aItem ) )
  865. handleBoardItemsChanged();
  866. }
  867. void APPEARANCE_CONTROLS::OnBoardItemsRemoved( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems )
  868. {
  869. if( doesBoardItemNeedRebuild( aItems ) )
  870. handleBoardItemsChanged();
  871. }
  872. void APPEARANCE_CONTROLS::OnBoardItemChanged( BOARD& aBoard, BOARD_ITEM* aItem )
  873. {
  874. if( doesBoardItemNeedRebuild( aItem ) )
  875. handleBoardItemsChanged();
  876. }
  877. void APPEARANCE_CONTROLS::OnBoardItemsChanged( BOARD& aBoard, std::vector<BOARD_ITEM*>& aItems )
  878. {
  879. if( doesBoardItemNeedRebuild( aItems ) )
  880. handleBoardItemsChanged();
  881. }
  882. void APPEARANCE_CONTROLS::OnBoardCompositeUpdate( BOARD& aBoard,
  883. std::vector<BOARD_ITEM*>& aAddedItems,
  884. std::vector<BOARD_ITEM*>& aRemovedItems,
  885. std::vector<BOARD_ITEM*>& aDeletedItems )
  886. {
  887. if( doesBoardItemNeedRebuild( aAddedItems ) || doesBoardItemNeedRebuild( aRemovedItems )
  888. || doesBoardItemNeedRebuild( aDeletedItems ) )
  889. {
  890. handleBoardItemsChanged();
  891. }
  892. }
  893. void APPEARANCE_CONTROLS::handleBoardItemsChanged()
  894. {
  895. Freeze();
  896. rebuildNets();
  897. Thaw();
  898. }
  899. void APPEARANCE_CONTROLS::OnColorThemeChanged()
  900. {
  901. syncColorsAndVisibility();
  902. syncObjectSettings();
  903. }
  904. void APPEARANCE_CONTROLS::OnDarkModeToggle()
  905. {
  906. // This is essentially a list of hacks because DarkMode isn't yet implemented inside
  907. // wxWidgets.
  908. //
  909. // The individual wxPanels, COLOR_SWATCHes and GRID_CELL_COLOR_RENDERERs should really be
  910. // overriding some virtual method or responding to some wxWidgets event so that the parent
  911. // doesn't have to know what it contains. But, that's not where we are, so... :shrug:
  912. m_layerPanelColour = m_panelLayers->GetBackgroundColour().ChangeLightness( 110 );
  913. m_windowLayers->SetBackgroundColour( m_layerPanelColour );
  914. for( wxSizerItem* child : m_layersOuterSizer->GetChildren() )
  915. {
  916. if( child && child->GetWindow() )
  917. child->GetWindow()->SetBackgroundColour( m_layerPanelColour );
  918. }
  919. // Easier than calling OnDarkModeToggle on all the GRID_CELL_COLOR_RENDERERs:
  920. m_netsGrid->RegisterDataType( wxT( "COLOR4D" ),
  921. new GRID_CELL_COLOR_RENDERER( m_frame, SWATCH_SMALL ),
  922. new GRID_CELL_COLOR_SELECTOR( m_frame, m_netsGrid ) );
  923. for( const std::pair<const wxString, APPEARANCE_SETTING*>& pair : m_netclassSettingsMap )
  924. {
  925. if( pair.second->ctl_color )
  926. pair.second->ctl_color->OnDarkModeToggle();
  927. }
  928. OnLayerChanged(); // Update selected highlighting
  929. }
  930. void APPEARANCE_CONTROLS::OnLayerChanged()
  931. {
  932. for( const std::unique_ptr<APPEARANCE_SETTING>& setting : m_layerSettings )
  933. {
  934. setting->ctl_panel->SetBackgroundColour( m_layerPanelColour );
  935. setting->ctl_indicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::OFF );
  936. }
  937. wxChar r = m_layerPanelColour.Red();
  938. wxChar g = m_layerPanelColour.Green();
  939. wxChar b = m_layerPanelColour.Blue();
  940. if( r < 240 || g < 240 || b < 240 )
  941. {
  942. r = wxChar( std::min( (int) r + 15, 255 ) );
  943. g = wxChar( std::min( (int) g + 15, 255 ) );
  944. b = wxChar( std::min( (int) b + 15, 255 ) );
  945. }
  946. else
  947. {
  948. r = wxChar( std::max( (int) r - 15, 0 ) );
  949. g = wxChar( std::max( (int) g - 15, 0 ) );
  950. b = wxChar( std::max( (int) b - 15, 0 ) );
  951. }
  952. PCB_LAYER_ID current = m_frame->GetActiveLayer();
  953. if( !m_layerSettingsMap.count( current ) )
  954. {
  955. wxASSERT( m_layerSettingsMap.count( F_Cu ) );
  956. current = F_Cu;
  957. }
  958. APPEARANCE_SETTING* newSetting = m_layerSettingsMap[ current ];
  959. newSetting->ctl_panel->SetBackgroundColour( wxColour( r, g, b ) );
  960. newSetting->ctl_indicator->SetIndicatorState( ROW_ICON_PROVIDER::STATE::ON );
  961. Refresh();
  962. }
  963. void APPEARANCE_CONTROLS::SetLayerVisible( int aLayer, bool isVisible )
  964. {
  965. LSET visible = getVisibleLayers();
  966. PCB_LAYER_ID layer = ToLAYER_ID( aLayer );
  967. if( visible.test( layer ) == isVisible )
  968. return;
  969. visible.set( layer, isVisible );
  970. setVisibleLayers( visible );
  971. m_frame->GetCanvas()->GetView()->SetLayerVisible( layer, isVisible );
  972. syncColorsAndVisibility();
  973. }
  974. void APPEARANCE_CONTROLS::SetObjectVisible( GAL_LAYER_ID aLayer, bool isVisible )
  975. {
  976. if( m_objectSettingsMap.count( aLayer ) )
  977. {
  978. APPEARANCE_SETTING* setting = m_objectSettingsMap.at( aLayer );
  979. if( setting->can_control_visibility )
  980. setting->ctl_visibility->SetValue( isVisible );
  981. }
  982. m_frame->GetBoard()->SetElementVisibility( aLayer, isVisible );
  983. m_frame->Update3DView( true, m_frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  984. m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, isVisible );
  985. m_frame->GetCanvas()->Refresh();
  986. }
  987. void APPEARANCE_CONTROLS::setVisibleLayers( LSET aLayers )
  988. {
  989. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  990. if( m_isFpEditor )
  991. {
  992. for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
  993. view->SetLayerVisible( layer, aLayers.Contains( layer ) );
  994. }
  995. else
  996. {
  997. m_frame->GetBoard()->SetVisibleLayers( aLayers );
  998. // Note: KIGFX::REPAINT isn't enough for things that go from invisible to visible as
  999. // they won't be found in the view layer's itemset for repainting.
  1000. view->UpdateAllItemsConditionally( KIGFX::ALL,
  1001. []( KIGFX::VIEW_ITEM* aItem ) -> bool
  1002. {
  1003. // Items rendered to composite layers (such as LAYER_PAD_TH) must be redrawn
  1004. // whether they're optionally flashed or not (as the layer being hidden/shown
  1005. // might be the last layer the item is visible on).
  1006. return dynamic_cast<PCB_VIA*>( aItem ) || dynamic_cast<PAD*>( aItem );
  1007. } );
  1008. m_frame->Update3DView( true, m_frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  1009. }
  1010. }
  1011. void APPEARANCE_CONTROLS::setVisibleObjects( GAL_SET aLayers )
  1012. {
  1013. if( m_isFpEditor )
  1014. {
  1015. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  1016. for( size_t i = 0; i < GAL_LAYER_INDEX( LAYER_ZONE_START ); i++ )
  1017. view->SetLayerVisible( GAL_LAYER_ID_START + GAL_LAYER_ID( i ), aLayers.test( i ) );
  1018. }
  1019. else
  1020. {
  1021. // Ratsnest visibility is controlled by the ratsnest option, and not by the preset
  1022. if( m_frame->IsType( FRAME_PCB_EDITOR ) )
  1023. aLayers.set( LAYER_RATSNEST, m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest );
  1024. m_frame->GetBoard()->SetVisibleElements( aLayers );
  1025. m_frame->Update3DView( true, m_frame->GetPcbNewSettings()->m_Display.m_Live3DRefresh );
  1026. }
  1027. }
  1028. LSET APPEARANCE_CONTROLS::getVisibleLayers()
  1029. {
  1030. if( m_isFpEditor )
  1031. {
  1032. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  1033. LSET set;
  1034. for( PCB_LAYER_ID layer : LSET::AllLayersMask().Seq() )
  1035. set.set( layer, view->IsLayerVisible( layer ) );
  1036. return set;
  1037. }
  1038. else
  1039. {
  1040. return m_frame->GetBoard()->GetVisibleLayers();
  1041. }
  1042. }
  1043. GAL_SET APPEARANCE_CONTROLS::getVisibleObjects()
  1044. {
  1045. if( m_isFpEditor )
  1046. {
  1047. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  1048. GAL_SET set;
  1049. set.reset();
  1050. for( size_t i = 0; i < set.size(); i++ )
  1051. set.set( i, view->IsLayerVisible( GAL_LAYER_ID_START + GAL_LAYER_ID( i ) ) );
  1052. return set;
  1053. }
  1054. else
  1055. {
  1056. return m_frame->GetBoard()->GetVisibleElements();
  1057. }
  1058. }
  1059. void APPEARANCE_CONTROLS::UpdateDisplayOptions()
  1060. {
  1061. const PCB_DISPLAY_OPTIONS& options = m_frame->GetDisplayOptions();
  1062. switch( options.m_ContrastModeDisplay )
  1063. {
  1064. case HIGH_CONTRAST_MODE::NORMAL: m_rbHighContrastNormal->SetValue( true ); break;
  1065. case HIGH_CONTRAST_MODE::DIMMED: m_rbHighContrastDim->SetValue( true ); break;
  1066. case HIGH_CONTRAST_MODE::HIDDEN: m_rbHighContrastOff->SetValue( true ); break;
  1067. }
  1068. switch( options.m_NetColorMode )
  1069. {
  1070. case NET_COLOR_MODE::ALL: m_rbNetColorAll->SetValue( true ); break;
  1071. case NET_COLOR_MODE::RATSNEST: m_rbNetColorRatsnest->SetValue( true ); break;
  1072. case NET_COLOR_MODE::OFF: m_rbNetColorOff->SetValue( true ); break;
  1073. }
  1074. m_cbFlipBoard->SetValue( m_frame->GetCanvas()->GetView()->IsMirroredX() );
  1075. if( !m_isFpEditor )
  1076. {
  1077. PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
  1078. if( !cfg->m_Display.m_ShowGlobalRatsnest )
  1079. m_rbRatsnestNone->SetValue( true );
  1080. else if( cfg->m_Display.m_RatsnestMode == RATSNEST_MODE::ALL )
  1081. m_rbRatsnestAllLayers->SetValue( true );
  1082. else
  1083. m_rbRatsnestVisLayers->SetValue( true );
  1084. wxASSERT( m_objectSettingsMap.count( LAYER_RATSNEST ) );
  1085. APPEARANCE_SETTING* ratsnest = m_objectSettingsMap.at( LAYER_RATSNEST );
  1086. ratsnest->ctl_visibility->SetValue( cfg->m_Display.m_ShowGlobalRatsnest );
  1087. }
  1088. }
  1089. std::vector<LAYER_PRESET> APPEARANCE_CONTROLS::GetUserLayerPresets() const
  1090. {
  1091. std::vector<LAYER_PRESET> ret;
  1092. for( const std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets )
  1093. {
  1094. if( !pair.second.readOnly )
  1095. ret.emplace_back( pair.second );
  1096. }
  1097. return ret;
  1098. }
  1099. void APPEARANCE_CONTROLS::SetUserLayerPresets( std::vector<LAYER_PRESET>& aPresetList )
  1100. {
  1101. // Reset to defaults
  1102. loadDefaultLayerPresets();
  1103. for( const LAYER_PRESET& preset : aPresetList )
  1104. {
  1105. if( m_layerPresets.count( preset.name ) )
  1106. continue;
  1107. m_layerPresets[preset.name] = preset;
  1108. m_presetMRU.Add( preset.name );
  1109. }
  1110. rebuildLayerPresetsWidget();
  1111. }
  1112. void APPEARANCE_CONTROLS::loadDefaultLayerPresets()
  1113. {
  1114. m_layerPresets.clear();
  1115. // Load the read-only defaults
  1116. for( const LAYER_PRESET& preset :
  1117. { presetAllLayers, presetNoLayers, presetAllCopper, presetInnerCopper, presetFront,
  1118. presetFrontAssembly, presetBack, presetBackAssembly } )
  1119. {
  1120. m_layerPresets[preset.name] = preset;
  1121. m_layerPresets[preset.name].readOnly = true;
  1122. }
  1123. }
  1124. void APPEARANCE_CONTROLS::ApplyLayerPreset( const wxString& aPresetName )
  1125. {
  1126. updateLayerPresetSelection( aPresetName );
  1127. wxCommandEvent dummy;
  1128. onLayerPresetChanged( dummy );
  1129. }
  1130. void APPEARANCE_CONTROLS::ApplyLayerPreset( const LAYER_PRESET& aPreset )
  1131. {
  1132. if( m_layerPresets.count( aPreset.name ) )
  1133. m_currentPreset = &m_layerPresets[aPreset.name];
  1134. else
  1135. m_currentPreset = nullptr;
  1136. m_lastSelectedUserPreset = ( m_currentPreset && !m_currentPreset->readOnly ) ? m_currentPreset
  1137. : nullptr;
  1138. updateLayerPresetSelection( aPreset.name );
  1139. doApplyLayerPreset( aPreset );
  1140. }
  1141. std::vector<VIEWPORT> APPEARANCE_CONTROLS::GetUserViewports() const
  1142. {
  1143. std::vector<VIEWPORT> ret;
  1144. for( const std::pair<const wxString, VIEWPORT>& pair : m_viewports )
  1145. ret.emplace_back( pair.second );
  1146. return ret;
  1147. }
  1148. void APPEARANCE_CONTROLS::SetUserViewports( std::vector<VIEWPORT>& aViewportList )
  1149. {
  1150. m_viewports.clear();
  1151. for( const VIEWPORT& viewport : aViewportList )
  1152. {
  1153. if( m_viewports.count( viewport.name ) )
  1154. continue;
  1155. m_viewports[viewport.name] = viewport;
  1156. m_viewportMRU.Add( viewport.name );
  1157. }
  1158. rebuildViewportsWidget();
  1159. }
  1160. void APPEARANCE_CONTROLS::ApplyViewport( const wxString& aViewportName )
  1161. {
  1162. updateViewportSelection( aViewportName );
  1163. wxCommandEvent dummy;
  1164. onViewportChanged( dummy );
  1165. }
  1166. void APPEARANCE_CONTROLS::ApplyViewport( const VIEWPORT& aViewport )
  1167. {
  1168. updateViewportSelection( aViewport.name );
  1169. doApplyViewport( aViewport );
  1170. }
  1171. void APPEARANCE_CONTROLS::rebuildLayers()
  1172. {
  1173. BOARD* board = m_frame->GetBoard();
  1174. LSET enabled = board->GetEnabledLayers();
  1175. LSET visible = getVisibleLayers();
  1176. COLOR_SETTINGS* theme = m_frame->GetColorSettings();
  1177. COLOR4D bgColor = theme->GetColor( LAYER_PCB_BACKGROUND );
  1178. bool readOnly = theme->IsReadOnly();
  1179. #ifdef __WXMAC__
  1180. wxSizerItem* m_windowLayersSizerItem = m_panelLayersSizer->GetItem( m_windowLayers );
  1181. m_windowLayersSizerItem->SetFlag( m_windowLayersSizerItem->GetFlag() & ~wxTOP );
  1182. #endif
  1183. auto appendLayer =
  1184. [&]( std::unique_ptr<APPEARANCE_SETTING>& aSetting )
  1185. {
  1186. int layer = aSetting->id;
  1187. wxPanel* panel = new wxPanel( m_windowLayers, layer );
  1188. wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL );
  1189. panel->SetSizer( sizer );
  1190. panel->SetBackgroundColour( m_layerPanelColour );
  1191. aSetting->visible = visible[layer];
  1192. // TODO(JE) consider restyling this indicator
  1193. INDICATOR_ICON* indicator = new INDICATOR_ICON( panel, *m_iconProvider,
  1194. ROW_ICON_PROVIDER::STATE::OFF,
  1195. layer );
  1196. COLOR_SWATCH* swatch = new COLOR_SWATCH( panel, COLOR4D::UNSPECIFIED, layer,
  1197. bgColor, theme->GetColor( layer ),
  1198. SWATCH_SMALL );
  1199. swatch->SetToolTip( _( "Double click or middle click for color change, "
  1200. "right click for menu" ) );
  1201. BITMAP_TOGGLE* btn_visible = new BITMAP_TOGGLE(
  1202. panel, layer, KiBitmapBundle( BITMAPS::visibility ),
  1203. KiBitmapBundle( BITMAPS::visibility_off ), aSetting->visible );
  1204. btn_visible->SetToolTip( _( "Show or hide this layer" ) );
  1205. wxStaticText* label = new wxStaticText( panel, layer, aSetting->label );
  1206. label->Wrap( -1 );
  1207. label->SetToolTip( aSetting->tooltip );
  1208. sizer->AddSpacer( 1 );
  1209. sizer->Add( indicator, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 );
  1210. sizer->AddSpacer( 5 );
  1211. sizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 );
  1212. sizer->AddSpacer( 6 );
  1213. sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL | wxTOP, 2 );
  1214. sizer->AddSpacer( 5 );
  1215. sizer->Add( label, 1, wxALIGN_CENTER_VERTICAL | wxTOP, 2 );
  1216. m_layersOuterSizer->Add( panel, 0, wxEXPAND, 0 );
  1217. aSetting->ctl_panel = panel;
  1218. aSetting->ctl_indicator = indicator;
  1219. aSetting->ctl_visibility = btn_visible;
  1220. aSetting->ctl_color = swatch;
  1221. aSetting->ctl_text = label;
  1222. panel->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this );
  1223. indicator->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this );
  1224. swatch->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this );
  1225. label->Bind( wxEVT_LEFT_DOWN, &APPEARANCE_CONTROLS::onLayerLeftClick, this );
  1226. btn_visible->Bind( TOGGLE_CHANGED,
  1227. [&]( wxCommandEvent& aEvent )
  1228. {
  1229. wxObject* btn = aEvent.GetEventObject();
  1230. int layerId = static_cast<wxWindow*>( btn )->GetId();
  1231. onLayerVisibilityToggled( static_cast<PCB_LAYER_ID>( layerId ) );
  1232. } );
  1233. swatch->Bind( COLOR_SWATCH_CHANGED, &APPEARANCE_CONTROLS::OnColorSwatchChanged,
  1234. this );
  1235. swatch->SetReadOnlyCallback( std::bind( &APPEARANCE_CONTROLS::onReadOnlySwatch,
  1236. this ) );
  1237. swatch->SetReadOnly( readOnly );
  1238. panel->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1239. indicator->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1240. swatch->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1241. btn_visible->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1242. label->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1243. };
  1244. auto updateLayer =
  1245. [&]( std::unique_ptr<APPEARANCE_SETTING>& aSetting )
  1246. {
  1247. int layer = aSetting->id;
  1248. aSetting->visible = visible[layer];
  1249. aSetting->ctl_panel->Show();
  1250. aSetting->ctl_panel->SetId( layer );
  1251. aSetting->ctl_indicator->SetWindowID( layer );
  1252. aSetting->ctl_color->SetWindowID( layer );
  1253. aSetting->ctl_color->SetSwatchColor( theme->GetColor( layer ), false );
  1254. aSetting->ctl_visibility->SetWindowID( layer );
  1255. aSetting->ctl_text->SetLabelText( aSetting->label );
  1256. aSetting->ctl_text->SetId( layer );
  1257. aSetting->ctl_text->SetToolTip( aSetting->tooltip );
  1258. };
  1259. // technical layers are shown in this order:
  1260. // Because they are static, wxGetTranslation must be explicitly
  1261. // called for tooltips.
  1262. static const struct {
  1263. PCB_LAYER_ID layerId;
  1264. wxString tooltip;
  1265. } non_cu_seq[] = {
  1266. { F_Adhes, _HKI( "Adhesive on board's front" ) },
  1267. { B_Adhes, _HKI( "Adhesive on board's back" ) },
  1268. { F_Paste, _HKI( "Solder paste on board's front" ) },
  1269. { B_Paste, _HKI( "Solder paste on board's back" ) },
  1270. { F_SilkS, _HKI( "Silkscreen on board's front" ) },
  1271. { B_SilkS, _HKI( "Silkscreen on board's back" ) },
  1272. { F_Mask, _HKI( "Solder mask on board's front" ) },
  1273. { B_Mask, _HKI( "Solder mask on board's back" ) },
  1274. { Dwgs_User, _HKI( "Explanatory drawings" ) },
  1275. { Cmts_User, _HKI( "Explanatory comments" ) },
  1276. { Eco1_User, _HKI( "User defined meaning" ) },
  1277. { Eco2_User, _HKI( "User defined meaning" ) },
  1278. { Edge_Cuts, _HKI( "Board's perimeter definition" ) },
  1279. { Margin, _HKI( "Board's edge setback outline" ) },
  1280. { F_CrtYd, _HKI( "Footprint courtyards on board's front" ) },
  1281. { B_CrtYd, _HKI( "Footprint courtyards on board's back" ) },
  1282. { F_Fab, _HKI( "Footprint assembly on board's front" ) },
  1283. { B_Fab, _HKI( "Footprint assembly on board's back" ) },
  1284. { User_1, _HKI( "User defined layer 1" ) },
  1285. { User_2, _HKI( "User defined layer 2" ) },
  1286. { User_3, _HKI( "User defined layer 3" ) },
  1287. { User_4, _HKI( "User defined layer 4" ) },
  1288. { User_5, _HKI( "User defined layer 5" ) },
  1289. { User_6, _HKI( "User defined layer 6" ) },
  1290. { User_7, _HKI( "User defined layer 7" ) },
  1291. { User_8, _HKI( "User defined layer 8" ) },
  1292. { User_9, _HKI( "User defined layer 9" ) },
  1293. { User_10, _HKI( "User defined layer 10" ) },
  1294. { User_11, _HKI( "User defined layer 11" ) },
  1295. { User_12, _HKI( "User defined layer 12" ) },
  1296. { User_13, _HKI( "User defined layer 13" ) },
  1297. { User_14, _HKI( "User defined layer 14" ) },
  1298. { User_15, _HKI( "User defined layer 15" ) },
  1299. { User_16, _HKI( "User defined layer 16" ) },
  1300. { User_17, _HKI( "User defined layer 17" ) },
  1301. { User_18, _HKI( "User defined layer 18" ) },
  1302. { User_19, _HKI( "User defined layer 19" ) },
  1303. { User_20, _HKI( "User defined layer 20" ) },
  1304. { User_21, _HKI( "User defined layer 21" ) },
  1305. { User_22, _HKI( "User defined layer 22" ) },
  1306. { User_23, _HKI( "User defined layer 23" ) },
  1307. { User_24, _HKI( "User defined layer 24" ) },
  1308. { User_25, _HKI( "User defined layer 25" ) },
  1309. { User_26, _HKI( "User defined layer 26" ) },
  1310. { User_27, _HKI( "User defined layer 27" ) },
  1311. { User_28, _HKI( "User defined layer 28" ) },
  1312. { User_29, _HKI( "User defined layer 29" ) },
  1313. { User_30, _HKI( "User defined layer 30" ) },
  1314. { User_31, _HKI( "User defined layer 31" ) },
  1315. { User_32, _HKI( "User defined layer 32" ) },
  1316. { User_33, _HKI( "User defined layer 33" ) },
  1317. { User_34, _HKI( "User defined layer 34" ) },
  1318. { User_35, _HKI( "User defined layer 35" ) },
  1319. { User_36, _HKI( "User defined layer 36" ) },
  1320. { User_37, _HKI( "User defined layer 37" ) },
  1321. { User_38, _HKI( "User defined layer 38" ) },
  1322. { User_39, _HKI( "User defined layer 39" ) },
  1323. { User_40, _HKI( "User defined layer 40" ) },
  1324. { User_41, _HKI( "User defined layer 41" ) },
  1325. { User_42, _HKI( "User defined layer 42" ) },
  1326. { User_43, _HKI( "User defined layer 43" ) },
  1327. { User_44, _HKI( "User defined layer 44" ) },
  1328. { User_45, _HKI( "User defined layer 45" ) },
  1329. };
  1330. // There is a spacer added to the end of the list that we need to remove and re-add
  1331. // after possibly adding additional layers
  1332. if( m_layersOuterSizer->GetItemCount() > 0 )
  1333. {
  1334. m_layersOuterSizer->Detach( m_layersOuterSizer->GetItemCount() - 1 );
  1335. }
  1336. // Otherwise, this is the first time we are updating the control, so we need to attach
  1337. // the handler
  1338. else
  1339. {
  1340. // Add right click handling to show the context menu when clicking to the free area in
  1341. // m_windowLayers (below the layer items)
  1342. m_windowLayers->Bind( wxEVT_RIGHT_DOWN, &APPEARANCE_CONTROLS::rightClickHandler, this );
  1343. }
  1344. std::size_t total_layers = enabled.CuStack().size();
  1345. for( const auto& entry : non_cu_seq )
  1346. {
  1347. if( enabled[entry.layerId] )
  1348. total_layers++;
  1349. }
  1350. // Adds layers to the panel until we have enough to hold our total count
  1351. while( total_layers > m_layerSettings.size() )
  1352. m_layerSettings.push_back( std::make_unique<APPEARANCE_SETTING>() );
  1353. // We never delete layers from the panel, only hide them. This saves us
  1354. // having to recreate the (possibly) later with minimal overhead
  1355. for( std::size_t ii = total_layers; ii < m_layerSettings.size(); ++ii )
  1356. {
  1357. if( m_layerSettings[ii]->ctl_panel )
  1358. m_layerSettings[ii]->ctl_panel->Show( false );
  1359. }
  1360. auto layer_it = m_layerSettings.begin();
  1361. // show all coppers first, with front on top, back on bottom, then technical layers
  1362. for( PCB_LAYER_ID layer : enabled.CuStack() )
  1363. {
  1364. wxString dsc;
  1365. switch( layer )
  1366. {
  1367. case F_Cu: dsc = _( "Front copper layer" ); break;
  1368. case B_Cu: dsc = _( "Back copper layer" ); break;
  1369. default: dsc = _( "Inner copper layer" ); break;
  1370. }
  1371. std::unique_ptr<APPEARANCE_SETTING>& setting = *layer_it;
  1372. setting->label = board->GetLayerName( layer );
  1373. setting->id = layer;
  1374. setting->tooltip = dsc;
  1375. if( setting->ctl_panel == nullptr )
  1376. appendLayer( setting );
  1377. else
  1378. updateLayer( setting );
  1379. m_layerSettingsMap[layer] = setting.get();
  1380. if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) )
  1381. {
  1382. setting->ctl_text->Disable();
  1383. setting->ctl_color->SetToolTip( wxEmptyString );
  1384. }
  1385. ++layer_it;
  1386. }
  1387. for( const auto& entry : non_cu_seq )
  1388. {
  1389. PCB_LAYER_ID layer = entry.layerId;
  1390. if( !enabled[layer] )
  1391. continue;
  1392. std::unique_ptr<APPEARANCE_SETTING>& setting = *layer_it;
  1393. setting->label = board->GetLayerName( layer );
  1394. setting->id = layer;
  1395. // Because non_cu_seq is created static, we must explicitly call wxGetTranslation for
  1396. // texts which are internationalized
  1397. setting->tooltip = wxGetTranslation( entry.tooltip );
  1398. if( setting->ctl_panel == nullptr )
  1399. appendLayer( setting );
  1400. else
  1401. updateLayer( setting );
  1402. m_layerSettingsMap[layer] = setting.get();
  1403. if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) )
  1404. {
  1405. setting->ctl_text->Disable();
  1406. setting->ctl_color->SetToolTip( wxEmptyString );
  1407. }
  1408. ++layer_it;
  1409. }
  1410. m_layersOuterSizer->AddSpacer( 10 );
  1411. m_windowLayers->SetBackgroundColour( m_layerPanelColour );
  1412. m_windowLayers->Layout();
  1413. m_paneLayerDisplayOptions->SetLabel( _( "Layer Display Options" ) );
  1414. int hotkey = PCB_ACTIONS::highContrastModeCycle.GetHotKey();
  1415. wxString msg;
  1416. if( hotkey )
  1417. msg = wxString::Format( _( "Inactive layers (%s):" ), KeyNameFromKeyCode( hotkey ) );
  1418. else
  1419. msg = _( "Inactive layers:" );
  1420. m_inactiveLayersLabel->SetLabel( msg );
  1421. m_rbHighContrastNormal->SetLabel( _( "Normal" ) );
  1422. m_rbHighContrastNormal->SetToolTip( _( "Inactive layers will be shown in full color" ) );
  1423. m_rbHighContrastDim->SetLabel( _( "Dim" ) );
  1424. m_rbHighContrastDim->SetToolTip( _( "Inactive layers will be dimmed" ) );
  1425. m_rbHighContrastOff->SetLabel( _( "Hide" ) );
  1426. m_rbHighContrastOff->SetToolTip( _( "Inactive layers will be hidden" ) );
  1427. m_cbFlipBoard->SetLabel( _( "Flip board view" ) );
  1428. }
  1429. void APPEARANCE_CONTROLS::rebuildLayerContextMenu()
  1430. {
  1431. delete m_layerContextMenu;
  1432. m_layerContextMenu = new wxMenu;
  1433. KIUI::AddMenuItem( m_layerContextMenu, ID_SHOW_ALL_COPPER_LAYERS, _( "Show All Copper Layers" ),
  1434. KiBitmap( BITMAPS::show_all_copper_layers ) );
  1435. KIUI::AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_COPPER_LAYERS, _( "Hide All Copper Layers" ),
  1436. KiBitmap( BITMAPS::show_no_copper_layers ) );
  1437. m_layerContextMenu->AppendSeparator();
  1438. KIUI::AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_BUT_ACTIVE,
  1439. _( "Hide All Layers But Active" ), KiBitmap( BITMAPS::select_w_layer ) );
  1440. m_layerContextMenu->AppendSeparator();
  1441. KIUI::AddMenuItem( m_layerContextMenu, ID_SHOW_ALL_NON_COPPER,
  1442. _( "Show All Non Copper Layers" ),
  1443. KiBitmap( BITMAPS::show_no_copper_layers ) );
  1444. KIUI::AddMenuItem( m_layerContextMenu, ID_HIDE_ALL_NON_COPPER,
  1445. _( "Hide All Non Copper Layers" ),
  1446. KiBitmap( BITMAPS::show_all_copper_layers ) );
  1447. m_layerContextMenu->AppendSeparator();
  1448. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_ALL_LAYERS, _( "Show All Layers" ),
  1449. KiBitmap( BITMAPS::show_all_layers ) );
  1450. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_NO_LAYERS, _( "Hide All Layers" ),
  1451. KiBitmap( BITMAPS::show_no_layers ) );
  1452. m_layerContextMenu->AppendSeparator();
  1453. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_FRONT_ASSEMBLY,
  1454. _( "Show Only Front Assembly Layers" ),
  1455. KiBitmap( BITMAPS::show_front_assembly_layers ) );
  1456. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_FRONT, _( "Show Only Front Layers" ),
  1457. KiBitmap( BITMAPS::show_all_front_layers ) );
  1458. // Only show the internal layer option if internal layers are enabled
  1459. if( m_frame->GetBoard()->GetCopperLayerCount() > 2 )
  1460. {
  1461. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_INNER_COPPER,
  1462. _( "Show Only Inner Layers" ),
  1463. KiBitmap( BITMAPS::show_all_copper_layers ) );
  1464. }
  1465. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_BACK, _( "Show Only Back Layers" ),
  1466. KiBitmap( BITMAPS::show_all_back_layers ) );
  1467. KIUI::AddMenuItem( m_layerContextMenu, ID_PRESET_BACK_ASSEMBLY,
  1468. _( "Show Only Back Assembly Layers" ),
  1469. KiBitmap( BITMAPS::show_back_assembly_layers ) );
  1470. }
  1471. void APPEARANCE_CONTROLS::OnLayerContextMenu( wxCommandEvent& aEvent )
  1472. {
  1473. BOARD* board = m_frame->GetBoard();
  1474. LSET visible = getVisibleLayers();
  1475. PCB_LAYER_ID current = m_frame->GetActiveLayer();
  1476. // The new preset. We keep the visibility state of objects:
  1477. LAYER_PRESET preset;
  1478. preset.renderLayers = getVisibleObjects();
  1479. switch( aEvent.GetId() )
  1480. {
  1481. case ID_PRESET_NO_LAYERS:
  1482. preset.layers = presetNoLayers.layers;
  1483. ApplyLayerPreset( preset );
  1484. return;
  1485. case ID_PRESET_ALL_LAYERS:
  1486. preset.layers = presetAllLayers.layers;
  1487. ApplyLayerPreset( preset );
  1488. return;
  1489. case ID_SHOW_ALL_COPPER_LAYERS:
  1490. visible |= presetAllCopper.layers;
  1491. setVisibleLayers( visible );
  1492. break;
  1493. case ID_HIDE_ALL_BUT_ACTIVE:
  1494. preset.layers = presetNoLayers.layers | LSET( { current } );
  1495. ApplyLayerPreset( preset );
  1496. break;
  1497. case ID_HIDE_ALL_COPPER_LAYERS:
  1498. visible &= ~presetAllCopper.layers;
  1499. if( !visible.test( current ) && visible.count() > 0 )
  1500. m_frame->SetActiveLayer( *visible.Seq().begin() );
  1501. setVisibleLayers( visible );
  1502. break;
  1503. case ID_HIDE_ALL_NON_COPPER:
  1504. visible &= presetAllCopper.layers;
  1505. if( !visible.test( current ) && visible.count() > 0 )
  1506. m_frame->SetActiveLayer( *visible.Seq().begin() );
  1507. setVisibleLayers( visible );
  1508. break;
  1509. case ID_SHOW_ALL_NON_COPPER:
  1510. visible |= ~presetAllCopper.layers;
  1511. setVisibleLayers( visible );
  1512. break;
  1513. case ID_PRESET_FRONT_ASSEMBLY:
  1514. preset.layers = presetFrontAssembly.layers;
  1515. ApplyLayerPreset( preset );
  1516. return;
  1517. case ID_PRESET_FRONT:
  1518. preset.layers = presetFront.layers;
  1519. ApplyLayerPreset( preset );
  1520. return;
  1521. case ID_PRESET_INNER_COPPER:
  1522. preset.layers = presetInnerCopper.layers;
  1523. ApplyLayerPreset( preset );
  1524. return;
  1525. case ID_PRESET_BACK:
  1526. preset.layers = presetBack.layers;
  1527. ApplyLayerPreset( preset );
  1528. return;
  1529. case ID_PRESET_BACK_ASSEMBLY:
  1530. preset.layers = presetBackAssembly.layers;
  1531. ApplyLayerPreset( preset );
  1532. return;
  1533. }
  1534. syncLayerPresetSelection();
  1535. syncColorsAndVisibility();
  1536. if( !m_isFpEditor )
  1537. m_frame->GetCanvas()->SyncLayersVisibility( board );
  1538. m_frame->GetCanvas()->Refresh();
  1539. }
  1540. int APPEARANCE_CONTROLS::GetTabIndex() const
  1541. {
  1542. return m_notebook->GetSelection();
  1543. }
  1544. void APPEARANCE_CONTROLS::SetTabIndex( int aTab )
  1545. {
  1546. size_t max = m_notebook->GetPageCount();
  1547. if( aTab >= 0 && static_cast<size_t>( aTab ) < max )
  1548. m_notebook->SetSelection( aTab );
  1549. }
  1550. void APPEARANCE_CONTROLS::syncColorsAndVisibility()
  1551. {
  1552. COLOR_SETTINGS* theme = m_frame->GetColorSettings();
  1553. bool readOnly = theme->IsReadOnly();
  1554. LSET visible = getVisibleLayers();
  1555. GAL_SET objects = getVisibleObjects();
  1556. Freeze();
  1557. for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_layerSettings )
  1558. {
  1559. int layer = setting->id;
  1560. if( setting->ctl_visibility )
  1561. setting->ctl_visibility->SetValue( visible[layer] );
  1562. if( setting->ctl_color )
  1563. {
  1564. const COLOR4D& color = theme->GetColor( layer );
  1565. setting->ctl_color->SetSwatchColor( color, false );
  1566. setting->ctl_color->SetReadOnly( readOnly );
  1567. }
  1568. }
  1569. for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings )
  1570. {
  1571. GAL_LAYER_ID layer = static_cast<GAL_LAYER_ID>( setting->id );
  1572. if( setting->ctl_visibility )
  1573. setting->ctl_visibility->SetValue( objects.Contains( layer ) );
  1574. if( setting->ctl_color )
  1575. {
  1576. const COLOR4D& color = theme->GetColor( layer );
  1577. setting->ctl_color->SetSwatchColor( color, false );
  1578. setting->ctl_color->SetReadOnly( readOnly );
  1579. }
  1580. }
  1581. // Update indicators and panel background colors
  1582. OnLayerChanged();
  1583. Thaw();
  1584. m_windowLayers->Refresh();
  1585. }
  1586. void APPEARANCE_CONTROLS::onLayerLeftClick( wxMouseEvent& aEvent )
  1587. {
  1588. wxWindow* eventSource = static_cast<wxWindow*>( aEvent.GetEventObject() );
  1589. PCB_LAYER_ID layer = ToLAYER_ID( eventSource->GetId() );
  1590. if( m_isFpEditor && LSET::ForbiddenFootprintLayers().test( layer ) )
  1591. return;
  1592. m_frame->SetActiveLayer( layer );
  1593. passOnFocus();
  1594. }
  1595. void APPEARANCE_CONTROLS::rightClickHandler( wxMouseEvent& aEvent )
  1596. {
  1597. wxASSERT( m_layerContextMenu );
  1598. PopupMenu( m_layerContextMenu );
  1599. passOnFocus();
  1600. };
  1601. void APPEARANCE_CONTROLS::onLayerVisibilityToggled( PCB_LAYER_ID aLayer )
  1602. {
  1603. LSET visibleLayers = getVisibleLayers();
  1604. visibleLayers.set( aLayer, !visibleLayers.test( aLayer ) );
  1605. setVisibleLayers( visibleLayers );
  1606. m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, visibleLayers.test( aLayer ) );
  1607. syncLayerPresetSelection();
  1608. m_frame->GetCanvas()->Refresh();
  1609. }
  1610. void APPEARANCE_CONTROLS::onObjectVisibilityChanged( GAL_LAYER_ID aLayer, bool isVisible,
  1611. bool isFinal )
  1612. {
  1613. // Special-case controls
  1614. switch( aLayer )
  1615. {
  1616. case LAYER_RATSNEST:
  1617. {
  1618. // don't touch the layers. ratsnest is enabled on per-item basis.
  1619. m_frame->GetCanvas()->GetView()->MarkTargetDirty( KIGFX::TARGET_NONCACHED );
  1620. m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, true );
  1621. if( m_frame->IsType( FRAME_PCB_EDITOR ) )
  1622. {
  1623. m_frame->GetPcbNewSettings()->m_Display.m_ShowGlobalRatsnest = isVisible;
  1624. m_frame->GetBoard()->SetElementVisibility( aLayer, isVisible );
  1625. m_frame->OnDisplayOptionsChanged();
  1626. m_frame->GetCanvas()->RedrawRatsnest();
  1627. }
  1628. break;
  1629. }
  1630. case LAYER_GRID:
  1631. m_frame->SetGridVisibility( isVisible );
  1632. m_frame->GetCanvas()->Refresh();
  1633. syncLayerPresetSelection();
  1634. break;
  1635. case LAYER_FP_TEXT:
  1636. // Because Footprint Text is a meta-control that also can disable values/references,
  1637. // drag them along here so that the user is less likely to be confused.
  1638. if( isFinal )
  1639. {
  1640. // Should only trigger when you actually click the Footprint Text button
  1641. // Otherwise it goes into infinite recursive loop with the following case section
  1642. onObjectVisibilityChanged( LAYER_FP_REFERENCES, isVisible, false );
  1643. onObjectVisibilityChanged( LAYER_FP_VALUES, isVisible, false );
  1644. m_objectSettingsMap[LAYER_FP_REFERENCES]->ctl_visibility->SetValue( isVisible );
  1645. m_objectSettingsMap[LAYER_FP_VALUES]->ctl_visibility->SetValue( isVisible );
  1646. }
  1647. break;
  1648. case LAYER_FP_REFERENCES:
  1649. case LAYER_FP_VALUES:
  1650. // In case that user changes Footprint Value/References when the Footprint Text
  1651. // meta-control is disabled, we should put it back on.
  1652. if( isVisible )
  1653. {
  1654. onObjectVisibilityChanged( LAYER_FP_TEXT, isVisible, false );
  1655. m_objectSettingsMap[LAYER_FP_TEXT]->ctl_visibility->SetValue( isVisible );
  1656. }
  1657. break;
  1658. default:
  1659. break;
  1660. }
  1661. GAL_SET visible = getVisibleObjects();
  1662. if( visible.Contains( aLayer ) != isVisible )
  1663. {
  1664. visible.set( aLayer, isVisible );
  1665. setVisibleObjects( visible );
  1666. m_frame->GetCanvas()->GetView()->SetLayerVisible( aLayer, isVisible );
  1667. syncLayerPresetSelection();
  1668. }
  1669. if( isFinal )
  1670. {
  1671. m_frame->GetCanvas()->Refresh();
  1672. passOnFocus();
  1673. }
  1674. }
  1675. void APPEARANCE_CONTROLS::rebuildObjects()
  1676. {
  1677. COLOR_SETTINGS* theme = m_frame->GetColorSettings();
  1678. COLOR4D bgColor = theme->GetColor( LAYER_PCB_BACKGROUND );
  1679. GAL_SET visible = getVisibleObjects();
  1680. int swatchWidth = m_windowObjects->ConvertDialogToPixels( wxSize( 8, 0 ) ).x;
  1681. int labelWidth = 0;
  1682. int btnWidth =
  1683. KiBitmapBundle( BITMAPS::visibility ).GetPreferredLogicalSizeFor( m_windowObjects ).x;
  1684. m_objectSettings.clear();
  1685. m_objectsOuterSizer->Clear( true );
  1686. m_objectsOuterSizer->AddSpacer( 5 );
  1687. auto appendObject =
  1688. [&]( const std::unique_ptr<APPEARANCE_SETTING>& aSetting )
  1689. {
  1690. wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL );
  1691. int layer = aSetting->id;
  1692. aSetting->visible = visible.Contains( ToGalLayer( layer ) );
  1693. COLOR4D color = theme->GetColor( layer );
  1694. COLOR4D defColor = theme->GetDefaultColor( layer );
  1695. if( color != COLOR4D::UNSPECIFIED )
  1696. {
  1697. COLOR_SWATCH* swatch = new COLOR_SWATCH( m_windowObjects, color, layer,
  1698. bgColor, defColor, SWATCH_SMALL );
  1699. swatch->SetToolTip( _( "Left double click or middle click for color change, "
  1700. "right click for menu" ) );
  1701. sizer->Add( swatch, 0, wxALIGN_CENTER_VERTICAL, 0 );
  1702. aSetting->ctl_color = swatch;
  1703. swatch->Bind( COLOR_SWATCH_CHANGED, &APPEARANCE_CONTROLS::OnColorSwatchChanged,
  1704. this );
  1705. swatch->SetReadOnlyCallback( std::bind( &APPEARANCE_CONTROLS::onReadOnlySwatch,
  1706. this ) );
  1707. }
  1708. else
  1709. {
  1710. sizer->AddSpacer( swatchWidth );
  1711. }
  1712. BITMAP_TOGGLE* btn_visible = nullptr;
  1713. wxString tip;
  1714. if( aSetting->can_control_visibility )
  1715. {
  1716. btn_visible = new BITMAP_TOGGLE(
  1717. m_windowObjects, layer, KiBitmapBundle( BITMAPS::visibility ),
  1718. KiBitmapBundle( BITMAPS::visibility_off ), aSetting->visible );
  1719. tip.Printf( _( "Show or hide %s" ), aSetting->label.Lower() );
  1720. btn_visible->SetToolTip( tip );
  1721. aSetting->ctl_visibility = btn_visible;
  1722. btn_visible->Bind( TOGGLE_CHANGED,
  1723. [&]( wxCommandEvent& aEvent )
  1724. {
  1725. int id = static_cast<wxWindow*>( aEvent.GetEventObject() )->GetId();
  1726. bool isVisible = aEvent.GetInt();
  1727. onObjectVisibilityChanged( ToGalLayer( id ), isVisible, true );
  1728. } );
  1729. }
  1730. sizer->AddSpacer( 5 );
  1731. wxStaticText* label = new wxStaticText( m_windowObjects, layer, aSetting->label );
  1732. label->Wrap( -1 );
  1733. label->SetToolTip( aSetting->tooltip );
  1734. if( aSetting->can_control_opacity )
  1735. {
  1736. label->SetMinSize( wxSize( labelWidth, -1 ) );
  1737. #ifdef __WXMAC__
  1738. if( btn_visible )
  1739. sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, 10 );
  1740. else
  1741. sizer->AddSpacer( btnWidth );
  1742. sizer->AddSpacer( 5 );
  1743. sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL | wxBOTTOM, 10 );
  1744. #else
  1745. if( btn_visible )
  1746. sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL, 0 );
  1747. else
  1748. sizer->AddSpacer( btnWidth );
  1749. sizer->AddSpacer( 5 );
  1750. sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL, 0 );
  1751. #endif
  1752. wxSlider* slider = new wxSlider( m_windowObjects, wxID_ANY, 100, 0, 100,
  1753. wxDefaultPosition, wxDefaultSize,
  1754. wxSL_HORIZONTAL );
  1755. #ifdef __WXMAC__
  1756. slider->SetMinSize( wxSize( 80, 16 ) );
  1757. #else
  1758. slider->SetMinSize( wxSize( 80, -1 ) );
  1759. #endif
  1760. tip.Printf( _( "Set opacity of %s" ), aSetting->label.Lower() );
  1761. slider->SetToolTip( tip );
  1762. sizer->Add( slider, 1, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, 5 );
  1763. aSetting->ctl_opacity = slider;
  1764. auto opacitySliderHandler =
  1765. [this, layer]( wxCommandEvent& aEvent )
  1766. {
  1767. wxSlider* ctrl = static_cast<wxSlider*>( aEvent.GetEventObject() );
  1768. int value = ctrl->GetValue();
  1769. onObjectOpacitySlider( layer, value / 100.0f );
  1770. };
  1771. slider->Bind( wxEVT_SCROLL_CHANGED, opacitySliderHandler );
  1772. slider->Bind( wxEVT_SCROLL_THUMBTRACK, opacitySliderHandler );
  1773. slider->Bind( wxEVT_SET_FOCUS, &APPEARANCE_CONTROLS::OnSetFocus, this );
  1774. }
  1775. else
  1776. {
  1777. if( btn_visible )
  1778. sizer->Add( btn_visible, 0, wxALIGN_CENTER_VERTICAL, 0 );
  1779. else
  1780. sizer->AddSpacer( btnWidth );
  1781. sizer->AddSpacer( 5 );
  1782. sizer->Add( label, 0, wxALIGN_CENTER_VERTICAL, 0 );
  1783. }
  1784. aSetting->ctl_text = label;
  1785. m_objectsOuterSizer->Add( sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, 5 );
  1786. if( !aSetting->can_control_opacity )
  1787. m_objectsOuterSizer->AddSpacer( 2 );
  1788. };
  1789. for( const APPEARANCE_SETTING& s_setting : s_objectSettings )
  1790. {
  1791. if( m_isFpEditor && !s_allowedInFpEditor.count( s_setting.id ) )
  1792. continue;
  1793. m_objectSettings.emplace_back( std::make_unique<APPEARANCE_SETTING>( s_setting ) );
  1794. std::unique_ptr<APPEARANCE_SETTING>& setting = m_objectSettings.back();
  1795. // Because s_render_rows is created static, we must explicitly call wxGetTranslation
  1796. // for texts which are internationalized (tool tips and item names)
  1797. setting->tooltip = wxGetTranslation( s_setting.tooltip );
  1798. setting->label = wxGetTranslation( s_setting.label );
  1799. if( setting->can_control_opacity )
  1800. {
  1801. int width = m_windowObjects->GetTextExtent( setting->label ).x + 5;
  1802. labelWidth = std::max( labelWidth, width );
  1803. }
  1804. if( !s_setting.spacer )
  1805. m_objectSettingsMap[ToGalLayer( setting->id )] = setting.get();
  1806. }
  1807. for( const std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings )
  1808. {
  1809. if( setting->spacer )
  1810. m_objectsOuterSizer->AddSpacer( m_pointSize / 2 );
  1811. else
  1812. appendObject( setting );
  1813. }
  1814. m_objectsOuterSizer->Layout();
  1815. }
  1816. void APPEARANCE_CONTROLS::syncObjectSettings()
  1817. {
  1818. GAL_SET visible = getVisibleObjects();
  1819. const PCB_DISPLAY_OPTIONS& opts = m_frame->GetDisplayOptions();
  1820. for( std::unique_ptr<APPEARANCE_SETTING>& setting : m_objectSettings )
  1821. {
  1822. if( setting->spacer )
  1823. continue;
  1824. GAL_LAYER_ID layer = ToGalLayer( setting->id );
  1825. if( setting->ctl_visibility )
  1826. setting->ctl_visibility->SetValue( visible.Contains( layer ) );
  1827. if( setting->ctl_color )
  1828. {
  1829. COLOR4D color = m_frame->GetColorSettings()->GetColor( setting->id );
  1830. setting->ctl_color->SetSwatchColor( color, false );
  1831. }
  1832. }
  1833. wxASSERT( m_objectSettingsMap.count( LAYER_TRACKS )
  1834. && m_objectSettingsMap.count( LAYER_VIAS )
  1835. && m_objectSettingsMap.count( LAYER_PADS )
  1836. && m_objectSettingsMap.count( LAYER_ZONES )
  1837. && m_objectSettingsMap.count( LAYER_DRAW_BITMAPS )
  1838. && m_objectSettingsMap.count( LAYER_SHAPES ) );
  1839. m_objectSettingsMap[LAYER_TRACKS]->ctl_opacity->SetValue( opts.m_TrackOpacity * 100 );
  1840. m_objectSettingsMap[LAYER_VIAS]->ctl_opacity->SetValue( opts.m_ViaOpacity * 100 );
  1841. m_objectSettingsMap[LAYER_PADS]->ctl_opacity->SetValue( opts.m_PadOpacity * 100 );
  1842. m_objectSettingsMap[LAYER_ZONES]->ctl_opacity->SetValue( opts.m_ZoneOpacity * 100 );
  1843. m_objectSettingsMap[LAYER_DRAW_BITMAPS]->ctl_opacity->SetValue( opts.m_ImageOpacity * 100 );
  1844. m_objectSettingsMap[LAYER_SHAPES]->ctl_opacity->SetValue( opts.m_FilledShapeOpacity * 100 );
  1845. }
  1846. void APPEARANCE_CONTROLS::buildNetClassMenu( wxMenu& aMenu, bool isDefaultClass,
  1847. const wxString& aName )
  1848. {
  1849. BOARD* board = m_frame->GetBoard();
  1850. std::shared_ptr<NET_SETTINGS>& netSettings = board->GetDesignSettings().m_NetSettings;
  1851. if( !isDefaultClass)
  1852. {
  1853. aMenu.Append( new wxMenuItem( &aMenu, ID_SET_NET_COLOR, _( "Set Netclass Color" ),
  1854. wxEmptyString, wxITEM_NORMAL ) );
  1855. wxMenuItem* schematicColor =
  1856. new wxMenuItem( &aMenu, ID_USE_SCHEMATIC_NET_COLOR, _( "Use Color from Schematic" ),
  1857. wxEmptyString, wxITEM_NORMAL );
  1858. std::shared_ptr<NETCLASS> nc = netSettings->GetNetClassByName( aName );
  1859. const KIGFX::COLOR4D ncColor = nc->GetSchematicColor();
  1860. aMenu.Append( schematicColor );
  1861. if( ncColor == KIGFX::COLOR4D::UNSPECIFIED )
  1862. schematicColor->Enable( false );
  1863. aMenu.Append( new wxMenuItem( &aMenu, ID_CLEAR_NET_COLOR, _( "Clear Netclass Color" ),
  1864. wxEmptyString, wxITEM_NORMAL ) );
  1865. aMenu.AppendSeparator();
  1866. }
  1867. wxString name = UnescapeString( aName );
  1868. aMenu.Append( new wxMenuItem( &aMenu, ID_HIGHLIGHT_NET,
  1869. wxString::Format( _( "Highlight Nets in %s" ), name ),
  1870. wxEmptyString, wxITEM_NORMAL ) );
  1871. aMenu.Append( new wxMenuItem( &aMenu, ID_SELECT_NET,
  1872. wxString::Format( _( "Select Tracks and Vias in %s" ), name ),
  1873. wxEmptyString, wxITEM_NORMAL ) );
  1874. aMenu.Append( new wxMenuItem( &aMenu, ID_DESELECT_NET,
  1875. wxString::Format( _( "Unselect Tracks and Vias in %s" ), name ),
  1876. wxEmptyString, wxITEM_NORMAL ) );
  1877. aMenu.AppendSeparator();
  1878. aMenu.Append( new wxMenuItem( &aMenu, ID_SHOW_ALL_NETS, _( "Show All Netclasses" ),
  1879. wxEmptyString, wxITEM_NORMAL ) );
  1880. aMenu.Append( new wxMenuItem( &aMenu, ID_HIDE_OTHER_NETS, _( "Hide All Other Netclasses" ),
  1881. wxEmptyString, wxITEM_NORMAL ) );
  1882. aMenu.Bind( wxEVT_COMMAND_MENU_SELECTED, &APPEARANCE_CONTROLS::onNetclassContextMenu, this );
  1883. }
  1884. void APPEARANCE_CONTROLS::rebuildNets()
  1885. {
  1886. BOARD* board = m_frame->GetBoard();
  1887. COLOR_SETTINGS* theme = m_frame->GetColorSettings();
  1888. COLOR4D bgColor = theme->GetColor( LAYER_PCB_BACKGROUND );
  1889. // If the board isn't fully loaded, we can't yet rebuild
  1890. if( !board->GetProject() )
  1891. return;
  1892. m_staticTextNets->SetLabel( _( "Nets" ) );
  1893. m_staticTextNetClasses->SetLabel( _( "Net Classes" ) );
  1894. std::shared_ptr<NET_SETTINGS>& netSettings = board->GetDesignSettings().m_NetSettings;
  1895. const std::set<wxString>& hiddenClasses = m_frame->Prj().GetLocalSettings().m_HiddenNetclasses;
  1896. m_netclassOuterSizer->Clear( true );
  1897. auto appendNetclass =
  1898. [&]( int aId, const std::shared_ptr<NETCLASS>& aClass, bool isDefaultClass = false )
  1899. {
  1900. wxString name = aClass->GetName();
  1901. m_netclassSettings.emplace_back( std::make_unique<APPEARANCE_SETTING>() );
  1902. APPEARANCE_SETTING* setting = m_netclassSettings.back().get();
  1903. m_netclassSettingsMap[name] = setting;
  1904. setting->ctl_panel = new wxPanel( m_netclassScrolledWindow, aId );
  1905. wxBoxSizer* sizer = new wxBoxSizer( wxHORIZONTAL );
  1906. setting->ctl_panel->SetSizer( sizer );
  1907. COLOR4D color = netSettings->HasNetclass( name )
  1908. ? netSettings->GetNetClassByName( name )->GetPcbColor()
  1909. : COLOR4D::UNSPECIFIED;
  1910. setting->ctl_color = new COLOR_SWATCH( setting->ctl_panel, color, aId, bgColor,
  1911. COLOR4D::UNSPECIFIED, SWATCH_SMALL );
  1912. setting->ctl_color->SetToolTip( _( "Left double click or middle click for color "
  1913. "change, right click for menu" ) );
  1914. setting->ctl_color->Bind( COLOR_SWATCH_CHANGED,
  1915. &APPEARANCE_CONTROLS::onNetclassColorChanged, this );
  1916. // Default netclass can't have an override color
  1917. if( isDefaultClass )
  1918. setting->ctl_color->Hide();
  1919. setting->ctl_visibility = new BITMAP_TOGGLE(
  1920. setting->ctl_panel, aId, KiBitmapBundle( BITMAPS::visibility ),
  1921. KiBitmapBundle( BITMAPS::visibility_off ), !hiddenClasses.count( name ) );
  1922. wxString tip;
  1923. tip.Printf( _( "Show or hide ratsnest for nets in %s" ), name );
  1924. setting->ctl_visibility->SetToolTip( tip );
  1925. setting->ctl_text = new wxStaticText( setting->ctl_panel, aId, name );
  1926. setting->ctl_text->Wrap( -1 );
  1927. int flags = wxALIGN_CENTER_VERTICAL;
  1928. sizer->Add( setting->ctl_color, 0, flags | wxRESERVE_SPACE_EVEN_IF_HIDDEN, 5 );
  1929. sizer->AddSpacer( 7 );
  1930. sizer->Add( setting->ctl_visibility, 0, flags, 5 );
  1931. sizer->AddSpacer( 3 );
  1932. sizer->Add( setting->ctl_text, 1, flags, 5 );
  1933. m_netclassOuterSizer->Add( setting->ctl_panel, 0, wxEXPAND, 5 );
  1934. m_netclassOuterSizer->AddSpacer( 2 );
  1935. setting->ctl_visibility->Bind( TOGGLE_CHANGED,
  1936. &APPEARANCE_CONTROLS::onNetclassVisibilityChanged,
  1937. this );
  1938. auto menuHandler =
  1939. [&, name, isDefaultClass]( wxMouseEvent& aEvent )
  1940. {
  1941. wxMenu menu;
  1942. buildNetClassMenu( menu, isDefaultClass, name );
  1943. m_contextMenuNetclass = name;
  1944. PopupMenu( &menu );
  1945. };
  1946. setting->ctl_panel->Bind( wxEVT_RIGHT_DOWN, menuHandler );
  1947. setting->ctl_visibility->Bind( wxEVT_RIGHT_DOWN, menuHandler );
  1948. setting->ctl_color->Bind( wxEVT_RIGHT_DOWN, menuHandler );
  1949. setting->ctl_text->Bind( wxEVT_RIGHT_DOWN, menuHandler );
  1950. };
  1951. std::vector<wxString> names;
  1952. for( const auto& [name, netclass] : netSettings->GetNetclasses() )
  1953. names.emplace_back( name );
  1954. std::sort( names.begin(), names.end() );
  1955. m_netclassIdMap.clear();
  1956. int idx = wxID_HIGHEST;
  1957. m_netclassIdMap[idx] = netSettings->GetDefaultNetclass()->GetName();
  1958. appendNetclass( idx++, netSettings->GetDefaultNetclass(), true );
  1959. for( const wxString& name : names )
  1960. {
  1961. m_netclassIdMap[idx] = name;
  1962. appendNetclass( idx++, netSettings->GetNetclasses().at( name ) );
  1963. }
  1964. int hotkey;
  1965. wxString msg;
  1966. m_paneNetDisplayOptions->SetLabel( _( "Net Display Options" ) );
  1967. hotkey = PCB_ACTIONS::netColorModeCycle.GetHotKey();
  1968. if( hotkey )
  1969. msg = wxString::Format( _( "Net colors (%s):" ), KeyNameFromKeyCode( hotkey ) );
  1970. else
  1971. msg = _( "Net colors:" );
  1972. m_txtNetDisplayTitle->SetLabel( msg );
  1973. m_txtNetDisplayTitle->SetToolTip( _( "Choose when to show net and netclass colors" ) );
  1974. m_rbNetColorAll->SetLabel( _( "All" ) );
  1975. m_rbNetColorAll->SetToolTip( _( "Net and netclass colors are shown on all copper items" ) );
  1976. m_rbNetColorRatsnest->SetLabel( _( "Ratsnest" ) );
  1977. m_rbNetColorRatsnest->SetToolTip( _( "Net and netclass colors are shown on the ratsnest only" ) );
  1978. m_rbNetColorOff->SetLabel( _( "None" ) );
  1979. m_rbNetColorOff->SetToolTip( _( "Net and netclass colors are not shown" ) );
  1980. hotkey = PCB_ACTIONS::ratsnestModeCycle.GetHotKey();
  1981. if( hotkey )
  1982. msg = wxString::Format( _( "Ratsnest display (%s):" ), KeyNameFromKeyCode( hotkey ) );
  1983. else
  1984. msg = _( "Ratsnest display:" );
  1985. m_txtRatsnestVisibility->SetLabel( msg );
  1986. m_txtRatsnestVisibility->SetToolTip( _( "Choose which ratsnest lines to display" ) );
  1987. m_rbRatsnestAllLayers->SetLabel( _( "All" ) );
  1988. m_rbRatsnestAllLayers->SetToolTip( _( "Show ratsnest lines to items on all layers" ) );
  1989. m_rbRatsnestVisLayers->SetLabel( _( "Visible layers" ) );
  1990. m_rbRatsnestVisLayers->SetToolTip( _( "Show ratsnest lines to items on visible layers" ) );
  1991. m_rbRatsnestNone->SetLabel( _( "None" ) );
  1992. m_rbRatsnestNone->SetToolTip( _( "Hide all ratsnest lines" ) );
  1993. m_netclassOuterSizer->Layout();
  1994. m_netsTable->Rebuild();
  1995. m_panelNets->GetSizer()->Layout();
  1996. }
  1997. void APPEARANCE_CONTROLS::rebuildLayerPresetsWidget()
  1998. {
  1999. m_viewportsLabel->SetLabel( wxString::Format( _( "Presets (%s+Tab):" ),
  2000. KeyNameFromKeyCode( PRESET_SWITCH_KEY ) ) );
  2001. m_cbLayerPresets->Clear();
  2002. m_presetMRU.clear();
  2003. // Build the layers preset list.
  2004. // By default, the presetAllLayers will be selected
  2005. int idx = 0;
  2006. int default_idx = 0;
  2007. for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets )
  2008. {
  2009. const wxString translatedName = wxGetTranslation( pair.first );
  2010. m_cbLayerPresets->Append( wxGetTranslation( translatedName ),
  2011. static_cast<void*>( &pair.second ) );
  2012. m_presetMRU.push_back( translatedName );
  2013. if( pair.first == presetAllLayers.name )
  2014. default_idx = idx;
  2015. idx++;
  2016. }
  2017. m_cbLayerPresets->Append( wxT( "---" ) );
  2018. m_cbLayerPresets->Append( _( "Save preset..." ) );
  2019. m_cbLayerPresets->Append( _( "Delete preset..." ) );
  2020. // At least the built-in presets should always be present
  2021. wxASSERT( !m_layerPresets.empty() );
  2022. // Default preset: all layers
  2023. m_cbLayerPresets->SetSelection( default_idx );
  2024. m_currentPreset = &m_layerPresets[presetAllLayers.name];
  2025. }
  2026. void APPEARANCE_CONTROLS::syncLayerPresetSelection()
  2027. {
  2028. LSET visibleLayers = getVisibleLayers();
  2029. GAL_SET visibleObjects = getVisibleObjects();
  2030. bool flipBoard = m_cbFlipBoard->GetValue();
  2031. auto it = std::find_if( m_layerPresets.begin(), m_layerPresets.end(),
  2032. [&]( const std::pair<const wxString, LAYER_PRESET>& aPair )
  2033. {
  2034. return ( aPair.second.layers == visibleLayers
  2035. && aPair.second.renderLayers == visibleObjects
  2036. && aPair.second.flipBoard == flipBoard );
  2037. } );
  2038. if( it != m_layerPresets.end() )
  2039. {
  2040. // Select the right m_cbLayersPresets item.
  2041. // but these items are translated if they are predefined items.
  2042. bool do_translate = it->second.readOnly;
  2043. wxString text = do_translate ? wxGetTranslation( it->first ) : it->first;
  2044. m_cbLayerPresets->SetStringSelection( text );
  2045. }
  2046. else
  2047. {
  2048. m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 ); // separator
  2049. }
  2050. m_currentPreset = static_cast<LAYER_PRESET*>(
  2051. m_cbLayerPresets->GetClientData( m_cbLayerPresets->GetSelection() ) );
  2052. }
  2053. void APPEARANCE_CONTROLS::updateLayerPresetSelection( const wxString& aName )
  2054. {
  2055. // look at m_layerPresets to know if aName is a read only preset, or a user preset.
  2056. // Read only presets have translated names in UI, so we have to use
  2057. // a translated name in UI selection.
  2058. // But for a user preset name we should search for aName (not translated)
  2059. wxString ui_label = aName;
  2060. for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets )
  2061. {
  2062. if( pair.first != aName )
  2063. continue;
  2064. if( pair.second.readOnly == true )
  2065. ui_label = wxGetTranslation( aName );
  2066. break;
  2067. }
  2068. int idx = m_cbLayerPresets->FindString( ui_label );
  2069. if( idx >= 0 && m_cbLayerPresets->GetSelection() != idx )
  2070. {
  2071. m_cbLayerPresets->SetSelection( idx );
  2072. m_currentPreset = static_cast<LAYER_PRESET*>( m_cbLayerPresets->GetClientData( idx ) );
  2073. }
  2074. else if( idx < 0 )
  2075. {
  2076. m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 ); // separator
  2077. }
  2078. }
  2079. void APPEARANCE_CONTROLS::onLayerPresetChanged( wxCommandEvent& aEvent )
  2080. {
  2081. int count = m_cbLayerPresets->GetCount();
  2082. int index = m_cbLayerPresets->GetSelection();
  2083. auto resetSelection =
  2084. [&]()
  2085. {
  2086. if( m_currentPreset )
  2087. m_cbLayerPresets->SetStringSelection( m_currentPreset->name );
  2088. else
  2089. m_cbLayerPresets->SetSelection( m_cbLayerPresets->GetCount() - 3 );
  2090. };
  2091. if( index == count - 3 )
  2092. {
  2093. // Separator: reject the selection
  2094. resetSelection();
  2095. return;
  2096. }
  2097. else if( index == count - 2 )
  2098. {
  2099. // Save current state to new preset
  2100. wxString name;
  2101. if( m_lastSelectedUserPreset )
  2102. name = m_lastSelectedUserPreset->name;
  2103. wxTextEntryDialog dlg( wxGetTopLevelParent( this ), _( "Layer preset name:" ),
  2104. _( "Save Layer Preset" ), name );
  2105. if( dlg.ShowModal() != wxID_OK )
  2106. {
  2107. resetSelection();
  2108. return;
  2109. }
  2110. name = dlg.GetValue();
  2111. bool exists = m_layerPresets.count( name );
  2112. if( !exists )
  2113. {
  2114. m_layerPresets[name] = LAYER_PRESET( name, getVisibleLayers(), getVisibleObjects(),
  2115. UNSELECTED_LAYER, m_cbFlipBoard->GetValue() );
  2116. }
  2117. LAYER_PRESET* preset = &m_layerPresets[name];
  2118. if( !exists )
  2119. {
  2120. index = m_cbLayerPresets->Insert( name, index - 1, static_cast<void*>( preset ) );
  2121. }
  2122. else if( preset->readOnly )
  2123. {
  2124. wxMessageBox( _( "Default presets cannot be modified.\nPlease use a different name." ),
  2125. _( "Error" ), wxOK | wxICON_ERROR, wxGetTopLevelParent( this ) );
  2126. resetSelection();
  2127. return;
  2128. }
  2129. else
  2130. {
  2131. // Ask the user if they want to overwrite the existing preset
  2132. if( !IsOK( wxGetTopLevelParent( this ), _( "Overwrite existing preset?" ) ) )
  2133. {
  2134. resetSelection();
  2135. return;
  2136. }
  2137. preset->layers = getVisibleLayers();
  2138. preset->renderLayers = getVisibleObjects();
  2139. preset->flipBoard = m_cbFlipBoard->GetValue();
  2140. index = m_cbLayerPresets->FindString( name );
  2141. m_presetMRU.Remove( name );
  2142. }
  2143. m_currentPreset = preset;
  2144. m_cbLayerPresets->SetSelection( index );
  2145. m_presetMRU.Insert( name, 0 );
  2146. return;
  2147. }
  2148. else if( index == count - 1 )
  2149. {
  2150. // Delete a preset
  2151. wxArrayString headers;
  2152. std::vector<wxArrayString> items;
  2153. headers.Add( _( "Presets" ) );
  2154. for( std::pair<const wxString, LAYER_PRESET>& pair : m_layerPresets )
  2155. {
  2156. if( !pair.second.readOnly )
  2157. {
  2158. wxArrayString item;
  2159. item.Add( pair.first );
  2160. items.emplace_back( item );
  2161. }
  2162. }
  2163. EDA_LIST_DIALOG dlg( m_frame, _( "Delete Preset" ), headers, items );
  2164. dlg.SetListLabel( _( "Select preset:" ) );
  2165. if( dlg.ShowModal() == wxID_OK )
  2166. {
  2167. wxString presetName = dlg.GetTextSelection();
  2168. int idx = m_cbLayerPresets->FindString( presetName );
  2169. if( idx != wxNOT_FOUND )
  2170. {
  2171. m_layerPresets.erase( presetName );
  2172. m_cbLayerPresets->Delete( idx );
  2173. m_currentPreset = nullptr;
  2174. m_presetMRU.Remove( presetName );
  2175. }
  2176. }
  2177. resetSelection();
  2178. return;
  2179. }
  2180. // Store the objects visibility settings if the presedt is not a user preset,
  2181. // to be reused when selecting a new built-in layer preset, even if a previous
  2182. // user preset has changed the object visibility
  2183. if( !m_currentPreset || m_currentPreset->readOnly )
  2184. {
  2185. m_lastBuiltinPreset.renderLayers = getVisibleObjects();
  2186. }
  2187. LAYER_PRESET* preset = static_cast<LAYER_PRESET*>( m_cbLayerPresets->GetClientData( index ) );
  2188. m_currentPreset = preset;
  2189. m_lastSelectedUserPreset = ( !preset || preset->readOnly ) ? nullptr : preset;
  2190. if( preset )
  2191. {
  2192. // Change board layers visibility, but do not change objects visibility
  2193. LAYER_PRESET curr_layers_choice = *preset;
  2194. // For predefined presets that do not manage objects visibility, use
  2195. // the objects visibility settings of the last used predefined preset.
  2196. if( curr_layers_choice.readOnly )
  2197. curr_layers_choice.renderLayers = m_lastBuiltinPreset.renderLayers;
  2198. doApplyLayerPreset( curr_layers_choice );
  2199. }
  2200. if( !m_currentPreset->name.IsEmpty() )
  2201. {
  2202. const wxString translatedName = wxGetTranslation( m_currentPreset->name );
  2203. m_presetMRU.Remove( translatedName );
  2204. m_presetMRU.Insert( translatedName, 0 );
  2205. }
  2206. passOnFocus();
  2207. }
  2208. void APPEARANCE_CONTROLS::doApplyLayerPreset( const LAYER_PRESET& aPreset )
  2209. {
  2210. BOARD* board = m_frame->GetBoard();
  2211. KIGFX::PCB_VIEW* view = m_frame->GetCanvas()->GetView();
  2212. setVisibleLayers( aPreset.layers );
  2213. setVisibleObjects( aPreset.renderLayers );
  2214. // If the preset doesn't have an explicit active layer to restore, we can at least
  2215. // force the active layer to be something in the preset's layer set
  2216. PCB_LAYER_ID activeLayer = UNSELECTED_LAYER;
  2217. if( aPreset.activeLayer != UNSELECTED_LAYER )
  2218. activeLayer = aPreset.activeLayer;
  2219. else if( aPreset.layers.any() && !aPreset.layers.test( m_frame->GetActiveLayer() ) )
  2220. activeLayer = *aPreset.layers.Seq().begin();
  2221. LSET boardLayers = board->GetLayerSet();
  2222. if( activeLayer != UNSELECTED_LAYER && boardLayers.Contains( activeLayer ) )
  2223. m_frame->SetActiveLayer( activeLayer );
  2224. if( !m_isFpEditor )
  2225. m_frame->GetCanvas()->SyncLayersVisibility( board );
  2226. if( aPreset.flipBoard != view->IsMirroredX() )
  2227. {
  2228. view->SetMirror( !view->IsMirroredX(), view->IsMirroredY() );
  2229. view->RecacheAllItems();
  2230. }
  2231. m_frame->GetCanvas()->Refresh();
  2232. syncColorsAndVisibility();
  2233. UpdateDisplayOptions();
  2234. }
  2235. void APPEARANCE_CONTROLS::rebuildViewportsWidget()
  2236. {
  2237. m_viewportsLabel->SetLabel( wxString::Format( _( "Viewports (%s+Tab):" ),
  2238. KeyNameFromKeyCode( VIEWPORT_SWITCH_KEY ) ) );
  2239. m_cbViewports->Clear();
  2240. for( std::pair<const wxString, VIEWPORT>& pair : m_viewports )
  2241. m_cbViewports->Append( pair.first, static_cast<void*>( &pair.second ) );
  2242. m_cbViewports->Append( wxT( "---" ) );
  2243. m_cbViewports->Append( _( "Save viewport..." ) );
  2244. m_cbViewports->Append( _( "Delete viewport..." ) );
  2245. m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 );
  2246. m_lastSelectedViewport = nullptr;
  2247. }
  2248. void APPEARANCE_CONTROLS::updateViewportSelection( const wxString& aName )
  2249. {
  2250. int idx = m_cbViewports->FindString( aName );
  2251. if( idx >= 0 && idx < (int)m_cbViewports->GetCount() - 3 /* separator */ )
  2252. {
  2253. m_cbViewports->SetSelection( idx );
  2254. m_lastSelectedViewport = static_cast<VIEWPORT*>( m_cbViewports->GetClientData( idx ) );
  2255. }
  2256. else if( idx < 0 )
  2257. {
  2258. m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 ); // separator
  2259. m_lastSelectedViewport = nullptr;
  2260. }
  2261. }
  2262. void APPEARANCE_CONTROLS::onViewportChanged( wxCommandEvent& aEvent )
  2263. {
  2264. int count = m_cbViewports->GetCount();
  2265. int index = m_cbViewports->GetSelection();
  2266. if( index >= 0 && index < count - 3 )
  2267. {
  2268. VIEWPORT* viewport = static_cast<VIEWPORT*>( m_cbViewports->GetClientData( index ) );
  2269. wxCHECK( viewport, /* void */ );
  2270. doApplyViewport( *viewport );
  2271. if( !viewport->name.IsEmpty() )
  2272. {
  2273. m_viewportMRU.Remove( viewport->name );
  2274. m_viewportMRU.Insert( viewport->name, 0 );
  2275. }
  2276. }
  2277. else if( index == count - 2 )
  2278. {
  2279. // Save current state to new preset
  2280. wxString name;
  2281. wxTextEntryDialog dlg( wxGetTopLevelParent( this ),
  2282. _( "Viewport name:" ), _( "Save Viewport" ), name );
  2283. if( dlg.ShowModal() != wxID_OK )
  2284. {
  2285. if( m_lastSelectedViewport )
  2286. m_cbViewports->SetStringSelection( m_lastSelectedViewport->name );
  2287. else
  2288. m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 );
  2289. return;
  2290. }
  2291. name = dlg.GetValue();
  2292. bool exists = m_viewports.count( name );
  2293. if( !exists )
  2294. {
  2295. m_viewports[name] = VIEWPORT( name, m_frame->GetCanvas()->GetView()->GetViewport() );
  2296. index = m_cbViewports->Insert( name, index-1, static_cast<void*>( &m_viewports[name] ) );
  2297. }
  2298. else
  2299. {
  2300. m_viewports[name].rect = m_frame->GetCanvas()->GetView()->GetViewport();
  2301. index = m_cbViewports->FindString( name );
  2302. m_viewportMRU.Remove( name );
  2303. }
  2304. m_cbViewports->SetSelection( index );
  2305. m_viewportMRU.Insert( name, 0 );
  2306. return;
  2307. }
  2308. else if( index == count - 1 )
  2309. {
  2310. // Delete an existing viewport
  2311. wxArrayString headers;
  2312. std::vector<wxArrayString> items;
  2313. headers.Add( _( "Viewports" ) );
  2314. for( std::pair<const wxString, VIEWPORT>& pair : m_viewports )
  2315. {
  2316. wxArrayString item;
  2317. item.Add( pair.first );
  2318. items.emplace_back( item );
  2319. }
  2320. EDA_LIST_DIALOG dlg( m_frame, _( "Delete Viewport" ), headers, items );
  2321. dlg.SetListLabel( _( "Select viewport:" ) );
  2322. if( dlg.ShowModal() == wxID_OK )
  2323. {
  2324. wxString viewportName = dlg.GetTextSelection();
  2325. int idx = m_cbViewports->FindString( viewportName );
  2326. if( idx != wxNOT_FOUND )
  2327. {
  2328. m_viewports.erase( viewportName );
  2329. m_cbViewports->Delete( idx );
  2330. m_viewportMRU.Remove( viewportName );
  2331. }
  2332. }
  2333. if( m_lastSelectedViewport )
  2334. m_cbViewports->SetStringSelection( m_lastSelectedViewport->name );
  2335. else
  2336. m_cbViewports->SetSelection( m_cbViewports->GetCount() - 3 );
  2337. return;
  2338. }
  2339. passOnFocus();
  2340. }
  2341. void APPEARANCE_CONTROLS::doApplyViewport( const VIEWPORT& aViewport )
  2342. {
  2343. m_frame->GetCanvas()->GetView()->SetViewport( aViewport.rect );
  2344. m_frame->GetCanvas()->Refresh();
  2345. }
  2346. void APPEARANCE_CONTROLS::OnColorSwatchChanged( wxCommandEvent& aEvent )
  2347. {
  2348. COLOR_SWATCH* swatch = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
  2349. COLOR4D newColor = swatch->GetSwatchColor();
  2350. int layer = swatch->GetId();
  2351. COLOR_SETTINGS* cs = m_frame->GetColorSettings();
  2352. cs->SetColor( layer, newColor );
  2353. m_frame->GetSettingsManager()->SaveColorSettings( cs, "board" );
  2354. m_frame->GetCanvas()->UpdateColors();
  2355. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  2356. view->UpdateLayerColor( layer );
  2357. view->UpdateLayerColor( GetNetnameLayer( layer ) );
  2358. if( IsCopperLayer( layer ) )
  2359. {
  2360. view->UpdateLayerColor( ZONE_LAYER_FOR( layer ) );
  2361. view->UpdateLayerColor( VIA_COPPER_LAYER_FOR( layer ) );
  2362. view->UpdateLayerColor( PAD_COPPER_LAYER_FOR( layer ) );
  2363. view->UpdateLayerColor( CLEARANCE_LAYER_FOR( layer ) );
  2364. }
  2365. // Update the bitmap of the layer box
  2366. if( m_frame->IsType( FRAME_PCB_EDITOR ) )
  2367. static_cast<PCB_EDIT_FRAME*>( m_frame )->ReCreateLayerBox( false );
  2368. m_frame->GetCanvas()->Refresh();
  2369. if( layer == LAYER_PCB_BACKGROUND )
  2370. m_frame->SetDrawBgColor( newColor );
  2371. passOnFocus();
  2372. }
  2373. void APPEARANCE_CONTROLS::onObjectOpacitySlider( int aLayer, float aOpacity )
  2374. {
  2375. PCB_DISPLAY_OPTIONS options = m_frame->GetDisplayOptions();
  2376. switch( aLayer )
  2377. {
  2378. case static_cast<int>( LAYER_TRACKS ): options.m_TrackOpacity = aOpacity; break;
  2379. case static_cast<int>( LAYER_VIAS ): options.m_ViaOpacity = aOpacity; break;
  2380. case static_cast<int>( LAYER_PADS ): options.m_PadOpacity = aOpacity; break;
  2381. case static_cast<int>( LAYER_ZONES ): options.m_ZoneOpacity = aOpacity; break;
  2382. case static_cast<int>( LAYER_DRAW_BITMAPS ): options.m_ImageOpacity = aOpacity; break;
  2383. case static_cast<int>( LAYER_SHAPES ): options.m_FilledShapeOpacity = aOpacity; break;
  2384. default: return;
  2385. }
  2386. m_frame->SetDisplayOptions( options );
  2387. passOnFocus();
  2388. }
  2389. void APPEARANCE_CONTROLS::onNetContextMenu( wxCommandEvent& aEvent )
  2390. {
  2391. wxASSERT( m_netsGrid->GetSelectedRows().size() == 1 );
  2392. int row = m_netsGrid->GetSelectedRows()[0];
  2393. NET_GRID_ENTRY& net = m_netsTable->GetEntry( row );
  2394. m_netsGrid->ClearSelection();
  2395. switch( aEvent.GetId() )
  2396. {
  2397. case ID_SET_NET_COLOR:
  2398. {
  2399. wxGridCellEditor* editor = m_netsGrid->GetCellEditor( row, NET_GRID_TABLE::COL_COLOR );
  2400. editor->BeginEdit( row, NET_GRID_TABLE::COL_COLOR, m_netsGrid );
  2401. break;
  2402. }
  2403. case ID_CLEAR_NET_COLOR:
  2404. m_netsGrid->SetCellValue( row, NET_GRID_TABLE::COL_COLOR, wxS( "rgba(0,0,0,0)" ) );
  2405. break;
  2406. case ID_HIGHLIGHT_NET:
  2407. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::highlightNet, net.code );
  2408. m_frame->GetCanvas()->Refresh();
  2409. break;
  2410. case ID_SELECT_NET:
  2411. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectNet, net.code );
  2412. m_frame->GetCanvas()->Refresh();
  2413. break;
  2414. case ID_DESELECT_NET:
  2415. m_frame->GetToolManager()->RunAction( PCB_ACTIONS::deselectNet, net.code );
  2416. m_frame->GetCanvas()->Refresh();
  2417. break;
  2418. case ID_SHOW_ALL_NETS:
  2419. m_netsTable->ShowAllNets();
  2420. break;
  2421. case ID_HIDE_OTHER_NETS:
  2422. m_netsTable->HideOtherNets( net );
  2423. break;
  2424. default:
  2425. break;
  2426. }
  2427. passOnFocus();
  2428. }
  2429. void APPEARANCE_CONTROLS::onNetclassVisibilityChanged( wxCommandEvent& aEvent )
  2430. {
  2431. wxString className = netclassNameFromEvent( aEvent );
  2432. bool show = aEvent.GetInt();
  2433. showNetclass( className, show );
  2434. passOnFocus();
  2435. }
  2436. void APPEARANCE_CONTROLS::showNetclass( const wxString& aClassName, bool aShow )
  2437. {
  2438. m_togglingNetclassRatsnestVisibility = true;
  2439. for( NETINFO_ITEM* net : m_frame->GetBoard()->GetNetInfo() )
  2440. {
  2441. if( net->GetNetClass()->ContainsNetclassWithName( aClassName ) )
  2442. {
  2443. m_frame->GetToolManager()->RunAction( aShow ? PCB_ACTIONS::showNetInRatsnest
  2444. : PCB_ACTIONS::hideNetInRatsnest,
  2445. net->GetNetCode() );
  2446. int row = m_netsTable->GetRowByNetcode( net->GetNetCode() );
  2447. if( row >= 0 )
  2448. m_netsTable->SetValueAsBool( row, NET_GRID_TABLE::COL_VISIBILITY, aShow );
  2449. }
  2450. }
  2451. PROJECT_LOCAL_SETTINGS& localSettings = m_frame->Prj().GetLocalSettings();
  2452. if( !aShow )
  2453. localSettings.m_HiddenNetclasses.insert( aClassName );
  2454. else
  2455. localSettings.m_HiddenNetclasses.erase( aClassName );
  2456. m_netsGrid->ForceRefresh();
  2457. m_frame->GetCanvas()->RedrawRatsnest();
  2458. m_frame->GetCanvas()->Refresh();
  2459. m_togglingNetclassRatsnestVisibility = false;
  2460. }
  2461. void APPEARANCE_CONTROLS::onNetclassColorChanged( wxCommandEvent& aEvent )
  2462. {
  2463. COLOR_SWATCH* swatch = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
  2464. wxString netclassName = netclassNameFromEvent( aEvent );
  2465. BOARD* board = m_frame->GetBoard();
  2466. std::shared_ptr<NET_SETTINGS>& netSettings = board->GetDesignSettings().m_NetSettings;
  2467. std::shared_ptr<NETCLASS> nc = netSettings->GetNetClassByName( netclassName );
  2468. nc->SetPcbColor( swatch->GetSwatchColor() );
  2469. netSettings->RecomputeEffectiveNetclasses();
  2470. m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
  2471. m_frame->GetCanvas()->RedrawRatsnest();
  2472. m_frame->GetCanvas()->Refresh();
  2473. }
  2474. wxString APPEARANCE_CONTROLS::netclassNameFromEvent( wxEvent& aEvent )
  2475. {
  2476. COLOR_SWATCH* s = static_cast<COLOR_SWATCH*>( aEvent.GetEventObject() );
  2477. int classId = s->GetId();
  2478. wxASSERT( m_netclassIdMap.count( classId ) );
  2479. return m_netclassIdMap.at( classId );
  2480. }
  2481. void APPEARANCE_CONTROLS::onNetColorMode( wxCommandEvent& aEvent )
  2482. {
  2483. PCB_DISPLAY_OPTIONS options = m_frame->GetDisplayOptions();
  2484. if( m_rbNetColorAll->GetValue() )
  2485. options.m_NetColorMode = NET_COLOR_MODE::ALL;
  2486. else if( m_rbNetColorRatsnest->GetValue() )
  2487. options.m_NetColorMode = NET_COLOR_MODE::RATSNEST;
  2488. else
  2489. options.m_NetColorMode = NET_COLOR_MODE::OFF;
  2490. m_frame->SetDisplayOptions( options );
  2491. m_frame->GetCanvas()->GetView()->UpdateAllLayersColor();
  2492. passOnFocus();
  2493. }
  2494. void APPEARANCE_CONTROLS::onRatsnestMode( wxCommandEvent& aEvent )
  2495. {
  2496. PCBNEW_SETTINGS* cfg = m_frame->GetPcbNewSettings();
  2497. if( m_rbRatsnestAllLayers->GetValue() )
  2498. {
  2499. cfg->m_Display.m_ShowGlobalRatsnest = true;
  2500. cfg->m_Display.m_RatsnestMode = RATSNEST_MODE::ALL;
  2501. }
  2502. else if( m_rbRatsnestVisLayers->GetValue() )
  2503. {
  2504. cfg->m_Display.m_ShowGlobalRatsnest = true;
  2505. cfg->m_Display.m_RatsnestMode = RATSNEST_MODE::VISIBLE;
  2506. }
  2507. else
  2508. {
  2509. cfg->m_Display.m_ShowGlobalRatsnest = false;
  2510. }
  2511. if( PCB_EDIT_FRAME* editframe = dynamic_cast<PCB_EDIT_FRAME*>( m_frame ) )
  2512. {
  2513. editframe->SetElementVisibility( LAYER_RATSNEST, cfg->m_Display.m_ShowGlobalRatsnest );
  2514. editframe->OnDisplayOptionsChanged();
  2515. editframe->GetCanvas()->RedrawRatsnest();
  2516. editframe->GetCanvas()->Refresh();
  2517. }
  2518. passOnFocus();
  2519. }
  2520. void APPEARANCE_CONTROLS::onNetclassContextMenu( wxCommandEvent& aEvent )
  2521. {
  2522. KIGFX::VIEW* view = m_frame->GetCanvas()->GetView();
  2523. KIGFX::PCB_RENDER_SETTINGS* rs =
  2524. static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
  2525. BOARD* board = m_frame->GetBoard();
  2526. std::shared_ptr<NET_SETTINGS>& netSettings = board->GetDesignSettings().m_NetSettings;
  2527. APPEARANCE_SETTING* setting = nullptr;
  2528. auto it = m_netclassSettingsMap.find( m_contextMenuNetclass );
  2529. if( it != m_netclassSettingsMap.end() )
  2530. setting = it->second;
  2531. auto runOnNetsOfClass =
  2532. [&]( const wxString& netClassName, std::function<void( NETINFO_ITEM* )> aFunction )
  2533. {
  2534. for( NETINFO_ITEM* net : board->GetNetInfo() )
  2535. {
  2536. if( net->GetNetClass()->ContainsNetclassWithName( netClassName ) )
  2537. aFunction( net );
  2538. }
  2539. };
  2540. switch( aEvent.GetId() )
  2541. {
  2542. case ID_SET_NET_COLOR:
  2543. {
  2544. if( setting )
  2545. {
  2546. setting->ctl_color->GetNewSwatchColor();
  2547. COLOR4D color = setting->ctl_color->GetSwatchColor();
  2548. if( color != COLOR4D::UNSPECIFIED )
  2549. {
  2550. netSettings->GetNetClassByName( m_contextMenuNetclass )->SetPcbColor( color );
  2551. netSettings->RecomputeEffectiveNetclasses();
  2552. }
  2553. view->UpdateAllLayersColor();
  2554. }
  2555. break;
  2556. }
  2557. case ID_CLEAR_NET_COLOR:
  2558. {
  2559. if( setting )
  2560. {
  2561. setting->ctl_color->SetSwatchColor( COLOR4D( 0, 0, 0, 0 ), true );
  2562. netSettings->GetNetClassByName( m_contextMenuNetclass )
  2563. ->SetPcbColor( COLOR4D::UNSPECIFIED );
  2564. netSettings->RecomputeEffectiveNetclasses();
  2565. view->UpdateAllLayersColor();
  2566. }
  2567. break;
  2568. }
  2569. case ID_USE_SCHEMATIC_NET_COLOR:
  2570. {
  2571. if( setting )
  2572. {
  2573. std::shared_ptr<NETCLASS> nc =
  2574. netSettings->GetNetClassByName( m_contextMenuNetclass );
  2575. const KIGFX::COLOR4D ncColor = nc->GetSchematicColor();
  2576. setting->ctl_color->SetSwatchColor( ncColor, true );
  2577. netSettings->GetNetClassByName( m_contextMenuNetclass )->SetPcbColor( ncColor );
  2578. netSettings->RecomputeEffectiveNetclasses();
  2579. view->UpdateAllLayersColor();
  2580. }
  2581. break;
  2582. }
  2583. case ID_HIGHLIGHT_NET:
  2584. {
  2585. if( !m_contextMenuNetclass.IsEmpty() )
  2586. {
  2587. runOnNetsOfClass( m_contextMenuNetclass,
  2588. [&]( NETINFO_ITEM* aItem )
  2589. {
  2590. static bool first = true;
  2591. int code = aItem->GetNetCode();
  2592. if( first )
  2593. {
  2594. board->SetHighLightNet( code );
  2595. rs->SetHighlight( true, code );
  2596. first = false;
  2597. }
  2598. else
  2599. {
  2600. board->SetHighLightNet( code, true );
  2601. rs->SetHighlight( true, code, true );
  2602. }
  2603. } );
  2604. view->UpdateAllLayersColor();
  2605. board->HighLightON();
  2606. }
  2607. break;
  2608. }
  2609. case ID_SELECT_NET:
  2610. case ID_DESELECT_NET:
  2611. {
  2612. if( !m_contextMenuNetclass.IsEmpty() )
  2613. {
  2614. TOOL_MANAGER* toolMgr = m_frame->GetToolManager();
  2615. TOOL_ACTION& action = aEvent.GetId() == ID_SELECT_NET ? PCB_ACTIONS::selectNet
  2616. : PCB_ACTIONS::deselectNet;
  2617. runOnNetsOfClass( m_contextMenuNetclass,
  2618. [&]( NETINFO_ITEM* aItem )
  2619. {
  2620. toolMgr->RunAction( action, aItem->GetNetCode() );
  2621. } );
  2622. }
  2623. break;
  2624. }
  2625. case ID_SHOW_ALL_NETS:
  2626. {
  2627. showNetclass( NETCLASS::Default );
  2628. wxASSERT( m_netclassSettingsMap.count( NETCLASS::Default ) );
  2629. m_netclassSettingsMap.at( NETCLASS::Default )->ctl_visibility->SetValue( true );
  2630. for( const auto& [name, netclass] : netSettings->GetNetclasses() )
  2631. {
  2632. showNetclass( name );
  2633. if( m_netclassSettingsMap.count( name ) )
  2634. m_netclassSettingsMap.at( name )->ctl_visibility->SetValue( true );
  2635. }
  2636. break;
  2637. }
  2638. case ID_HIDE_OTHER_NETS:
  2639. {
  2640. bool showDefault = m_contextMenuNetclass == NETCLASS::Default;
  2641. showNetclass( NETCLASS::Default, showDefault );
  2642. wxASSERT( m_netclassSettingsMap.count( NETCLASS::Default ) );
  2643. m_netclassSettingsMap.at( NETCLASS::Default )->ctl_visibility->SetValue( showDefault );
  2644. for( const auto& [name, netclass] : netSettings->GetNetclasses() )
  2645. {
  2646. bool show = ( name == m_contextMenuNetclass );
  2647. showNetclass( name, show );
  2648. if( m_netclassSettingsMap.count( name ) )
  2649. m_netclassSettingsMap.at( name )->ctl_visibility->SetValue( show );
  2650. }
  2651. break;
  2652. }
  2653. default:
  2654. break;
  2655. }
  2656. m_frame->GetCanvas()->RedrawRatsnest();
  2657. m_frame->GetCanvas()->Refresh();
  2658. m_contextMenuNetclass.clear();
  2659. }
  2660. void APPEARANCE_CONTROLS::passOnFocus()
  2661. {
  2662. m_focusOwner->SetFocus();
  2663. }
  2664. void APPEARANCE_CONTROLS::onReadOnlySwatch()
  2665. {
  2666. WX_INFOBAR* infobar = m_frame->GetInfoBar();
  2667. wxHyperlinkCtrl* button = new wxHyperlinkCtrl( infobar, wxID_ANY, _( "Open Preferences" ),
  2668. wxEmptyString );
  2669. button->Bind( wxEVT_COMMAND_HYPERLINK, std::function<void( wxHyperlinkEvent& aEvent )>(
  2670. [&]( wxHyperlinkEvent& aEvent )
  2671. {
  2672. m_frame->ShowPreferences( wxEmptyString, wxEmptyString );
  2673. } ) );
  2674. infobar->RemoveAllButtons();
  2675. infobar->AddButton( button );
  2676. infobar->AddCloseButton();
  2677. infobar->ShowMessageFor( _( "The current color theme is read-only. Create a new theme in "
  2678. "Preferences to enable color editing." ),
  2679. 10000, wxICON_INFORMATION );
  2680. }
  2681. void APPEARANCE_CONTROLS::RefreshCollapsiblePanes()
  2682. {
  2683. m_paneLayerDisplayOptions->Refresh();
  2684. }
  2685. bool APPEARANCE_CONTROLS::IsTogglingNetclassRatsnestVisibility()
  2686. {
  2687. return m_togglingNetclassRatsnestVisibility;
  2688. }