KiCad PCB EDA Suite
Loading...
Searching...
No Matches
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-2024 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 <wx/msgdlg.h>
27
28#include <base_units.h>
29#include <footprint.h>
30#include <pcb_edit_frame.h>
31#include <tools/pcb_actions.h>
33#include <tool/tool_manager.h>
35
36
41{
47 m_OptionsSet( true ),
48 m_GridNx( 5 ),
49 m_GridNy( 5 ),
50 m_GridDx( pcbIUScale.mmToIU( 2.54 ) ),
51 m_GridDy( pcbIUScale.mmToIU( 2.54 ) ),
52 m_GridOffsetX( 0 ),
53 m_GridOffsetY( 0 ),
54 m_GridStagger( 1 ),
55 m_GridStaggerRows( true ),
57 m_GridNumberingAxis( 0 ), // h then v
58 m_GridNumReverseAlt( false ),
59 m_GridNumStartSet( 1 ), // use specified start
60 m_Grid2dArrayNumbering( 0 ), // linear numbering
61 m_GridPrimaryAxisScheme( 0 ), // numeric
62 m_GridSecondaryAxisScheme( 0 ), // numeric
63 m_GridPrimaryNumOffset( wxT( "1" ) ), // numeric
64 m_GridSecondaryNumOffset( wxT( "1" ) ), // numeric
67 m_CircCentreX( 0 ),
68 m_CircCentreY( 0 ),
70 m_CircCount( 4 ),
71 m_CircNumStartSet( 1 ), // use specified start
73 m_CircNumberingOffset( wxT( "1" ) ),
75 m_CircRotatationStep( false ),
76 m_ArrayTypeTab( 0 ), // start on grid view
78 m_FootprintReannotate( true ) // Assign unique by default
79 {
80 }
81
83
103
117};
118
119// Persistent options settings
121
126{
128 wxString m_label;
129};
130
135static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
136 {
138 _( "Numerals (0,1,2,...,9,10)" ),
139 },
140 {
142 _( "Hexadecimal (0,1,...,F,10,...)" ),
143 },
144 {
146 _( "Alphabet, minus IOSQXZ" ),
147 },
148 {
150 _( "Alphabet, full 26 characters" ),
151 },
152};
153
155 std::unique_ptr<ARRAY_OPTIONS>& aSettings,
156 bool aIsFootprintEditor, const VECTOR2I& aOrigPos ) :
157 DIALOG_CREATE_ARRAY_BASE( aParent ),
158 m_frame( aParent ),
159 m_settings( aSettings ),
160 m_originalItemPosition( aOrigPos ), m_isFootprintEditor( aIsFootprintEditor ),
161 m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
162 m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
163 m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
164 m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
165 m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
166 m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
167 m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
168 m_cfg_persister( pcbIUScale, s_arrayOptions.m_OptionsSet )
169{
170 // Configure display origin transforms
177
178 // Set up numbering scheme drop downs character set strings
179 for( const auto& numData : numberingTypeData )
180 {
181 const wxString label = wxGetTranslation( numData.m_label );
182 void* clientData = (void*) &numData;
183
184 m_choicePriAxisNumbering->Append( label, clientData );
185 m_choiceSecAxisNumbering->Append( label, clientData );
186 m_choiceCircNumbering->Append( label, clientData );
187 }
188
189 m_choicePriAxisNumbering->SetSelection( 0 );
190 m_choiceSecAxisNumbering->SetSelection( 0 );
191 m_choiceCircNumbering->SetSelection( 0 );
192
193 m_circAngle.SetUnits( EDA_UNITS::DEGREES );
194
195 // bind grid options to persister
200
204
206
208
211
216
221
222 // bind circular options to persister
225
230
235
237
240
242
243 // Run the callbacks once to process the dialog contents
246
248 Fit();
249 SetMinSize( GetSize() );
250}
251
252
254{
255}
256
257
258void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
259{
260 if( m_checkBoxFullCircle->GetValue() && m_entryCircAngle == event.GetEventObject() )
261 {
262 return;
263 }
264
267}
268
269
270void DIALOG_CREATE_ARRAY::OnSelectCenterButton( wxCommandEvent& event )
271{
272 event.Skip();
273
275 wxCHECK( pickerTool, /* void */ );
276
277 if( event.GetEventObject() == m_btnSelectCenterItem )
278 {
281 PCB_PICKER_TOOL::INTERACTIVE_PARAMS{ this, _( "Select center item..." ) } );
282 }
283 else if( event.GetEventObject() == m_btnSelectCenterPoint )
284 {
287 PCB_PICKER_TOOL::INTERACTIVE_PARAMS{ this, _( "Select center point..." ) } );
288 }
289 else
290 {
291 wxFAIL_MSG( "Unknown event source" );
292 }
293
294 // Hide, but do not close, the dialog
295 Hide();
296}
297
298
299// Implement the RECEIVER interface for the callback from the TOOL
301{
302 if( aItem )
303 {
304 m_hCentre.SetValue( aItem->GetPosition().x );
305 m_vCentre.SetValue( aItem->GetPosition().y );
306 }
307
308 Show( true );
309}
310
311
312void DIALOG_CREATE_ARRAY::UpdatePickedPoint( const std::optional<VECTOR2I>& aPoint )
313{
314 if( aPoint )
315 {
316 m_hCentre.SetValue( aPoint->x );
317 m_vCentre.SetValue( aPoint->y );
318 }
319
320 Show( true );
321}
322
323
333static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
334 wxArrayString& errors )
335{
336 bool ok = true;
337
338 if( !entry.GetValue().ToLong( &dest ) )
339 {
340 wxString err;
341 err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
342 errors.Add( err );
343 ok = false;
344 }
345
346 return ok;
347}
348
349
360static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
361 const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis,
362 wxArrayString& errors )
363{
364 void* clientData = typeEntry.GetClientData( typeEntry.GetSelection() );
365 const NUMBERING_LIST_DATA* numberingData = static_cast<NUMBERING_LIST_DATA*>( clientData );
366
367 wxCHECK_MSG( numberingData, false, wxT( "Failed to get client data from list control." ) );
368
369 aAxis.SetAxisType( numberingData->m_numbering_type );
370
371 const wxString text = offsetEntry.GetValue();
372
373 bool ok = aAxis.SetOffset( text );
374
375 if( !ok )
376 {
377 errors.Add( wxString::Format( _( "Could not determine numbering start from '%s': "
378 "expected value consistent with alphabet '%s'." ),
379 text,
380 aAxis.GetAlphabet() ) );
381 return false;
382 }
383
384 long step;
385 ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
386
387 if( ok )
388 aAxis.SetStep( step );
389
390 return ok;
391}
392
393
395{
396 std::cout << "DIALOG_CREATE_ARRAY::TransferDataFromWindow()" << std::endl;
397 std::unique_ptr<ARRAY_OPTIONS> newSettings;
398
399 wxArrayString errors;
400 const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
401
402 if( page == m_gridPanel )
403 {
404 auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
405 bool ok = true;
406
407 // ints
408 ok &= validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
409 ok &= validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
410
411 newGrid->m_delta.x = m_hSpacing.GetIntValue();
412 newGrid->m_delta.y = m_vSpacing.GetIntValue();
413
414 newGrid->m_offset.x = m_hOffset.GetIntValue();
415 newGrid->m_offset.y = m_vOffset.GetIntValue();
416
417 newGrid->m_centred = m_rbCentreOnSource->GetValue();
418
419 ok &= validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
420
421 newGrid->m_stagger_rows = m_staggerRows->GetValue();
422
423 newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
424 newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
425
426 newGrid->SetShouldNumber( m_isFootprintEditor );
427
429 {
430 newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
431
432 if( newGrid->GetNumberingStartIsSpecified() )
433 {
434 newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
435
436 // validate from the input fields
440 newGrid->m_pri_axis, errors );
441
442 if( newGrid->m_2dArrayNumbering )
443 {
447 newGrid->m_sec_axis, errors );
448 }
449
450 ok &= numOk;
451 }
452 else
453 {
454 // artificial linear numeric scheme from 1
455 newGrid->m_2dArrayNumbering = false;
456 newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
457 newGrid->m_pri_axis.SetOffset( 1 );
458 }
459 }
460
461 // Only use settings if all values are good
462 if( ok )
463 newSettings = std::move( newGrid );
464 }
465 else if( page == m_circularPanel )
466 {
467 auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
468 bool ok = true;
470
471 newCirc->m_centre.x = m_hCentre.GetIntValue();
472 newCirc->m_centre.y = m_vCentre.GetIntValue();
473 newCirc->m_angle = EDA_ANGLE( angle, DEGREES_T );
474
475 ok = validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
476
477 newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
478 newCirc->SetShouldNumber( m_isFootprintEditor );
479
481 {
482 newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
483
484 if( newCirc->GetNumberingStartIsSpecified() )
485 {
487 *m_entryCircNumberingStep, newCirc->m_axis, errors );
488 }
489 else
490 {
491 // artificial linear numeric scheme from 1
492 newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
493 newCirc->m_axis.SetOffset( 1 ); // Start at "1"
494 }
495 }
496
497 // Only use settings if all values are good
498 if( ok )
499 newSettings = std::move( newCirc );
500 }
501
502 bool ret = false;
503
504 // If we got good settings, send them out and finish
505 if( newSettings )
506 {
507 // assign pointer and ownership here
508 m_settings = std::move( newSettings );
509
510 m_settings->SetSShouldReannotateFootprints( m_radioBtnUniqueRefs->GetValue() );
511
512 // persist the control state for next time
514
515 ret = true;
516 }
517 else
518 {
519 wxString errorStr;
520
521 if( errors.IsEmpty() )
522 errorStr = _("Bad parameters");
523 else
524 errorStr = wxJoin( errors, '\n' );
525
526 wxMessageBox( errorStr );
527 ret = false;
528 }
529
530 // This dialog is not modal, so close it now
531 Close();
532 return ret;
533}
534
535
537{
538 if( m_checkBoxFullCircle->GetValue() )
539 {
540 m_entryCircAngle->Disable();
541 }
542 else
543 {
544 m_entryCircAngle->Enable();
545 }
546
548 {
549 m_footprintReannotatePanel->Show( false );
550
551 m_gridPadNumberingPanel->Show( true );
552 m_circularPadNumberingPanel->Show( true );
553
554 // If we set the start number, we can set the other options,
555 // otherwise it's a hardcoded linear array
556 const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
557
558 m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
559 m_labelPriAxisNumbering->Enable( use_set_start_grid );
560 m_choicePriAxisNumbering->Enable( use_set_start_grid );
561
562 // Disable the secondary axis numbering option if the
563 // numbering scheme doesn't have two axes
564 const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
565
566 m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
567 m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
568
569 // We can only set an offset if we're setting the start number
570 m_labelGridNumberingOffset->Enable( use_set_start_grid );
571 m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
572 m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
573 m_entryGridSecNumberingStep->Enable( use_set_start_grid && num2d );
574
575 // disable the circular number offset in the same way
576 const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
577 m_entryCircNumberingStart->Enable( use_set_start_circ );
578 }
579 else
580 {
581 // grid
582 m_rbGridStartNumberingOpt->Enable( false );
583 m_radioBoxGridNumberingScheme->Enable( false );
584
585 m_labelPriAxisNumbering->Enable( false );
586 m_labelSecAxisNumbering->Enable( false );
587
588 m_choiceSecAxisNumbering->Enable( false );
589 m_choicePriAxisNumbering->Enable( false );
590
591 m_labelGridNumberingOffset->Enable( false );
592 m_entryGridPriNumberingOffset->Enable( false );
593 m_entryGridSecNumberingOffset->Enable( false );
594
595 m_gridPadNumberingPanel->Show( false );
596
597 // circular
598 m_rbCircStartNumberingOpt->Enable( false );
599 m_entryCircNumberingStart->Enable( false );
600
601 m_circularPadNumberingPanel->Show( false );
602
603 m_footprintReannotatePanel->Show( true );
604 }
605}
606
607
609{
610 // In full circle mode, the division angle is computed from the number of points
611 if( m_checkBoxFullCircle->GetValue() )
612 {
613 long nPts;
614 if( m_entryCircCount->GetValue().ToLong( &nPts ) )
615 {
616 EDA_ANGLE division = EDA_ANGLE( 360, DEGREES_T ) / nPts;
617 m_circAngle.SetAngleValue( division );
618 }
619 }
620}
constexpr EDA_IU_SCALE pcbIUScale
Definition: base_units.h:108
Class that contains information about a single array axis and the numbering of items along that axis.
Definition: array_axis.h:40
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:104
const wxString & GetAlphabet() const
Get the alphabet for the current numbering scheme.
Definition: array_axis.cpp:45
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:98
@ NUMBERING_NUMERIC
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
Definition: array_axis.h:44
@ NUMBERING_HEX
Definition: array_axis.h:45
@ NUMBERING_ALPHA_NO_IOSQXZ
Definition: array_axis.h:46
@ NUMBERING_ALPHA_FULL
Full 26-character alphabet.
Definition: array_axis.h:52
void SetStep(int aStep)
Set the skip between consecutive numbers (useful when doing a partial array, e.g.
Definition: array_axis.cpp:129
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
PCB_BASE_FRAME * m_frame
void OnParameterChanged(wxCommandEvent &event) override
std::unique_ptr< ARRAY_OPTIONS > & m_settings
The settings to re-seat on dialog OK.
void UpdatePickedItem(const EDA_ITEM *aItem) override
void UpdatePickedPoint(const std::optional< VECTOR2I > &aPoint) override
void OnSelectCenterButton(wxCommandEvent &event) override
bool Show(bool show) override
void SetupStandardButtons(std::map< int, wxString > aLabels={})
A base class for most all the KiCad significant classes used in schematics and boards.
Definition: eda_item.h:89
virtual VECTOR2I GetPosition() const
Definition: eda_item.h:243
static TOOL_ACTION selectPointInteractively
Definition: pcb_actions.h:332
static TOOL_ACTION selectItemInteractively
Selection of reference points/items.
Definition: pcb_actions.h:331
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
Generic tool for picking an item.
TOOL_MANAGER * GetToolManager() const
Return the MVC controller.
Definition: tools_holder.h:55
bool RunAction(const std::string &aActionName, T aParam)
Run the specified action immediately, pausing the current action to run the new one.
Definition: tool_manager.h:150
int GetIntValue()
Definition: unit_binder.h:129
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 SetAngleValue(const EDA_ANGLE &aValue)
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:199
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)
static constexpr EDA_ANGLE ANGLE_90
Definition: eda_angle.h:403
@ DEGREES_T
Definition: eda_angle.h:31
KICOMMON_API 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:576
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