KiCad PCB EDA Suite
Loading...
Searching...
No Matches
dialog_drill_groups.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.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, see <https://www.gnu.org/licenses/>.
18 */
19
20#include "dialog_drill_groups.h"
21
22#include <algorithm>
23
24#include <board.h>
25#include <board_commit.h>
27#include <confirm.h>
30#include <pad.h>
31#include <pcb_edit_frame.h>
32#include <pcb_track.h>
33#include <widgets/wx_grid.h>
34
35#include <wx/grid.h>
36
37
38namespace
39{
40
41enum GROUP_GRID_COL
42{
43 COL_SYMBOL = 0,
44 COL_SIZE,
45 COL_PLATING,
46 COL_SPAN,
47 COL_OPS,
48 COL_SITES
49};
50
51
52wxString markText( const DRILL_SYMBOL_ASSIGNMENT& aSymbol, int aDiameter )
53{
54 switch( aSymbol.m_MarkMode )
55 {
56 case DRILL_MARK_MODE::LETTER: return aSymbol.m_Letter;
57 case DRILL_MARK_MODE::SIZE_TEXT: return wxString::Format( wxT( "%.2f" ),
58 pcbIUScale.IUTomm( aDiameter ) );
59 case DRILL_MARK_MODE::SHAPE: break;
60 }
61
62 return wxString::Format( _( "Shape %d" ), aSymbol.m_ShapeIndex + 1 );
63}
64
65} // namespace
66
67
70 m_frame( aFrame ),
71 m_loading( false ),
72 m_detailRow( -1 ),
73 m_reloadPending( false )
74{
75 m_groupGrid->SetColLabelValue( COL_SYMBOL, _( "Symbol" ) );
76 m_groupGrid->SetColLabelValue( COL_SIZE, _( "Size" ) );
77 m_groupGrid->SetColLabelValue( COL_PLATING, _( "Plated" ) );
78 m_groupGrid->SetColLabelValue( COL_SPAN, _( "From / To" ) );
79 m_groupGrid->SetColLabelValue( COL_OPS, _( "Ops" ) );
80 m_groupGrid->SetColLabelValue( COL_SITES, _( "Sites" ) );
81
82 m_groupGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
83 m_groupGrid->EnableEditing( false );
84
85 m_groupGrid->Bind( wxEVT_GRID_SELECT_CELL, &DIALOG_DRILL_GROUPS::onGridSelect, this );
86
87 // Modeless, so closing has to actually destroy. The default would only hide and the
88 // owner's weak reference would keep pointing at a live but invisible dialog
89 Bind( wxEVT_CLOSE_WINDOW,
90 [this]( wxCloseEvent& aEvent )
91 {
93 Destroy();
94 } );
95
96 m_frame->GetBoard()->AddListener( this );
97
98 Reload();
100}
101
102
104{
105 if( m_frame && m_frame->GetBoard() )
106 m_frame->GetBoard()->RemoveListener( this );
107}
108
109
111{
112 if( m_reloadPending )
113 return;
114
115 // Deferred, because the notification can arrive part way through a commit and the board
116 // is not necessarily coherent yet
117 m_reloadPending = true;
118
119 CallAfter(
120 [this]()
121 {
122 m_reloadPending = false;
123 Reload();
124 } );
125}
126
127
129{
130 // Whatever is in the detail pane belongs to the row we are about to discard
132
133 BOARD* board = m_frame->GetBoard();
135
136 m_profileName->SetLabel( profile.GetName().IsEmpty() ? _( "Default" ) : profile.GetName() );
137 m_freezeAssignments->SetValue( profile.GetFreezeAssignments() );
138
139 DRILL_CHART_MODEL model( profile );
140 model.Build( *board, EnumerateDrillSpans( *board ) );
141
142 m_groups = model.Groups();
143
144 // Symbols come from the resolver, not the profile, so a board where no chart has ever
145 // been placed still shows the marks its map would draw
146 const std::shared_ptr<const DRILL_SYMBOL_CACHE> cache = board->DrillSymbolCache();
147
149 {
150 const auto it = cache->m_ByGroup.find( group.m_SymbolKey );
151
152 if( it != cache->m_ByGroup.end() )
153 group.m_Symbol = it->second;
154 }
155
156 if( m_groupGrid->GetNumberRows() )
157 m_groupGrid->DeleteRows( 0, m_groupGrid->GetNumberRows() );
158
159 m_groupGrid->AppendRows( static_cast<int>( m_groups.size() ) );
160
161 for( size_t row = 0; row < m_groups.size(); ++row )
162 {
163 const DRILL_CHART_GROUP& group = m_groups[row];
164 const int r = static_cast<int>( row );
165
166 m_groupGrid->SetCellValue( r, COL_SYMBOL, markText( group.m_Symbol, group.m_Diameter ) );
167
168 if( group.m_IsSlot )
169 {
170 m_groupGrid->SetCellValue( r, COL_SIZE,
171 wxString::Format( wxT( "%.3f x %.3f" ),
172 pcbIUScale.IUTomm( group.m_SizeXY.x ),
173 pcbIUScale.IUTomm( group.m_SizeXY.y ) ) );
174 }
175 else
176 {
177 m_groupGrid->SetCellValue( r, COL_SIZE,
178 wxString::Format( wxT( "%.3f" ),
179 pcbIUScale.IUTomm( group.m_Diameter ) ) );
180 }
181
182 m_groupGrid->SetCellValue( r, COL_PLATING,
183 group.m_NotPlated ? _( "No" ) : _( "Yes" ) );
184 m_groupGrid->SetCellValue( r, COL_SPAN,
185 wxString::Format( wxT( "%s / %s" ),
186 LSET::Name( group.m_TopLayer ),
187 LSET::Name( group.m_BottomLayer ) ) );
188 m_groupGrid->SetCellValue( r, COL_OPS,
189 wxString::Format( wxT( "%d" ), group.m_OperationCount ) );
190 m_groupGrid->SetCellValue( r, COL_SITES,
191 wxString::Format( wxT( "%d" ), group.m_SiteCount ) );
192 }
193
194 m_groupGrid->AutoSizeColumns();
195
196 showDetail( m_groups.empty() ? -1 : 0 );
197}
198
199
201{
202 const int row = m_groupGrid->GetGridCursorRow();
203
204 if( row < 0 || row >= static_cast<int>( m_groups.size() ) )
205 return nullptr;
206
207 return &m_groups[row];
208}
209
210
212{
213 m_loading = true;
214
215 const bool valid = aRow >= 0 && aRow < static_cast<int>( m_groups.size() );
216
217 m_markCtrl->Enable( valid );
218 m_shapeCtrl->Enable( valid );
219 m_letterCtrl->Enable( valid );
220 m_descriptionCtrl->Enable( valid );
221 m_flashButton->Enable( valid );
222
223 if( valid )
224 {
225 const DRILL_CHART_GROUP& group = m_groups[aRow];
226
227 m_markCtrl->SetSelection( static_cast<int>( group.m_Symbol.m_MarkMode ) );
228 m_shapeCtrl->SetValue( group.m_Symbol.m_ShapeIndex );
229 m_letterCtrl->SetValue( group.m_Symbol.m_Letter );
230 m_descriptionCtrl->SetValue( group.m_Symbol.m_Description );
231
232 // The cursor column is -1 until the grid has one, and moving to an invalid cell leaves
233 // the grid re-sending the selection
234 m_groupGrid->SetGridCursor( aRow, std::max( 0, m_groupGrid->GetGridCursorCol() ) );
235 m_groupGrid->SelectRow( aRow );
236 }
237
238 m_detailRow = valid ? aRow : -1;
239 m_loading = false;
240}
241
242
244{
245 if( m_loading || aRow < 0 || aRow >= static_cast<int>( m_groups.size() ) )
246 return;
247
248 const DRILL_CHART_GROUP* group = &m_groups[aRow];
249
250 DRILL_SYMBOL_ASSIGNMENT assignment;
251 assignment.m_MarkMode = static_cast<DRILL_MARK_MODE>( m_markCtrl->GetSelection() );
252 assignment.m_ShapeIndex = m_shapeCtrl->GetValue();
253 assignment.m_Letter = m_letterCtrl->GetValue();
254 assignment.m_Description = m_descriptionCtrl->GetValue();
255
256 BOARD* board = m_frame->GetBoard();
257
258 if( const DRILL_SYMBOL_ASSIGNMENT* existing =
260 {
261 const bool same = existing->m_MarkMode == assignment.m_MarkMode
262 && existing->m_ShapeIndex == assignment.m_ShapeIndex
263 && existing->m_Letter == assignment.m_Letter
264 && existing->m_Description == assignment.m_Description;
265
266 if( same )
267 return;
268 }
269
270 board->GetDesignSettings().GetDrillSymbolProfile().SetAssignment( group->m_SymbolKey, assignment );
272
273 // The profile lives in board design settings, which KiCad does not put on the undo
274 // stack, so the least this can do is mark the board dirty
275 m_frame->OnModify();
276 m_frame->RefreshDrillSymbols( KIGFX::REPAINT );
277}
278
279
280void DIALOG_DRILL_GROUPS::onGridSelect( wxGridEvent& aEvent )
281{
282 // showDetail() moves the cursor itself and lands back here, so without m_loading telling
283 // our own selection from the user's the two recurse until the stack runs out
284 if( !m_loading )
285 {
286 // The row being left owns whatever is in the detail pane. Without this its edits are
287 // simply overwritten by the incoming row
289 showDetail( aEvent.GetRow() );
290 }
291
292 aEvent.Skip();
293}
294
295
296void DIALOG_DRILL_GROUPS::onFreezeChanged( wxCommandEvent& aEvent )
297{
298 BOARD* board = m_frame->GetBoard();
300 m_freezeAssignments->GetValue() );
302
303 m_frame->OnModify();
304 m_frame->RefreshDrillSymbols( KIGFX::REPAINT );
305}
306
307
308void DIALOG_DRILL_GROUPS::onFlash( wxCommandEvent& aEvent )
309{
311
312 if( !group )
313 return;
314
316
317 BOARD* board = m_frame->GetBoard();
318 std::vector<BOARD_ITEM*> items;
319
320 for( const DRILL_OPERATION_ID& id : group->m_Members )
321 {
322 if( BOARD_ITEM* item = board->ResolveItem( id.m_Owner, true ) )
323 items.push_back( item );
324 }
325
326 // Brightens rather than selects, so the canvas selection the user already had survives
327 if( !items.empty() )
328 m_frame->FocusOnItems( items );
329}
330
331
332
333void DIALOG_DRILL_GROUPS::onClose( wxCommandEvent& aEvent )
334{
336 Close();
337}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:128
DRILL_SYMBOL_PROFILE & GetDrillSymbolProfile()
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
Definition board_item.h:84
Information pertinent to a Pcbnew printed circuit board.
Definition board.h:410
std::shared_ptr< const DRILL_SYMBOL_CACHE > DrillSymbolCache() const
Resolved drill symbols, by group and by owning item.
Definition board.cpp:263
void BumpDrillModelGeneration()
Bumped whenever anything a drill chart or map reports on has moved.
Definition board.cpp:179
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1301
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2143
DIALOG_DRILL_GROUPS_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Drill Groups"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxSize(760, 620), long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
const DRILL_CHART_GROUP * selectedGroup() const
void onFreezeChanged(wxCommandEvent &aEvent) override
int m_detailRow
The row the detail pane is showing, so it can be committed before the selection moves.
void onClose(wxCommandEvent &aEvent) override
std::vector< DRILL_CHART_GROUP > m_groups
void Reload()
Rebuild from the board.
void scheduleReload()
Coalesce a burst of board notifications into one rebuild, and keep the rebuild out of the notificatio...
bool m_loading
Guards against the detail pane writing back while it is being filled in.
void commitDetail(int aRow)
Push the detail pane back into the profile for the given row.
void onFlash(wxCommandEvent &aEvent) override
void onGridSelect(wxGridEvent &aEvent)
DIALOG_DRILL_GROUPS(PCB_EDIT_FRAME *aFrame)
PCB_EDIT_FRAME * m_frame
void finishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
Grouping rules and symbol assignments, shared by reference so a chart and its map can never disagree ...
const wxString & GetName() const
const DRILL_SYMBOL_ASSIGNMENT * GetAssignment(const std::string &aKey) const
void SetFreezeAssignments(bool aFreeze)
void SetAssignment(const std::string &aKey, const DRILL_SYMBOL_ASSIGNMENT &aAssignment)
static wxString Name(PCB_LAYER_ID aLayerId)
Return the fixed name association with aLayerId.
Definition lset.cpp:184
The main frame for Pcbnew.
This file is part of the common library.
std::vector< DRILL_SPAN > EnumerateDrillSpans(const BOARD &aBoard, bool aPadBackdrills)
Every drill span present on the board, through-holes first.
#define _(s)
@ REPAINT
Item needs to be redrawn.
Definition view_item.h:54
Globally unique handle for one machining action.
KIBIS_MODEL * model