1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2018-2021 KiCad Developers, see change_log.txt for contributors.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, you may find one here:
18  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19  * or you may search the http://www.gnu.org website for the version 2 license,
20  * or you may write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22  */
23 
24 
25 #include <base_units.h>
26 #include <pcb_edit_frame.h>
27 #include <board_design_settings.h>
28 #include <bitmaps.h>
29 #include <widgets/wx_grid.h>
30 #include <wx/treebook.h>
31 #include <grid_tricks.h>
32 
33 #include <panel_setup_tracks_and_vias.h>
34 
35 
36 enum TRACK_VAR_GRID_COLUMNS
37 {
38     TR_WIDTH_COL = 0
39 };
40 
41 enum VIA_VAR_GRID_COLUMNS
42 {
43     VIA_SIZE_COL = 0,
44     VIA_DRILL_COL
45 };
46 
47 enum DIFF_VAR_GRID_COLUMNS
48 {
49     DP_WIDTH_COL = 0,
50     DP_GAP_COL,
51     DP_VIA_GAP_COL
52 };
53 
54 
PANEL_SETUP_TRACKS_AND_VIAS(PAGED_DIALOG * aParent,PCB_EDIT_FRAME * aFrame,PANEL_SETUP_CONSTRAINTS * aConstraintsPanel)55 PANEL_SETUP_TRACKS_AND_VIAS::PANEL_SETUP_TRACKS_AND_VIAS( PAGED_DIALOG* aParent,
56                                                           PCB_EDIT_FRAME* aFrame,
57                                                           PANEL_SETUP_CONSTRAINTS* aConstraintsPanel ) :
58     PANEL_SETUP_TRACKS_AND_VIAS_BASE( aParent->GetTreebook() )
59 {
60     m_Parent = aParent;
61     m_Frame = aFrame;
62     m_Pcb = m_Frame->GetBoard();
63     m_BrdSettings = &m_Pcb->GetDesignSettings();
64     m_ConstraintsPanel = aConstraintsPanel;
65 
66     m_trackWidthsAddButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
67     m_trackWidthsRemoveButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
68     m_viaSizesAddButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
69     m_viaSizesRemoveButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
70     m_diffPairsAddButton->SetBitmap( KiBitmap( BITMAPS::small_plus ) );
71     m_diffPairsRemoveButton->SetBitmap( KiBitmap( BITMAPS::small_trash ) );
72 
73     // Membership combobox editors require a bit more room, so increase the row size of
74     // all our grids for consistency
75     m_trackWidthsGrid->SetDefaultRowSize( m_trackWidthsGrid->GetDefaultRowSize() + 4 );
76     m_viaSizesGrid->SetDefaultRowSize(    m_viaSizesGrid->GetDefaultRowSize()    + 4 );
77     m_diffPairsGrid->SetDefaultRowSize(   m_diffPairsGrid->GetDefaultRowSize()   + 4 );
78 
79     m_trackWidthsGrid->PushEventHandler( new GRID_TRICKS( m_trackWidthsGrid ) );
80     m_viaSizesGrid->PushEventHandler( new GRID_TRICKS( m_viaSizesGrid ) );
81     m_diffPairsGrid->PushEventHandler( new GRID_TRICKS( m_diffPairsGrid ) );
82 
83     m_trackWidthsGrid->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
84     m_viaSizesGrid->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
85     m_diffPairsGrid->SetSelectionMode( wxGrid::wxGridSelectionModes::wxGridSelectRows );
86 
87     // Ensure width of columns is enough to enter any reasonable value
88     WX_GRID* grid_list[] = { m_trackWidthsGrid, m_viaSizesGrid, m_diffPairsGrid, nullptr };
89     int min_linesize = m_trackWidthsGrid->GetTextExtent( "000.000000 mm " ).x;
90 
91     for( int ii = 0; grid_list[ii]; ii++ )
92     {
93         WX_GRID* curr_grid = grid_list[ii];
94 
95         for( int col = 0; col < curr_grid->GetNumberCols(); col++ )
96         {
97             int min_w = curr_grid->GetVisibleWidth( col, true, true, true );
98             int best_w = std::max( min_linesize, min_w );
99             curr_grid->SetColMinimalWidth( col, best_w );
100             curr_grid->SetColSize( col,best_w );
101         }
102     }
103 }
104 
~PANEL_SETUP_TRACKS_AND_VIAS()105 PANEL_SETUP_TRACKS_AND_VIAS::~PANEL_SETUP_TRACKS_AND_VIAS()
106 {
107     // Delete the GRID_TRICKS.
108     m_trackWidthsGrid->PopEventHandler( true );
109     m_viaSizesGrid->PopEventHandler( true );
110     m_diffPairsGrid->PopEventHandler( true );
111 }
112 
TransferDataToWindow()113 bool PANEL_SETUP_TRACKS_AND_VIAS::TransferDataToWindow()
114 {
115     m_trackWidthsGrid->ClearRows();
116     m_viaSizesGrid->ClearRows();
117     m_diffPairsGrid->ClearRows();
118 
119     // Skip the first item, which is the current netclass value
120     for( unsigned ii = 1; ii < m_BrdSettings->m_TrackWidthList.size(); ii++ )
121     {
122         AppendTrackWidth( m_BrdSettings->m_TrackWidthList[ii] );
123     }
124 
125     // Skip the first item, which is the current netclass value
126     for( unsigned ii = 1; ii < m_BrdSettings->m_ViasDimensionsList.size(); ii++ )
127     {
128         AppendViaSize( m_BrdSettings->m_ViasDimensionsList[ii].m_Diameter,
129                        m_BrdSettings->m_ViasDimensionsList[ii].m_Drill );
130     }
131 
132     // Skip the first item, which is the current netclass value
133     for( unsigned ii = 1; ii < m_BrdSettings->m_DiffPairDimensionsList.size(); ii++ )
134     {
135         AppendDiffPairs( m_BrdSettings->m_DiffPairDimensionsList[ii].m_Width,
136                          m_BrdSettings->m_DiffPairDimensionsList[ii].m_Gap,
137                          m_BrdSettings->m_DiffPairDimensionsList[ii].m_ViaGap );
138     }
139 
140     return true;
141 }
142 
143 
TransferDataFromWindow()144 bool PANEL_SETUP_TRACKS_AND_VIAS::TransferDataFromWindow()
145 {
146     if( !m_trackWidthsGrid->CommitPendingChanges()
147         || !m_viaSizesGrid->CommitPendingChanges()
148         || !m_diffPairsGrid->CommitPendingChanges() )
149     {
150         return false;
151     }
152 
153     wxString                         msg;
154     std::vector<int>                 trackWidths;
155     std::vector<VIA_DIMENSION>       vias;
156     std::vector<DIFF_PAIR_DIMENSION> diffPairs;
157 
158     if( !m_trackWidthsGrid->CommitPendingChanges()
159             || !m_viaSizesGrid->CommitPendingChanges()
160             || !m_diffPairsGrid->CommitPendingChanges() )
161     {
162         return false;
163     }
164 
165     // Test ONLY for malformed data.  Design rules and constraints are the business of DRC.
166 
167     for( int row = 0; row < m_trackWidthsGrid->GetNumberRows();  ++row )
168     {
169         msg = m_trackWidthsGrid->GetCellValue( row, TR_WIDTH_COL );
170 
171         if( !msg.IsEmpty() )
172             trackWidths.push_back( ValueFromString( m_Frame->GetUserUnits(), msg ) );
173     }
174 
175     for( int row = 0; row < m_viaSizesGrid->GetNumberRows();  ++row )
176     {
177         msg = m_viaSizesGrid->GetCellValue( row, VIA_SIZE_COL );
178 
179         if( !msg.IsEmpty() )
180         {
181             VIA_DIMENSION via_dim;
182             via_dim.m_Diameter = ValueFromString( m_Frame->GetUserUnits(), msg );
183 
184             msg = m_viaSizesGrid->GetCellValue( row, VIA_DRILL_COL );
185 
186             if( !msg.IsEmpty() )
187                 via_dim.m_Drill = ValueFromString( m_Frame->GetUserUnits(), msg );
188 
189             vias.push_back( via_dim );
190         }
191     }
192 
193     for( int row = 0; row < m_diffPairsGrid->GetNumberRows();  ++row )
194     {
195         msg = m_diffPairsGrid->GetCellValue( row, DP_WIDTH_COL );
196 
197         if( !msg.IsEmpty() )
198         {
199             DIFF_PAIR_DIMENSION diffPair_dim;
200             diffPair_dim.m_Width = ValueFromString( m_Frame->GetUserUnits(), msg );
201 
202             msg = m_diffPairsGrid->GetCellValue( row, DP_GAP_COL );
203             diffPair_dim.m_Gap = ValueFromString( m_Frame->GetUserUnits(), msg );
204 
205             msg = m_diffPairsGrid->GetCellValue( row, DP_VIA_GAP_COL );
206 
207             if( !msg.IsEmpty() )
208                 diffPair_dim.m_ViaGap = ValueFromString( m_Frame->GetUserUnits(), msg );
209 
210             diffPairs.push_back( diffPair_dim );
211         }
212     }
213 
214     // Sort lists by increasing value
215     sort( trackWidths.begin(), trackWidths.end() );
216     sort( vias.begin(), vias.end() );
217     sort( diffPairs.begin(), diffPairs.end() );
218 
219     // These are all stored in project file, not board, so no need for OnModify()
220 
221     trackWidths.insert( trackWidths.begin(), 0 );         // dummy value for "use netclass"
222     m_BrdSettings->m_TrackWidthList = trackWidths;
223 
224     vias.insert( vias.begin(), { 0, 0 } );                // dummy value for "use netclass"
225     m_BrdSettings->m_ViasDimensionsList = vias;
226 
227     diffPairs.insert( diffPairs.begin(), { 0, 0, 0 } );   // dummy value for "use netclass"
228     m_BrdSettings->m_DiffPairDimensionsList = diffPairs;
229 
230     return true;
231 }
232 
233 
Validate()234 bool PANEL_SETUP_TRACKS_AND_VIAS::Validate()
235 {
236     if( !m_trackWidthsGrid->CommitPendingChanges()
237             || !m_viaSizesGrid->CommitPendingChanges()
238             || !m_diffPairsGrid->CommitPendingChanges() )
239     {
240         return false;
241     }
242 
243     wxString msg;
244 
245     // Test vias
246     for( int row = 0; row < m_viaSizesGrid->GetNumberRows();  ++row )
247     {
248         wxString viaDia = m_viaSizesGrid->GetCellValue( row, VIA_SIZE_COL );
249         wxString viaDrill = m_viaSizesGrid->GetCellValue( row, VIA_DRILL_COL );
250 
251         if( !viaDia.IsEmpty() && viaDrill.IsEmpty() )
252         {
253             msg = _( "No via hole size defined." );
254             m_Parent->SetError( msg, this, m_viaSizesGrid, row, VIA_DRILL_COL );
255             return false;
256         }
257 
258     }
259 
260     // Test diff pairs
261     for( int row = 0; row < m_diffPairsGrid->GetNumberRows();  ++row )
262     {
263         wxString dpWidth = m_diffPairsGrid->GetCellValue( row, 0 );
264         wxString dpGap = m_diffPairsGrid->GetCellValue( row, 1 );
265 
266         if( !dpWidth.IsEmpty() && dpGap.IsEmpty() )
267         {
268             msg = _( "No differential pair gap defined." );
269             m_Parent->SetError( msg, this, m_diffPairsGrid, row, 1 );
270             return false;
271         }
272     }
273 
274     return true;
275 }
276 
277 
AppendTrackWidth(const int aWidth)278 void PANEL_SETUP_TRACKS_AND_VIAS::AppendTrackWidth( const int aWidth )
279 {
280     int i = m_trackWidthsGrid->GetNumberRows();
281 
282     m_trackWidthsGrid->AppendRows( 1 );
283 
284     wxString val = StringFromValue( m_Frame->GetUserUnits(), aWidth );
285     m_trackWidthsGrid->SetCellValue( i, TR_WIDTH_COL, val );
286 }
287 
288 
AppendViaSize(const int aSize,const int aDrill)289 void PANEL_SETUP_TRACKS_AND_VIAS::AppendViaSize( const int aSize, const int aDrill )
290 {
291     int i = m_viaSizesGrid->GetNumberRows();
292 
293     m_viaSizesGrid->AppendRows( 1 );
294 
295     wxString val = StringFromValue( m_Frame->GetUserUnits(), aSize );
296     m_viaSizesGrid->SetCellValue( i, VIA_SIZE_COL, val );
297 
298     if( aDrill > 0 )
299     {
300         val = StringFromValue( m_Frame->GetUserUnits(), aDrill );
301         m_viaSizesGrid->SetCellValue( i, VIA_DRILL_COL, val );
302     }
303 }
304 
305 
AppendDiffPairs(const int aWidth,const int aGap,const int aViaGap)306 void PANEL_SETUP_TRACKS_AND_VIAS::AppendDiffPairs( const int aWidth, const int aGap,
307                                                    const int aViaGap )
308 {
309     int i = m_diffPairsGrid->GetNumberRows();
310 
311     m_diffPairsGrid->AppendRows( 1 );
312 
313     wxString val = StringFromValue( m_Frame->GetUserUnits(), aWidth );
314     m_diffPairsGrid->SetCellValue( i, DP_WIDTH_COL, val );
315 
316     if( aGap > 0 )
317     {
318         val = StringFromValue( m_Frame->GetUserUnits(), aGap );
319         m_diffPairsGrid->SetCellValue( i, DP_GAP_COL, val );
320     }
321 
322     if( aViaGap > 0 )
323     {
324         val = StringFromValue( m_Frame->GetUserUnits(), aViaGap );
325         m_diffPairsGrid->SetCellValue( i, DP_VIA_GAP_COL, val );
326     }
327 }
328 
OnAddTrackWidthsClick(wxCommandEvent & aEvent)329 void PANEL_SETUP_TRACKS_AND_VIAS::OnAddTrackWidthsClick( wxCommandEvent& aEvent )
330 {
331     AppendTrackWidth( 0 );
332 
333     m_trackWidthsGrid->MakeCellVisible( m_trackWidthsGrid->GetNumberRows() - 1, TR_WIDTH_COL );
334     m_trackWidthsGrid->SetGridCursor( m_trackWidthsGrid->GetNumberRows() - 1, TR_WIDTH_COL );
335 
336     m_trackWidthsGrid->EnableCellEditControl( true );
337     m_trackWidthsGrid->ShowCellEditControl();
338 }
339 
340 
OnRemoveTrackWidthsClick(wxCommandEvent & event)341 void PANEL_SETUP_TRACKS_AND_VIAS::OnRemoveTrackWidthsClick( wxCommandEvent& event )
342 {
343     int curRow = m_trackWidthsGrid->GetGridCursorRow();
344 
345     if( curRow < 0 || m_trackWidthsGrid->GetNumberRows() <= curRow )
346         return;
347 
348     m_trackWidthsGrid->DeleteRows( curRow, 1 );
349 
350     curRow = std::max( 0, curRow - 1 );
351     m_trackWidthsGrid->MakeCellVisible( curRow, m_trackWidthsGrid->GetGridCursorCol() );
352     m_trackWidthsGrid->SetGridCursor( curRow, m_trackWidthsGrid->GetGridCursorCol() );
353 }
354 
355 
OnAddViaSizesClick(wxCommandEvent & event)356 void PANEL_SETUP_TRACKS_AND_VIAS::OnAddViaSizesClick( wxCommandEvent& event )
357 {
358     AppendViaSize( 0, 0 );
359 
360     m_viaSizesGrid->MakeCellVisible( m_viaSizesGrid->GetNumberRows() - 1, VIA_SIZE_COL );
361     m_viaSizesGrid->SetGridCursor( m_viaSizesGrid->GetNumberRows() - 1, VIA_SIZE_COL );
362 
363     m_viaSizesGrid->EnableCellEditControl( true );
364     m_viaSizesGrid->ShowCellEditControl();
365 }
366 
367 
OnRemoveViaSizesClick(wxCommandEvent & event)368 void PANEL_SETUP_TRACKS_AND_VIAS::OnRemoveViaSizesClick( wxCommandEvent& event )
369 {
370     int curRow = m_viaSizesGrid->GetGridCursorRow();
371 
372     if( curRow < 0 || m_viaSizesGrid->GetNumberRows() <= curRow )
373         return;
374 
375     m_viaSizesGrid->DeleteRows( curRow, 1 );
376 
377     curRow = std::max( 0, curRow - 1 );
378     m_viaSizesGrid->MakeCellVisible( curRow, m_viaSizesGrid->GetGridCursorCol() );
379     m_viaSizesGrid->SetGridCursor( curRow, m_viaSizesGrid->GetGridCursorCol() );
380 }
381 
382 
OnAddDiffPairsClick(wxCommandEvent & event)383 void PANEL_SETUP_TRACKS_AND_VIAS::OnAddDiffPairsClick( wxCommandEvent& event )
384 {
385     AppendDiffPairs( 0, 0, 0 );
386 
387     m_diffPairsGrid->MakeCellVisible( m_diffPairsGrid->GetNumberRows() - 1, DP_WIDTH_COL );
388     m_diffPairsGrid->SetGridCursor( m_diffPairsGrid->GetNumberRows() - 1, DP_WIDTH_COL );
389 
390     m_diffPairsGrid->EnableCellEditControl( true );
391     m_diffPairsGrid->ShowCellEditControl();
392 }
393 
394 
OnRemoveDiffPairsClick(wxCommandEvent & event)395 void PANEL_SETUP_TRACKS_AND_VIAS::OnRemoveDiffPairsClick( wxCommandEvent& event )
396 {
397     int curRow = m_diffPairsGrid->GetGridCursorRow();
398 
399     if( curRow < 0 || m_diffPairsGrid->GetNumberRows() <= curRow )
400         return;
401 
402     m_diffPairsGrid->DeleteRows( curRow, 1 );
403 
404     curRow = std::max( 0, curRow - 1 );
405     m_diffPairsGrid->MakeCellVisible( curRow, m_diffPairsGrid->GetGridCursorCol() );
406     m_diffPairsGrid->SetGridCursor( curRow, m_diffPairsGrid->GetGridCursorCol() );
407 }
408 
409 
ImportSettingsFrom(BOARD * aBoard)410 void PANEL_SETUP_TRACKS_AND_VIAS::ImportSettingsFrom( BOARD* aBoard )
411 {
412     m_trackWidthsGrid->CommitPendingChanges( true );
413     m_viaSizesGrid->CommitPendingChanges( true );
414     m_diffPairsGrid->CommitPendingChanges( true );
415 
416     // Note: do not change the board, as we need to get the current nets from it for
417     // netclass memberships.  All the netclass definitions and dimension lists are in
418     // the BOARD_DESIGN_SETTINGS.
419 
420     BOARD_DESIGN_SETTINGS* savedSettings = m_BrdSettings;
421 
422     m_BrdSettings = &aBoard->GetDesignSettings();
423     TransferDataToWindow();
424 
425     m_BrdSettings = savedSettings;
426 }
427 
428