1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
5  * Copyright (C) 2004-2021 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
23  */
24 
25 /*
26  * Fields are texts attached to a symbol, some of which have a special meaning.
27  * Fields 0 and 1 are very important: reference and value.
28  * Field 2 is used as default footprint name.
29  * Field 3 is used to point to a datasheet (usually a URL).
30  * Fields 4+ are user fields.  They can be renamed and can appear in reports.
31  */
32 
33 #include <wx/log.h>
34 #include <wx/menu.h>
35 #include <common.h>     // for ExpandTextVars
36 #include <eda_item.h>
37 #include <sch_edit_frame.h>
38 #include <plotters/plotter.h>
39 #include <bitmaps.h>
40 #include <core/kicad_algo.h>
41 #include <core/mirror.h>
42 #include <kiway.h>
43 #include <general.h>
44 #include <symbol_library.h>
45 #include <sch_symbol.h>
46 #include <sch_field.h>
47 #include <schematic.h>
48 #include <settings/color_settings.h>
49 #include <string_utils.h>
50 #include <trace_helpers.h>
51 #include <trigo.h>
52 #include <eeschema_id.h>
53 #include <tool/tool_manager.h>
54 #include <tools/ee_actions.h>
55 
SCH_FIELD(const wxPoint & aPos,int aFieldId,SCH_ITEM * aParent,const wxString & aName)56 SCH_FIELD::SCH_FIELD( const wxPoint& aPos, int aFieldId, SCH_ITEM* aParent,
57                       const wxString& aName ) :
58     SCH_ITEM( aParent, SCH_FIELD_T ),
59     EDA_TEXT( wxEmptyString ),
60     m_id( 0 ),
61     m_name( aName )
62 {
63     SetTextPos( aPos );
64     SetId( aFieldId );  // will also set the layer
65     SetVisible( false );
66 }
67 
68 
~SCH_FIELD()69 SCH_FIELD::~SCH_FIELD()
70 {
71 }
72 
73 
Clone() const74 EDA_ITEM* SCH_FIELD::Clone() const
75 {
76     return new SCH_FIELD( *this );
77 }
78 
79 
SetId(int aId)80 void SCH_FIELD::SetId( int aId )
81 {
82     m_id = aId;
83 
84     if( m_parent && m_parent->Type() == SCH_SHEET_T )
85     {
86         switch( m_id )
87         {
88         case SHEETNAME:     SetLayer( LAYER_SHEETNAME );     break;
89         case SHEETFILENAME: SetLayer( LAYER_SHEETFILENAME ); break;
90         default:            SetLayer( LAYER_SHEETFIELDS );   break;
91         }
92     }
93     else
94     {
95         switch( m_id )
96         {
97         case REFERENCE_FIELD: SetLayer( LAYER_REFERENCEPART ); break;
98         case VALUE_FIELD:     SetLayer( LAYER_VALUEPART );     break;
99         default:              SetLayer( LAYER_FIELDS );        break;
100         }
101     }
102 }
103 
104 
GetShownText(int aDepth) const105 wxString SCH_FIELD::GetShownText( int aDepth ) const
106 {
107     std::function<bool( wxString* )> symbolResolver =
108             [&]( wxString* token ) -> bool
109             {
110                 if( token->Contains( ':' ) )
111                 {
112                     if( Schematic()->ResolveCrossReference( token, aDepth ) )
113                         return true;
114                 }
115                 else
116                 {
117                     SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
118 
119                     if( parentSymbol->ResolveTextVar( token, aDepth + 1 ) )
120                         return true;
121 
122                     SCHEMATIC* schematic = parentSymbol->Schematic();
123                     SCH_SHEET* sheet = schematic ? schematic->CurrentSheet().Last() : nullptr;
124 
125                     if( sheet && sheet->ResolveTextVar( token, aDepth + 1 ) )
126                         return true;
127                 }
128 
129                 return false;
130             };
131 
132     std::function<bool( wxString* )> sheetResolver =
133             [&]( wxString* token ) -> bool
134             {
135                 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
136                 return sheet->ResolveTextVar( token, aDepth + 1 );
137             };
138 
139     std::function<bool( wxString* )> globalLabelResolver =
140             [&]( wxString* token ) -> bool
141             {
142                 SCH_GLOBALLABEL* globalLabel = static_cast<SCH_GLOBALLABEL*>( m_parent );
143                 return globalLabel->ResolveTextVar( token, aDepth + 1 );
144             };
145 
146     PROJECT*  project = nullptr;
147     wxString  text = EDA_TEXT::GetShownText();
148 
149     if( text == "~" )    // Legacy placeholder for empty string
150     {
151         text = "";
152     }
153     else if( HasTextVars() )
154     {
155         if( Schematic() )
156             project = &Schematic()->Prj();
157 
158         if( aDepth < 10 )
159         {
160             if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
161                 text = ExpandTextVars( text, &symbolResolver, nullptr, project );
162             else if( m_parent && m_parent->Type() == SCH_SHEET_T )
163                 text = ExpandTextVars( text, &sheetResolver, nullptr, project );
164             else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
165                 text = ExpandTextVars( text, &globalLabelResolver, nullptr, project );
166             else
167                 text = ExpandTextVars( text, project );
168         }
169     }
170 
171     // WARNING: the IDs of FIELDS and SHEETS overlap, so one must check *both* the
172     // id and the parent's type.
173 
174     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
175     {
176         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
177 
178         if( m_id == REFERENCE_FIELD )
179         {
180             // For more than one part per package, we must add the part selection
181             // A, B, ... or 1, 2, .. to the reference.
182             if( parentSymbol->GetUnitCount() > 1 )
183                 text << LIB_SYMBOL::SubReference( parentSymbol->GetUnit() );
184         }
185     }
186     else if( m_parent && m_parent->Type() == SCH_SHEET_T )
187     {
188         if( m_id == SHEETFILENAME )
189             text = _( "File:" ) + wxS( " " )+ text;
190     }
191 
192     return text;
193 }
194 
195 
GetPenWidth() const196 int SCH_FIELD::GetPenWidth() const
197 {
198     return GetEffectiveTextPenWidth();
199 }
200 
201 
Print(const RENDER_SETTINGS * aSettings,const wxPoint & aOffset)202 void SCH_FIELD::Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset )
203 {
204     wxDC*    DC = aSettings->GetPrintDC();
205     COLOR4D  color = aSettings->GetLayerColor( IsForceVisible() ? LAYER_HIDDEN : m_layer );
206     int      orient;
207     wxPoint  textpos;
208     int      penWidth = GetEffectiveTextPenWidth( aSettings->GetDefaultPenWidth() );
209 
210     if( ( !IsVisible() && !IsForceVisible() ) || IsVoid() )
211         return;
212 
213     // Calculate the text orientation according to the symbol orientation.
214     orient = GetTextAngle();
215 
216     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
217     {
218         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
219 
220         if( parentSymbol && parentSymbol->GetTransform().y1 )  // Rotate symbol 90 degrees.
221         {
222             if( orient == TEXT_ANGLE_HORIZ )
223                 orient = TEXT_ANGLE_VERT;
224             else
225                 orient = TEXT_ANGLE_HORIZ;
226         }
227     }
228 
229     /*
230      * Calculate the text justification, according to the symbol orientation/mirror.
231      * This is a bit complicated due to cumulative calculations:
232      * - numerous cases (mirrored or not, rotation)
233      * - the GRText function will also recalculate H and V justifications according to the text
234      *   orientation.
235      * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
236      *   to calculate so the more easily way is to use no justifications (centered text) and use
237      *   GetBoundingBox to know the text coordinate considered as centered
238      */
239     textpos = GetBoundingBox().Centre() + aOffset;
240 
241     GRText( DC, textpos, color, GetShownText(), orient, GetTextSize(), GR_TEXT_HJUSTIFY_CENTER,
242             GR_TEXT_VJUSTIFY_CENTER, penWidth, IsItalic(), IsBold() );
243 }
244 
245 
ImportValues(const LIB_FIELD & aSource)246 void SCH_FIELD::ImportValues( const LIB_FIELD& aSource )
247 {
248     SetEffects( aSource );
249 }
250 
251 
SwapData(SCH_ITEM * aItem)252 void SCH_FIELD::SwapData( SCH_ITEM* aItem )
253 {
254     wxCHECK_RET( ( aItem != nullptr ) && ( aItem->Type() == SCH_FIELD_T ),
255                  wxT( "Cannot swap field data with invalid item." ) );
256 
257     SCH_FIELD* item = (SCH_FIELD*) aItem;
258 
259     std::swap( m_layer, item->m_layer );
260     SwapText( *item );
261     SwapEffects( *item );
262 }
263 
264 
GetDrawRotation() const265 double SCH_FIELD::GetDrawRotation() const
266 {
267     // Calculate the text orientation according to the symbol orientation.
268     int orient = GetTextAngle();
269 
270     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
271     {
272         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
273 
274         if( parentSymbol && parentSymbol->GetTransform().y1 )  // Rotate symbol 90 degrees.
275         {
276             if( orient == TEXT_ANGLE_HORIZ )
277                 orient = TEXT_ANGLE_VERT;
278             else
279                 orient = TEXT_ANGLE_HORIZ;
280         }
281     }
282 
283     return orient;
284 }
285 
286 
GetDrawPos() const287 wxPoint SCH_FIELD::GetDrawPos() const
288 {
289     return GetBoundingBox().Centre();
290 }
291 
292 
GetDrawHorizJustify() const293 EDA_TEXT_HJUSTIFY_T SCH_FIELD::GetDrawHorizJustify() const
294 {
295     return GR_TEXT_HJUSTIFY_CENTER;
296 }
297 
298 
GetDrawVertJustify() const299 EDA_TEXT_VJUSTIFY_T SCH_FIELD::GetDrawVertJustify() const
300 {
301     return GR_TEXT_VJUSTIFY_CENTER;
302 }
303 
304 
GetBoundingBox() const305 const EDA_RECT SCH_FIELD::GetBoundingBox() const
306 {
307     // Calculate the text bounding box:
308     EDA_RECT rect = GetTextBox();
309 
310     // Calculate the bounding box position relative to the parent:
311     wxPoint origin = GetParentPosition();
312     wxPoint pos = GetTextPos() - origin;
313     wxPoint begin = rect.GetOrigin() - origin;
314     wxPoint end = rect.GetEnd() - origin;
315     RotatePoint( &begin, pos, GetTextAngle() );
316     RotatePoint( &end, pos, GetTextAngle() );
317 
318     // Now, apply the symbol transform (mirror/rot)
319     TRANSFORM transform;
320 
321     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
322     {
323         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
324 
325         // Due to the Y axis direction, we must mirror the bounding box,
326         // relative to the text position:
327         MIRROR( begin.y, pos.y );
328         MIRROR( end.y,   pos.y );
329 
330         transform = parentSymbol->GetTransform();
331     }
332     else
333     {
334         transform = TRANSFORM( 1, 0, 0, 1 );  // identity transform
335     }
336 
337     rect.SetOrigin( transform.TransformCoordinate( begin ) );
338     rect.SetEnd( transform.TransformCoordinate( end ) );
339 
340     rect.Move( origin );
341     rect.Normalize();
342 
343     return rect;
344 }
345 
346 
IsHorizJustifyFlipped() const347 bool SCH_FIELD::IsHorizJustifyFlipped() const
348 {
349     wxPoint render_center = GetBoundingBox().Centre();
350     wxPoint pos = GetPosition();
351 
352     switch( GetHorizJustify() )
353     {
354     case GR_TEXT_HJUSTIFY_LEFT:
355         if( GetDrawRotation() == TEXT_ANGLE_VERT )
356             return render_center.y > pos.y;
357         else
358             return render_center.x < pos.x;
359     case GR_TEXT_HJUSTIFY_RIGHT:
360         if( GetDrawRotation() == TEXT_ANGLE_VERT )
361             return render_center.y < pos.y;
362         else
363             return render_center.x > pos.x;
364     default:
365         return false;
366     }
367 }
368 
369 
GetEffectiveHorizJustify() const370 EDA_TEXT_HJUSTIFY_T SCH_FIELD::GetEffectiveHorizJustify() const
371 {
372     switch( GetHorizJustify() )
373     {
374     case GR_TEXT_HJUSTIFY_LEFT:
375         return IsHorizJustifyFlipped() ? GR_TEXT_HJUSTIFY_RIGHT : GR_TEXT_HJUSTIFY_LEFT;
376     case GR_TEXT_HJUSTIFY_RIGHT:
377         return IsHorizJustifyFlipped() ? GR_TEXT_HJUSTIFY_LEFT : GR_TEXT_HJUSTIFY_RIGHT;
378     default:
379         return GR_TEXT_HJUSTIFY_CENTER;
380     }
381 }
382 
383 
IsVertJustifyFlipped() const384 bool SCH_FIELD::IsVertJustifyFlipped() const
385 {
386     wxPoint render_center = GetBoundingBox().Centre();
387     wxPoint pos = GetPosition();
388 
389     switch( GetVertJustify() )
390     {
391     case GR_TEXT_VJUSTIFY_TOP:
392         if( GetDrawRotation() == TEXT_ANGLE_VERT )
393             return render_center.x < pos.x;
394         else
395             return render_center.y < pos.y;
396     case GR_TEXT_VJUSTIFY_BOTTOM:
397         if( GetDrawRotation() == TEXT_ANGLE_VERT )
398             return render_center.x > pos.x;
399         else
400             return render_center.y > pos.y;
401     default:
402         return false;
403     }
404 }
405 
406 
GetEffectiveVertJustify() const407 EDA_TEXT_VJUSTIFY_T SCH_FIELD::GetEffectiveVertJustify() const
408 {
409     switch( GetVertJustify() )
410     {
411     case GR_TEXT_VJUSTIFY_TOP:
412         return IsVertJustifyFlipped() ? GR_TEXT_VJUSTIFY_BOTTOM : GR_TEXT_VJUSTIFY_TOP;
413     case GR_TEXT_VJUSTIFY_BOTTOM:
414         return IsVertJustifyFlipped() ? GR_TEXT_VJUSTIFY_TOP : GR_TEXT_VJUSTIFY_BOTTOM;
415     default:
416         return GR_TEXT_VJUSTIFY_CENTER;
417     }
418 }
419 
420 
IsVoid() const421 bool SCH_FIELD::IsVoid() const
422 {
423     return GetText().Len() == 0;
424 }
425 
426 
Matches(const wxFindReplaceData & aSearchData,void * aAuxData) const427 bool SCH_FIELD::Matches( const wxFindReplaceData& aSearchData, void* aAuxData ) const
428 {
429     wxString text = GetShownText();
430     int      flags = aSearchData.GetFlags();
431     bool     searchHiddenFields = flags & FR_SEARCH_ALL_FIELDS;
432     bool     searchAndReplace = flags & FR_SEARCH_REPLACE;
433     bool     replaceReferences = flags & FR_REPLACE_REFERENCES;
434 
435     wxLogTrace( traceFindItem, wxT( "    child item " )
436                     + GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
437 
438     if( !IsVisible() && !searchHiddenFields )
439         return false;
440 
441     if( m_parent && m_parent->Type() == SCH_SYMBOL_T && m_id == REFERENCE_FIELD )
442     {
443         if( searchAndReplace && !replaceReferences )
444             return false;
445 
446         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
447         wxASSERT( aAuxData );
448 
449         // Take sheet path into account which effects the reference field and the unit for
450         // symbols with multiple parts.
451         if( aAuxData )
452         {
453             text = parentSymbol->GetRef((SCH_SHEET_PATH*) aAuxData );
454 
455             if( SCH_ITEM::Matches( text, aSearchData ) )
456                 return true;
457 
458             if( parentSymbol->GetUnitCount() > 1 )
459                 text << LIB_SYMBOL::SubReference( parentSymbol->GetUnit() );
460         }
461     }
462 
463     return SCH_ITEM::Matches( text, aSearchData );
464 }
465 
466 
IsReplaceable() const467 bool SCH_FIELD::IsReplaceable() const
468 {
469     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
470     {
471         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
472 
473         if( m_id == VALUE_FIELD )
474         {
475             if( parentSymbol->GetLibSymbolRef() && parentSymbol->GetLibSymbolRef()->IsPower() )
476                 return false;
477         }
478     }
479     else if( m_parent && m_parent->Type() == SCH_SHEET_T )
480     {
481         // See comments in SCH_FIELD::Replace(), below.
482         if( m_id == SHEETFILENAME )
483             return false;
484     }
485     else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
486     {
487         return false;
488     }
489 
490     return true;
491 }
492 
493 
Replace(const wxFindReplaceData & aSearchData,void * aAuxData)494 bool SCH_FIELD::Replace( const wxFindReplaceData& aSearchData, void* aAuxData )
495 {
496     wxString text;
497     bool     resolve = false;    // Replace in source text, not shown text
498     bool     isReplaced = false;
499 
500     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
501     {
502         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
503 
504         switch( m_id )
505         {
506         case REFERENCE_FIELD:
507             wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in refdes." ) );
508 
509             if( !( aSearchData.GetFlags() & FR_REPLACE_REFERENCES ) )
510                 return false;
511 
512             text = parentSymbol->GetRef( (SCH_SHEET_PATH*) aAuxData );
513             isReplaced = EDA_ITEM::Replace( aSearchData, text );
514 
515             if( isReplaced )
516                 parentSymbol->SetRef( (SCH_SHEET_PATH*) aAuxData, text );
517 
518             break;
519 
520         case VALUE_FIELD:
521             wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in value field." ) );
522 
523             text = parentSymbol->GetValue((SCH_SHEET_PATH*) aAuxData, resolve );
524             isReplaced = EDA_ITEM::Replace( aSearchData, text );
525 
526             if( isReplaced )
527                 parentSymbol->SetValue( (SCH_SHEET_PATH*) aAuxData, text );
528 
529             break;
530 
531         case FOOTPRINT_FIELD:
532             wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in footprint field." ) );
533 
534             text = parentSymbol->GetFootprint((SCH_SHEET_PATH*) aAuxData, resolve );
535             isReplaced = EDA_ITEM::Replace( aSearchData, text );
536 
537             if( isReplaced )
538                 parentSymbol->SetFootprint( (SCH_SHEET_PATH*) aAuxData, text );
539 
540             break;
541 
542         default:
543             isReplaced = EDA_TEXT::Replace( aSearchData );
544         }
545     }
546     else if( m_parent && m_parent->Type() == SCH_SHEET_T )
547     {
548         isReplaced = EDA_TEXT::Replace( aSearchData );
549 
550         if( m_id == SHEETFILENAME && isReplaced )
551         {
552             // If we allowed this we'd have a bunch of work to do here, including warning
553             // about it not being undoable, checking for recursive hierarchies, reloading
554             // sheets, etc.  See DIALOG_SHEET_PROPERTIES::TransferDataFromWindow().
555         }
556     }
557 
558     return isReplaced;
559 }
560 
561 
Rotate(const wxPoint & aCenter)562 void SCH_FIELD::Rotate( const wxPoint& aCenter )
563 {
564     wxPoint pt = GetPosition();
565     RotatePoint( &pt, aCenter, 900 );
566     SetPosition( pt );
567 }
568 
569 
GetSelectMenuText(EDA_UNITS aUnits) const570 wxString SCH_FIELD::GetSelectMenuText( EDA_UNITS aUnits ) const
571 {
572     return wxString::Format( "%s '%s'", GetName(), ShortenedShownText() );
573 }
574 
575 
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)576 void SCH_FIELD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
577 {
578     wxString msg;
579 
580     aList.emplace_back( _( "Symbol Field" ), GetName() );
581 
582     // Don't use GetShownText() here; we want to show the user the variable references
583     aList.emplace_back( _( "Text" ), UnescapeString( GetText() ) );
584 
585     aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
586 
587     aList.emplace_back( _( "Style" ), GetTextStyleName() );
588 
589     aList.emplace_back( _( "Text Size" ), MessageTextFromValue( aFrame->GetUserUnits(),
590                                                                 GetTextWidth() ) );
591 
592     switch ( GetHorizJustify() )
593     {
594     case GR_TEXT_HJUSTIFY_LEFT:   msg = _( "Left" );   break;
595     case GR_TEXT_HJUSTIFY_CENTER: msg = _( "Center" ); break;
596     case GR_TEXT_HJUSTIFY_RIGHT:  msg = _( "Right" );  break;
597     }
598 
599     aList.emplace_back( _( "H Justification" ), msg );
600 
601     switch ( GetVertJustify() )
602     {
603     case GR_TEXT_VJUSTIFY_TOP:    msg = _( "Top" );    break;
604     case GR_TEXT_VJUSTIFY_CENTER: msg = _( "Center" ); break;
605     case GR_TEXT_VJUSTIFY_BOTTOM: msg = _( "Bottom" ); break;
606     }
607 
608     aList.emplace_back( _( "V Justification" ), msg );
609 }
610 
611 
DoHypertextMenu(EDA_DRAW_FRAME * aFrame)612 void SCH_FIELD::DoHypertextMenu( EDA_DRAW_FRAME* aFrame )
613 {
614     constexpr int START_ID = 1;
615 
616     static wxString back = "HYPERTEXT_BACK";
617     wxMenu          menu;
618     SCH_TEXT*       label = dynamic_cast<SCH_TEXT*>( m_parent );
619 
620     if( label && Schematic() )
621     {
622         auto it = Schematic()->GetPageRefsMap().find( label->GetText() );
623 
624         if( it != Schematic()->GetPageRefsMap().end() )
625         {
626             std::map<wxString, wxString> sheetNames;
627             std::vector<wxString>        pageListCopy;
628 
629             pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
630             if( !Schematic()->Settings().m_IntersheetRefsListOwnPage )
631             {
632                 wxString currentPage = Schematic()->CurrentSheet().GetPageNumber();
633                 alg::delete_matching( pageListCopy, currentPage );
634 
635                 if( pageListCopy.empty() )
636                     return;
637             }
638 
639             std::sort( pageListCopy.begin(), pageListCopy.end(),
640                        []( const wxString& a, const wxString& b ) -> bool
641                        {
642                            return StrNumCmp( a, b, true ) < 0;
643                        } );
644 
645             for( const SCH_SHEET_PATH& sheet : Schematic()->GetSheets() )
646             {
647                 if( sheet.size() == 1 )
648                     sheetNames[ sheet.GetPageNumber() ] = _( "<root sheet>" );
649                 else
650                     sheetNames[ sheet.GetPageNumber() ] = sheet.Last()->GetName();
651             }
652 
653             for( int i = 0; i < (int) pageListCopy.size(); ++i )
654             {
655                 menu.Append( i + START_ID, wxString::Format( _( "Go to Page %s (%s)" ),
656                                                              pageListCopy[i],
657                                                              sheetNames[ pageListCopy[i] ] ) );
658             }
659 
660             menu.AppendSeparator();
661             menu.Append( 999, _( "Back to Previous Selected Sheet" ) );
662 
663             int   sel = aFrame->GetPopupMenuSelectionFromUser( menu ) - START_ID;
664             void* param = nullptr;
665 
666             if( sel >= 0 && sel < (int) pageListCopy.size() )
667                 param = (void*) &pageListCopy[ sel ];
668             else if( sel == 999 )
669                 param = (void*) &back;
670 
671             if( param )
672                 aFrame->GetToolManager()->RunAction( EE_ACTIONS::hypertextCommand, true, param );
673         }
674     }
675 }
676 
677 
GetName(bool aUseDefaultName) const678 wxString SCH_FIELD::GetName( bool aUseDefaultName ) const
679 {
680     if( !m_name.IsEmpty() )
681         return m_name;
682     else if( aUseDefaultName )
683     {
684         if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
685             return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
686         else if( m_parent && m_parent->Type() == SCH_SHEET_T )
687             return SCH_SHEET::GetDefaultFieldName( m_id );
688         else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
689             return _( "Intersheet References" );
690     }
691 
692     return wxEmptyString;
693 }
694 
695 
GetCanonicalName() const696 wxString SCH_FIELD::GetCanonicalName() const
697 {
698     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
699     {
700         switch( m_id )
701         {
702         case  REFERENCE_FIELD: return wxT( "Reference" );
703         case  VALUE_FIELD:     return wxT( "Value" );
704         case  FOOTPRINT_FIELD: return wxT( "Footprint" );
705         case  DATASHEET_FIELD: return wxT( "Datasheet" );
706         }
707     }
708     else if( m_parent && m_parent->Type() == SCH_SHEET_T )
709     {
710         switch( m_id )
711         {
712         case  SHEETNAME:     return wxT( "Sheetname" );
713         case  SHEETFILENAME: return wxT( "Sheetfile" );
714         }
715     }
716     else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
717     {
718         return wxT( "Intersheet References" );
719     }
720 
721     return m_name;
722 }
723 
724 
GetMenuImage() const725 BITMAPS SCH_FIELD::GetMenuImage() const
726 {
727     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
728     {
729         switch( m_id )
730         {
731         case REFERENCE_FIELD: return BITMAPS::edit_comp_ref;
732         case VALUE_FIELD:     return BITMAPS::edit_comp_value;
733         case FOOTPRINT_FIELD: return BITMAPS::edit_comp_footprint;
734         default:              return BITMAPS::text;
735         }
736     }
737 
738     return BITMAPS::text;
739 }
740 
741 
HitTest(const wxPoint & aPosition,int aAccuracy) const742 bool SCH_FIELD::HitTest( const wxPoint& aPosition, int aAccuracy ) const
743 {
744     // Do not hit test hidden or empty fields.
745     if( !IsVisible() || IsVoid() )
746         return false;
747 
748     EDA_RECT rect = GetBoundingBox();
749 
750     rect.Inflate( aAccuracy );
751 
752     return rect.Contains( aPosition );
753 }
754 
755 
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const756 bool SCH_FIELD::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
757 {
758     // Do not hit test hidden fields.
759     if( !IsVisible() || IsVoid() )
760         return false;
761 
762     EDA_RECT rect = aRect;
763 
764     rect.Inflate( aAccuracy );
765 
766     if( aContained )
767         return rect.Contains( GetBoundingBox() );
768 
769     return rect.Intersects( GetBoundingBox() );
770 }
771 
772 
Plot(PLOTTER * aPlotter) const773 void SCH_FIELD::Plot( PLOTTER* aPlotter ) const
774 {
775     RENDER_SETTINGS* settings = aPlotter->RenderSettings();
776     COLOR4D          color = settings->GetLayerColor( GetLayer() );
777     int              penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
778 
779     penWidth = std::max( penWidth, settings->GetMinPenWidth() );
780 
781     if( !IsVisible() )
782         return;
783 
784     if( IsVoid() )
785         return;
786 
787     // Calculate the text orientation, according to the symbol orientation/mirror
788     int orient = GetTextAngle();
789 
790     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
791     {
792         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
793 
794         if( parentSymbol->GetTransform().y1 )  // Rotate symbol 90 deg.
795         {
796             if( orient == TEXT_ANGLE_HORIZ )
797                 orient = TEXT_ANGLE_VERT;
798             else
799                 orient = TEXT_ANGLE_HORIZ;
800         }
801     }
802 
803     /*
804      * Calculate the text justification, according to the symbol orientation/mirror.
805      * This is a bit complicated due to cumulative calculations:
806      * - numerous cases (mirrored or not, rotation)
807      * - the plotter's Text function will also recalculate H and V justifications according to
808      *   the text orientation.
809      * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
810      *   to calculate so the easier way is to use no justifications (centered text) and use
811      *   GetBoundingBox to know the text coordinate considered as centered
812      */
813     EDA_TEXT_HJUSTIFY_T hjustify = GR_TEXT_HJUSTIFY_CENTER;
814     EDA_TEXT_VJUSTIFY_T vjustify = GR_TEXT_VJUSTIFY_CENTER;
815     wxPoint             textpos = GetBoundingBox().Centre();
816 
817     aPlotter->Text( textpos, color, GetShownText(), orient, GetTextSize(),  hjustify, vjustify,
818                     penWidth, IsItalic(), IsBold() );
819 }
820 
821 
SetPosition(const wxPoint & aPosition)822 void SCH_FIELD::SetPosition( const wxPoint& aPosition )
823 {
824     // Actual positions are calculated by the rotation/mirror transform of the parent symbol
825     // of the field.  The inverse transform is used to calculate the position relative to the
826     // parent symbol.
827     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
828     {
829         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
830         wxPoint     relPos = aPosition - parentSymbol->GetPosition();
831 
832         relPos = parentSymbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
833 
834         SetTextPos( relPos + parentSymbol->GetPosition() );
835         return;
836     }
837 
838     SetTextPos( aPosition );
839 }
840 
841 
GetPosition() const842 wxPoint SCH_FIELD::GetPosition() const
843 {
844     if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
845     {
846         SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
847         wxPoint     relativePos = GetTextPos() - parentSymbol->GetPosition();
848 
849         relativePos = parentSymbol->GetTransform().TransformCoordinate( relativePos );
850 
851         return relativePos + parentSymbol->GetPosition();
852     }
853 
854     return GetTextPos();
855 }
856 
857 
GetParentPosition() const858 wxPoint SCH_FIELD::GetParentPosition() const
859 {
860     return m_parent ? m_parent->GetPosition() : wxPoint( 0, 0 );
861 }
862 
863 
operator <(const SCH_ITEM & aItem) const864 bool SCH_FIELD::operator <( const SCH_ITEM& aItem ) const
865 {
866     if( Type() != aItem.Type() )
867         return Type() < aItem.Type();
868 
869     auto field = static_cast<const SCH_FIELD*>( &aItem );
870 
871     if( GetId() != field->GetId() )
872         return GetId() < field->GetId();
873 
874     if( GetText() != field->GetText() )
875         return GetText() < field->GetText();
876 
877     if( GetLibPosition().x != field->GetLibPosition().x )
878         return GetLibPosition().x < field->GetLibPosition().x;
879 
880     if( GetLibPosition().y != field->GetLibPosition().y )
881         return GetLibPosition().y < field->GetLibPosition().y;
882 
883     return GetName() < field->GetName();
884 }
885