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-2019 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 
28 #include <widgets/text_ctrl_eval.h>
29 
30 #include <class_board.h>
31 #include <class_module.h>
32 #include <pcb_edit_frame.h>
33 
34 #include <boost/algorithm/string/join.hpp>
35 
40 {
46  : m_optionsSet( true ),
47  m_gridNx( 5 ),
48  m_gridNy( 5 ),
49  m_gridDx( Millimeter2iu( 2.54 ) ),
50  m_gridDy( Millimeter2iu( 2.54 ) ),
51  m_gridOffsetX( 0 ),
52  m_gridOffsetY( 0 ),
53  m_gridStagger( 1 ),
54  m_gridStaggerType( 0 ), // rows
55  m_gridNumberingAxis( 0 ), // h then v
57  m_gridNumberingStartSet( 1 ), // use specified start
58  m_grid2dArrayNumbering( 0 ), // linear numbering
59  m_gridPriAxisNumScheme( 0 ), // numeric
60  m_gridSecAxisNumScheme( 0 ), // numeric
61  m_gridPriNumberingOffset( "1" ), // numeric
62  m_gridSecNumberingOffset( "1" ), // numeric
63  m_gridPriAxisStep( 1 ),
64  m_gridSecAxisStep( 1 ),
65  m_circCentreX( 0 ),
66  m_circCentreY( 0 ),
67  m_circAngle( 0.0 ),
68  m_circCount( 4 ),
69  m_circNumberingStartSet( 1 ), // use specified start
70  m_circNumberingOffset( "1" ),
72  m_circRotate( false ),
73  m_arrayTypeTab( 0 ) // start on grid view
74  {
75  }
76 
78 
83 
91 
101 };
102 
103 // Persistent options settings
105 
110 {
112  wxString m_label;
113 };
114 
119 static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
120  {
121  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
122  _( "Numerals (0,1,2,...,9,10)" ),
123  },
124  {
125  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_HEX,
126  _( "Hexadecimal (0,1,...,F,10,...)" ),
127  },
128  {
129  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_NO_IOSQXZ,
130  _( "Alphabet, minus IOSQXZ" ),
131  },
132  {
133  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
134  _( "Alphabet, full 26 characters" ),
135  },
136 };
137 
139  std::unique_ptr<ARRAY_OPTIONS>& aSettings, bool enableNumbering, wxPoint aOrigPos )
140  : DIALOG_CREATE_ARRAY_BASE( aParent ),
141  m_settings( aSettings ),
142  m_originalItemPosition( aOrigPos ),
143  m_numberingEnabled( enableNumbering ),
144  m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
145  m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
146  m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
147  m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
148  m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
149  m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
150  m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius ),
151  m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
152  m_cfg_persister( saved_array_options.m_optionsSet )
153 {
154  // Set up numbering scheme drop downs character set strings
155  for( const auto& numData : numberingTypeData )
156  {
157  const wxString label = wxGetTranslation( numData.m_label );
158  void* clientData = (void*) &numData;
159 
160  m_choicePriAxisNumbering->Append( label, clientData );
161  m_choiceSecAxisNumbering->Append( label, clientData );
162  m_choiceCircNumbering->Append( label, clientData );
163  }
164 
165  m_choicePriAxisNumbering->SetSelection( 0 );
166  m_choiceSecAxisNumbering->SetSelection( 0 );
167  m_choiceCircNumbering->SetSelection( 0 );
168 
170 
171  // bind grid options to persister
176 
180 
182 
186 
192 
199 
200  // bind circular options to persister
206 
211 
213 
215 
216  // Run the callbacks once to process the dialog contents
219 
220  m_stdButtonsOK->SetDefault();
221  Fit();
222  SetMinSize( GetSize() );
223 }
224 
225 
226 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
227 {
230 }
231 
232 
242 static bool validateLongEntry(
243  const wxTextEntry& entry, long& dest, const wxString& description, wxArrayString& errors )
244 {
245  bool ok = true;
246 
247  if( !entry.GetValue().ToLong( &dest ) )
248  {
249  wxString err;
250  err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
251  errors.Add( err );
252  ok = false;
253  }
254 
255  return ok;
256 }
257 
258 
269 static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
270  const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis, wxArrayString& errors )
271 {
272  const auto* typeData = static_cast<NUMBERING_LIST_DATA*>(
273  typeEntry.GetClientData( typeEntry.GetSelection() ) );
274 
275  wxCHECK_MSG( typeData, false, "Failed to get client data from list control." );
276 
277  aAxis.SetAxisType( typeData->m_numbering_type );
278 
279  const wxString text = offsetEntry.GetValue();
280 
281  bool ok = aAxis.SetOffset( text );
282 
283  if( !ok )
284  {
285  const wxString& alphabet = aAxis.GetAlphabet();
286 
287  wxString err;
288  err.Printf( _( "Could not determine numbering start from \"%s\": "
289  "expected value consistent with alphabet \"%s\"" ),
290  text, alphabet );
291  errors.Add(err);
292  return false;
293  }
294 
295  long step;
296  ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
297 
298  if( ok )
299  aAxis.SetStep( step );
300 
301  return ok;
302 }
303 
304 
306 {
307  std::unique_ptr<ARRAY_OPTIONS> newSettings;
308 
309  wxArrayString errors;
310  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
311 
312  if( page == m_gridPanel )
313  {
314  auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
315  bool ok = true;
316 
317  // ints
318  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
319  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
320 
321  newGrid->m_delta.x = m_hSpacing.GetValue();
322  newGrid->m_delta.y = m_vSpacing.GetValue();
323 
324  newGrid->m_offset.x = m_hOffset.GetValue();
325  newGrid->m_offset.y = m_vOffset.GetValue();
326 
327  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
328 
329  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
330 
331  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
332  newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
333 
334  newGrid->SetShouldNumber( m_numberingEnabled );
335 
336  if ( m_numberingEnabled )
337  {
338  newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
339 
340  if( newGrid->GetNumberingStartIsSpecified() )
341  {
342  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
343 
344  // validate from the input fields
347  newGrid->m_pri_axis, errors );
348 
349  if( newGrid->m_2dArrayNumbering )
350  {
353  newGrid->m_sec_axis, errors )
354  && numOk;
355  }
356 
357  ok = ok && numOk;
358  }
359  else
360  {
361  // artificial linear numeric scheme from 1
362  newGrid->m_2dArrayNumbering = false;
363  newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
364  newGrid->m_pri_axis.SetOffset( 1 );
365  }
366  }
367 
368  // Only use settings if all values are good
369  if( ok )
370  newSettings = std::move( newGrid );
371  }
372  else if( page == m_circularPanel )
373  {
374  auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
375  bool ok = true;
376 
377  newCirc->m_centre.x = m_hCentre.GetValue();
378  newCirc->m_centre.y = m_vCentre.GetValue();
379  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
380 
381  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
382 
383  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
384  newCirc->SetShouldNumber( m_numberingEnabled );
385 
386  if ( m_numberingEnabled )
387  {
388  newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
389 
390  if( newCirc->GetNumberingStartIsSpecified() )
391  {
392  ok = ok
394  *m_entryCircNumberingStep, newCirc->m_axis, errors );
395  }
396  else
397  {
398  // artificial linear numeric scheme from 1
399  newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
400  newCirc->m_axis.SetOffset( 1 ); // Start at "1"
401  }
402  }
403 
404  // Only use settings if all values are good
405  if( ok )
406  newSettings = std::move( newCirc );
407  }
408 
409  // If we got good settings, send them out and finish
410  if( newSettings )
411  {
412  // assign pointer and ownership here
413  m_settings = std::move( newSettings );
414 
415  // persist the control state for next time
417 
418  return true;
419  }
420  else
421  {
422  wxString errorStr;
423 
424  if( errors.IsEmpty() )
425  errorStr = _("Bad parameters");
426  else
427  errorStr = boost::algorithm::join( errors, "\n" );
428 
429  wxMessageBox( errorStr );
430  return false;
431  }
432 }
433 
434 
436 {
437  if ( m_numberingEnabled )
438  {
439  // If we set the start number, we can set the other options,
440  // otherwise it's a hardcoded linear array
441  const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
442 
443  m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
444  m_labelPriAxisNumbering->Enable( use_set_start_grid );
445  m_choicePriAxisNumbering->Enable( use_set_start_grid );
446 
447  // Disable the secondary axis numbering option if the
448  // numbering scheme doesn't have two axes
449  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
450 
451  m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
452  m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
453 
454  // We can only set an offset if we're setting the start number
455  m_labelGridNumberingOffset->Enable( use_set_start_grid );
456  m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
457  m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
458 
459  // disable the circular number offset in the same way
460  const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
461  m_entryCircNumberingStart->Enable( use_set_start_circ );
462  }
463  else
464  {
465  // grid
466  m_rbGridStartNumberingOpt->Enable( false );
467  m_radioBoxGridNumberingScheme->Enable( false );
468 
469  m_labelPriAxisNumbering->Enable( false );
470  m_labelSecAxisNumbering->Enable( false );
471 
472  m_choiceSecAxisNumbering->Enable( false );
473  m_choicePriAxisNumbering->Enable( false );
474 
475  m_labelGridNumberingOffset->Enable( false );
476  m_entryGridPriNumberingOffset->Enable( false );
477  m_entryGridSecNumberingOffset->Enable( false );
478 
479  // circular
480  m_rbCircStartNumberingOpt->Enable( false );
481  m_entryCircNumberingStart->Enable( false );
482  }
483 }
484 
485 
487 {
489 
490  // Find the radius, etc of the circle
491  centre -= m_originalItemPosition;
492 
493  m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
494 }
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.
virtual void SetUnits(EDA_UNITS_T aUnits, bool aUseMils=false)
Function SetUnits Normally not needed (as the UNIT_BINDER inherits from the parent frame),...
Definition: unit_binder.cpp:68
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...
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:314
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
#define _(s)
string & err
Definition: json11.cpp:598
void ReadConfigFromControls()
Read values of all bound controls into the internally-stored references to the underlying data.
Struct containing the last-entered values for the dialog.
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.
virtual void SetValue(int aValue)
Function SetValue Sets new value (in Internal Units) for the text field, taking care of units convers...
virtual long long int GetValue()
Function GetValue Returns the current value in Internal Units.
Module description (excepted pads)
ARRAY_AXIS::NUMBERING_TYPE m_numbering_type
Class DIALOG_CREATE_ARRAY_BASE.
const wxString & GetAlphabet() const
Get the alphabet for the current numbering scheme.
Definition: array_axis.cpp:43
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static CREATE_ARRAY_DIALOG_ENTRIES saved_array_options
const wxPoint m_originalItemPosition