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.

1679 lines
49 KiB

  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-2022 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( "%%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( Type() == SCH_GLOBAL_LABEL_T && field.GetId() == 0 )
  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( Type() == SCH_GLOBAL_LABEL_T && field.GetId() == 0 )
  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( Type() == SCH_GLOBAL_LABEL_T && field.GetId() == 0 )
  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( Type() == SCH_GLOBAL_LABEL_T && field.GetId() == 0 )
  344. offset.y = labelLen + margin;
  345. else
  346. offset.x = accumulated + field.GetTextHeight() / 2;
  347. break;
  348. }
  349. field.SetTextPos( GetTextPos() + offset );
  350. if( Type() != SCH_GLOBAL_LABEL_T || field.GetId() > 0 )
  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( token->Contains( ':' ) )
  383. {
  384. if( !Schematic() )
  385. return false;
  386. if( Schematic()->ResolveCrossReference( token, aDepth ) )
  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. if( Type() == SCH_SHEET_PIN_T && m_parent )
  429. {
  430. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
  431. if( sheet->ResolveTextVar( token, aDepth ) )
  432. return true;
  433. }
  434. else if( Schematic() )
  435. {
  436. if( SCH_SHEET* sheet = Schematic()->CurrentSheet().Last() )
  437. {
  438. if( sheet->ResolveTextVar( token, aDepth ) )
  439. return true;
  440. }
  441. }
  442. return false;
  443. }
  444. wxString SCH_LABEL_BASE::GetShownText( int aDepth, bool aAllowExtraText ) const
  445. {
  446. std::function<bool( wxString* )> textResolver =
  447. [&]( wxString* token ) -> bool
  448. {
  449. return ResolveTextVar( token, aDepth );
  450. };
  451. std::function<bool( wxString* )> schematicTextResolver =
  452. [&]( wxString* token ) -> bool
  453. {
  454. return Schematic()->ResolveTextVar( token, aDepth + 1 );
  455. };
  456. wxString text = EDA_TEXT::GetShownText();
  457. if( text == wxS( "~" ) ) // Legacy placeholder for empty string
  458. {
  459. text = wxS( "" );
  460. }
  461. else if( HasTextVars() )
  462. {
  463. PROJECT* project = nullptr;
  464. if( Schematic() )
  465. project = &Schematic()->Prj();
  466. if( aDepth < 10 )
  467. text = ExpandTextVars( text, &textResolver, &schematicTextResolver, project );
  468. }
  469. return text;
  470. }
  471. void SCH_LABEL_BASE::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
  472. {
  473. for( SCH_FIELD& field : m_fields )
  474. aFunction( &field );
  475. }
  476. bool SCH_LABEL_BASE::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  477. {
  478. return SCH_ITEM::Matches( UnescapeString( GetText() ), aSearchData );
  479. }
  480. bool SCH_LABEL_BASE::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  481. {
  482. EDA_SEARCH_DATA localSearchData( aSearchData );
  483. localSearchData.findString = EscapeString( aSearchData.findString, CTX_NETNAME );
  484. localSearchData.replaceString = EscapeString( aSearchData.replaceString, CTX_NETNAME );
  485. return EDA_TEXT::Replace( localSearchData );
  486. }
  487. INSPECT_RESULT SCH_LABEL_BASE::Visit( INSPECTOR aInspector, void* testData,
  488. const std::vector<KICAD_T>& aScanTypes )
  489. {
  490. if( IsType( aScanTypes ) )
  491. {
  492. if( INSPECT_RESULT::QUIT == aInspector( this, nullptr ) )
  493. return INSPECT_RESULT::QUIT;
  494. }
  495. for( KICAD_T scanType : aScanTypes )
  496. {
  497. if( scanType == SCH_LOCATE_ANY_T || scanType == SCH_FIELD_T )
  498. {
  499. for( SCH_FIELD& field : m_fields )
  500. {
  501. if( INSPECT_RESULT::QUIT == aInspector( &field, this ) )
  502. return INSPECT_RESULT::QUIT;
  503. }
  504. }
  505. }
  506. return INSPECT_RESULT::CONTINUE;
  507. }
  508. void SCH_LABEL_BASE::GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList )
  509. {
  510. DANGLING_END_ITEM item( LABEL_END, this, GetTextPos() );
  511. aItemList.push_back( item );
  512. }
  513. std::vector<VECTOR2I> SCH_LABEL_BASE::GetConnectionPoints() const
  514. {
  515. return { GetTextPos() };
  516. }
  517. void SCH_LABEL_BASE::ViewGetLayers( int aLayers[], int& aCount ) const
  518. {
  519. aCount = 5;
  520. aLayers[0] = LAYER_DANGLING;
  521. aLayers[1] = LAYER_DEVICE;
  522. aLayers[2] = LAYER_NETCLASS_REFS;
  523. aLayers[3] = LAYER_FIELDS;
  524. aLayers[4] = LAYER_SELECTION_SHADOWS;
  525. }
  526. int SCH_LABEL_BASE::GetLabelBoxExpansion( const RENDER_SETTINGS* aSettings ) const
  527. {
  528. double ratio;
  529. if( aSettings )
  530. ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_LabelSizeRatio;
  531. else if( Schematic() )
  532. ratio = Schematic()->Settings().m_LabelSizeRatio;
  533. else
  534. ratio = DEFAULT_LABEL_SIZE_RATIO; // For previews (such as in Preferences), etc.
  535. return KiROUND( ratio * GetTextSize().y );
  536. }
  537. const BOX2I SCH_LABEL_BASE::GetBodyBoundingBox() const
  538. {
  539. // build the bounding box of the label only, without taking into account its fields
  540. BOX2I box;
  541. std::vector<VECTOR2I> pts;
  542. CreateGraphicShape( nullptr, pts, GetTextPos() );
  543. for( const VECTOR2I& pt : pts )
  544. box.Merge( pt );
  545. box.Inflate( GetEffectiveTextPenWidth() / 2 );
  546. box.Normalize();
  547. return box;
  548. }
  549. const BOX2I SCH_LABEL_BASE::GetBoundingBox() const
  550. {
  551. // build the bounding box of the entire label, including its fields
  552. BOX2I box = GetBodyBoundingBox();
  553. for( const SCH_FIELD& field : m_fields )
  554. {
  555. if( field.IsVisible() )
  556. {
  557. BOX2I fieldBBox = field.GetBoundingBox();
  558. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  559. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  560. box.Merge( fieldBBox );
  561. }
  562. }
  563. box.Normalize();
  564. return box;
  565. }
  566. bool SCH_LABEL_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  567. {
  568. BOX2I bbox = GetBodyBoundingBox();
  569. bbox.Inflate( aAccuracy );
  570. if( bbox.Contains( aPosition ) )
  571. return true;
  572. for( const SCH_FIELD& field : m_fields )
  573. {
  574. if( field.IsVisible() )
  575. {
  576. BOX2I fieldBBox = field.GetBoundingBox();
  577. fieldBBox.Inflate( aAccuracy );
  578. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  579. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  580. if( fieldBBox.Contains( aPosition ) )
  581. return true;
  582. }
  583. }
  584. return false;
  585. }
  586. bool SCH_LABEL_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  587. {
  588. BOX2I rect = aRect;
  589. rect.Inflate( aAccuracy );
  590. if( aContained )
  591. {
  592. return rect.Contains( GetBoundingBox() );
  593. }
  594. else
  595. {
  596. if( rect.Intersects( GetBodyBoundingBox() ) )
  597. return true;
  598. for( const SCH_FIELD& field : m_fields )
  599. {
  600. if( field.IsVisible() )
  601. {
  602. BOX2I fieldBBox = field.GetBoundingBox();
  603. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  604. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  605. if( rect.Intersects( fieldBBox ) )
  606. return true;
  607. }
  608. }
  609. return false;
  610. }
  611. }
  612. bool SCH_LABEL_BASE::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  613. const SCH_SHEET_PATH* aPath )
  614. {
  615. bool previousState = m_isDangling;
  616. m_isDangling = true;
  617. m_connectionType = CONNECTION_TYPE::NONE;
  618. for( unsigned ii = 0; ii < aItemList.size(); ii++ )
  619. {
  620. DANGLING_END_ITEM& item = aItemList[ii];
  621. if( item.GetItem() == this )
  622. continue;
  623. switch( item.GetType() )
  624. {
  625. case PIN_END:
  626. case LABEL_END:
  627. case SHEET_LABEL_END:
  628. case NO_CONNECT_END:
  629. if( GetTextPos() == item.GetPosition() )
  630. {
  631. m_isDangling = false;
  632. if( aPath && item.GetType() != PIN_END )
  633. AddConnectionTo( *aPath, static_cast<SCH_ITEM*>( item.GetItem() ) );
  634. }
  635. break;
  636. case BUS_END:
  637. m_connectionType = CONNECTION_TYPE::BUS;
  638. KI_FALLTHROUGH;
  639. case WIRE_END:
  640. {
  641. DANGLING_END_ITEM& nextItem = aItemList[++ii];
  642. int accuracy = 1; // We have rounding issues with an accuracy of 0
  643. m_isDangling = !TestSegmentHit( GetTextPos(), item.GetPosition(),
  644. nextItem.GetPosition(), accuracy );
  645. if( !m_isDangling )
  646. {
  647. if( m_connectionType != CONNECTION_TYPE::BUS )
  648. m_connectionType = CONNECTION_TYPE::NET;
  649. // Add the line to the connected items, since it won't be picked
  650. // up by a search of intersecting connection points
  651. if( aPath )
  652. {
  653. auto sch_item = static_cast<SCH_ITEM*>( item.GetItem() );
  654. AddConnectionTo( *aPath, sch_item );
  655. sch_item->AddConnectionTo( *aPath, this );
  656. }
  657. }
  658. }
  659. break;
  660. default:
  661. break;
  662. }
  663. if( !m_isDangling )
  664. break;
  665. }
  666. if( m_isDangling )
  667. m_connectionType = CONNECTION_TYPE::NONE;
  668. return previousState != m_isDangling;
  669. }
  670. void SCH_LABEL_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  671. {
  672. wxString msg;
  673. switch( Type() )
  674. {
  675. case SCH_LABEL_T: msg = _( "Label" ); break;
  676. case SCH_DIRECTIVE_LABEL_T: msg = _( "Directive Label" ); break;
  677. case SCH_GLOBAL_LABEL_T: msg = _( "Global Label" ); break;
  678. case SCH_HIER_LABEL_T: msg = _( "Hierarchical Label" ); break;
  679. case SCH_SHEET_PIN_T: msg = _( "Hierarchical Sheet Pin" ); break;
  680. default: return;
  681. }
  682. // Don't use GetShownText() here; we want to show the user the variable references
  683. aList.emplace_back( msg, UnescapeString( GetText() ) );
  684. // Display electrical type if it is relevant
  685. if( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
  686. aList.emplace_back( _( "Type" ), getElectricalTypeLabel( GetShape() ) );
  687. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  688. wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
  689. int style = IsBold() && IsItalic() ? 3 : IsBold() ? 2 : IsItalic() ? 1 : 0;
  690. aList.emplace_back( _( "Style" ), textStyle[style] );
  691. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  692. switch( GetTextSpinStyle() )
  693. {
  694. case TEXT_SPIN_STYLE::LEFT: msg = _( "Align right" ); break;
  695. case TEXT_SPIN_STYLE::UP: msg = _( "Align bottom" ); break;
  696. case TEXT_SPIN_STYLE::RIGHT: msg = _( "Align left" ); break;
  697. case TEXT_SPIN_STYLE::BOTTOM: msg = _( "Align top" ); break;
  698. default: msg = wxT( "???" ); break;
  699. }
  700. aList.emplace_back( _( "Justification" ), msg );
  701. SCH_CONNECTION* conn = nullptr;
  702. if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  703. conn = Connection();
  704. if( conn )
  705. {
  706. conn->AppendInfoToMsgPanel( aList );
  707. if( !conn->IsBus() )
  708. {
  709. aList.emplace_back( _( "Resolved Netclass" ),
  710. UnescapeString( GetEffectiveNetClass()->GetName() ) );
  711. }
  712. }
  713. }
  714. void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground ) const
  715. {
  716. static std::vector<VECTOR2I> s_poly;
  717. RENDER_SETTINGS* settings = aPlotter->RenderSettings();
  718. SCH_CONNECTION* connection = Connection();
  719. int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
  720. COLOR4D color = settings->GetLayerColor( layer );
  721. int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
  722. if( aPlotter->GetColorMode() && GetTextColor() != COLOR4D::UNSPECIFIED )
  723. color = GetTextColor();
  724. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  725. aPlotter->SetCurrentLineWidth( penWidth );
  726. KIFONT::FONT* font = GetFont();
  727. if( !font )
  728. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  729. VECTOR2I textpos = GetTextPos() + GetSchematicTextOffset( aPlotter->RenderSettings() );
  730. CreateGraphicShape( aPlotter->RenderSettings(), s_poly, GetTextPos() );
  731. if( aBackground )
  732. {
  733. // No filled shapes (yet)
  734. }
  735. else
  736. {
  737. aPlotter->Text( textpos, color, GetShownText(), GetTextAngle(), GetTextSize(),
  738. GetHorizJustify(), GetVertJustify(), penWidth, IsItalic(), IsBold(),
  739. false, font );
  740. if( s_poly.size() )
  741. aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
  742. // Plot attributes to a hypertext menu
  743. std::vector<wxString> properties;
  744. if( connection )
  745. {
  746. properties.emplace_back(
  747. wxString::Format( wxT( "!%s = %s" ), _( "Net" ), connection->Name() ) );
  748. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
  749. GetEffectiveNetClass()->GetName() ) );
  750. }
  751. for( const SCH_FIELD& field : GetFields() )
  752. {
  753. properties.emplace_back(
  754. wxString::Format( wxT( "!%s = %s" ), field.GetName(), field.GetShownText() ) );
  755. }
  756. if( !properties.empty() )
  757. aPlotter->HyperlinkMenu( GetBodyBoundingBox(), properties );
  758. if( Type() == SCH_HIER_LABEL_T )
  759. {
  760. aPlotter->Bookmark( GetBodyBoundingBox(), GetShownText(), _( "Hierarchical Labels" ) );
  761. }
  762. }
  763. for( const SCH_FIELD& field : m_fields )
  764. field.Plot( aPlotter, aBackground );
  765. }
  766. void SCH_LABEL_BASE::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  767. {
  768. static std::vector<VECTOR2I> s_poly;
  769. SCH_CONNECTION* connection = Connection();
  770. int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
  771. wxDC* DC = aSettings->GetPrintDC();
  772. COLOR4D color = aSettings->GetLayerColor( layer );
  773. bool blackAndWhiteMode = GetGRForceBlackPenState();
  774. int penWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
  775. VECTOR2I text_offset = aOffset + GetSchematicTextOffset( aSettings );
  776. if( !blackAndWhiteMode && GetTextColor() != COLOR4D::UNSPECIFIED )
  777. color = GetTextColor();
  778. EDA_TEXT::Print( aSettings, text_offset, color );
  779. CreateGraphicShape( aSettings, s_poly, GetTextPos() + aOffset );
  780. if( !s_poly.empty() )
  781. GRPoly( DC, s_poly.size(), &s_poly[0], false, penWidth, color, color );
  782. for( SCH_FIELD& field : m_fields )
  783. field.Print( aSettings, aOffset );
  784. }
  785. bool SCH_LABEL_BASE::AutoRotateOnPlacement() const
  786. {
  787. return m_autoRotateOnPlacement;
  788. }
  789. void SCH_LABEL_BASE::SetAutoRotateOnPlacement( bool autoRotate )
  790. {
  791. m_autoRotateOnPlacement = autoRotate;
  792. }
  793. SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) :
  794. SCH_LABEL_BASE( pos, text, SCH_LABEL_T )
  795. {
  796. m_layer = LAYER_LOCLABEL;
  797. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  798. m_isDangling = true;
  799. }
  800. const BOX2I SCH_LABEL::GetBodyBoundingBox() const
  801. {
  802. BOX2I rect = GetTextBox();
  803. rect.Offset( 0, -GetTextOffset() );
  804. rect.Inflate( GetEffectiveTextPenWidth() );
  805. if( !GetTextAngle().IsZero() )
  806. {
  807. // Rotate rect
  808. VECTOR2I pos = rect.GetOrigin();
  809. VECTOR2I end = rect.GetEnd();
  810. RotatePoint( pos, GetTextPos(), GetTextAngle() );
  811. RotatePoint( end, GetTextPos(), GetTextAngle() );
  812. rect.SetOrigin( pos );
  813. rect.SetEnd( end );
  814. rect.Normalize();
  815. }
  816. // Labels have a position point that is outside of the TextBox
  817. rect.Merge( GetPosition() );
  818. return rect;
  819. }
  820. wxString SCH_LABEL::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  821. {
  822. return wxString::Format( _( "Label '%s'" ), KIUI::EllipsizeMenuText( GetShownText() ) );
  823. }
  824. BITMAPS SCH_LABEL::GetMenuImage() const
  825. {
  826. return BITMAPS::add_line_label;
  827. }
  828. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const VECTOR2I& pos ) :
  829. SCH_LABEL_BASE( pos, wxEmptyString, SCH_DIRECTIVE_LABEL_T )
  830. {
  831. m_layer = LAYER_NETCLASS_REFS;
  832. m_shape = LABEL_FLAG_SHAPE::F_ROUND;
  833. m_pinLength = schIUScale.MilsToIU( 100 );
  834. m_symbolSize = schIUScale.MilsToIU( 20 );
  835. m_isDangling = true;
  836. }
  837. void SCH_DIRECTIVE_LABEL::SwapData( SCH_ITEM* aItem )
  838. {
  839. SCH_LABEL_BASE::SwapData( aItem );
  840. SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( aItem );
  841. std::swap( m_pinLength, label->m_pinLength );
  842. std::swap( m_symbolSize, label->m_symbolSize );
  843. }
  844. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel ) :
  845. SCH_LABEL_BASE( aClassLabel )
  846. {
  847. m_pinLength = aClassLabel.m_pinLength;
  848. m_symbolSize = aClassLabel.m_symbolSize;
  849. }
  850. int SCH_DIRECTIVE_LABEL::GetPenWidth() const
  851. {
  852. int pen = 0;
  853. if( Schematic() )
  854. pen = Schematic()->Settings().m_DefaultLineWidth;
  855. return GetEffectiveTextPenWidth( pen );
  856. }
  857. void SCH_DIRECTIVE_LABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  858. std::vector<VECTOR2I>& aPoints,
  859. const VECTOR2I& aPos ) const
  860. {
  861. int symbolSize = m_symbolSize;
  862. aPoints.clear();
  863. switch( m_shape )
  864. {
  865. case LABEL_FLAG_SHAPE::F_DOT:
  866. symbolSize = KiROUND( symbolSize * 0.7 );
  867. KI_FALLTHROUGH;
  868. case LABEL_FLAG_SHAPE::F_ROUND:
  869. // First 3 points are used for generating shape
  870. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  871. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  872. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  873. // These points are just used to bulk out the bounding box
  874. aPoints.emplace_back( VECTOR2I( -m_symbolSize, m_pinLength ) );
  875. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  876. aPoints.emplace_back( VECTOR2I( m_symbolSize, m_pinLength + symbolSize ) );
  877. break;
  878. case LABEL_FLAG_SHAPE::F_DIAMOND:
  879. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  880. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  881. aPoints.emplace_back( VECTOR2I( -2 * m_symbolSize, m_pinLength ) );
  882. aPoints.emplace_back( VECTOR2I( 0, m_pinLength + symbolSize ) );
  883. aPoints.emplace_back( VECTOR2I( 2 * m_symbolSize, m_pinLength ) );
  884. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  885. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  886. break;
  887. case LABEL_FLAG_SHAPE::F_RECTANGLE:
  888. symbolSize = KiROUND( symbolSize * 0.8 );
  889. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  890. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  891. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength - symbolSize ) );
  892. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength + symbolSize ) );
  893. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength + symbolSize ) );
  894. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength - symbolSize ) );
  895. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  896. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  897. break;
  898. default:
  899. break;
  900. }
  901. // Rotate outlines and move corners to real position
  902. for( VECTOR2I& aPoint : aPoints )
  903. {
  904. switch( GetTextSpinStyle() )
  905. {
  906. default:
  907. case TEXT_SPIN_STYLE::LEFT: break;
  908. case TEXT_SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  909. case TEXT_SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  910. case TEXT_SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  911. }
  912. aPoint += aPos;
  913. }
  914. }
  915. void SCH_DIRECTIVE_LABEL::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
  916. {
  917. int margin = GetTextOffset();
  918. int symbolWidth = m_symbolSize;
  919. int origin = m_pinLength;
  920. if( m_shape == LABEL_FLAG_SHAPE::F_DIAMOND || m_shape == LABEL_FLAG_SHAPE::F_RECTANGLE )
  921. symbolWidth *= 2;
  922. if( IsItalic() )
  923. margin = KiROUND( margin * 1.5 );
  924. VECTOR2I offset;
  925. for( SCH_FIELD& field : m_fields )
  926. {
  927. switch( GetTextSpinStyle() )
  928. {
  929. default:
  930. case TEXT_SPIN_STYLE::LEFT:
  931. field.SetTextAngle( ANGLE_HORIZONTAL );
  932. offset = { symbolWidth + margin, origin };
  933. break;
  934. case TEXT_SPIN_STYLE::UP:
  935. field.SetTextAngle( ANGLE_VERTICAL );
  936. offset = { -origin, -( symbolWidth + margin ) };
  937. break;
  938. case TEXT_SPIN_STYLE::RIGHT:
  939. field.SetTextAngle( ANGLE_HORIZONTAL );
  940. offset = { symbolWidth + margin, -origin };
  941. break;
  942. case TEXT_SPIN_STYLE::BOTTOM:
  943. field.SetTextAngle( ANGLE_VERTICAL );
  944. offset = { origin, -( symbolWidth + margin ) };
  945. break;
  946. }
  947. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  948. field.SetTextPos( GetPosition() + offset );
  949. origin -= field.GetTextHeight() + margin;
  950. }
  951. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  952. }
  953. wxString SCH_DIRECTIVE_LABEL::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  954. {
  955. if( m_fields.empty() )
  956. {
  957. return _( "Directive Label" );
  958. }
  959. else
  960. {
  961. return wxString::Format( _( "Directive Label [%s %s]" ),
  962. m_fields[0].GetName(),
  963. m_fields[0].GetShownText() );
  964. }
  965. }
  966. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const VECTOR2I& pos, const wxString& text ) :
  967. SCH_LABEL_BASE( pos, text, SCH_GLOBAL_LABEL_T )
  968. {
  969. m_layer = LAYER_GLOBLABEL;
  970. m_shape = LABEL_FLAG_SHAPE::L_BIDI;
  971. m_isDangling = true;
  972. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  973. m_fields.emplace_back( SCH_FIELD( pos, 0, this, _( "Sheet References" ) ) );
  974. m_fields[0].SetText( wxT( "${INTERSHEET_REFS}" ) );
  975. m_fields[0].SetVisible( false );
  976. m_fields[0].SetLayer( LAYER_INTERSHEET_REFS );
  977. m_fields[0].SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  978. }
  979. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
  980. SCH_LABEL_BASE( aGlobalLabel )
  981. {
  982. }
  983. VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  984. {
  985. int horiz = GetLabelBoxExpansion( aSettings );
  986. // Center the text on the center line of "E" instead of "R" to make room for an overbar
  987. int vert = GetTextHeight() * 0.0715;
  988. switch( m_shape )
  989. {
  990. case LABEL_FLAG_SHAPE::L_INPUT:
  991. case LABEL_FLAG_SHAPE::L_BIDI:
  992. case LABEL_FLAG_SHAPE::L_TRISTATE:
  993. horiz += GetTextHeight() * 3 / 4; // Use three-quarters-height as proxy for triangle size
  994. break;
  995. case LABEL_FLAG_SHAPE::L_OUTPUT:
  996. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  997. default:
  998. break;
  999. }
  1000. switch( GetTextSpinStyle() )
  1001. {
  1002. default:
  1003. case TEXT_SPIN_STYLE::LEFT: return VECTOR2I( -horiz, vert );
  1004. case TEXT_SPIN_STYLE::UP: return VECTOR2I( vert, -horiz );
  1005. case TEXT_SPIN_STYLE::RIGHT: return VECTOR2I( horiz, vert );
  1006. case TEXT_SPIN_STYLE::BOTTOM: return VECTOR2I( vert, horiz );
  1007. }
  1008. }
  1009. void SCH_GLOBALLABEL::SetTextSpinStyle( TEXT_SPIN_STYLE aSpinStyle )
  1010. {
  1011. SCH_TEXT::SetTextSpinStyle( aSpinStyle );
  1012. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1013. }
  1014. void SCH_GLOBALLABEL::MirrorSpinStyle( bool aLeftRight )
  1015. {
  1016. SCH_TEXT::MirrorSpinStyle( aLeftRight );
  1017. for( SCH_FIELD& field : m_fields )
  1018. {
  1019. if( ( aLeftRight && field.GetTextAngle().IsHorizontal() )
  1020. || ( !aLeftRight && field.GetTextAngle().IsVertical() ) )
  1021. {
  1022. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1023. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1024. else
  1025. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1026. }
  1027. VECTOR2I pos = field.GetTextPos();
  1028. VECTOR2I delta = (VECTOR2I)GetPosition() - pos;
  1029. if( aLeftRight )
  1030. pos.x = GetPosition().x + delta.x;
  1031. else
  1032. pos.y = GetPosition().y + delta.y;
  1033. field.SetTextPos( pos );
  1034. }
  1035. }
  1036. void SCH_GLOBALLABEL::MirrorHorizontally( int aCenter )
  1037. {
  1038. VECTOR2I old_pos = GetPosition();
  1039. SCH_TEXT::MirrorHorizontally( aCenter );
  1040. for( SCH_FIELD& field : m_fields )
  1041. {
  1042. switch( field.GetHorizJustify() )
  1043. {
  1044. case GR_TEXT_H_ALIGN_LEFT:
  1045. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1046. break;
  1047. case GR_TEXT_H_ALIGN_CENTER:
  1048. break;
  1049. case GR_TEXT_H_ALIGN_RIGHT:
  1050. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1051. break;
  1052. }
  1053. VECTOR2I pos = field.GetTextPos();
  1054. VECTOR2I delta = old_pos - pos;
  1055. pos.x = GetPosition().x + delta.x;
  1056. field.SetPosition( pos );
  1057. }
  1058. }
  1059. void SCH_GLOBALLABEL::MirrorVertically( int aCenter )
  1060. {
  1061. VECTOR2I old_pos = GetPosition();
  1062. SCH_TEXT::MirrorVertically( aCenter );
  1063. for( SCH_FIELD& field : m_fields )
  1064. {
  1065. VECTOR2I pos = field.GetTextPos();
  1066. VECTOR2I delta = old_pos - pos;
  1067. pos.y = GetPosition().y + delta.y;
  1068. field.SetPosition( pos );
  1069. }
  1070. }
  1071. bool SCH_GLOBALLABEL::ResolveTextVar( wxString* token, int aDepth ) const
  1072. {
  1073. if( token->IsSameAs( wxT( "INTERSHEET_REFS" ) ) && Schematic() )
  1074. {
  1075. SCHEMATIC_SETTINGS& settings = Schematic()->Settings();
  1076. wxString ref;
  1077. auto it = Schematic()->GetPageRefsMap().find( GetText() );
  1078. if( it == Schematic()->GetPageRefsMap().end() )
  1079. {
  1080. ref = "?";
  1081. }
  1082. else
  1083. {
  1084. std::vector<int> pageListCopy;
  1085. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  1086. std::sort( pageListCopy.begin(), pageListCopy.end() );
  1087. if( !settings.m_IntersheetRefsListOwnPage )
  1088. {
  1089. int currentPage = Schematic()->CurrentSheet().GetVirtualPageNumber();
  1090. alg::delete_matching( pageListCopy, currentPage );
  1091. }
  1092. std::map<int, wxString> sheetPages = Schematic()->GetVirtualPageToSheetPagesMap();
  1093. if( ( settings.m_IntersheetRefsFormatShort ) && ( pageListCopy.size() > 2 ) )
  1094. {
  1095. ref.Append( wxString::Format( wxT( "%s..%s" ),
  1096. sheetPages[pageListCopy.front()],
  1097. sheetPages[pageListCopy.back()] ) );
  1098. }
  1099. else
  1100. {
  1101. for( const int& pageNo : pageListCopy )
  1102. ref.Append( wxString::Format( wxT( "%s," ), sheetPages[pageNo] ) );
  1103. if( !ref.IsEmpty() && ref.Last() == ',' )
  1104. ref.RemoveLast();
  1105. }
  1106. }
  1107. *token = settings.m_IntersheetRefsPrefix + ref + settings.m_IntersheetRefsSuffix;
  1108. return true;
  1109. }
  1110. return SCH_LABEL_BASE::ResolveTextVar( token, aDepth );
  1111. }
  1112. void SCH_GLOBALLABEL::ViewGetLayers( int aLayers[], int& aCount ) const
  1113. {
  1114. aCount = 6;
  1115. aLayers[0] = LAYER_DANGLING;
  1116. aLayers[1] = LAYER_DEVICE;
  1117. aLayers[2] = LAYER_INTERSHEET_REFS;
  1118. aLayers[3] = LAYER_NETCLASS_REFS;
  1119. aLayers[4] = LAYER_FIELDS;
  1120. aLayers[5] = LAYER_SELECTION_SHADOWS;
  1121. }
  1122. void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1123. std::vector<VECTOR2I>& aPoints,
  1124. const VECTOR2I& aPos ) const
  1125. {
  1126. int margin = GetLabelBoxExpansion( aRenderSettings );
  1127. int halfSize = ( GetTextHeight() / 2 ) + margin;
  1128. int linewidth = GetPenWidth();
  1129. int symb_len = GetTextBox().GetWidth() + 2 * margin;
  1130. int x = symb_len + linewidth + 3;
  1131. int y = halfSize + linewidth + 3;
  1132. aPoints.clear();
  1133. // Create outline shape : 6 points
  1134. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1135. aPoints.emplace_back( VECTOR2I( 0, -y ) ); // Up
  1136. aPoints.emplace_back( VECTOR2I( -x, -y ) ); // left
  1137. aPoints.emplace_back( VECTOR2I( -x, 0 ) ); // Up left
  1138. aPoints.emplace_back( VECTOR2I( -x, y ) ); // left down
  1139. aPoints.emplace_back( VECTOR2I( 0, y ) ); // down
  1140. int x_offset = 0;
  1141. switch( m_shape )
  1142. {
  1143. case LABEL_FLAG_SHAPE::L_INPUT:
  1144. x_offset = -halfSize;
  1145. aPoints[0].x += halfSize;
  1146. break;
  1147. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1148. aPoints[3].x -= halfSize;
  1149. break;
  1150. case LABEL_FLAG_SHAPE::L_BIDI:
  1151. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1152. x_offset = -halfSize;
  1153. aPoints[0].x += halfSize;
  1154. aPoints[3].x -= halfSize;
  1155. break;
  1156. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1157. default:
  1158. break;
  1159. }
  1160. // Rotate outlines and move corners in real position
  1161. for( VECTOR2I& aPoint : aPoints )
  1162. {
  1163. aPoint.x += x_offset;
  1164. switch( GetTextSpinStyle() )
  1165. {
  1166. default:
  1167. case TEXT_SPIN_STYLE::LEFT: break;
  1168. case TEXT_SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1169. case TEXT_SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1170. case TEXT_SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1171. }
  1172. aPoint += aPos;
  1173. }
  1174. aPoints.push_back( aPoints[0] ); // closing
  1175. }
  1176. wxString SCH_GLOBALLABEL::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  1177. {
  1178. return wxString::Format( _( "Global Label '%s'" ), KIUI::EllipsizeMenuText( GetShownText() ) );
  1179. }
  1180. BITMAPS SCH_GLOBALLABEL::GetMenuImage() const
  1181. {
  1182. return BITMAPS::add_glabel;
  1183. }
  1184. SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T aType ) :
  1185. SCH_LABEL_BASE( pos, text, aType )
  1186. {
  1187. m_layer = LAYER_HIERLABEL;
  1188. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1189. m_isDangling = true;
  1190. }
  1191. void SCH_HIERLABEL::SetTextSpinStyle( TEXT_SPIN_STYLE aSpinStyle )
  1192. {
  1193. SCH_TEXT::SetTextSpinStyle( aSpinStyle );
  1194. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1195. }
  1196. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1197. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos ) const
  1198. {
  1199. CreateGraphicShape( aSettings, aPoints, aPos, m_shape );
  1200. }
  1201. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1202. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos,
  1203. LABEL_FLAG_SHAPE aShape ) const
  1204. {
  1205. int* Template = TemplateShape[static_cast<int>( aShape )][static_cast<int>( m_spin_style )];
  1206. int halfSize = GetTextHeight() / 2;
  1207. int imax = *Template;
  1208. Template++;
  1209. aPoints.clear();
  1210. for( int ii = 0; ii < imax; ii++ )
  1211. {
  1212. VECTOR2I corner;
  1213. corner.x = ( halfSize * (*Template) ) + aPos.x;
  1214. Template++;
  1215. corner.y = ( halfSize * (*Template) ) + aPos.y;
  1216. Template++;
  1217. aPoints.push_back( corner );
  1218. }
  1219. }
  1220. const BOX2I SCH_HIERLABEL::GetBodyBoundingBox() const
  1221. {
  1222. int penWidth = GetEffectiveTextPenWidth();
  1223. int margin = GetTextOffset();
  1224. int x = GetTextPos().x;
  1225. int y = GetTextPos().y;
  1226. int height = GetTextHeight() + penWidth + margin;
  1227. int length = GetTextBox().GetWidth();
  1228. length += height; // add height for triangular shapes
  1229. int dx, dy;
  1230. switch( GetTextSpinStyle() )
  1231. {
  1232. default:
  1233. case TEXT_SPIN_STYLE::LEFT:
  1234. dx = -length;
  1235. dy = height;
  1236. x += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1237. y -= height / 2;
  1238. break;
  1239. case TEXT_SPIN_STYLE::UP:
  1240. dx = height;
  1241. dy = -length;
  1242. x -= height / 2;
  1243. y += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1244. break;
  1245. case TEXT_SPIN_STYLE::RIGHT:
  1246. dx = length;
  1247. dy = height;
  1248. x -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1249. y -= height / 2;
  1250. break;
  1251. case TEXT_SPIN_STYLE::BOTTOM:
  1252. dx = height;
  1253. dy = length;
  1254. x -= height / 2;
  1255. y -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1256. break;
  1257. }
  1258. BOX2I box( VECTOR2I( x, y ), VECTOR2I( dx, dy ) );
  1259. box.Normalize();
  1260. return box;
  1261. }
  1262. VECTOR2I SCH_HIERLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1263. {
  1264. VECTOR2I text_offset;
  1265. int dist = GetTextOffset( aSettings );
  1266. dist += GetTextWidth();
  1267. switch( GetTextSpinStyle() )
  1268. {
  1269. default:
  1270. case TEXT_SPIN_STYLE::LEFT: text_offset.x = -dist; break; // Orientation horiz normale
  1271. case TEXT_SPIN_STYLE::UP: text_offset.y = -dist; break; // Orientation vert UP
  1272. case TEXT_SPIN_STYLE::RIGHT: text_offset.x = dist; break; // Orientation horiz inverse
  1273. case TEXT_SPIN_STYLE::BOTTOM: text_offset.y = dist; break; // Orientation vert BOTTOM
  1274. }
  1275. return text_offset;
  1276. }
  1277. wxString SCH_HIERLABEL::GetSelectMenuText( UNITS_PROVIDER* aUnitsProvider ) const
  1278. {
  1279. return wxString::Format( _( "Hierarchical Label '%s'" ),
  1280. KIUI::EllipsizeMenuText( GetShownText() ) );
  1281. }
  1282. BITMAPS SCH_HIERLABEL::GetMenuImage() const
  1283. {
  1284. return BITMAPS::add_hierarchical_label;
  1285. }
  1286. HTML_MESSAGE_BOX* SCH_TEXT::ShowSyntaxHelp( wxWindow* aParentWindow )
  1287. {
  1288. wxString msg =
  1289. #include "sch_text_help_md.h"
  1290. ;
  1291. HTML_MESSAGE_BOX* dlg = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
  1292. wxSize sz( 320, 320 );
  1293. dlg->SetMinSize( dlg->ConvertDialogToPixels( sz ) );
  1294. dlg->SetDialogSizeInDU( sz.x, sz.y );
  1295. wxString html_txt;
  1296. ConvertMarkdown2Html( wxGetTranslation( msg ), html_txt );
  1297. dlg->AddHTML_Text( html_txt );
  1298. dlg->ShowModeless();
  1299. return dlg;
  1300. }