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.

2311 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, RECURSE_MODE aMode )
  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. if( aPlotter->GetColorMode() && GetLabelColor() != COLOR4D::UNSPECIFIED )
  981. color = GetLabelColor();
  982. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  983. aPlotter->SetCurrentLineWidth( penWidth );
  984. KIFONT::FONT* font = GetFont();
  985. if( !font )
  986. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  987. VECTOR2I textpos = GetTextPos() + GetSchematicTextOffset( aPlotter->RenderSettings() );
  988. CreateGraphicShape( aPlotter->RenderSettings(), s_poly, GetTextPos() );
  989. TEXT_ATTRIBUTES attrs = GetAttributes();
  990. attrs.m_StrokeWidth = penWidth;
  991. attrs.m_Multiline = false;
  992. if( aBackground )
  993. {
  994. // No filled shapes (yet)
  995. }
  996. else
  997. {
  998. aPlotter->PlotText( textpos, color, GetShownText( sheet, true ), attrs, font,
  999. GetFontMetrics() );
  1000. if( aPlotter->GetColorMode() )
  1001. {
  1002. // For the graphic shape use the override color or the layer color, but not the
  1003. // net/netclass color.
  1004. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  1005. aPlotter->SetColor( GetTextColor() );
  1006. else
  1007. aPlotter->SetColor( settings->GetLayerColor( m_layer ) );
  1008. }
  1009. if( GetShape() == LABEL_FLAG_SHAPE::F_DOT )
  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->FilledCircle( s_poly[2], diameter , FILLED, nullptr );
  1016. }
  1017. else if( GetShape() == LABEL_FLAG_SHAPE::F_ROUND )
  1018. {
  1019. aPlotter->MoveTo( s_poly[0] );
  1020. aPlotter->LineTo( s_poly[1] );
  1021. aPlotter->PenFinish();
  1022. int diameter = ( s_poly[2] - s_poly[1] ).EuclideanNorm() * 2;
  1023. aPlotter->ThickCircle( s_poly[2], diameter, penWidth, FILLED, nullptr );
  1024. }
  1025. else
  1026. {
  1027. if( !s_poly.empty() )
  1028. aPlotter->PlotPoly( s_poly, FILL_T::NO_FILL, penWidth );
  1029. }
  1030. // Make sheet pins and hierarchical labels clickable hyperlinks
  1031. bool linkAlreadyPlotted = false;
  1032. if( aPlotOpts.m_PDFHierarchicalLinks )
  1033. {
  1034. if( Type() == SCH_HIER_LABEL_T )
  1035. {
  1036. if( sheet->size() >= 2 )
  1037. {
  1038. SCH_SHEET_PATH path = *sheet;
  1039. path.pop_back();
  1040. aPlotter->HyperlinkBox( GetBodyBoundingBox(),
  1041. EDA_TEXT::GotoPageHref( path.GetPageNumber() ) );
  1042. linkAlreadyPlotted = true;
  1043. }
  1044. }
  1045. else if( Type() == SCH_SHEET_PIN_T )
  1046. {
  1047. SCH_SHEET_PATH path = *sheet;
  1048. SCH_SHEET* parent = static_cast<SCH_SHEET*>( m_parent );
  1049. path.push_back( parent );
  1050. aPlotter->HyperlinkBox( GetBodyBoundingBox(),
  1051. EDA_TEXT::GotoPageHref( path.GetPageNumber() ) );
  1052. linkAlreadyPlotted = true;
  1053. }
  1054. }
  1055. // Plot attributes to a hypertext menu
  1056. if( aPlotOpts.m_PDFPropertyPopups && !linkAlreadyPlotted )
  1057. {
  1058. std::vector<wxString> properties;
  1059. if( connection )
  1060. {
  1061. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
  1062. _( "Net" ),
  1063. connection->Name() ) );
  1064. properties.emplace_back(
  1065. wxString::Format( wxT( "!%s = %s" ), _( "Resolved netclass" ),
  1066. GetEffectiveNetClass()->GetHumanReadableName() ) );
  1067. }
  1068. for( const SCH_FIELD& field : GetFields() )
  1069. {
  1070. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
  1071. field.GetName(),
  1072. field.GetShownText( false ) ) );
  1073. }
  1074. if( !properties.empty() )
  1075. aPlotter->HyperlinkMenu( GetBodyBoundingBox(), properties );
  1076. }
  1077. if( Type() == SCH_HIER_LABEL_T )
  1078. {
  1079. aPlotter->Bookmark( GetBodyBoundingBox(), GetShownText( false ),
  1080. _( "Hierarchical Labels" ) );
  1081. }
  1082. }
  1083. for( SCH_FIELD& field : m_fields )
  1084. field.Plot( aPlotter, aBackground, aPlotOpts, aUnit, aBodyStyle, aOffset, aDimmed );
  1085. }
  1086. bool SCH_LABEL_BASE::AutoRotateOnPlacement() const
  1087. {
  1088. return m_autoRotateOnPlacement;
  1089. }
  1090. void SCH_LABEL_BASE::SetAutoRotateOnPlacement( bool autoRotate )
  1091. {
  1092. m_autoRotateOnPlacement = autoRotate;
  1093. }
  1094. SCH_LABEL::SCH_LABEL( const VECTOR2I& pos, const wxString& text ) :
  1095. SCH_LABEL_BASE( pos, text, SCH_LABEL_T )
  1096. {
  1097. m_layer = LAYER_LOCLABEL;
  1098. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1099. m_isDangling = true;
  1100. }
  1101. void SCH_LABEL::Serialize( google::protobuf::Any &aContainer ) const
  1102. {
  1103. kiapi::schematic::types::LocalLabel label;
  1104. label.mutable_id()->set_value( m_Uuid.AsStdString() );
  1105. kiapi::common::PackVector2( *label.mutable_position(), GetPosition() );
  1106. aContainer.PackFrom( label );
  1107. }
  1108. bool SCH_LABEL::Deserialize( const google::protobuf::Any &aContainer )
  1109. {
  1110. kiapi::schematic::types::LocalLabel label;
  1111. if( !aContainer.UnpackTo( &label ) )
  1112. return false;
  1113. const_cast<KIID&>( m_Uuid ) = KIID( label.id().value() );
  1114. SetPosition( kiapi::common::UnpackVector2( label.position() ) );
  1115. return true;
  1116. }
  1117. const BOX2I SCH_LABEL::GetBodyBoundingBox() const
  1118. {
  1119. BOX2I rect = GetTextBox();
  1120. rect.Offset( 0, -GetTextOffset() );
  1121. rect.Inflate( GetEffectiveTextPenWidth() );
  1122. if( !GetTextAngle().IsZero() )
  1123. {
  1124. // Rotate rect
  1125. VECTOR2I pos = rect.GetOrigin();
  1126. VECTOR2I end = rect.GetEnd();
  1127. RotatePoint( pos, GetTextPos(), GetTextAngle() );
  1128. RotatePoint( end, GetTextPos(), GetTextAngle() );
  1129. rect.SetOrigin( pos );
  1130. rect.SetEnd( end );
  1131. rect.Normalize();
  1132. }
  1133. // Labels have a position point that is outside of the TextBox
  1134. rect.Merge( GetPosition() );
  1135. return rect;
  1136. }
  1137. wxString SCH_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1138. {
  1139. return wxString::Format( _( "Label '%s'" ),
  1140. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1141. }
  1142. BITMAPS SCH_LABEL::GetMenuImage() const
  1143. {
  1144. return BITMAPS::add_line_label;
  1145. }
  1146. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const VECTOR2I& pos ) :
  1147. SCH_LABEL_BASE( pos, wxEmptyString, SCH_DIRECTIVE_LABEL_T )
  1148. {
  1149. m_layer = LAYER_NETCLASS_REFS;
  1150. m_shape = LABEL_FLAG_SHAPE::F_ROUND;
  1151. m_pinLength = schIUScale.MilsToIU( 100 );
  1152. m_symbolSize = schIUScale.MilsToIU( 20 );
  1153. m_isDangling = true;
  1154. }
  1155. void SCH_DIRECTIVE_LABEL::swapData( SCH_ITEM* aItem )
  1156. {
  1157. SCH_LABEL_BASE::swapData( aItem );
  1158. SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( aItem );
  1159. std::swap( m_pinLength, label->m_pinLength );
  1160. std::swap( m_symbolSize, label->m_symbolSize );
  1161. }
  1162. SCH_DIRECTIVE_LABEL::SCH_DIRECTIVE_LABEL( const SCH_DIRECTIVE_LABEL& aClassLabel ) :
  1163. SCH_LABEL_BASE( aClassLabel )
  1164. {
  1165. m_pinLength = aClassLabel.m_pinLength;
  1166. m_symbolSize = aClassLabel.m_symbolSize;
  1167. }
  1168. void SCH_DIRECTIVE_LABEL::Serialize( google::protobuf::Any &aContainer ) const
  1169. {
  1170. // TODO
  1171. }
  1172. bool SCH_DIRECTIVE_LABEL::Deserialize( const google::protobuf::Any &aContainer )
  1173. {
  1174. // TODO
  1175. return false;
  1176. }
  1177. int SCH_DIRECTIVE_LABEL::GetPenWidth() const
  1178. {
  1179. int pen = 0;
  1180. if( Schematic() )
  1181. pen = Schematic()->Settings().m_DefaultLineWidth;
  1182. return GetEffectiveTextPenWidth( pen );
  1183. }
  1184. void SCH_DIRECTIVE_LABEL::MirrorSpinStyle( bool aLeftRight )
  1185. {
  1186. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1187. // vertical shape (like a text reduced to only "I" letter).
  1188. // So the mirroring is not exactly similar to a SCH_TEXT item
  1189. SCH_TEXT::MirrorSpinStyle( !aLeftRight );
  1190. for( SCH_FIELD& field : m_fields )
  1191. {
  1192. if( ( aLeftRight && field.GetTextAngle().IsHorizontal() )
  1193. || ( !aLeftRight && field.GetTextAngle().IsVertical() ) )
  1194. {
  1195. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1196. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1197. else
  1198. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1199. }
  1200. VECTOR2I pos = field.GetTextPos();
  1201. VECTOR2I delta = (VECTOR2I)GetPosition() - pos;
  1202. if( aLeftRight )
  1203. pos.x = GetPosition().x + delta.x;
  1204. else
  1205. pos.y = GetPosition().y + delta.y;
  1206. field.SetTextPos( pos );
  1207. }
  1208. }
  1209. void SCH_DIRECTIVE_LABEL::MirrorHorizontally( int aCenter )
  1210. {
  1211. VECTOR2I old_pos = GetPosition();
  1212. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1213. // vertical shape (like a text reduced to only "I" letter).
  1214. // So the mirroring is not exactly similar to a SCH_TEXT item
  1215. // Text is NOT really mirrored; it is moved to a suitable horizontal position
  1216. SetSpinStyle( GetSpinStyle().MirrorX() );
  1217. SetTextX( MIRRORVAL( GetTextPos().x, aCenter ) );
  1218. for( SCH_FIELD& field : m_fields )
  1219. {
  1220. if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  1221. field.SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  1222. else if( field.GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  1223. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1224. VECTOR2I pos = field.GetTextPos();
  1225. VECTOR2I delta = old_pos - pos;
  1226. pos.x = GetPosition().x + delta.x;
  1227. field.SetPosition( pos );
  1228. }
  1229. }
  1230. void SCH_DIRECTIVE_LABEL::MirrorVertically( int aCenter )
  1231. {
  1232. VECTOR2I old_pos = GetPosition();
  1233. // The "text" is in fact a graphic shape. For a horizontal "text", it looks like a
  1234. // vertical shape (like a text reduced to only "I" letter).
  1235. // So the mirroring is not exactly similar to a SCH_TEXT item
  1236. // Text is NOT really mirrored; it is moved to a suitable vertical position
  1237. SetSpinStyle( GetSpinStyle().MirrorY() );
  1238. SetTextY( MIRRORVAL( GetTextPos().y, aCenter ) );
  1239. for( SCH_FIELD& field : m_fields )
  1240. {
  1241. VECTOR2I pos = field.GetTextPos();
  1242. VECTOR2I delta = old_pos - pos;
  1243. pos.y = GetPosition().y + delta.y;
  1244. field.SetPosition( pos );
  1245. }
  1246. }
  1247. void SCH_DIRECTIVE_LABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1248. std::vector<VECTOR2I>& aPoints,
  1249. const VECTOR2I& aPos ) const
  1250. {
  1251. int symbolSize = m_symbolSize;
  1252. aPoints.clear();
  1253. switch( m_shape )
  1254. {
  1255. case LABEL_FLAG_SHAPE::F_DOT:
  1256. symbolSize = KiROUND( symbolSize * 0.7 );
  1257. KI_FALLTHROUGH;
  1258. case LABEL_FLAG_SHAPE::F_ROUND:
  1259. // First 3 points are used for generating shape
  1260. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1261. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1262. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  1263. // These points are just used to bulk out the bounding box
  1264. aPoints.emplace_back( VECTOR2I( -m_symbolSize, m_pinLength ) );
  1265. aPoints.emplace_back( VECTOR2I( 0, m_pinLength ) );
  1266. aPoints.emplace_back( VECTOR2I( m_symbolSize, m_pinLength + symbolSize ) );
  1267. break;
  1268. case LABEL_FLAG_SHAPE::F_DIAMOND:
  1269. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1270. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1271. aPoints.emplace_back( VECTOR2I( -2 * m_symbolSize, m_pinLength ) );
  1272. aPoints.emplace_back( VECTOR2I( 0, m_pinLength + symbolSize ) );
  1273. aPoints.emplace_back( VECTOR2I( 2 * m_symbolSize, m_pinLength ) );
  1274. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1275. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1276. break;
  1277. case LABEL_FLAG_SHAPE::F_RECTANGLE:
  1278. symbolSize = KiROUND( symbolSize * 0.8 );
  1279. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1280. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1281. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength - symbolSize ) );
  1282. aPoints.emplace_back( VECTOR2I( -2 * symbolSize, m_pinLength + symbolSize ) );
  1283. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength + symbolSize ) );
  1284. aPoints.emplace_back( VECTOR2I( 2 * symbolSize, m_pinLength - symbolSize ) );
  1285. aPoints.emplace_back( VECTOR2I( 0, m_pinLength - symbolSize ) );
  1286. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1287. break;
  1288. default:
  1289. break;
  1290. }
  1291. // Rotate outlines and move corners to real position
  1292. for( VECTOR2I& aPoint : aPoints )
  1293. {
  1294. switch( GetSpinStyle() )
  1295. {
  1296. default:
  1297. case SPIN_STYLE::LEFT: break;
  1298. case SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1299. case SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1300. case SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1301. }
  1302. aPoint += aPos;
  1303. }
  1304. }
  1305. void SCH_DIRECTIVE_LABEL::AutoplaceFields( SCH_SCREEN* aScreen, AUTOPLACE_ALGO aAlgo )
  1306. {
  1307. int margin = GetTextOffset();
  1308. int symbolWidth = m_symbolSize;
  1309. int origin = m_pinLength;
  1310. if( m_shape == LABEL_FLAG_SHAPE::F_DIAMOND || m_shape == LABEL_FLAG_SHAPE::F_RECTANGLE )
  1311. symbolWidth *= 2;
  1312. if( IsItalic() )
  1313. margin = KiROUND( margin * 1.5 );
  1314. VECTOR2I offset;
  1315. for( SCH_FIELD& field : m_fields )
  1316. {
  1317. if( field.GetText() == wxEmptyString )
  1318. continue;
  1319. switch( GetSpinStyle() )
  1320. {
  1321. default:
  1322. case SPIN_STYLE::LEFT:
  1323. field.SetTextAngle( ANGLE_HORIZONTAL );
  1324. offset = { symbolWidth + margin, origin };
  1325. break;
  1326. case SPIN_STYLE::UP:
  1327. field.SetTextAngle( ANGLE_VERTICAL );
  1328. offset = { -origin, -( symbolWidth + margin ) };
  1329. break;
  1330. case SPIN_STYLE::RIGHT:
  1331. field.SetTextAngle( ANGLE_HORIZONTAL );
  1332. offset = { symbolWidth + margin, -origin };
  1333. break;
  1334. case SPIN_STYLE::BOTTOM:
  1335. field.SetTextAngle( ANGLE_VERTICAL );
  1336. offset = { origin, -( symbolWidth + margin ) };
  1337. break;
  1338. }
  1339. field.SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  1340. field.SetTextPos( GetPosition() + offset );
  1341. origin -= field.GetTextHeight() + margin;
  1342. }
  1343. if( aAlgo == AUTOPLACE_AUTO || aAlgo == AUTOPLACE_MANUAL )
  1344. m_fieldsAutoplaced = aAlgo;
  1345. }
  1346. wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1347. {
  1348. if( m_fields.empty() )
  1349. {
  1350. return _( "Directive Label" );
  1351. }
  1352. else
  1353. {
  1354. return wxString::Format( _( "Directive Label [%s %s]" ),
  1355. UnescapeString( m_fields[0].GetName() ),
  1356. aFull ? m_fields[0].GetShownText( false )
  1357. : KIUI::EllipsizeMenuText( m_fields[0].GetText() ) );
  1358. }
  1359. }
  1360. void SCH_DIRECTIVE_LABEL::AddConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
  1361. {
  1362. m_connected_rule_areas.insert( aRuleArea );
  1363. }
  1364. void SCH_DIRECTIVE_LABEL::ClearConnectedRuleAreas()
  1365. {
  1366. m_connected_rule_areas.clear();
  1367. }
  1368. void SCH_DIRECTIVE_LABEL::RemoveConnectedRuleArea( SCH_RULE_AREA* aRuleArea )
  1369. {
  1370. m_connected_rule_areas.erase( aRuleArea );
  1371. }
  1372. bool SCH_DIRECTIVE_LABEL::IsDangling() const
  1373. {
  1374. return m_isDangling && m_connected_rule_areas.empty();
  1375. }
  1376. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const VECTOR2I& pos, const wxString& text ) :
  1377. SCH_LABEL_BASE( pos, text, SCH_GLOBAL_LABEL_T )
  1378. {
  1379. m_layer = LAYER_GLOBLABEL;
  1380. m_shape = LABEL_FLAG_SHAPE::L_BIDI;
  1381. m_isDangling = true;
  1382. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1383. m_fields.emplace_back( SCH_FIELD( pos, FIELD_T::INTERSHEET_REFS, this,
  1384. ::GetDefaultFieldName( FIELD_T::INTERSHEET_REFS, false ) ) );
  1385. m_fields.back().SetText( wxT( "${INTERSHEET_REFS}" ) );
  1386. m_fields.back().SetVisible( false );
  1387. m_fields.back().SetLayer( LAYER_INTERSHEET_REFS );
  1388. m_fields.back().SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1389. }
  1390. SCH_GLOBALLABEL::SCH_GLOBALLABEL( const SCH_GLOBALLABEL& aGlobalLabel ) :
  1391. SCH_LABEL_BASE( aGlobalLabel )
  1392. {
  1393. }
  1394. void SCH_GLOBALLABEL::Serialize( google::protobuf::Any &aContainer ) const
  1395. {
  1396. // TODO
  1397. }
  1398. bool SCH_GLOBALLABEL::Deserialize( const google::protobuf::Any &aContainer )
  1399. {
  1400. // TODO
  1401. return false;
  1402. }
  1403. SCH_FIELD* SCH_GLOBALLABEL::GetField( FIELD_T aFieldType )
  1404. {
  1405. if( SCH_FIELD* field = FindField( m_fields, aFieldType ) )
  1406. return field;
  1407. m_fields.emplace_back( this, aFieldType );
  1408. return &m_fields.back();
  1409. }
  1410. const SCH_FIELD* SCH_GLOBALLABEL::GetField( FIELD_T aFieldType ) const
  1411. {
  1412. return FindField( m_fields, aFieldType );
  1413. }
  1414. VECTOR2I SCH_GLOBALLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1415. {
  1416. int horiz = GetLabelBoxExpansion( aSettings );
  1417. // Center the text on the center line of "E" instead of "R" to make room for an overbar
  1418. int vert = GetTextHeight() * 0.0715;
  1419. switch( m_shape )
  1420. {
  1421. case LABEL_FLAG_SHAPE::L_INPUT:
  1422. case LABEL_FLAG_SHAPE::L_BIDI:
  1423. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1424. horiz += GetTextHeight() * 3 / 4; // Use three-quarters-height as proxy for triangle size
  1425. break;
  1426. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1427. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1428. default:
  1429. break;
  1430. }
  1431. switch( GetSpinStyle() )
  1432. {
  1433. default:
  1434. case SPIN_STYLE::LEFT: return VECTOR2I( -horiz, vert );
  1435. case SPIN_STYLE::UP: return VECTOR2I( vert, -horiz );
  1436. case SPIN_STYLE::RIGHT: return VECTOR2I( horiz, vert );
  1437. case SPIN_STYLE::BOTTOM: return VECTOR2I( vert, horiz );
  1438. }
  1439. }
  1440. void SCH_GLOBALLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle )
  1441. {
  1442. SCH_LABEL_BASE::SetSpinStyle( aSpinStyle );
  1443. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1444. }
  1445. bool SCH_GLOBALLABEL::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token,
  1446. int aDepth ) const
  1447. {
  1448. wxCHECK( aPath, false );
  1449. SCHEMATIC* schematic = Schematic();
  1450. if( !schematic )
  1451. return false;
  1452. if( token->IsSameAs( wxT( "INTERSHEET_REFS" ) ) )
  1453. {
  1454. SCHEMATIC_SETTINGS& settings = schematic->Settings();
  1455. wxString ref;
  1456. auto it = schematic->GetPageRefsMap().find( GetShownText( aPath ) );
  1457. if( it == schematic->GetPageRefsMap().end() )
  1458. {
  1459. ref = "?";
  1460. }
  1461. else
  1462. {
  1463. std::vector<int> pageListCopy;
  1464. pageListCopy.insert( pageListCopy.end(), it->second.begin(), it->second.end() );
  1465. std::sort( pageListCopy.begin(), pageListCopy.end() );
  1466. if( !settings.m_IntersheetRefsListOwnPage )
  1467. {
  1468. int currentPage = schematic->CurrentSheet().GetVirtualPageNumber();
  1469. alg::delete_matching( pageListCopy, currentPage );
  1470. }
  1471. std::map<int, wxString> sheetPages = schematic->GetVirtualPageToSheetPagesMap();
  1472. if( ( settings.m_IntersheetRefsFormatShort ) && ( pageListCopy.size() > 2 ) )
  1473. {
  1474. ref.Append( wxString::Format( wxT( "%s..%s" ),
  1475. sheetPages[pageListCopy.front()],
  1476. sheetPages[pageListCopy.back()] ) );
  1477. }
  1478. else
  1479. {
  1480. for( const int& pageNo : pageListCopy )
  1481. ref.Append( wxString::Format( wxT( "%s," ), sheetPages[pageNo] ) );
  1482. if( !ref.IsEmpty() && ref.Last() == ',' )
  1483. ref.RemoveLast();
  1484. }
  1485. }
  1486. *token = settings.m_IntersheetRefsPrefix + ref + settings.m_IntersheetRefsSuffix;
  1487. return true;
  1488. }
  1489. return SCH_LABEL_BASE::ResolveTextVar( aPath, token, aDepth );
  1490. }
  1491. std::vector<int> SCH_GLOBALLABEL::ViewGetLayers() const
  1492. {
  1493. return { LAYER_DANGLING,
  1494. LAYER_GLOBLABEL,
  1495. LAYER_DEVICE,
  1496. LAYER_INTERSHEET_REFS,
  1497. LAYER_NETCLASS_REFS,
  1498. LAYER_FIELDS,
  1499. LAYER_SELECTION_SHADOWS };
  1500. }
  1501. void SCH_GLOBALLABEL::CreateGraphicShape( const RENDER_SETTINGS* aRenderSettings,
  1502. std::vector<VECTOR2I>& aPoints,
  1503. const VECTOR2I& aPos ) const
  1504. {
  1505. int margin = GetLabelBoxExpansion( aRenderSettings );
  1506. int halfSize = ( GetTextHeight() / 2 ) + margin;
  1507. int linewidth = GetPenWidth();
  1508. int symb_len = GetTextBox().GetWidth() + 2 * margin;
  1509. int x = symb_len + linewidth + 3;
  1510. int y = halfSize + linewidth + 3;
  1511. aPoints.clear();
  1512. // Create outline shape : 6 points
  1513. aPoints.emplace_back( VECTOR2I( 0, 0 ) );
  1514. aPoints.emplace_back( VECTOR2I( 0, -y ) ); // Up
  1515. aPoints.emplace_back( VECTOR2I( -x, -y ) ); // left
  1516. aPoints.emplace_back( VECTOR2I( -x, 0 ) ); // Up left
  1517. aPoints.emplace_back( VECTOR2I( -x, y ) ); // left down
  1518. aPoints.emplace_back( VECTOR2I( 0, y ) ); // down
  1519. int x_offset = 0;
  1520. switch( m_shape )
  1521. {
  1522. case LABEL_FLAG_SHAPE::L_INPUT:
  1523. x_offset = -halfSize;
  1524. aPoints[0].x += halfSize;
  1525. break;
  1526. case LABEL_FLAG_SHAPE::L_OUTPUT:
  1527. aPoints[3].x -= halfSize;
  1528. break;
  1529. case LABEL_FLAG_SHAPE::L_BIDI:
  1530. case LABEL_FLAG_SHAPE::L_TRISTATE:
  1531. x_offset = -halfSize;
  1532. aPoints[0].x += halfSize;
  1533. aPoints[3].x -= halfSize;
  1534. break;
  1535. case LABEL_FLAG_SHAPE::L_UNSPECIFIED:
  1536. default:
  1537. break;
  1538. }
  1539. // Rotate outlines and move corners in real position
  1540. for( VECTOR2I& aPoint : aPoints )
  1541. {
  1542. aPoint.x += x_offset;
  1543. switch( GetSpinStyle() )
  1544. {
  1545. default:
  1546. case SPIN_STYLE::LEFT: break;
  1547. case SPIN_STYLE::UP: RotatePoint( aPoint, -ANGLE_90 ); break;
  1548. case SPIN_STYLE::RIGHT: RotatePoint( aPoint, ANGLE_180 ); break;
  1549. case SPIN_STYLE::BOTTOM: RotatePoint( aPoint, ANGLE_90 ); break;
  1550. }
  1551. aPoint += aPos;
  1552. }
  1553. aPoints.push_back( aPoints[0] ); // closing
  1554. }
  1555. wxString SCH_GLOBALLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1556. {
  1557. return wxString::Format( _( "Global Label '%s'" ),
  1558. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1559. }
  1560. BITMAPS SCH_GLOBALLABEL::GetMenuImage() const
  1561. {
  1562. return BITMAPS::add_glabel;
  1563. }
  1564. SCH_HIERLABEL::SCH_HIERLABEL( const VECTOR2I& pos, const wxString& text, KICAD_T aType ) :
  1565. SCH_LABEL_BASE( pos, text, aType )
  1566. {
  1567. m_layer = LAYER_HIERLABEL;
  1568. m_shape = LABEL_FLAG_SHAPE::L_INPUT;
  1569. m_isDangling = true;
  1570. }
  1571. void SCH_HIERLABEL::Serialize( google::protobuf::Any &aContainer ) const
  1572. {
  1573. // TODO
  1574. }
  1575. bool SCH_HIERLABEL::Deserialize( const google::protobuf::Any &aContainer )
  1576. {
  1577. // TODO
  1578. return false;
  1579. }
  1580. void SCH_HIERLABEL::SetSpinStyle( SPIN_STYLE aSpinStyle )
  1581. {
  1582. SCH_LABEL_BASE::SetSpinStyle( aSpinStyle );
  1583. SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
  1584. }
  1585. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1586. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos ) const
  1587. {
  1588. CreateGraphicShape( aSettings, aPoints, aPos, m_shape );
  1589. }
  1590. void SCH_HIERLABEL::CreateGraphicShape( const RENDER_SETTINGS* aSettings,
  1591. std::vector<VECTOR2I>& aPoints, const VECTOR2I& aPos,
  1592. LABEL_FLAG_SHAPE aShape ) const
  1593. {
  1594. int* Template = TemplateShape[static_cast<int>( aShape )][static_cast<int>( GetSpinStyle() )];
  1595. int halfSize = GetTextHeight() / 2;
  1596. int imax = *Template;
  1597. Template++;
  1598. aPoints.clear();
  1599. for( int ii = 0; ii < imax; ii++ )
  1600. {
  1601. VECTOR2I corner;
  1602. corner.x = ( halfSize * (*Template) ) + aPos.x;
  1603. Template++;
  1604. corner.y = ( halfSize * (*Template) ) + aPos.y;
  1605. Template++;
  1606. aPoints.push_back( corner );
  1607. }
  1608. }
  1609. const BOX2I SCH_HIERLABEL::GetBodyBoundingBox() const
  1610. {
  1611. int penWidth = GetEffectiveTextPenWidth();
  1612. int margin = GetTextOffset();
  1613. int x = GetTextPos().x;
  1614. int y = GetTextPos().y;
  1615. int height = GetTextHeight() + penWidth + margin;
  1616. int length = GetTextBox().GetWidth();
  1617. length += height; // add height for triangular shapes
  1618. int dx, dy;
  1619. switch( GetSpinStyle() )
  1620. {
  1621. default:
  1622. case SPIN_STYLE::LEFT:
  1623. dx = -length;
  1624. dy = height;
  1625. x += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1626. y -= height / 2;
  1627. break;
  1628. case SPIN_STYLE::UP:
  1629. dx = height;
  1630. dy = -length;
  1631. x -= height / 2;
  1632. y += schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1633. break;
  1634. case SPIN_STYLE::RIGHT:
  1635. dx = length;
  1636. dy = height;
  1637. x -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1638. y -= height / 2;
  1639. break;
  1640. case SPIN_STYLE::BOTTOM:
  1641. dx = height;
  1642. dy = length;
  1643. x -= height / 2;
  1644. y -= schIUScale.MilsToIU( DANGLING_SYMBOL_SIZE );
  1645. break;
  1646. }
  1647. BOX2I box( VECTOR2I( x, y ), VECTOR2I( dx, dy ) );
  1648. box.Normalize();
  1649. return box;
  1650. }
  1651. VECTOR2I SCH_HIERLABEL::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  1652. {
  1653. VECTOR2I text_offset;
  1654. int dist = GetTextOffset( aSettings );
  1655. dist += GetTextWidth();
  1656. switch( GetSpinStyle() )
  1657. {
  1658. default:
  1659. case SPIN_STYLE::LEFT: text_offset.x = -dist; break; // Orientation horiz normale
  1660. case SPIN_STYLE::UP: text_offset.y = -dist; break; // Orientation vert UP
  1661. case SPIN_STYLE::RIGHT: text_offset.x = dist; break; // Orientation horiz inverse
  1662. case SPIN_STYLE::BOTTOM: text_offset.y = dist; break; // Orientation vert BOTTOM
  1663. }
  1664. return text_offset;
  1665. }
  1666. wxString SCH_HIERLABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  1667. {
  1668. return wxString::Format( _( "Hierarchical Label '%s'" ),
  1669. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  1670. }
  1671. BITMAPS SCH_HIERLABEL::GetMenuImage() const
  1672. {
  1673. return BITMAPS::add_hierarchical_label;
  1674. }
  1675. HTML_MESSAGE_BOX* SCH_TEXT::ShowSyntaxHelp( wxWindow* aParentWindow )
  1676. {
  1677. wxString msg =
  1678. #include "sch_text_help_md.h"
  1679. ;
  1680. HTML_MESSAGE_BOX* dlg = new HTML_MESSAGE_BOX( nullptr, _( "Syntax Help" ) );
  1681. wxSize sz( 320, 320 );
  1682. dlg->SetMinSize( dlg->ConvertDialogToPixels( sz ) );
  1683. dlg->SetDialogSizeInDU( sz.x, sz.y );
  1684. wxString html_txt;
  1685. ConvertMarkdown2Html( wxGetTranslation( msg ), html_txt );
  1686. dlg->AddHTML_Text( html_txt );
  1687. dlg->ShowModeless();
  1688. return dlg;
  1689. }
  1690. static struct SCH_LABEL_DESC
  1691. {
  1692. SCH_LABEL_DESC()
  1693. {
  1694. auto& labelShapeEnum = ENUM_MAP<LABEL_SHAPE>::Instance();
  1695. if( labelShapeEnum.Choices().GetCount() == 0 )
  1696. {
  1697. labelShapeEnum.Map( LABEL_SHAPE::LABEL_INPUT, _HKI( "Input" ) )
  1698. .Map( LABEL_SHAPE::LABEL_OUTPUT, _HKI( "Output" ) )
  1699. .Map( LABEL_SHAPE::LABEL_BIDI, _HKI( "Bidirectional" ) )
  1700. .Map( LABEL_SHAPE::LABEL_TRISTATE, _HKI( "Tri-state" ) )
  1701. .Map( LABEL_SHAPE::LABEL_PASSIVE, _HKI( "Passive" ) );
  1702. }
  1703. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1704. REGISTER_TYPE( SCH_LABEL_BASE );
  1705. REGISTER_TYPE( SCH_LABEL );
  1706. REGISTER_TYPE( SCH_HIERLABEL );
  1707. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, SCH_LABEL_BASE> );
  1708. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, SCH_LABEL_BASE> );
  1709. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, SCH_LABEL_BASE> );
  1710. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, SCH_TEXT> );
  1711. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, SCH_TEXT> );
  1712. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, SCH_TEXT> );
  1713. propMgr.AddTypeCast( new TYPE_CAST<SCH_LABEL, EDA_TEXT> );
  1714. propMgr.AddTypeCast( new TYPE_CAST<SCH_HIERLABEL, EDA_TEXT> );
  1715. propMgr.AddTypeCast( new TYPE_CAST<SCH_GLOBALLABEL, EDA_TEXT> );
  1716. propMgr.InheritsAfter( TYPE_HASH( SCH_LABEL_BASE ), TYPE_HASH( SCH_TEXT ) );
  1717. propMgr.InheritsAfter( TYPE_HASH( SCH_LABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1718. propMgr.InheritsAfter( TYPE_HASH( SCH_HIERLABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1719. propMgr.InheritsAfter( TYPE_HASH( SCH_GLOBALLABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1720. auto hasLabelShape =
  1721. []( INSPECTABLE* aItem ) -> bool
  1722. {
  1723. if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( aItem ) )
  1724. return label->IsType( { SCH_GLOBAL_LABEL_T, SCH_HIER_LABEL_T } );
  1725. return false;
  1726. };
  1727. propMgr.AddProperty( new PROPERTY_ENUM<SCH_LABEL_BASE, LABEL_SHAPE>( _HKI( "Shape" ),
  1728. &SCH_LABEL_BASE::SetLabelShape, &SCH_LABEL_BASE::GetLabelShape ) )
  1729. .SetAvailableFunc( hasLabelShape );
  1730. propMgr.Mask( TYPE_HASH( SCH_LABEL_BASE ), TYPE_HASH( EDA_TEXT ), _HKI( "Hyperlink" ) );
  1731. }
  1732. } _SCH_LABEL_DESC;
  1733. static struct SCH_DIRECTIVE_LABEL_DESC
  1734. {
  1735. SCH_DIRECTIVE_LABEL_DESC()
  1736. {
  1737. auto& flagShapeEnum = ENUM_MAP<FLAG_SHAPE>::Instance();
  1738. if( flagShapeEnum.Choices().GetCount() == 0 )
  1739. {
  1740. flagShapeEnum.Map( FLAG_SHAPE::FLAG_DOT, _HKI( "Dot" ) )
  1741. .Map( FLAG_SHAPE::FLAG_CIRCLE, _HKI( "Circle" ) )
  1742. .Map( FLAG_SHAPE::FLAG_DIAMOND, _HKI( "Diamond" ) )
  1743. .Map( FLAG_SHAPE::FLAG_RECTANGLE, _HKI( "Rectangle" ) );
  1744. }
  1745. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1746. REGISTER_TYPE( SCH_DIRECTIVE_LABEL );
  1747. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, SCH_LABEL_BASE> );
  1748. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, SCH_TEXT> );
  1749. propMgr.AddTypeCast( new TYPE_CAST<SCH_DIRECTIVE_LABEL, EDA_TEXT> );
  1750. propMgr.InheritsAfter( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( SCH_LABEL_BASE ) );
  1751. propMgr.AddProperty( new PROPERTY_ENUM<SCH_DIRECTIVE_LABEL, FLAG_SHAPE>(
  1752. _HKI( "Shape" ), &SCH_DIRECTIVE_LABEL::SetFlagShape,
  1753. &SCH_DIRECTIVE_LABEL::GetFlagShape ) );
  1754. propMgr.AddProperty( new PROPERTY<SCH_DIRECTIVE_LABEL, int>( _HKI( "Pin length" ),
  1755. &SCH_DIRECTIVE_LABEL::SetPinLength, &SCH_DIRECTIVE_LABEL::GetPinLength,
  1756. PROPERTY_DISPLAY::PT_SIZE ) );
  1757. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Text" ) );
  1758. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Thickness" ) );
  1759. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Italic" ) );
  1760. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ), _HKI( "Bold" ) );
  1761. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ),
  1762. _HKI( "Horizontal Justification" ) );
  1763. propMgr.Mask( TYPE_HASH( SCH_DIRECTIVE_LABEL ), TYPE_HASH( EDA_TEXT ),
  1764. _HKI( "Vertical Justification" ) );
  1765. }
  1766. } _SCH_DIRECTIVE_LABEL_DESC;
  1767. ENUM_TO_WXANY( LABEL_SHAPE )
  1768. ENUM_TO_WXANY( FLAG_SHAPE )