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