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 
24 #include <pcb_edit_frame.h>
25 #include <base_units.h>
26 #include <boost/algorithm/string/join.hpp>
27 #include <widgets/text_ctrl_eval.h>
28 #include <class_board.h>
29 #include <class_module.h>
30 
31 #include "dialog_create_array.h"
32 
37 {
43  : m_optionsSet( true ),
44  m_gridNx( 5 ),
45  m_gridNy( 5 ),
46  m_gridDx( Millimeter2iu( 2.54 ) ),
47  m_gridDy( Millimeter2iu( 2.54 ) ),
48  m_gridOffsetX( 0 ),
49  m_gridOffsetY( 0 ),
50  m_gridStagger( 1 ),
51  m_gridStaggerType( 0 ), // rows
52  m_gridNumberingAxis( 0 ), // h then v
54  m_gridNumberingStartSet( 1 ), // use specified start
55  m_grid2dArrayNumbering( 0 ), // linear numbering
56  m_gridPriAxisNumScheme( 0 ), // numeric
57  m_gridSecAxisNumScheme( 0 ), // numeric
58  m_gridPriNumberingOffset( "1" ), // numeric
59  m_gridSecNumberingOffset( "1" ), // numeric
60  m_circCentreX( 0 ),
61  m_circCentreY( 0 ),
62  m_circAngle( 0.0 ),
63  m_circCount( 4 ),
64  m_circNumberingStartSet( 1 ), // use specified start
65  m_circNumberingOffset( "1" ),
66  m_circRotate( false ),
67  m_arrayTypeTab( 0 ) // start on grid view
68  {
69  }
70 
72 
77 
84 
92 };
93 
94 // Persistent options settings
96 
97 
99  PCB_BASE_FRAME* aParent, bool enableNumbering, wxPoint aOrigPos )
100  : DIALOG_CREATE_ARRAY_BASE( aParent ),
101  m_settings( NULL ),
102  m_hSpacing( aParent, m_labelDx, m_entryDx, m_unitLabelDx ),
103  m_vSpacing( aParent, m_labelDy, m_entryDy, m_unitLabelDy ),
104  m_hOffset( aParent, m_labelOffsetX, m_entryOffsetX, m_unitLabelOffsetX ),
105  m_vOffset( aParent, m_labelOffsetY, m_entryOffsetY, m_unitLabelOffsetY ),
106  m_hCentre( aParent, m_labelCentreX, m_entryCentreX, m_unitLabelCentreX ),
107  m_vCentre( aParent, m_labelCentreY, m_entryCentreY, m_unitLabelCentreY ),
108  m_circRadius( aParent, m_labelCircRadius, m_valueCircRadius, m_unitLabelCircRadius ),
109  m_circAngle( aParent, m_labelCircAngle, m_entryCircAngle, m_unitLabelCircAngle ),
110  m_cfg_persister( saved_array_options.m_optionsSet ),
111  m_originalItemPosition( aOrigPos ),
112  m_numberingEnabled( enableNumbering )
113 {
114  // Set up numbering scheme drop downs
115  //
116  // character set
117  // NOTE: do not change the order of this relative to the NUMBERING_TYPE_T enum
118  const wxString charSetDescriptions[] =
119  {
120  _( "Numerals (0,1,2,...,9,10)" ),
121  _( "Hexadecimal (0,1,...,F,10,...)" ),
122  _( "Alphabet, minus IOSQXZ" ),
123  _( "Alphabet, full 26 characters" )
124  };
125  m_choicePriAxisNumbering->Set( arrayDim( charSetDescriptions ), charSetDescriptions );
126  m_choiceSecAxisNumbering->Set( arrayDim( charSetDescriptions ), charSetDescriptions );
127 
128  m_choicePriAxisNumbering->SetSelection( 0 );
129  m_choiceSecAxisNumbering->SetSelection( 0 );
130 
132 
133  // bind grid options to persister
138 
142 
144 
148 
154 
159 
160  // bind circular options to persister
166 
169 
171 
173 
174  // Run the callbacks once to process the dialog contents
177 
178  m_stdButtonsOK->SetDefault();
179  Fit();
180  SetMinSize( GetSize() );
181 }
182 
183 
185 {
186  if( m_settings != NULL )
187  delete m_settings;
188 }
189 
190 
191 void DIALOG_CREATE_ARRAY::OnParameterChanged( wxCommandEvent& event )
192 {
195 }
196 
197 
208 static bool validateNumberingTypeAndOffset( const wxTextCtrl& offsetEntry,
209  const wxChoice& typeEntry,
211  int& offset, wxArrayString& errors )
212 {
213  const int typeVal = typeEntry.GetSelection();
214  // mind undefined casts to enums (should not be able to happen)
215  bool ok = typeVal <= ARRAY_OPTIONS::NUMBERING_TYPE_MAX;
216 
217  if( ok )
218  {
219  type = (ARRAY_OPTIONS::NUMBERING_TYPE_T) typeVal;
220  }
221  else
222  {
223  wxString err;
224  err.Printf( _("Unrecognized numbering scheme: %d"), typeVal );
225  errors.Add( err );
226  // we can't proceed - we don't know the numbering type
227  return false;
228  }
229 
230  const wxString text = offsetEntry.GetValue();
231  ok = ARRAY_OPTIONS::GetNumberingOffset( text, type, offset );
232 
233  if( !ok )
234  {
235  const wxString& alphabet = ARRAY_OPTIONS::AlphabetFromNumberingScheme( type );
236 
237  wxString err;
238  err.Printf( _( "Could not determine numbering start from \"%s\": "
239  "expected value consistent with alphabet \"%s\"" ),
240  text, alphabet );
241  errors.Add(err);
242  }
243 
244  return ok;
245 }
246 
247 
257 static bool validateLongEntry( const wxTextEntry& entry, long& dest, const wxString& description,
258  wxArrayString& errors )
259 {
260  bool ok = true;
261 
262  if( !entry.GetValue().ToLong( &dest ) )
263  {
264  wxString err;
265  err.Printf( _("Bad numeric value for %s: %s"), description, entry.GetValue() );
266  errors.Add( err );
267  ok = false;
268  }
269 
270  return ok;
271 }
272 
273 
275 {
276  ARRAY_OPTIONS* newSettings = NULL;
277  wxArrayString errors;
278  const wxWindow* page = m_gridTypeNotebook->GetCurrentPage();
279 
280  if( page == m_gridPanel )
281  {
282  ARRAY_GRID_OPTIONS* newGrid = new ARRAY_GRID_OPTIONS();
283  bool ok = true;
284 
285  // ints
286  ok = ok && validateLongEntry(*m_entryNx, newGrid->m_nx, _("horizontal count"), errors);
287  ok = ok && validateLongEntry(*m_entryNy, newGrid->m_ny, _("vertical count"), errors);
288 
289  newGrid->m_delta.x = m_hSpacing.GetValue();
290  newGrid->m_delta.y = m_vSpacing.GetValue();
291 
292  newGrid->m_offset.x = m_hOffset.GetValue();
293  newGrid->m_offset.y = m_vOffset.GetValue();
294 
295  ok = ok && validateLongEntry(*m_entryStagger, newGrid->m_stagger, _("stagger"), errors);
296 
297  newGrid->m_stagger_rows = m_radioBoxGridStaggerType->GetSelection() == 0;
298 
299  newGrid->m_horizontalThenVertical = m_radioBoxGridNumberingAxis->GetSelection() == 0;
301 
303 
304  if ( m_numberingEnabled )
305  {
306  newGrid->SetNumberingStartIsSpecified( m_rbGridStartNumberingOpt->GetSelection() == 1 );
307 
308  if( newGrid->GetNumberingStartIsSpecified() )
309  {
310  newGrid->m_2dArrayNumbering = m_radioBoxGridNumberingScheme->GetSelection() != 0;
311 
312  // validate from the input fields
315  newGrid->m_numberingOffsetX, errors );
316 
317  if( newGrid->m_2dArrayNumbering )
318  {
321  newGrid->m_numberingOffsetY, errors )
322  && numOk;
323  }
324 
325  ok = ok && numOk;
326  }
327  else
328  {
329  // artificial linear numeric scheme from 1
330  newGrid->m_2dArrayNumbering = false;
331  newGrid->m_priAxisNumType = ARRAY_OPTIONS::NUMBERING_TYPE_T::NUMBERING_NUMERIC;
332  newGrid->m_numberingOffsetX = 1; // Start at "1"
333  }
334  }
335 
336  // Only use settings if all values are good
337  if( ok )
338  newSettings = newGrid;
339  else
340  delete newGrid;
341  }
342  else if( page == m_circularPanel )
343  {
345  bool ok = true;
346 
347  newCirc->m_centre.x = m_hCentre.GetValue();
348  newCirc->m_centre.y = m_vCentre.GetValue();
349  newCirc->m_angle = DoubleValueFromString( DEGREES, m_entryCircAngle->GetValue() );
350 
351  ok = ok && validateLongEntry(*m_entryCircCount, newCirc->m_nPts, _("point count"), errors);
352 
353  newCirc->m_rotateItems = m_entryRotateItemsCb->GetValue();
355 
356  if ( m_numberingEnabled )
357  {
358  newCirc->SetNumberingStartIsSpecified( m_rbCircStartNumberingOpt->GetSelection() == 1 );
359 
360  if( newCirc->GetNumberingStartIsSpecified() )
361  {
363 
364  ok = ok
366  _( "numbering start" ), errors );
367  }
368  else
369  {
370  // artificial linear numeric scheme from 1
371  newCirc->m_numberingOffset = 1; // Start at "1"
372  }
373  }
374 
375  // Only use settings if all values are good
376  if( ok )
377  newSettings = newCirc;
378  else
379  delete newCirc;
380  }
381 
382  // If we got good settings, send them out and finish
383  if( newSettings )
384  {
385  delete m_settings;
386 
387  // assign pointer and ownership here
388  m_settings = newSettings;
390 
391  return true;
392  }
393  else
394  {
395  wxString errorStr;
396 
397  if( errors.IsEmpty() )
398  errorStr = _("Bad parameters");
399  else
400  errorStr = boost::algorithm::join( errors, "\n" );
401 
402  wxMessageBox( errorStr );
403  return false;
404  }
405 }
406 
407 
409 {
410  if ( m_numberingEnabled )
411  {
412  // If we set the start number, we can set the other options,
413  // otherwise it's a hardcoded linear array
414  const bool use_set_start_grid = m_rbGridStartNumberingOpt->GetSelection() == 1;
415 
416  m_radioBoxGridNumberingScheme->Enable( use_set_start_grid );
417  m_labelPriAxisNumbering->Enable( use_set_start_grid );
418  m_choicePriAxisNumbering->Enable( use_set_start_grid );
419 
420  // Disable the secondary axis numbering option if the
421  // numbering scheme doesn't have two axes
422  const bool num2d = m_radioBoxGridNumberingScheme->GetSelection() != 0;
423 
424  m_labelSecAxisNumbering->Enable( use_set_start_grid && num2d );
425  m_choiceSecAxisNumbering->Enable( use_set_start_grid && num2d );
426 
427  // We can only set an offset if we're setting the start number
428  m_labelGridNumberingOffset->Enable( use_set_start_grid );
429  m_entryGridPriNumberingOffset->Enable( use_set_start_grid );
430  m_entryGridSecNumberingOffset->Enable( use_set_start_grid && num2d );
431 
432  // disable the circular number offset in the same way
433  const bool use_set_start_circ = m_rbCircStartNumberingOpt->GetSelection() == 1;
434  m_entryCircNumberingStart->Enable( use_set_start_circ );
435  }
436  else
437  {
438  // grid
439  m_rbGridStartNumberingOpt->Enable( false );
440  m_radioBoxGridNumberingScheme->Enable( false );
441 
442  m_labelPriAxisNumbering->Enable( false );
443  m_labelSecAxisNumbering->Enable( false );
444 
445  m_choiceSecAxisNumbering->Enable( false );
446  m_choicePriAxisNumbering->Enable( false );
447 
448  m_labelGridNumberingOffset->Enable( false );
449  m_entryGridPriNumberingOffset->Enable( false );
450  m_entryGridSecNumberingOffset->Enable( false );
451 
452  // circular
453  m_rbCircStartNumberingOpt->Enable( false );
454  m_entryCircNumberingStart->Enable( false );
455  }
456 }
457 
458 
460 {
462 
463  // Find the radius, etc of the circle
464  centre -= m_originalItemPosition;
465 
466  m_circRadius.SetValue( int( centre.EuclideanNorm() ) );
467 }
DIALOG_CREATE_ARRAY(PCB_BASE_FRAME *aParent, bool enableNumbering, wxPoint aOrigPos)
static bool validateNumberingTypeAndOffset(const wxTextCtrl &offsetEntry, const wxChoice &typeEntry, ARRAY_OPTIONS::NUMBERING_TYPE_T &type, int &offset, wxArrayString &errors)
Validates and saves (if valid) the type and offset of an array axis numbering.
void OnParameterChanged(wxCommandEvent &event) override
Implementation of conversion functions that require both schematic and board internal units.
ARRAY_OPTIONS * m_settings
The settings object returned to the caller.
Class BOARD to handle a board.
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:65
static bool validateLongEntry(const wxTextEntry &entry, long &dest, const wxString &description, wxArrayString &errors)
Validate and save a long integer entry.
NUMBERING_TYPE_T m_secAxisNumType
WIDGET_SAVE_RESTORE m_cfg_persister
virtual int GetValue()
Function GetValue Returns the current value in Internal Units.
long m_nPts
number of point in the array
Options that govern the setup of an "array" of multiple item.
Definition: array_options.h:34
CREATE_ARRAY_DIALOG_ENTRIES()
Construct with some sensible defaults.
void RestoreConfigToControls()
Restore the values from the internally-stored references to the underlying data to each bound control...
NUMBERING_TYPE_T m_numberingType
#define NUMBERING_TYPE_MAX
bool m_reverseNumberingAlternate
double DoubleValueFromString(EDA_UNITS_T aUnits, const wxString &aTextValue, bool aUseMils)
Function DoubleValueFromString converts aTextValue to a double.
Definition: base_units.cpp:302
Arabic numerals: 0,1,2,3,4,5,6,7,8,9,10,11...
Definition: array_options.h:46
bool TransferDataFromWindow() override
string & err
Definition: json11.cpp:598
void ReadConfigFromControls()
Read values of all bound controls into the internally-stored references to the underlying data.
constexpr std::size_t arrayDim(T const (&)[N]) noexcept
Definition: macros.h:99
Struct containing the last-entered values for the dialog.
static const wxString & AlphabetFromNumberingScheme(NUMBERING_TYPE_T type)
Get the alphabet for a particular numbering scheme.
static bool GetNumberingOffset(const wxString &str, ARRAY_OPTIONS::NUMBERING_TYPE_T type, int &offsetToFill)
Get the numbering offset for a given numbering string.
void Add(wxRadioBox &ctrl, long &dest)
Bind a radiobox to a choice.
virtual void SetValue(int aValue)
Function SetValue Sets new value (in Internal Units) for the text field, taking care of units convers...
NUMBERING_TYPE_T m_priAxisNumType
void SetShouldNumber(bool aShouldNumber)
Module description (excepted pads)
Class DIALOG_CREATE_ARRAY_BASE.
class PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
static CREATE_ARRAY_DIALOG_ENTRIES saved_array_options
bool GetNumberingStartIsSpecified() const
void SetNumberingStartIsSpecified(bool aIsSpecified)
double m_angle
angle between points, or 0 for each point separated by this value (decideg)
const wxPoint m_originalItemPosition