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