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.

2302 lines
68 KiB

2 years ago
2 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 The 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 <advanced_config.h>
  26. #include <base_units.h>
  27. #include <increment.h>
  28. #include <pgm_base.h>
  29. #include <sch_edit_frame.h>
  30. #include <sch_plotter.h>
  31. #include <widgets/msgpanel.h>
  32. #include <bitmaps.h>
  33. #include <string_utils.h>
  34. #include <schematic.h>
  35. #include <settings/color_settings.h>
  36. #include <sch_painter.h>
  37. #include <default_values.h>
  38. #include <wx/debug.h>
  39. #include <wx/log.h>
  40. #include <dialogs/html_message_box.h>
  41. #include <project/project_file.h>
  42. #include <project/net_settings.h>
  43. #include <core/kicad_algo.h>
  44. #include <core/mirror.h>
  45. #include <trigo.h>
  46. #include <sch_label.h>
  47. #include <magic_enum.hpp>
  48. #include <api/api_utils.h>
  49. #include <api/schematic/schematic_types.pb.h>
  50. /* Coding polygons for global symbol graphic shapes.
  51. * the first parml is the number of corners
  52. * others are the corners coordinates in reduced units
  53. * the real coordinate is the reduced coordinate * text half size
  54. */
  55. static int TemplateIN_HN[] = { 6, 0, 0, -1, -1, -2, -1, -2, 1, -1, 1, 0, 0 };
  56. static int TemplateIN_HI[] = { 6, 0, 0, 1, 1, 2, 1, 2, -1, 1, -1, 0, 0 };
  57. static int TemplateIN_UP[] = { 6, 0, 0, 1, -1, 1, -2, -1, -2, -1, -1, 0, 0 };
  58. static int TemplateIN_BOTTOM[] = { 6, 0, 0, 1, 1, 1, 2, -1, 2, -1, 1, 0, 0 };
  59. static int TemplateOUT_HN[] = { 6, -2, 0, -1, 1, 0, 1, 0, -1, -1, -1, -2, 0 };
  60. static int TemplateOUT_HI[] = { 6, 2, 0, 1, -1, 0, -1, 0, 1, 1, 1, 2, 0 };
  61. static int TemplateOUT_UP[] = { 6, 0, -2, 1, -1, 1, 0, -1, 0, -1, -1, 0, -2 };
  62. static int TemplateOUT_BOTTOM[] = { 6, 0, 2, 1, 1, 1, 0, -1, 0, -1, 1, 0, 2 };
  63. static int TemplateUNSPC_HN[] = { 5, 0, -1, -2, -1, -2, 1, 0, 1, 0, -1 };
  64. static int TemplateUNSPC_HI[] = { 5, 0, -1, 2, -1, 2, 1, 0, 1, 0, -1 };
  65. static int TemplateUNSPC_UP[] = { 5, 1, 0, 1, -2, -1, -2, -1, 0, 1, 0 };
  66. static int TemplateUNSPC_BOTTOM[] = { 5, 1, 0, 1, 2, -1, 2, -1, 0, 1, 0 };
  67. static int TemplateBIDI_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
  68. static int TemplateBIDI_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
  69. static int TemplateBIDI_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
  70. static int TemplateBIDI_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
  71. static int Template3STATE_HN[] = { 5, 0, 0, -1, -1, -2, 0, -1, 1, 0, 0 };
  72. static int Template3STATE_HI[] = { 5, 0, 0, 1, -1, 2, 0, 1, 1, 0, 0 };
  73. static int Template3STATE_UP[] = { 5, 0, 0, -1, -1, 0, -2, 1, -1, 0, 0 };
  74. static int Template3STATE_BOTTOM[] = { 5, 0, 0, -1, 1, 0, 2, 1, 1, 0, 0 };
  75. static int* TemplateShape[5][4] =
  76. {
  77. { TemplateIN_HN, TemplateIN_UP, TemplateIN_HI, TemplateIN_BOTTOM },
  78. { TemplateOUT_HN, TemplateOUT_UP, TemplateOUT_HI, TemplateOUT_BOTTOM },
  79. { TemplateBIDI_HN, TemplateBIDI_UP, TemplateBIDI_HI, TemplateBIDI_BOTTOM },
  80. { Template3STATE_HN, Template3STATE_UP, Template3STATE_HI, Template3STATE_BOTTOM },
  81. { TemplateUNSPC_HN, TemplateUNSPC_UP, TemplateUNSPC_HI, TemplateUNSPC_BOTTOM }
  82. };
  83. wxString getElectricalTypeLabel( LABEL_FLAG_SHAPE aType )
  84. {
  85. switch( aType )
  86. {
  87. case LABEL_FLAG_SHAPE::L_INPUT: return _( "Input" );
  88. case LABEL_FLAG_SHAPE::L_OUTPUT: return _( "Output" );
  89. case LABEL_FLAG_SHAPE::L_BIDI: return _( "Bidirectional" );
  90. case LABEL_FLAG_SHAPE::L_TRISTATE: return _( "Tri-State" );
  91. case LABEL_FLAG_SHAPE::L_UNSPECIFIED: return _( "Passive" );
  92. default: return wxT( "???" );
  93. }
  94. }
  95. SPIN_STYLE SPIN_STYLE::RotateCCW()
  96. {
  97. SPIN newSpin = m_spin;
  98. switch( m_spin )
  99. {
  100. case SPIN_STYLE::LEFT: newSpin = SPIN_STYLE::BOTTOM; break;
  101. case SPIN_STYLE::BOTTOM: newSpin = SPIN_STYLE::RIGHT; break;
  102. case SPIN_STYLE::RIGHT: newSpin = SPIN_STYLE::UP; break;
  103. case SPIN_STYLE::UP: newSpin = SPIN_STYLE::LEFT; break;
  104. }
  105. return SPIN_STYLE( newSpin );
  106. }
  107. SPIN_STYLE SPIN_STYLE::MirrorX()
  108. {
  109. SPIN newSpin = m_spin;
  110. switch( m_spin )
  111. {
  112. case SPIN_STYLE::UP: newSpin = SPIN_STYLE::BOTTOM; break;
  113. case SPIN_STYLE::BOTTOM: newSpin = SPIN_STYLE::UP; break;
  114. case SPIN_STYLE::LEFT: break;
  115. case SPIN_STYLE::RIGHT: break;
  116. }
  117. return SPIN_STYLE( newSpin );
  118. }
  119. SPIN_STYLE SPIN_STYLE::MirrorY()
  120. {
  121. SPIN newSpin = m_spin;
  122. switch( m_spin )
  123. {
  124. case SPIN_STYLE::LEFT: newSpin = SPIN_STYLE::RIGHT; break;
  125. case SPIN_STYLE::RIGHT: newSpin = SPIN_STYLE::LEFT; break;
  126. case SPIN_STYLE::UP: break;
  127. case SPIN_STYLE::BOTTOM: break;
  128. }
  129. return SPIN_STYLE( newSpin );
  130. }
  131. unsigned SPIN_STYLE::CCWRotationsTo( const SPIN_STYLE& aOther ) const
  132. {
  133. return ( ( (int) m_spin - (int) aOther.m_spin ) % 4 + 4 ) % 4;
  134. }
  135. SCH_LABEL_BASE::SCH_LABEL_BASE( const VECTOR2I& aPos, const wxString& aText, KICAD_T aType ) :
  136. SCH_TEXT( aPos, aText, LAYER_NOTES, aType ),
  137. m_shape( L_UNSPECIFIED ),
  138. m_connectionType( CONNECTION_TYPE::NONE ),
  139. m_isDangling( true ),
  140. m_lastResolvedColor( COLOR4D::UNSPECIFIED )
  141. {
  142. SetMultilineAllowed( false );
  143. if( !HasTextVars() )
  144. m_cached_driver_name = EscapeString( EDA_TEXT::GetShownText( true, 0 ), CTX_NETNAME );
  145. }
  146. SCH_LABEL_BASE::SCH_LABEL_BASE( const SCH_LABEL_BASE& aLabel ) :
  147. SCH_TEXT( aLabel ),
  148. m_shape( aLabel.m_shape ),
  149. m_connectionType( aLabel.m_connectionType ),
  150. m_isDangling( aLabel.m_isDangling ),
  151. m_lastResolvedColor( aLabel.m_lastResolvedColor ),
  152. m_cached_driver_name( aLabel.m_cached_driver_name )
  153. {
  154. SetMultilineAllowed( false );
  155. m_fields = aLabel.m_fields;
  156. for( SCH_FIELD& field : m_fields )
  157. field.SetParent( this );
  158. }
  159. SCH_LABEL_BASE& SCH_LABEL_BASE::operator=( const SCH_LABEL_BASE& aLabel )
  160. {
  161. SCH_TEXT::operator=( aLabel );
  162. m_cached_driver_name = aLabel.m_cached_driver_name;
  163. return *this;
  164. }
  165. const wxString SCH_LABEL_BASE::GetDefaultFieldName( const wxString& aName, bool aUseDefaultName )
  166. {
  167. if( aName == wxT( "Intersheetrefs" ) )
  168. return _( "Sheet References" );
  169. else if( aName == wxT( "Netclass" ) )
  170. return _( "Net Class" );
  171. else if( aName.IsEmpty() && aUseDefaultName )
  172. return _( "Field" );
  173. else
  174. return aName;
  175. }
  176. int SCH_LABEL_BASE::GetNextFieldOrdinal() const
  177. {
  178. return NextFieldOrdinal( m_fields );
  179. }
  180. bool SCH_LABEL_BASE::IsType( const std::vector<KICAD_T>& aScanTypes ) const
  181. {
  182. static const std::vector<KICAD_T> wireAndPinTypes = { SCH_ITEM_LOCATE_WIRE_T, SCH_PIN_T };
  183. static const std::vector<KICAD_T> busTypes = { SCH_ITEM_LOCATE_BUS_T };
  184. if( SCH_TEXT::IsType( aScanTypes ) )
  185. return true;
  186. for( KICAD_T scanType : aScanTypes )
  187. {
  188. if( scanType == SCH_LABEL_LOCATE_ANY_T )
  189. return true;
  190. }
  191. wxCHECK_MSG( Schematic(), false, wxT( "No parent SCHEMATIC set for SCH_LABEL!" ) );
  192. // Ensure m_connected_items for Schematic()->CurrentSheet() exists.
  193. // Can be not the case when "this" is living in clipboard
  194. if( m_connected_items.find( Schematic()->CurrentSheet() ) == m_connected_items.end() )
  195. return false;
  196. const SCH_ITEM_VEC& item_set = m_connected_items.at( Schematic()->CurrentSheet() );
  197. for( KICAD_T scanType : aScanTypes )
  198. {
  199. if( scanType == SCH_LABEL_LOCATE_WIRE_T )
  200. {
  201. for( SCH_ITEM* connection : item_set )
  202. {
  203. if( connection->IsType( wireAndPinTypes ) )
  204. return true;
  205. }
  206. }
  207. if ( scanType == SCH_LABEL_LOCATE_BUS_T )
  208. {
  209. for( SCH_ITEM* connection : item_set )
  210. {
  211. if( connection->IsType( busTypes ) )
  212. return true;
  213. }
  214. }
  215. }
  216. return false;
  217. }
  218. void SCH_LABEL_BASE::SwapData( SCH_ITEM* aItem )
  219. {
  220. SCH_TEXT::SwapData( aItem );
  221. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( aItem );
  222. m_fields.swap( label->m_fields );
  223. std::swap( m_fieldsAutoplaced, label->m_fieldsAutoplaced );
  224. for( SCH_FIELD& field : m_fields )
  225. field.SetParent( this );
  226. for( SCH_FIELD& field : label->m_fields )
  227. field.SetParent( label );
  228. std::swap( m_shape, label->m_shape );
  229. std::swap( m_connectionType, label->m_connectionType );
  230. std::swap( m_isDangling, label->m_isDangling );
  231. std::swap( m_lastResolvedColor, label->m_lastResolvedColor );
  232. }
  233. COLOR4D SCH_LABEL_BASE::GetLabelColor() const
  234. {
  235. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  236. m_lastResolvedColor = GetTextColor();
  237. else if( !IsConnectivityDirty() )
  238. m_lastResolvedColor = GetEffectiveNetClass()->GetSchematicColor();
  239. return m_lastResolvedColor;
  240. }
  241. void SCH_LABEL_BASE::SetSpinStyle( SPIN_STYLE aSpinStyle )
  242. {
  243. // Assume "Right" and Left" mean which side of the anchor the text will be on
  244. // Thus we want to left justify text up against the anchor if we are on the right
  245. switch( aSpinStyle )
  246. {
  247. default:
  248. wxFAIL_MSG( "Bad spin style" );
  249. KI_FALLTHROUGH;
  250. case SPIN_STYLE::RIGHT: // Horiz Normal Orientation
  251. SetTextAngle( ANGLE_HORIZONTAL );
  252. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  253. break;
  254. case SPIN_STYLE::UP: // Vert Orientation UP
  255. SetTextAngle( ANGLE_VERTICAL );
  256. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  257. break;
  258. case SPIN_STYLE::LEFT: // Horiz Orientation - Right justified
  259. SetTextAngle( ANGLE_HORIZONTAL );
  260. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  261. break;
  262. case SPIN_STYLE::BOTTOM: // Vert Orientation BOTTOM
  263. SetTextAngle( ANGLE_VERTICAL );
  264. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  265. break;
  266. }
  267. SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  268. }
  269. SPIN_STYLE SCH_LABEL_BASE::GetSpinStyle() const
  270. {
  271. if( GetTextAngle() == ANGLE_VERTICAL )
  272. {
  273. if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  274. return SPIN_STYLE::BOTTOM;
  275. else
  276. return SPIN_STYLE::UP;
  277. }
  278. else
  279. {
  280. if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  281. return SPIN_STYLE::LEFT;
  282. else
  283. return SPIN_STYLE::RIGHT;
  284. }
  285. }
  286. VECTOR2I SCH_LABEL_BASE::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  287. {
  288. VECTOR2I text_offset;
  289. // add an offset to x (or y) position to aid readability of text on a wire or line
  290. int dist = GetTextOffset( aSettings ) + GetPenWidth();
  291. switch( GetSpinStyle() )
  292. {
  293. case SPIN_STYLE::UP:
  294. case SPIN_STYLE::BOTTOM: text_offset.x = -dist; break; // Vert Orientation
  295. default:
  296. case SPIN_STYLE::LEFT:
  297. case SPIN_STYLE::RIGHT: text_offset.y = -dist; break; // Horiz Orientation
  298. }
  299. return text_offset;
  300. }
  301. void SCH_LABEL_BASE::SetPosition( const VECTOR2I& aPosition )
  302. {
  303. VECTOR2I offset = aPosition - GetTextPos();
  304. Move( offset );
  305. }
  306. void SCH_LABEL_BASE::Move( const VECTOR2I& aMoveVector )
  307. {
  308. SCH_TEXT::Move( aMoveVector );
  309. for( SCH_FIELD& field : m_fields )
  310. field.Offset( aMoveVector );
  311. }
  312. void SCH_LABEL_BASE::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
  313. {
  314. VECTOR2I pt = GetTextPos();
  315. RotatePoint( pt, aCenter, aRotateCCW ? ANGLE_90 : ANGLE_270 );
  316. VECTOR2I offset = pt - GetTextPos();
  317. Rotate90( !aRotateCCW );
  318. SetTextPos( GetTextPos() + offset );
  319. for( SCH_FIELD& field : m_fields )
  320. field.SetTextPos( field.GetTextPos() + offset );
  321. }
  322. void SCH_LABEL_BASE::Rotate90( bool aClockwise )
  323. {
  324. SCH_TEXT::Rotate90( aClockwise );
  325. if( m_fieldsAutoplaced == AUTOPLACE_AUTO || m_fieldsAutoplaced == AUTOPLACE_MANUAL )
  326. {
  327. AutoplaceFields( nullptr, m_fieldsAutoplaced );
  328. }
  329. else
  330. {
  331. for( SCH_FIELD& field : m_fields )
  332. field.Rotate( GetPosition(), !aClockwise );
  333. }
  334. }
  335. void SCH_LABEL_BASE::MirrorSpinStyle( bool aLeftRight )
  336. {
  337. SCH_TEXT::MirrorSpinStyle( aLeftRight );
  338. for( SCH_FIELD& field : m_fields )
  339. {
  340. if( ( aLeftRight && field.GetTextAngle().IsHorizontal() )
  341. || ( !aLeftRight && field.GetTextAngle().IsVertical() ) )
  342. {
  343. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  344. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  345. else
  346. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  347. }
  348. VECTOR2I pos = field.GetTextPos();
  349. VECTOR2I delta = (VECTOR2I)GetPosition() - pos;
  350. if( aLeftRight )
  351. pos.x = GetPosition().x + delta.x;
  352. else
  353. pos.y = GetPosition().y + delta.y;
  354. field.SetTextPos( pos );
  355. }
  356. }
  357. void SCH_LABEL_BASE::MirrorHorizontally( int aCenter )
  358. {
  359. VECTOR2I old_pos = GetPosition();
  360. SCH_TEXT::MirrorHorizontally( aCenter );
  361. for( SCH_FIELD& field : m_fields )
  362. {
  363. if( field.GetTextAngle() == ANGLE_HORIZONTAL )
  364. field.FlipHJustify();
  365. VECTOR2I pos = field.GetTextPos();
  366. VECTOR2I delta = old_pos - pos;
  367. pos.x = GetPosition().x + delta.x;
  368. field.SetPosition( pos );
  369. }
  370. }
  371. void SCH_LABEL_BASE::MirrorVertically( int aCenter )
  372. {
  373. VECTOR2I old_pos = GetPosition();
  374. SCH_TEXT::MirrorVertically( aCenter );
  375. for( SCH_FIELD& field : m_fields )
  376. {
  377. if( field.GetTextAngle() == ANGLE_VERTICAL )
  378. field.FlipHJustify();
  379. VECTOR2I pos = field.GetTextPos();
  380. VECTOR2I delta = old_pos - pos;
  381. pos.y = GetPosition().y + delta.y;
  382. field.SetPosition( pos );
  383. }
  384. }
  385. bool SCH_LABEL_BASE::IncrementLabel( int aIncrement )
  386. {
  387. wxString text = GetText();
  388. if( IncrementString( text, aIncrement ) )
  389. {
  390. SetText( text );
  391. return true;
  392. }
  393. return false;
  394. }
  395. bool SCH_LABEL_BASE::operator==( const SCH_ITEM& aOther ) const
  396. {
  397. const SCH_LABEL_BASE* other = dynamic_cast<const SCH_LABEL_BASE*>( &aOther );
  398. if( !other )
  399. return false;
  400. if( m_shape != other->m_shape )
  401. return false;
  402. if( m_connectionType != other->m_connectionType )
  403. return false;
  404. if( m_fields.size() != other->m_fields.size() )
  405. return false;
  406. for( size_t ii = 0; ii < m_fields.size(); ++ii )
  407. {
  408. if( !( m_fields[ii] == other->m_fields[ii] ) )
  409. return false;
  410. }
  411. return SCH_TEXT::operator==( aOther );
  412. }
  413. double SCH_LABEL_BASE::Similarity( const SCH_ITEM& aOther ) const
  414. {
  415. const SCH_LABEL_BASE* other = dynamic_cast<const SCH_LABEL_BASE*>( &aOther );
  416. if( !other )
  417. return 0.0;
  418. if( m_Uuid == other->m_Uuid )
  419. return 1.0;
  420. double similarity = SCH_TEXT::Similarity( aOther );
  421. if( typeid( *this ) != typeid( aOther ) )
  422. similarity *= 0.9;
  423. if( m_shape == other->m_shape )
  424. similarity *= 0.9;
  425. if( m_connectionType == other->m_connectionType )
  426. similarity *= 0.9;
  427. for( size_t ii = 0; ii < m_fields.size(); ++ii )
  428. {
  429. if( ii >= other->m_fields.size() )
  430. break;
  431. similarity *= m_fields[ii].Similarity( other->m_fields[ii] );
  432. }
  433. int diff = std::abs( int( m_fields.size() ) - int( other->m_fields.size() ) );
  434. similarity *= std::pow( 0.9, diff );
  435. return similarity;
  436. }
  437. void SCH_LABEL_BASE::AutoplaceFields( SCH_SCREEN* aScreen, AUTOPLACE_ALGO aAlgo )
  438. {
  439. int margin = GetTextOffset() * 2;
  440. int labelLen = GetBodyBoundingBox().GetSizeMax();
  441. int accumulated = GetTextHeight() / 2;
  442. if( Type() == SCH_GLOBAL_LABEL_T )
  443. accumulated += margin + GetPenWidth() + margin;
  444. for( SCH_FIELD& field : m_fields )
  445. {
  446. VECTOR2I offset( 0, 0 );
  447. switch( GetSpinStyle() )
  448. {
  449. default:
  450. case SPIN_STYLE::LEFT:
  451. field.SetTextAngle( ANGLE_HORIZONTAL );
  452. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  453. if( field.GetId() == FIELD_T::INTERSHEET_REFS )
  454. offset.x = - ( labelLen + margin );
  455. else
  456. offset.y = accumulated + field.GetTextHeight() / 2;
  457. break;
  458. case SPIN_STYLE::UP:
  459. field.SetTextAngle( ANGLE_VERTICAL );
  460. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  461. if( field.GetId() == FIELD_T::INTERSHEET_REFS )
  462. offset.y = - ( labelLen + margin );
  463. else
  464. offset.x = accumulated + field.GetTextHeight() / 2;
  465. break;
  466. case SPIN_STYLE::RIGHT:
  467. field.SetTextAngle( ANGLE_HORIZONTAL );
  468. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  469. if( field.GetId() == FIELD_T::INTERSHEET_REFS )
  470. offset.x = labelLen + margin;
  471. else
  472. offset.y = accumulated + field.GetTextHeight() / 2;
  473. break;
  474. case SPIN_STYLE::BOTTOM:
  475. field.SetTextAngle( ANGLE_VERTICAL );
  476. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  477. if( field.GetId() == FIELD_T::INTERSHEET_REFS )
  478. offset.y = labelLen + margin;
  479. else
  480. offset.x = accumulated + field.GetTextHeight() / 2;
  481. break;
  482. }
  483. field.SetTextPos( GetTextPos() + offset );
  484. if( field.GetId() == FIELD_T::INTERSHEET_REFS )
  485. accumulated += field.GetTextHeight() + margin;
  486. }
  487. if( aAlgo == AUTOPLACE_AUTO || aAlgo == AUTOPLACE_MANUAL )
  488. m_fieldsAutoplaced = aAlgo;
  489. }
  490. void SCH_LABEL_BASE::GetIntersheetRefs( const SCH_SHEET_PATH* aPath,
  491. std::vector<std::pair<wxString, wxString>>* pages )
  492. {
  493. wxCHECK( pages, /* void */ );
  494. if( Schematic() )
  495. {
  496. wxString resolvedLabel = GetShownText( &Schematic()->CurrentSheet(), false );
  497. auto it = Schematic()->GetPageRefsMap().find( resolvedLabel );
  498. if( it != Schematic()->GetPageRefsMap().end() )
  499. {
  500. std::vector<int> pageListCopy;
  501. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  502. if( !Schematic()->Settings().m_IntersheetRefsListOwnPage )
  503. {
  504. int currentPage = Schematic()->CurrentSheet().GetVirtualPageNumber();
  505. alg::delete_matching( pageListCopy, currentPage );
  506. if( pageListCopy.empty() )
  507. return;
  508. }
  509. std::sort( pageListCopy.begin(), pageListCopy.end() );
  510. std::map<int, wxString> sheetPages = Schematic()->GetVirtualPageToSheetPagesMap();
  511. std::map<int, wxString> sheetNames = Schematic()->GetVirtualPageToSheetNamesMap();
  512. for( int pageNum : pageListCopy )
  513. pages->push_back( { sheetPages[ pageNum ], sheetNames[ pageNum ] } );
  514. }
  515. }
  516. }
  517. void SCH_LABEL_BASE::GetContextualTextVars( wxArrayString* aVars ) const
  518. {
  519. for( const SCH_FIELD& field : m_fields )
  520. {
  521. if( field.IsMandatory() )
  522. aVars->push_back( field.GetCanonicalName().Upper() );
  523. else
  524. aVars->push_back( field.GetName() );
  525. }
  526. aVars->push_back( wxT( "OP" ) );
  527. aVars->push_back( wxT( "CONNECTION_TYPE" ) );
  528. aVars->push_back( wxT( "SHORT_NET_NAME" ) );
  529. aVars->push_back( wxT( "NET_NAME" ) );
  530. aVars->push_back( wxT( "NET_CLASS" ) );
  531. }
  532. bool SCH_LABEL_BASE::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token,
  533. int aDepth ) const
  534. {
  535. static wxRegEx operatingPoint( wxT( "^"
  536. "OP"
  537. "(.([0-9])?([a-zA-Z]*))?"
  538. "$" ) );
  539. wxCHECK( aPath, false );
  540. SCHEMATIC* schematic = Schematic();
  541. if( !schematic )
  542. return false;
  543. if( operatingPoint.Matches( *token ) )
  544. {
  545. int precision = 3;
  546. wxString precisionStr( operatingPoint.GetMatch( *token, 2 ) );
  547. wxString range( operatingPoint.GetMatch( *token, 3 ) );
  548. if( !precisionStr.IsEmpty() )
  549. precision = precisionStr[0] - '0';
  550. if( range.IsEmpty() )
  551. range = wxS( "~V" );
  552. const SCH_CONNECTION* connection = Connection();
  553. *token = wxS( "?" );
  554. if( connection )
  555. *token = schematic->GetOperatingPoint( connection->Name( false ), precision, range );
  556. return true;
  557. }
  558. if( token->Contains( ':' ) )
  559. {
  560. if( schematic->ResolveCrossReference( token, aDepth + 1 ) )
  561. return true;
  562. }
  563. if( ( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
  564. && token->IsSameAs( wxT( "CONNECTION_TYPE" ) ) )
  565. {
  566. const SCH_LABEL_BASE* label = static_cast<const SCH_LABEL_BASE*>( this );
  567. *token = getElectricalTypeLabel( label->GetShape() );
  568. return true;
  569. }
  570. else if( token->IsSameAs( wxT( "SHORT_NET_NAME" ) ) )
  571. {
  572. const SCH_CONNECTION* connection = Connection();
  573. *token = wxEmptyString;
  574. if( connection )
  575. *token = connection->LocalName();
  576. return true;
  577. }
  578. else if( token->IsSameAs( wxT( "NET_NAME" ) ) )
  579. {
  580. const SCH_CONNECTION* connection = Connection();
  581. *token = wxEmptyString;
  582. if( connection )
  583. *token = connection->Name();
  584. return true;
  585. }
  586. else if( token->IsSameAs( wxT( "NET_CLASS" ) ) )
  587. {
  588. const SCH_CONNECTION* connection = Connection();
  589. *token = wxEmptyString;
  590. if( connection )
  591. *token = GetEffectiveNetClass()->GetName();
  592. return true;
  593. }
  594. for( const SCH_FIELD& field : m_fields)
  595. {
  596. if( token->IsSameAs( field.GetName() ) )
  597. {
  598. *token = field.GetShownText( false, aDepth + 1 );
  599. return true;
  600. }
  601. }
  602. // See if parent can resolve it (these will recurse to ancestors)
  603. if( Type() == SCH_SHEET_PIN_T && m_parent )
  604. {
  605. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
  606. SCH_SHEET_PATH path = *aPath;
  607. path.push_back( sheet );
  608. if( sheet->ResolveTextVar( &path, token, aDepth + 1 ) )
  609. return true;
  610. }
  611. else
  612. {
  613. if( aPath->Last()->ResolveTextVar( aPath, token, aDepth + 1 ) )
  614. return true;
  615. }
  616. return false;
  617. }
  618. bool SCH_LABEL_BASE::HasCachedDriverName() const
  619. {
  620. return !HasTextVars();
  621. }
  622. const wxString& SCH_LABEL_BASE::GetCachedDriverName() const
  623. {
  624. return m_cached_driver_name;
  625. }
  626. void SCH_LABEL_BASE::cacheShownText()
  627. {
  628. EDA_TEXT::cacheShownText();
  629. if( !HasTextVars() )
  630. m_cached_driver_name = EscapeString( EDA_TEXT::GetShownText( true, 0 ), CTX_NETNAME );
  631. }
  632. wxString SCH_LABEL_BASE::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText,
  633. int aDepth ) const
  634. {
  635. std::function<bool( wxString* )> textResolver =
  636. [&]( wxString* token ) -> bool
  637. {
  638. return ResolveTextVar( aPath, token, aDepth + 1 );
  639. };
  640. wxString text = EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
  641. if( HasTextVars() )
  642. {
  643. if( aDepth < ADVANCED_CFG::GetCfg().m_ResolveTextRecursionDepth )
  644. text = ExpandTextVars( text, &textResolver );
  645. }
  646. return text;
  647. }
  648. void SCH_LABEL_BASE::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
  649. {
  650. for( SCH_FIELD& field : m_fields )
  651. aFunction( &field );
  652. }
  653. bool SCH_LABEL_BASE::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  654. {
  655. if( SCH_ITEM::Matches( UnescapeString( GetText() ), aSearchData ) )
  656. {
  657. return true;
  658. }
  659. const SCH_SEARCH_DATA* searchData = dynamic_cast<const SCH_SEARCH_DATA*>( &aSearchData );
  660. SCH_CONNECTION* connection = nullptr;
  661. SCH_SHEET_PATH* sheetPath = reinterpret_cast<SCH_SHEET_PATH*>( aAuxData );
  662. if( searchData && searchData->searchNetNames && sheetPath
  663. && ( connection = Connection( sheetPath ) ) )
  664. {
  665. if( connection->IsBus() )
  666. {
  667. auto allMembers = connection->AllMembers();
  668. std::set<wxString> netNames;
  669. for( std::shared_ptr<SCH_CONNECTION> member : allMembers )
  670. netNames.insert( member->GetNetName() );
  671. for( const wxString& netName : netNames )
  672. {
  673. if( EDA_ITEM::Matches( netName, aSearchData ) )
  674. return true;
  675. }
  676. return false;
  677. }
  678. wxString netName = connection->GetNetName();
  679. if( EDA_ITEM::Matches( netName, aSearchData ) )
  680. return true;
  681. }
  682. return false;
  683. }
  684. bool SCH_LABEL_BASE::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  685. {
  686. EDA_SEARCH_DATA localSearchData( aSearchData );
  687. localSearchData.findString = EscapeString( aSearchData.findString, CTX_NETNAME );
  688. localSearchData.replaceString = EscapeString( aSearchData.replaceString, CTX_NETNAME );
  689. return EDA_TEXT::Replace( localSearchData );
  690. }
  691. INSPECT_RESULT SCH_LABEL_BASE::Visit( INSPECTOR aInspector, void* testData,
  692. const std::vector<KICAD_T>& aScanTypes )
  693. {
  694. if( IsType( aScanTypes ) )
  695. {
  696. if( INSPECT_RESULT::QUIT == aInspector( this, nullptr ) )
  697. return INSPECT_RESULT::QUIT;
  698. }
  699. for( KICAD_T scanType : aScanTypes )
  700. {
  701. if( scanType == SCH_LOCATE_ANY_T || scanType == SCH_FIELD_T )
  702. {
  703. for( SCH_FIELD& field : m_fields )
  704. {
  705. if( INSPECT_RESULT::QUIT == aInspector( &field, this ) )
  706. return INSPECT_RESULT::QUIT;
  707. }
  708. }
  709. }
  710. return INSPECT_RESULT::CONTINUE;
  711. }
  712. void SCH_LABEL_BASE::GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList )
  713. {
  714. DANGLING_END_ITEM item( LABEL_END, this, GetTextPos() );
  715. aItemList.push_back( item );
  716. }
  717. std::vector<VECTOR2I> SCH_LABEL_BASE::GetConnectionPoints() const
  718. {
  719. return { GetTextPos() };
  720. }
  721. std::vector<int> SCH_LABEL_BASE::ViewGetLayers() const
  722. {
  723. return { LAYER_DANGLING,
  724. LAYER_DEVICE,
  725. LAYER_NETCLASS_REFS,
  726. LAYER_FIELDS,
  727. LAYER_SELECTION_SHADOWS };
  728. }
  729. int SCH_LABEL_BASE::GetLabelBoxExpansion( const RENDER_SETTINGS* aSettings ) const
  730. {
  731. double ratio;
  732. if( aSettings )
  733. ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_LabelSizeRatio;
  734. else if( Schematic() )
  735. ratio = Schematic()->Settings().m_LabelSizeRatio;
  736. else
  737. ratio = DEFAULT_LABEL_SIZE_RATIO; // For previews (such as in Preferences), etc.
  738. return KiROUND( ratio * GetTextSize().y );
  739. }
  740. const BOX2I SCH_LABEL_BASE::GetBodyBoundingBox() const
  741. {
  742. // build the bounding box of the label only, without taking into account its fields
  743. BOX2I box;
  744. std::vector<VECTOR2I> pts;
  745. CreateGraphicShape( nullptr, pts, GetTextPos() );
  746. for( const VECTOR2I& pt : pts )
  747. box.Merge( pt );
  748. box.Inflate( GetEffectiveTextPenWidth() / 2 );
  749. box.Normalize();
  750. return box;
  751. }
  752. const BOX2I SCH_LABEL_BASE::GetBoundingBox() const
  753. {
  754. // build the bounding box of the entire label, including its fields
  755. BOX2I box = GetBodyBoundingBox();
  756. for( const SCH_FIELD& field : m_fields )
  757. {
  758. if( field.IsVisible() && field.GetText() != wxEmptyString )
  759. {
  760. BOX2I fieldBBox = field.GetBoundingBox();
  761. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  762. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  763. box.Merge( fieldBBox );
  764. }
  765. }
  766. box.Normalize();
  767. return box;
  768. }
  769. bool SCH_LABEL_BASE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  770. {
  771. BOX2I bbox = GetBodyBoundingBox();
  772. bbox.Inflate( aAccuracy );
  773. if( bbox.Contains( aPosition ) )
  774. return true;
  775. for( const SCH_FIELD& field : m_fields )
  776. {
  777. if( field.IsVisible() )
  778. {
  779. BOX2I fieldBBox = field.GetBoundingBox();
  780. fieldBBox.Inflate( aAccuracy );
  781. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  782. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  783. if( fieldBBox.Contains( aPosition ) )
  784. return true;
  785. }
  786. }
  787. return false;
  788. }
  789. bool SCH_LABEL_BASE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  790. {
  791. BOX2I rect = aRect;
  792. rect.Inflate( aAccuracy );
  793. if( aContained )
  794. {
  795. return rect.Contains( GetBoundingBox() );
  796. }
  797. else
  798. {
  799. if( rect.Intersects( GetBodyBoundingBox() ) )
  800. return true;
  801. for( const SCH_FIELD& field : m_fields )
  802. {
  803. if( field.IsVisible() )
  804. {
  805. BOX2I fieldBBox = field.GetBoundingBox();
  806. if( Type() == SCH_LABEL_T || Type() == SCH_GLOBAL_LABEL_T )
  807. fieldBBox.Offset( GetSchematicTextOffset( nullptr ) );
  808. if( rect.Intersects( fieldBBox ) )
  809. return true;
  810. }
  811. }
  812. return false;
  813. }
  814. }
  815. bool SCH_LABEL_BASE::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemListByType,
  816. std::vector<DANGLING_END_ITEM>& aItemListByPos,
  817. const SCH_SHEET_PATH* aPath )
  818. {
  819. bool previousState = m_isDangling;
  820. VECTOR2I text_pos = GetTextPos();
  821. m_isDangling = true;
  822. m_connectionType = CONNECTION_TYPE::NONE;
  823. for( auto it = DANGLING_END_ITEM_HELPER::get_lower_pos( aItemListByPos, text_pos );
  824. it < aItemListByPos.end() && it->GetPosition() == text_pos; it++ )
  825. {
  826. DANGLING_END_ITEM& item = *it;
  827. if( item.GetItem() == this )
  828. continue;
  829. switch( item.GetType() )
  830. {
  831. case PIN_END:
  832. case LABEL_END:
  833. case SHEET_LABEL_END:
  834. case NO_CONNECT_END:
  835. if( text_pos == item.GetPosition() )
  836. {
  837. m_isDangling = false;
  838. if( aPath && item.GetType() != PIN_END )
  839. AddConnectionTo( *aPath, static_cast<SCH_ITEM*>( item.GetItem() ) );
  840. }
  841. break;
  842. default: break;
  843. }
  844. if( !m_isDangling )
  845. break;
  846. }
  847. if( m_isDangling )
  848. {
  849. for( auto it = DANGLING_END_ITEM_HELPER::get_lower_type( aItemListByType, BUS_END );
  850. it < aItemListByType.end() && it->GetType() == BUS_END; it++ )
  851. {
  852. DANGLING_END_ITEM& item = *it;
  853. DANGLING_END_ITEM& nextItem = *( ++it );
  854. int accuracy = 1; // We have rounding issues with an accuracy of 0
  855. m_isDangling = !TestSegmentHit( text_pos, item.GetPosition(), nextItem.GetPosition(),
  856. accuracy );
  857. if( m_isDangling )
  858. continue;
  859. m_connectionType = CONNECTION_TYPE::BUS;
  860. // Add the line to the connected items, since it won't be picked
  861. // up by a search of intersecting connection points
  862. if( aPath )
  863. {
  864. auto sch_item = static_cast<SCH_ITEM*>( item.GetItem() );
  865. AddConnectionTo( *aPath, sch_item );
  866. sch_item->AddConnectionTo( *aPath, this );
  867. }
  868. break;
  869. }
  870. if( m_isDangling )
  871. {
  872. for( auto it = DANGLING_END_ITEM_HELPER::get_lower_type( aItemListByType, WIRE_END );
  873. it < aItemListByType.end() && it->GetType() == WIRE_END; it++ )
  874. {
  875. DANGLING_END_ITEM& item = *it;
  876. DANGLING_END_ITEM& nextItem = *( ++it );
  877. int accuracy = 1; // We have rounding issues with an accuracy of 0
  878. m_isDangling = !TestSegmentHit( text_pos, item.GetPosition(),
  879. nextItem.GetPosition(), accuracy );
  880. if( m_isDangling )
  881. continue;
  882. m_connectionType = CONNECTION_TYPE::NET;
  883. // Add the line to the connected items, since it won't be picked
  884. // up by a search of intersecting connection points
  885. if( aPath )
  886. {
  887. auto sch_item = static_cast<SCH_ITEM*>( item.GetItem() );
  888. AddConnectionTo( *aPath, sch_item );
  889. sch_item->AddConnectionTo( *aPath, this );
  890. }
  891. break;
  892. }
  893. }
  894. }
  895. if( m_isDangling )
  896. m_connectionType = CONNECTION_TYPE::NONE;
  897. return previousState != m_isDangling;
  898. }
  899. bool SCH_LABEL_BASE::HasConnectivityChanges( const SCH_ITEM* aItem,
  900. const SCH_SHEET_PATH* aInstance ) const
  901. {
  902. // Do not compare to ourself.
  903. if( aItem == this || !IsConnectable() )
  904. return false;
  905. const SCH_LABEL_BASE* label = dynamic_cast<const SCH_LABEL_BASE*>( aItem );
  906. // Don't compare against a different SCH_ITEM.
  907. wxCHECK( label, false );
  908. if( GetPosition() != label->GetPosition() )
  909. return true;
  910. if( GetShownText( aInstance ) != label->GetShownText( aInstance ) )
  911. return true;
  912. std::vector<wxString> netclasses;
  913. std::vector<wxString> otherNetclasses;
  914. for( const SCH_FIELD& field : m_fields )
  915. {
  916. if( field.GetCanonicalName() == wxT( "Netclass" ) )
  917. netclasses.push_back( field.GetText() );
  918. }
  919. for( const SCH_FIELD& field : label->m_fields )
  920. {
  921. if( field.GetCanonicalName() == wxT( "Netclass" ) )
  922. otherNetclasses.push_back( field.GetText() );
  923. }
  924. return netclasses != otherNetclasses;
  925. }
  926. void SCH_LABEL_BASE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  927. {
  928. wxString msg;
  929. switch( Type() )
  930. {
  931. case SCH_LABEL_T: msg = _( "Label" ); break;
  932. case SCH_DIRECTIVE_LABEL_T: msg = _( "Directive Label" ); break;
  933. case SCH_GLOBAL_LABEL_T: msg = _( "Global Label" ); break;
  934. case SCH_HIER_LABEL_T: msg = _( "Hierarchical Label" ); break;
  935. case SCH_SHEET_PIN_T: msg = _( "Hierarchical Sheet Pin" ); break;
  936. default: return;
  937. }
  938. // Don't use GetShownText() here; we want to show the user the variable references
  939. aList.emplace_back( msg, UnescapeString( GetText() ) );
  940. // Display electrical type if it is relevant
  941. if( Type() == SCH_GLOBAL_LABEL_T || Type() == SCH_HIER_LABEL_T || Type() == SCH_SHEET_PIN_T )
  942. aList.emplace_back( _( "Type" ), getElectricalTypeLabel( GetShape() ) );
  943. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  944. wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
  945. int style = IsBold() && IsItalic() ? 3 : IsBold() ? 2 : IsItalic() ? 1 : 0;
  946. aList.emplace_back( _( "Style" ), textStyle[style] );
  947. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  948. switch( GetSpinStyle() )
  949. {
  950. case SPIN_STYLE::LEFT: msg = _( "Align right" ); break;
  951. case SPIN_STYLE::UP: msg = _( "Align bottom" ); break;
  952. case SPIN_STYLE::RIGHT: msg = _( "Align left" ); break;
  953. case SPIN_STYLE::BOTTOM: msg = _( "Align top" ); break;
  954. default: msg = wxT( "???" ); break;
  955. }
  956. aList.emplace_back( _( "Justification" ), msg );
  957. SCH_CONNECTION* conn = nullptr;
  958. if( !IsConnectivityDirty() && dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  959. conn = Connection();
  960. if( conn )
  961. {
  962. conn->AppendInfoToMsgPanel( aList );
  963. if( !conn->IsBus() )
  964. {
  965. aList.emplace_back( _( "Resolved Netclass" ),
  966. UnescapeString( GetEffectiveNetClass()->GetHumanReadableName() ) );
  967. }
  968. }
  969. }
  970. void SCH_LABEL_BASE::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
  971. int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed )
  972. {
  973. static std::vector<VECTOR2I> s_poly;
  974. SCH_SHEET_PATH* sheet = &Schematic()->CurrentSheet();
  975. RENDER_SETTINGS* settings = aPlotter->RenderSettings();
  976. SCH_CONNECTION* connection = Connection();
  977. int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
  978. COLOR4D color = settings->GetLayerColor( layer );
  979. int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
  980. COLOR4D labelColor = GetLabelColor();
  981. if( aPlotter->GetColorMode() && labelColor != COLOR4D::UNSPECIFIED )
  982. color = labelColor;
  983. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  984. aPlotter->SetCurrentLineWidth( penWidth );
  985. KIFONT::FONT* font = GetFont();
  986. if( !font )
  987. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  988. VECTOR2I textpos = GetTextPos() + GetSchematicTextOffset( aPlotter->RenderSettings() );
  989. CreateGraphicShape( aPlotter->RenderSettings(), s_poly, GetTextPos() );
  990. TEXT_ATTRIBUTES attrs = GetAttributes();
  991. attrs.m_StrokeWidth = penWidth;
  992. attrs.m_Multiline = false;
  993. if( aBackground )
  994. {
  995. // No filled shapes (yet)
  996. }
  997. else
  998. {
  999. aPlotter->PlotText( textpos, color, GetShownText( sheet, true ), attrs, font,
  1000. GetFontMetrics() );
  1001. if( GetShape() == LABEL_FLAG_SHAPE::F_DOT )
  1002. {
  1003. aPlotter->MoveTo( s_poly[0] );
  1004. aPlotter->LineTo( s_poly[1] );
  1005. aPlotter->PenFinish();
  1006. int diameter = ( s_poly[2] - s_poly[1] ).EuclideanNorm() * 2;
  1007. aPlotter->FilledCircle( s_poly[2], diameter , FILLED, nullptr );
  1008. }
  1009. else if( GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
  1010. {
  1011. aPlotter->MoveTo( s_poly[0] );
  1012. aPlotter->LineTo( s_poly[1] );
  1013. aPlotter->PenFinish();
  1014. int diameter = ( s_poly[2] - s_poly[1] ).EuclideanNorm() * 2;
  1015. aPlotter->ThickCircle( s_poly[2], diameter, penWidth, FILLED, nullptr );
  1016. }
  1017. else
  1018. {
  1019. if( !s_poly.empty() )
  1020. aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
  1021. }
  1022. // Make sheet pins and hierarchical labels clickable hyperlinks
  1023. bool linkAlreadyPlotted = false;
  1024. if( aPlotOpts.m_PDFHierarchicalLinks )
  1025. {
  1026. if( Type() == SCH_HIER_LABEL_T )
  1027. {
  1028. if( sheet->size() >= 2 )
  1029. {
  1030. SCH_SHEET_PATH path = *sheet;
  1031. path.pop_back();
  1032. aPlotter->HyperlinkBox( GetBodyBoundingBox(),
  1033. EDA_TEXT::GotoPageHref( path.GetPageNumber() ) );
  1034. linkAlreadyPlotted = true;
  1035. }
  1036. }
  1037. else if( Type() == SCH_SHEET_PIN_T )
  1038. {
  1039. SCH_SHEET_PATH path = *sheet;
  1040. SCH_SHEET* parent = static_cast<SCH_SHEET*>( m_parent );
  1041. path.push_back( parent );
  1042. aPlotter->HyperlinkBox( GetBodyBoundingBox(),
  1043. EDA_TEXT::GotoPageHref( path.GetPageNumber() ) );
  1044. linkAlreadyPlotted = true;
  1045. }
  1046. }
  1047. // Plot attributes to a hypertext menu
  1048. if( aPlotOpts.m_PDFPropertyPopups && !linkAlreadyPlotted )
  1049. {
  1050. std::vector<wxString> properties;
  1051. if( connection )
  1052. {
  1053. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
  1054. _( "Net" ),
  1055. connection->Name() ) );
  1056. properties.emplace_back(
  1057. wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
  1058. GetEffectiveNetClass()->GetHumanReadableName() ) );
  1059. }
  1060. for( const SCH_FIELD& field : GetFields() )
  1061. {
  1062. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
  1063. field.GetName(),
  1064. field.GetShownText( false ) ) );
  1065. }
  1066. if( !properties.empty() )
  1067. aPlotter->HyperlinkMenu( GetBodyBoundingBox(), properties );
  1068. }
  1069. if( Type() == SCH_HIER_LABEL_T )
  1070. {
  1071. aPlotter->Bookmark( GetBodyBoundingBox(), GetShownText( false ),
  1072. _( "Hierarchical Labels" ) );
  1073. }
  1074. }
  1075. for( SCH_FIELD& field : m_fields )
  1076. field.Plot( aPlotter, aBackground, aPlotOpts, aUnit, aBodyStyle, aOffset, aDimmed );
  1077. }
  1078. bool SCH_LABEL_BASE::AutoRotateOnPlacement() const
  1079. {
  1080. return m_autoRotateOnPlacement;
  1081. }
  1082. void SCH_LABEL_BASE::SetAutoRotateOnPlacement( bool autoRotate )
  1083. {
  1084. m_autoRotateOnPlacement = autoRotate;
  1085. }
  1086. SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) :
  1087. SCH_LABEL_BASE( pos, text, SCH_LABEL_T )
  1088. {
  1089. m_layer = LAYER_LOCLABEL;
  1090. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1091. m_isDangling = true;
  1092. }
  1093. void SCH_LABEL::Serialize( google::protobuf::Any &aContainer ) const
  1094. {
  1095. kiapi::schematic::types::LocalLabel label;
  1096. label.mutable_id()->set_value( m_Uuid.AsStdString() );
  1097. kiapi::common::PackVector2( *label.mutable_position(), GetPosition() );
  1098. aContainer.PackFrom( label );
  1099. }
  1100. bool SCH_LABEL::Deserialize( const google::protobuf::Any &aContainer )
  1101. {
  1102. kiapi::schematic::types::LocalLabel label;
  1103. if( !aContainer.UnpackTo( &label ) )
  1104. return false;
  1105. const_cast<KIID&>( m_Uuid ) = KIID( label.id().value() );
  1106. SetPosition( kiapi::common::UnpackVector2( label.position() ) );
  1107. return true;
  1108. }
  1109. const BOX2I SCH_LABEL::GetBodyBoundingBox() const
  1110. {
  1111. BOX2I rect = GetTextBox();
  1112. rect.Offset( 0, -GetTextOffset() );
  1113. rect.Inflate( GetEffectiveTextPenWidth() );
  1114. if( !GetTextAngle().IsZero() )
  1115. {
  1116. // Rotate rect
  1117. VECTOR2I pos = rect.GetOrigin();
  1118. VECTOR2I end = rect.GetEnd();
  1119. RotatePoint( pos, GetTextPos(), GetTextAngle() );
  1120. RotatePoint( end, GetTextPos(), GetTextAngle() );
  1121. rect.SetOrigin( pos );
  1122. rect.SetEnd( end );
  1123. rect.Normalize();
  1124. }
  1125. // Labels have a position point that is outside of the TextBox
  1126. rect.Merge( GetPosition() );
  1127. return rect;
  1128. }
  1129. wxString SCH_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1130. {
  1131. return wxString::Format( _( "Label '%s'" ),
  1132. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1133. }
  1134. BITMAPS SCH_LABEL::GetMenuImage() const
  1135. {
  1136. return BITMAPS::add_line_label;
  1137. }
  1138. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const VECTOR2I& pos ) :
  1139. SCH_LABEL_BASE( pos, wxEmptyString, SCH_DIRECTIVE_LABEL_T )
  1140. {
  1141. m_layer = LAYER_NETCLASS_REFS;
  1142. m_shape = LABEL_FLAG_SHAPE::F_ROUND;
  1143. m_pinLength = schIUScale.MilsToIU( 100 );
  1144. m_symbolSize = schIUScale.MilsToIU( 20 );
  1145. m_isDangling = true;
  1146. }
  1147. void SCH_DIRECTIVE_LABEL::SwapData( SCH_ITEM* aItem )
  1148. {
  1149. SCH_LABEL_BASE::SwapData( aItem );
  1150. SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( aItem );
  1151. std::swap( m_pinLength, label->m_pinLength );
  1152. std::swap( m_symbolSize, label->m_symbolSize );
  1153. }
  1154. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel ) :
  1155. SCH_LABEL_BASE( aClassLabel )
  1156. {
  1157. m_pinLength = aClassLabel.m_pinLength;
  1158. m_symbolSize = aClassLabel.m_symbolSize;
  1159. }
  1160. void SCH_DIRECTIVE_LABEL::Serialize( google::protobuf::Any &aContainer ) const
  1161. {
  1162. // TODO
  1163. }
  1164. bool SCH_DIRECTIVE_LABEL::Deserialize( const google::protobuf::Any &aContainer )
  1165. {
  1166. // TODO
  1167. return false;
  1168. }
  1169. int SCH_DIRECTIVE_LABEL::GetPenWidth() const
  1170. {
  1171. int pen = 0;
  1172. if( Schematic() )
  1173. pen = Schematic()->Settings().m_DefaultLineWidth;
  1174. return GetEffectiveTextPenWidth( pen );
  1175. }
  1176. void SCH_DIRECTIVE_LABEL::MirrorSpinStyle( bool aLeftRight )
  1177. {
  1178. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1179. // vertical shape (like a text reduced to only "I" letter).
  1180. // So the mirroring is not exactly similar to a SCH_TEXT item
  1181. SCH_TEXT::MirrorSpinStyle( !aLeftRight );
  1182. for( SCH_FIELD& field : m_fields )
  1183. {
  1184. if( ( aLeftRight && field.GetTextAngle().IsHorizontal() )
  1185. || ( !aLeftRight && field.GetTextAngle().IsVertical() ) )
  1186. {
  1187. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1188. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1189. else
  1190. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1191. }
  1192. VECTOR2I pos = field.GetTextPos();
  1193. VECTOR2I delta = (VECTOR2I)GetPosition() - pos;
  1194. if( aLeftRight )
  1195. pos.x = GetPosition().x + delta.x;
  1196. else
  1197. pos.y = GetPosition().y + delta.y;
  1198. field.SetTextPos( pos );
  1199. }
  1200. }
  1201. void SCH_DIRECTIVE_LABEL::MirrorHorizontally( int aCenter )
  1202. {
  1203. VECTOR2I old_pos = GetPosition();
  1204. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1205. // vertical shape (like a text reduced to only "I" letter).
  1206. // So the mirroring is not exactly similar to a SCH_TEXT item
  1207. // Text is NOT really mirrored; it is moved to a suitable horizontal position
  1208. SetSpinStyle( GetSpinStyle().MirrorX() );
  1209. SetTextX( MIRRORVAL( GetTextPos().x, aCenter ) );
  1210. for( SCH_FIELD& field : m_fields )
  1211. {
  1212. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1213. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1214. else if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1215. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1216. VECTOR2I pos = field.GetTextPos();
  1217. VECTOR2I delta = old_pos - pos;
  1218. pos.x = GetPosition().x + delta.x;
  1219. field.SetPosition( pos );
  1220. }
  1221. }
  1222. void SCH_DIRECTIVE_LABEL::MirrorVertically( int aCenter )
  1223. {
  1224. VECTOR2I old_pos = GetPosition();
  1225. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1226. // vertical shape (like a text reduced to only "I" letter).
  1227. // So the mirroring is not exactly similar to a SCH_TEXT item
  1228. // Text is NOT really mirrored; it is moved to a suitable vertical position
  1229. SetSpinStyle( GetSpinStyle().MirrorY() );
  1230. SetTextY( MIRRORVAL( GetTextPos().y, aCenter ) );
  1231. for( SCH_FIELD& field : m_fields )
  1232. {
  1233. VECTOR2I pos = field.GetTextPos();
  1234. VECTOR2I delta = old_pos - pos;
  1235. pos.y = GetPosition().y + delta.y;
  1236. field.SetPosition( pos );
  1237. }
  1238. }
  1239. void SCH_DIRECTIVE_LABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1240. std::vector<VECTOR2I>& aPoints,
  1241. const VECTOR2I& aPos ) const
  1242. {
  1243. int symbolSize = m_symbolSize;
  1244. aPoints.clear();
  1245. switch( m_shape )
  1246. {
  1247. case LABEL_FLAG_SHAPE::F_DOT:
  1248. symbolSize = KiROUND( symbolSize * 0.7 );
  1249. KI_FALLTHROUGH;
  1250. case LABEL_FLAG_SHAPE::F_ROUND:
  1251. // First 3 points are used for generating shape
  1252. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1253. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1254. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  1255. // These points are just used to bulk out the bounding box
  1256. aPoints.emplace_back( VECTOR2I( -m_symbolSize, m_pinLength ) );
  1257. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  1258. aPoints.emplace_back( VECTOR2I( m_symbolSize, m_pinLength + symbolSize ) );
  1259. break;
  1260. case LABEL_FLAG_SHAPE::F_DIAMOND:
  1261. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1262. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1263. aPoints.emplace_back( VECTOR2I( -2 * m_symbolSize, m_pinLength ) );
  1264. aPoints.emplace_back( VECTOR2I( 0, m_pinLength + symbolSize ) );
  1265. aPoints.emplace_back( VECTOR2I( 2 * m_symbolSize, m_pinLength ) );
  1266. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1267. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1268. break;
  1269. case LABEL_FLAG_SHAPE::F_RECTANGLE:
  1270. symbolSize = KiROUND( symbolSize * 0.8 );
  1271. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1272. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1273. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength - symbolSize ) );
  1274. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength + symbolSize ) );
  1275. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength + symbolSize ) );
  1276. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength - symbolSize ) );
  1277. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1278. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1279. break;
  1280. default:
  1281. break;
  1282. }
  1283. // Rotate outlines and move corners to real position
  1284. for( VECTOR2I& aPoint : aPoints )
  1285. {
  1286. switch( GetSpinStyle() )
  1287. {
  1288. default:
  1289. case SPIN_STYLE::LEFT: break;
  1290. case SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1291. case SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1292. case SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1293. }
  1294. aPoint += aPos;
  1295. }
  1296. }
  1297. void SCH_DIRECTIVE_LABEL::AutoplaceFields( SCH_SCREEN* aScreen, AUTOPLACE_ALGO aAlgo )
  1298. {
  1299. int margin = GetTextOffset();
  1300. int symbolWidth = m_symbolSize;
  1301. int origin = m_pinLength;
  1302. if( m_shape == LABEL_FLAG_SHAPE::F_DIAMOND || m_shape == LABEL_FLAG_SHAPE::F_RECTANGLE )
  1303. symbolWidth *= 2;
  1304. if( IsItalic() )
  1305. margin = KiROUND( margin * 1.5 );
  1306. VECTOR2I offset;
  1307. for( SCH_FIELD& field : m_fields )
  1308. {
  1309. if( field.GetText() == wxEmptyString )
  1310. continue;
  1311. switch( GetSpinStyle() )
  1312. {
  1313. default:
  1314. case SPIN_STYLE::LEFT:
  1315. field.SetTextAngle( ANGLE_HORIZONTAL );
  1316. offset = { symbolWidth + margin, origin };
  1317. break;
  1318. case SPIN_STYLE::UP:
  1319. field.SetTextAngle( ANGLE_VERTICAL );
  1320. offset = { -origin, -( symbolWidth + margin ) };
  1321. break;
  1322. case SPIN_STYLE::RIGHT:
  1323. field.SetTextAngle( ANGLE_HORIZONTAL );
  1324. offset = { symbolWidth + margin, -origin };
  1325. break;
  1326. case SPIN_STYLE::BOTTOM:
  1327. field.SetTextAngle( ANGLE_VERTICAL );
  1328. offset = { origin, -( symbolWidth + margin ) };
  1329. break;
  1330. }
  1331. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1332. field.SetTextPos( GetPosition() + offset );
  1333. origin -= field.GetTextHeight() + margin;
  1334. }
  1335. if( aAlgo == AUTOPLACE_AUTO || aAlgo == AUTOPLACE_MANUAL )
  1336. m_fieldsAutoplaced = aAlgo;
  1337. }
  1338. wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1339. {
  1340. if( m_fields.empty() )
  1341. {
  1342. return _( "Directive Label" );
  1343. }
  1344. else
  1345. {
  1346. return wxString::Format( _( "Directive Label [%s %s]" ),
  1347. UnescapeString( m_fields[0].GetName() ),
  1348. aFull ? m_fields[0].GetShownText( false )
  1349. : KIUI::EllipsizeMenuText( m_fields[0].GetText() ) );
  1350. }
  1351. }
  1352. void SCH_DIRECTIVE_LABEL::AddConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
  1353. {
  1354. m_connected_rule_areas.insert( aRuleArea );
  1355. }
  1356. void SCH_DIRECTIVE_LABEL::ClearConnectedRuleAreas()
  1357. {
  1358. m_connected_rule_areas.clear();
  1359. }
  1360. void SCH_DIRECTIVE_LABEL::RemoveConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
  1361. {
  1362. m_connected_rule_areas.erase( aRuleArea );
  1363. }
  1364. bool SCH_DIRECTIVE_LABEL::IsDangling() const
  1365. {
  1366. return m_isDangling && m_connected_rule_areas.empty();
  1367. }
  1368. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const VECTOR2I& pos, const wxString& text ) :
  1369. SCH_LABEL_BASE( pos, text, SCH_GLOBAL_LABEL_T )
  1370. {
  1371. m_layer = LAYER_GLOBLABEL;
  1372. m_shape = LABEL_FLAG_SHAPE::L_BIDI;
  1373. m_isDangling = true;
  1374. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1375. m_fields.emplace_back( SCH_FIELD( pos, FIELD_T::INTERSHEET_REFS, this,
  1376. ::GetDefaultFieldName( FIELD_T::INTERSHEET_REFS, false ) ) );
  1377. m_fields.back().SetText( wxT( "${INTERSHEET_REFS}" ) );
  1378. m_fields.back().SetVisible( false );
  1379. m_fields.back().SetLayer( LAYER_INTERSHEET_REFS );
  1380. m_fields.back().SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1381. }
  1382. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
  1383. SCH_LABEL_BASE( aGlobalLabel )
  1384. {
  1385. }
  1386. void SCH_GLOBALLABEL::Serialize( google::protobuf::Any &aContainer ) const
  1387. {
  1388. // TODO
  1389. }
  1390. bool SCH_GLOBALLABEL::Deserialize( const google::protobuf::Any &aContainer )
  1391. {
  1392. // TODO
  1393. return false;
  1394. }
  1395. SCH_FIELD* SCH_GLOBALLABEL::GetField( FIELD_T aFieldType )
  1396. {
  1397. if( SCH_FIELD* field = FindField( m_fields, aFieldType ) )
  1398. return field;
  1399. m_fields.emplace_back( this, aFieldType );
  1400. return &m_fields.back();
  1401. }
  1402. const SCH_FIELD* SCH_GLOBALLABEL::GetField( FIELD_T aFieldType ) const
  1403. {
  1404. return FindField( m_fields, aFieldType );
  1405. }
  1406. VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1407. {
  1408. int horiz = GetLabelBoxExpansion( aSettings );
  1409. // Center the text on the center line of "E" instead of "R" to make room for an overbar
  1410. int vert = GetTextHeight() * 0.0715;
  1411. switch( m_shape )
  1412. {
  1413. case LABEL_FLAG_SHAPE::L_INPUT:
  1414. case LABEL_FLAG_SHAPE::L_BIDI:
  1415. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1416. horiz += GetTextHeight() * 3 / 4; // Use three-quarters-height as proxy for triangle size
  1417. break;
  1418. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1419. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1420. default:
  1421. break;
  1422. }
  1423. switch( GetSpinStyle() )
  1424. {
  1425. default:
  1426. case SPIN_STYLE::LEFT: return VECTOR2I( -horiz, vert );
  1427. case SPIN_STYLE::UP: return VECTOR2I( vert, -horiz );
  1428. case SPIN_STYLE::RIGHT: return VECTOR2I( horiz, vert );
  1429. case SPIN_STYLE::BOTTOM: return VECTOR2I( vert, horiz );
  1430. }
  1431. }
  1432. void SCH_GLOBALLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle )
  1433. {
  1434. SCH_LABEL_BASE::SetSpinStyle( aSpinStyle );
  1435. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1436. }
  1437. bool SCH_GLOBALLABEL::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token,
  1438. int aDepth ) const
  1439. {
  1440. wxCHECK( aPath, false );
  1441. SCHEMATIC* schematic = Schematic();
  1442. if( !schematic )
  1443. return false;
  1444. if( token->IsSameAs( wxT( "INTERSHEET_REFS" ) ) )
  1445. {
  1446. SCHEMATIC_SETTINGS& settings = schematic->Settings();
  1447. wxString ref;
  1448. auto it = schematic->GetPageRefsMap().find( GetShownText( aPath ) );
  1449. if( it == schematic->GetPageRefsMap().end() )
  1450. {
  1451. ref = "?";
  1452. }
  1453. else
  1454. {
  1455. std::vector<int> pageListCopy;
  1456. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  1457. std::sort( pageListCopy.begin(), pageListCopy.end() );
  1458. if( !settings.m_IntersheetRefsListOwnPage )
  1459. {
  1460. int currentPage = schematic->CurrentSheet().GetVirtualPageNumber();
  1461. alg::delete_matching( pageListCopy, currentPage );
  1462. }
  1463. std::map<int, wxString> sheetPages = schematic->GetVirtualPageToSheetPagesMap();
  1464. if( ( settings.m_IntersheetRefsFormatShort ) && ( pageListCopy.size() > 2 ) )
  1465. {
  1466. ref.Append( wxString::Format( wxT( "%s..%s" ),
  1467. sheetPages[pageListCopy.front()],
  1468. sheetPages[pageListCopy.back()] ) );
  1469. }
  1470. else
  1471. {
  1472. for( const int& pageNo : pageListCopy )
  1473. ref.Append( wxString::Format( wxT( "%s," ), sheetPages[pageNo] ) );
  1474. if( !ref.IsEmpty() && ref.Last() == ',' )
  1475. ref.RemoveLast();
  1476. }
  1477. }
  1478. *token = settings.m_IntersheetRefsPrefix + ref + settings.m_IntersheetRefsSuffix;
  1479. return true;
  1480. }
  1481. return SCH_LABEL_BASE::ResolveTextVar( aPath, token, aDepth );
  1482. }
  1483. std::vector<int> SCH_GLOBALLABEL::ViewGetLayers() const
  1484. {
  1485. return { LAYER_DANGLING,
  1486. LAYER_GLOBLABEL,
  1487. LAYER_DEVICE,
  1488. LAYER_INTERSHEET_REFS,
  1489. LAYER_NETCLASS_REFS,
  1490. LAYER_FIELDS,
  1491. LAYER_SELECTION_SHADOWS };
  1492. }
  1493. void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1494. std::vector<VECTOR2I>& aPoints,
  1495. const VECTOR2I& aPos ) const
  1496. {
  1497. int margin = GetLabelBoxExpansion( aRenderSettings );
  1498. int halfSize = ( GetTextHeight() / 2 ) + margin;
  1499. int linewidth = GetPenWidth();
  1500. int symb_len = GetTextBox().GetWidth() + 2 * margin;
  1501. int x = symb_len + linewidth + 3;
  1502. int y = halfSize + linewidth + 3;
  1503. aPoints.clear();
  1504. // Create outline shape : 6 points
  1505. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1506. aPoints.emplace_back( VECTOR2I( 0, -y ) ); // Up
  1507. aPoints.emplace_back( VECTOR2I( -x, -y ) ); // left
  1508. aPoints.emplace_back( VECTOR2I( -x, 0 ) ); // Up left
  1509. aPoints.emplace_back( VECTOR2I( -x, y ) ); // left down
  1510. aPoints.emplace_back( VECTOR2I( 0, y ) ); // down
  1511. int x_offset = 0;
  1512. switch( m_shape )
  1513. {
  1514. case LABEL_FLAG_SHAPE::L_INPUT:
  1515. x_offset = -halfSize;
  1516. aPoints[0].x += halfSize;
  1517. break;
  1518. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1519. aPoints[3].x -= halfSize;
  1520. break;
  1521. case LABEL_FLAG_SHAPE::L_BIDI:
  1522. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1523. x_offset = -halfSize;
  1524. aPoints[0].x += halfSize;
  1525. aPoints[3].x -= halfSize;
  1526. break;
  1527. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1528. default:
  1529. break;
  1530. }
  1531. // Rotate outlines and move corners in real position
  1532. for( VECTOR2I& aPoint : aPoints )
  1533. {
  1534. aPoint.x += x_offset;
  1535. switch( GetSpinStyle() )
  1536. {
  1537. default:
  1538. case SPIN_STYLE::LEFT: break;
  1539. case SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1540. case SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1541. case SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1542. }
  1543. aPoint += aPos;
  1544. }
  1545. aPoints.push_back( aPoints[0] ); // closing
  1546. }
  1547. wxString SCH_GLOBALLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1548. {
  1549. return wxString::Format( _( "Global Label '%s'" ),
  1550. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1551. }
  1552. BITMAPS SCH_GLOBALLABEL::GetMenuImage() const
  1553. {
  1554. return BITMAPS::add_glabel;
  1555. }
  1556. SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T aType ) :
  1557. SCH_LABEL_BASE( pos, text, aType )
  1558. {
  1559. m_layer = LAYER_HIERLABEL;
  1560. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1561. m_isDangling = true;
  1562. }
  1563. void SCH_HIERLABEL::Serialize( google::protobuf::Any &aContainer ) const
  1564. {
  1565. // TODO
  1566. }
  1567. bool SCH_HIERLABEL::Deserialize( const google::protobuf::Any &aContainer )
  1568. {
  1569. // TODO
  1570. return false;
  1571. }
  1572. void SCH_HIERLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle )
  1573. {
  1574. SCH_LABEL_BASE::SetSpinStyle( aSpinStyle );
  1575. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1576. }
  1577. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1578. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos ) const
  1579. {
  1580. CreateGraphicShape( aSettings, aPoints, aPos, m_shape );
  1581. }
  1582. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1583. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos,
  1584. LABEL_FLAG_SHAPE aShape ) const
  1585. {
  1586. int* Template = TemplateShape[static_cast<int>( aShape )][static_cast<int>( GetSpinStyle() )];
  1587. int halfSize = GetTextHeight() / 2;
  1588. int imax = *Template;
  1589. Template++;
  1590. aPoints.clear();
  1591. for( int ii = 0; ii < imax; ii++ )
  1592. {
  1593. VECTOR2I corner;
  1594. corner.x = ( halfSize * (*Template) ) + aPos.x;
  1595. Template++;
  1596. corner.y = ( halfSize * (*Template) ) + aPos.y;
  1597. Template++;
  1598. aPoints.push_back( corner );
  1599. }
  1600. }
  1601. const BOX2I SCH_HIERLABEL::GetBodyBoundingBox() const
  1602. {
  1603. int penWidth = GetEffectiveTextPenWidth();
  1604. int margin = GetTextOffset();
  1605. int x = GetTextPos().x;
  1606. int y = GetTextPos().y;
  1607. int height = GetTextHeight() + penWidth + margin;
  1608. int length = GetTextBox().GetWidth();
  1609. length += height; // add height for triangular shapes
  1610. int dx, dy;
  1611. switch( GetSpinStyle() )
  1612. {
  1613. default:
  1614. case SPIN_STYLE::LEFT:
  1615. dx = -length;
  1616. dy = height;
  1617. x += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1618. y -= height / 2;
  1619. break;
  1620. case SPIN_STYLE::UP:
  1621. dx = height;
  1622. dy = -length;
  1623. x -= height / 2;
  1624. y += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1625. break;
  1626. case SPIN_STYLE::RIGHT:
  1627. dx = length;
  1628. dy = height;
  1629. x -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1630. y -= height / 2;
  1631. break;
  1632. case SPIN_STYLE::BOTTOM:
  1633. dx = height;
  1634. dy = length;
  1635. x -= height / 2;
  1636. y -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1637. break;
  1638. }
  1639. BOX2I box( VECTOR2I( x, y ), VECTOR2I( dx, dy ) );
  1640. box.Normalize();
  1641. return box;
  1642. }
  1643. VECTOR2I SCH_HIERLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1644. {
  1645. VECTOR2I text_offset;
  1646. int dist = GetTextOffset( aSettings );
  1647. dist += GetTextWidth();
  1648. switch( GetSpinStyle() )
  1649. {
  1650. default:
  1651. case SPIN_STYLE::LEFT: text_offset.x = -dist; break; // Orientation horiz normale
  1652. case SPIN_STYLE::UP: text_offset.y = -dist; break; // Orientation vert UP
  1653. case SPIN_STYLE::RIGHT: text_offset.x = dist; break; // Orientation horiz inverse
  1654. case SPIN_STYLE::BOTTOM: text_offset.y = dist; break; // Orientation vert BOTTOM
  1655. }
  1656. return text_offset;
  1657. }
  1658. wxString SCH_HIERLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1659. {
  1660. return wxString::Format( _( "Hierarchical Label '%s'" ),
  1661. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1662. }
  1663. BITMAPS SCH_HIERLABEL::GetMenuImage() const
  1664. {
  1665. return BITMAPS::add_hierarchical_label;
  1666. }
  1667. HTML_MESSAGE_BOX* SCH_TEXT::ShowSyntaxHelp( wxWindow* aParentWindow )
  1668. {
  1669. wxString msg =
  1670. #include "sch_text_help_md.h"
  1671. ;
  1672. HTML_MESSAGE_BOX* dlg = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
  1673. wxSize sz( 320, 320 );
  1674. dlg->SetMinSize( dlg->ConvertDialogToPixels( sz ) );
  1675. dlg->SetDialogSizeInDU( sz.x, sz.y );
  1676. wxString html_txt;
  1677. ConvertMarkdown2Html( wxGetTranslation( msg ), html_txt );
  1678. dlg->AddHTML_Text( html_txt );
  1679. dlg->ShowModeless();
  1680. return dlg;
  1681. }
  1682. static struct SCH_LABEL_DESC
  1683. {
  1684. SCH_LABEL_DESC()
  1685. {
  1686. auto& labelShapeEnum = ENUM_MAP<LABEL_SHAPE>::Instance();
  1687. if( labelShapeEnum.Choices().GetCount() == 0 )
  1688. {
  1689. labelShapeEnum.Map( LABEL_SHAPE::LABEL_INPUT, _HKI( "Input" ) )
  1690. .Map( LABEL_SHAPE::LABEL_OUTPUT, _HKI( "Output" ) )
  1691. .Map( LABEL_SHAPE::LABEL_BIDI, _HKI( "Bidirectional" ) )
  1692. .Map( LABEL_SHAPE::LABEL_TRISTATE, _HKI( "Tri-state" ) )
  1693. .Map( LABEL_SHAPE::LABEL_PASSIVE, _HKI( "Passive" ) );
  1694. }
  1695. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1696. REGISTER_TYPE( SCH_LABEL_BASE );
  1697. REGISTER_TYPE( SCH_LABEL );
  1698. REGISTER_TYPE( SCH_HIERLABEL );
  1699. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, SCH_LABEL_BASE> );
  1700. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, SCH_LABEL_BASE> );
  1701. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, SCH_LABEL_BASE> );
  1702. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, SCH_TEXT> );
  1703. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, SCH_TEXT> );
  1704. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, SCH_TEXT> );
  1705. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, EDA_TEXT> );
  1706. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, EDA_TEXT> );
  1707. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, EDA_TEXT> );
  1708. propMgr.InheritsAfter( TYPE_HASH( SCH_LABEL_BASE ), TYPE_HASH( SCH_TEXT ) );
  1709. propMgr.InheritsAfter( TYPE_HASH( SCH_LABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1710. propMgr.InheritsAfter( TYPE_HASH( SCH_HIERLABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1711. propMgr.InheritsAfter( TYPE_HASH( SCH_GLOBALLABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1712. auto hasLabelShape =
  1713. []( INSPECTABLE* aItem ) -> bool
  1714. {
  1715. if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( aItem ) )
  1716. return label->IsType( { SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T } );
  1717. return false;
  1718. };
  1719. propMgr.AddProperty( new PROPERTY_ENUM<SCH_LABEL_BASE, LABEL_SHAPE>( _HKI( "Shape" ),
  1720. &SCH_LABEL_BASE::SetLabelShape, &SCH_LABEL_BASE::GetLabelShape ) )
  1721. .SetAvailableFunc( hasLabelShape );
  1722. propMgr.Mask( TYPE_HASH( SCH_LABEL_BASE ), TYPE_HASH( EDA_TEXT ), _HKI( "Hyperlink" ) );
  1723. }
  1724. } _SCH_LABEL_DESC;
  1725. static struct SCH_DIRECTIVE_LABEL_DESC
  1726. {
  1727. SCH_DIRECTIVE_LABEL_DESC()
  1728. {
  1729. auto& flagShapeEnum = ENUM_MAP<FLAG_SHAPE>::Instance();
  1730. if( flagShapeEnum.Choices().GetCount() == 0 )
  1731. {
  1732. flagShapeEnum.Map( FLAG_SHAPE::FLAG_DOT, _HKI( "Dot" ) )
  1733. .Map( FLAG_SHAPE::FLAG_CIRCLE, _HKI( "Circle" ) )
  1734. .Map( FLAG_SHAPE::FLAG_DIAMOND, _HKI( "Diamond" ) )
  1735. .Map( FLAG_SHAPE::FLAG_RECTANGLE, _HKI( "Rectangle" ) );
  1736. }
  1737. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1738. REGISTER_TYPE( SCH_DIRECTIVE_LABEL );
  1739. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, SCH_LABEL_BASE> );
  1740. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, SCH_TEXT> );
  1741. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, EDA_TEXT> );
  1742. propMgr.InheritsAfter( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1743. propMgr.AddProperty( new PROPERTY_ENUM<SCH_DIRECTIVE_LABEL, FLAG_SHAPE>(
  1744. _HKI( "Shape" ), &SCH_DIRECTIVE_LABEL::SetFlagShape,
  1745. &SCH_DIRECTIVE_LABEL::GetFlagShape ) );
  1746. propMgr.AddProperty( new PROPERTY<SCH_DIRECTIVE_LABEL, int>( _HKI( "Pin length" ),
  1747. &SCH_DIRECTIVE_LABEL::SetPinLength, &SCH_DIRECTIVE_LABEL::GetPinLength,
  1748. PROPERTY_DISPLAY::PT_SIZE ) );
  1749. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Text" ) );
  1750. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Thickness" ) );
  1751. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Italic" ) );
  1752. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Bold" ) );
  1753. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ),
  1754. _HKI( "Horizontal Justification" ) );
  1755. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ),
  1756. _HKI( "Vertical Justification" ) );
  1757. }
  1758. } _SCH_DIRECTIVE_LABEL_DESC;
  1759. ENUM_TO_WXANY( LABEL_SHAPE )
  1760. ENUM_TO_WXANY( FLAG_SHAPE )