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
71  m_circNumberingOffset( "1" ),
73  m_circRotate( false ),
74  m_arrayTypeTab( 0 ) // start on grid view
75  {
76  }
77 
79 
84 
92 
102 };
103 
104 // Persistent options settings
106 
111 {
113  wxString m_label;
114 };
115 
120 static const std::vector<NUMBERING_LIST_DATA> numberingTypeData {
121  {
122  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC,
123  _( "Numerals (0,1,2,...,9,10)" ),
124  },
125  {
126  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_HEX,
127  _( "Hexadecimal (0,1,...,F,10,...)" ),
128  },
129  {
130  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_NO_IOSQXZ,
131  _( "Alphabet, minus IOSQXZ" ),
132  },
133  {
134  ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_ALPHA_FULL,
135  _( "Alphabet, full 26 characters" ),
136  },
137 };
138 
140  std::unique_ptr<ARRAY_OPTIONS>& aSettings, bool enableNumbering, wxPoint aOrigPos )
141  : DIALOG_CREATE_ARRAY_BASE( aParent ),
142  m_settings( aSettings ),
143  m_originalItemPosition( aOrigPos ),
144  m_numberingEnabled( enableNumbering ),
145  m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
146  m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
147  m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
148  m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
149  m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
150  m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
151  m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius ),
152  m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
153  m_cfg_persister( saved_array_options.m_optionsSet )
154 {
155  // Set up numbering scheme drop downs character set strings
156  for( const auto& numData : numberingTypeData )
157  {
158  const wxString label = wxGetTranslation( numData.m_label );
159  void* clientData = (void*) &numData;
160 
161  m_choicePriAxisNumbering->Append( label, clientData );
162  m_choiceSecAxisNumbering->Append( label, clientData );
163  m_choiceCircNumbering->Append( label, clientData );
164  }
165 
166  m_choicePriAxisNumbering->SetSelection( 0 );
167  m_choiceSecAxisNumbering->SetSelection( 0 );
168  m_choiceCircNumbering->SetSelection( 0 );
169 
171 
172  // bind grid options to persister
177 
181 
183 
187 
193 
200 
201  // bind circular options to persister
207 
212 
214 
216 
217  // Run the callbacks once to process the dialog contents
220 
221  m_stdButtonsOK->SetDefault();
222  Fit();
223  SetMinSize( GetSize() );
224 }
225 
226 
227 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
228 {
231 }
232 
233 
243 static bool validateLongEntry(
244  const wxTextEntry& entry, long& dest, const wxString& description, wxArrayString& errors )
245 {
246  bool ok = true;
247 
248  if( !entry.GetValue().ToLong( &dest ) )
249  {
250  wxString err;
251  err.Printf( _( "Bad numeric value for %s: %s" ), description, entry.GetValue() );
252  errors.Add( err );
253  ok = false;
254  }
255 
256  return ok;
257 }
258 
259 
270 static bool validateAxisOptions( const wxTextCtrl& offsetEntry, const wxChoice& typeEntry,
271  const wxTextCtrl& aStepEntry, ARRAY_AXIS& aAxis, wxArrayString& errors )
272 {
273  const auto* typeData = static_cast<NUMBERING_LIST_DATA*>(
274  typeEntry.GetClientData( typeEntry.GetSelection() ) );
275 
276  wxCHECK_MSG( typeData, false, "Failed to get client data from list control." );
277 
278  aAxis.SetAxisType( typeData->m_numbering_type );
279 
280  const wxString text = offsetEntry.GetValue();
281 
282  bool ok = aAxis.SetOffset( text );
283 
284  if( !ok )
285  {
286  const wxString& alphabet = aAxis.GetAlphabet();
287 
288  wxString err;
289  err.Printf( _( "Could not determine numbering start from \"%s\": "
290  "expected value consistent with alphabet \"%s\"" ),
291  text, alphabet );
292  errors.Add(err);
293  return false;
294  }
295 
296  long step;
297  ok = validateLongEntry( aStepEntry, step, _( "step value" ), errors );
298 
299  if( ok )
300  aAxis.SetStep( step );
301 
302  return ok;
303 }
304 
305 
307 {
308  std::unique_ptr<ARRAY_OPTIONS> newSettings;
309 
310  wxArrayString errors;
311  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
312 
313  if( page == m_gridPanel )
314  {
315  auto newGrid = std::make_unique<ARRAY_GRID_OPTIONS>();
316  bool ok = true;
317 
318  // ints
319  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
320  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
321 
322  newGrid->m_delta.x = m_hSpacing.GetValue();
323  newGrid->m_delta.y = m_vSpacing.GetValue();
324 
325  newGrid->m_offset.x = m_hOffset.GetValue();
326  newGrid->m_offset.y = m_vOffset.GetValue();
327 
328  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
329 
330  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
331 
332  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
333  newGrid->m_reverseNumberingAlternate = m_checkBoxGridReverseNumbering->GetValue();
334 
335  newGrid->SetShouldNumber( m_numberingEnabled );
336 
337  if ( m_numberingEnabled )
338  {
339  newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
340 
341  if( newGrid->GetNumberingStartIsSpecified() )
342  {
343  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
344 
345  // validate from the input fields
348  newGrid->m_pri_axis, errors );
349 
350  if( newGrid->m_2dArrayNumbering )
351  {
354  newGrid->m_sec_axis, errors )
355  && numOk;
356  }
357 
358  ok = ok && numOk;
359  }
360  else
361  {
362  // artificial linear numeric scheme from 1
363  newGrid->m_2dArrayNumbering = false;
364  newGrid->m_pri_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
365  newGrid->m_pri_axis.SetOffset( 1 );
366  }
367  }
368 
369  // Only use settings if all values are good
370  if( ok )
371  newSettings = std::move( newGrid );
372  }
373  else if( page == m_circularPanel )
374  {
375  auto newCirc = std::make_unique<ARRAY_CIRCULAR_OPTIONS>();
376  bool ok = true;
377 
378  newCirc->m_centre.x = m_hCentre.GetValue();
379  newCirc->m_centre.y = m_vCentre.GetValue();
380  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
381 
382  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
383 
384  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
385  newCirc->SetShouldNumber( m_numberingEnabled );
386 
387  if ( m_numberingEnabled )
388  {
389  newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
390 
391  if( newCirc->GetNumberingStartIsSpecified() )
392  {
393  ok = ok
395  *m_entryCircNumberingStep, newCirc->m_axis, errors );
396  }
397  else
398  {
399  // artificial linear numeric scheme from 1
400  newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
401  newCirc->m_axis.SetOffset( 1 ); // Start at "1"
402  }
403  }
404 
405  // Only use settings if all values are good
406  if( ok )
407  newSettings = std::move( newCirc );
408  }
409 
410  // If we got good settings, send them out and finish
411  if( newSettings )
412  {
413  // assign pointer and ownership here
414  m_settings = std::move( newSettings );
415 
416  // persist the control state for next time
418 
419  return true;
420  }
421  else
422  {
423  wxString errorStr;
424 
425  if( errors.IsEmpty() )
426  errorStr = _("Bad parameters");
427  else
428  errorStr = boost::algorithm::join( errors, "\n" );
429 
430  wxMessageBox( errorStr );
431  return false;
432  }
433 }
434 
435 
437 {
438  if ( m_numberingEnabled )
439  {
440  // If we set the start number, we can set the other options,
441  // otherwise it's a hardcoded linear array
442  const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
443 
444  m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
445  m_labelPriAxisNumbering->Enable( use_set_start_grid );
446  m_choicePriAxisNumbering->Enable( use_set_start_grid );
447 
448  // Disable the secondary axis numbering option if the
449  // numbering scheme doesn't have two axes
450  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
451 
452  m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
453  m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
454 
455  // We can only set an offset if we're setting the start number
456  m_labelGridNumberingOffset->Enable( use_set_start_grid );
457  m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
458  m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
459 
460  // disable the circular number offset in the same way
461  const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
462  m_entryCircNumberingStart->Enable( use_set_start_circ );
463  }
464  else
465  {
466  // grid
467  m_rbGridStartNumberingOpt->Enable( false );
468  m_radioBoxGridNumberingScheme->Enable( false );
469 
470  m_labelPriAxisNumbering->Enable( false );
471  m_labelSecAxisNumbering->Enable( false );
472 
473  m_choiceSecAxisNumbering->Enable( false );
474  m_choicePriAxisNumbering->Enable( false );
475 
476  m_labelGridNumberingOffset->Enable( false );
477  m_entryGridPriNumberingOffset->Enable( false );
478  m_entryGridSecNumberingOffset->Enable( false );
479 
480  // circular
481  m_rbCircStartNumberingOpt->Enable( false );
482  m_entryCircNumberingStart->Enable( false );
483  }
484 }
485 
486 
488 {
490 
491  // Find the radius, etc of the circle
492  centre -= m_originalItemPosition;
493 
494  m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
495 }
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:650
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