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.

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