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 =
382 
383  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
384 
385  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
386  newCirc->SetShouldNumber( m_numberingEnabled );
387 
388  if ( m_numberingEnabled )
389  {
390  newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
391 
392  if( newCirc->GetNumberingStartIsSpecified() )
393  {
394  ok = ok
396  *m_entryCircNumberingStep, newCirc->m_axis, errors );
397  }
398  else
399  {
400  // artificial linear numeric scheme from 1
401  newCirc->m_axis.SetAxisType( ARRAY_AXIS::NUMBERING_TYPE::NUMBERING_NUMERIC );
402  newCirc->m_axis.SetOffset( 1 ); // Start at "1"
403  }
404  }
405 
406  // Only use settings if all values are good
407  if( ok )
408  newSettings = std::move( newCirc );
409  }
410 
411  // If we got good settings, send them out and finish
412  if( newSettings )
413  {
414  // assign pointer and ownership here
415  m_settings = std::move( newSettings );
416 
417  // persist the control state for next time
419 
420  return true;
421  }
422  else
423  {
424  wxString errorStr;
425 
426  if( errors.IsEmpty() )
427  errorStr = _("Bad parameters");
428  else
429  errorStr = boost::algorithm::join( errors, "\n" );
430 
431  wxMessageBox( errorStr );
432  return false;
433  }
434 }
435 
436 
438 {
439  if ( m_numberingEnabled )
440  {
441  // If we set the start number, we can set the other options,
442  // otherwise it's a hardcoded linear array
443  const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
444 
445  m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
446  m_labelPriAxisNumbering->Enable( use_set_start_grid );
447  m_choicePriAxisNumbering->Enable( use_set_start_grid );
448 
449  // Disable the secondary axis numbering option if the
450  // numbering scheme doesn't have two axes
451  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
452 
453  m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
454  m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
455 
456  // We can only set an offset if we're setting the start number
457  m_labelGridNumberingOffset->Enable( use_set_start_grid );
458  m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
459  m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
460 
461  // disable the circular number offset in the same way
462  const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
463  m_entryCircNumberingStart->Enable( use_set_start_circ );
464  }
465  else
466  {
467  // grid
468  m_rbGridStartNumberingOpt->Enable( false );
469  m_radioBoxGridNumberingScheme->Enable( false );
470 
471  m_labelPriAxisNumbering->Enable( false );
472  m_labelSecAxisNumbering->Enable( false );
473 
474  m_choiceSecAxisNumbering->Enable( false );
475  m_choicePriAxisNumbering->Enable( false );
476 
477  m_labelGridNumberingOffset->Enable( false );
478  m_entryGridPriNumberingOffset->Enable( false );
479  m_entryGridSecNumberingOffset->Enable( false );
480 
481  // circular
482  m_rbCircStartNumberingOpt->Enable( false );
483  m_entryCircNumberingStart->Enable( false );
484  }
485 }
486 
487 
489 {
491 
492  // Find the radius, etc of the circle
493  centre -= m_originalItemPosition;
494 
495  m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
496 }
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.
double DoubleValueFromString(EDA_UNITS aUnits, const wxString &aTextValue, bool aUseMils, EDA_DATA_TYPE aType)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:346
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.
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.
#define _(s)
Definition: 3d_actions.cpp:33
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.
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
PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static CREATE_ARRAY_DIALOG_ENTRIES saved_array_options
virtual void SetUnits(EDA_UNITS aUnits, bool aUseMils=false)
Function SetUnits Normally not needed (as the UNIT_BINDER inherits from the parent frame),...
Definition: unit_binder.cpp:79
const wxPoint m_originalItemPosition