KiCad PCB EDA Suite
dialog_edit_component_in_lib.cpp
Go to the documentation of this file.
1 
5 /*
6  * This program source code file is part of KiCad, a free EDA CAD application.
7  *
8  * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, you may find one here:
22  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
23  * or you may search the http://www.gnu.org website for the version 2 license,
24  * or you may write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
26  */
27 
28 #include <fctsys.h>
29 #include <kiway.h>
30 #include <common.h>
31 #include <confirm.h>
32 #include <gestfich.h>
33 #include <pgm_base.h>
34 
35 #include <general.h>
36 #include <lib_edit_frame.h>
37 #include <class_library.h>
38 #include <eeschema_id.h> // for MAX_UNIT_COUNT_PER_PACKAGE definition
39 #include <symbol_lib_table.h>
40 
42 
44 
47 {
48  m_Parent = aParent;
49  m_RecreateToolbar = false;
50 
51  initDlg();
52 
53  // Now all widgets have the size fixed, call FinishDialogSettings
55 }
56 
57 
59 {
60  m_lastOpenedPage = m_NoteBook->GetSelection( );
61 }
62 
63 /* Initialize state of check boxes and texts
64 */
66 {
67  m_AliasLocation = -1;
68 
69  LIB_PART* component = m_Parent->GetCurPart();
70 
71  if( component == NULL )
72  {
73  SetTitle( _( "Library Component Properties" ) );
74  return;
75  }
76 
77  wxString title, staticText;
78  bool isRoot = m_Parent->GetAliasName().CmpNoCase( component->GetName() ) == 0;
79 
80  if( !isRoot )
81  {
82  title.Printf( _( "Properties for %s (alias of %s)" ),
84  GetChars( component->GetName() ) );
85 
86  staticText.Printf( _( "Alias List of %s" ), GetChars( component->GetName() ) );
87  m_staticTextAlias->SetLabelText( staticText );
88  }
89  else
90  title.Printf( _( "Properties for %s" ), GetChars( component->GetName() ) );
91 
92  SetTitle( title );
93  InitPanelDoc();
95 
96  // The component's alias list contains all names (including the root). The UI list
97  // contains only aliases, so exclude the root.
98  m_PartAliasListCtrl->Append( component->GetAliasNames( false ) );
99 
100  // Note: disabling the delete buttons gives us no opportunity to tell the user
101  // why they're disabled. Leave them enabled and bring up an error message instead.
102 
103  /* Read the Footprint Filter list */
104  m_FootprintFilterListBox->Append( component->GetFootprints() );
105 
106  if( component->GetFootprints().GetCount() == 0 )
107  {
108  m_ButtonDeleteAllFootprintFilter->Enable( false );
109  m_ButtonDeleteOneFootprintFilter->Enable( false );
110  m_buttonEditOneFootprintFilter->Enable( false );
111  }
112 
113  m_NoteBook->SetSelection( m_lastOpenedPage );
114 
115  m_stdSizerButtonOK->SetDefault();
116 }
117 
118 
120 {
121  EndModal( wxID_CANCEL );
122 }
123 
124 
126 {
127  LIB_ALIAS* alias;
128  LIB_PART* component = m_Parent->GetCurPart();
129 
130  if( component == NULL )
131  return;
132 
133  wxString aliasname = m_Parent->GetAliasName();
134 
135  if( aliasname.IsEmpty() )
136  return;
137 
138  alias = component->GetAlias( aliasname );
139 
140  if( alias != NULL )
141  {
142  m_DocCtrl->SetValue( alias->GetDescription() );
143  m_KeywordsCtrl->SetValue( alias->GetKeyWords() );
144  m_DocfileCtrl->SetValue( alias->GetDocFileName() );
145  }
146 }
147 
148 
149 /*
150  * create the basic panel for component properties editing
151  */
153 {
154  LIB_PART* component = m_Parent->GetCurPart();
155 
156  if( m_Parent->GetShowDeMorgan() )
157  m_AsConvertButt->SetValue( true );
158 
159  int maxUnits = MAX_UNIT_COUNT_PER_PACKAGE;
160  m_SelNumberOfUnits->SetRange (1, maxUnits );
161 
163  _( "Number of Units (max allowed %d)" ), maxUnits ) );
164 
165 
166  /* Default values for a new component. */
167  if( component == NULL )
168  {
169  m_ShowPinNumButt->SetValue( true );
170  m_ShowPinNameButt->SetValue( true );
171  m_PinsNameInsideButt->SetValue( true );
172  m_SelNumberOfUnits->SetValue( 1 );
173  m_SetSkew->SetValue( 40 );
174  m_OptionPower->SetValue( false );
175  m_OptionPartsLocked->SetValue( false );
176  return;
177  }
178 
179  m_ShowPinNumButt->SetValue( component->ShowPinNumbers() );
180  m_ShowPinNameButt->SetValue( component->ShowPinNames() );
181  m_PinsNameInsideButt->SetValue( component->GetPinNameOffset() != 0 );
182  m_SelNumberOfUnits->SetValue( component->GetUnitCount() );
183  m_SetSkew->SetValue( component->GetPinNameOffset() );
184  m_OptionPower->SetValue( component->IsPower() );
185  m_OptionPartsLocked->SetValue( component->UnitsLocked() && component->GetUnitCount() > 1 );
186 }
187 
188 
189 void DIALOG_EDIT_COMPONENT_IN_LIBRARY::OnOkClick( wxCommandEvent& event )
190 {
191  /* Update the doc, keyword and doc filename strings */
192  LIB_ALIAS* alias;
193  LIB_PART* component = m_Parent->GetCurPart();
194 
195  if( component == NULL )
196  {
197  EndModal( wxID_CANCEL );
198  return;
199  }
200 
201  m_Parent->SaveCopyInUndoList( component );
202 
203  alias = component->GetAlias( m_Parent->GetAliasName() );
204 
205  wxCHECK_RET( alias != NULL,
206  wxT( "Alias \"" ) + m_Parent->GetAliasName() + wxT( "\" of symbol \"" ) +
207  component->GetName() + wxT( "\" does not exist." ) );
208 
209  alias->SetDescription( m_DocCtrl->GetValue() );
210  alias->SetKeyWords( m_KeywordsCtrl->GetValue() );
211  alias->SetDocFileName( m_DocfileCtrl->GetValue() );
212 
213  // The UI list contains only aliases (ie: not the root's name), while the component's
214  // alias list contains all names (including the root).
215  wxArrayString aliases = m_PartAliasListCtrl->GetStrings();
216  aliases.Add( component->GetName() );
217  component->SetAliases( aliases );
218 
219  int unitCount = m_SelNumberOfUnits->GetValue();
220  ChangeNbUnitsPerPackage( unitCount );
221 
222  if( m_AsConvertButt->GetValue() )
223  {
224  if( !m_Parent->GetShowDeMorgan() )
225  {
226  m_Parent->SetShowDeMorgan( true );
227  SetUnsetConvert();
228  }
229  }
230  else
231  {
232  if( m_Parent->GetShowDeMorgan() )
233  {
234  m_Parent->SetShowDeMorgan( false );
235  SetUnsetConvert();
236  }
237  }
238 
239  component->SetShowPinNumbers( m_ShowPinNumButt->GetValue() );
240  component->SetShowPinNames( m_ShowPinNameButt->GetValue() );
241 
242  if( m_PinsNameInsideButt->GetValue() == false )
243  component->SetPinNameOffset( 0 ); // pin text outside the body (name is on the pin)
244  else
245  {
246  component->SetPinNameOffset( m_SetSkew->GetValue() );
247  // Ensure component->m_TextInside != 0, because the meaning is "text outside".
248  if( component->GetPinNameOffset() == 0 )
249  component->SetPinNameOffset( 20 ); // give a reasonnable value
250  }
251 
252  if( m_OptionPower->GetValue() == true )
253  component->SetPower();
254  else
255  component->SetNormal();
256 
257  /* Set the option "Units locked".
258  * Obviously, cannot be true if there is only one part */
259  component->LockUnits( m_OptionPartsLocked->GetValue() );
260 
261  if( component->GetUnitCount() <= 1 )
262  component->LockUnits( false );
263 
264  /* Update the footprint filter list */
265  component->GetFootprints().Clear();
266  component->GetFootprints() = m_FootprintFilterListBox->GetStrings();
267 
268  EndModal( wxID_OK );
269 }
270 
271 
273 {
274  if( m_Parent == NULL )
275  return;
276 
277  LIB_ALIAS* parent_alias;
278  LIB_PART* component = m_Parent->GetCurPart();
279 
280  if( component == NULL )
281  return;
282 
283  // search for the main alias: this is the first alias in alias list
284  // something like the main component
285  parent_alias = component->GetAlias( 0 );
286 
287  if( parent_alias == NULL ) // Should never occur (bug)
288  return;
289 
290  m_DocCtrl->SetValue( parent_alias->GetDescription() );
291  m_DocfileCtrl->SetValue( parent_alias->GetDocFileName() );
292  m_KeywordsCtrl->SetValue( parent_alias->GetKeyWords() );
293 }
294 
295 
297 {
298  if( m_PartAliasListCtrl->GetCount() == 0 )
299  return;
300 
301  if( m_PartAliasListCtrl->FindString( m_Parent->GetAliasName() ) != wxNOT_FOUND )
302  {
303  DisplayErrorMessage( this, _( "Delete All can be done only when editing the main symbol." ) );
304  return;
305  }
306 
307  if( IsOK( this, _( "Remove all aliases from list?" ) ) )
308  m_PartAliasListCtrl->Clear();
309 }
310 
311 
313 {
314  int sel = m_PartAliasListCtrl->GetSelection();
315 
316  if( sel == wxNOT_FOUND )
317  return;
318 
319  wxString aliasname = m_PartAliasListCtrl->GetString( sel );
320 
321  if( aliasname.CmpNoCase( m_Parent->GetAliasName() ) == 0 )
322  {
323  wxString msg;
324  msg.Printf( _( "Current alias \"%s\" cannot be edited." ), GetChars( aliasname ) );
325  DisplayError( this, msg );
326  return;
327  }
328 
329  wxTextEntryDialog dlg( this, _( "New Alias:" ), _( "Symbol alias:" ), aliasname );
330 
331  if( dlg.ShowModal() != wxID_OK )
332  return; // cancelled by user
333 
334  aliasname = dlg.GetValue( );
335  aliasname.Replace( wxT( " " ), wxT( "_" ) );
336 
337  if( checkNewAlias( aliasname) )
338  m_PartAliasListCtrl->SetString( sel, aliasname );
339 }
340 
341 
343 {
344  wxString aliasname;
345 
346  wxTextEntryDialog dlg( this, _( "New Alias:" ), _( "Symbol alias:" ), aliasname );
347 
348  if( dlg.ShowModal() != wxID_OK )
349  return; // cancelled by user
350 
351  aliasname = dlg.GetValue( );
352  aliasname.Replace( wxT( " " ), wxT( "_" ) );
353 
354  if( checkNewAlias( aliasname ) )
355  m_PartAliasListCtrl->Append( aliasname );
356 }
357 
358 
360 {
361  if( aliasname.IsEmpty() )
362  return false;
363 
364  if( m_PartAliasListCtrl->FindString( aliasname ) != wxNOT_FOUND )
365  {
366  wxString msg;
367  msg.Printf( _( "Alias \"%s\" already exists." ), GetChars( aliasname ) );
368  DisplayInfoMessage( this, msg );
369  return false;
370  }
371 
372  wxString library = m_Parent->GetCurLib();
373 
374  if( !library.empty() && Prj().SchSymbolLibTable()->LoadSymbol( library, aliasname ) != NULL )
375  {
376  wxString msg;
377  msg.Printf( _( "Symbol name \"%s\" already exists in library \"%s\"." ), aliasname, library );
378  DisplayErrorMessage( this, msg );
379  return false;
380  }
381 
382  return true;
383 }
384 
385 
387 {
388  int sel = m_PartAliasListCtrl->GetSelection();
389 
390  if( sel == wxNOT_FOUND )
391  return;
392 
393  wxString aliasname = m_PartAliasListCtrl->GetString( sel );
394 
395  if( aliasname.CmpNoCase( m_Parent->GetAliasName() ) == 0 )
396  {
397  wxString msg;
398  msg.Printf( _( "Current alias \"%s\" cannot be removed." ), GetChars( aliasname ) );
399  DisplayError( this, msg );
400  return;
401  }
402 
403  m_PartAliasListCtrl->Delete( sel );
404 }
405 
406 
407 /*
408  * Change the number of parts per package.
409  */
411 {
412  LIB_PART* part = m_Parent->GetCurPart();
413 
414  if( !part || part->GetUnitCount() == MaxUnit || MaxUnit < 1 )
415  return false;
416 
417  if( MaxUnit < part->GetUnitCount()
418  && !IsOK( this, _( "Delete extra parts from component?" ) ) )
419  return false;
420 
421  part->SetUnitCount( MaxUnit );
422  return true;
423 }
424 
425 
426 /*
427  * Set or clear the component alternate body style ( DeMorgan ).
428  */
430 {
431  LIB_PART* component = m_Parent->GetCurPart();
432 
433  if( component == NULL || ( m_Parent->GetShowDeMorgan() == component->HasConversion() ) )
434  return false;
435 
436  if( m_Parent->GetShowDeMorgan() )
437  {
438  if( !IsOK( this, _( "Add new pins for alternate body style ( DeMorgan ) to component?" ) ) )
439  return false;
440  }
441  else if( component->HasConversion() )
442  {
443  if( !IsOK( this, _( "Delete alternate body style (DeMorgan) draw items from component?" ) ) )
444  {
445  m_Parent->SetShowDeMorgan( true );
446  return false;
447  }
448  }
449 
450  component->SetConversion( m_Parent->GetShowDeMorgan() );
451  m_Parent->OnModify();
452 
453  return true;
454 }
455 
456 
458 {
459  PROJECT& prj = Prj();
460  SEARCH_STACK* search = prj.SchSearchS();
461 
462  wxString mask = wxT( "*" );
463  wxString docpath = prj.GetRString( PROJECT::DOC_PATH );
464 
465  if( !docpath )
466  docpath = search->LastVisitedPath( wxT( "doc" ) );
467 
468  wxString fullFileName = EDA_FILE_SELECTOR( _( "Doc Files" ),
469  docpath,
470  wxEmptyString,
471  wxEmptyString,
472  mask,
473  this,
474  wxFD_OPEN,
475  true );
476  if( fullFileName.IsEmpty() )
477  return;
478 
479  /* If the path is already in the library search paths
480  * list, just add the library name to the list. Otherwise, add
481  * the library name with the full or relative path.
482  * the relative path, when possible is preferable,
483  * because it preserve use of default libraries paths, when the path is a sub path of
484  * these default paths
485  */
486  wxFileName fn = fullFileName;
487 
488  prj.SetRString( PROJECT::DOC_PATH, fn.GetPath() );
489 
490  wxString filename = search->FilenameWithRelativePathInSearchList(
491  fullFileName, wxPathOnly( Prj().GetProjectFullName() ) );
492 
493  // Filenames are always stored in unix like mode, ie separator "\" is stored as "/"
494  // to ensure files are identical under unices and windows
495 #ifdef __WINDOWS__
496  filename.Replace( wxT( "\\" ), wxT( "/" ) );
497 #endif
498  m_DocfileCtrl->SetValue( filename );
499 }
500 
501 
503 {
504  if( IsOK( this, _( "OK to delete the footprint filter list ?" ) ) )
505  {
506  m_FootprintFilterListBox->Clear();
507  m_ButtonDeleteAllFootprintFilter->Enable( false );
508  m_ButtonDeleteOneFootprintFilter->Enable( false );
509  m_buttonEditOneFootprintFilter->Enable( false );
510  }
511 }
512 
513 
514 /* Add a new name to the footprint filter list box
515  * Obvioulsy, cannot be void
516  */
518 {
519  wxString Line;
520  LIB_PART* component = m_Parent->GetCurPart();
521 
522  if( component == NULL )
523  return;
524 
525  wxTextEntryDialog dlg( this, _( "Add Footprint Filter" ), _( "Footprint Filter" ), Line );
526  if( dlg.ShowModal() != wxID_OK )
527  return; // cancelled by user
528 
529  Line = dlg.GetValue();
530  Line.Replace( wxT( " " ), wxT( "_" ) );
531 
532  if( Line.IsEmpty() )
533  return;
534 
535  /* test for an existing name: */
536  int index = m_FootprintFilterListBox->FindString( Line );
537 
538  if( index != wxNOT_FOUND )
539  {
540  wxString msg;
541 
542  msg.Printf( _( "Foot print filter \"%s\" is already defined." ), GetChars( Line ) );
543  DisplayError( this, msg );
544  return;
545  }
546 
547  m_FootprintFilterListBox->Append( Line );
548  m_ButtonDeleteAllFootprintFilter->Enable( true );
549  m_ButtonDeleteOneFootprintFilter->Enable( true );
550  m_buttonEditOneFootprintFilter->Enable( true );
551 }
552 
553 
555 {
556  LIB_PART* component = m_Parent->GetCurPart();
557  int ii = m_FootprintFilterListBox->GetSelection();
558 
559  m_FootprintFilterListBox->Delete( ii );
560 
561  if( !component || ( m_FootprintFilterListBox->GetCount() == 0 ) )
562  {
563  m_ButtonDeleteAllFootprintFilter->Enable( false );
564  m_ButtonDeleteOneFootprintFilter->Enable( false );
565  m_buttonEditOneFootprintFilter->Enable( false );
566  }
567 }
568 
570 {
571  int idx = m_FootprintFilterListBox->GetSelection();
572 
573  if( idx < 0 )
574  return;
575 
576  wxString filter = m_FootprintFilterListBox->GetStringSelection();
577 
578  wxTextEntryDialog dlg( this, wxEmptyString, _( "Edit footprint filter" ), filter );
579 
580  if( dlg.ShowModal() != wxID_OK )
581  return; // Aborted by user
582 
583  filter = dlg.GetValue();
584 
585  if( filter.IsEmpty() )
586  return; // do not accept blank filter.
587 
588  m_FootprintFilterListBox->SetString( idx, filter );
589 }
590 
591 
593 {
594  if( m_SelNumberOfUnits->GetValue() <= 1 )
595  m_OptionPartsLocked->Enable( false );
596  else
597  m_OptionPartsLocked->Enable( true );
598 }
bool UnitsLocked() const
Check whether part units are interchangeable.
Part library alias object definition.
void SetConversion(bool aSetConvert)
Set or clear the alternate body style (DeMorgan) for the part.
void SetPower()
DIALOG_EDIT_COMPONENT_IN_LIBRARY(LIB_EDIT_FRAME *parent)
Constructors.
Class PROJECT holds project specific data.
Definition: project.h:56
int GetPinNameOffset()
bool HasConversion() const
Test if part has more than one body conversion type (DeMorgan).
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Function DisplayErrorMessage displays an error message with aMessage.
Definition: confirm.cpp:88
This file is part of the common library TODO brief description.
bool IsPower() const
LIB_ALIAS * GetAlias(size_t aIndex)
This file is part of the common library.
void CopyDocFromRootToAlias(wxCommandEvent &event) override
void AddFootprintFilter(wxCommandEvent &event) override
void AddAliasOfPart(wxCommandEvent &event) override
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
void OnUpdateInterchangeableUnits(wxUpdateUIEvent &event) override
void OnModify()
Must be called after a schematic change in order to set the "modify" flag of the current screen...
#define MAX_UNIT_COUNT_PER_PACKAGE
The maximum number of units per package.
Definition: eeschema_id.h:48
Class SEARCH_STACK looks for files in a number of places.
Definition: search_stack.h:41
void DeleteAliasOfPart(wxCommandEvent &event) override
void SetDocFileName(const wxString &aDocFileName)
void SetShowDeMorgan(bool show)
void OnCancelClick(wxCommandEvent &event) override
void OnOkClick(wxCommandEvent &event) override
bool ShowPinNames()
PROJECT & Prj() const
Function Prj returns a reference to the PROJECT "associated with" this KIWAY.
bool ShowPinNumbers()
LIB_PART * GetCurPart() const
Return the current part being edited or NULL if none selected.
void SetUnitCount(int count)
Set the units per part count.
wxString GetDescription() const
wxString EDA_FILE_SELECTOR(const wxString &aTitle, const wxString &aPath, const wxString &aFileName, const wxString &aExtension, const wxString &aWildcard, wxWindow *aParent, int aStyle, const bool aKeepWorkingDirectory, const wxPoint &aPosition, wxString *aMruPath)
Function EDA_FILE_SELECTOR.
Definition: gestfich.cpp:82
Define a library symbol object.
void EditOneFootprintFilter(wxCommandEvent &event) override
wxString GetCurLib() const
The nickname of the current library being edited and empty string if none.
void EditAliasOfPart(wxCommandEvent &event) override
void DeleteAllFootprintFilter(wxCommandEvent &event) override
void SetDescription(const wxString &aDescription)
wxArrayString & GetFootprints()
void DeleteOneFootprintFilter(wxCommandEvent &event) override
VTBL_ENTRY void SetRString(RSTRING_T aStringId, const wxString &aString)
Function SetRString stores a "retained string", which is any session and project specific string iden...
Definition: project.cpp:171
int GetUnitCount() const
wxString GetDocFileName() const
void SetAliases(const wxArrayString &aAliasList)
const wxString & GetAliasName()
VTBL_ENTRY const wxString & GetRString(RSTRING_T aStringId)
Function GetRString returns a "retained string", which is any session and project specific string ide...
Definition: project.cpp:186
The symbol library editor main window.
static const wxChar * GetChars(const wxString &s)
Function GetChars returns a wxChar* to the actual wxChar* data within a wxString, and is helpful for ...
Definition: macros.h:92
see class PGM_BASE
bool GetShowDeMorgan()
void Format(OUTPUTFORMATTER *out, int aNestLevel, int aCtl, CPTREE &aTree)
Function Format outputs a PTREE into s-expression format via an OUTPUTFORMATTER derivative.
Definition: ptree.cpp:205
void SetShowPinNumbers(bool aShow)
Set or clear the pin number visibility flag.
void SetKeyWords(const wxString &aKeyWords)
const wxString LastVisitedPath(const wxString &aSubPathToSearch=wxEmptyString)
Function LastVisitedPath is a quirky function inherited from old code that seems to serve particular ...
void SaveCopyInUndoList(EDA_ITEM *ItemToCopy, UNDO_REDO_T undoType=UR_LIBEDIT)
Create a copy of the current component, and save it in the undo list.
wxArrayString GetAliasNames(bool aIncludeRoot=true) const
void DeleteAllAliasOfPart(wxCommandEvent &event) override
void BrowseAndSelectDocFile(wxCommandEvent &event) override
The common library.
Class DIALOG_EDIT_COMPONENT_IN_LIBRARY_BASE.
const wxString & GetName() const
Definition of class LIB_EDIT_FRAME.
Definition for part library class.
void SetShowPinNames(bool aShow)
Set or clear the pin name visibility flag.
void SetPinNameOffset(int aOffset)
Set the offset in mils of the pin name text from the pin symbol.
void DisplayInfoMessage(wxWindow *aParent, const wxString &aMessage, const wxString &aExtraInfo)
Function DisplayInfoMessage displays an informational message box with aMessage.
Definition: confirm.cpp:105
wxString GetKeyWords() const
void DisplayError(wxWindow *parent, const wxString &text, int displaytime)
Function DisplayError displays an error or warning message box with aMessage.
Definition: confirm.cpp:74
bool IsOK(wxWindow *aParent, const wxString &aMessage)
Function IsOK displays a yes/no dialog with aMessage and returns the user response.
Definition: confirm.cpp:122
wxString FilenameWithRelativePathInSearchList(const wxString &aFullFilename, const wxString &aBaseDir)
Function FilenameWithRelativePathInSearchList returns the shortest possible path which can be use lat...
void LockUnits(bool aLockUnits)
Set interchangeable the property for part units.
void SetNormal()