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.

1549 lines
41 KiB

18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 years ago
18 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) 2023 CERN
  6. * Copyright (C) 1992-2024 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 <cstdlib>
  26. #include <bitmaps.h>
  27. #include <core/mirror.h>
  28. #include <core/kicad_algo.h>
  29. #include <sch_draw_panel.h>
  30. #include <trigo.h>
  31. #include <sch_edit_frame.h>
  32. #include <plotters/plotter.h>
  33. #include <string_utils.h>
  34. #include <widgets/msgpanel.h>
  35. #include <math/util.h> // for KiROUND
  36. #include <sch_sheet.h>
  37. #include <sch_sheet_path.h>
  38. #include <sch_sheet_pin.h>
  39. #include <sch_symbol.h>
  40. #include <sch_painter.h>
  41. #include <schematic.h>
  42. #include <settings/color_settings.h>
  43. #include <trace_helpers.h>
  44. #include <pgm_base.h>
  45. #include <wx/log.h>
  46. // N.B. Do not change these values without transitioning the file format
  47. #define SHEET_NAME_CANONICAL "Sheetname"
  48. #define SHEET_FILE_CANONICAL "Sheetfile"
  49. #define USER_FIELD_CANONICAL "Field%d"
  50. const wxString SCH_SHEET::GetDefaultFieldName( int aFieldNdx, bool aTranslated )
  51. {
  52. if( !aTranslated )
  53. {
  54. switch( aFieldNdx )
  55. {
  56. case SHEETNAME: return SHEET_NAME_CANONICAL;
  57. case SHEETFILENAME: return SHEET_FILE_CANONICAL;
  58. default: return wxString::Format( USER_FIELD_CANONICAL, aFieldNdx );
  59. }
  60. }
  61. // Fixed values for the mandatory fields
  62. switch( aFieldNdx )
  63. {
  64. case SHEETNAME: return _( SHEET_NAME_CANONICAL );
  65. case SHEETFILENAME: return _( SHEET_FILE_CANONICAL );
  66. default: return wxString::Format( _( USER_FIELD_CANONICAL ), aFieldNdx );
  67. }
  68. }
  69. SCH_SHEET::SCH_SHEET( EDA_ITEM* aParent, const VECTOR2I& aPos, VECTOR2I aSize,
  70. FIELDS_AUTOPLACED aAutoplaceFields ) :
  71. SCH_ITEM( aParent, SCH_SHEET_T )
  72. {
  73. m_layer = LAYER_SHEET;
  74. m_pos = aPos;
  75. m_size = aSize;
  76. m_screen = nullptr;
  77. m_borderWidth = 0;
  78. m_borderColor = COLOR4D::UNSPECIFIED;
  79. m_backgroundColor = COLOR4D::UNSPECIFIED;
  80. m_fieldsAutoplaced = aAutoplaceFields;
  81. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  82. {
  83. m_fields.emplace_back( aPos, i, this, GetDefaultFieldName( i ) );
  84. m_fields.back().SetVisible( true );
  85. if( i == SHEETNAME )
  86. m_fields.back().SetLayer( LAYER_SHEETNAME );
  87. else if( i == SHEETFILENAME )
  88. m_fields.back().SetLayer( LAYER_SHEETFILENAME );
  89. else
  90. m_fields.back().SetLayer( LAYER_SHEETFIELDS );
  91. }
  92. AutoAutoplaceFields( nullptr );
  93. }
  94. SCH_SHEET::SCH_SHEET( const SCH_SHEET& aSheet ) :
  95. SCH_ITEM( aSheet )
  96. {
  97. m_pos = aSheet.m_pos;
  98. m_size = aSheet.m_size;
  99. m_layer = aSheet.m_layer;
  100. const_cast<KIID&>( m_Uuid ) = aSheet.m_Uuid;
  101. m_fields = aSheet.m_fields;
  102. m_fieldsAutoplaced = aSheet.m_fieldsAutoplaced;
  103. m_screen = aSheet.m_screen;
  104. m_borderWidth = aSheet.m_borderWidth;
  105. m_borderColor = aSheet.m_borderColor;
  106. m_backgroundColor = aSheet.m_backgroundColor;
  107. m_instances = aSheet.m_instances;
  108. for( SCH_SHEET_PIN* pin : aSheet.m_pins )
  109. {
  110. m_pins.emplace_back( new SCH_SHEET_PIN( *pin ) );
  111. m_pins.back()->SetParent( this );
  112. }
  113. for( SCH_FIELD& field : m_fields )
  114. field.SetParent( this );
  115. if( m_screen )
  116. m_screen->IncRefCount();
  117. }
  118. SCH_SHEET::~SCH_SHEET()
  119. {
  120. // also, look at the associated sheet & its reference count
  121. // perhaps it should be deleted also.
  122. if( m_screen )
  123. {
  124. m_screen->DecRefCount();
  125. if( m_screen->GetRefCount() == 0 )
  126. delete m_screen;
  127. }
  128. // We own our pins; delete them
  129. for( SCH_SHEET_PIN* pin : m_pins )
  130. delete pin;
  131. }
  132. EDA_ITEM* SCH_SHEET::Clone() const
  133. {
  134. return new SCH_SHEET( *this );
  135. }
  136. void SCH_SHEET::SetScreen( SCH_SCREEN* aScreen )
  137. {
  138. if( aScreen == m_screen )
  139. return;
  140. if( m_screen != nullptr )
  141. {
  142. m_screen->DecRefCount();
  143. if( m_screen->GetRefCount() == 0 )
  144. {
  145. delete m_screen;
  146. m_screen = nullptr;
  147. }
  148. }
  149. m_screen = aScreen;
  150. if( m_screen )
  151. m_screen->IncRefCount();
  152. }
  153. int SCH_SHEET::GetScreenCount() const
  154. {
  155. if( m_screen == nullptr )
  156. return 0;
  157. return m_screen->GetRefCount();
  158. }
  159. bool SCH_SHEET::IsRootSheet() const
  160. {
  161. wxCHECK_MSG( Schematic(), false, "Can't call IsRootSheet without setting a schematic" );
  162. return &Schematic()->Root() == this;
  163. }
  164. void SCH_SHEET::GetContextualTextVars( wxArrayString* aVars ) const
  165. {
  166. auto add =
  167. [&]( const wxString& aVar )
  168. {
  169. if( !alg::contains( *aVars, aVar ) )
  170. aVars->push_back( aVar );
  171. };
  172. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  173. add( m_fields[i].GetCanonicalName().Upper() );
  174. for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields.size(); ++i )
  175. add( m_fields[i].GetName() );
  176. SCH_SHEET_PATH sheetPath = findSelf();
  177. if( sheetPath.size() >= 2 )
  178. {
  179. sheetPath.pop_back();
  180. sheetPath.Last()->GetContextualTextVars( aVars );
  181. }
  182. else if( Schematic() )
  183. {
  184. Schematic()->GetContextualTextVars( aVars );
  185. }
  186. add( wxT( "#" ) );
  187. add( wxT( "##" ) );
  188. add( wxT( "SHEETPATH" ) );
  189. m_screen->GetTitleBlock().GetContextualTextVars( aVars );
  190. }
  191. bool SCH_SHEET::ResolveTextVar( const SCH_SHEET_PATH* aPath, wxString* token, int aDepth ) const
  192. {
  193. wxCHECK( aPath, false );
  194. SCHEMATIC* schematic = Schematic();
  195. if( !schematic )
  196. return false;
  197. if( token->Contains( ':' ) )
  198. {
  199. if( schematic->ResolveCrossReference( token, aDepth + 1 ) )
  200. return true;
  201. }
  202. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  203. {
  204. if( token->IsSameAs( m_fields[i].GetCanonicalName().Upper() ) )
  205. {
  206. *token = m_fields[i].GetShownText( aPath, false, aDepth + 1 );
  207. return true;
  208. }
  209. }
  210. for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields.size(); ++i )
  211. {
  212. if( token->IsSameAs( m_fields[i].GetName() ) )
  213. {
  214. *token = m_fields[i].GetShownText( aPath, false, aDepth + 1 );
  215. return true;
  216. }
  217. }
  218. PROJECT* project = &schematic->Prj();
  219. // We cannot resolve text variables initially on load as we need to first load the screen and
  220. // then parse the hierarchy. So skip the resolution if the screen isn't set yet
  221. if( m_screen && m_screen->GetTitleBlock().TextVarResolver( token, project ) )
  222. {
  223. return true;
  224. }
  225. if( token->IsSameAs( wxT( "#" ) ) )
  226. {
  227. *token = wxString::Format( "%s", aPath->GetPageNumber() );
  228. return true;
  229. }
  230. else if( token->IsSameAs( wxT( "##" ) ) )
  231. {
  232. SCH_SHEET_LIST sheetList = schematic->GetSheets();
  233. *token = wxString::Format( wxT( "%d" ), (int) sheetList.size() );
  234. return true;
  235. }
  236. else if( token->IsSameAs( wxT( "SHEETPATH" ) ) )
  237. {
  238. *token = aPath->PathHumanReadable();
  239. return true;
  240. }
  241. // See if parent can resolve it (these will recurse to ancestors)
  242. if( aPath->size() >= 2 )
  243. {
  244. SCH_SHEET_PATH path = *aPath;
  245. path.pop_back();
  246. if( path.Last()->ResolveTextVar( &path, token, aDepth + 1 ) )
  247. return true;
  248. }
  249. else
  250. {
  251. if( schematic->ResolveTextVar( aPath, token, aDepth + 1 ) )
  252. return true;
  253. }
  254. return false;
  255. }
  256. void SCH_SHEET::SwapData( SCH_ITEM* aItem )
  257. {
  258. SCH_ITEM::SwapFlags( aItem );
  259. wxCHECK_RET( aItem->Type() == SCH_SHEET_T,
  260. wxString::Format( wxT( "SCH_SHEET object cannot swap data with %s object." ),
  261. aItem->GetClass() ) );
  262. SCH_SHEET* sheet = ( SCH_SHEET* ) aItem;
  263. std::swap( m_pos, sheet->m_pos );
  264. std::swap( m_size, sheet->m_size );
  265. m_fields.swap( sheet->m_fields );
  266. std::swap( m_fieldsAutoplaced, sheet->m_fieldsAutoplaced );
  267. m_pins.swap( sheet->m_pins );
  268. // Update parent pointers after swapping.
  269. for( SCH_SHEET_PIN* sheetPin : m_pins )
  270. sheetPin->SetParent( this );
  271. for( SCH_SHEET_PIN* sheetPin : sheet->m_pins )
  272. sheetPin->SetParent( sheet );
  273. for( SCH_FIELD& field : m_fields )
  274. field.SetParent( this );
  275. for( SCH_FIELD& field : sheet->m_fields )
  276. field.SetParent( sheet );
  277. std::swap( m_borderWidth, sheet->m_borderWidth );
  278. std::swap( m_borderColor, sheet->m_borderColor );
  279. std::swap( m_backgroundColor, sheet->m_backgroundColor );
  280. std::swap( m_instances, sheet->m_instances );
  281. }
  282. void SCH_SHEET::SetFields( const std::vector<SCH_FIELD>& aFields )
  283. {
  284. m_fields = aFields;
  285. // Ensure that mandatory fields are at the beginning
  286. std::sort( m_fields.begin(), m_fields.end(),
  287. []( const SCH_FIELD& a, const SCH_FIELD& b )
  288. {
  289. return a.GetId() < b.GetId();
  290. } );
  291. // After mandatory fields, the rest should be sequential user fields
  292. for( int ii = SHEET_MANDATORY_FIELDS; ii < static_cast<int>( m_fields.size() ); ++ii )
  293. m_fields[ii].SetId( ii );
  294. // Make sure that we get the UNIX variant of the file path
  295. SetFileName( m_fields[SHEETFILENAME].GetText() );
  296. }
  297. void SCH_SHEET::AddPin( SCH_SHEET_PIN* aSheetPin )
  298. {
  299. wxASSERT( aSheetPin != nullptr );
  300. wxASSERT( aSheetPin->Type() == SCH_SHEET_PIN_T );
  301. aSheetPin->SetParent( this );
  302. m_pins.push_back( aSheetPin );
  303. renumberPins();
  304. }
  305. void SCH_SHEET::RemovePin( const SCH_SHEET_PIN* aSheetPin )
  306. {
  307. wxASSERT( aSheetPin != nullptr );
  308. wxASSERT( aSheetPin->Type() == SCH_SHEET_PIN_T );
  309. for( auto i = m_pins.begin(); i < m_pins.end(); ++i )
  310. {
  311. if( *i == aSheetPin )
  312. {
  313. m_pins.erase( i );
  314. renumberPins();
  315. return;
  316. }
  317. }
  318. }
  319. bool SCH_SHEET::HasPin( const wxString& aName ) const
  320. {
  321. for( SCH_SHEET_PIN* pin : m_pins )
  322. {
  323. if( pin->GetText().Cmp( aName ) == 0 )
  324. return true;
  325. }
  326. return false;
  327. }
  328. bool SCH_SHEET::doIsConnected( const VECTOR2I& aPosition ) const
  329. {
  330. for( SCH_SHEET_PIN* sheetPin : m_pins )
  331. {
  332. if( sheetPin->GetPosition() == aPosition )
  333. return true;
  334. }
  335. return false;
  336. }
  337. bool SCH_SHEET::IsVerticalOrientation() const
  338. {
  339. int leftRight = 0;
  340. int topBottom = 0;
  341. for( SCH_SHEET_PIN* pin : m_pins )
  342. {
  343. switch( pin->GetSide() )
  344. {
  345. case SHEET_SIDE::LEFT: leftRight++; break;
  346. case SHEET_SIDE::RIGHT: leftRight++; break;
  347. case SHEET_SIDE::TOP: topBottom++; break;
  348. case SHEET_SIDE::BOTTOM: topBottom++; break;
  349. default: break;
  350. }
  351. }
  352. return topBottom > 0 && leftRight == 0;
  353. }
  354. bool SCH_SHEET::HasUndefinedPins() const
  355. {
  356. for( SCH_SHEET_PIN* pin : m_pins )
  357. {
  358. /* Search the schematic for a hierarchical label corresponding to this sheet label. */
  359. const SCH_HIERLABEL* HLabel = nullptr;
  360. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_HIER_LABEL_T ) )
  361. {
  362. if( !pin->GetText().Cmp( static_cast<SCH_HIERLABEL*>( aItem )->GetText() ) )
  363. {
  364. HLabel = static_cast<SCH_HIERLABEL*>( aItem );
  365. break;
  366. }
  367. }
  368. if( HLabel == nullptr ) // Corresponding hierarchical label not found.
  369. return true;
  370. }
  371. return false;
  372. }
  373. int bumpToNextGrid( const int aVal, const int aDirection )
  374. {
  375. constexpr int gridSize = schIUScale.MilsToIU( 50 );
  376. int base = aVal / gridSize;
  377. int excess = abs( aVal % gridSize );
  378. if( aDirection > 0 )
  379. {
  380. return ( base + 1 ) * gridSize;
  381. }
  382. else if( excess > 0 )
  383. {
  384. return ( base ) * gridSize;
  385. }
  386. else
  387. {
  388. return ( base - 1 ) * gridSize;
  389. }
  390. }
  391. int SCH_SHEET::GetMinWidth( bool aFromLeft ) const
  392. {
  393. int pinsLeft = m_pos.x + m_size.x;
  394. int pinsRight = m_pos.x;
  395. for( size_t i = 0; i < m_pins.size(); i++ )
  396. {
  397. SHEET_SIDE edge = m_pins[i]->GetSide();
  398. if( edge == SHEET_SIDE::TOP || edge == SHEET_SIDE::BOTTOM )
  399. {
  400. BOX2I pinRect = m_pins[i]->GetBoundingBox();
  401. pinsLeft = std::min( pinsLeft, pinRect.GetLeft() );
  402. pinsRight = std::max( pinsRight, pinRect.GetRight() );
  403. }
  404. }
  405. pinsLeft = bumpToNextGrid( pinsLeft, -1 );
  406. pinsRight = bumpToNextGrid( pinsRight, 1 );
  407. int pinMinWidth;
  408. if( pinsLeft >= pinsRight )
  409. pinMinWidth = 0;
  410. else if( aFromLeft )
  411. pinMinWidth = pinsRight - m_pos.x;
  412. else
  413. pinMinWidth = m_pos.x + m_size.x - pinsLeft;
  414. return std::max( pinMinWidth, schIUScale.MilsToIU( MIN_SHEET_WIDTH ) );
  415. }
  416. int SCH_SHEET::GetMinHeight( bool aFromTop ) const
  417. {
  418. int pinsTop = m_pos.y + m_size.y;
  419. int pinsBottom = m_pos.y;
  420. for( size_t i = 0; i < m_pins.size(); i++ )
  421. {
  422. SHEET_SIDE edge = m_pins[i]->GetSide();
  423. if( edge == SHEET_SIDE::RIGHT || edge == SHEET_SIDE::LEFT )
  424. {
  425. BOX2I pinRect = m_pins[i]->GetBoundingBox();
  426. pinsTop = std::min( pinsTop, pinRect.GetTop() );
  427. pinsBottom = std::max( pinsBottom, pinRect.GetBottom() );
  428. }
  429. }
  430. pinsTop = bumpToNextGrid( pinsTop, -1 );
  431. pinsBottom = bumpToNextGrid( pinsBottom, 1 );
  432. int pinMinHeight;
  433. if( pinsTop >= pinsBottom )
  434. pinMinHeight = 0;
  435. else if( aFromTop )
  436. pinMinHeight = pinsBottom - m_pos.y;
  437. else
  438. pinMinHeight = m_pos.y + m_size.y - pinsTop;
  439. return std::max( pinMinHeight, schIUScale.MilsToIU( MIN_SHEET_HEIGHT ) );
  440. }
  441. void SCH_SHEET::CleanupSheet()
  442. {
  443. std::vector<SCH_SHEET_PIN*> pins = m_pins;
  444. m_pins.clear();
  445. for( SCH_SHEET_PIN* pin : pins )
  446. {
  447. /* Search the schematic for a hierarchical label corresponding to this sheet label. */
  448. const SCH_HIERLABEL* HLabel = nullptr;
  449. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_HIER_LABEL_T ) )
  450. {
  451. if( pin->GetText().CmpNoCase( static_cast<SCH_HIERLABEL*>( aItem )->GetText() ) == 0 )
  452. {
  453. HLabel = static_cast<SCH_HIERLABEL*>( aItem );
  454. break;
  455. }
  456. }
  457. if( HLabel )
  458. m_pins.push_back( pin );
  459. }
  460. }
  461. SCH_SHEET_PIN* SCH_SHEET::GetPin( const VECTOR2I& aPosition )
  462. {
  463. for( SCH_SHEET_PIN* pin : m_pins )
  464. {
  465. if( pin->HitTest( aPosition ) )
  466. return pin;
  467. }
  468. return nullptr;
  469. }
  470. int SCH_SHEET::GetPenWidth() const
  471. {
  472. if( GetBorderWidth() > 0 )
  473. return GetBorderWidth();
  474. if( Schematic() )
  475. return Schematic()->Settings().m_DefaultLineWidth;
  476. return schIUScale.MilsToIU( DEFAULT_LINE_WIDTH_MILS );
  477. }
  478. void SCH_SHEET::AutoplaceFields( SCH_SCREEN* aScreen, bool /* aManual */ )
  479. {
  480. VECTOR2I textSize = m_fields[SHEETNAME].GetTextSize();
  481. int borderMargin = KiROUND( GetPenWidth() / 2.0 ) + 4;
  482. int margin = borderMargin + KiROUND( std::max( textSize.x, textSize.y ) * 0.5 );
  483. if( IsVerticalOrientation() )
  484. {
  485. m_fields[SHEETNAME].SetTextPos( m_pos + VECTOR2I( -margin, m_size.y ) );
  486. m_fields[ SHEETNAME ].SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  487. m_fields[ SHEETNAME ].SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  488. m_fields[ SHEETNAME ].SetTextAngle( ANGLE_VERTICAL );
  489. }
  490. else
  491. {
  492. m_fields[SHEETNAME].SetTextPos( m_pos + VECTOR2I( 0, -margin ) );
  493. m_fields[ SHEETNAME ].SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  494. m_fields[ SHEETNAME ].SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  495. m_fields[ SHEETNAME ].SetTextAngle( ANGLE_HORIZONTAL );
  496. }
  497. textSize = m_fields[ SHEETFILENAME ].GetTextSize();
  498. margin = borderMargin + KiROUND( std::max( textSize.x, textSize.y ) * 0.4 );
  499. if( IsVerticalOrientation() )
  500. {
  501. m_fields[SHEETFILENAME].SetTextPos( m_pos + VECTOR2I( m_size.x + margin, m_size.y ) );
  502. m_fields[ SHEETFILENAME ].SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  503. m_fields[ SHEETFILENAME ].SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  504. m_fields[ SHEETFILENAME ].SetTextAngle( ANGLE_VERTICAL );
  505. }
  506. else
  507. {
  508. m_fields[SHEETFILENAME].SetTextPos( m_pos + VECTOR2I( 0, m_size.y + margin ) );
  509. m_fields[ SHEETFILENAME ].SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  510. m_fields[ SHEETFILENAME ].SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  511. m_fields[ SHEETFILENAME ].SetTextAngle( ANGLE_HORIZONTAL );
  512. }
  513. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  514. }
  515. void SCH_SHEET::ViewGetLayers( int aLayers[], int& aCount ) const
  516. {
  517. aCount = 8;
  518. aLayers[0] = LAYER_DANGLING; // Sheet pins are drawn by their parent sheet, so the
  519. // parent needs to draw to LAYER_DANGLING
  520. aLayers[1] = LAYER_HIERLABEL;
  521. aLayers[2] = LAYER_SHEETNAME;
  522. aLayers[3] = LAYER_SHEETFILENAME;
  523. aLayers[4] = LAYER_SHEETFIELDS;
  524. aLayers[5] = LAYER_SHEET;
  525. aLayers[6] = LAYER_SHEET_BACKGROUND;
  526. aLayers[7] = LAYER_SELECTION_SHADOWS;
  527. }
  528. const BOX2I SCH_SHEET::GetBodyBoundingBox() const
  529. {
  530. VECTOR2I end;
  531. BOX2I box( m_pos, m_size );
  532. int lineWidth = GetPenWidth();
  533. int textLength = 0;
  534. // Calculate bounding box X size:
  535. end.x = std::max( m_size.x, textLength );
  536. // Calculate bounding box pos:
  537. end.y = m_size.y;
  538. end += m_pos;
  539. box.SetEnd( end );
  540. box.Inflate( lineWidth / 2 );
  541. return box;
  542. }
  543. const BOX2I SCH_SHEET::GetBoundingBox() const
  544. {
  545. BOX2I bbox = GetBodyBoundingBox();
  546. for( const SCH_FIELD& field : m_fields )
  547. bbox.Merge( field.GetBoundingBox() );
  548. return bbox;
  549. }
  550. VECTOR2I SCH_SHEET::GetRotationCenter() const
  551. {
  552. BOX2I box( m_pos, m_size );
  553. return box.GetCenter();
  554. }
  555. int SCH_SHEET::SymbolCount() const
  556. {
  557. int n = 0;
  558. if( m_screen )
  559. {
  560. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_SYMBOL_T ) )
  561. {
  562. SCH_SYMBOL* symbol = (SCH_SYMBOL*) aItem;
  563. if( symbol->GetField( VALUE_FIELD )->GetText().GetChar( 0 ) != '#' )
  564. n++;
  565. }
  566. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  567. n += static_cast<const SCH_SHEET*>( aItem )->SymbolCount();
  568. }
  569. return n;
  570. }
  571. bool SCH_SHEET::SearchHierarchy( const wxString& aFilename, SCH_SCREEN** aScreen )
  572. {
  573. if( m_screen )
  574. {
  575. // Only check the root sheet once and don't recurse.
  576. if( !GetParent() )
  577. {
  578. if( m_screen && m_screen->GetFileName().Cmp( aFilename ) == 0 )
  579. {
  580. *aScreen = m_screen;
  581. return true;
  582. }
  583. }
  584. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  585. {
  586. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
  587. SCH_SCREEN* screen = sheet->m_screen;
  588. // Must use the screen's path (which is always absolute) rather than the
  589. // sheet's (which could be relative).
  590. if( screen && screen->GetFileName().Cmp( aFilename ) == 0 )
  591. {
  592. *aScreen = screen;
  593. return true;
  594. }
  595. if( sheet->SearchHierarchy( aFilename, aScreen ) )
  596. return true;
  597. }
  598. }
  599. return false;
  600. }
  601. bool SCH_SHEET::LocatePathOfScreen( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aList )
  602. {
  603. if( m_screen )
  604. {
  605. aList->push_back( this );
  606. if( m_screen == aScreen )
  607. return true;
  608. for( EDA_ITEM* item : m_screen->Items().OfType( SCH_SHEET_T ) )
  609. {
  610. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  611. if( sheet->LocatePathOfScreen( aScreen, aList ) )
  612. return true;
  613. }
  614. aList->pop_back();
  615. }
  616. return false;
  617. }
  618. int SCH_SHEET::CountSheets() const
  619. {
  620. int count = 1; //1 = this!!
  621. if( m_screen )
  622. {
  623. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  624. count += static_cast<SCH_SHEET*>( aItem )->CountSheets();
  625. }
  626. return count;
  627. }
  628. void SCH_SHEET::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  629. {
  630. // Don't use GetShownText(); we want to see the variable references here
  631. aList.emplace_back( _( "Sheet Name" ),
  632. KIUI::EllipsizeStatusText( aFrame, m_fields[ SHEETNAME ].GetText() ) );
  633. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  634. {
  635. SCH_SHEET_PATH path = schframe->GetCurrentSheet();
  636. path.push_back( this );
  637. aList.emplace_back( _( "Hierarchical Path" ), path.PathHumanReadable( false, true ) );
  638. }
  639. // Don't use GetShownText(); we want to see the variable references here
  640. aList.emplace_back( _( "File Name" ),
  641. KIUI::EllipsizeStatusText( aFrame, m_fields[ SHEETFILENAME ].GetText() ) );
  642. }
  643. void SCH_SHEET::SetPositionIgnoringPins( const VECTOR2I& aPosition )
  644. {
  645. VECTOR2I delta = aPosition - m_pos;
  646. m_pos = aPosition;
  647. for( SCH_FIELD& field : m_fields )
  648. field.Move( delta );
  649. }
  650. void SCH_SHEET::Move( const VECTOR2I& aMoveVector )
  651. {
  652. m_pos += aMoveVector;
  653. for( SCH_SHEET_PIN* pin : m_pins )
  654. pin->Move( aMoveVector );
  655. for( SCH_FIELD& field : m_fields )
  656. field.Move( aMoveVector );
  657. }
  658. void SCH_SHEET::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
  659. {
  660. VECTOR2I prev = m_pos;
  661. RotatePoint( m_pos, aCenter, aRotateCCW ? ANGLE_270 : ANGLE_90 );
  662. RotatePoint( &m_size.x, &m_size.y, aRotateCCW ? ANGLE_270 : ANGLE_90 );
  663. if( m_size.x < 0 )
  664. {
  665. m_pos.x += m_size.x;
  666. m_size.x = -m_size.x;
  667. }
  668. if( m_size.y < 0 )
  669. {
  670. m_pos.y += m_size.y;
  671. m_size.y = -m_size.y;
  672. }
  673. // Pins must be rotated first as that's how we determine vertical vs horizontal
  674. // orientation for auto-placement
  675. for( SCH_SHEET_PIN* sheetPin : m_pins )
  676. sheetPin->Rotate( aCenter, aRotateCCW );
  677. if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
  678. {
  679. AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
  680. }
  681. else
  682. {
  683. // Move the fields to the new position because the parent itself has moved.
  684. for( SCH_FIELD& field : m_fields )
  685. {
  686. VECTOR2I pos = field.GetTextPos();
  687. pos.x -= prev.x - m_pos.x;
  688. pos.y -= prev.y - m_pos.y;
  689. field.SetTextPos( pos );
  690. }
  691. }
  692. }
  693. void SCH_SHEET::MirrorVertically( int aCenter )
  694. {
  695. int dy = m_pos.y;
  696. MIRROR( m_pos.y, aCenter );
  697. m_pos.y -= m_size.y;
  698. dy -= m_pos.y; // 0,dy is the move vector for this transform
  699. for( SCH_SHEET_PIN* sheetPin : m_pins )
  700. sheetPin->MirrorVertically( aCenter );
  701. for( SCH_FIELD& field : m_fields )
  702. {
  703. VECTOR2I pos = field.GetTextPos();
  704. pos.y -= dy;
  705. field.SetTextPos( pos );
  706. }
  707. }
  708. void SCH_SHEET::MirrorHorizontally( int aCenter )
  709. {
  710. int dx = m_pos.x;
  711. MIRROR( m_pos.x, aCenter );
  712. m_pos.x -= m_size.x;
  713. dx -= m_pos.x; // dx,0 is the move vector for this transform
  714. for( SCH_SHEET_PIN* sheetPin : m_pins )
  715. sheetPin->MirrorHorizontally( aCenter );
  716. for( SCH_FIELD& field : m_fields )
  717. {
  718. VECTOR2I pos = field.GetTextPos();
  719. pos.x -= dx;
  720. field.SetTextPos( pos );
  721. }
  722. }
  723. void SCH_SHEET::SetPosition( const VECTOR2I& aPosition )
  724. {
  725. // Remember the sheet and all pin sheet positions must be
  726. // modified. So use Move function to do that.
  727. Move( aPosition - m_pos );
  728. }
  729. void SCH_SHEET::Resize( const VECTOR2I& aSize )
  730. {
  731. if( aSize == m_size )
  732. return;
  733. m_size = aSize;
  734. // Move the fields if we're in autoplace mode
  735. if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
  736. AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
  737. // Move the sheet labels according to the new sheet size.
  738. for( SCH_SHEET_PIN* sheetPin : m_pins )
  739. sheetPin->ConstrainOnEdge( sheetPin->GetPosition(), false );
  740. }
  741. bool SCH_SHEET::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  742. {
  743. // Sheets are searchable via the child field and pin item text.
  744. return false;
  745. }
  746. void SCH_SHEET::renumberPins()
  747. {
  748. int id = 2;
  749. for( SCH_SHEET_PIN* pin : m_pins )
  750. {
  751. pin->SetNumber( id );
  752. id++;
  753. }
  754. }
  755. SCH_SHEET_PATH SCH_SHEET::findSelf() const
  756. {
  757. wxCHECK_MSG( Schematic(), SCH_SHEET_PATH(), "Can't call findSelf without a schematic" );
  758. SCH_SHEET_PATH sheetPath = Schematic()->CurrentSheet();
  759. while( !sheetPath.empty() && sheetPath.Last() != this )
  760. sheetPath.pop_back();
  761. if( sheetPath.empty() )
  762. {
  763. // If we weren't in the hierarchy, then we must be a child of the current sheet.
  764. sheetPath = Schematic()->CurrentSheet();
  765. sheetPath.push_back( const_cast<SCH_SHEET*>( this ) );
  766. }
  767. return sheetPath;
  768. }
  769. void SCH_SHEET::GetEndPoints( std::vector <DANGLING_END_ITEM>& aItemList )
  770. {
  771. for( SCH_SHEET_PIN* sheetPin : m_pins )
  772. {
  773. wxCHECK2_MSG( sheetPin->Type() == SCH_SHEET_PIN_T, continue,
  774. wxT( "Invalid item in schematic sheet pin list. Bad programmer!" ) );
  775. sheetPin->GetEndPoints( aItemList );
  776. }
  777. }
  778. bool SCH_SHEET::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemListByType,
  779. std::vector<DANGLING_END_ITEM>& aItemListByPos,
  780. const SCH_SHEET_PATH* aPath )
  781. {
  782. bool changed = false;
  783. for( SCH_SHEET_PIN* sheetPin : m_pins )
  784. changed |= sheetPin->UpdateDanglingState( aItemListByType, aItemListByPos );
  785. return changed;
  786. }
  787. bool SCH_SHEET::HasConnectivityChanges( const SCH_ITEM* aItem,
  788. const SCH_SHEET_PATH* aInstance ) const
  789. {
  790. // Do not compare to ourself.
  791. if( aItem == this )
  792. return false;
  793. const SCH_SHEET* sheet = dynamic_cast<const SCH_SHEET*>( aItem );
  794. // Don't compare against a different SCH_ITEM.
  795. wxCHECK( sheet, false );
  796. if( GetPosition() != sheet->GetPosition() )
  797. return true;
  798. // Technically this cannot happen because undo/redo does not support reloading sheet
  799. // file association changes. This was just added so that it doesn't get missed should
  800. // we ever fix the undo/redo issue.
  801. if( ( GetFileName() != sheet->GetFileName() ) || ( GetName() != sheet->GetName() ) )
  802. return true;
  803. if( m_pins.size() != sheet->m_pins.size() )
  804. return true;
  805. for( size_t i = 0; i < m_pins.size(); i++ )
  806. {
  807. if( m_pins[i]->HasConnectivityChanges( sheet->m_pins[i] ) )
  808. return true;
  809. }
  810. return false;
  811. }
  812. std::vector<VECTOR2I> SCH_SHEET::GetConnectionPoints() const
  813. {
  814. std::vector<VECTOR2I> retval;
  815. for( SCH_SHEET_PIN* sheetPin : m_pins )
  816. retval.push_back( sheetPin->GetPosition() );
  817. return retval;
  818. }
  819. INSPECT_RESULT SCH_SHEET::Visit( INSPECTOR aInspector, void* testData,
  820. const std::vector<KICAD_T>& aScanTypes )
  821. {
  822. for( KICAD_T scanType : aScanTypes )
  823. {
  824. // If caller wants to inspect my type
  825. if( scanType == SCH_LOCATE_ANY_T || scanType == Type() )
  826. {
  827. if( INSPECT_RESULT::QUIT == aInspector( this, nullptr ) )
  828. return INSPECT_RESULT::QUIT;
  829. }
  830. if( scanType == SCH_LOCATE_ANY_T || scanType == SCH_FIELD_T )
  831. {
  832. // Test the sheet fields.
  833. for( SCH_FIELD& field : m_fields )
  834. {
  835. if( INSPECT_RESULT::QUIT == aInspector( &field, this ) )
  836. return INSPECT_RESULT::QUIT;
  837. }
  838. }
  839. if( scanType == SCH_LOCATE_ANY_T || scanType == SCH_SHEET_PIN_T )
  840. {
  841. // Test the sheet labels.
  842. for( SCH_SHEET_PIN* sheetPin : m_pins )
  843. {
  844. if( INSPECT_RESULT::QUIT == aInspector( sheetPin, this ) )
  845. return INSPECT_RESULT::QUIT;
  846. }
  847. }
  848. }
  849. return INSPECT_RESULT::CONTINUE;
  850. }
  851. void SCH_SHEET::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
  852. {
  853. for( SCH_FIELD& field : m_fields )
  854. aFunction( &field );
  855. for( SCH_SHEET_PIN* pin : m_pins )
  856. aFunction( pin );
  857. }
  858. wxString SCH_SHEET::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  859. {
  860. return wxString::Format( _( "Hierarchical Sheet %s" ),
  861. KIUI::EllipsizeMenuText( m_fields[ SHEETNAME ].GetText() ) );
  862. }
  863. BITMAPS SCH_SHEET::GetMenuImage() const
  864. {
  865. return BITMAPS::add_hierarchical_subsheet;
  866. }
  867. bool SCH_SHEET::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  868. {
  869. BOX2I rect = GetBodyBoundingBox();
  870. rect.Inflate( aAccuracy );
  871. return rect.Contains( aPosition );
  872. }
  873. bool SCH_SHEET::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  874. {
  875. BOX2I rect = aRect;
  876. rect.Inflate( aAccuracy );
  877. if( aContained )
  878. return rect.Contains( GetBodyBoundingBox() );
  879. return rect.Intersects( GetBodyBoundingBox() );
  880. }
  881. void SCH_SHEET::Plot( PLOTTER* aPlotter, bool aBackground,
  882. const SCH_PLOT_SETTINGS& aPlotSettings ) const
  883. {
  884. if( aBackground && !aPlotter->GetColorMode() )
  885. return;
  886. SCH_RENDER_SETTINGS* cfg = static_cast<SCH_RENDER_SETTINGS*>( aPlotter->RenderSettings() );
  887. COLOR4D borderColor = GetBorderColor();
  888. COLOR4D backgroundColor = GetBackgroundColor();
  889. if( cfg->m_OverrideItemColors || borderColor == COLOR4D::UNSPECIFIED )
  890. borderColor = aPlotter->RenderSettings()->GetLayerColor( LAYER_SHEET );
  891. if( cfg->m_OverrideItemColors || backgroundColor == COLOR4D::UNSPECIFIED )
  892. backgroundColor = aPlotter->RenderSettings()->GetLayerColor( LAYER_SHEET_BACKGROUND );
  893. if( aBackground && backgroundColor.a > 0.0 )
  894. {
  895. aPlotter->SetColor( backgroundColor );
  896. aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::FILLED_SHAPE, 1 );
  897. }
  898. else
  899. {
  900. aPlotter->SetColor( borderColor );
  901. int penWidth = std::max( GetPenWidth(), aPlotter->RenderSettings()->GetMinPenWidth() );
  902. aPlotter->Rect( m_pos, m_pos + m_size, FILL_T::NO_FILL, penWidth );
  903. }
  904. // Make the sheet object a clickable hyperlink (e.g. for PDF plotter)
  905. std::vector<wxString> properties;
  906. properties.emplace_back( EDA_TEXT::GotoPageHref( findSelf().GetPageNumber() ) );
  907. for( const SCH_FIELD& field : GetFields() )
  908. {
  909. properties.emplace_back( wxString::Format( wxT( "!%s = %s" ),
  910. field.GetName(),
  911. field.GetShownText( false ) ) );
  912. }
  913. aPlotter->HyperlinkMenu( GetBoundingBox(), properties );
  914. // Plot sheet pins
  915. for( SCH_SHEET_PIN* sheetPin : m_pins )
  916. sheetPin->Plot( aPlotter, aBackground, aPlotSettings );
  917. // Plot the fields
  918. for( const SCH_FIELD& field : m_fields )
  919. field.Plot( aPlotter, aBackground, aPlotSettings );
  920. }
  921. void SCH_SHEET::Print( const SCH_RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  922. {
  923. wxDC* DC = aSettings->GetPrintDC();
  924. VECTOR2I pos = m_pos + aOffset;
  925. int lineWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
  926. COLOR4D border = GetBorderColor();
  927. COLOR4D background = GetBackgroundColor();
  928. if( aSettings->m_OverrideItemColors || border == COLOR4D::UNSPECIFIED )
  929. border = aSettings->GetLayerColor( LAYER_SHEET );
  930. if( aSettings->m_OverrideItemColors || background == COLOR4D::UNSPECIFIED )
  931. background = aSettings->GetLayerColor( LAYER_SHEET_BACKGROUND );
  932. if( GetGRForceBlackPenState() ) // printing in black & white
  933. background = COLOR4D::UNSPECIFIED;
  934. if( background.a > 0.0 )
  935. GRFilledRect( DC, pos, pos + m_size, 0, background, background );
  936. GRRect( DC, pos, pos + m_size, lineWidth, border );
  937. for( SCH_FIELD& field : m_fields )
  938. field.Print( aSettings, aOffset );
  939. for( SCH_SHEET_PIN* sheetPin : m_pins )
  940. sheetPin->Print( aSettings, aOffset );
  941. }
  942. SCH_SHEET& SCH_SHEET::operator=( const SCH_ITEM& aItem )
  943. {
  944. wxCHECK_MSG( Type() == aItem.Type(), *this,
  945. wxT( "Cannot assign object type " ) + aItem.GetClass() + wxT( " to type " ) +
  946. GetClass() );
  947. if( &aItem != this )
  948. {
  949. SCH_ITEM::operator=( aItem );
  950. SCH_SHEET* sheet = (SCH_SHEET*) &aItem;
  951. m_pos = sheet->m_pos;
  952. m_size = sheet->m_size;
  953. m_fields = sheet->m_fields;
  954. for( SCH_SHEET_PIN* pin : sheet->m_pins )
  955. {
  956. m_pins.emplace_back( new SCH_SHEET_PIN( *pin ) );
  957. m_pins.back()->SetParent( this );
  958. }
  959. for( const SCH_SHEET_INSTANCE& instance : sheet->m_instances )
  960. m_instances.emplace_back( instance );
  961. }
  962. return *this;
  963. }
  964. bool SCH_SHEET::operator <( const SCH_ITEM& aItem ) const
  965. {
  966. if( Type() != aItem.Type() )
  967. return Type() < aItem.Type();
  968. auto sheet = static_cast<const SCH_SHEET*>( &aItem );
  969. if (m_fields[ SHEETNAME ].GetText() != sheet->m_fields[ SHEETNAME ].GetText() )
  970. return m_fields[ SHEETNAME ].GetText() < sheet->m_fields[ SHEETNAME ].GetText();
  971. if (m_fields[ SHEETFILENAME ].GetText() != sheet->m_fields[ SHEETFILENAME ].GetText() )
  972. return m_fields[ SHEETFILENAME ].GetText() < sheet->m_fields[ SHEETFILENAME ].GetText();
  973. return false;
  974. }
  975. void SCH_SHEET::RemoveInstance( const KIID_PATH& aInstancePath )
  976. {
  977. // Search for an existing path and remove it if found (should not occur)
  978. for( unsigned ii = 0; ii < m_instances.size(); ii++ )
  979. {
  980. if( m_instances[ii].m_Path == aInstancePath )
  981. {
  982. wxLogTrace( traceSchSheetPaths, "Removing sheet instance:\n"
  983. " sheet path %s\n"
  984. " page %s, from project %s.",
  985. aInstancePath.AsString(),
  986. m_instances[ii].m_PageNumber,
  987. m_instances[ii].m_ProjectName );
  988. m_instances.erase( m_instances.begin() + ii );
  989. ii--;
  990. }
  991. }
  992. }
  993. void SCH_SHEET::AddInstance( const SCH_SHEET_INSTANCE& aInstance )
  994. {
  995. SCH_SHEET_INSTANCE oldInstance;
  996. if( getInstance( oldInstance, aInstance.m_Path ) )
  997. RemoveInstance( aInstance.m_Path );
  998. m_instances.emplace_back( aInstance );
  999. }
  1000. bool SCH_SHEET::addInstance( const SCH_SHEET_PATH& aSheetPath )
  1001. {
  1002. wxCHECK( aSheetPath.IsFullPath(), false );
  1003. wxCHECK( !aSheetPath.Last() || ( aSheetPath.Last()->m_Uuid != m_Uuid ), false );
  1004. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  1005. {
  1006. // if aSheetPath is found, nothing to do:
  1007. if( instance.m_Path == aSheetPath.Path() )
  1008. return false;
  1009. }
  1010. wxLogTrace( traceSchSheetPaths, wxT( "Adding instance `%s` to sheet `%s`." ),
  1011. aSheetPath.Path().AsString(),
  1012. ( GetName().IsEmpty() ) ? wxString( wxT( "root" ) ) : GetName() );
  1013. SCH_SHEET_INSTANCE instance;
  1014. instance.m_Path = aSheetPath.Path();
  1015. // This entry does not exist: add it with an empty page number.
  1016. m_instances.emplace_back( instance );
  1017. return true;
  1018. }
  1019. bool SCH_SHEET::getInstance( SCH_SHEET_INSTANCE& aInstance, const KIID_PATH& aSheetPath,
  1020. bool aTestFromEnd ) const
  1021. {
  1022. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  1023. {
  1024. if( !aTestFromEnd )
  1025. {
  1026. if( instance.m_Path == aSheetPath )
  1027. {
  1028. aInstance = instance;
  1029. return true;
  1030. }
  1031. }
  1032. else if( instance.m_Path.EndsWith( aSheetPath ) )
  1033. {
  1034. aInstance = instance;
  1035. return true;
  1036. }
  1037. }
  1038. return false;
  1039. }
  1040. bool SCH_SHEET::HasRootInstance() const
  1041. {
  1042. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  1043. {
  1044. if( instance.m_Path.size() == 0 )
  1045. return true;
  1046. }
  1047. return false;
  1048. }
  1049. const SCH_SHEET_INSTANCE& SCH_SHEET::GetRootInstance() const
  1050. {
  1051. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  1052. {
  1053. if( instance.m_Path.size() == 0 )
  1054. return instance;
  1055. }
  1056. wxFAIL;
  1057. static SCH_SHEET_INSTANCE dummy;
  1058. return dummy;
  1059. }
  1060. wxString SCH_SHEET::getPageNumber( const SCH_SHEET_PATH& aSheetPath ) const
  1061. {
  1062. wxCHECK( aSheetPath.IsFullPath(), wxEmptyString );
  1063. wxCHECK( !aSheetPath.Last() || ( aSheetPath.Last()->m_Uuid != m_Uuid ), wxEmptyString );
  1064. wxString pageNumber;
  1065. KIID_PATH path = aSheetPath.Path();
  1066. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  1067. {
  1068. if( instance.m_Path == path )
  1069. {
  1070. pageNumber = instance.m_PageNumber;
  1071. break;
  1072. }
  1073. }
  1074. return pageNumber;
  1075. }
  1076. void SCH_SHEET::setPageNumber( const SCH_SHEET_PATH& aSheetPath, const wxString& aPageNumber )
  1077. {
  1078. wxCHECK( aSheetPath.IsFullPath(), /* void */ );
  1079. wxCHECK( !aSheetPath.Last() || ( aSheetPath.Last()->m_Uuid != m_Uuid ), /* void */ );
  1080. KIID_PATH path = aSheetPath.Path();
  1081. for( SCH_SHEET_INSTANCE& instance : m_instances )
  1082. {
  1083. if( instance.m_Path == path )
  1084. {
  1085. instance.m_PageNumber = aPageNumber;
  1086. break;
  1087. }
  1088. }
  1089. }
  1090. int SCH_SHEET::ComparePageNum( const wxString& aPageNumberA, const wxString& aPageNumberB )
  1091. {
  1092. if( aPageNumberA == aPageNumberB )
  1093. return 0; // A == B
  1094. // First sort numerically if the page numbers are integers
  1095. long pageA, pageB;
  1096. bool isIntegerPageA = aPageNumberA.ToLong( &pageA );
  1097. bool isIntegerPageB = aPageNumberB.ToLong( &pageB );
  1098. if( isIntegerPageA && isIntegerPageB )
  1099. {
  1100. if( pageA < pageB )
  1101. return -1; //A < B
  1102. else
  1103. return 1; // A > B
  1104. }
  1105. // Numerical page numbers always before strings
  1106. if( isIntegerPageA )
  1107. return -1; //A < B
  1108. else if( isIntegerPageB )
  1109. return 1; // A > B
  1110. // If not numeric, then sort as strings using natural sort
  1111. int result = StrNumCmp( aPageNumberA, aPageNumberB );
  1112. // Divide by zero bad.
  1113. wxCHECK( result != 0, 0 );
  1114. result = result / std::abs( result );
  1115. return result;
  1116. }
  1117. bool SCH_SHEET::operator==( const SCH_ITEM& aOther ) const
  1118. {
  1119. if( Type() != aOther.Type() )
  1120. return false;
  1121. const SCH_SHEET* other = static_cast<const SCH_SHEET*>( &aOther );
  1122. if( m_pos != other->m_pos )
  1123. return false;
  1124. if( m_size != other->m_size )
  1125. return false;
  1126. if( GetBorderColor() != other->GetBorderColor() )
  1127. return false;
  1128. if( GetBackgroundColor() != other->GetBackgroundColor() )
  1129. return false;
  1130. if( GetBorderWidth() != other->GetBorderWidth() )
  1131. return false;
  1132. if( GetFields().size() != other->GetFields().size() )
  1133. return false;
  1134. for( size_t i = 0; i < GetFields().size(); ++i )
  1135. {
  1136. if( !( GetFields()[i] == other->GetFields()[i] ) )
  1137. return false;
  1138. }
  1139. return true;
  1140. }
  1141. double SCH_SHEET::Similarity( const SCH_ITEM& aOther ) const
  1142. {
  1143. if( Type() != aOther.Type() )
  1144. return 0.0;
  1145. const SCH_SHEET* other = static_cast<const SCH_SHEET*>( &aOther );
  1146. if( m_screen->GetFileName() == other->m_screen->GetFileName() )
  1147. return 1.0;
  1148. return 0.0;
  1149. }
  1150. #if defined(DEBUG)
  1151. void SCH_SHEET::Show( int nestLevel, std::ostream& os ) const
  1152. {
  1153. // XML output:
  1154. wxString s = GetClass();
  1155. NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str() << ">" << " sheet_name=\""
  1156. << TO_UTF8( m_fields[ SHEETNAME ].GetText() ) << '"' << ">\n";
  1157. // show all the pins, and check the linked list integrity
  1158. for( SCH_SHEET_PIN* sheetPin : m_pins )
  1159. sheetPin->Show( nestLevel + 1, os );
  1160. NestedSpace( nestLevel, os ) << "</" << s.Lower().mb_str() << ">\n" << std::flush;
  1161. }
  1162. #endif
  1163. static struct SCH_SHEET_DESC
  1164. {
  1165. SCH_SHEET_DESC()
  1166. {
  1167. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1168. REGISTER_TYPE( SCH_SHEET );
  1169. propMgr.InheritsAfter( TYPE_HASH( SCH_SHEET ), TYPE_HASH( SCH_ITEM ) );
  1170. propMgr.AddProperty( new PROPERTY<SCH_SHEET, wxString>( _HKI( "Sheet Name" ),
  1171. &SCH_SHEET::SetName, &SCH_SHEET::GetName ) );
  1172. propMgr.AddProperty( new PROPERTY<SCH_SHEET, int>( _HKI( "Border Width" ),
  1173. &SCH_SHEET::SetBorderWidth, &SCH_SHEET::GetBorderWidth,
  1174. PROPERTY_DISPLAY::PT_SIZE ) );
  1175. propMgr.AddProperty( new PROPERTY<SCH_SHEET, COLOR4D>( _HKI( "Border Color" ),
  1176. &SCH_SHEET::SetBorderColor, &SCH_SHEET::GetBorderColor ) );
  1177. propMgr.AddProperty( new PROPERTY<SCH_SHEET, COLOR4D>( _HKI( "Background Color" ),
  1178. &SCH_SHEET::SetBackgroundColor, &SCH_SHEET::GetBackgroundColor ) );
  1179. }
  1180. } _SCH_SHEET_DESC;