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