You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1667 lines
49 KiB

3 years ago
  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-2023 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. #include <base_units.h>
  26. #include <pgm_base.h>
  27. #include <sch_edit_frame.h>
  28. #include <plotters/plotter.h>
  29. #include <widgets/msgpanel.h>
  30. #include <bitmaps.h>
  31. #include <string_utils.h>
  32. #include <schematic.h>
  33. #include <settings/color_settings.h>
  34. #include <sch_painter.h>
  35. #include <default_values.h>
  36. #include <wx/debug.h>
  37. #include <wx/log.h>
  38. #include <dialogs/html_message_box.h>
  39. #include <project/project_file.h>
  40. #include <project/net_settings.h>
  41. #include <core/kicad_algo.h>
  42. #include <trigo.h>
  43. #include <sch_label.h>
  44. using KIGFX::SCH_RENDER_SETTINGS;
  45. bool IncrementLabelMember( wxString& name, int aIncrement )
  46. {
  47. if( name.IsEmpty() )
  48. return true;
  49. wxString suffix;
  50. wxString digits;
  51. wxString outputFormat;
  52. wxString outputNumber;
  53. int ii = name.Len() - 1;
  54. int dCount = 0;
  55. while( ii >= 0 && !wxIsdigit( name.GetChar( ii ) ) )
  56. {
  57. suffix = name.GetChar( ii ) + suffix;
  58. ii--;
  59. }
  60. while( ii >= 0 && wxIsdigit( name.GetChar( ii ) ) )
  61. {
  62. digits = name.GetChar( ii ) + digits;
  63. ii--;
  64. dCount++;
  65. }
  66. if( digits.IsEmpty() )
  67. return true;
  68. long number = 0;
  69. if( digits.ToLong( &number ) )
  70. {
  71. number += aIncrement;
  72. // Don't let result go below zero
  73. if( number > -1 )
  74. {
  75. name.Remove( ii + 1 );
  76. //write out a format string with correct number of leading zeroes
  77. outputFormat.Printf( wxS( "%%0%dld" ), dCount );
  78. //write out the number using the format string
  79. outputNumber.Printf( outputFormat, number );
  80. name << outputNumber << suffix;
  81. return true;
  82. }
  83. }
  84. return false;
  85. }
  86. /* Coding polygons for global symbol graphic shapes.
  87. * the first parml is the number of corners
  88. * others are the corners coordinates in reduced units
  89. * the real coordinate is the reduced coordinate * text half size
  90. */
  91. static int TemplateIN_HN[] = { 6, 0, 0, -1, -1, -2, -1, -2, 1, -1, 1, 0, 0 };
  92. static int TemplateIN_HI[] = { 6, 0, 0, 1, 1, 2, 1, 2, -1, 1, -1, 0, 0 };
  93. static int TemplateIN_UP[] = { 6, 0, 0, 1, -1, 1, -2, -1, -2, -1, -1, 0, 0 };
  94. static int TemplateIN_BOTTOM[] = { 6, 0, 0, 1, 1, 1, 2, -1, 2, -1, 1, 0, 0 };
  95. static int TemplateOUT_HN[] = { 6, -2, 0, -1, 1, 0, 1, 0, -1, -1, -1, -2, 0 };
  96. static int TemplateOUT_HI[] = { 6, 2, 0, 1, -1, 0, -1, 0, 1, 1, 1, 2, 0 };
  97. static int TemplateOUT_UP[] = { 6, 0, -2, 1, -1, 1, 0, -1, 0, -1, -1, 0, -2 };
  98. static int TemplateOUT_BOTTOM[] = { 6, 0, 2, 1, 1, 1, 0, -1, 0, -1, 1, 0, 2 };
  99. static int TemplateUNSPC_HN[] = { 5, 0, -1, -2, -1, -2, 1, 0, 1, 0, -1 };
  100. static int TemplateUNSPC_HI[] = { 5, 0, -1, 2, -1, 2, 1, 0, 1, 0, -1 };
  101. static int TemplateUNSPC_UP[] = { 5, 1, 0, 1, -2, -1, -2, -1, 0, 1, 0 };
  102. static int TemplateUNSPC_BOTTOM[] = { 5, 1, 0, 1, 2, -1, 2, -1, 0, 1, 0 };
  103. static int TemplateBIDI_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
  104. static int TemplateBIDI_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
  105. static int TemplateBIDI_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
  106. static int TemplateBIDI_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
  107. static int Template3STATE_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
  108. static int Template3STATE_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
  109. static int Template3STATE_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
  110. static int Template3STATE_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
  111. static int* TemplateShape[5][4] =
  112. {
  113. { TemplateIN_HN, TemplateIN_UP, TemplateIN_HI, TemplateIN_BOTTOM },
  114. { TemplateOUT_HN, TemplateOUT_UP, TemplateOUT_HI, TemplateOUT_BOTTOM },
  115. { TemplateBIDI_HN, TemplateBIDI_UP, TemplateBIDI_HI, TemplateBIDI_BOTTOM },
  116. { Template3STATE_HN, Template3STATE_UP, Template3STATE_HI, Template3STATE_BOTTOM },
  117. { TemplateUNSPC_HN, TemplateUNSPC_UP, TemplateUNSPC_HI, TemplateUNSPC_BOTTOM }
  118. };
  119. wxString getElectricalTypeLabel( LABEL_FLAG_SHAPE aType )
  120. {
  121. switch( aType )
  122. {
  123. case LABEL_FLAG_SHAPE::L_INPUT: return _( "Input" );
  124. case LABEL_FLAG_SHAPE::L_OUTPUT: return _( "Output" );
  125. case LABEL_FLAG_SHAPE::L_BIDI: return _( "Bidirectional" );
  126. case LABEL_FLAG_SHAPE::L_TRISTATE: return _( "Tri-State" );
  127. case LABEL_FLAG_SHAPE::L_UNSPECIFIED: return _( "Passive" );
  128. default: return wxT( "???" );
  129. }
  130. }
  131. SCH_LABEL_BASE::SCH_LABEL_BASE( const VECTOR2I& aPos, const wxString& aText, KICAD_T aType ) :
  132. SCH_TEXT( aPos, aText, aType ),
  133. m_shape( L_UNSPECIFIED ),
  134. m_connectionType( CONNECTION_TYPE::NONE ),
  135. m_isDangling( true ),
  136. m_lastResolvedColor( COLOR4D::UNSPECIFIED )
  137. {
  138. SetMultilineAllowed( false );
  139. ClearFieldsAutoplaced(); // fiels are not yet autoplaced.
  140. }
  141. SCH_LABEL_BASE::SCH_LABEL_BASE( const SCH_LABEL_BASE& aLabel ) :
  142. SCH_TEXT( aLabel ),
  143. m_shape( aLabel.m_shape ),
  144. m_connectionType( aLabel.m_connectionType ),
  145. m_isDangling( aLabel.m_isDangling ),
  146. m_lastResolvedColor( aLabel.m_lastResolvedColor )
  147. {
  148. SetMultilineAllowed( false );
  149. m_fields = aLabel.m_fields;
  150. for( SCH_FIELD& field : m_fields )
  151. field.SetParent( this );
  152. }
  153. const wxString SCH_LABEL_BASE::GetDefaultFieldName( const wxString& aName, bool aUseDefaultName )
  154. {
  155. if( aName == wxT( "Intersheetrefs" ) )
  156. return _( "Sheet References" );
  157. else if( aName == wxT( "Netclass" ) )
  158. return _( "Net Class" );
  159. else if( aName.IsEmpty() && aUseDefaultName )
  160. return _( "Field" );
  161. else
  162. return aName;
  163. }
  164. bool SCH_LABEL_BASE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
  165. {
  166. if( SCH_TEXT::IsType( aScanTypes ) )
  167. return true;
  168. for( KICAD_T scanType : aScanTypes )
  169. {
  170. if( scanType == SCH_LABEL_LOCATE_ANY_T )
  171. return true;
  172. }
  173. wxCHECK_MSG( Schematic(), false, wxT( "No parent SCHEMATIC set for SCH_LABEL!" ) );
  174. // Ensure m_connected_items for Schematic()->CurrentSheet() exists.
  175. // Can be not the case when "this" is living in clipboard
  176. if( m_connected_items.find( Schematic()->CurrentSheet() ) == m_connected_items.end() )
  177. return false;
  178. const SCH_ITEM_SET& item_set = m_connected_items.at( Schematic()->CurrentSheet() );
  179. for( KICAD_T scanType : aScanTypes )
  180. {
  181. if( scanType == SCH_LABEL_LOCATE_WIRE_T )
  182. {
  183. for( SCH_ITEM* connection : item_set )
  184. {
  185. if( connection->IsType( { SCH_ITEM_LOCATE_WIRE_T, SCH_PIN_T } ) )
  186. return true;
  187. }
  188. }
  189. if ( scanType == SCH_LABEL_LOCATE_BUS_T )
  190. {
  191. for( SCH_ITEM* connection : item_set )
  192. {
  193. if( connection->IsType( { SCH_ITEM_LOCATE_BUS_T } ) )
  194. return true;
  195. }
  196. }
  197. }
  198. return false;
  199. }
  200. void SCH_LABEL_BASE::SwapData( SCH_ITEM* aItem )
  201. {
  202. SCH_TEXT::SwapData( aItem );
  203. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
  204. m_fields.swap( label->m_fields );
  205. std::swap( m_fieldsAutoplaced, label->m_fieldsAutoplaced );
  206. for( SCH_FIELD& field : m_fields )
  207. field.SetParent( this );
  208. for( SCH_FIELD& field : label->m_fields )
  209. field.SetParent( label );
  210. std::swap( m_shape, label->m_shape );
  211. std::swap( m_connectionType, label->m_connectionType );
  212. std::swap( m_isDangling, label->m_isDangling );
  213. std::swap( m_lastResolvedColor, label->m_lastResolvedColor );
  214. }
  215. COLOR4D SCH_LABEL_BASE::GetLabelColor() const
  216. {
  217. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  218. m_lastResolvedColor = GetTextColor();
  219. else if( !IsConnectivityDirty() )
  220. m_lastResolvedColor = GetEffectiveNetClass()->GetSchematicColor();
  221. return m_lastResolvedColor;
  222. }
  223. VECTOR2I SCH_LABEL_BASE::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  224. {
  225. VECTOR2I text_offset;
  226. // add an offset to x (or y) position to aid readability of text on a wire or line
  227. int dist = GetTextOffset( aSettings ) + GetPenWidth();
  228. switch( GetTextSpinStyle() )
  229. {
  230. case TEXT_SPIN_STYLE::UP:
  231. case TEXT_SPIN_STYLE::BOTTOM: text_offset.x = -dist; break; // Vert Orientation
  232. default:
  233. case TEXT_SPIN_STYLE::LEFT:
  234. case TEXT_SPIN_STYLE::RIGHT: text_offset.y = -dist; break; // Horiz Orientation
  235. }
  236. return text_offset;
  237. }
  238. void SCH_LABEL_BASE::Rotate( const VECTOR2I& aCenter )
  239. {
  240. VECTOR2I pt = GetTextPos();
  241. RotatePoint( pt, aCenter, ANGLE_90 );
  242. VECTOR2I offset = pt - GetTextPos();
  243. Rotate90( false );
  244. SetTextPos( GetTextPos() + offset );
  245. for( SCH_FIELD& field : m_fields )
  246. field.SetTextPos( field.GetTextPos() + offset );
  247. }
  248. void SCH_LABEL_BASE::Rotate90( bool aClockwise )
  249. {
  250. SCH_TEXT::Rotate90( aClockwise );
  251. if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
  252. {
  253. AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
  254. }
  255. else
  256. {
  257. for( SCH_FIELD& field : m_fields )
  258. {
  259. if( field.GetTextAngle().IsVertical()
  260. && field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  261. {
  262. if( !aClockwise )
  263. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  264. field.SetTextAngle( ANGLE_HORIZONTAL );
  265. }
  266. else if( field.GetTextAngle().IsVertical()
  267. && field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  268. {
  269. if( !aClockwise )
  270. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  271. field.SetTextAngle( ANGLE_HORIZONTAL );
  272. }
  273. else if( field.GetTextAngle().IsHorizontal()
  274. && field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  275. {
  276. if( aClockwise )
  277. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  278. field.SetTextAngle( ANGLE_VERTICAL );
  279. }
  280. else if( field.GetTextAngle().IsHorizontal()
  281. && field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  282. {
  283. if( aClockwise )
  284. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  285. field.SetTextAngle( ANGLE_VERTICAL );
  286. }
  287. VECTOR2I pos = field.GetTextPos();
  288. RotatePoint( pos, GetPosition(), aClockwise ? -ANGLE_90 : ANGLE_90 );
  289. field.SetTextPos( pos );
  290. }
  291. }
  292. }
  293. bool SCH_LABEL_BASE::IncrementLabel( int aIncrement )
  294. {
  295. wxString text = GetText();
  296. if( IncrementLabelMember( text, aIncrement ) )
  297. {
  298. SetText( text );
  299. return true;
  300. }
  301. return false;
  302. }
  303. void SCH_LABEL_BASE::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
  304. {
  305. int margin = GetTextOffset() * 2;
  306. int labelLen = GetBodyBoundingBox().GetSizeMax();
  307. int accumulated = GetTextHeight() / 2;
  308. if( Type() == SCH_GLOBAL_LABEL_T )
  309. accumulated += margin + GetPenWidth() + margin;
  310. for( SCH_FIELD& field : m_fields )
  311. {
  312. VECTOR2I offset( 0, 0 );
  313. switch( GetTextSpinStyle() )
  314. {
  315. default:
  316. case TEXT_SPIN_STYLE::LEFT:
  317. field.SetTextAngle( ANGLE_HORIZONTAL );
  318. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  319. if( field.GetCanonicalName() == wxT( "Intersheetrefs" ) )
  320. offset.x = - ( labelLen + margin );
  321. else
  322. offset.y = accumulated + field.GetTextHeight() / 2;
  323. break;
  324. case TEXT_SPIN_STYLE::UP:
  325. field.SetTextAngle( ANGLE_VERTICAL );
  326. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  327. if( field.GetCanonicalName() == wxT( "Intersheetrefs" ) )
  328. offset.y = - ( labelLen + margin );
  329. else
  330. offset.x = accumulated + field.GetTextHeight() / 2;
  331. break;
  332. case TEXT_SPIN_STYLE::RIGHT:
  333. field.SetTextAngle( ANGLE_HORIZONTAL );
  334. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  335. if( field.GetCanonicalName() == wxT( "Intersheetrefs" ) )
  336. offset.x = labelLen + margin;
  337. else
  338. offset.y = accumulated + field.GetTextHeight() / 2;
  339. break;
  340. case TEXT_SPIN_STYLE::BOTTOM:
  341. field.SetTextAngle( ANGLE_VERTICAL );
  342. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  343. if( field.GetCanonicalName() == wxT( "Intersheetrefs" ) )
  344. offset.y = labelLen + margin;
  345. else
  346. offset.x = accumulated + field.GetTextHeight() / 2;
  347. break;
  348. }
  349. field.SetTextPos( GetTextPos() + offset );
  350. if( field.GetCanonicalName() != wxT( "Intersheetrefs" ) )
  351. accumulated += field.GetTextHeight() + margin;
  352. }
  353. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  354. }
  355. void SCH_LABEL_BASE::GetIntersheetRefs( std::vector<std::pair<wxString, wxString>>* pages )
  356. {
  357. wxCHECK( pages, /* void */ );
  358. if( Schematic() )
  359. {
  360. auto it = Schematic()->GetPageRefsMap().find( GetText() );
  361. if( it != Schematic()->GetPageRefsMap().end() )
  362. {
  363. std::vector<int> pageListCopy;
  364. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  365. if( !Schematic()->Settings().m_IntersheetRefsListOwnPage )
  366. {
  367. int currentPage = Schematic()->CurrentSheet().GetVirtualPageNumber();
  368. alg::delete_matching( pageListCopy, currentPage );
  369. if( pageListCopy.empty() )
  370. return;
  371. }
  372. std::sort( pageListCopy.begin(), pageListCopy.end() );
  373. std::map<int, wxString> sheetPages = Schematic()->GetVirtualPageToSheetPagesMap();
  374. std::map<int, wxString> sheetNames = Schematic()->GetVirtualPageToSheetNamesMap();
  375. for( int pageNum : pageListCopy )
  376. pages->push_back( { sheetPages[ pageNum ], sheetNames[ pageNum ] } );
  377. }
  378. }
  379. }
  380. bool SCH_LABEL_BASE::ResolveTextVar( wxString* token, int aDepth ) const
  381. {
  382. if( !Schematic() )
  383. return false;
  384. if( token->Contains( ':' ) )
  385. {
  386. if( Schematic()->ResolveCrossReference( token, aDepth + 1 ) )
  387. return true;
  388. }
  389. if( ( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
  390. && token->IsSameAs( wxT( "CONNECTION_TYPE" ) ) )
  391. {
  392. const SCH_LABEL_BASE* label = static_cast<const SCH_LABEL_BASE*>( this );
  393. *token = getElectricalTypeLabel( label->GetShape() );
  394. return true;
  395. }
  396. else if( token->IsSameAs( wxT( "SHORT_NET_NAME" ) ) )
  397. {
  398. const SCH_CONNECTION* connection = Connection();
  399. *token = wxEmptyString;
  400. if( connection )
  401. *token = connection->LocalName();
  402. return true;
  403. }
  404. else if( token->IsSameAs( wxT( "NET_NAME" ) ) )
  405. {
  406. const SCH_CONNECTION* connection = Connection();
  407. *token = wxEmptyString;
  408. if( connection )
  409. *token = connection->Name();
  410. return true;
  411. }
  412. else if( token->IsSameAs( wxT( "NET_CLASS" ) ) )
  413. {
  414. const SCH_CONNECTION* connection = Connection();
  415. *token = wxEmptyString;
  416. if( connection )
  417. *token = GetEffectiveNetClass()->GetName();
  418. return true;
  419. }
  420. for( size_t i = 0; i < m_fields.size(); ++i )
  421. {
  422. if( token->IsSameAs( m_fields[i].GetName() ) )
  423. {
  424. *token = m_fields[i].GetShownText( aDepth + 1 );
  425. return true;
  426. }
  427. }
  428. // See if parent can resolve it (these will recurse to ancestors)
  429. if( Type() == SCH_SHEET_PIN_T && m_parent )
  430. {
  431. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
  432. if( sheet->ResolveTextVar( token, aDepth + 1 ) )
  433. return true;
  434. }
  435. else if( SCH_SHEET* sheet = Schematic()->CurrentSheet().Last() )
  436. {
  437. if( sheet->ResolveTextVar( token, aDepth + 1 ) )
  438. return true;
  439. }
  440. return false;
  441. }
  442. wxString SCH_LABEL_BASE::GetShownText( int aDepth, bool aAllowExtraText ) const
  443. {
  444. std::function<bool( wxString* )> textResolver =
  445. [&]( wxString* token ) -> bool
  446. {
  447. return ResolveTextVar( token, aDepth );
  448. };
  449. wxString text = EDA_TEXT::GetShownText();
  450. if( text == wxS( "~" ) ) // Legacy placeholder for empty string
  451. {
  452. text = wxS( "" );
  453. }
  454. else if( HasTextVars() )
  455. {
  456. if( aDepth < 10 )
  457. text = ExpandTextVars( text, &textResolver );
  458. }
  459. return text;
  460. }
  461. void SCH_LABEL_BASE::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
  462. {
  463. for( SCH_FIELD& field : m_fields )
  464. aFunction( &field );
  465. }
  466. bool SCH_LABEL_BASE::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  467. {
  468. return SCH_ITEM::Matches( UnescapeString( GetText() ), aSearchData );
  469. }
  470. bool SCH_LABEL_BASE::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  471. {
  472. EDA_SEARCH_DATA localSearchData( aSearchData );
  473. localSearchData.findString = EscapeString( aSearchData.findString, CTX_NETNAME );
  474. localSearchData.replaceString = EscapeString( aSearchData.replaceString, CTX_NETNAME );
  475. return EDA_TEXT::Replace( localSearchData );
  476. }
  477. INSPECT_RESULT SCH_LABEL_BASE::Visit( INSPECTOR aInspector, void* testData,
  478. const std::vector<KICAD_T>& aScanTypes )
  479. {
  480. if( IsType( aScanTypes ) )
  481. {
  482. if( INSPECT_RESULT::QUIT == aInspector( this, nullptr ) )
  483. return INSPECT_RESULT::QUIT;
  484. }
  485. for( KICAD_T scanType : aScanTypes )
  486. {
  487. if( scanType == SCH_LOCATE_ANY_T || scanType == SCH_FIELD_T )
  488. {
  489. for( SCH_FIELD& field : m_fields )
  490. {
  491. if( INSPECT_RESULT::QUIT == aInspector( &field, this ) )
  492. return INSPECT_RESULT::QUIT;
  493. }
  494. }
  495. }
  496. return INSPECT_RESULT::CONTINUE;
  497. }
  498. void SCH_LABEL_BASE::GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList )
  499. {
  500. DANGLING_END_ITEM item( LABEL_END, this, GetTextPos() );
  501. aItemList.push_back( item );
  502. }
  503. std::vector<VECTOR2I> SCH_LABEL_BASE::GetConnectionPoints() const
  504. {
  505. return { GetTextPos() };
  506. }
  507. void SCH_LABEL_BASE::ViewGetLayers( int aLayers[], int& aCount ) const
  508. {
  509. aCount = 5;
  510. aLayers[0] = LAYER_DANGLING;
  511. aLayers[1] = LAYER_DEVICE;
  512. aLayers[2] = LAYER_NETCLASS_REFS;
  513. aLayers[3] = LAYER_FIELDS;
  514. aLayers[4] = LAYER_SELECTION_SHADOWS;
  515. }
  516. int SCH_LABEL_BASE::GetLabelBoxExpansion( const RENDER_SETTINGS* aSettings ) const
  517. {
  518. double ratio;
  519. if( aSettings )
  520. ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_LabelSizeRatio;
  521. else if( Schematic() )
  522. ratio = Schematic()->Settings().m_LabelSizeRatio;
  523. else
  524. ratio = DEFAULT_LABEL_SIZE_RATIO; // For previews (such as in Preferences), etc.
  525. return KiROUND( ratio * GetTextSize().y );
  526. }
  527. const BOX2I SCH_LABEL_BASE::GetBodyBoundingBox() const
  528. {
  529. // build the bounding box of the label only, without taking into account its fields
  530. BOX2I box;
  531. std::vector<VECTOR2I> pts;
  532. CreateGraphicShape( nullptr, pts, GetTextPos() );
  533. for( const VECTOR2I& pt : pts )
  534. box.Merge( pt );
  535. box.Inflate( GetEffectiveTextPenWidth() / 2 );
  536. box.Normalize();
  537. return box;
  538. }
  539. const BOX2I SCH_LABEL_BASE::GetBoundingBox() const
  540. {
  541. // build the bounding box of the entire label, including its fields
  542. BOX2I box = GetBodyBoundingBox();
  543. for( const SCH_FIELD& field : m_fields )
  544. {
  545. if( field.IsVisible() )
  546. {
  547. BOX2I fieldBBox = field.GetBoundingBox();
  548. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  549. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  550. box.Merge( fieldBBox );
  551. }
  552. }
  553. box.Normalize();
  554. return box;
  555. }
  556. bool SCH_LABEL_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  557. {
  558. BOX2I bbox = GetBodyBoundingBox();
  559. bbox.Inflate( aAccuracy );
  560. if( bbox.Contains( aPosition ) )
  561. return true;
  562. for( const SCH_FIELD& field : m_fields )
  563. {
  564. if( field.IsVisible() )
  565. {
  566. BOX2I fieldBBox = field.GetBoundingBox();
  567. fieldBBox.Inflate( aAccuracy );
  568. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  569. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  570. if( fieldBBox.Contains( aPosition ) )
  571. return true;
  572. }
  573. }
  574. return false;
  575. }
  576. bool SCH_LABEL_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  577. {
  578. BOX2I rect = aRect;
  579. rect.Inflate( aAccuracy );
  580. if( aContained )
  581. {
  582. return rect.Contains( GetBoundingBox() );
  583. }
  584. else
  585. {
  586. if( rect.Intersects( GetBodyBoundingBox() ) )
  587. return true;
  588. for( const SCH_FIELD& field : m_fields )
  589. {
  590. if( field.IsVisible() )
  591. {
  592. BOX2I fieldBBox = field.GetBoundingBox();
  593. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  594. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  595. if( rect.Intersects( fieldBBox ) )
  596. return true;
  597. }
  598. }
  599. return false;
  600. }
  601. }
  602. bool SCH_LABEL_BASE::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  603. const SCH_SHEET_PATH* aPath )
  604. {
  605. bool previousState = m_isDangling;
  606. m_isDangling = true;
  607. m_connectionType = CONNECTION_TYPE::NONE;
  608. for( unsigned ii = 0; ii < aItemList.size(); ii++ )
  609. {
  610. DANGLING_END_ITEM& item = aItemList[ii];
  611. if( item.GetItem() == this )
  612. continue;
  613. switch( item.GetType() )
  614. {
  615. case PIN_END:
  616. case LABEL_END:
  617. case SHEET_LABEL_END:
  618. case NO_CONNECT_END:
  619. if( GetTextPos() == item.GetPosition() )
  620. {
  621. m_isDangling = false;
  622. if( aPath && item.GetType() != PIN_END )
  623. AddConnectionTo( *aPath, static_cast<SCH_ITEM*>( item.GetItem() ) );
  624. }
  625. break;
  626. case BUS_END:
  627. m_connectionType = CONNECTION_TYPE::BUS;
  628. KI_FALLTHROUGH;
  629. case WIRE_END:
  630. {
  631. DANGLING_END_ITEM& nextItem = aItemList[++ii];
  632. int accuracy = 1; // We have rounding issues with an accuracy of 0
  633. m_isDangling = !TestSegmentHit( GetTextPos(), item.GetPosition(),
  634. nextItem.GetPosition(), accuracy );
  635. if( !m_isDangling )
  636. {
  637. if( m_connectionType != CONNECTION_TYPE::BUS )
  638. m_connectionType = CONNECTION_TYPE::NET;
  639. // Add the line to the connected items, since it won't be picked
  640. // up by a search of intersecting connection points
  641. if( aPath )
  642. {
  643. auto sch_item = static_cast<SCH_ITEM*>( item.GetItem() );
  644. AddConnectionTo( *aPath, sch_item );
  645. sch_item->AddConnectionTo( *aPath, this );
  646. }
  647. }
  648. }
  649. break;
  650. default:
  651. break;
  652. }
  653. if( !m_isDangling )
  654. break;
  655. }
  656. if( m_isDangling )
  657. m_connectionType = CONNECTION_TYPE::NONE;
  658. return previousState != m_isDangling;
  659. }
  660. void SCH_LABEL_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  661. {
  662. wxString msg;
  663. switch( Type() )
  664. {
  665. case SCH_LABEL_T: msg = _( "Label" ); break;
  666. case SCH_DIRECTIVE_LABEL_T: msg = _( "Directive Label" ); break;
  667. case SCH_GLOBAL_LABEL_T: msg = _( "Global Label" ); break;
  668. case SCH_HIER_LABEL_T: msg = _( "Hierarchical Label" ); break;
  669. case SCH_SHEET_PIN_T: msg = _( "Hierarchical Sheet Pin" ); break;
  670. default: return;
  671. }
  672. // Don't use GetShownText() here; we want to show the user the variable references
  673. aList.emplace_back( msg, UnescapeString( GetText() ) );
  674. // Display electrical type if it is relevant
  675. if( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
  676. aList.emplace_back( _( "Type" ), getElectricalTypeLabel( GetShape() ) );
  677. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  678. wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
  679. int style = IsBold() && IsItalic() ? 3 : IsBold() ? 2 : IsItalic() ? 1 : 0;
  680. aList.emplace_back( _( "Style" ), textStyle[style] );
  681. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  682. switch( GetTextSpinStyle() )
  683. {
  684. case TEXT_SPIN_STYLE::LEFT: msg = _( "Align right" ); break;
  685. case TEXT_SPIN_STYLE::UP: msg = _( "Align bottom" ); break;
  686. case TEXT_SPIN_STYLE::RIGHT: msg = _( "Align left" ); break;
  687. case TEXT_SPIN_STYLE::BOTTOM: msg = _( "Align top" ); break;
  688. default: msg = wxT( "???" ); break;
  689. }
  690. aList.emplace_back( _( "Justification" ), msg );
  691. SCH_CONNECTION* conn = nullptr;
  692. if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  693. conn = Connection();
  694. if( conn )
  695. {
  696. conn->AppendInfoToMsgPanel( aList );
  697. if( !conn->IsBus() )
  698. {
  699. aList.emplace_back( _( "Resolved Netclass" ),
  700. UnescapeString( GetEffectiveNetClass()->GetName() ) );
  701. }
  702. }
  703. }
  704. void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground ) const
  705. {
  706. static std::vector<VECTOR2I> s_poly;
  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. if( aPlotter->GetColorMode() && GetTextColor() != COLOR4D::UNSPECIFIED )
  713. color = GetTextColor();
  714. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  715. aPlotter->SetCurrentLineWidth( penWidth );
  716. KIFONT::FONT* font = GetFont();
  717. if( !font )
  718. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  719. VECTOR2I textpos = GetTextPos() + GetSchematicTextOffset( aPlotter->RenderSettings() );
  720. CreateGraphicShape( aPlotter->RenderSettings(), s_poly, GetTextPos() );
  721. if( aBackground )
  722. {
  723. // No filled shapes (yet)
  724. }
  725. else
  726. {
  727. aPlotter->Text( textpos, color, GetShownText(), GetTextAngle(), GetTextSize(),
  728. GetHorizJustify(), GetVertJustify(), penWidth, IsItalic(), IsBold(),
  729. false, font );
  730. if( s_poly.size() )
  731. aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
  732. // Plot attributes to a hypertext menu
  733. std::vector<wxString> properties;
  734. if( connection )
  735. {
  736. properties.emplace_back(
  737. wxString::Format( wxT( "!%s = %s" ), _( "Net" ), connection->Name() ) );
  738. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
  739. GetEffectiveNetClass()->GetName() ) );
  740. }
  741. for( const SCH_FIELD& field : GetFields() )
  742. {
  743. properties.emplace_back(
  744. wxString::Format( wxT( "!%s = %s" ), field.GetName(), field.GetShownText() ) );
  745. }
  746. if( !properties.empty() )
  747. aPlotter->HyperlinkMenu( GetBodyBoundingBox(), properties );
  748. if( Type() == SCH_HIER_LABEL_T )
  749. {
  750. aPlotter->Bookmark( GetBodyBoundingBox(), GetShownText(), _( "Hierarchical Labels" ) );
  751. }
  752. }
  753. for( const SCH_FIELD& field : m_fields )
  754. field.Plot( aPlotter, aBackground );
  755. }
  756. void SCH_LABEL_BASE::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  757. {
  758. static std::vector<VECTOR2I> s_poly;
  759. SCH_CONNECTION* connection = Connection();
  760. int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
  761. wxDC* DC = aSettings->GetPrintDC();
  762. COLOR4D color = aSettings->GetLayerColor( layer );
  763. bool blackAndWhiteMode = GetGRForceBlackPenState();
  764. int penWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
  765. VECTOR2I text_offset = aOffset + GetSchematicTextOffset( aSettings );
  766. if( !blackAndWhiteMode && GetTextColor() != COLOR4D::UNSPECIFIED )
  767. color = GetTextColor();
  768. EDA_TEXT::Print( aSettings, text_offset, color );
  769. CreateGraphicShape( aSettings, s_poly, GetTextPos() + aOffset );
  770. if( !s_poly.empty() )
  771. GRPoly( DC, s_poly.size(), &s_poly[0], false, penWidth, color, color );
  772. for( SCH_FIELD& field : m_fields )
  773. field.Print( aSettings, aOffset );
  774. }
  775. bool SCH_LABEL_BASE::AutoRotateOnPlacement() const
  776. {
  777. return m_autoRotateOnPlacement;
  778. }
  779. void SCH_LABEL_BASE::SetAutoRotateOnPlacement( bool autoRotate )
  780. {
  781. m_autoRotateOnPlacement = autoRotate;
  782. }
  783. SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) :
  784. SCH_LABEL_BASE( pos, text, SCH_LABEL_T )
  785. {
  786. m_layer = LAYER_LOCLABEL;
  787. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  788. m_isDangling = true;
  789. }
  790. const BOX2I SCH_LABEL::GetBodyBoundingBox() const
  791. {
  792. BOX2I rect = GetTextBox();
  793. rect.Offset( 0, -GetTextOffset() );
  794. rect.Inflate( GetEffectiveTextPenWidth() );
  795. if( !GetTextAngle().IsZero() )
  796. {
  797. // Rotate rect
  798. VECTOR2I pos = rect.GetOrigin();
  799. VECTOR2I end = rect.GetEnd();
  800. RotatePoint( pos, GetTextPos(), GetTextAngle() );
  801. RotatePoint( end, GetTextPos(), GetTextAngle() );
  802. rect.SetOrigin( pos );
  803. rect.SetEnd( end );
  804. rect.Normalize();
  805. }
  806. // Labels have a position point that is outside of the TextBox
  807. rect.Merge( GetPosition() );
  808. return rect;
  809. }
  810. wxString SCH_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  811. {
  812. return wxString::Format( _( "Label '%s'" ), KIUI::EllipsizeMenuText( GetShownText() ) );
  813. }
  814. BITMAPS SCH_LABEL::GetMenuImage() const
  815. {
  816. return BITMAPS::add_line_label;
  817. }
  818. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const VECTOR2I& pos ) :
  819. SCH_LABEL_BASE( pos, wxEmptyString, SCH_DIRECTIVE_LABEL_T )
  820. {
  821. m_layer = LAYER_NETCLASS_REFS;
  822. m_shape = LABEL_FLAG_SHAPE::F_ROUND;
  823. m_pinLength = schIUScale.MilsToIU( 100 );
  824. m_symbolSize = schIUScale.MilsToIU( 20 );
  825. m_isDangling = true;
  826. }
  827. void SCH_DIRECTIVE_LABEL::SwapData( SCH_ITEM* aItem )
  828. {
  829. SCH_LABEL_BASE::SwapData( aItem );
  830. SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( aItem );
  831. std::swap( m_pinLength, label->m_pinLength );
  832. std::swap( m_symbolSize, label->m_symbolSize );
  833. }
  834. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel ) :
  835. SCH_LABEL_BASE( aClassLabel )
  836. {
  837. m_pinLength = aClassLabel.m_pinLength;
  838. m_symbolSize = aClassLabel.m_symbolSize;
  839. }
  840. int SCH_DIRECTIVE_LABEL::GetPenWidth() const
  841. {
  842. int pen = 0;
  843. if( Schematic() )
  844. pen = Schematic()->Settings().m_DefaultLineWidth;
  845. return GetEffectiveTextPenWidth( pen );
  846. }
  847. void SCH_DIRECTIVE_LABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  848. std::vector<VECTOR2I>& aPoints,
  849. const VECTOR2I& aPos ) const
  850. {
  851. int symbolSize = m_symbolSize;
  852. aPoints.clear();
  853. switch( m_shape )
  854. {
  855. case LABEL_FLAG_SHAPE::F_DOT:
  856. symbolSize = KiROUND( symbolSize * 0.7 );
  857. KI_FALLTHROUGH;
  858. case LABEL_FLAG_SHAPE::F_ROUND:
  859. // First 3 points are used for generating shape
  860. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  861. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  862. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  863. // These points are just used to bulk out the bounding box
  864. aPoints.emplace_back( VECTOR2I( -m_symbolSize, m_pinLength ) );
  865. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  866. aPoints.emplace_back( VECTOR2I( m_symbolSize, m_pinLength + symbolSize ) );
  867. break;
  868. case LABEL_FLAG_SHAPE::F_DIAMOND:
  869. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  870. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  871. aPoints.emplace_back( VECTOR2I( -2 * m_symbolSize, m_pinLength ) );
  872. aPoints.emplace_back( VECTOR2I( 0, m_pinLength + symbolSize ) );
  873. aPoints.emplace_back( VECTOR2I( 2 * m_symbolSize, m_pinLength ) );
  874. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  875. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  876. break;
  877. case LABEL_FLAG_SHAPE::F_RECTANGLE:
  878. symbolSize = KiROUND( symbolSize * 0.8 );
  879. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  880. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  881. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength - symbolSize ) );
  882. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength + symbolSize ) );
  883. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength + symbolSize ) );
  884. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength - symbolSize ) );
  885. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  886. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  887. break;
  888. default:
  889. break;
  890. }
  891. // Rotate outlines and move corners to real position
  892. for( VECTOR2I& aPoint : aPoints )
  893. {
  894. switch( GetTextSpinStyle() )
  895. {
  896. default:
  897. case TEXT_SPIN_STYLE::LEFT: break;
  898. case TEXT_SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  899. case TEXT_SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  900. case TEXT_SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  901. }
  902. aPoint += aPos;
  903. }
  904. }
  905. void SCH_DIRECTIVE_LABEL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
  906. {
  907. int margin = GetTextOffset();
  908. int symbolWidth = m_symbolSize;
  909. int origin = m_pinLength;
  910. if( m_shape == LABEL_FLAG_SHAPE::F_DIAMOND || m_shape == LABEL_FLAG_SHAPE::F_RECTANGLE )
  911. symbolWidth *= 2;
  912. if( IsItalic() )
  913. margin = KiROUND( margin * 1.5 );
  914. VECTOR2I offset;
  915. for( SCH_FIELD& field : m_fields )
  916. {
  917. switch( GetTextSpinStyle() )
  918. {
  919. default:
  920. case TEXT_SPIN_STYLE::LEFT:
  921. field.SetTextAngle( ANGLE_HORIZONTAL );
  922. offset = { symbolWidth + margin, origin };
  923. break;
  924. case TEXT_SPIN_STYLE::UP:
  925. field.SetTextAngle( ANGLE_VERTICAL );
  926. offset = { -origin, -( symbolWidth + margin ) };
  927. break;
  928. case TEXT_SPIN_STYLE::RIGHT:
  929. field.SetTextAngle( ANGLE_HORIZONTAL );
  930. offset = { symbolWidth + margin, -origin };
  931. break;
  932. case TEXT_SPIN_STYLE::BOTTOM:
  933. field.SetTextAngle( ANGLE_VERTICAL );
  934. offset = { origin, -( symbolWidth + margin ) };
  935. break;
  936. }
  937. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  938. field.SetTextPos( GetPosition() + offset );
  939. origin -= field.GetTextHeight() + margin;
  940. }
  941. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  942. }
  943. wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  944. {
  945. if( m_fields.empty() )
  946. {
  947. return _( "Directive Label" );
  948. }
  949. else
  950. {
  951. return wxString::Format( _( "Directive Label [%s %s]" ),
  952. m_fields[0].GetName(),
  953. m_fields[0].GetShownText() );
  954. }
  955. }
  956. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const VECTOR2I& pos, const wxString& text ) :
  957. SCH_LABEL_BASE( pos, text, SCH_GLOBAL_LABEL_T )
  958. {
  959. m_layer = LAYER_GLOBLABEL;
  960. m_shape = LABEL_FLAG_SHAPE::L_BIDI;
  961. m_isDangling = true;
  962. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  963. m_fields.emplace_back( SCH_FIELD( pos, 0, this, wxT( "Sheet References" ) ) );
  964. m_fields[0].SetText( wxT( "${INTERSHEET_REFS}" ) );
  965. m_fields[0].SetVisible( false );
  966. m_fields[0].SetLayer( LAYER_INTERSHEET_REFS );
  967. m_fields[0].SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  968. }
  969. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
  970. SCH_LABEL_BASE( aGlobalLabel )
  971. {
  972. }
  973. VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  974. {
  975. int horiz = GetLabelBoxExpansion( aSettings );
  976. // Center the text on the center line of "E" instead of "R" to make room for an overbar
  977. int vert = GetTextHeight() * 0.0715;
  978. switch( m_shape )
  979. {
  980. case LABEL_FLAG_SHAPE::L_INPUT:
  981. case LABEL_FLAG_SHAPE::L_BIDI:
  982. case LABEL_FLAG_SHAPE::L_TRISTATE:
  983. horiz += GetTextHeight() * 3 / 4; // Use three-quarters-height as proxy for triangle size
  984. break;
  985. case LABEL_FLAG_SHAPE::L_OUTPUT:
  986. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  987. default:
  988. break;
  989. }
  990. switch( GetTextSpinStyle() )
  991. {
  992. default:
  993. case TEXT_SPIN_STYLE::LEFT: return VECTOR2I( -horiz, vert );
  994. case TEXT_SPIN_STYLE::UP: return VECTOR2I( vert, -horiz );
  995. case TEXT_SPIN_STYLE::RIGHT: return VECTOR2I( horiz, vert );
  996. case TEXT_SPIN_STYLE::BOTTOM: return VECTOR2I( vert, horiz );
  997. }
  998. }
  999. void SCH_GLOBALLABEL::SetTextSpinStyle( TEXT_SPIN_STYLE aSpinStyle )
  1000. {
  1001. SCH_TEXT::SetTextSpinStyle( aSpinStyle );
  1002. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1003. }
  1004. void SCH_GLOBALLABEL::MirrorSpinStyle( bool aLeftRight )
  1005. {
  1006. SCH_TEXT::MirrorSpinStyle( aLeftRight );
  1007. for( SCH_FIELD& field : m_fields )
  1008. {
  1009. if( ( aLeftRight && field.GetTextAngle().IsHorizontal() )
  1010. || ( !aLeftRight && field.GetTextAngle().IsVertical() ) )
  1011. {
  1012. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1013. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1014. else
  1015. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1016. }
  1017. VECTOR2I pos = field.GetTextPos();
  1018. VECTOR2I delta = (VECTOR2I)GetPosition() - pos;
  1019. if( aLeftRight )
  1020. pos.x = GetPosition().x + delta.x;
  1021. else
  1022. pos.y = GetPosition().y + delta.y;
  1023. field.SetTextPos( pos );
  1024. }
  1025. }
  1026. void SCH_GLOBALLABEL::MirrorHorizontally( int aCenter )
  1027. {
  1028. VECTOR2I old_pos = GetPosition();
  1029. SCH_TEXT::MirrorHorizontally( aCenter );
  1030. for( SCH_FIELD& field : m_fields )
  1031. {
  1032. switch( field.GetHorizJustify() )
  1033. {
  1034. case GR_TEXT_H_ALIGN_LEFT:
  1035. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1036. break;
  1037. case GR_TEXT_H_ALIGN_CENTER:
  1038. break;
  1039. case GR_TEXT_H_ALIGN_RIGHT:
  1040. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1041. break;
  1042. }
  1043. VECTOR2I pos = field.GetTextPos();
  1044. VECTOR2I delta = old_pos - pos;
  1045. pos.x = GetPosition().x + delta.x;
  1046. field.SetPosition( pos );
  1047. }
  1048. }
  1049. void SCH_GLOBALLABEL::MirrorVertically( int aCenter )
  1050. {
  1051. VECTOR2I old_pos = GetPosition();
  1052. SCH_TEXT::MirrorVertically( aCenter );
  1053. for( SCH_FIELD& field : m_fields )
  1054. {
  1055. VECTOR2I pos = field.GetTextPos();
  1056. VECTOR2I delta = old_pos - pos;
  1057. pos.y = GetPosition().y + delta.y;
  1058. field.SetPosition( pos );
  1059. }
  1060. }
  1061. bool SCH_GLOBALLABEL::ResolveTextVar( wxString* token, int aDepth ) const
  1062. {
  1063. if( token->IsSameAs( wxT( "INTERSHEET_REFS" ) ) && Schematic() )
  1064. {
  1065. SCHEMATIC_SETTINGS& settings = Schematic()->Settings();
  1066. wxString ref;
  1067. auto it = Schematic()->GetPageRefsMap().find( GetText() );
  1068. if( it == Schematic()->GetPageRefsMap().end() )
  1069. {
  1070. ref = "?";
  1071. }
  1072. else
  1073. {
  1074. std::vector<int> pageListCopy;
  1075. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  1076. std::sort( pageListCopy.begin(), pageListCopy.end() );
  1077. if( !settings.m_IntersheetRefsListOwnPage )
  1078. {
  1079. int currentPage = Schematic()->CurrentSheet().GetVirtualPageNumber();
  1080. alg::delete_matching( pageListCopy, currentPage );
  1081. }
  1082. std::map<int, wxString> sheetPages = Schematic()->GetVirtualPageToSheetPagesMap();
  1083. if( ( settings.m_IntersheetRefsFormatShort ) && ( pageListCopy.size() > 2 ) )
  1084. {
  1085. ref.Append( wxString::Format( wxT( "%s..%s" ),
  1086. sheetPages[pageListCopy.front()],
  1087. sheetPages[pageListCopy.back()] ) );
  1088. }
  1089. else
  1090. {
  1091. for( const int& pageNo : pageListCopy )
  1092. ref.Append( wxString::Format( wxT( "%s," ), sheetPages[pageNo] ) );
  1093. if( !ref.IsEmpty() && ref.Last() == ',' )
  1094. ref.RemoveLast();
  1095. }
  1096. }
  1097. *token = settings.m_IntersheetRefsPrefix + ref + settings.m_IntersheetRefsSuffix;
  1098. return true;
  1099. }
  1100. return SCH_LABEL_BASE::ResolveTextVar( token, aDepth );
  1101. }
  1102. void SCH_GLOBALLABEL::ViewGetLayers( int aLayers[], int& aCount ) const
  1103. {
  1104. aCount = 6;
  1105. aLayers[0] = LAYER_DANGLING;
  1106. aLayers[1] = LAYER_DEVICE;
  1107. aLayers[2] = LAYER_INTERSHEET_REFS;
  1108. aLayers[3] = LAYER_NETCLASS_REFS;
  1109. aLayers[4] = LAYER_FIELDS;
  1110. aLayers[5] = LAYER_SELECTION_SHADOWS;
  1111. }
  1112. void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1113. std::vector<VECTOR2I>& aPoints,
  1114. const VECTOR2I& aPos ) const
  1115. {
  1116. int margin = GetLabelBoxExpansion( aRenderSettings );
  1117. int halfSize = ( GetTextHeight() / 2 ) + margin;
  1118. int linewidth = GetPenWidth();
  1119. int symb_len = GetTextBox().GetWidth() + 2 * margin;
  1120. int x = symb_len + linewidth + 3;
  1121. int y = halfSize + linewidth + 3;
  1122. aPoints.clear();
  1123. // Create outline shape : 6 points
  1124. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1125. aPoints.emplace_back( VECTOR2I( 0, -y ) ); // Up
  1126. aPoints.emplace_back( VECTOR2I( -x, -y ) ); // left
  1127. aPoints.emplace_back( VECTOR2I( -x, 0 ) ); // Up left
  1128. aPoints.emplace_back( VECTOR2I( -x, y ) ); // left down
  1129. aPoints.emplace_back( VECTOR2I( 0, y ) ); // down
  1130. int x_offset = 0;
  1131. switch( m_shape )
  1132. {
  1133. case LABEL_FLAG_SHAPE::L_INPUT:
  1134. x_offset = -halfSize;
  1135. aPoints[0].x += halfSize;
  1136. break;
  1137. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1138. aPoints[3].x -= halfSize;
  1139. break;
  1140. case LABEL_FLAG_SHAPE::L_BIDI:
  1141. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1142. x_offset = -halfSize;
  1143. aPoints[0].x += halfSize;
  1144. aPoints[3].x -= halfSize;
  1145. break;
  1146. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1147. default:
  1148. break;
  1149. }
  1150. // Rotate outlines and move corners in real position
  1151. for( VECTOR2I& aPoint : aPoints )
  1152. {
  1153. aPoint.x += x_offset;
  1154. switch( GetTextSpinStyle() )
  1155. {
  1156. default:
  1157. case TEXT_SPIN_STYLE::LEFT: break;
  1158. case TEXT_SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1159. case TEXT_SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1160. case TEXT_SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1161. }
  1162. aPoint += aPos;
  1163. }
  1164. aPoints.push_back( aPoints[0] ); // closing
  1165. }
  1166. wxString SCH_GLOBALLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  1167. {
  1168. return wxString::Format( _( "Global Label '%s'" ), KIUI::EllipsizeMenuText( GetShownText() ) );
  1169. }
  1170. BITMAPS SCH_GLOBALLABEL::GetMenuImage() const
  1171. {
  1172. return BITMAPS::add_glabel;
  1173. }
  1174. SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T aType ) :
  1175. SCH_LABEL_BASE( pos, text, aType )
  1176. {
  1177. m_layer = LAYER_HIERLABEL;
  1178. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1179. m_isDangling = true;
  1180. }
  1181. void SCH_HIERLABEL::SetTextSpinStyle( TEXT_SPIN_STYLE aSpinStyle )
  1182. {
  1183. SCH_TEXT::SetTextSpinStyle( aSpinStyle );
  1184. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1185. }
  1186. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1187. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos ) const
  1188. {
  1189. CreateGraphicShape( aSettings, aPoints, aPos, m_shape );
  1190. }
  1191. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1192. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos,
  1193. LABEL_FLAG_SHAPE aShape ) const
  1194. {
  1195. int* Template = TemplateShape[static_cast<int>( aShape )][static_cast<int>( m_spin_style )];
  1196. int halfSize = GetTextHeight() / 2;
  1197. int imax = *Template;
  1198. Template++;
  1199. aPoints.clear();
  1200. for( int ii = 0; ii < imax; ii++ )
  1201. {
  1202. VECTOR2I corner;
  1203. corner.x = ( halfSize * (*Template) ) + aPos.x;
  1204. Template++;
  1205. corner.y = ( halfSize * (*Template) ) + aPos.y;
  1206. Template++;
  1207. aPoints.push_back( corner );
  1208. }
  1209. }
  1210. const BOX2I SCH_HIERLABEL::GetBodyBoundingBox() const
  1211. {
  1212. int penWidth = GetEffectiveTextPenWidth();
  1213. int margin = GetTextOffset();
  1214. int x = GetTextPos().x;
  1215. int y = GetTextPos().y;
  1216. int height = GetTextHeight() + penWidth + margin;
  1217. int length = GetTextBox().GetWidth();
  1218. length += height; // add height for triangular shapes
  1219. int dx, dy;
  1220. switch( GetTextSpinStyle() )
  1221. {
  1222. default:
  1223. case TEXT_SPIN_STYLE::LEFT:
  1224. dx = -length;
  1225. dy = height;
  1226. x += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1227. y -= height / 2;
  1228. break;
  1229. case TEXT_SPIN_STYLE::UP:
  1230. dx = height;
  1231. dy = -length;
  1232. x -= height / 2;
  1233. y += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1234. break;
  1235. case TEXT_SPIN_STYLE::RIGHT:
  1236. dx = length;
  1237. dy = height;
  1238. x -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1239. y -= height / 2;
  1240. break;
  1241. case TEXT_SPIN_STYLE::BOTTOM:
  1242. dx = height;
  1243. dy = length;
  1244. x -= height / 2;
  1245. y -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1246. break;
  1247. }
  1248. BOX2I box( VECTOR2I( x, y ), VECTOR2I( dx, dy ) );
  1249. box.Normalize();
  1250. return box;
  1251. }
  1252. VECTOR2I SCH_HIERLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1253. {
  1254. VECTOR2I text_offset;
  1255. int dist = GetTextOffset( aSettings );
  1256. dist += GetTextWidth();
  1257. switch( GetTextSpinStyle() )
  1258. {
  1259. default:
  1260. case TEXT_SPIN_STYLE::LEFT: text_offset.x = -dist; break; // Orientation horiz normale
  1261. case TEXT_SPIN_STYLE::UP: text_offset.y = -dist; break; // Orientation vert UP
  1262. case TEXT_SPIN_STYLE::RIGHT: text_offset.x = dist; break; // Orientation horiz inverse
  1263. case TEXT_SPIN_STYLE::BOTTOM: text_offset.y = dist; break; // Orientation vert BOTTOM
  1264. }
  1265. return text_offset;
  1266. }
  1267. wxString SCH_HIERLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  1268. {
  1269. return wxString::Format( _( "Hierarchical Label '%s'" ),
  1270. KIUI::EllipsizeMenuText( GetShownText() ) );
  1271. }
  1272. BITMAPS SCH_HIERLABEL::GetMenuImage() const
  1273. {
  1274. return BITMAPS::add_hierarchical_label;
  1275. }
  1276. HTML_MESSAGE_BOX* SCH_TEXT::ShowSyntaxHelp( wxWindow* aParentWindow )
  1277. {
  1278. wxString msg =
  1279. #include "sch_text_help_md.h"
  1280. ;
  1281. HTML_MESSAGE_BOX* dlg = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
  1282. wxSize sz( 320, 320 );
  1283. dlg->SetMinSize( dlg->ConvertDialogToPixels( sz ) );
  1284. dlg->SetDialogSizeInDU( sz.x, sz.y );
  1285. wxString html_txt;
  1286. ConvertMarkdown2Html( wxGetTranslation( msg ), html_txt );
  1287. dlg->AddHTML_Text( html_txt );
  1288. dlg->ShowModeless();
  1289. return dlg;
  1290. }