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-2020 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>
27 #include <widgets/text_ctrl_eval.h>
28 #include <board.h>
29 #include <footprint.h>
30 #include <pcb_edit_frame.h>
31 
32 #include <boost/algorithm/string/join.hpp>
33 
38 {
44  m_OptionsSet( true ),
45  m_GridNx( 5 ),
46  m_GridNy( 5 ),
47  m_GridDx( Millimeter2iu( 2.54 ) ),
48  m_GridDy( Millimeter2iu( 2.54 ) ),
49  m_GridOffsetX( 0 ),
50  m_GridOffsetY( 0 ),
51  m_GridStagger( 1 ),
52  m_GridStaggerType( 0 ), // rows
53  m_GridNumberingAxis( 0 ), // h then v
54  m_GridNumReverseAlt( false ),
55  m_GridNumStartSet( 1 ), // use specified start
56  m_Grid2dArrayNumbering( 0 ), // linear numbering
57  m_GridPrimaryAxisScheme( 0 ), // numeric
58  m_GridSecondaryAxisScheme( 0 ), // numeric
59  m_GridPrimaryNumOffset( "1" ), // numeric
60  m_GridSecondaryNumOffset( "1" ), // numeric
63  m_CircCentreX( 0 ),
64  m_CircCentreY( 0 ),
65  m_CircAngle( 0.0 ),
66  m_CircCount( 4 ),
67  m_CircNumStartSet( 1 ), // use specified start
69  m_CircNumberingOffset( "1" ),
71  m_CircRotatationStep( false ),
72  m_ArrayTypeTab( 0 ) // start on grid view
73  {
74  }
75 
77 
78  long m_GridNx;
79  long m_GridNy;
80  long m_GridDx;
81  long m_GridDy;
85 
97 
108 };
109 
110 // Persistent options settings
112 
117 {
119  wxString m_label;
120 };
121 
126 static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
127  {
128  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
129  _( "Numerals (0,1,2,...,9,10)" ),
130  },
131  {
132  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_HEX,
133  _( "Hexadecimal (0,1,...,F,10,...)" ),
134  },
135  {
136  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_NO_IOSQXZ,
137  _( "Alphabet, minus IOSQXZ" ),
138  },
139  {
140  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
141  _( "Alphabet, full 26 characters" ),
142  },
143 };
144 
146  std::unique_ptr<ARRAY_OPTIONS>& aSettings,
147  bool enableNumbering, wxPoint aOrigPos ) :
148  DIALOG_CREATE_ARRAY_BASE( aParent ),
149  m_settings( aSettings ),
150  m_originalItemPosition( aOrigPos ),
151  m_numberingEnabled( enableNumbering ),
152  m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
153  m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
154  m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
155  m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
156  m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
157  m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
158  m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius ),
159  m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
160  m_cfg_persister( s_arrayOptions.m_OptionsSet )
161 {
162  // Configure display origin transforms
169 
170  // Set up numbering scheme drop downs character set strings
171  for( const auto& numData : numberingTypeData )
172  {
173  const wxString label = wxGetTranslation( numData.m_label );
174  void* clientData = (void*) &numData;
175 
176  m_choicePriAxisNumbering->Append( label, clientData );
177  m_choiceSecAxisNumbering->Append( label, clientData );
178  m_choiceCircNumbering->Append( label, clientData );
179  }
180 
181  m_choicePriAxisNumbering->SetSelection( 0 );
182  m_choiceSecAxisNumbering->SetSelection( 0 );
183  m_choiceCircNumbering->SetSelection( 0 );
184 
186 
187  // bind grid options to persister
192 
196 
198 
201 
206 
211 
212  // bind circular options to persister
218 
223 
225 
227 
228  // Run the callbacks once to process the dialog contents
231 
232  m_stdButtonsOK->SetDefault();
233  Fit();
234  SetMinSize( GetSize() );
235 }
236 
237 
238 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
239 {
242 }
243 
244 
254 static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
255  wxArrayString& errors )
256 {
257  bool ok = true;
258 
259  if( !entry.GetValue().ToLong( &dest ) )
260  {
261  wxString err;
262  err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
263  errors.Add( err );
264  ok = false;
265  }
266 
267  return ok;
268 }
269 
270 
281 static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
282  const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis,
283  wxArrayString& errors )
284 {
285  void* clientData = typeEntry.GetClientData( typeEntry.GetSelection() );
286  const NUMBERING_LIST_DATA* numberingData = static_cast<NUMBERING_LIST_DATA*>( clientData );
287 
288  wxCHECK_MSG( numberingData, false, "Failed to get client data from list control." );
289 
290  aAxis.SetAxisType( numberingData->m_numbering_type );
291 
292  const wxString text = offsetEntry.GetValue();
293 
294  bool ok = aAxis.SetOffset( text );
295 
296  if( !ok )
297  {
298  errors.Add( wxString::Format( _( "Could not determine numbering start from \"%s\": "
299  "expected value consistent with alphabet \"%s\"" ),
300  text,
301  aAxis.GetAlphabet() ) );
302  return false;
303  }
304 
305  long step;
306  ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
307 
308  if( ok )
309  aAxis.SetStep( step );
310 
311  return ok;
312 }
313 
314 
316 {
317  std::unique_ptr<ARRAY_OPTIONS> newSettings;
318 
319  wxArrayString errors;
320  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
321 
322  if( page == m_gridPanel )
323  {
324  auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
325  bool ok = true;
326 
327  // ints
328  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
329  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
330 
331  newGrid->m_delta.x = m_hSpacing.GetValue();
332  newGrid->m_delta.y = m_vSpacing.GetValue();
333 
334  newGrid->m_offset.x = m_hOffset.GetValue();
335  newGrid->m_offset.y = m_vOffset.GetValue();
336 
337  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
338 
339  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
340 
341  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
342  newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
343 
344  newGrid->SetShouldNumber( m_numberingEnabled );
345 
346  if ( m_numberingEnabled )
347  {
348  newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
349 
350  if( newGrid->GetNumberingStartIsSpecified() )
351  {
352  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
353 
354  // validate from the input fields
357  newGrid->m_pri_axis, errors );
358 
359  if( newGrid->m_2dArrayNumbering )
360  {
363  newGrid->m_sec_axis, errors )
364  && numOk;
365  }
366 
367  ok = ok && numOk;
368  }
369  else
370  {
371  // artificial linear numeric scheme from 1
372  newGrid->m_2dArrayNumbering = false;
373  newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
374  newGrid->m_pri_axis.SetOffset( 1 );
375  }
376  }
377 
378  // Only use settings if all values are good
379  if( ok )
380  newSettings = std::move( newGrid );
381  }
382  else if( page == m_circularPanel )
383  {
384  auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
385  bool ok = true;
386 
387  newCirc->m_centre.x = m_hCentre.GetValue();
388  newCirc->m_centre.y = m_vCentre.GetValue();
389  newCirc->m_angle =
391 
392  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
393 
394  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
395  newCirc->SetShouldNumber( m_numberingEnabled );
396 
397  if ( m_numberingEnabled )
398  {
399  newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
400 
401  if( newCirc->GetNumberingStartIsSpecified() )
402  {
403  ok = ok
405  *m_entryCircNumberingStep, newCirc->m_axis, errors );
406  }
407  else
408  {
409  // artificial linear numeric scheme from 1
410  newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
411  newCirc->m_axis.SetOffset( 1 ); // Start at "1"
412  }
413  }
414 
415  // Only use settings if all values are good
416  if( ok )
417  newSettings = std::move( newCirc );
418  }
419 
420  // If we got good settings, send them out and finish
421  if( newSettings )
422  {
423  // assign pointer and ownership here
424  m_settings = std::move( newSettings );
425 
426  // persist the control state for next time
428 
429  return true;
430  }
431  else
432  {
433  wxString errorStr;
434 
435  if( errors.IsEmpty() )
436  errorStr = _("Bad parameters");
437  else
438  errorStr = boost::algorithm::join( errors, "\n" );
439 
440  wxMessageBox( errorStr );
441  return false;
442  }
443 }
444 
445 
447 {
448  if ( m_numberingEnabled )
449  {
450  // If we set the start number, we can set the other options,
451  // otherwise it's a hardcoded linear array
452  const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
453 
454  m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
455  m_labelPriAxisNumbering->Enable( use_set_start_grid );
456  m_choicePriAxisNumbering->Enable( use_set_start_grid );
457 
458  // Disable the secondary axis numbering option if the
459  // numbering scheme doesn't have two axes
460  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
461 
462  m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
463  m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
464 
465  // We can only set an offset if we're setting the start number
466  m_labelGridNumberingOffset->Enable( use_set_start_grid );
467  m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
468  m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
469 
470  // disable the circular number offset in the same way
471  const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
472  m_entryCircNumberingStart->Enable( use_set_start_circ );
473  }
474  else
475  {
476  // grid
477  m_rbGridStartNumberingOpt->Enable( false );
478  m_radioBoxGridNumberingScheme->Enable( false );
479 
480  m_labelPriAxisNumbering->Enable( false );
481  m_labelSecAxisNumbering->Enable( false );
482 
483  m_choiceSecAxisNumbering->Enable( false );
484  m_choicePriAxisNumbering->Enable( false );
485 
486  m_labelGridNumberingOffset->Enable( false );
487  m_entryGridPriNumberingOffset->Enable( false );
488  m_entryGridSecNumberingOffset->Enable( false );
489 
490  // circular
491  m_rbCircStartNumberingOpt->Enable( false );
492  m_entryCircNumberingStart->Enable( false );
493  }
494 }
495 
496 
498 {
500 
501  // Find the radius, etc of the circle
502  centre -= m_originalItemPosition;
503 
504  m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
505 }
void OnParameterChanged(wxCommandEvent &event) override
Implementation of conversion functions that require both schematic and board internal units.
std::unique_ptr< ARRAY_OPTIONS > & m_settings
The settings to re-seat on dialog OK.
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
void SetAxisType(NUMBERING_TYPE aType)
Set the axis numbering type.
Definition: array_axis.cpp:96
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
WIDGET_SAVE_RESTORE m_cfg_persister
CREATE_ARRAY_DIALOG_ENTRIES()
Construct with some sensible defaults.
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, std::unique_ptr< ARRAY_OPTIONS > &aOptions, bool enableNumbering, wxPoint aOrigPos)
Construct a new dialog.
void SetStep(int aStep)
Set the skip between consecutive numbers (useful when doing a partial array, e.g.
Definition: array_axis.cpp:127
void RestoreConfigToControls()
Restore the values from the internally-stored references to the underlying data to each bound control...
Class that contains information about a single array axis and the numbering of items along that axis.
Definition: array_axis.h:38
bool TransferDataFromWindow() override
void ReadConfigFromControls()
Read values of all bound controls into the internally-stored references to the underlying data.
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.
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...
Definition: unit_binder.cpp:81
void Add(wxRadioBox &ctrl, long &dest)
Bind a radiobox to a choice.
static const std::vector< NUMBERING_LIST_DATA > numberingTypeData
List of type <--> name mappings (in order) for the numbering type list boxes.
Local mapping for list-box <-> numbering type.
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.
#define _(s)
Definition: 3d_actions.cpp:33
virtual void SetValue(int aValue)
Set new value (in Internal Units) for the text field, taking care of units conversion.
virtual long long int GetValue()
Return the current value in Internal Units.
void SetCoordType(ORIGIN_TRANSFORMS::COORD_TYPES_T aCoordType)
Set the current origin transform mode.
Definition: unit_binder.h:173
ARRAY_AXIS::NUMBERING_TYPE m_numbering_type
Class DIALOG_CREATE_ARRAY_BASE.
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:338
static constexpr int Millimeter2iu(double mm)
const wxString & GetAlphabet() const
Get the alphabet for the current numbering scheme.
Definition: array_axis.cpp:43
static CREATE_ARRAY_DIALOG_ENTRIES s_arrayOptions
Base PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
const wxPoint m_originalItemPosition