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.

1177 lines
31 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-2020 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 <core/mirror.h>
  25. #include <sch_draw_panel.h>
  26. #include <gr_text.h>
  27. #include <trigo.h>
  28. #include <sch_edit_frame.h>
  29. #include <plotter.h>
  30. #include <kicad_string.h>
  31. #include <widgets/msgpanel.h>
  32. #include <math/util.h> // for KiROUND
  33. #include <sch_sheet.h>
  34. #include <sch_sheet_path.h>
  35. #include <sch_component.h>
  36. #include <sch_painter.h>
  37. #include <schematic.h>
  38. #include <settings/color_settings.h>
  39. #include <trace_helpers.h>
  40. #include <pgm_base.h>
  41. const wxString SCH_SHEET::GetDefaultFieldName( int aFieldNdx )
  42. {
  43. static void* locale = nullptr;
  44. static wxString sheetnameDefault;
  45. static wxString sheetfilenameDefault;
  46. static wxString fieldDefault;
  47. // Fetching translations can take a surprising amount of time when loading libraries,
  48. // so only do it when necessary.
  49. if( Pgm().GetLocale() != locale )
  50. {
  51. sheetnameDefault = _( "Sheet name" );
  52. sheetfilenameDefault = _( "Sheet file" );
  53. fieldDefault = _( "Field%d" );
  54. locale = Pgm().GetLocale();
  55. }
  56. // Fixed values for the mandatory fields
  57. switch( aFieldNdx )
  58. {
  59. case SHEETNAME: return sheetnameDefault;
  60. case SHEETFILENAME: return sheetfilenameDefault;
  61. default: return wxString::Format( fieldDefault, aFieldNdx );
  62. }
  63. }
  64. SCH_SHEET::SCH_SHEET( EDA_ITEM* aParent, const wxPoint& pos ) :
  65. SCH_ITEM( aParent, SCH_SHEET_T )
  66. {
  67. m_layer = LAYER_SHEET;
  68. m_pos = pos;
  69. m_size = wxSize( Mils2iu( MIN_SHEET_WIDTH ), Mils2iu( MIN_SHEET_HEIGHT ) );
  70. m_screen = NULL;
  71. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  72. {
  73. m_fields.emplace_back( pos, i, this, GetDefaultFieldName( i ) );
  74. m_fields.back().SetVisible( true );
  75. if( i == SHEETNAME )
  76. m_fields.back().SetLayer( LAYER_SHEETNAME );
  77. else if( i == SHEETFILENAME )
  78. m_fields.back().SetLayer( LAYER_SHEETFILENAME );
  79. else
  80. m_fields.back().SetLayer( LAYER_SHEETFIELDS );
  81. }
  82. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  83. m_borderWidth = 0;
  84. m_borderColor = COLOR4D::UNSPECIFIED;
  85. m_backgroundColor = COLOR4D::UNSPECIFIED;
  86. }
  87. SCH_SHEET::SCH_SHEET( const SCH_SHEET& aSheet ) :
  88. SCH_ITEM( aSheet )
  89. {
  90. m_pos = aSheet.m_pos;
  91. m_size = aSheet.m_size;
  92. m_layer = aSheet.m_layer;
  93. const_cast<KIID&>( m_Uuid ) = aSheet.m_Uuid;
  94. m_fields = aSheet.m_fields;
  95. m_fieldsAutoplaced = aSheet.m_fieldsAutoplaced;
  96. m_screen = aSheet.m_screen;
  97. for( SCH_SHEET_PIN* pin : aSheet.m_pins )
  98. {
  99. m_pins.emplace_back( new SCH_SHEET_PIN( *pin ) );
  100. m_pins.back()->SetParent( this );
  101. }
  102. m_borderWidth = aSheet.m_borderWidth;
  103. m_borderColor = aSheet.m_borderColor;
  104. m_backgroundColor = aSheet.m_backgroundColor;
  105. m_instances = aSheet.m_instances;
  106. if( m_screen )
  107. m_screen->IncRefCount();
  108. }
  109. SCH_SHEET::~SCH_SHEET()
  110. {
  111. // also, look at the associated sheet & its reference count
  112. // perhaps it should be deleted also.
  113. if( m_screen )
  114. {
  115. m_screen->DecRefCount();
  116. if( m_screen->GetRefCount() == 0 )
  117. delete m_screen;
  118. }
  119. // We own our pins; delete them
  120. for( SCH_SHEET_PIN* pin : m_pins )
  121. delete pin;
  122. }
  123. EDA_ITEM* SCH_SHEET::Clone() const
  124. {
  125. return new SCH_SHEET( *this );
  126. }
  127. void SCH_SHEET::SetScreen( SCH_SCREEN* aScreen )
  128. {
  129. if( aScreen == m_screen )
  130. return;
  131. if( m_screen != NULL )
  132. {
  133. m_screen->DecRefCount();
  134. if( m_screen->GetRefCount() == 0 )
  135. {
  136. delete m_screen;
  137. m_screen = NULL;
  138. }
  139. }
  140. m_screen = aScreen;
  141. if( m_screen )
  142. m_screen->IncRefCount();
  143. }
  144. int SCH_SHEET::GetScreenCount() const
  145. {
  146. if( m_screen == NULL )
  147. return 0;
  148. return m_screen->GetRefCount();
  149. }
  150. bool SCH_SHEET::IsRootSheet() const
  151. {
  152. wxCHECK_MSG( Schematic(), false, "Can't call IsRootSheet without setting a schematic" );
  153. return &Schematic()->Root() == this;
  154. }
  155. void SCH_SHEET::GetContextualTextVars( wxArrayString* aVars ) const
  156. {
  157. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  158. aVars->push_back( m_fields[i].GetCanonicalName().Upper() );
  159. for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields.size(); ++i )
  160. aVars->push_back( m_fields[i].GetName() );
  161. aVars->push_back( wxT( "#" ) );
  162. aVars->push_back( wxT( "##" ) );
  163. }
  164. bool SCH_SHEET::ResolveTextVar( wxString* token, int aDepth ) const
  165. {
  166. for( int i = 0; i < SHEET_MANDATORY_FIELDS; ++i )
  167. {
  168. if( token->IsSameAs( m_fields[i].GetCanonicalName().Upper() ) )
  169. {
  170. *token = m_fields[i].GetShownText( aDepth + 1 );
  171. return true;
  172. }
  173. }
  174. for( size_t i = SHEET_MANDATORY_FIELDS; i < m_fields.size(); ++i )
  175. {
  176. if( token->IsSameAs( m_fields[i].GetName() ) )
  177. {
  178. *token = m_fields[i].GetShownText( aDepth + 1 );
  179. return true;
  180. }
  181. }
  182. if( token->IsSameAs( wxT( "#" ) ) )
  183. {
  184. for( const SCH_SHEET_PATH& sheet : Schematic()->GetSheets() )
  185. {
  186. if( sheet.Last() == this ) // Current sheet path found
  187. {
  188. *token = wxString::Format( "%s", sheet.GetPageNumber() );
  189. return true;
  190. }
  191. }
  192. }
  193. else if( token->IsSameAs( wxT( "##" ) ) )
  194. {
  195. SCH_SHEET_LIST sheetList = Schematic()->GetSheets();
  196. *token = wxString::Format( wxT( "%d" ), (int) sheetList.size() );
  197. return true;
  198. }
  199. return false;
  200. }
  201. bool SCH_SHEET::UsesDefaultStroke() const
  202. {
  203. return m_borderWidth == 0 && m_borderColor == COLOR4D::UNSPECIFIED;
  204. }
  205. void SCH_SHEET::SwapData( SCH_ITEM* aItem )
  206. {
  207. wxCHECK_RET( aItem->Type() == SCH_SHEET_T,
  208. wxString::Format( wxT( "SCH_SHEET object cannot swap data with %s object." ),
  209. aItem->GetClass() ) );
  210. SCH_SHEET* sheet = ( SCH_SHEET* ) aItem;
  211. std::swap( m_pos, sheet->m_pos );
  212. std::swap( m_size, sheet->m_size );
  213. m_fields.swap( sheet->m_fields );
  214. std::swap( m_fieldsAutoplaced, sheet->m_fieldsAutoplaced );
  215. m_pins.swap( sheet->m_pins );
  216. // Update parent pointers after swapping.
  217. for( SCH_SHEET_PIN* sheetPin : m_pins )
  218. sheetPin->SetParent( this );
  219. for( SCH_SHEET_PIN* sheetPin : sheet->m_pins )
  220. sheetPin->SetParent( sheet );
  221. std::swap( m_borderWidth, sheet->m_borderWidth );
  222. std::swap( m_borderColor, sheet->m_borderColor );
  223. std::swap( m_backgroundColor, sheet->m_backgroundColor );
  224. std::swap( m_instances, sheet->m_instances );
  225. }
  226. void SCH_SHEET::AddPin( SCH_SHEET_PIN* aSheetPin )
  227. {
  228. wxASSERT( aSheetPin != NULL );
  229. wxASSERT( aSheetPin->Type() == SCH_SHEET_PIN_T );
  230. aSheetPin->SetParent( this );
  231. m_pins.push_back( aSheetPin );
  232. renumberPins();
  233. }
  234. void SCH_SHEET::RemovePin( SCH_SHEET_PIN* aSheetPin )
  235. {
  236. wxASSERT( aSheetPin != NULL );
  237. wxASSERT( aSheetPin->Type() == SCH_SHEET_PIN_T );
  238. for( auto i = m_pins.begin(); i < m_pins.end(); ++i )
  239. {
  240. if( *i == aSheetPin )
  241. {
  242. m_pins.erase( i );
  243. renumberPins();
  244. return;
  245. }
  246. }
  247. }
  248. bool SCH_SHEET::HasPin( const wxString& aName )
  249. {
  250. for( SCH_SHEET_PIN* pin : m_pins )
  251. {
  252. if( pin->GetText().CmpNoCase( aName ) == 0 )
  253. return true;
  254. }
  255. return false;
  256. }
  257. bool SCH_SHEET::doIsConnected( const wxPoint& aPosition ) const
  258. {
  259. for( SCH_SHEET_PIN* sheetPin : m_pins )
  260. {
  261. if( sheetPin->GetPosition() == aPosition )
  262. return true;
  263. }
  264. return false;
  265. }
  266. bool SCH_SHEET::IsVerticalOrientation() const
  267. {
  268. int leftRight = 0;
  269. int topBottom = 0;
  270. for( SCH_SHEET_PIN* pin : m_pins )
  271. {
  272. switch( pin->GetEdge() )
  273. {
  274. case SHEET_LEFT_SIDE: leftRight++; break;
  275. case SHEET_RIGHT_SIDE: leftRight++; break;
  276. case SHEET_TOP_SIDE: topBottom++; break;
  277. case SHEET_BOTTOM_SIDE: topBottom++; break;
  278. default: break;
  279. }
  280. }
  281. return topBottom > 0 && leftRight == 0;
  282. }
  283. bool SCH_SHEET::HasUndefinedPins()
  284. {
  285. for( SCH_SHEET_PIN* pin : m_pins )
  286. {
  287. /* Search the schematic for a hierarchical label corresponding to this sheet label. */
  288. const SCH_HIERLABEL* HLabel = nullptr;
  289. for( auto aItem : m_screen->Items().OfType( SCH_HIER_LABEL_T ) )
  290. {
  291. if( !pin->GetText().CmpNoCase( static_cast<SCH_HIERLABEL*>( aItem )->GetText() ) )
  292. {
  293. HLabel = static_cast<SCH_HIERLABEL*>( aItem );
  294. break;
  295. }
  296. }
  297. if( HLabel == nullptr ) // Corresponding hierarchical label not found.
  298. return true;
  299. }
  300. return false;
  301. }
  302. int SCH_SHEET::GetMinWidth() const
  303. {
  304. int width = Mils2iu( MIN_SHEET_WIDTH );
  305. for( size_t i = 0; i < m_pins.size(); i++ )
  306. {
  307. int edge = m_pins[i]->GetEdge();
  308. EDA_RECT pinRect = m_pins[i]->GetBoundingBox();
  309. wxASSERT( edge != SHEET_UNDEFINED_SIDE );
  310. if( edge == SHEET_TOP_SIDE || edge == SHEET_BOTTOM_SIDE )
  311. {
  312. if( width < pinRect.GetRight() - m_pos.x )
  313. width = pinRect.GetRight() - m_pos.x;
  314. }
  315. else
  316. {
  317. if( width < pinRect.GetWidth() )
  318. width = pinRect.GetWidth();
  319. for( size_t j = 0; j < m_pins.size(); j++ )
  320. {
  321. // Check for pin directly across from the current pin.
  322. if( (i == j) || (m_pins[i]->GetPosition().y != m_pins[j]->GetPosition().y) )
  323. continue;
  324. if( width < pinRect.GetWidth() + m_pins[j]->GetBoundingBox().GetWidth() )
  325. {
  326. width = pinRect.GetWidth() + m_pins[j]->GetBoundingBox().GetWidth();
  327. break;
  328. }
  329. }
  330. }
  331. }
  332. return width;
  333. }
  334. int SCH_SHEET::GetMinHeight() const
  335. {
  336. int height = Mils2iu( MIN_SHEET_HEIGHT );
  337. for( size_t i = 0; i < m_pins.size(); i++ )
  338. {
  339. int edge = m_pins[i]->GetEdge();
  340. EDA_RECT pinRect = m_pins[i]->GetBoundingBox();
  341. // Make sure pin is on top or bottom side of sheet.
  342. if( edge == SHEET_RIGHT_SIDE || edge == SHEET_LEFT_SIDE )
  343. {
  344. if( height < pinRect.GetBottom() - m_pos.y )
  345. height = pinRect.GetBottom() - m_pos.y;
  346. }
  347. else
  348. {
  349. if( height < pinRect.GetHeight() )
  350. height = pinRect.GetHeight();
  351. for( size_t j = 0; j < m_pins.size(); j++ )
  352. {
  353. // Check for pin directly above or below the current pin.
  354. if( (i == j) || (m_pins[i]->GetPosition().x != m_pins[j]->GetPosition().x) )
  355. continue;
  356. if( height < pinRect.GetHeight() + m_pins[j]->GetBoundingBox().GetHeight() )
  357. {
  358. height = pinRect.GetHeight() + m_pins[j]->GetBoundingBox().GetHeight();
  359. break;
  360. }
  361. }
  362. }
  363. }
  364. return height;
  365. }
  366. void SCH_SHEET::CleanupSheet()
  367. {
  368. auto i = m_pins.begin();
  369. while( i != m_pins.end() )
  370. {
  371. /* Search the schematic for a hierarchical label corresponding to this sheet label. */
  372. const SCH_HIERLABEL* HLabel = NULL;
  373. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_HIER_LABEL_T ) )
  374. {
  375. if( (*i)->GetText().CmpNoCase( static_cast<SCH_HIERLABEL*>( aItem )->GetText() ) == 0 )
  376. {
  377. HLabel = static_cast<SCH_HIERLABEL*>( aItem );
  378. break;
  379. }
  380. }
  381. if( HLabel == NULL ) // Hlabel not found: delete sheet label.
  382. i = m_pins.erase( i );
  383. else
  384. ++i;
  385. }
  386. }
  387. SCH_SHEET_PIN* SCH_SHEET::GetPin( const wxPoint& aPosition )
  388. {
  389. for( SCH_SHEET_PIN* pin : m_pins )
  390. {
  391. if( pin->HitTest( aPosition ) )
  392. return pin;
  393. }
  394. return NULL;
  395. }
  396. int SCH_SHEET::GetPenWidth() const
  397. {
  398. return std::max( GetBorderWidth(), 1 );
  399. }
  400. void SCH_SHEET::AutoplaceFields( SCH_SCREEN* aScreen, bool aManual )
  401. {
  402. wxSize textSize = m_fields[ SHEETNAME ].GetTextSize();
  403. int borderMargin = KiROUND( GetPenWidth() / 2.0 ) + 4;
  404. int margin = borderMargin + KiROUND( std::max( textSize.x, textSize.y ) * 0.5 );
  405. if( IsVerticalOrientation() )
  406. {
  407. m_fields[ SHEETNAME ].SetTextPos( m_pos + wxPoint( -margin, m_size.y ) );
  408. m_fields[ SHEETNAME ].SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  409. m_fields[ SHEETNAME ].SetVertJustify(GR_TEXT_VJUSTIFY_BOTTOM );
  410. m_fields[ SHEETNAME ].SetTextAngle( TEXT_ANGLE_VERT );
  411. }
  412. else
  413. {
  414. m_fields[ SHEETNAME ].SetTextPos( m_pos + wxPoint( 0, -margin ) );
  415. m_fields[ SHEETNAME ].SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  416. m_fields[ SHEETNAME ].SetVertJustify(GR_TEXT_VJUSTIFY_BOTTOM );
  417. m_fields[ SHEETNAME ].SetTextAngle( TEXT_ANGLE_HORIZ );
  418. }
  419. textSize = m_fields[ SHEETFILENAME ].GetTextSize();
  420. margin = borderMargin + KiROUND( std::max( textSize.x, textSize.y ) * 0.4 );
  421. if( IsVerticalOrientation() )
  422. {
  423. m_fields[ SHEETFILENAME ].SetTextPos( m_pos + wxPoint( m_size.x + margin, m_size.y ) );
  424. m_fields[ SHEETFILENAME ].SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  425. m_fields[ SHEETFILENAME ].SetVertJustify(GR_TEXT_VJUSTIFY_TOP );
  426. m_fields[ SHEETFILENAME ].SetTextAngle( TEXT_ANGLE_VERT );
  427. }
  428. else
  429. {
  430. m_fields[ SHEETFILENAME ].SetTextPos( m_pos + wxPoint( 0, m_size.y + margin ) );
  431. m_fields[ SHEETFILENAME ].SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  432. m_fields[ SHEETFILENAME ].SetVertJustify(GR_TEXT_VJUSTIFY_TOP );
  433. m_fields[ SHEETFILENAME ].SetTextAngle( TEXT_ANGLE_HORIZ );
  434. }
  435. m_fieldsAutoplaced = FIELDS_AUTOPLACED_AUTO;
  436. }
  437. void SCH_SHEET::ViewGetLayers( int aLayers[], int& aCount ) const
  438. {
  439. aCount = 4;
  440. aLayers[0] = LAYER_HIERLABEL;
  441. aLayers[1] = LAYER_SHEET;
  442. aLayers[2] = LAYER_SHEET_BACKGROUND;
  443. aLayers[3] = LAYER_SELECTION_SHADOWS;
  444. }
  445. const EDA_RECT SCH_SHEET::GetBodyBoundingBox() const
  446. {
  447. wxPoint end;
  448. EDA_RECT box( m_pos, m_size );
  449. int lineWidth = GetPenWidth();
  450. int textLength = 0;
  451. // Calculate bounding box X size:
  452. end.x = std::max( m_size.x, textLength );
  453. // Calculate bounding box pos:
  454. end.y = m_size.y;
  455. end += m_pos;
  456. box.SetEnd( end );
  457. box.Inflate( lineWidth / 2 );
  458. return box;
  459. }
  460. const EDA_RECT SCH_SHEET::GetBoundingBox() const
  461. {
  462. EDA_RECT box = GetBodyBoundingBox();
  463. for( const SCH_FIELD& field : m_fields )
  464. box.Merge( field.GetBoundingBox() );
  465. return box;
  466. }
  467. wxPoint SCH_SHEET::GetRotationCenter() const
  468. {
  469. EDA_RECT box( m_pos, m_size );
  470. return box.GetCenter();
  471. }
  472. int SCH_SHEET::SymbolCount() const
  473. {
  474. int n = 0;
  475. if( m_screen )
  476. {
  477. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_COMPONENT_T ) )
  478. {
  479. SCH_COMPONENT* symbol = (SCH_COMPONENT*) aItem;
  480. if( symbol->GetField( VALUE_FIELD )->GetText().GetChar( 0 ) != '#' )
  481. n++;
  482. }
  483. for( SCH_ITEM* aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  484. n += static_cast<const SCH_SHEET*>( aItem )->SymbolCount();
  485. }
  486. return n;
  487. }
  488. bool SCH_SHEET::SearchHierarchy( const wxString& aFilename, SCH_SCREEN** aScreen )
  489. {
  490. if( m_screen )
  491. {
  492. // Only check the root sheet once and don't recurse.
  493. if( !GetParent() )
  494. {
  495. if( m_screen && m_screen->GetFileName().Cmp( aFilename ) == 0 )
  496. {
  497. *aScreen = m_screen;
  498. return true;
  499. }
  500. }
  501. for( auto aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  502. {
  503. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( aItem );
  504. SCH_SCREEN* screen = sheet->m_screen;
  505. // Must use the screen's path (which is always absolute) rather than the
  506. // sheet's (which could be relative).
  507. if( screen && screen->GetFileName().Cmp( aFilename ) == 0 )
  508. {
  509. *aScreen = screen;
  510. return true;
  511. }
  512. if( sheet->SearchHierarchy( aFilename, aScreen ) )
  513. return true;
  514. }
  515. }
  516. return false;
  517. }
  518. bool SCH_SHEET::LocatePathOfScreen( SCH_SCREEN* aScreen, SCH_SHEET_PATH* aList )
  519. {
  520. if( m_screen )
  521. {
  522. aList->push_back( this );
  523. if( m_screen == aScreen )
  524. return true;
  525. for( auto item : m_screen->Items().OfType( SCH_SHEET_T ) )
  526. {
  527. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( item );
  528. if( sheet->LocatePathOfScreen( aScreen, aList ) )
  529. {
  530. return true;
  531. }
  532. }
  533. aList->pop_back();
  534. }
  535. return false;
  536. }
  537. int SCH_SHEET::CountSheets() const
  538. {
  539. int count = 1; //1 = this!!
  540. if( m_screen )
  541. {
  542. for( auto aItem : m_screen->Items().OfType( SCH_SHEET_T ) )
  543. count += static_cast<SCH_SHEET*>( aItem )->CountSheets();
  544. }
  545. return count;
  546. }
  547. void SCH_SHEET::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, MSG_PANEL_ITEMS& aList )
  548. {
  549. aList.emplace_back( _( "Sheet Name" ), m_fields[ SHEETNAME ].GetText() );
  550. if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( aFrame ) )
  551. {
  552. SCH_SHEET_PATH path = schframe->GetCurrentSheet();
  553. path.push_back( this );
  554. aList.emplace_back( _( "Hierarchical Path" ), path.PathHumanReadable( false ) );
  555. }
  556. aList.emplace_back( _( "File Name" ), m_fields[ SHEETFILENAME ].GetText() );
  557. }
  558. void SCH_SHEET::Rotate( wxPoint aPosition )
  559. {
  560. wxPoint prev = m_pos;
  561. RotatePoint( &m_pos, aPosition, 900 );
  562. RotatePoint( &m_size.x, &m_size.y, 900 );
  563. if( m_size.x < 0 )
  564. {
  565. m_pos.x += m_size.x;
  566. m_size.x = -m_size.x;
  567. }
  568. if( m_size.y < 0 )
  569. {
  570. m_pos.y += m_size.y;
  571. m_size.y = -m_size.y;
  572. }
  573. // Pins must be rotated first as that's how we determine vertical vs horizontal
  574. // orientation for auto-placement
  575. for( SCH_SHEET_PIN* sheetPin : m_pins )
  576. sheetPin->Rotate( aPosition );
  577. if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
  578. {
  579. AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  580. }
  581. else
  582. {
  583. // Move the fields to the new position because the component itself has moved.
  584. for( SCH_FIELD& field : m_fields )
  585. {
  586. wxPoint pos = field.GetTextPos();
  587. pos.x -= prev.x - m_pos.x;
  588. pos.y -= prev.y - m_pos.y;
  589. field.SetTextPos( pos );
  590. }
  591. }
  592. }
  593. void SCH_SHEET::MirrorX( int aXaxis_position )
  594. {
  595. MIRROR( m_pos.y, aXaxis_position );
  596. m_pos.y -= m_size.y;
  597. for( SCH_SHEET_PIN* sheetPin : m_pins )
  598. sheetPin->MirrorX( aXaxis_position );
  599. }
  600. void SCH_SHEET::MirrorY( int aYaxis_position )
  601. {
  602. MIRROR( m_pos.x, aYaxis_position );
  603. m_pos.x -= m_size.x;
  604. for( SCH_SHEET_PIN* sheetPin : m_pins )
  605. sheetPin->MirrorY( aYaxis_position );
  606. }
  607. void SCH_SHEET::SetPosition( const wxPoint& aPosition )
  608. {
  609. // Remember the sheet and all pin sheet positions must be
  610. // modified. So use Move function to do that.
  611. Move( aPosition - m_pos );
  612. }
  613. void SCH_SHEET::Resize( const wxSize& aSize )
  614. {
  615. if( aSize == m_size )
  616. return;
  617. m_size = aSize;
  618. // Move the fields if we're in autoplace mode
  619. if( m_fieldsAutoplaced == FIELDS_AUTOPLACED_AUTO )
  620. AutoplaceFields( /* aScreen */ NULL, /* aManual */ false );
  621. // Move the sheet labels according to the new sheet size.
  622. for( SCH_SHEET_PIN* sheetPin : m_pins )
  623. sheetPin->ConstrainOnEdge( sheetPin->GetPosition() );
  624. }
  625. bool SCH_SHEET::Matches( wxFindReplaceData& aSearchData, void* aAuxData )
  626. {
  627. wxLogTrace( traceFindItem, wxT( " item " ) + GetSelectMenuText( EDA_UNITS::MILLIMETRES ) );
  628. // Sheets are searchable via the child field and pin item text.
  629. return false;
  630. }
  631. void SCH_SHEET::renumberPins()
  632. {
  633. int id = 2;
  634. for( SCH_SHEET_PIN* pin : m_pins )
  635. {
  636. pin->SetNumber( id );
  637. id++;
  638. }
  639. }
  640. void SCH_SHEET::GetEndPoints( std::vector <DANGLING_END_ITEM>& aItemList )
  641. {
  642. for( SCH_SHEET_PIN* sheetPin : m_pins )
  643. {
  644. wxCHECK2_MSG( sheetPin->Type() == SCH_SHEET_PIN_T, continue,
  645. wxT( "Invalid item in schematic sheet pin list. Bad programmer!" ) );
  646. sheetPin->GetEndPoints( aItemList );
  647. }
  648. }
  649. bool SCH_SHEET::UpdateDanglingState( std::vector<DANGLING_END_ITEM>& aItemList,
  650. const SCH_SHEET_PATH* aPath )
  651. {
  652. bool changed = false;
  653. for( SCH_SHEET_PIN* sheetPin : m_pins )
  654. changed |= sheetPin->UpdateDanglingState( aItemList );
  655. return changed;
  656. }
  657. std::vector<wxPoint> SCH_SHEET::GetConnectionPoints() const
  658. {
  659. std::vector<wxPoint> retval;
  660. for( SCH_SHEET_PIN* sheetPin : m_pins )
  661. retval.push_back( sheetPin->GetPosition() );
  662. return retval;
  663. }
  664. SEARCH_RESULT SCH_SHEET::Visit( INSPECTOR aInspector, void* testData, const KICAD_T aFilterTypes[] )
  665. {
  666. KICAD_T stype;
  667. for( const KICAD_T* p = aFilterTypes; (stype = *p) != EOT; ++p )
  668. {
  669. // If caller wants to inspect my type
  670. if( stype == SCH_LOCATE_ANY_T || stype == Type() )
  671. {
  672. if( SEARCH_RESULT::QUIT == aInspector( this, NULL ) )
  673. return SEARCH_RESULT::QUIT;
  674. }
  675. if( stype == SCH_LOCATE_ANY_T || stype == SCH_FIELD_T )
  676. {
  677. // Test the sheet fields.
  678. for( SCH_FIELD& field : m_fields )
  679. {
  680. if( SEARCH_RESULT::QUIT == aInspector( &field, this ) )
  681. return SEARCH_RESULT::QUIT;
  682. }
  683. }
  684. if( stype == SCH_LOCATE_ANY_T || stype == SCH_SHEET_PIN_T )
  685. {
  686. // Test the sheet labels.
  687. for( SCH_SHEET_PIN* sheetPin : m_pins )
  688. {
  689. if( SEARCH_RESULT::QUIT == aInspector( sheetPin, this ) )
  690. return SEARCH_RESULT::QUIT;
  691. }
  692. }
  693. }
  694. return SEARCH_RESULT::CONTINUE;
  695. }
  696. void SCH_SHEET::RunOnChildren( const std::function<void( SCH_ITEM* )>& aFunction )
  697. {
  698. for( SCH_FIELD& field : m_fields )
  699. aFunction( &field );
  700. for( SCH_SHEET_PIN* pin : m_pins )
  701. aFunction( pin );
  702. }
  703. wxString SCH_SHEET::GetSelectMenuText( EDA_UNITS aUnits ) const
  704. {
  705. return wxString::Format( _( "Hierarchical Sheet %s" ),
  706. m_fields[ SHEETNAME ].GetText() );
  707. }
  708. BITMAP_DEF SCH_SHEET::GetMenuImage() const
  709. {
  710. return add_hierarchical_subsheet_xpm;
  711. }
  712. bool SCH_SHEET::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  713. {
  714. EDA_RECT rect = GetBodyBoundingBox();
  715. rect.Inflate( aAccuracy );
  716. return rect.Contains( aPosition );
  717. }
  718. bool SCH_SHEET::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  719. {
  720. EDA_RECT rect = aRect;
  721. rect.Inflate( aAccuracy );
  722. if( aContained )
  723. return rect.Contains( GetBodyBoundingBox() );
  724. return rect.Intersects( GetBodyBoundingBox() );
  725. }
  726. void SCH_SHEET::Plot( PLOTTER* aPlotter )
  727. {
  728. wxString msg;
  729. wxPoint pos;
  730. auto* settings = dynamic_cast<KIGFX::SCH_RENDER_SETTINGS*>( aPlotter->RenderSettings() );
  731. bool override = settings ? settings->m_OverrideItemColors : false;
  732. COLOR4D borderColor = GetBorderColor();
  733. COLOR4D backgroundColor = GetBackgroundColor();
  734. if( override || borderColor == COLOR4D::UNSPECIFIED )
  735. borderColor = aPlotter->RenderSettings()->GetLayerColor( LAYER_SHEET );
  736. if( override || backgroundColor == COLOR4D::UNSPECIFIED )
  737. backgroundColor = aPlotter->RenderSettings()->GetLayerColor( LAYER_SHEET_BACKGROUND );
  738. aPlotter->SetColor( backgroundColor );
  739. // Do not fill shape in B&W mode, otherwise texts are unreadable
  740. bool fill = aPlotter->GetColorMode();
  741. aPlotter->Rect( m_pos, m_pos + m_size, fill ? FILL_TYPE::FILLED_SHAPE : FILL_TYPE::NO_FILL,
  742. 1.0 );
  743. aPlotter->SetColor( borderColor );
  744. int penWidth = std::max( GetPenWidth(), aPlotter->RenderSettings()->GetMinPenWidth() );
  745. aPlotter->SetCurrentLineWidth( penWidth );
  746. aPlotter->MoveTo( m_pos );
  747. pos = m_pos;
  748. pos.x += m_size.x;
  749. aPlotter->LineTo( pos );
  750. pos.y += m_size.y;
  751. aPlotter->LineTo( pos );
  752. pos = m_pos;
  753. pos.y += m_size.y;
  754. aPlotter->LineTo( pos );
  755. aPlotter->FinishTo( m_pos );
  756. for( SCH_FIELD field : m_fields )
  757. field.Plot( aPlotter );
  758. /* Draw texts : SheetLabel */
  759. for( SCH_SHEET_PIN* sheetPin : m_pins )
  760. sheetPin->Plot( aPlotter );
  761. }
  762. void SCH_SHEET::Print( RENDER_SETTINGS* aSettings, const wxPoint& aOffset )
  763. {
  764. wxDC* DC = aSettings->GetPrintDC();
  765. wxPoint pos = m_pos + aOffset;
  766. int lineWidth = std::max( GetPenWidth(), aSettings->GetDefaultPenWidth() );
  767. auto* settings = dynamic_cast<KIGFX::SCH_RENDER_SETTINGS*>( aSettings );
  768. bool override = settings && settings->m_OverrideItemColors;
  769. COLOR4D border = GetBorderColor();
  770. COLOR4D background = GetBackgroundColor();
  771. if( override || border == COLOR4D::UNSPECIFIED )
  772. border = aSettings->GetLayerColor( LAYER_SHEET );
  773. if( override || background == COLOR4D::UNSPECIFIED )
  774. background = aSettings->GetLayerColor( LAYER_SHEET_BACKGROUND );
  775. if( GetGRForceBlackPenState() ) // printing in black & white
  776. background = COLOR4D::UNSPECIFIED;
  777. if( background != COLOR4D::UNSPECIFIED )
  778. {
  779. GRFilledRect( nullptr, DC, pos.x, pos.y, pos.x + m_size.x, pos.y + m_size.y, 0,
  780. background, background );
  781. }
  782. GRRect( nullptr, DC, pos.x, pos.y, pos.x + m_size.x, pos.y + m_size.y, lineWidth, border );
  783. for( SCH_FIELD& field : m_fields )
  784. field.Print( aSettings, aOffset );
  785. /* Draw text : SheetLabel */
  786. for( SCH_SHEET_PIN* sheetPin : m_pins )
  787. sheetPin->Print( aSettings, aOffset );
  788. }
  789. SCH_SHEET& SCH_SHEET::operator=( const SCH_ITEM& aItem )
  790. {
  791. wxCHECK_MSG( Type() == aItem.Type(), *this,
  792. wxT( "Cannot assign object type " ) + aItem.GetClass() + wxT( " to type " ) +
  793. GetClass() );
  794. if( &aItem != this )
  795. {
  796. SCH_ITEM::operator=( aItem );
  797. SCH_SHEET* sheet = (SCH_SHEET*) &aItem;
  798. m_pos = sheet->m_pos;
  799. m_size = sheet->m_size;
  800. m_fields = sheet->m_fields;
  801. for( SCH_SHEET_PIN* pin : sheet->m_pins )
  802. {
  803. m_pins.emplace_back( new SCH_SHEET_PIN( *pin ) );
  804. m_pins.back()->SetParent( this );
  805. }
  806. for( const SCH_SHEET_INSTANCE& instance : sheet->m_instances )
  807. m_instances.emplace_back( instance );
  808. }
  809. return *this;
  810. }
  811. bool SCH_SHEET::operator <( const SCH_ITEM& aItem ) const
  812. {
  813. if( Type() != aItem.Type() )
  814. return Type() < aItem.Type();
  815. auto sheet = static_cast<const SCH_SHEET*>( &aItem );
  816. if (m_fields[ SHEETNAME ].GetText() != sheet->m_fields[ SHEETNAME ].GetText() )
  817. return m_fields[ SHEETNAME ].GetText() < sheet->m_fields[ SHEETNAME ].GetText();
  818. if (m_fields[ SHEETFILENAME ].GetText() != sheet->m_fields[ SHEETFILENAME ].GetText() )
  819. return m_fields[ SHEETFILENAME ].GetText() < sheet->m_fields[ SHEETFILENAME ].GetText();
  820. return false;
  821. }
  822. bool SCH_SHEET::AddInstance( const KIID_PATH& aSheetPath )
  823. {
  824. // a empty sheet path is illegal:
  825. wxCHECK( aSheetPath.size() > 0, false );
  826. wxString path;
  827. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  828. {
  829. // if aSheetPath is found, nothing to do:
  830. if( instance.m_Path == aSheetPath )
  831. return false;
  832. }
  833. SCH_SHEET_INSTANCE instance;
  834. instance.m_Path = aSheetPath;
  835. // This entry does not exist: add it with an empty page number.
  836. m_instances.emplace_back( instance );
  837. return true;
  838. }
  839. wxString SCH_SHEET::GetPageNumber( const SCH_SHEET_PATH& aInstance ) const
  840. {
  841. wxString pageNumber;
  842. KIID_PATH path = aInstance.Path();
  843. for( const SCH_SHEET_INSTANCE& instance : m_instances )
  844. {
  845. if( instance.m_Path == path )
  846. {
  847. pageNumber = instance.m_PageNumber;
  848. break;
  849. }
  850. }
  851. return pageNumber;
  852. }
  853. void SCH_SHEET::SetPageNumber( const SCH_SHEET_PATH& aInstance, const wxString& aPageNumber )
  854. {
  855. KIID_PATH path = aInstance.Path();
  856. for( SCH_SHEET_INSTANCE& instance : m_instances )
  857. {
  858. if( instance.m_Path == path )
  859. {
  860. instance.m_PageNumber = aPageNumber;
  861. break;
  862. }
  863. }
  864. }
  865. int SCH_SHEET::ComparePageNum( const wxString& aPageNumberA, const wxString aPageNumberB )
  866. {
  867. if( aPageNumberA == aPageNumberB )
  868. return 1;
  869. // First sort numerically if the page numbers are integers
  870. long pageA, pageB;
  871. bool isIntegerPageA = aPageNumberA.ToLong( &pageA );
  872. bool isIntegerPageB = aPageNumberB.ToLong( &pageB );
  873. if( isIntegerPageA && isIntegerPageB )
  874. {
  875. if( pageA > pageB )
  876. return 1;
  877. else if( pageA == pageB )
  878. return 0;
  879. else
  880. return -1;
  881. }
  882. // Numerical page numbers always before strings
  883. if( isIntegerPageA )
  884. return -1;
  885. else if( isIntegerPageB )
  886. return 1;
  887. // If not numeric, then sort as strings
  888. int result = aPageNumberA.Cmp( aPageNumberB );
  889. if( result == 0 )
  890. return 0;
  891. else if( result > 0 )
  892. return 1;
  893. return -1;
  894. }
  895. #if defined(DEBUG)
  896. void SCH_SHEET::Show( int nestLevel, std::ostream& os ) const
  897. {
  898. // XML output:
  899. wxString s = GetClass();
  900. NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str() << ">" << " sheet_name=\""
  901. << TO_UTF8( m_fields[ SHEETNAME ].GetText() ) << '"' << ">\n";
  902. // show all the pins, and check the linked list integrity
  903. for( SCH_SHEET_PIN* sheetPin : m_pins )
  904. sheetPin->Show( nestLevel + 1, os );
  905. NestedSpace( nestLevel, os ) << "</" << s.Lower().mb_str() << ">\n" << std::flush;
  906. }
  907. #endif