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