KiCad PCB EDA Suite
dialog_create_array.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 (C) 1992-2021 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, 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
25
26#include <base_units.h>
28#include <board.h>
29#include <footprint.h>
30#include <pcb_edit_frame.h>
31#include <wx/msgdlg.h>
32
33#include <boost/algorithm/string/join.hpp>
34
39{
45 m_OptionsSet( true ),
46 m_GridNx( 5 ),
47 m_GridNy( 5 ),
48 m_GridDx( pcbIUScale.mmToIU( 2.54 ) ),
49 m_GridDy( pcbIUScale.mmToIU( 2.54 ) ),
50 m_GridOffsetX( 0 ),
51 m_GridOffsetY( 0 ),
52 m_GridStagger( 1 ),
53 m_GridStaggerType( 0 ), // rows
54 m_GridNumberingAxis( 0 ), // h then v
55 m_GridNumReverseAlt( false ),
56 m_GridNumStartSet( 1 ), // use specified start
57 m_Grid2dArrayNumbering( 0 ), // linear numbering
58 m_GridPrimaryAxisScheme( 0 ), // numeric
59 m_GridSecondaryAxisScheme( 0 ), // numeric
60 m_GridPrimaryNumOffset( wxT( "1" ) ), // numeric
61 m_GridSecondaryNumOffset( wxT( "1" ) ), // numeric
64 m_CircCentreX( 0 ),
65 m_CircCentreY( 0 ),
66 m_CircCount( 4 ),
67 m_CircNumStartSet( 1 ), // use specified start
69 m_CircNumberingOffset( wxT( "1" ) ),
71 m_CircRotatationStep( false ),
72 m_ArrayTypeTab( 0 ), // start on grid view
74 m_FootprintReannotate( true ) // Assign unique by default
75 {
76 }
77
79
87
99
112};
113
114// Persistent options settings
116
121{
123 wxString m_label;
124};
125
130static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
131 {
132 ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
133 _( "Numerals (0,1,2,...,9,10)" ),
134 },
135 {
136 ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_HEX,
137 _( "Hexadecimal (0,1,...,F,10,...)" ),
138 },
139 {
140 ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_NO_IOSQXZ,
141 _( "Alphabet, minus IOSQXZ" ),
142 },
143 {
144 ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
145 _( "Alphabet, full 26 characters" ),
146 },
147};
148
150 std::unique_ptr<ARRAY_OPTIONS>& aSettings,
151 bool aIsFootprintEditor, const VECTOR2I& aOrigPos ) :
152 DIALOG_CREATE_ARRAY_BASE( aParent ),
153 m_settings( aSettings ),
154 m_originalItemPosition( aOrigPos ),
155 m_isFootprintEditor( aIsFootprintEditor ),
156 m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
157 m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
158 m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
159 m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
160 m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
161 m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
162 m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius ),
163 m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
164 m_cfg_persister( pcbIUScale, s_arrayOptions.m_OptionsSet )
165{
166 // Configure display origin transforms
173
174 // Set up numbering scheme drop downs character set strings
175 for( const auto& numData : numberingTypeData )
176 {
177 const wxString label = wxGetTranslation( numData.m_label );
178 void* clientData = (void*) &numData;
179
180 m_choicePriAxisNumbering->Append( label, clientData );
181 m_choiceSecAxisNumbering->Append( label, clientData );
182 m_choiceCircNumbering->Append( label, clientData );
183 }
184
185 m_choicePriAxisNumbering->SetSelection( 0 );
186 m_choiceSecAxisNumbering->SetSelection( 0 );
187 m_choiceCircNumbering->SetSelection( 0 );
188
190
191 // bind grid options to persister
196
200
202
205
210
215
216 // bind circular options to persister
222
227
229
232
234
235 // Run the callbacks once to process the dialog contents
238
240 Fit();
241 SetMinSize( GetSize() );
242}
243
244
245void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
246{
249}
250
251
261static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
262 wxArrayString& errors )
263{
264 bool ok = true;
265
266 if( !entry.GetValue().ToLong( &dest ) )
267 {
268 wxString err;
269 err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
270 errors.Add( err );
271 ok = false;
272 }
273
274 return ok;
275}
276
277
288static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
289 const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis,
290 wxArrayString& errors )
291{
292 void* clientData = typeEntry.GetClientData( typeEntry.GetSelection() );
293 const NUMBERING_LIST_DATA* numberingData = static_cast<NUMBERING_LIST_DATA*>( clientData );
294
295 wxCHECK_MSG( numberingData, false, wxT( "Failed to get client data from list control." ) );
296
297 aAxis.SetAxisType( numberingData->m_numbering_type );
298
299 const wxString text = offsetEntry.GetValue();
300
301 bool ok = aAxis.SetOffset( text );
302
303 if( !ok )
304 {
305 errors.Add( wxString::Format( _( "Could not determine numbering start from '%s': "
306 "expected value consistent with alphabet '%s'." ),
307 text,
308 aAxis.GetAlphabet() ) );
309 return false;
310 }
311
312 long step;
313 ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
314
315 if( ok )
316 aAxis.SetStep( step );
317
318 return ok;
319}
320
321
323{
324 std::unique_ptr<ARRAY_OPTIONS> newSettings;
325
326 wxArrayString errors;
327 const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
328
329 if( page == m_gridPanel )
330 {
331 auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
332 bool ok = true;
333
334 // ints
335 ok &= validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
336 ok &= validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
337
338 newGrid->m_delta.x = m_hSpacing.GetValue();
339 newGrid->m_delta.y = m_vSpacing.GetValue();
340
341 newGrid->m_offset.x = m_hOffset.GetValue();
342 newGrid->m_offset.y = m_vOffset.GetValue();
343
344 ok &= validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
345
346 newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
347
348 newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
349 newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
350
351 newGrid->SetShouldNumber( m_isFootprintEditor );
352
354 {
355 newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
356
357 if( newGrid->GetNumberingStartIsSpecified() )
358 {
359 newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
360
361 // validate from the input fields
365 newGrid->m_pri_axis, errors );
366
367 if( newGrid->m_2dArrayNumbering )
368 {
372 newGrid->m_sec_axis, errors );
373 }
374
375 ok &= numOk;
376 }
377 else
378 {
379 // artificial linear numeric scheme from 1
380 newGrid->m_2dArrayNumbering = false;
381 newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
382 newGrid->m_pri_axis.SetOffset( 1 );
383 }
384 }
385
386 // Only use settings if all values are good
387 if( ok )
388 newSettings = std::move( newGrid );
389 }
390 else if( page == m_circularPanel )
391 {
392 auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
393 bool ok = true;
395
396 newCirc->m_centre.x = m_hCentre.GetValue();
397 newCirc->m_centre.y = m_vCentre.GetValue();
398 newCirc->m_angle = EDA_ANGLE( angle, DEGREES_T );
399
400 ok = validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
401
402 newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
403 newCirc->SetShouldNumber( m_isFootprintEditor );
404
406 {
407 newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
408
409 if( newCirc->GetNumberingStartIsSpecified() )
410 {
412 *m_entryCircNumberingStep, newCirc->m_axis, errors );
413 }
414 else
415 {
416 // artificial linear numeric scheme from 1
417 newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
418 newCirc->m_axis.SetOffset( 1 ); // Start at "1"
419 }
420 }
421
422 // Only use settings if all values are good
423 if( ok )
424 newSettings = std::move( newCirc );
425 }
426
427 // If we got good settings, send them out and finish
428 if( newSettings )
429 {
430 // assign pointer and ownership here
431 m_settings = std::move( newSettings );
432
433 m_settings->SetSShouldReannotateFootprints( m_radioBtnUniqueRefs->GetValue() );
434
435 // persist the control state for next time
437
438 return true;
439 }
440 else
441 {
442 wxString errorStr;
443
444 if( errors.IsEmpty() )
445 errorStr = _("Bad parameters");
446 else
447 errorStr = boost::algorithm::join( errors, wxT( "\n" ) );
448
449 wxMessageBox( errorStr );
450 return false;
451 }
452}
453
454
456{
458 {
459 m_footprintReannotatePanel->Show( false );
460
461 m_gridPadNumberingPanel->Show( true );
462 m_circularPadNumberingPanel->Show( true );
463
464 // If we set the start number, we can set the other options,
465 // otherwise it's a hardcoded linear array
466 const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
467
468 m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
469 m_labelPriAxisNumbering->Enable( use_set_start_grid );
470 m_choicePriAxisNumbering->Enable( use_set_start_grid );
471
472 // Disable the secondary axis numbering option if the
473 // numbering scheme doesn't have two axes
474 const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
475
476 m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
477 m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
478
479 // We can only set an offset if we're setting the start number
480 m_labelGridNumberingOffset->Enable( use_set_start_grid );
481 m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
482 m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
483
484 // disable the circular number offset in the same way
485 const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
486 m_entryCircNumberingStart->Enable( use_set_start_circ );
487 }
488 else
489 {
490 // grid
491 m_rbGridStartNumberingOpt->Enable( false );
492 m_radioBoxGridNumberingScheme->Enable( false );
493
494 m_labelPriAxisNumbering->Enable( false );
495 m_labelSecAxisNumbering->Enable( false );
496
497 m_choiceSecAxisNumbering->Enable( false );
498 m_choicePriAxisNumbering->Enable( false );
499
500 m_labelGridNumberingOffset->Enable( false );
501 m_entryGridPriNumberingOffset->Enable( false );
502 m_entryGridSecNumberingOffset->Enable( false );
503
504 m_gridPadNumberingPanel->Show( false );
505
506 // circular
507 m_rbCircStartNumberingOpt->Enable( false );
508 m_entryCircNumberingStart->Enable( false );
509
510 m_circularPadNumberingPanel->Show( false );
511
512 m_footprintReannotatePanel->Show( true );
513 }
514}
515
516
518{
520
521 // Find the radius, etc of the circle
522 centre -= m_originalItemPosition;
523
524 m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
525}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:109
Class that contains information about a single array axis and the numbering of items along that axis.
Definition: array_axis.h:39
bool SetOffset(const wxString &aOffsetName)
Set the axis start (as a string, which should decode to a valid index in the alphabet)
Definition: array_axis.cpp:102
const wxString & GetAlphabet() const
Get the alphabet for the current numbering scheme.
Definition: array_axis.cpp:43
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:96
void SetStep(int aStep)
Set the skip between consecutive numbers (useful when doing a partial array, e.g.
Definition: array_axis.cpp:127
Class DIALOG_CREATE_ARRAY_BASE.
WIDGET_SAVE_RESTORE m_cfg_persister
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, std::unique_ptr< ARRAY_OPTIONS > &aOptions, bool enableNumbering, const VECTOR2I &aOrigPos)
Construct a new dialog.
bool TransferDataFromWindow() override
void OnParameterChanged(wxCommandEvent &event) override
const VECTOR2I m_originalItemPosition
std::unique_ptr< ARRAY_OPTIONS > & m_settings
The settings to re-seat on dialog OK.
void SetupStandardButtons(std::map< int, wxString > aLabels={})
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
virtual long long int GetValue()
Return the current value in Internal Units.
virtual void SetUnits(EDA_UNITS aUnits)
Normally not needed (as the UNIT_BINDER inherits from the parent frame), but can be used to set to DE...
virtual void SetValue(long long int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
void SetCoordType(ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType)
Set the current origin transform mode.
Definition: unit_binder.h:186
T EuclideanNorm() const
Compute the Euclidean norm of the vector, which is defined as sqrt(x ** 2 + y ** 2).
Definition: vector2d.h:293
void Add(wxRadioBox &ctrl, long &dest)
Bind a radiobox to a choice.
void RestoreConfigToControls()
Restore the values from the internally-stored references to the underlying data to each bound control...
void ReadConfigFromControls()
Read values of all bound controls into the internally-stored references to the underlying data.
static const std::vector< NUMBERING_LIST_DATA > numberingTypeData
List of type <--> name mappings (in order) for the numbering type list boxes.
static bool validateAxisOptions(const wxTextCtrl &offsetEntry, const wxChoice &typeEntry, const wxTextCtrl &aStepEntry, ARRAY_AXIS &aAxis, wxArrayString &errors)
Validates and saves (if valid) the type and offset of an array axis numbering.
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
static CREATE_ARRAY_DIALOG_ENTRIES s_arrayOptions
#define _(s)
@ DEGREES_T
Definition: eda_angle.h:31
double DoubleValueFromString(const EDA_IU_SCALE &aIuScale, EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType=EDA_DATA_TYPE::DISTANCE)
Function DoubleValueFromString converts aTextValue to a double.
Definition: eda_units.cpp:451
static DIRECTION_45::AngleType angle(const VECTOR2I &a, const VECTOR2I &b)
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, const CPTREE &aTree)
Output a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:200
Struct containing the last-entered values for the dialog.
CREATE_ARRAY_DIALOG_ENTRIES()
Construct with some sensible defaults.
Local mapping for list-box <-> numbering type.
ARRAY_AXIS::NUMBERING_TYPE m_numbering_type