1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2004-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2008 Wayne Stambaugh <stambaughw@gmail.com>
6  * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, you may find one here:
20  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21  * or you may search the http://www.gnu.org website for the version 2 license,
22  * or you may write to the Free Software Foundation, Inc.,
23  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
24  */
25 
26 #include <sch_draw_panel.h>
27 #include <plotters/plotter.h>
28 #include <sch_screen.h>
29 #include <richio.h>
30 #include <general.h>
31 #include <template_fieldnames.h>
32 #include <transform.h>
33 #include <symbol_library.h>
34 #include <lib_pin.h>
35 #include <settings/color_settings.h>
36 #include <lib_shape.h>
37 
38 // the separator char between the subpart id and the reference
39 // 0 (no separator) or '.' or some other character
40 int LIB_SYMBOL::m_subpartIdSeparator = 0;
41 
42 // the ascii char value to calculate the subpart symbol id from the part number:
43 // 'A' or '1' usually. (to print U1.A or U1.1)
44 // if this a digit, a number is used as id symbol
45 int LIB_SYMBOL::m_subpartFirstId = 'A';
46 
47 
GetSearchText()48 wxString LIB_SYMBOL::GetSearchText()
49 {
50     // Matches are scored by offset from front of string, so inclusion of this spacer
51     // discounts matches found after it.
52     static const wxString discount( wxT( "        " ) );
53 
54     wxString  text = GetKeyWords() + discount + GetDescription();
55     wxString  footprint = GetFootprintField().GetText();
56 
57     if( !footprint.IsEmpty() )
58     {
59         text += discount + footprint;
60     }
61 
62     return text;
63 }
64 
65 
operator <(const LIB_SYMBOL & aItem1,const LIB_SYMBOL & aItem2)66 bool operator<( const LIB_SYMBOL& aItem1, const LIB_SYMBOL& aItem2 )
67 {
68     return aItem1.GetName() < aItem2.GetName();
69 }
70 
71 
72 /// http://www.boost.org/doc/libs/1_55_0/libs/smart_ptr/sp_techniques.html#weak_without_shared
73 struct null_deleter
74 {
operator ()null_deleter75     void operator()(void const *) const
76     {
77     }
78 };
79 
80 
LIB_SYMBOL(const wxString & aName,LIB_SYMBOL * aParent,SYMBOL_LIB * aLibrary)81 LIB_SYMBOL::LIB_SYMBOL( const wxString& aName, LIB_SYMBOL* aParent, SYMBOL_LIB* aLibrary ) :
82     EDA_ITEM( LIB_SYMBOL_T ),
83     m_me( this, null_deleter() ),
84     m_includeInBom( true ),
85     m_includeOnBoard( true )
86 {
87     m_lastModDate    = 0;
88     m_unitCount      = 1;
89     m_pinNameOffset  = Mils2iu( DEFAULT_PIN_NAME_OFFSET );
90     m_options        = ENTRY_NORMAL;
91     m_unitsLocked    = false;
92     m_showPinNumbers = true;
93     m_showPinNames   = true;
94 
95     // Add the MANDATORY_FIELDS in RAM only.  These are assumed to be present
96     // when the field editors are invoked.
97     m_drawings[LIB_FIELD_T].reserve( 4 );
98     m_drawings[LIB_FIELD_T].push_back( new LIB_FIELD( this, VALUE_FIELD ) );
99     m_drawings[LIB_FIELD_T].push_back( new LIB_FIELD( this, REFERENCE_FIELD ) );
100     m_drawings[LIB_FIELD_T].push_back( new LIB_FIELD( this, FOOTPRINT_FIELD ) );
101     m_drawings[LIB_FIELD_T].push_back( new LIB_FIELD( this, DATASHEET_FIELD ) );
102 
103     SetName( aName );
104 
105     if( aParent )
106         SetParent( aParent );
107 
108     SetLib( aLibrary );
109 }
110 
111 
LIB_SYMBOL(const LIB_SYMBOL & aSymbol,SYMBOL_LIB * aLibrary)112 LIB_SYMBOL::LIB_SYMBOL( const LIB_SYMBOL& aSymbol, SYMBOL_LIB* aLibrary ) :
113     EDA_ITEM( aSymbol ),
114     m_me( this, null_deleter() )
115 {
116     LIB_ITEM* newItem;
117 
118     m_library        = aLibrary;
119     m_name           = aSymbol.m_name;
120     m_fpFilters      = wxArrayString( aSymbol.m_fpFilters );
121     m_unitCount      = aSymbol.m_unitCount;
122     m_unitsLocked    = aSymbol.m_unitsLocked;
123     m_pinNameOffset  = aSymbol.m_pinNameOffset;
124     m_showPinNumbers = aSymbol.m_showPinNumbers;
125     m_includeInBom   = aSymbol.m_includeInBom;
126     m_includeOnBoard = aSymbol.m_includeOnBoard;
127     m_showPinNames   = aSymbol.m_showPinNames;
128     m_lastModDate    = aSymbol.m_lastModDate;
129     m_options        = aSymbol.m_options;
130     m_libId          = aSymbol.m_libId;
131     m_description    = aSymbol.m_description;
132     m_keyWords       = aSymbol.m_keyWords;
133 
134     ClearSelected();
135 
136     for( const LIB_ITEM& oldItem : aSymbol.m_drawings )
137     {
138         if( ( oldItem.GetFlags() & ( IS_NEW | STRUCT_DELETED ) ) != 0 )
139             continue;
140 
141         try
142         {
143             newItem = (LIB_ITEM*) oldItem.Clone();
144             newItem->ClearSelected();
145             newItem->SetParent( this );
146             m_drawings.push_back( newItem );
147         }
148         catch( ... )
149         {
150             wxFAIL_MSG( "Failed to clone LIB_ITEM." );
151         }
152     }
153 
154     LIB_SYMBOL_SPTR parent = aSymbol.m_parent.lock();
155 
156     if( parent )
157         SetParent( parent.get() );
158 }
159 
160 
~LIB_SYMBOL()161 LIB_SYMBOL::~LIB_SYMBOL()
162 {
163 }
164 
165 
operator =(const LIB_SYMBOL & aSymbol)166 const LIB_SYMBOL& LIB_SYMBOL::operator=( const LIB_SYMBOL& aSymbol )
167 {
168     if( &aSymbol == this )
169         return aSymbol;
170 
171     LIB_ITEM* newItem;
172 
173     m_library        = aSymbol.m_library;
174     m_name           = aSymbol.m_name;
175     m_fpFilters      = wxArrayString( aSymbol.m_fpFilters );
176     m_unitCount      = aSymbol.m_unitCount;
177     m_unitsLocked    = aSymbol.m_unitsLocked;
178     m_pinNameOffset  = aSymbol.m_pinNameOffset;
179     m_showPinNumbers = aSymbol.m_showPinNumbers;
180     m_showPinNames   = aSymbol.m_showPinNames;
181     m_includeInBom   = aSymbol.m_includeInBom;
182     m_includeOnBoard = aSymbol.m_includeOnBoard;
183     m_lastModDate    = aSymbol.m_lastModDate;
184     m_options        = aSymbol.m_options;
185     m_libId          = aSymbol.m_libId;
186     m_description    = aSymbol.m_description;
187     m_keyWords       = aSymbol.m_keyWords;
188 
189     m_drawings.clear();
190 
191     for( const LIB_ITEM& oldItem : aSymbol.m_drawings )
192     {
193         if( ( oldItem.GetFlags() & ( IS_NEW | STRUCT_DELETED ) ) != 0 )
194             continue;
195 
196         newItem = (LIB_ITEM*) oldItem.Clone();
197         newItem->SetParent( this );
198         m_drawings.push_back( newItem );
199     }
200 
201     m_drawings.sort();
202 
203     LIB_SYMBOL_SPTR parent = aSymbol.m_parent.lock();
204 
205     if( parent )
206         SetParent( parent.get() );
207 
208     return *this;
209 }
210 
211 
Compare(const LIB_SYMBOL & aRhs,LIB_ITEM::COMPARE_FLAGS aCompareFlags) const212 int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const
213 {
214     if( m_me == aRhs.m_me )
215         return 0;
216 
217     int retv = m_name.Cmp( aRhs.m_name );
218 
219     if( retv )
220         return retv;
221 
222     retv = m_libId.compare( aRhs.m_libId );
223 
224     if( retv )
225         return retv;
226 
227     if( m_parent.lock() < aRhs.m_parent.lock() )
228         return -1;
229 
230     if( m_parent.lock() > aRhs.m_parent.lock() )
231         return 1;
232 
233     if( m_options != aRhs.m_options )
234         return ( m_options == ENTRY_NORMAL ) ? -1 : 1;
235 
236     if( m_unitCount != aRhs.m_unitCount )
237         return m_unitCount - aRhs.m_unitCount;
238 
239     if( m_drawings.size() != aRhs.m_drawings.size() )
240         return m_drawings.size() - aRhs.m_drawings.size();
241 
242     LIB_ITEMS_CONTAINER::CONST_ITERATOR lhsItemIt = m_drawings.begin();
243     LIB_ITEMS_CONTAINER::CONST_ITERATOR rhsItemIt = aRhs.m_drawings.begin();
244 
245     while( lhsItemIt != m_drawings.end() )
246     {
247         const LIB_ITEM* lhsItem = static_cast<const LIB_ITEM*>( &(*lhsItemIt) );
248         const LIB_ITEM* rhsItem = static_cast<const LIB_ITEM*>( &(*rhsItemIt) );
249 
250         wxCHECK( lhsItem && rhsItem, lhsItem - rhsItem );
251 
252         if( lhsItem->Type() != rhsItem->Type() )
253             return lhsItem->Type() - rhsItem->Type();
254 
255         // Non-mandatory fields are a special case.  They can have different ordinal numbers
256         // and are compared separately below.
257         if( lhsItem->Type() == LIB_FIELD_T )
258         {
259             const LIB_FIELD* lhsField = static_cast<const LIB_FIELD*>( lhsItem );
260 
261             if( lhsField->IsMandatory() )
262                 retv = lhsItem->compare( *rhsItem, aCompareFlags );
263         }
264         else
265         {
266             retv = lhsItem->compare( *rhsItem, aCompareFlags );
267         }
268 
269         if( retv )
270             return retv;
271 
272         ++lhsItemIt;
273         ++rhsItemIt;
274     }
275 
276     // Compare the optional fields.
277     for( const LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
278     {
279         const LIB_FIELD* field = dynamic_cast<const LIB_FIELD*>( &item );
280 
281         wxCHECK2( field, continue );
282 
283         // Mandatory fields were already compared above.
284         if( field->IsMandatory() )
285             continue;
286 
287         const LIB_FIELD* foundField = aRhs.FindField( field->GetName() );
288 
289         if( foundField == nullptr )
290             return 1;
291 
292         retv = item.compare( static_cast<const LIB_ITEM&>( *foundField ), aCompareFlags );
293 
294         if( retv )
295             return retv;
296     }
297 
298     if( m_fpFilters.GetCount() != aRhs.m_fpFilters.GetCount() )
299         return m_fpFilters.GetCount() - aRhs.m_fpFilters.GetCount();
300 
301     for( size_t i = 0; i < m_fpFilters.GetCount(); i++ )
302     {
303         retv = m_fpFilters[i].Cmp( aRhs.m_fpFilters[i] );
304 
305         if( retv )
306             return retv;
307     }
308 
309     retv = m_description.Cmp( aRhs.m_description );
310 
311     if( retv )
312         return retv;
313 
314     retv = m_keyWords.Cmp( aRhs.m_keyWords );
315 
316     if( retv )
317         return retv;
318 
319     if( m_pinNameOffset != aRhs.m_pinNameOffset )
320         return m_pinNameOffset - aRhs.m_pinNameOffset;
321 
322     if( m_unitsLocked != aRhs.m_unitsLocked )
323         return ( m_unitsLocked ) ? 1 : -1;
324 
325     if( m_showPinNames != aRhs.m_showPinNames )
326         return ( m_showPinNames ) ? 1 : -1;
327 
328     if( m_showPinNumbers != aRhs.m_showPinNumbers )
329         return ( m_showPinNumbers ) ? 1 : -1;
330 
331     if( m_includeInBom != aRhs.m_includeInBom )
332         return ( m_includeInBom ) ? 1 : -1;
333 
334     if( m_includeOnBoard != aRhs.m_includeOnBoard )
335         return ( m_includeOnBoard ) ? 1 : -1;
336 
337     return 0;
338 }
339 
340 
GetUnitReference(int aUnit)341 wxString LIB_SYMBOL::GetUnitReference( int aUnit )
342 {
343     return LIB_SYMBOL::SubReference( aUnit, false );
344 }
345 
346 
SetName(const wxString & aName)347 void LIB_SYMBOL::SetName( const wxString& aName )
348 {
349     m_name = aName;
350     m_libId.SetLibItemName( aName );
351 
352     GetValueField().SetText( aName );
353 }
354 
355 
SetParent(LIB_SYMBOL * aParent)356 void LIB_SYMBOL::SetParent( LIB_SYMBOL* aParent )
357 {
358     if( aParent )
359         m_parent = aParent->SharedPtr();
360     else
361         m_parent.reset();
362 }
363 
364 
Flatten() const365 std::unique_ptr< LIB_SYMBOL > LIB_SYMBOL::Flatten() const
366 {
367     std::unique_ptr< LIB_SYMBOL > retv;
368 
369     if( IsAlias() )
370     {
371         LIB_SYMBOL_SPTR parent = m_parent.lock();
372 
373         wxCHECK_MSG( parent, retv,
374                      wxString::Format( "Parent of derived symbol '%s' undefined", m_name ) );
375 
376         // Copy the parent.
377         retv.reset( new LIB_SYMBOL( *parent.get() ) );
378 
379         retv->m_name = m_name;
380         retv->SetLibId( m_libId );
381 
382         // Now add the inherited part mandatory field (this) information.
383         for( int i = 0; i < MANDATORY_FIELDS; i++ )
384         {
385             wxString tmp = GetFieldById( i )->GetText();
386 
387             // If the field isn't defined then inherit the parent field value.
388             if( tmp.IsEmpty() )
389                 retv->GetFieldById( i )->SetText( parent->GetFieldById( i )->GetText() );
390             else
391                 *retv->GetFieldById( i ) = *GetFieldById( i );
392         }
393 
394         // Grab all the rest of derived symbol fields.
395         for( const LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
396         {
397             const LIB_FIELD* aliasField = dynamic_cast<const LIB_FIELD*>( &item );
398 
399             wxCHECK2( aliasField, continue );
400 
401             // Mandatory fields were already resolved.
402             if( aliasField->IsMandatory() )
403                 continue;
404 
405             LIB_FIELD* newField = new LIB_FIELD( *aliasField );
406             newField->SetParent( retv.get() );
407 
408             LIB_FIELD* parentField = retv->FindField( aliasField->GetName() );
409 
410             if( !parentField )  // Derived symbol field does not exist in parent symbol.
411             {
412                 retv->AddDrawItem( newField );
413             }
414             else                // Derived symbol field overrides the parent symbol field.
415             {
416                 retv->RemoveDrawItem( parentField );
417                 retv->AddDrawItem( newField );
418             }
419         }
420 
421         retv->SetKeyWords( m_keyWords.IsEmpty() ? parent->GetKeyWords() : m_keyWords );
422         retv->SetDescription( m_description.IsEmpty() ? parent->GetDescription() : m_description );
423         retv->SetFPFilters( m_fpFilters.IsEmpty() ? parent->GetFPFilters() : m_fpFilters );
424         retv->UpdateFieldOrdinals();
425     }
426     else
427     {
428         retv.reset( new LIB_SYMBOL( *this ) );
429     }
430 
431     return retv;
432 }
433 
434 
GetLibraryName() const435 const wxString LIB_SYMBOL::GetLibraryName() const
436 {
437     if( m_library )
438         return m_library->GetName();
439 
440     return m_libId.GetLibNickname();
441 }
442 
443 
IsPower() const444 bool LIB_SYMBOL::IsPower() const
445 {
446     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
447         return parent->m_options == ENTRY_POWER;
448 
449     return m_options == ENTRY_POWER;
450 }
451 
452 
SetPower()453 void LIB_SYMBOL::SetPower()
454 {
455     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
456         parent->m_options = ENTRY_POWER;
457 
458     m_options = ENTRY_POWER;
459 }
460 
461 
IsNormal() const462 bool LIB_SYMBOL::IsNormal() const
463 {
464     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
465         return parent->m_options == ENTRY_NORMAL;
466 
467     return m_options == ENTRY_NORMAL;
468 }
469 
470 
SetNormal()471 void LIB_SYMBOL::SetNormal()
472 {
473     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
474         parent->m_options = ENTRY_NORMAL;
475 
476     m_options = ENTRY_NORMAL;
477 }
478 
479 
SubReference(int aUnit,bool aAddSeparator)480 wxString LIB_SYMBOL::SubReference( int aUnit, bool aAddSeparator )
481 {
482     wxString subRef;
483 
484     if( m_subpartIdSeparator != 0 && aAddSeparator )
485         subRef << wxChar( m_subpartIdSeparator );
486 
487     if( m_subpartFirstId >= '0' && m_subpartFirstId <= '9' )
488         subRef << aUnit;
489     else
490     {
491         // use letters as notation. To allow more than 26 units, the sub ref
492         // use one letter if letter = A .. Z or a ... z, and 2 letters otherwise
493         // first letter is expected to be 'A' or 'a' (i.e. 26 letters are available)
494         int u;
495         aUnit -= 1;     // Unit number starts to 1. now to 0.
496 
497         while( aUnit >= 26 )    // more than one letter are needed
498         {
499             u = aUnit / 26;
500             subRef << wxChar( m_subpartFirstId + u -1 );
501             aUnit %= 26;
502         }
503 
504         u = m_subpartFirstId + aUnit;
505         subRef << wxChar( u );
506     }
507 
508     return subRef;
509 }
510 
511 
Print(const RENDER_SETTINGS * aSettings,const wxPoint & aOffset,int aUnit,int aConvert,const LIB_SYMBOL_OPTIONS & aOpts)512 void LIB_SYMBOL::Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset,
513                         int aUnit, int aConvert, const LIB_SYMBOL_OPTIONS& aOpts )
514 {
515     /* draw background for filled items using background option
516      * Solid lines will be drawn after the background
517      * Note also, background is not drawn when printing in black and white
518      */
519     if( !GetGRForceBlackPenState() )
520     {
521         for( LIB_ITEM& item : m_drawings )
522         {
523             if( item.Type() == LIB_SHAPE_T )
524             {
525                 LIB_SHAPE& shape = static_cast<LIB_SHAPE&>( item );
526 
527                 // Do not draw items not attached to the current part
528                 if( aUnit && shape.m_unit && ( shape.m_unit != aUnit ) )
529                     continue;
530 
531                 if( aConvert && shape.m_convert && ( shape.m_convert != aConvert ) )
532                     continue;
533 
534                 if( shape.GetFillType() == FILL_T::FILLED_WITH_BG_BODYCOLOR )
535                     shape.Print( aSettings, aOffset, (void*) false, aOpts.transform );
536             }
537         }
538     }
539 
540     for( LIB_ITEM& item : m_drawings )
541     {
542         // Do not draw items not attached to the current part
543         if( aUnit && item.m_unit && ( item.m_unit != aUnit ) )
544             continue;
545 
546         if( aConvert && item.m_convert && ( item.m_convert != aConvert ) )
547             continue;
548 
549         if( item.Type() == LIB_FIELD_T )
550         {
551             LIB_FIELD& field = static_cast<LIB_FIELD&>( item );
552 
553             if( field.IsVisible() && !aOpts.draw_visible_fields )
554                 continue;
555 
556             if( !field.IsVisible() && !aOpts.draw_hidden_fields )
557                 continue;
558         }
559 
560         if( item.Type() == LIB_PIN_T )
561         {
562             item.Print( aSettings, aOffset, (void*) &aOpts, aOpts.transform );
563         }
564         else if( item.Type() == LIB_FIELD_T )
565         {
566             item.Print( aSettings, aOffset, (void*) NULL, aOpts.transform );
567         }
568         else if( item.Type() == LIB_SHAPE_T )
569         {
570             LIB_SHAPE& shape = static_cast<LIB_SHAPE&>( item );
571             bool       forceNoFill = shape.GetFillType() == FILL_T::FILLED_WITH_BG_BODYCOLOR;
572 
573             shape.Print( aSettings, aOffset, (void*) forceNoFill, aOpts.transform );
574         }
575         else
576         {
577             item.Print( aSettings, aOffset, (void*) false, aOpts.transform );
578         }
579     }
580 }
581 
582 
Plot(PLOTTER * aPlotter,int aUnit,int aConvert,const wxPoint & aOffset,const TRANSFORM & aTransform) const583 void LIB_SYMBOL::Plot( PLOTTER* aPlotter, int aUnit, int aConvert, const wxPoint& aOffset,
584                        const TRANSFORM& aTransform ) const
585 {
586     wxASSERT( aPlotter != nullptr );
587 
588     aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_DEVICE ) );
589 
590     // draw background for filled items using background option
591     // Solid lines will be drawn after the background
592     for( const LIB_ITEM& item : m_drawings )
593     {
594         if( item.Type() != LIB_SHAPE_T )
595             continue;
596 
597         const LIB_SHAPE& shape = static_cast<const LIB_SHAPE&>( item );
598 
599         // Do not draw items not attached to the current part
600         if( aUnit && shape.m_unit && ( shape.m_unit != aUnit ) )
601             continue;
602 
603         if( aConvert && shape.m_convert && ( shape.m_convert != aConvert ) )
604             continue;
605 
606         if( shape.GetFillType() == FILL_T::FILLED_WITH_BG_BODYCOLOR && aPlotter->GetColorMode() )
607             shape.Plot( aPlotter, aOffset, true, aTransform );
608     }
609 
610     // Not filled items and filled shapes are now plotted
611     // Items that have BG fills only get re-stroked to ensure the edges are in the foreground
612     for( const LIB_ITEM& item : m_drawings )
613     {
614         // Lib Fields are not plotted here, because this plot function
615         // is used to plot schematic items, which have they own fields
616         if( item.Type() == LIB_FIELD_T )
617             continue;
618 
619         if( aUnit && item.m_unit && ( item.m_unit != aUnit ) )
620             continue;
621 
622         if( aConvert && item.m_convert && ( item.m_convert != aConvert ) )
623             continue;
624 
625         bool forceNoFill = false;
626 
627         if( item.Type() == LIB_SHAPE_T )
628         {
629             const LIB_SHAPE& shape = static_cast<const LIB_SHAPE&>( item );
630             forceNoFill = shape.GetFillType() == FILL_T::FILLED_WITH_BG_BODYCOLOR;
631         }
632 
633         item.Plot( aPlotter, aOffset, !forceNoFill, aTransform );
634     }
635 }
636 
637 
PlotLibFields(PLOTTER * aPlotter,int aUnit,int aConvert,const wxPoint & aOffset,const TRANSFORM & aTransform)638 void LIB_SYMBOL::PlotLibFields( PLOTTER* aPlotter, int aUnit, int aConvert,
639                                 const wxPoint& aOffset, const TRANSFORM& aTransform )
640 {
641     wxASSERT( aPlotter != nullptr );
642 
643     aPlotter->SetColor( aPlotter->RenderSettings()->GetLayerColor( LAYER_FIELDS ) );
644     bool fill = aPlotter->GetColorMode();
645 
646     for( LIB_ITEM& item : m_drawings )
647     {
648         if( item.Type() != LIB_FIELD_T )
649             continue;
650 
651         if( aUnit && item.m_unit && ( item.m_unit != aUnit ) )
652             continue;
653 
654         if( aConvert && item.m_convert && ( item.m_convert != aConvert ) )
655             continue;
656 
657         LIB_FIELD& field = (LIB_FIELD&) item;
658 
659         // The reference is a special case: we should change the basic text
660         // to add '?' and the part id
661         wxString tmp = field.GetShownText();
662 
663         if( field.GetId() == REFERENCE_FIELD )
664         {
665             wxString text = field.GetFullText( aUnit );
666             field.SetText( text );
667         }
668 
669         item.Plot( aPlotter, aOffset, fill, aTransform );
670         field.SetText( tmp );
671     }
672 }
673 
674 
RemoveDrawItem(LIB_ITEM * aItem)675 void LIB_SYMBOL::RemoveDrawItem( LIB_ITEM* aItem )
676 {
677     wxASSERT( aItem != nullptr );
678 
679     // none of the MANDATORY_FIELDS may be removed in RAM, but they may be
680     // omitted when saving to disk.
681     if( aItem->Type() == LIB_FIELD_T )
682     {
683         if( static_cast<LIB_FIELD*>( aItem )->IsMandatory() )
684             return;
685     }
686 
687     LIB_ITEMS& items = m_drawings[ aItem->Type() ];
688 
689     for( LIB_ITEMS::iterator i = items.begin(); i != items.end(); i++ )
690     {
691         if( &*i == aItem )
692         {
693             items.erase( i );
694             SetModified();
695             break;
696         }
697     }
698 }
699 
700 
AddDrawItem(LIB_ITEM * aItem,bool aSort)701 void LIB_SYMBOL::AddDrawItem( LIB_ITEM* aItem, bool aSort )
702 {
703     wxCHECK( aItem, /* void */ );
704 
705     m_drawings.push_back( aItem );
706 
707     if( aSort )
708         m_drawings.sort();
709 }
710 
711 
GetNextDrawItem(const LIB_ITEM * aItem,KICAD_T aType)712 LIB_ITEM* LIB_SYMBOL::GetNextDrawItem( const LIB_ITEM* aItem, KICAD_T aType )
713 {
714     if( aItem == nullptr )
715     {
716         LIB_ITEMS_CONTAINER::ITERATOR it1 = m_drawings.begin( aType );
717 
718         return (it1 != m_drawings.end( aType ) ) ? &( *( m_drawings.begin( aType ) ) ) : nullptr;
719     }
720 
721     // Search for the last item, assume aItem is of type aType
722     wxASSERT( ( aType == TYPE_NOT_INIT ) || ( aType == aItem->Type() ) );
723     LIB_ITEMS_CONTAINER::ITERATOR it = m_drawings.begin( aType );
724 
725     while( ( it != m_drawings.end( aType ) ) && ( aItem != &( *it ) ) )
726         ++it;
727 
728     // Search the next item
729     if( it != m_drawings.end( aType ) )
730     {
731         ++it;
732 
733         if( it != m_drawings.end( aType ) )
734             return &( *it );
735     }
736 
737     return nullptr;
738 }
739 
740 
GetPins(LIB_PINS & aList,int aUnit,int aConvert) const741 void LIB_SYMBOL::GetPins( LIB_PINS& aList, int aUnit, int aConvert ) const
742 {
743     /* Notes:
744      * when aUnit == 0: no unit filtering
745      * when aConvert == 0: no convert (shape selection) filtering
746      * when m_unit == 0, the body item is common to units
747      * when m_convert == 0, the body item is common to shapes
748      */
749 
750     LIB_SYMBOL_SPTR                  parent = m_parent.lock();
751     const LIB_ITEMS_CONTAINER& drawItems = parent ? parent->m_drawings : m_drawings;
752 
753     for( const LIB_ITEM& item : drawItems[LIB_PIN_T] )
754     {
755         // Unit filtering:
756         if( aUnit && item.m_unit && ( item.m_unit != aUnit ) )
757             continue;
758 
759         // Shape filtering:
760         if( aConvert && item.m_convert && ( item.m_convert != aConvert ) )
761             continue;
762 
763         aList.push_back( (LIB_PIN*) &item );
764     }
765 }
766 
767 
GetPin(const wxString & aNumber,int aUnit,int aConvert) const768 LIB_PIN* LIB_SYMBOL::GetPin( const wxString& aNumber, int aUnit, int aConvert ) const
769 {
770     LIB_PINS pinList;
771 
772     GetPins( pinList, aUnit, aConvert );
773 
774     for( size_t i = 0; i < pinList.size(); i++ )
775     {
776         wxASSERT( pinList[i]->Type() == LIB_PIN_T );
777 
778         if( aNumber == pinList[i]->GetNumber() )
779             return pinList[i];
780     }
781 
782     return nullptr;
783 }
784 
785 
PinsConflictWith(const LIB_SYMBOL & aOtherPart,bool aTestNums,bool aTestNames,bool aTestType,bool aTestOrientation,bool aTestLength) const786 bool LIB_SYMBOL::PinsConflictWith( const LIB_SYMBOL& aOtherPart, bool aTestNums, bool aTestNames,
787                                    bool aTestType, bool aTestOrientation, bool aTestLength ) const
788 {
789     LIB_PINS thisPinList;
790     GetPins( thisPinList, /* aUnit */ 0, /* aConvert */ 0 );
791 
792     for( const LIB_PIN* eachThisPin : thisPinList )
793     {
794         wxASSERT( eachThisPin );
795         LIB_PINS otherPinList;
796         aOtherPart.GetPins( otherPinList, /* aUnit */ 0, /* aConvert */ 0 );
797         bool foundMatch = false;
798 
799         for( const LIB_PIN* eachOtherPin : otherPinList )
800         {
801             wxASSERT( eachOtherPin );
802 
803             // Same unit?
804             if( eachThisPin->GetUnit() != eachOtherPin->GetUnit() )
805                 continue;
806 
807             // Same body stype?
808             if( eachThisPin->GetConvert() != eachOtherPin->GetConvert() )
809                 continue;
810 
811             // Same position?
812             if( eachThisPin->GetPosition() != eachOtherPin->GetPosition() )
813                 continue;
814 
815             // Same number?
816             if( aTestNums && ( eachThisPin->GetNumber() != eachOtherPin->GetNumber() ) )
817                 continue;
818 
819             // Same name?
820             if( aTestNames && ( eachThisPin->GetName() != eachOtherPin->GetName() ) )
821                 continue;
822 
823             // Same electrical type?
824             if( aTestType && ( eachThisPin->GetType() != eachOtherPin->GetType() ) )
825                 continue;
826 
827             // Same orientation?
828             if( aTestOrientation
829               && ( eachThisPin->GetOrientation() != eachOtherPin->GetOrientation() ) )
830                 continue;
831 
832             // Same length?
833             if( aTestLength && ( eachThisPin->GetLength() != eachOtherPin->GetLength() ) )
834                 continue;
835 
836             foundMatch = true;
837             break;                    // Match found so search is complete.
838         }
839 
840         if( !foundMatch )
841         {
842             // This means there was not an identical (according to the arguments)
843             // pin at the same position in the other symbol.
844             return true;
845         }
846     }
847 
848     // The loop never gave up, so no conflicts were found.
849     return false;
850 }
851 
852 
GetUnitBoundingBox(int aUnit,int aConvert) const853 const EDA_RECT LIB_SYMBOL::GetUnitBoundingBox( int aUnit, int aConvert ) const
854 {
855     EDA_RECT bBox;
856     bool initialized = false;
857 
858     for( const LIB_ITEM& item : m_drawings )
859     {
860         if( item.m_unit > 0
861                 && m_unitCount > 1
862                 && aUnit > 0
863                 && aUnit != item.m_unit )
864         {
865             continue;
866         }
867 
868         if( item.m_convert > 0 && aConvert > 0 && aConvert != item.m_convert )
869             continue;
870 
871         if ( ( item.Type() == LIB_FIELD_T ) && !( ( LIB_FIELD& ) item ).IsVisible() )
872             continue;
873 
874         if( initialized )
875         {
876             bBox.Merge( item.GetBoundingBox() );
877         }
878         else
879         {
880             bBox = item.GetBoundingBox();
881             initialized = true;
882         }
883     }
884 
885     return bBox;
886 }
887 
888 
ViewGetLayers(int aLayers[],int & aCount) const889 void LIB_SYMBOL::ViewGetLayers( int aLayers[], int& aCount ) const
890 {
891     aCount      = 6;
892     aLayers[0]  = LAYER_DEVICE;
893     aLayers[1]  = LAYER_DEVICE_BACKGROUND;
894     aLayers[2]  = LAYER_REFERENCEPART;
895     aLayers[3]  = LAYER_VALUEPART;
896     aLayers[4]  = LAYER_FIELDS;
897     aLayers[5]  = LAYER_SELECTION_SHADOWS;
898 }
899 
900 
GetBodyBoundingBox(int aUnit,int aConvert,bool aIncludePins) const901 const EDA_RECT LIB_SYMBOL::GetBodyBoundingBox( int aUnit, int aConvert, bool aIncludePins ) const
902 {
903     EDA_RECT bbox;
904 
905     for( const LIB_ITEM& item : m_drawings )
906     {
907         if( item.m_unit > 0 && aUnit > 0 && aUnit != item.m_unit )
908             continue;
909 
910         if( item.m_convert > 0 && aConvert > 0 && aConvert != item.m_convert )
911             continue;
912 
913         if( item.Type() == LIB_FIELD_T )
914             continue;
915 
916         if( item.Type() == LIB_PIN_T )
917         {
918             const LIB_PIN& pin = static_cast<const LIB_PIN&>( item );
919 
920             // Note: the roots of the pins are always inlcuded for symbols that don't have a
921             // well-defined body.
922 
923             if( aIncludePins )
924                 bbox.Merge( pin.GetBoundingBox( false, true ) );
925             else
926                 bbox.Merge( pin.GetPinRoot() );
927         }
928         else
929         {
930             bbox.Merge( item.GetBoundingBox() );
931         }
932     }
933 
934     return bbox;
935 }
936 
937 
deleteAllFields()938 void LIB_SYMBOL::deleteAllFields()
939 {
940     m_drawings[ LIB_FIELD_T ].clear();
941 }
942 
943 
AddField(LIB_FIELD * aField)944 void LIB_SYMBOL::AddField( LIB_FIELD* aField )
945 {
946     AddDrawItem( aField );
947 }
948 
949 
SetFields(const std::vector<LIB_FIELD> & aFields)950 void LIB_SYMBOL::SetFields( const std::vector <LIB_FIELD>& aFields )
951 {
952     deleteAllFields();
953 
954     for( unsigned i=0;  i<aFields.size();  ++i )
955     {
956         // drawings is a ptr_vector, new and copy an object on the heap.
957         LIB_FIELD* field = new LIB_FIELD( aFields[i] );
958 
959         field->SetParent( this );
960         m_drawings.push_back( field );
961     }
962 
963     m_drawings.sort();
964 }
965 
966 
GetFields(std::vector<LIB_FIELD * > & aList)967 void LIB_SYMBOL::GetFields( std::vector<LIB_FIELD*>& aList )
968 {
969     // Grab the MANDATORY_FIELDS first, in expected order given by enum MANDATORY_FIELD_T
970     for( int id = 0; id < MANDATORY_FIELDS; ++id )
971         aList.push_back( GetFieldById( id ) );
972 
973     // Now grab all the rest of fields.
974     for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
975     {
976         LIB_FIELD* field = static_cast<LIB_FIELD*>( &item );
977 
978         if( !field->IsMandatory() )
979             aList.push_back( field );
980     }
981 }
982 
983 
GetFields(std::vector<LIB_FIELD> & aList)984 void LIB_SYMBOL::GetFields( std::vector<LIB_FIELD>& aList )
985 {
986     // Grab the MANDATORY_FIELDS first, in expected order given by enum MANDATORY_FIELD_T
987     for( int id = 0; id < MANDATORY_FIELDS; ++id )
988         aList.push_back( *GetFieldById( id ) );
989 
990     // Now grab all the rest of fields.
991     for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
992     {
993         LIB_FIELD* field = static_cast<LIB_FIELD*>( &item );
994 
995         if( !field->IsMandatory() )
996             aList.push_back( *field );
997     }
998 }
999 
1000 
GetFieldById(int aId) const1001 LIB_FIELD* LIB_SYMBOL::GetFieldById( int aId ) const
1002 {
1003     for( const LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
1004     {
1005         LIB_FIELD* field = ( LIB_FIELD* ) &item;
1006 
1007         if( field->GetId() == aId )
1008             return field;
1009     }
1010 
1011     return nullptr;
1012 }
1013 
1014 
FindField(const wxString & aFieldName)1015 LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName )
1016 {
1017     for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
1018     {
1019         if( static_cast<LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
1020             return static_cast<LIB_FIELD*>( &item );
1021     }
1022 
1023     return nullptr;
1024 }
1025 
1026 
FindField(const wxString & aFieldName) const1027 const LIB_FIELD* LIB_SYMBOL::FindField( const wxString& aFieldName ) const
1028 {
1029     for( const LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
1030     {
1031         if( static_cast<const LIB_FIELD*>( &item )->GetCanonicalName() == aFieldName )
1032             return static_cast<const LIB_FIELD*>( &item );
1033     }
1034 
1035     return nullptr;
1036 }
1037 
1038 
GetValueField()1039 LIB_FIELD& LIB_SYMBOL::GetValueField()
1040 {
1041     LIB_FIELD* field = GetFieldById( VALUE_FIELD );
1042     wxASSERT( field != nullptr );
1043     return *field;
1044 }
1045 
1046 
GetReferenceField()1047 LIB_FIELD& LIB_SYMBOL::GetReferenceField()
1048 {
1049     LIB_FIELD* field = GetFieldById( REFERENCE_FIELD );
1050     wxASSERT( field != nullptr );
1051     return *field;
1052 }
1053 
1054 
GetFootprintField()1055 LIB_FIELD& LIB_SYMBOL::GetFootprintField()
1056 {
1057     LIB_FIELD* field = GetFieldById( FOOTPRINT_FIELD );
1058     wxASSERT( field != nullptr );
1059     return *field;
1060 }
1061 
1062 
GetDatasheetField()1063 LIB_FIELD& LIB_SYMBOL::GetDatasheetField()
1064 {
1065     LIB_FIELD* field = GetFieldById( DATASHEET_FIELD );
1066     wxASSERT( field != nullptr );
1067     return *field;
1068 }
1069 
1070 
UpdateFieldOrdinals()1071 int LIB_SYMBOL::UpdateFieldOrdinals()
1072 {
1073     int retv = 0;
1074     int lastOrdinal = MANDATORY_FIELDS;
1075 
1076     for( LIB_ITEM& item : m_drawings[ LIB_FIELD_T ] )
1077     {
1078         LIB_FIELD* field = dynamic_cast<LIB_FIELD*>( &item );
1079 
1080         wxCHECK2( field, continue );
1081 
1082         // Mandatory fields were already resolved always have the same ordinal values.
1083         if( field->IsMandatory() )
1084             continue;
1085 
1086         if( field->GetId() != lastOrdinal )
1087         {
1088             field->SetId( lastOrdinal );
1089             retv += 1;
1090         }
1091 
1092         lastOrdinal += 1;
1093     }
1094 
1095     return retv;
1096 }
1097 
1098 
GetNextAvailableFieldId() const1099 int LIB_SYMBOL::GetNextAvailableFieldId() const
1100 {
1101     int retv = MANDATORY_FIELDS;
1102 
1103     while( GetFieldById( retv ) )
1104         retv += 1;
1105 
1106     return retv;
1107 }
1108 
1109 
SetOffset(const wxPoint & aOffset)1110 void LIB_SYMBOL::SetOffset( const wxPoint& aOffset )
1111 {
1112     for( LIB_ITEM& item : m_drawings )
1113         item.Offset( aOffset );
1114 }
1115 
1116 
RemoveDuplicateDrawItems()1117 void LIB_SYMBOL::RemoveDuplicateDrawItems()
1118 {
1119     m_drawings.unique();
1120 }
1121 
1122 
HasConversion() const1123 bool LIB_SYMBOL::HasConversion() const
1124 {
1125     for( const LIB_ITEM& item : m_drawings )
1126     {
1127         if( item.m_convert > LIB_ITEM::LIB_CONVERT::BASE )
1128             return true;
1129     }
1130 
1131     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
1132     {
1133         for( const LIB_ITEM& item : parent->GetDrawItems() )
1134         {
1135             if( item.m_convert > LIB_ITEM::LIB_CONVERT::BASE )
1136                 return true;
1137         }
1138     }
1139 
1140     return false;
1141 }
1142 
1143 
ClearTempFlags()1144 void LIB_SYMBOL::ClearTempFlags()
1145 {
1146     for( LIB_ITEM& item : m_drawings )
1147         item.ClearTempFlags();
1148 }
1149 
1150 
ClearEditFlags()1151 void LIB_SYMBOL::ClearEditFlags()
1152 {
1153     for( LIB_ITEM& item : m_drawings )
1154         item.ClearEditFlags();
1155 }
1156 
1157 
LocateDrawItem(int aUnit,int aConvert,KICAD_T aType,const wxPoint & aPoint)1158 LIB_ITEM* LIB_SYMBOL::LocateDrawItem( int aUnit, int aConvert,
1159                                       KICAD_T aType, const wxPoint& aPoint )
1160 {
1161     for( LIB_ITEM& item : m_drawings )
1162     {
1163         if( ( aUnit && item.m_unit && aUnit != item.m_unit )
1164                 || ( aConvert && item.m_convert && aConvert != item.m_convert )
1165                 || ( item.Type() != aType && aType != TYPE_NOT_INIT ) )
1166         {
1167             continue;
1168         }
1169 
1170         if( item.HitTest( aPoint ) )
1171             return &item;
1172     }
1173 
1174     return nullptr;
1175 }
1176 
1177 
LocateDrawItem(int aUnit,int aConvert,KICAD_T aType,const wxPoint & aPoint,const TRANSFORM & aTransform)1178 LIB_ITEM* LIB_SYMBOL::LocateDrawItem( int aUnit, int aConvert, KICAD_T aType,
1179                                       const wxPoint& aPoint, const TRANSFORM& aTransform )
1180 {
1181     /* we use LocateDrawItem( int aUnit, int convert, KICAD_T type, const
1182      * wxPoint& pt ) to search items.
1183      * because this function uses DefaultTransform as orient/mirror matrix
1184      * we temporary copy aTransform in DefaultTransform
1185      */
1186     LIB_ITEM* item;
1187     TRANSFORM transform = DefaultTransform;
1188     DefaultTransform = aTransform;
1189 
1190     item = LocateDrawItem( aUnit, aConvert, aType, aPoint );
1191 
1192     // Restore matrix
1193     DefaultTransform = transform;
1194 
1195     return item;
1196 }
1197 
1198 
Visit(INSPECTOR aInspector,void * aTestData,const KICAD_T aFilterTypes[])1199 SEARCH_RESULT LIB_SYMBOL::Visit( INSPECTOR aInspector, void* aTestData,
1200                                  const KICAD_T aFilterTypes[] )
1201 {
1202     // The part itself is never inspected, only its children
1203     for( LIB_ITEM& item : m_drawings )
1204     {
1205         if( item.IsType( aFilterTypes ) )
1206         {
1207             if( aInspector( &item, aTestData ) == SEARCH_RESULT::QUIT )
1208                 return SEARCH_RESULT::QUIT;
1209         }
1210     }
1211 
1212     return SEARCH_RESULT::CONTINUE;
1213 }
1214 
1215 
SetUnitCount(int aCount,bool aDuplicateDrawItems)1216 void LIB_SYMBOL::SetUnitCount( int aCount, bool aDuplicateDrawItems )
1217 {
1218     if( m_unitCount == aCount )
1219         return;
1220 
1221     if( aCount < m_unitCount )
1222     {
1223         LIB_ITEMS_CONTAINER::ITERATOR i = m_drawings.begin();
1224 
1225         while( i != m_drawings.end() )
1226         {
1227             if( i->m_unit > aCount )
1228                 i = m_drawings.erase( i );
1229             else
1230                 ++i;
1231         }
1232     }
1233     else if( aDuplicateDrawItems )
1234     {
1235         int prevCount = m_unitCount;
1236 
1237         // Temporary storage for new items, as adding new items directly to
1238         // m_drawings may cause the buffer reallocation which invalidates the
1239         // iterators
1240         std::vector< LIB_ITEM* > tmp;
1241 
1242         for( LIB_ITEM& item : m_drawings )
1243         {
1244             if( item.m_unit != 1 )
1245                 continue;
1246 
1247             for( int j = prevCount + 1; j <= aCount; j++ )
1248             {
1249                 LIB_ITEM* newItem = (LIB_ITEM*) item.Clone();
1250                 newItem->m_unit = j;
1251                 tmp.push_back( newItem );
1252             }
1253         }
1254 
1255         for( LIB_ITEM* item : tmp )
1256             m_drawings.push_back( item );
1257     }
1258 
1259     m_drawings.sort();
1260     m_unitCount = aCount;
1261 }
1262 
1263 
GetUnitCount() const1264 int LIB_SYMBOL::GetUnitCount() const
1265 {
1266     if( LIB_SYMBOL_SPTR parent = m_parent.lock() )
1267         return parent->GetUnitCount();
1268 
1269     return m_unitCount;
1270 }
1271 
1272 
SetConversion(bool aSetConvert,bool aDuplicatePins)1273 void LIB_SYMBOL::SetConversion( bool aSetConvert, bool aDuplicatePins )
1274 {
1275     if( aSetConvert == HasConversion() )
1276         return;
1277 
1278     // Duplicate items to create the converted shape
1279     if( aSetConvert )
1280     {
1281         if( aDuplicatePins )
1282         {
1283             std::vector< LIB_ITEM* > tmp;     // Temporarily store the duplicated pins here.
1284 
1285             for( LIB_ITEM& item : m_drawings )
1286             {
1287                 // Only pins are duplicated.
1288                 if( item.Type() != LIB_PIN_T )
1289                     continue;
1290 
1291                 if( item.m_convert == 1 )
1292                 {
1293                     LIB_ITEM* newItem = (LIB_ITEM*) item.Clone();
1294                     newItem->m_convert = 2;
1295                     tmp.push_back( newItem );
1296                 }
1297             }
1298 
1299             // Transfer the new pins to the LIB_SYMBOL.
1300             for( unsigned i = 0;  i < tmp.size();  i++ )
1301                 m_drawings.push_back( tmp[i] );
1302         }
1303     }
1304     else
1305     {
1306         // Delete converted shape items because the converted shape does
1307         // not exist
1308         LIB_ITEMS_CONTAINER::ITERATOR i = m_drawings.begin();
1309 
1310         while( i != m_drawings.end() )
1311         {
1312             if( i->m_convert > 1 )
1313                 i = m_drawings.erase( i );
1314             else
1315                 ++i;
1316         }
1317     }
1318 
1319     m_drawings.sort();
1320 }
1321 
1322 
SetSubpartIdNotation(int aSep,int aFirstId)1323 void LIB_SYMBOL::SetSubpartIdNotation( int aSep, int aFirstId )
1324 {
1325     m_subpartFirstId = 'A';
1326     m_subpartIdSeparator = 0;
1327 
1328     if( aSep == '.' || aSep == '-' || aSep == '_' )
1329         m_subpartIdSeparator = aSep;
1330 
1331     if( aFirstId == '1' && aSep != 0 )
1332         m_subpartFirstId = aFirstId;
1333 }
1334 
1335 
GetUnitDrawItems(int aUnit,int aConvert)1336 std::vector<LIB_ITEM*> LIB_SYMBOL::GetUnitDrawItems( int aUnit, int aConvert )
1337 {
1338     std::vector<LIB_ITEM*> unitItems;
1339 
1340     for( LIB_ITEM& item : m_drawings )
1341     {
1342         if( item.Type() == LIB_FIELD_T )
1343             continue;
1344 
1345         if( ( aConvert == -1 && item.GetUnit() == aUnit )
1346                 || ( aUnit == -1 && item.GetConvert() == aConvert )
1347                 || ( aUnit == item.GetUnit() && aConvert == item.GetConvert() ) )
1348         {
1349             unitItems.push_back( &item );
1350         }
1351     }
1352 
1353     return unitItems;
1354 }
1355 
1356 
GetUnitDrawItems()1357 std::vector<struct LIB_SYMBOL_UNIT> LIB_SYMBOL::GetUnitDrawItems()
1358 {
1359     std::vector<struct LIB_SYMBOL_UNIT> units;
1360 
1361     for( LIB_ITEM& item : m_drawings )
1362     {
1363         if( item.Type() == LIB_FIELD_T )
1364             continue;
1365 
1366         int unit = item.GetUnit();
1367         int convert = item.GetConvert();
1368 
1369         auto it = std::find_if( units.begin(), units.end(),
1370                 [unit, convert]( const LIB_SYMBOL_UNIT& a )
1371                 {
1372                     return a.m_unit == unit && a.m_convert == convert;
1373                 } );
1374 
1375         if( it == units.end() )
1376         {
1377             struct LIB_SYMBOL_UNIT newUnit;
1378             newUnit.m_unit = item.GetUnit();
1379             newUnit.m_convert = item.GetConvert();
1380             newUnit.m_items.push_back( &item );
1381             units.emplace_back( newUnit );
1382         }
1383         else
1384         {
1385             it->m_items.push_back( &item );
1386         }
1387     }
1388 
1389     return units;
1390 }
1391 
1392 
GetUniqueUnits()1393 std::vector<struct LIB_SYMBOL_UNIT> LIB_SYMBOL::GetUniqueUnits()
1394 {
1395     int unitNum;
1396     size_t i;
1397     struct LIB_SYMBOL_UNIT unit;
1398     std::vector<LIB_ITEM*> compareDrawItems;
1399     std::vector<LIB_ITEM*> currentDrawItems;
1400     std::vector<struct LIB_SYMBOL_UNIT> uniqueUnits;
1401 
1402     // The first unit is guaranteed to be unique so always include it.
1403     unit.m_unit = 1;
1404     unit.m_convert = 1;
1405     unit.m_items = GetUnitDrawItems( 1, 1 );
1406 
1407     // There are no unique units if there are no draw items other than fields.
1408     if( unit.m_items.size() == 0 )
1409         return uniqueUnits;
1410 
1411     uniqueUnits.emplace_back( unit );
1412 
1413     if( ( GetUnitCount() == 1 || UnitsLocked() ) && !HasConversion() )
1414         return uniqueUnits;
1415 
1416     currentDrawItems = unit.m_items;
1417 
1418     for( unitNum = 2; unitNum <= GetUnitCount(); unitNum++ )
1419     {
1420         compareDrawItems = GetUnitDrawItems( unitNum, 1 );
1421 
1422         wxCHECK2_MSG( compareDrawItems.size() != 0, continue,
1423                       "Multiple unit symbol defined with empty units." );
1424 
1425         if( currentDrawItems.size() != compareDrawItems.size() )
1426         {
1427             unit.m_unit = unitNum;
1428             unit.m_convert = 1;
1429             unit.m_items = compareDrawItems;
1430             uniqueUnits.emplace_back( unit );
1431         }
1432         else
1433         {
1434             for( i = 0; i < currentDrawItems.size(); i++ )
1435             {
1436                 if( currentDrawItems[i]->compare( *compareDrawItems[i],
1437                                                   LIB_ITEM::COMPARE_FLAGS::UNIT ) != 0 )
1438                 {
1439                     unit.m_unit = unitNum;
1440                     unit.m_convert = 1;
1441                     unit.m_items = compareDrawItems;
1442                     uniqueUnits.emplace_back( unit );
1443                 }
1444             }
1445         }
1446     }
1447 
1448     if( HasConversion() )
1449     {
1450         currentDrawItems = GetUnitDrawItems( 1, 2 );
1451 
1452         if( ( GetUnitCount() == 1 || UnitsLocked() ) )
1453         {
1454             unit.m_unit = 1;
1455             unit.m_convert = 2;
1456             unit.m_items = currentDrawItems;
1457             uniqueUnits.emplace_back( unit );
1458 
1459             return uniqueUnits;
1460         }
1461 
1462         for( unitNum = 2; unitNum <= GetUnitCount(); unitNum++ )
1463         {
1464             compareDrawItems = GetUnitDrawItems( unitNum, 2 );
1465 
1466             wxCHECK2_MSG( compareDrawItems.size() != 0, continue,
1467                           "Multiple unit symbol defined with empty units." );
1468 
1469             if( currentDrawItems.size() != compareDrawItems.size() )
1470             {
1471                 unit.m_unit = unitNum;
1472                 unit.m_convert = 2;
1473                 unit.m_items = compareDrawItems;
1474                 uniqueUnits.emplace_back( unit );
1475             }
1476             else
1477             {
1478                 for( i = 0; i < currentDrawItems.size(); i++ )
1479                 {
1480                     if( currentDrawItems[i]->compare( *compareDrawItems[i],
1481                                                       LIB_ITEM::COMPARE_FLAGS::UNIT ) != 0 )
1482                     {
1483                         unit.m_unit = unitNum;
1484                         unit.m_convert = 2;
1485                         unit.m_items = compareDrawItems;
1486                         uniqueUnits.emplace_back( unit );
1487                     }
1488                 }
1489             }
1490         }
1491     }
1492 
1493     return uniqueUnits;
1494 }
1495