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>
29#include <pad.h>
30#include <pcb_edit_frame.h>
31#include <pcb_track.h>
32#include <widgets/wx_grid.h>
33
34#include <wx/grid.h>
35
36
37namespace
38{
39
40enum GROUP_GRID_COL
41{
42 COL_SYMBOL = 0,
43 COL_SIZE,
44 COL_PLATING,
45 COL_SPAN,
46 COL_OPS,
47 COL_SITES
48};
49
50
51wxString markText( const DRILL_SYMBOL_ASSIGNMENT& aSymbol, int aDiameter )
52{
53 switch( aSymbol.m_MarkMode )
54 {
55 case DRILL_MARK_MODE::LETTER: return aSymbol.m_Letter;
56 case DRILL_MARK_MODE::SIZE_TEXT: return wxString::Format( wxT( "%.2f" ),
57 pcbIUScale.IUTomm( aDiameter ) );
58 case DRILL_MARK_MODE::SHAPE: break;
59 }
60
61 return wxString::Format( _( "Shape %d" ), aSymbol.m_ShapeIndex + 1 );
62}
63
64} // namespace
65
66
69 m_frame( aFrame ),
70 m_loading( false ),
71 m_detailRow( -1 ),
72 m_reloadPending( false )
73{
74 m_groupGrid->SetColLabelValue( COL_SYMBOL, _( "Symbol" ) );
75 m_groupGrid->SetColLabelValue( COL_SIZE, _( "Size" ) );
76 m_groupGrid->SetColLabelValue( COL_PLATING, _( "Plated" ) );
77 m_groupGrid->SetColLabelValue( COL_SPAN, _( "From / To" ) );
78 m_groupGrid->SetColLabelValue( COL_OPS, _( "Ops" ) );
79 m_groupGrid->SetColLabelValue( COL_SITES, _( "Sites" ) );
80
81 m_groupGrid->SetSelectionMode( wxGrid::wxGridSelectRows );
82 m_groupGrid->EnableEditing( false );
83
84 m_groupGrid->Bind( wxEVT_GRID_SELECT_CELL, &DIALOG_DRILL_GROUPS::onGridSelect, this );
85
86 // Modeless, so closing has to actually destroy. The default would only hide and the
87 // owner's weak reference would keep pointing at a live but invisible dialog
88 Bind( wxEVT_CLOSE_WINDOW,
89 [this]( wxCloseEvent& aEvent )
90 {
92 Destroy();
93 } );
94
95 m_frame->GetBoard()->AddListener( this );
96
97 Reload();
99}
100
101
103{
104 if( m_frame && m_frame->GetBoard() )
105 m_frame->GetBoard()->RemoveListener( this );
106}
107
108
110{
111 if( m_reloadPending )
112 return;
113
114 // Deferred, because the notification can arrive part way through a commit and the board
115 // is not necessarily coherent yet
116 m_reloadPending = true;
117
118 CallAfter(
119 [this]()
120 {
121 m_reloadPending = false;
122 Reload();
123 } );
124}
125
126
128{
129 // Whatever is in the detail pane belongs to the row we are about to discard
131
132 BOARD* board = m_frame->GetBoard();
134
135 m_profileName->SetLabel( profile.GetName().IsEmpty() ? _( "Default" ) : profile.GetName() );
136 m_freezeAssignments->SetValue( profile.GetFreezeAssignments() );
137
138 DRILL_CHART_MODEL model( profile );
139 model.Build( *board, EnumerateDrillSpans( *board ) );
140
141 m_groups = model.Groups();
142
143 // Symbols come from the resolver, not the profile, so a board where no chart has ever
144 // been placed still shows the marks its map would draw
145 const std::shared_ptr<const DRILL_SYMBOL_CACHE> cache = board->DrillSymbolCache();
146
148 {
149 const auto it = cache->m_ByGroup.find( group.m_SymbolKey );
150
151 if( it != cache->m_ByGroup.end() )
152 group.m_Symbol = it->second;
153 }
154
155 if( m_groupGrid->GetNumberRows() )
156 m_groupGrid->DeleteRows( 0, m_groupGrid->GetNumberRows() );
157
158 m_groupGrid->AppendRows( static_cast<int>( m_groups.size() ) );
159
160 for( size_t row = 0; row < m_groups.size(); ++row )
161 {
162 const DRILL_CHART_GROUP& group = m_groups[row];
163 const int r = static_cast<int>( row );
164
165 m_groupGrid->SetCellValue( r, COL_SYMBOL, markText( group.m_Symbol, group.m_Diameter ) );
166
167 if( group.m_IsSlot )
168 {
169 m_groupGrid->SetCellValue( r, COL_SIZE,
170 wxString::Format( wxT( "%.3f x %.3f" ),
171 pcbIUScale.IUTomm( group.m_SizeXY.x ),
172 pcbIUScale.IUTomm( group.m_SizeXY.y ) ) );
173 }
174 else
175 {
176 m_groupGrid->SetCellValue( r, COL_SIZE,
177 wxString::Format( wxT( "%.3f" ),
178 pcbIUScale.IUTomm( group.m_Diameter ) ) );
179 }
180
181 m_groupGrid->SetCellValue( r, COL_PLATING,
182 group.m_NotPlated ? _( "No" ) : _( "Yes" ) );
183 m_groupGrid->SetCellValue( r, COL_SPAN,
184 wxString::Format( wxT( "%s / %s" ),
185 LSET::Name( group.m_TopLayer ),
186 LSET::Name( group.m_BottomLayer ) ) );
187 m_groupGrid->SetCellValue( r, COL_OPS,
188 wxString::Format( wxT( "%d" ), group.m_OperationCount ) );
189 m_groupGrid->SetCellValue( r, COL_SITES,
190 wxString::Format( wxT( "%d" ), group.m_SiteCount ) );
191 }
192
193 m_groupGrid->AutoSizeColumns();
194
195 showDetail( m_groups.empty() ? -1 : 0 );
196}
197
198
200{
201 const int row = m_groupGrid->GetGridCursorRow();
202
203 if( row < 0 || row >= static_cast<int>( m_groups.size() ) )
204 return nullptr;
205
206 return &m_groups[row];
207}
208
209
211{
212 m_loading = true;
213
214 const bool valid = aRow >= 0 && aRow < static_cast<int>( m_groups.size() );
215
216 m_markCtrl->Enable( valid );
217 m_shapeCtrl->Enable( valid );
218 m_letterCtrl->Enable( valid );
219 m_descriptionCtrl->Enable( valid );
220 m_flashButton->Enable( valid );
221
222 if( valid )
223 {
224 const DRILL_CHART_GROUP& group = m_groups[aRow];
225
226 m_markCtrl->SetSelection( static_cast<int>( group.m_Symbol.m_MarkMode ) );
227 m_shapeCtrl->SetValue( group.m_Symbol.m_ShapeIndex );
228 m_letterCtrl->SetValue( group.m_Symbol.m_Letter );
229 m_descriptionCtrl->SetValue( group.m_Symbol.m_Description );
230
231 // The cursor column is -1 until the grid has one, and moving to an invalid cell leaves
232 // the grid re-sending the selection
233 m_groupGrid->SetGridCursor( aRow, std::max( 0, m_groupGrid->GetGridCursorCol() ) );
234 m_groupGrid->SelectRow( aRow );
235 }
236
237 m_detailRow = valid ? aRow : -1;
238 m_loading = false;
239}
240
241
243{
244 if( m_loading || aRow < 0 || aRow >= static_cast<int>( m_groups.size() ) )
245 return;
246
247 const DRILL_CHART_GROUP* group = &m_groups[aRow];
248
249 DRILL_SYMBOL_ASSIGNMENT assignment;
250 assignment.m_MarkMode = static_cast<DRILL_MARK_MODE>( m_markCtrl->GetSelection() );
251 assignment.m_ShapeIndex = m_shapeCtrl->GetValue();
252 assignment.m_Letter = m_letterCtrl->GetValue();
253 assignment.m_Description = m_descriptionCtrl->GetValue();
254
255 BOARD* board = m_frame->GetBoard();
256
257 if( const DRILL_SYMBOL_ASSIGNMENT* existing =
259 {
260 const bool same = existing->m_MarkMode == assignment.m_MarkMode
261 && existing->m_ShapeIndex == assignment.m_ShapeIndex
262 && existing->m_Letter == assignment.m_Letter
263 && existing->m_Description == assignment.m_Description;
264
265 if( same )
266 return;
267 }
268
269 board->GetDesignSettings().GetDrillSymbolProfile().SetAssignment( group->m_SymbolKey, assignment );
271
272 // The profile lives in board design settings, which KiCad does not put on the undo
273 // stack, so the least this can do is mark the board dirty
274 m_frame->OnModify();
275 m_frame->RefreshDrillSymbols( KIGFX::REPAINT );
276}
277
278
279void DIALOG_DRILL_GROUPS::onGridSelect( wxGridEvent& aEvent )
280{
281 // showDetail() moves the cursor itself and lands back here, so without m_loading telling
282 // our own selection from the user's the two recurse until the stack runs out
283 if( !m_loading )
284 {
285 // The row being left owns whatever is in the detail pane. Without this its edits are
286 // simply overwritten by the incoming row
288 showDetail( aEvent.GetRow() );
289 }
290
291 aEvent.Skip();
292}
293
294
295void DIALOG_DRILL_GROUPS::onFreezeChanged( wxCommandEvent& aEvent )
296{
297 BOARD* board = m_frame->GetBoard();
299 m_freezeAssignments->GetValue() );
301
302 m_frame->OnModify();
303 m_frame->RefreshDrillSymbols( KIGFX::REPAINT );
304}
305
306
307void DIALOG_DRILL_GROUPS::onFlash( wxCommandEvent& aEvent )
308{
310
311 if( !group )
312 return;
313
315
316 BOARD* board = m_frame->GetBoard();
317 std::vector<BOARD_ITEM*> items;
318
319 for( const DRILL_OPERATION_ID& id : group->m_Members )
320 {
321 if( BOARD_ITEM* item = board->ResolveItem( id.m_Owner, true ) )
322 items.push_back( item );
323 }
324
325 // Brightens rather than selects, so the canvas selection the user already had survives
326 if( !items.empty() )
327 m_frame->FocusOnItems( items );
328}
329
330
331
332void DIALOG_DRILL_GROUPS::onClose( wxCommandEvent& aEvent )
333{
335 Close();
336}
constexpr EDA_IU_SCALE pcbIUScale
Definition base_units.h:121
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:409
std::shared_ptr< const DRILL_SYMBOL_CACHE > DrillSymbolCache() const
Resolved drill symbols, by group and by owning item.
Definition board.cpp:260
void BumpDrillModelGeneration()
Bumped whenever anything a drill chart or map reports on has moved.
Definition board.cpp:176
BOARD_DESIGN_SETTINGS & GetDesignSettings() const
Definition board.cpp:1299
BOARD_ITEM * ResolveItem(const KIID &aID, bool aAllowNullptrReturn=false) const
Definition board.cpp:2116
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)
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