1 /*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
5 * Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com>
6 * Copyright (C) 1992-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 /**
27 * @file sch_text.cpp
28 * @brief Code for handling schematic texts (texts, labels, hlabels and global labels).
29 */
30
31 #include <sch_symbol.h>
32 #include <sch_edit_frame.h>
33 #include <plotters/plotter.h>
34 #include <widgets/msgpanel.h>
35 #include <gal/stroke_font.h>
36 #include <bitmaps.h>
37 #include <string_utils.h>
38 #include <sch_text.h>
39 #include <schematic.h>
40 #include <settings/color_settings.h>
41 #include <sch_painter.h>
42 #include <default_values.h>
43 #include <wx/debug.h>
44 #include <wx/log.h>
45 #include <dialogs/html_message_box.h>
46 #include <project/project_file.h>
47 #include <project/net_settings.h>
48 #include <core/mirror.h>
49 #include <core/kicad_algo.h>
50 #include <trigo.h>
51
52 using KIGFX::SCH_RENDER_SETTINGS;
53
54
IncrementLabelMember(wxString & name,int aIncrement)55 bool IncrementLabelMember( wxString& name, int aIncrement )
56 {
57 if( name.IsEmpty() )
58 return true;
59
60 wxString suffix;
61 wxString digits;
62 wxString outputFormat;
63 wxString outputNumber;
64 int ii = name.Len() - 1;
65 int dCount = 0;
66
67 while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) )
68 {
69 suffix = name.GetChar( ii ) + suffix;
70 ii--;
71 }
72
73 while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) )
74 {
75 digits = name.GetChar( ii ) + digits;
76 ii--;
77 dCount++;
78 }
79
80 if( digits.IsEmpty() )
81 return true;
82
83 long number = 0;
84
85 if( digits.ToLong( &number ) )
86 {
87 number += aIncrement;
88
89 // Don't let result go below zero
90
91 if( number > -1 )
92 {
93 name.Remove( ii + 1 );
94 //write out a format string with correct number of leading zeroes
95 outputFormat.Printf( "%%0%dd", dCount );
96 //write out the number using the format string
97 outputNumber.Printf( outputFormat, number );
98 name << outputNumber << suffix;
99 return true;
100 }
101 }
102
103 return false;
104 }
105
106
107 /* Coding polygons for global symbol graphic shapes.
108 * the first parml is the number of corners
109 * others are the corners coordinates in reduced units
110 * the real coordinate is the reduced coordinate * text half size
111 */
112 static int TemplateIN_HN[] = { 6, 0, 0, -1, -1, -2, -1, -2, 1, -1, 1, 0, 0 };
113 static int TemplateIN_HI[] = { 6, 0, 0, 1, 1, 2, 1, 2, -1, 1, -1, 0, 0 };
114 static int TemplateIN_UP[] = { 6, 0, 0, 1, -1, 1, -2, -1, -2, -1, -1, 0, 0 };
115 static int TemplateIN_BOTTOM[] = { 6, 0, 0, 1, 1, 1, 2, -1, 2, -1, 1, 0, 0 };
116
117 static int TemplateOUT_HN[] = { 6, -2, 0, -1, 1, 0, 1, 0, -1, -1, -1, -2, 0 };
118 static int TemplateOUT_HI[] = { 6, 2, 0, 1, -1, 0, -1, 0, 1, 1, 1, 2, 0 };
119 static int TemplateOUT_UP[] = { 6, 0, -2, 1, -1, 1, 0, -1, 0, -1, -1, 0, -2 };
120 static int TemplateOUT_BOTTOM[] = { 6, 0, 2, 1, 1, 1, 0, -1, 0, -1, 1, 0, 2 };
121
122 static int TemplateUNSPC_HN[] = { 5, 0, -1, -2, -1, -2, 1, 0, 1, 0, -1 };
123 static int TemplateUNSPC_HI[] = { 5, 0, -1, 2, -1, 2, 1, 0, 1, 0, -1 };
124 static int TemplateUNSPC_UP[] = { 5, 1, 0, 1, -2, -1, -2, -1, 0, 1, 0 };
125 static int TemplateUNSPC_BOTTOM[] = { 5, 1, 0, 1, 2, -1, 2, -1, 0, 1, 0 };
126
127 static int TemplateBIDI_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
128 static int TemplateBIDI_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
129 static int TemplateBIDI_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
130 static int TemplateBIDI_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
131
132 static int Template3STATE_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
133 static int Template3STATE_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
134 static int Template3STATE_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
135 static int Template3STATE_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
136
137 static int* TemplateShape[5][4] =
138 {
139 { TemplateIN_HN, TemplateIN_UP, TemplateIN_HI, TemplateIN_BOTTOM },
140 { TemplateOUT_HN, TemplateOUT_UP, TemplateOUT_HI, TemplateOUT_BOTTOM },
141 { TemplateBIDI_HN, TemplateBIDI_UP, TemplateBIDI_HI, TemplateBIDI_BOTTOM },
142 { Template3STATE_HN, Template3STATE_UP, Template3STATE_HI, Template3STATE_BOTTOM },
143 { TemplateUNSPC_HN, TemplateUNSPC_UP, TemplateUNSPC_HI, TemplateUNSPC_BOTTOM }
144 };
145
146
RotateCW()147 LABEL_SPIN_STYLE LABEL_SPIN_STYLE::RotateCW()
148 {
149 SPIN newSpin = m_spin;
150
151 switch( m_spin )
152 {
153 case LABEL_SPIN_STYLE::LEFT: newSpin = LABEL_SPIN_STYLE::UP; break;
154 case LABEL_SPIN_STYLE::UP: newSpin = LABEL_SPIN_STYLE::RIGHT; break;
155 case LABEL_SPIN_STYLE::RIGHT: newSpin = LABEL_SPIN_STYLE::BOTTOM; break;
156 case LABEL_SPIN_STYLE::BOTTOM: newSpin = LABEL_SPIN_STYLE::LEFT; break;
157 default: wxLogWarning( "RotateCW encountered unknown current spin style" ); break;
158 }
159
160 return LABEL_SPIN_STYLE( newSpin );
161 }
162
163
RotateCCW()164 LABEL_SPIN_STYLE LABEL_SPIN_STYLE::RotateCCW()
165 {
166 SPIN newSpin = m_spin;
167
168 switch( m_spin )
169 {
170 case LABEL_SPIN_STYLE::LEFT: newSpin = LABEL_SPIN_STYLE::BOTTOM; break;
171 case LABEL_SPIN_STYLE::BOTTOM: newSpin = LABEL_SPIN_STYLE::RIGHT; break;
172 case LABEL_SPIN_STYLE::RIGHT: newSpin = LABEL_SPIN_STYLE::UP; break;
173 case LABEL_SPIN_STYLE::UP: newSpin = LABEL_SPIN_STYLE::LEFT; break;
174 default: wxLogWarning( "RotateCCW encountered unknown current spin style" ); break;
175 }
176
177 return LABEL_SPIN_STYLE( newSpin );
178 }
179
180
MirrorX()181 LABEL_SPIN_STYLE LABEL_SPIN_STYLE::MirrorX()
182 {
183 SPIN newSpin = m_spin;
184
185 switch( m_spin )
186 {
187 case LABEL_SPIN_STYLE::UP: newSpin = LABEL_SPIN_STYLE::BOTTOM; break;
188 case LABEL_SPIN_STYLE::BOTTOM: newSpin = LABEL_SPIN_STYLE::UP; break;
189 case LABEL_SPIN_STYLE::LEFT: break;
190 case LABEL_SPIN_STYLE::RIGHT: break;
191 default: wxLogWarning( "MirrorX encountered unknown current spin style" ); break;
192 }
193
194 return LABEL_SPIN_STYLE( newSpin );
195 }
196
197
MirrorY()198 LABEL_SPIN_STYLE LABEL_SPIN_STYLE::MirrorY()
199 {
200 SPIN newSpin = m_spin;
201
202 switch( m_spin )
203 {
204 case LABEL_SPIN_STYLE::LEFT: newSpin = LABEL_SPIN_STYLE::RIGHT; break;
205 case LABEL_SPIN_STYLE::RIGHT: newSpin = LABEL_SPIN_STYLE::LEFT; break;
206 case LABEL_SPIN_STYLE::UP: break;
207 case LABEL_SPIN_STYLE::BOTTOM: break;
208 default: wxLogWarning( "MirrorY encountered unknown current spin style" ); break;
209 }
210
211 return LABEL_SPIN_STYLE( newSpin );
212 }
213
214
SCH_TEXT(const wxPoint & pos,const wxString & text,KICAD_T aType)215 SCH_TEXT::SCH_TEXT( const wxPoint& pos, const wxString& text, KICAD_T aType ) :
216 SCH_ITEM( nullptr, aType ),
217 EDA_TEXT( text ),
218 m_shape( PINSHEETLABEL_SHAPE::PS_INPUT ),
219 m_isDangling( false ),
220 m_connectionType( CONNECTION_TYPE::NONE )
221 {
222 m_layer = LAYER_NOTES;
223
224 SetTextPos( pos );
225 SetLabelSpinStyle( LABEL_SPIN_STYLE::LEFT );
226 SetMultilineAllowed( true );
227 }
228
229
SCH_TEXT(const SCH_TEXT & aText)230 SCH_TEXT::SCH_TEXT( const SCH_TEXT& aText ) :
231 SCH_ITEM( aText ),
232 EDA_TEXT( aText ),
233 m_shape( aText.m_shape ),
234 m_isDangling( aText.m_isDangling ),
235 m_connectionType( aText.m_connectionType ),
236 m_spin_style( aText.m_spin_style )
237 { }
238
239
Clone() const240 EDA_ITEM* SCH_TEXT::Clone() const
241 {
242 return new SCH_TEXT( *this );
243 }
244
245
IncrementLabel(int aIncrement)246 bool SCH_TEXT::IncrementLabel( int aIncrement )
247 {
248 wxString text = GetText();
249 bool ReturnVal = IncrementLabelMember( text, aIncrement );
250
251 if( ReturnVal )
252 SetText( text );
253
254 return ReturnVal;
255 }
256
257
GetSchematicTextOffset(const RENDER_SETTINGS * aSettings) const258 wxPoint SCH_TEXT::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
259 {
260 wxPoint text_offset;
261
262 // add an offset to x (or y) position to aid readability of text on a wire or line
263 int dist = GetTextOffset( aSettings ) + GetPenWidth();
264
265 switch( GetLabelSpinStyle() )
266 {
267 case LABEL_SPIN_STYLE::UP:
268 case LABEL_SPIN_STYLE::BOTTOM:
269 text_offset.x = -dist;
270 break; // Vert Orientation
271 default:
272 case LABEL_SPIN_STYLE::LEFT:
273 case LABEL_SPIN_STYLE::RIGHT:
274 text_offset.y = -dist;
275 break; // Horiz Orientation
276 }
277
278 return text_offset;
279 }
280
281
MirrorHorizontally(int aCenter)282 void SCH_TEXT::MirrorHorizontally( int aCenter )
283 {
284 // Text is NOT really mirrored; it is moved to a suitable horizontal position
285 SetLabelSpinStyle( GetLabelSpinStyle().MirrorY() );
286
287 SetTextX( MIRRORVAL( GetTextPos().x, aCenter ) );
288 }
289
290
MirrorVertically(int aCenter)291 void SCH_TEXT::MirrorVertically( int aCenter )
292 {
293 // Text is NOT really mirrored; it is moved to a suitable vertical position
294 SetLabelSpinStyle( GetLabelSpinStyle().MirrorX() );
295
296 SetTextY( MIRRORVAL( GetTextPos().y, aCenter ) );
297 }
298
299
Rotate(const wxPoint & aCenter)300 void SCH_TEXT::Rotate( const wxPoint& aCenter )
301 {
302 wxPoint pt = GetTextPos();
303 RotatePoint( &pt, aCenter, 900 );
304 wxPoint offset = pt - GetTextPos();
305
306 Rotate90( false );
307
308 SetTextPos( GetTextPos() + offset );
309 }
310
311
Rotate90(bool aClockwise)312 void SCH_TEXT::Rotate90( bool aClockwise )
313 {
314 if( aClockwise )
315 SetLabelSpinStyle( GetLabelSpinStyle().RotateCW() );
316 else
317 SetLabelSpinStyle( GetLabelSpinStyle().RotateCCW() );
318 }
319
320
MirrorSpinStyle(bool aLeftRight)321 void SCH_TEXT::MirrorSpinStyle( bool aLeftRight )
322 {
323 if( aLeftRight )
324 SetLabelSpinStyle( GetLabelSpinStyle().MirrorY() );
325 else
326 SetLabelSpinStyle( GetLabelSpinStyle().MirrorX() );
327 }
328
329
SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle)330 void SCH_TEXT::SetLabelSpinStyle( LABEL_SPIN_STYLE aSpinStyle )
331 {
332 m_spin_style = aSpinStyle;
333
334 // Assume "Right" and Left" mean which side of the anchor the text will be on
335 // Thus we want to left justify text up against the anchor if we are on the right
336 switch( aSpinStyle )
337 {
338 default:
339 wxASSERT_MSG( 1, "Bad spin style" );
340 break;
341
342 case LABEL_SPIN_STYLE::RIGHT: // Horiz Normal Orientation
343 //
344 m_spin_style = LABEL_SPIN_STYLE::RIGHT; // Handle the error spin style by resetting
345 SetTextAngle( TEXT_ANGLE_HORIZ );
346 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
347 SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
348 break;
349
350 case LABEL_SPIN_STYLE::UP: // Vert Orientation UP
351 SetTextAngle( TEXT_ANGLE_VERT );
352 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
353 SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
354 break;
355
356 case LABEL_SPIN_STYLE::LEFT: // Horiz Orientation - Right justified
357 SetTextAngle( TEXT_ANGLE_HORIZ );
358 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
359 SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
360 break;
361
362 case LABEL_SPIN_STYLE::BOTTOM: // Vert Orientation BOTTOM
363 SetTextAngle( TEXT_ANGLE_VERT );
364 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
365 SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
366 break;
367 }
368 }
369
370
SwapData(SCH_ITEM * aItem)371 void SCH_TEXT::SwapData( SCH_ITEM* aItem )
372 {
373 SCH_TEXT* item = (SCH_TEXT*) aItem;
374
375 std::swap( m_layer, item->m_layer );
376
377 std::swap( m_shape, item->m_shape );
378 std::swap( m_isDangling, item->m_isDangling );
379 std::swap( m_spin_style, item->m_spin_style );
380
381 SwapText( *item );
382 SwapEffects( *item );
383 }
384
385
operator <(const SCH_ITEM & aItem) const386 bool SCH_TEXT::operator<( const SCH_ITEM& aItem ) const
387 {
388 if( Type() != aItem.Type() )
389 return Type() < aItem.Type();
390
391 auto other = static_cast<const SCH_TEXT*>( &aItem );
392
393 if( GetLayer() != other->GetLayer() )
394 return GetLayer() < other->GetLayer();
395
396 if( GetPosition().x != other->GetPosition().x )
397 return GetPosition().x < other->GetPosition().x;
398
399 if( GetPosition().y != other->GetPosition().y )
400 return GetPosition().y < other->GetPosition().y;
401
402 return GetText() < other->GetText();
403 }
404
405
GetTextOffset(const RENDER_SETTINGS * aSettings) const406 int SCH_TEXT::GetTextOffset( const RENDER_SETTINGS* aSettings ) const
407 {
408 double ratio;
409
410 if( aSettings )
411 ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_TextOffsetRatio;
412 else if( Schematic() )
413 ratio = Schematic()->Settings().m_TextOffsetRatio;
414 else
415 ratio = DEFAULT_TEXT_OFFSET_RATIO; // For previews (such as in Preferences), etc.
416
417 return KiROUND( ratio * GetTextSize().y );
418
419 return 0;
420 }
421
422
GetLabelBoxExpansion(const RENDER_SETTINGS * aSettings) const423 int SCH_TEXT::GetLabelBoxExpansion( const RENDER_SETTINGS* aSettings ) const
424 {
425 double ratio;
426
427 if( aSettings )
428 ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_LabelSizeRatio;
429 else if( Schematic() )
430 ratio = Schematic()->Settings().m_LabelSizeRatio;
431 else
432 ratio = DEFAULT_LABEL_SIZE_RATIO; // For previews (such as in Preferences), etc.
433
434 return KiROUND( ratio * GetTextSize().y );
435
436 return 0;
437 }
438
439
GetPenWidth() const440 int SCH_TEXT::GetPenWidth() const
441 {
442 return GetEffectiveTextPenWidth();
443 }
444
445
Print(const RENDER_SETTINGS * aSettings,const wxPoint & aOffset)446 void SCH_TEXT::Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset )
447 {
448 COLOR4D color = aSettings->GetLayerColor( m_layer );
449 wxPoint text_offset = aOffset + GetSchematicTextOffset( aSettings );
450
451 EDA_TEXT::Print( aSettings, text_offset, color );
452 }
453
454
GetEndPoints(std::vector<DANGLING_END_ITEM> & aItemList)455 void SCH_TEXT::GetEndPoints( std::vector <DANGLING_END_ITEM>& aItemList )
456 {
457 // Normal text labels cannot be tested for dangling ends.
458 if( Type() == SCH_TEXT_T )
459 return;
460
461 DANGLING_END_ITEM item( LABEL_END, this, GetTextPos() );
462 aItemList.push_back( item );
463 }
464
465
UpdateDanglingState(std::vector<DANGLING_END_ITEM> & aItemList,const SCH_SHEET_PATH * aPath)466 bool SCH_TEXT::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
467 const SCH_SHEET_PATH* aPath )
468 {
469 // Normal text labels cannot be tested for dangling ends.
470 if( Type() == SCH_TEXT_T )
471 return false;
472
473 bool previousState = m_isDangling;
474 m_isDangling = true;
475 m_connectionType = CONNECTION_TYPE::NONE;
476
477 for( unsigned ii = 0; ii < aItemList.size(); ii++ )
478 {
479 DANGLING_END_ITEM& item = aItemList[ii];
480
481 if( item.GetItem() == this )
482 continue;
483
484 switch( item.GetType() )
485 {
486 case PIN_END:
487 case LABEL_END:
488 case SHEET_LABEL_END:
489 case NO_CONNECT_END:
490 if( GetTextPos() == item.GetPosition() )
491 {
492 m_isDangling = false;
493
494 if( aPath && item.GetType() != PIN_END )
495 m_connected_items[ *aPath ].insert( static_cast<SCH_ITEM*>( item.GetItem() ) );
496 }
497
498 break;
499
500 case BUS_END:
501 m_connectionType = CONNECTION_TYPE::BUS;
502 KI_FALLTHROUGH;
503
504 case WIRE_END:
505 {
506 DANGLING_END_ITEM& nextItem = aItemList[++ii];
507
508 int accuracy = 1; // We have rounding issues with an accuracy of 0
509
510 m_isDangling = !TestSegmentHit( GetTextPos(), item.GetPosition(),
511 nextItem.GetPosition(), accuracy );
512
513 if( !m_isDangling )
514 {
515 if( m_connectionType != CONNECTION_TYPE::BUS )
516 m_connectionType = CONNECTION_TYPE::NET;
517
518 // Add the line to the connected items, since it won't be picked
519 // up by a search of intersecting connection points
520 if( aPath )
521 {
522 auto sch_item = static_cast<SCH_ITEM*>( item.GetItem() );
523 AddConnectionTo( *aPath, sch_item );
524 sch_item->AddConnectionTo( *aPath, this );
525 }
526 }
527 }
528 break;
529
530 default:
531 break;
532 }
533
534 if( !m_isDangling )
535 break;
536 }
537
538 if( m_isDangling )
539 m_connectionType = CONNECTION_TYPE::NONE;
540
541 return previousState != m_isDangling;
542 }
543
544
GetConnectionPoints() const545 std::vector<wxPoint> SCH_TEXT::GetConnectionPoints() const
546 {
547 // Normal text labels do not have connection points. All others do.
548 if( Type() == SCH_TEXT_T )
549 return {};
550
551 return { GetTextPos() };
552 }
553
554
GetBoundingBox() const555 const EDA_RECT SCH_TEXT::GetBoundingBox() const
556 {
557 EDA_RECT rect = GetTextBox();
558
559 if( GetTextAngle() != 0 ) // Rotate rect.
560 {
561 wxPoint pos = rect.GetOrigin();
562 wxPoint end = rect.GetEnd();
563
564 RotatePoint( &pos, GetTextPos(), GetTextAngle() );
565 RotatePoint( &end, GetTextPos(), GetTextAngle() );
566
567 rect.SetOrigin( pos );
568 rect.SetEnd( end );
569 }
570
571 rect.Normalize();
572 return rect;
573 }
574
575
getElectricalTypeLabel(PINSHEETLABEL_SHAPE aType)576 wxString getElectricalTypeLabel( PINSHEETLABEL_SHAPE aType )
577 {
578 switch( aType )
579 {
580 case PINSHEETLABEL_SHAPE::PS_INPUT: return _( "Input" );
581 case PINSHEETLABEL_SHAPE::PS_OUTPUT: return _( "Output" );
582 case PINSHEETLABEL_SHAPE::PS_BIDI: return _( "Bidirectional" );
583 case PINSHEETLABEL_SHAPE::PS_TRISTATE: return _( "Tri-State" );
584 case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED: return _( "Passive" );
585 default: return wxT( "???" );
586 }
587 }
588
589
GetContextualTextVars(wxArrayString * aVars) const590 void SCH_TEXT::GetContextualTextVars( wxArrayString* aVars ) const
591 {
592 if( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
593 aVars->push_back( wxT( "CONNECTION_TYPE" ) );
594
595 if( Type() == SCH_SHEET_PIN_T && m_parent )
596 static_cast<SCH_SHEET*>( m_parent )->GetContextualTextVars( aVars );
597 }
598
599
GetShownText(int aDepth) const600 wxString SCH_TEXT::GetShownText( int aDepth ) const
601 {
602 std::function<bool( wxString* )> textResolver =
603 [&]( wxString* token ) -> bool
604 {
605 if( ( Type() == SCH_GLOBAL_LABEL_T
606 || Type() == SCH_HIER_LABEL_T
607 || Type() == SCH_SHEET_PIN_T )
608 && token->IsSameAs( wxT( "CONNECTION_TYPE" ) ) )
609 {
610 *token = getElectricalTypeLabel( GetShape() );
611 return true;
612 }
613
614 if( Type() == SCH_SHEET_PIN_T && m_parent )
615 {
616 SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
617
618 if( sheet->ResolveTextVar( token, aDepth ) )
619 return true;
620 }
621
622 if( Type() == SCH_TEXT_T )
623 {
624 if( token->Contains( ':' ) )
625 {
626 if( Schematic()->ResolveCrossReference( token, aDepth ) )
627 return true;
628 }
629 else
630 {
631 SCHEMATIC* schematic = Schematic();
632 SCH_SHEET* sheet = schematic ? schematic->CurrentSheet().Last() : nullptr;
633
634 if( sheet && sheet->ResolveTextVar( token, aDepth + 1 ) )
635 return true;
636 }
637 }
638
639 return false;
640 };
641
642 std::function<bool( wxString* )> schematicTextResolver =
643 [&]( wxString* token ) -> bool
644 {
645 return Schematic()->ResolveTextVar( token, aDepth + 1 );
646 };
647
648 wxString text = EDA_TEXT::GetShownText();
649
650 if( text == "~" ) // Legacy placeholder for empty string
651 {
652 text = "";
653 }
654 else if( HasTextVars() )
655 {
656 wxCHECK_MSG( Schematic(), wxEmptyString, "No parent SCHEMATIC set for SCH_TEXT!" );
657
658 PROJECT* project = nullptr;
659
660 if( Schematic() )
661 project = &Schematic()->Prj();
662
663 if( aDepth < 10 )
664 text = ExpandTextVars( text, &textResolver, &schematicTextResolver, project );
665 }
666
667 return text;
668 }
669
670
GetSelectMenuText(EDA_UNITS aUnits) const671 wxString SCH_TEXT::GetSelectMenuText( EDA_UNITS aUnits ) const
672 {
673 return wxString::Format( _( "Graphic Text '%s'" ), ShortenedShownText() );
674 }
675
676
GetMenuImage() const677 BITMAPS SCH_TEXT::GetMenuImage() const
678 {
679 return BITMAPS::text;
680 }
681
682
HitTest(const wxPoint & aPosition,int aAccuracy) const683 bool SCH_TEXT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
684 {
685 EDA_RECT bBox = GetBoundingBox();
686 bBox.Inflate( aAccuracy );
687 return bBox.Contains( aPosition );
688 }
689
690
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const691 bool SCH_TEXT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
692 {
693 EDA_RECT bBox = GetBoundingBox();
694 bBox.Inflate( aAccuracy );
695
696 if( aContained )
697 return aRect.Contains( bBox );
698
699 return aRect.Intersects( bBox );
700 }
701
702
Plot(PLOTTER * aPlotter) const703 void SCH_TEXT::Plot( PLOTTER* aPlotter ) const
704 {
705 static std::vector<wxPoint> s_poly;
706
707 RENDER_SETTINGS* settings = aPlotter->RenderSettings();
708 SCH_CONNECTION* connection = Connection();
709 int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
710 COLOR4D color = settings->GetLayerColor( layer );
711 int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
712
713 penWidth = std::max( penWidth, settings->GetMinPenWidth() );
714 aPlotter->SetCurrentLineWidth( penWidth );
715
716 if( IsMultilineAllowed() )
717 {
718 std::vector<wxPoint> positions;
719 wxArrayString strings_list;
720 wxStringSplit( GetShownText(), strings_list, '\n' );
721 positions.reserve( strings_list.Count() );
722
723 GetLinePositions( positions, (int) strings_list.Count() );
724
725 for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
726 {
727 wxPoint textpos = positions[ii] + GetSchematicTextOffset( aPlotter->RenderSettings() );
728 wxString& txt = strings_list.Item( ii );
729 aPlotter->Text( textpos, color, txt, GetTextAngle(), GetTextSize(), GetHorizJustify(),
730 GetVertJustify(), penWidth, IsItalic(), IsBold() );
731 }
732 }
733 else
734 {
735 wxPoint textpos = GetTextPos() + GetSchematicTextOffset( aPlotter->RenderSettings() );
736
737 aPlotter->Text( textpos, color, GetShownText(), GetTextAngle(), GetTextSize(),
738 GetHorizJustify(), GetVertJustify(), penWidth, IsItalic(), IsBold() );
739 }
740
741 // Draw graphic symbol for global or hierarchical labels
742 CreateGraphicShape( aPlotter->RenderSettings(), s_poly, GetTextPos() );
743
744 if( s_poly.size() )
745 aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
746 }
747
748
GetMsgPanelInfo(EDA_DRAW_FRAME * aFrame,std::vector<MSG_PANEL_ITEM> & aList)749 void SCH_TEXT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
750 {
751 wxString msg;
752
753 switch( Type() )
754 {
755 case SCH_TEXT_T: msg = _( "Graphic Text" ); break;
756 case SCH_LABEL_T: msg = _( "Label" ); break;
757 case SCH_GLOBAL_LABEL_T: msg = _( "Global Label" ); break;
758 case SCH_HIER_LABEL_T: msg = _( "Hierarchical Label" ); break;
759 case SCH_SHEET_PIN_T: msg = _( "Hierarchical Sheet Pin" ); break;
760 default: return;
761 }
762
763 // Don't use GetShownText() here; we want to show the user the variable references
764 aList.emplace_back( msg, UnescapeString( GetText() ) );
765
766 // Display electrical type if it is relevant
767 if( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
768 aList.emplace_back( _( "Type" ), getElectricalTypeLabel( GetShape() ) );
769
770 wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
771 int style = 0;
772
773 if( IsItalic() )
774 style = 1;
775
776 if( IsBold() )
777 style += 2;
778
779 aList.emplace_back( _( "Style" ), textStyle[style] );
780
781 aList.emplace_back( _( "Text Size" ), MessageTextFromValue( aFrame->GetUserUnits(),
782 GetTextWidth() ) );
783
784 switch( GetLabelSpinStyle() )
785 {
786 case LABEL_SPIN_STYLE::LEFT: msg = _( "Align right" ); break;
787 case LABEL_SPIN_STYLE::UP: msg = _( "Align bottom" ); break;
788 case LABEL_SPIN_STYLE::RIGHT: msg = _( "Align left" ); break;
789 case LABEL_SPIN_STYLE::BOTTOM: msg = _( "Align top" ); break;
790 default: msg = wxT( "???" ); break;
791 }
792
793 aList.emplace_back( _( "Justification" ), msg );
794
795 SCH_CONNECTION* conn = nullptr;
796
797 if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
798 conn = Connection();
799
800 if( conn )
801 {
802 conn->AppendInfoToMsgPanel( aList );
803
804 if( !conn->IsBus() )
805 {
806 NET_SETTINGS& netSettings = Schematic()->Prj().GetProjectFile().NetSettings();
807 const wxString& netname = conn->Name( true );
808
809 if( netSettings.m_NetClassAssignments.count( netname ) )
810 {
811 const wxString& netclassName = netSettings.m_NetClassAssignments[ netname ];
812 aList.emplace_back( _( "Assigned Netclass" ), netclassName );
813 }
814 }
815 }
816 }
817
818
819 #if defined(DEBUG)
820
Show(int nestLevel,std::ostream & os) const821 void SCH_TEXT::Show( int nestLevel, std::ostream& os ) const
822 {
823 // XML output:
824 wxString s = GetClass();
825
826 NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str()
827 << " layer=\"" << m_layer << '"'
828 << " shape=\"" << static_cast<int>( m_shape ) << '"'
829 << " dangling=\"" << m_isDangling << '"'
830 << '>'
831 << TO_UTF8( GetText() )
832 << "</" << s.Lower().mb_str() << ">\n";
833 }
834
835 #endif
836
837
SCH_LABEL(const wxPoint & pos,const wxString & text)838 SCH_LABEL::SCH_LABEL( const wxPoint& pos, const wxString& text )
839 : SCH_TEXT( pos, text, SCH_LABEL_T )
840 {
841 m_layer = LAYER_LOCLABEL;
842 m_shape = PINSHEETLABEL_SHAPE::PS_INPUT;
843 m_isDangling = true;
844 SetMultilineAllowed( false );
845 }
846
847
Clone() const848 EDA_ITEM* SCH_LABEL::Clone() const
849 {
850 return new SCH_LABEL( *this );
851 }
852
853
IsType(const KICAD_T aScanTypes[]) const854 bool SCH_LABEL::IsType( const KICAD_T aScanTypes[] ) const
855 {
856 static KICAD_T wireTypes[] = { SCH_LINE_LOCATE_WIRE_T, SCH_PIN_T, EOT };
857 static KICAD_T busTypes[] = { SCH_LINE_LOCATE_BUS_T, EOT };
858
859 if( SCH_ITEM::IsType( aScanTypes ) )
860 return true;
861
862 wxCHECK_MSG( Schematic(), false, "No parent SCHEMATIC set for SCH_LABEL!" );
863
864 SCH_SHEET_PATH current = Schematic()->CurrentSheet();
865
866 for( const KICAD_T* p = aScanTypes; *p != EOT; ++p )
867 {
868 if( *p == SCH_LABEL_LOCATE_WIRE_T )
869 {
870 wxASSERT( m_connected_items.count( current ) );
871
872 for( SCH_ITEM* connection : m_connected_items.at( current ) )
873 {
874 if( connection->IsType( wireTypes ) )
875 return true;
876 }
877 }
878 else if ( *p == SCH_LABEL_LOCATE_BUS_T )
879 {
880 wxASSERT( m_connected_items.count( current ) );
881
882 for( SCH_ITEM* connection : m_connected_items.at( current ) )
883 {
884 if( connection->IsType( busTypes ) )
885 return true;
886 }
887 }
888 }
889
890 return false;
891 }
892
893
GetBoundingBox() const894 const EDA_RECT SCH_LABEL::GetBoundingBox() const
895 {
896 EDA_RECT rect = GetTextBox();
897
898 rect.Offset( 0, -GetTextOffset() );
899
900 if( GetTextAngle() != 0.0 )
901 {
902 // Rotate rect
903 wxPoint pos = rect.GetOrigin();
904 wxPoint end = rect.GetEnd();
905
906 RotatePoint( &pos, GetTextPos(), GetTextAngle() );
907 RotatePoint( &end, GetTextPos(), GetTextAngle() );
908
909 rect.SetOrigin( pos );
910 rect.SetEnd( end );
911
912 rect.Normalize();
913 }
914
915 // Labels have a position point that is outside of the TextBox
916 rect.Merge( GetPosition() );
917
918 return rect;
919 }
920
921
ViewGetLayers(int aLayers[],int & aCount) const922 void SCH_TEXT::ViewGetLayers( int aLayers[], int& aCount ) const
923 {
924 aCount = 0;
925
926 if( m_layer != LAYER_NOTES )
927 aLayers[ aCount++ ] = LAYER_DANGLING;
928
929 aLayers[ aCount++ ] = m_layer;
930 aLayers[ aCount++ ] = LAYER_SELECTION_SHADOWS;
931 }
932
933
GetSelectMenuText(EDA_UNITS aUnits) const934 wxString SCH_LABEL::GetSelectMenuText( EDA_UNITS aUnits ) const
935 {
936 return wxString::Format( _( "Label '%s'" ), ShortenedShownText() );
937 }
938
939
GetMenuImage() const940 BITMAPS SCH_LABEL::GetMenuImage() const
941 {
942 return BITMAPS::add_line_label;
943 }
944
945
SCH_GLOBALLABEL(const wxPoint & pos,const wxString & text)946 SCH_GLOBALLABEL::SCH_GLOBALLABEL( const wxPoint& pos, const wxString& text ) :
947 SCH_TEXT( pos, text, SCH_GLOBAL_LABEL_T ),
948 m_intersheetRefsField( { 0, 0 }, 0, this )
949 {
950 m_layer = LAYER_GLOBLABEL;
951 m_shape = PINSHEETLABEL_SHAPE::PS_BIDI;
952 m_isDangling = true;
953 SetMultilineAllowed( false );
954
955 SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
956
957 m_intersheetRefsField.SetText( wxT( "${INTERSHEET_REFS}" ) );
958 m_intersheetRefsField.SetLayer( LAYER_GLOBLABEL );
959 m_intersheetRefsField.SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
960 m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
961 }
962
963
SCH_GLOBALLABEL(const SCH_GLOBALLABEL & aGlobalLabel)964 SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
965 SCH_TEXT( aGlobalLabel ),
966 m_intersheetRefsField( { 0, 0 }, 0, this )
967 {
968 m_intersheetRefsField = aGlobalLabel.m_intersheetRefsField;
969
970 // Re-parent the fields, which before this had aGlobalLabel as parent
971 m_intersheetRefsField.SetParent( this );
972
973 m_fieldsAutoplaced = aGlobalLabel.m_fieldsAutoplaced;
974 }
975
976
Clone() const977 EDA_ITEM* SCH_GLOBALLABEL::Clone() const
978 {
979 return new SCH_GLOBALLABEL( *this );
980 }
981
982
SwapData(SCH_ITEM * aItem)983 void SCH_GLOBALLABEL::SwapData( SCH_ITEM* aItem )
984 {
985 SCH_TEXT::SwapData( aItem );
986
987 SCH_GLOBALLABEL* globalLabel = static_cast<SCH_GLOBALLABEL*>( aItem );
988
989 // Swap field data wholesale...
990 std::swap( m_intersheetRefsField, globalLabel->m_intersheetRefsField );
991
992 // ...and then reset parent pointers.
993 globalLabel->m_intersheetRefsField.SetParent( globalLabel );
994 m_intersheetRefsField.SetParent( this );
995 }
996
997
Visit(INSPECTOR aInspector,void * testData,const KICAD_T aFilterTypes[])998 SEARCH_RESULT SCH_GLOBALLABEL::Visit( INSPECTOR aInspector, void* testData,
999 const KICAD_T aFilterTypes[] )
1000 {
1001 KICAD_T stype;
1002
1003 for( const KICAD_T* p = aFilterTypes; (stype = *p) != EOT; ++p )
1004 {
1005 // If caller wants to inspect my type
1006 if( stype == SCH_LOCATE_ANY_T || stype == Type() )
1007 {
1008 if( SEARCH_RESULT::QUIT == aInspector( this, nullptr ) )
1009 return SEARCH_RESULT::QUIT;
1010 }
1011
1012 if( stype == SCH_LOCATE_ANY_T || stype == SCH_FIELD_T )
1013 {
1014 if( SEARCH_RESULT::QUIT == aInspector( GetIntersheetRefs(), this ) )
1015 return SEARCH_RESULT::QUIT;
1016 }
1017 }
1018
1019 return SEARCH_RESULT::CONTINUE;
1020 }
1021
1022
RunOnChildren(const std::function<void (SCH_ITEM *)> & aFunction)1023 void SCH_GLOBALLABEL::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
1024 {
1025 aFunction( &m_intersheetRefsField );
1026 }
1027
1028
GetSchematicTextOffset(const RENDER_SETTINGS * aSettings) const1029 wxPoint SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
1030 {
1031 int horiz = GetLabelBoxExpansion( aSettings );
1032
1033 // Center the text on the center line of "E" instead of "R" to make room for an overbar
1034 int vert = GetTextHeight() * 0.0715;
1035
1036 switch( m_shape )
1037 {
1038 case PINSHEETLABEL_SHAPE::PS_INPUT:
1039 case PINSHEETLABEL_SHAPE::PS_BIDI:
1040 case PINSHEETLABEL_SHAPE::PS_TRISTATE:
1041 horiz += GetTextHeight() * 3 / 4; // Use three-quarters-height as proxy for triangle size
1042 break;
1043
1044 case PINSHEETLABEL_SHAPE::PS_OUTPUT:
1045 case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED:
1046 default:
1047 break;
1048 }
1049
1050 switch( GetLabelSpinStyle() )
1051 {
1052 default:
1053 case LABEL_SPIN_STYLE::LEFT: return wxPoint( -horiz, vert );
1054 case LABEL_SPIN_STYLE::UP: return wxPoint( vert, -horiz );
1055 case LABEL_SPIN_STYLE::RIGHT: return wxPoint( horiz, vert );
1056 case LABEL_SPIN_STYLE::BOTTOM: return wxPoint( vert, horiz );
1057 }
1058 }
1059
1060
SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle)1061 void SCH_GLOBALLABEL::SetLabelSpinStyle( LABEL_SPIN_STYLE aSpinStyle )
1062 {
1063 m_spin_style = aSpinStyle;
1064
1065 switch( aSpinStyle )
1066 {
1067 default:
1068 wxASSERT_MSG( 1, "Bad spin style" );
1069 m_spin_style = LABEL_SPIN_STYLE::RIGHT;
1070 KI_FALLTHROUGH;
1071
1072 case LABEL_SPIN_STYLE::RIGHT: // Horiz Normal Orientation
1073 SetTextAngle( TEXT_ANGLE_HORIZ );
1074 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1075 break;
1076
1077 case LABEL_SPIN_STYLE::UP: // Vert Orientation UP
1078 SetTextAngle( TEXT_ANGLE_VERT );
1079 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1080 break;
1081
1082 case LABEL_SPIN_STYLE::LEFT: // Horiz Orientation
1083 SetTextAngle( TEXT_ANGLE_HORIZ );
1084 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1085 break;
1086
1087 case LABEL_SPIN_STYLE::BOTTOM: // Vert Orientation BOTTOM
1088 SetTextAngle( TEXT_ANGLE_VERT );
1089 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1090 break;
1091 }
1092 }
1093
1094
Rotate(const wxPoint & aCenter)1095 void SCH_GLOBALLABEL::Rotate( const wxPoint& aCenter )
1096 {
1097 wxPoint pt = GetTextPos();
1098 RotatePoint( &pt, aCenter, 900 );
1099 wxPoint offset = pt - GetTextPos();
1100
1101 Rotate90( false );
1102
1103 SetTextPos( GetTextPos() + offset );
1104 m_intersheetRefsField.SetTextPos( m_intersheetRefsField.GetTextPos() + offset );
1105 }
1106
1107
Rotate90(bool aClockwise)1108 void SCH_GLOBALLABEL::Rotate90( bool aClockwise )
1109 {
1110 SCH_TEXT::Rotate90( aClockwise );
1111
1112 if( m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_VERT
1113 && m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
1114 {
1115 if( !aClockwise )
1116 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1117
1118 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_HORIZ );
1119 }
1120 else if( m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_VERT
1121 && m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
1122 {
1123 if( !aClockwise )
1124 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1125
1126 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_HORIZ );
1127 }
1128 else if( m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_HORIZ
1129 && m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
1130 {
1131 if( aClockwise )
1132 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1133
1134 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_VERT );
1135 }
1136 else if( m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_HORIZ
1137 && m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
1138 {
1139 if( aClockwise )
1140 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1141
1142 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_VERT );
1143 }
1144
1145 wxPoint pos = m_intersheetRefsField.GetTextPos();
1146 RotatePoint( &pos, GetPosition(), aClockwise ? -900 : 900 );
1147 m_intersheetRefsField.SetTextPos( pos );
1148 }
1149
1150
MirrorSpinStyle(bool aLeftRight)1151 void SCH_GLOBALLABEL::MirrorSpinStyle( bool aLeftRight )
1152 {
1153 SCH_TEXT::MirrorSpinStyle( aLeftRight );
1154
1155 if( ( aLeftRight && m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_HORIZ )
1156 || ( !aLeftRight && m_intersheetRefsField.GetTextAngle() == TEXT_ANGLE_VERT ) )
1157 {
1158 if( m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
1159 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1160 else
1161 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1162 }
1163
1164 wxPoint pos = m_intersheetRefsField.GetTextPos();
1165 wxPoint delta = GetPosition() - pos;
1166
1167 if( aLeftRight )
1168 pos.x = GetPosition().x + delta.x;
1169 else
1170 pos.y = GetPosition().y + delta.y;
1171
1172 m_intersheetRefsField.SetTextPos( pos );
1173 }
1174
1175
MirrorHorizontally(int aCenter)1176 void SCH_GLOBALLABEL::MirrorHorizontally( int aCenter )
1177 {
1178 wxPoint old_pos = GetPosition();
1179 SCH_TEXT::MirrorHorizontally( aCenter );
1180
1181 if( m_intersheetRefsField.GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
1182 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1183 else
1184 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1185
1186 wxPoint pos = m_intersheetRefsField.GetTextPos();
1187 wxPoint delta = old_pos - pos;
1188 pos.x = GetPosition().x + delta.x;
1189
1190 m_intersheetRefsField.SetPosition( pos );
1191 }
1192
1193
MirrorVertically(int aCenter)1194 void SCH_GLOBALLABEL::MirrorVertically( int aCenter )
1195 {
1196 wxPoint old_pos = GetPosition();
1197 SCH_TEXT::MirrorVertically( aCenter );
1198 wxPoint pos = m_intersheetRefsField.GetTextPos();
1199 wxPoint delta = old_pos - pos;
1200 pos.y = GetPosition().y + delta.y;
1201
1202 m_intersheetRefsField.SetPosition( pos );
1203 }
1204
1205
UpdateIntersheetRefProps()1206 void SCH_GLOBALLABEL::UpdateIntersheetRefProps()
1207 {
1208 m_intersheetRefsField.SetTextSize( GetTextSize() );
1209 m_intersheetRefsField.SetItalic( IsItalic() );
1210 m_intersheetRefsField.SetBold( IsBold() );
1211 m_intersheetRefsField.SetTextThickness( GetTextThickness() );
1212
1213 if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
1214 AutoplaceFields( nullptr, false );
1215 }
1216
1217
AutoplaceFields(SCH_SCREEN * aScreen,bool aManual)1218 void SCH_GLOBALLABEL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
1219 {
1220 int margin = GetTextOffset() * 2;
1221 int labelLen = GetBoundingBoxBase().GetSizeMax();
1222 int penOffset = GetPenWidth() / 2;
1223
1224 // Set both axes to penOffset; we're going to overwrite the text axis below
1225 wxPoint offset( -penOffset, -penOffset );
1226
1227 switch( GetLabelSpinStyle() )
1228 {
1229 default:
1230 case LABEL_SPIN_STYLE::LEFT:
1231 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_HORIZ );
1232 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1233 offset.x = - ( labelLen + margin / 2 );
1234 break;
1235
1236 case LABEL_SPIN_STYLE::UP:
1237 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_VERT );
1238 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1239 offset.y = - ( labelLen + margin / 2 );
1240 break;
1241
1242 case LABEL_SPIN_STYLE::RIGHT:
1243 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_HORIZ );
1244 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1245 offset.x = labelLen + margin /2 ;
1246 break;
1247
1248 case LABEL_SPIN_STYLE::BOTTOM:
1249 m_intersheetRefsField.SetTextAngle( TEXT_ANGLE_VERT );
1250 m_intersheetRefsField.SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1251 offset.y = labelLen + margin / 2;
1252 break;
1253 }
1254
1255 m_intersheetRefsField.SetTextPos( GetPosition() + offset );
1256
1257 m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
1258 }
1259
1260
ResolveTextVar(wxString * token,int aDepth) const1261 bool SCH_GLOBALLABEL::ResolveTextVar( wxString* token, int aDepth ) const
1262 {
1263 if( token->IsSameAs( wxT( "INTERSHEET_REFS" ) ) && Schematic() )
1264 {
1265 SCHEMATIC_SETTINGS& settings = Schematic()->Settings();
1266 wxString ref;
1267 auto it = Schematic()->GetPageRefsMap().find( GetText() );
1268
1269 if( it == Schematic()->GetPageRefsMap().end() )
1270 {
1271 ref = "?";
1272 }
1273 else
1274 {
1275 std::vector<wxString> pageListCopy;
1276
1277 pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
1278 std::sort( pageListCopy.begin(), pageListCopy.end(),
1279 []( const wxString& a, const wxString& b ) -> bool
1280 {
1281 return StrNumCmp( a, b, true ) < 0;
1282 } );
1283
1284 if( !settings.m_IntersheetRefsListOwnPage )
1285 {
1286 wxString currentPage = Schematic()->CurrentSheet().GetPageNumber();
1287 alg::delete_matching( pageListCopy, currentPage );
1288 }
1289
1290 if( ( settings.m_IntersheetRefsFormatShort ) && ( pageListCopy.size() > 2 ) )
1291 {
1292 ref.Append( wxString::Format( wxT( "%s..%s" ),
1293 pageListCopy.front(),
1294 pageListCopy.back() ) );
1295 }
1296 else
1297 {
1298 for( const wxString& pageNo : pageListCopy )
1299 ref.Append( wxString::Format( wxT( "%s," ), pageNo ) );
1300
1301 if( !ref.IsEmpty() && ref.Last() == ',' )
1302 ref.RemoveLast();
1303 }
1304 }
1305
1306 *token = settings.m_IntersheetRefsPrefix + ref + settings.m_IntersheetRefsSuffix;
1307 return true;
1308 }
1309
1310 return false;
1311 }
1312
1313
Print(const RENDER_SETTINGS * aSettings,const wxPoint & aOffset)1314 void SCH_GLOBALLABEL::Print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset )
1315 {
1316 static std::vector<wxPoint> s_poly;
1317
1318 SCH_CONNECTION* connection = Connection();
1319 int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
1320 wxDC* DC = aSettings->GetPrintDC();
1321 COLOR4D color = aSettings->GetLayerColor( layer );
1322 int penWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
1323 wxPoint text_offset = aOffset + GetSchematicTextOffset( aSettings );
1324
1325 EDA_TEXT::Print( aSettings, text_offset, color );
1326
1327 CreateGraphicShape( aSettings, s_poly, GetTextPos() + aOffset );
1328 GRPoly( nullptr, DC, s_poly.size(), &s_poly[0], false, penWidth, color, color );
1329
1330 if( Schematic()->Settings().m_IntersheetRefsShow )
1331 m_intersheetRefsField.Print( aSettings, aOffset );
1332 }
1333
1334
Plot(PLOTTER * aPlotter) const1335 void SCH_GLOBALLABEL::Plot( PLOTTER* aPlotter ) const
1336 {
1337 SCH_TEXT::Plot( aPlotter );
1338
1339 bool show = Schematic()->Settings().m_IntersheetRefsShow;
1340
1341 if ( show )
1342 m_intersheetRefsField.Plot( aPlotter );
1343 }
1344
1345
CreateGraphicShape(const RENDER_SETTINGS * aRenderSettings,std::vector<wxPoint> & aPoints,const wxPoint & Pos) const1346 void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
1347 std::vector<wxPoint>& aPoints, const wxPoint& Pos ) const
1348 {
1349 int margin = GetLabelBoxExpansion( aRenderSettings );
1350 int halfSize = ( GetTextHeight() / 2 ) + margin;
1351 int linewidth = GetPenWidth();
1352 int symb_len = LenSize( GetShownText(), linewidth ) + 2 * margin;
1353
1354 int x = symb_len + linewidth + 3;
1355 int y = halfSize + linewidth + 3;
1356
1357 aPoints.clear();
1358
1359 // Create outline shape : 6 points
1360 aPoints.emplace_back( wxPoint( 0, 0 ) );
1361 aPoints.emplace_back( wxPoint( 0, -y ) ); // Up
1362 aPoints.emplace_back( wxPoint( -x, -y ) ); // left
1363 aPoints.emplace_back( wxPoint( -x, 0 ) ); // Up left
1364 aPoints.emplace_back( wxPoint( -x, y ) ); // left down
1365 aPoints.emplace_back( wxPoint( 0, y ) ); // down
1366
1367 int x_offset = 0;
1368
1369 switch( m_shape )
1370 {
1371 case PINSHEETLABEL_SHAPE::PS_INPUT:
1372 x_offset = -halfSize;
1373 aPoints[0].x += halfSize;
1374 break;
1375
1376 case PINSHEETLABEL_SHAPE::PS_OUTPUT:
1377 aPoints[3].x -= halfSize;
1378 break;
1379
1380 case PINSHEETLABEL_SHAPE::PS_BIDI:
1381 case PINSHEETLABEL_SHAPE::PS_TRISTATE:
1382 x_offset = -halfSize;
1383 aPoints[0].x += halfSize;
1384 aPoints[3].x -= halfSize;
1385 break;
1386
1387 case PINSHEETLABEL_SHAPE::PS_UNSPECIFIED:
1388 default:
1389 break;
1390 }
1391
1392 int angle = 0;
1393
1394 switch( GetLabelSpinStyle() )
1395 {
1396 default:
1397 case LABEL_SPIN_STYLE::LEFT: break;
1398 case LABEL_SPIN_STYLE::UP: angle = -900; break;
1399 case LABEL_SPIN_STYLE::RIGHT: angle = 1800; break;
1400 case LABEL_SPIN_STYLE::BOTTOM: angle = 900; break;
1401 }
1402
1403 // Rotate outlines and move corners in real position
1404 for( wxPoint& aPoint : aPoints )
1405 {
1406 aPoint.x += x_offset;
1407
1408 if( angle )
1409 RotatePoint( &aPoint, angle );
1410
1411 aPoint += Pos;
1412 }
1413
1414 aPoints.push_back( aPoints[0] ); // closing
1415 }
1416
1417
GetBoundingBoxBase() const1418 const EDA_RECT SCH_GLOBALLABEL::GetBoundingBoxBase() const
1419 {
1420 // build the bounding box on the global label only, without taking in account
1421 // the intersheets references, just the bounding box of the graphic shape
1422 int x = GetTextPos().x;
1423 int y = GetTextPos().y;
1424 int penWidth = GetEffectiveTextPenWidth();
1425 int margin = GetTextOffset();
1426 int height = ( ( GetTextHeight() * 15 ) / 10 ) + penWidth + margin;
1427 int length = LenSize( GetShownText(), penWidth )
1428 + height // add height for triangular shapes
1429 - margin; // margin added to height not needed here
1430
1431 int dx, dy;
1432
1433 switch( GetLabelSpinStyle() ) // respect orientation
1434 {
1435 default:
1436 case LABEL_SPIN_STYLE::LEFT:
1437 dx = -length;
1438 dy = height;
1439 y -= height / 2;
1440 break;
1441
1442 case LABEL_SPIN_STYLE::UP:
1443 dx = height;
1444 dy = -length;
1445 x -= height / 2;
1446 break;
1447
1448 case LABEL_SPIN_STYLE::RIGHT:
1449 dx = length;
1450 dy = height;
1451 y -= height / 2;
1452 break;
1453
1454 case LABEL_SPIN_STYLE::BOTTOM:
1455 dx = height;
1456 dy = length;
1457 x -= height / 2;
1458 break;
1459 }
1460
1461 EDA_RECT box( wxPoint( x, y ), wxSize( dx, dy ) );
1462
1463 box.Normalize();
1464 return box;
1465 }
1466
1467
GetBoundingBox() const1468 const EDA_RECT SCH_GLOBALLABEL::GetBoundingBox() const
1469 {
1470 // build the bounding box on the global label only, including the intersheets references
1471 // full bounding box if they are shown
1472
1473 EDA_RECT box( GetBoundingBoxBase() );
1474
1475 // Note: Schematic() can be null in preference preview panel
1476 if( Schematic() && Schematic()->Settings().m_IntersheetRefsShow )
1477 {
1478 box.Merge( m_intersheetRefsField.GetBoundingBox() );
1479 box.Normalize();
1480 }
1481
1482 return box;
1483 }
1484
1485
GetSelectMenuText(EDA_UNITS aUnits) const1486 wxString SCH_GLOBALLABEL::GetSelectMenuText( EDA_UNITS aUnits ) const
1487 {
1488 return wxString::Format( _( "Global Label '%s'" ), ShortenedShownText() );
1489 }
1490
1491
GetMenuImage() const1492 BITMAPS SCH_GLOBALLABEL::GetMenuImage() const
1493 {
1494 return BITMAPS::add_glabel;
1495 }
1496
1497
HitTest(const wxPoint & aPosition,int aAccuracy) const1498 bool SCH_GLOBALLABEL::HitTest( const wxPoint& aPosition, int aAccuracy ) const
1499 {
1500 EDA_RECT bbox = GetBoundingBoxBase();
1501 bbox.Inflate( aAccuracy );
1502
1503 if( !bbox.Contains( aPosition ) )
1504 {
1505 if( Schematic() && Schematic()->Settings().m_IntersheetRefsShow )
1506 {
1507 bbox = m_intersheetRefsField.GetBoundingBox();
1508 bbox.Inflate( aAccuracy );
1509
1510 return bbox.Contains( aPosition );
1511 }
1512
1513 return false;
1514 }
1515
1516 return true;
1517 }
1518
1519
HitTest(const EDA_RECT & aRect,bool aContained,int aAccuracy) const1520 bool SCH_GLOBALLABEL::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
1521 {
1522 EDA_RECT bbox = GetBoundingBoxBase();
1523
1524 if( aContained )
1525 {
1526 if( Schematic() && Schematic()->Settings().m_IntersheetRefsShow )
1527 bbox.Merge( m_intersheetRefsField.GetBoundingBox() );
1528
1529 bbox.Inflate( aAccuracy );
1530 return aRect.Contains( bbox );
1531 }
1532
1533 bbox.Inflate( aAccuracy );
1534
1535 if( aRect.Intersects( bbox ) )
1536 return true;
1537
1538 if( Schematic() && Schematic()->Settings().m_IntersheetRefsShow )
1539 {
1540 bbox = m_intersheetRefsField.GetBoundingBox();
1541 bbox.Inflate( aAccuracy );
1542
1543 return aRect.Intersects( bbox );
1544 }
1545
1546 return false;
1547 }
1548
1549
SCH_HIERLABEL(const wxPoint & pos,const wxString & text,KICAD_T aType)1550 SCH_HIERLABEL::SCH_HIERLABEL( const wxPoint& pos, const wxString& text, KICAD_T aType )
1551 : SCH_TEXT( pos, text, aType )
1552 {
1553 m_layer = LAYER_HIERLABEL;
1554 m_shape = PINSHEETLABEL_SHAPE::PS_INPUT;
1555 m_isDangling = true;
1556 SetMultilineAllowed( false );
1557 }
1558
1559
Clone() const1560 EDA_ITEM* SCH_HIERLABEL::Clone() const
1561 {
1562 return new SCH_HIERLABEL( *this );
1563 }
1564
1565
SetLabelSpinStyle(LABEL_SPIN_STYLE aSpinStyle)1566 void SCH_HIERLABEL::SetLabelSpinStyle( LABEL_SPIN_STYLE aSpinStyle )
1567 {
1568 m_spin_style = aSpinStyle;
1569
1570 // Assume "Right" and Left" mean which side of the port symbol the text will be on
1571 // If we are left of the symbol, we want to right justify to line up with the symbol
1572 switch( aSpinStyle )
1573 {
1574 default:
1575 wxLogWarning( "SetLabelSpinStyle bad spin style" );
1576 break;
1577
1578 case LABEL_SPIN_STYLE::LEFT:
1579 //
1580 m_spin_style = LABEL_SPIN_STYLE::LEFT; // Handle the error spin style by resetting
1581 SetTextAngle( TEXT_ANGLE_HORIZ );
1582 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1583 SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1584 break;
1585
1586 case LABEL_SPIN_STYLE::UP:
1587 SetTextAngle( TEXT_ANGLE_VERT );
1588 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1589 SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1590 break;
1591
1592 case LABEL_SPIN_STYLE::RIGHT:
1593 SetTextAngle( TEXT_ANGLE_HORIZ );
1594 SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
1595 SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1596 break;
1597
1598 case LABEL_SPIN_STYLE::BOTTOM:
1599 SetTextAngle( TEXT_ANGLE_VERT );
1600 SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
1601 SetVertJustify( GR_TEXT_VJUSTIFY_CENTER );
1602 break;
1603 }
1604 }
1605
1606
Print(const RENDER_SETTINGS * aSettings,const wxPoint & offset)1607 void SCH_HIERLABEL::Print( const RENDER_SETTINGS* aSettings, const wxPoint& offset )
1608 {
1609 wxCHECK_RET( Schematic(), "No parent SCHEMATIC set for SCH_LABEL!" );
1610
1611 static std::vector <wxPoint> Poly;
1612
1613 wxDC* DC = aSettings->GetPrintDC();
1614 SCH_CONNECTION* conn = Connection();
1615 bool isBus = conn && conn->IsBus();
1616 COLOR4D color = aSettings->GetLayerColor( isBus ? LAYER_BUS : m_layer );
1617 int penWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
1618 wxPoint textOffset = offset + GetSchematicTextOffset( aSettings );
1619
1620 EDA_TEXT::Print( aSettings, textOffset, color );
1621
1622 CreateGraphicShape( aSettings, Poly, GetTextPos() + offset );
1623 GRPoly( nullptr, DC, Poly.size(), &Poly[0], false, penWidth, color, color );
1624 }
1625
1626
CreateGraphicShape(const RENDER_SETTINGS * aSettings,std::vector<wxPoint> & aPoints,const wxPoint & aPos) const1627 void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
1628 std::vector<wxPoint>& aPoints, const wxPoint& aPos ) const
1629 {
1630 CreateGraphicShape( aSettings, aPoints, aPos, m_shape );
1631 }
1632
1633
CreateGraphicShape(const RENDER_SETTINGS * aSettings,std::vector<wxPoint> & aPoints,const wxPoint & aPos,PINSHEETLABEL_SHAPE aShape) const1634 void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
1635 std::vector<wxPoint>& aPoints, const wxPoint& aPos,
1636 PINSHEETLABEL_SHAPE aShape ) const
1637 {
1638 int* Template = TemplateShape[static_cast<int>( aShape )][static_cast<int>( m_spin_style )];
1639 int halfSize = GetTextHeight() / 2;
1640 int imax = *Template;
1641 Template++;
1642
1643 aPoints.clear();
1644
1645 for( int ii = 0; ii < imax; ii++ )
1646 {
1647 wxPoint corner;
1648 corner.x = ( halfSize * (*Template) ) + aPos.x;
1649 Template++;
1650
1651 corner.y = ( halfSize * (*Template) ) + aPos.y;
1652 Template++;
1653
1654 aPoints.push_back( corner );
1655 }
1656 }
1657
1658
GetBoundingBox() const1659 const EDA_RECT SCH_HIERLABEL::GetBoundingBox() const
1660 {
1661 int penWidth = GetEffectiveTextPenWidth();
1662 int margin = GetTextOffset();
1663
1664 int x = GetTextPos().x;
1665 int y = GetTextPos().y;
1666
1667 int height = GetTextHeight() + penWidth + margin;
1668 int length = LenSize( GetShownText(), penWidth )
1669 + height; // add height for triangular shapes
1670
1671 int dx, dy;
1672
1673 switch( GetLabelSpinStyle() )
1674 {
1675 default:
1676 case LABEL_SPIN_STYLE::LEFT:
1677 dx = -length;
1678 dy = height;
1679 x += Mils2iu( DANGLING_SYMBOL_SIZE );
1680 y -= height / 2;
1681 break;
1682
1683 case LABEL_SPIN_STYLE::UP:
1684 dx = height;
1685 dy = -length;
1686 x -= height / 2;
1687 y += Mils2iu( DANGLING_SYMBOL_SIZE );
1688 break;
1689
1690 case LABEL_SPIN_STYLE::RIGHT:
1691 dx = length;
1692 dy = height;
1693 x -= Mils2iu( DANGLING_SYMBOL_SIZE );
1694 y -= height / 2;
1695 break;
1696
1697 case LABEL_SPIN_STYLE::BOTTOM:
1698 dx = height;
1699 dy = length;
1700 x -= height / 2;
1701 y -= Mils2iu( DANGLING_SYMBOL_SIZE );
1702 break;
1703 }
1704
1705 EDA_RECT box( wxPoint( x, y ), wxSize( dx, dy ) );
1706 box.Normalize();
1707 return box;
1708 }
1709
1710
GetSchematicTextOffset(const RENDER_SETTINGS * aSettings) const1711 wxPoint SCH_HIERLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
1712 {
1713 wxPoint text_offset;
1714 int dist = GetTextOffset( aSettings );
1715
1716 dist += GetTextWidth();
1717
1718 switch( GetLabelSpinStyle() )
1719 {
1720 default:
1721 case LABEL_SPIN_STYLE::LEFT: text_offset.x = -dist; break; // Orientation horiz normale
1722 case LABEL_SPIN_STYLE::UP: text_offset.y = -dist; break; // Orientation vert UP
1723 case LABEL_SPIN_STYLE::RIGHT: text_offset.x = dist; break; // Orientation horiz inverse
1724 case LABEL_SPIN_STYLE::BOTTOM: text_offset.y = dist; break; // Orientation vert BOTTOM
1725 }
1726
1727 return text_offset;
1728 }
1729
1730
GetSelectMenuText(EDA_UNITS aUnits) const1731 wxString SCH_HIERLABEL::GetSelectMenuText( EDA_UNITS aUnits ) const
1732 {
1733 return wxString::Format( _( "Hierarchical Label '%s'" ), ShortenedShownText() );
1734 }
1735
1736
GetMenuImage() const1737 BITMAPS SCH_HIERLABEL::GetMenuImage() const
1738 {
1739 return BITMAPS::add_hierarchical_label;
1740 }
1741
1742
ShowSyntaxHelp(wxWindow * aParentWindow)1743 HTML_MESSAGE_BOX* SCH_TEXT::ShowSyntaxHelp( wxWindow* aParentWindow )
1744 {
1745 wxString msg =
1746 #include "sch_text_help_md.h"
1747 ;
1748
1749 HTML_MESSAGE_BOX* dlg = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
1750 wxSize sz( 320, 320 );
1751
1752 dlg->SetMinSize( dlg->ConvertDialogToPixels( sz ) );
1753 dlg->SetDialogSizeInDU( sz.x, sz.y );
1754
1755 wxString html_txt;
1756 ConvertMarkdown2Html( wxGetTranslation( msg ), html_txt );
1757 dlg->AddHTML_Text( html_txt );
1758 dlg->ShowModeless();
1759
1760 return dlg;
1761 }
1762