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.

1121 lines
34 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2004-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. /*
  25. * Fields are texts attached to a symbol, some of which have a special meaning.
  26. * Fields 0 and 1 are very important: reference and value.
  27. * Field 2 is used as default footprint name.
  28. * Field 3 is used to point to a datasheet (usually a URL).
  29. * Fields 4+ are user fields. They can be renamed and can appear in reports.
  30. */
  31. #include <wx/log.h>
  32. #include <wx/menu.h>
  33. #include <base_units.h>
  34. #include <common.h> // for ExpandTextVars
  35. #include <eda_item.h>
  36. #include <sch_edit_frame.h>
  37. #include <plotters/plotter.h>
  38. #include <bitmaps.h>
  39. #include <core/kicad_algo.h>
  40. #include <core/mirror.h>
  41. #include <kiway.h>
  42. #include <general.h>
  43. #include <symbol_library.h>
  44. #include <sch_symbol.h>
  45. #include <sch_field.h>
  46. #include <sch_label.h>
  47. #include <schematic.h>
  48. #include <settings/color_settings.h>
  49. #include <string_utils.h>
  50. #include <trace_helpers.h>
  51. #include <trigo.h>
  52. #include <eeschema_id.h>
  53. #include <tool/tool_manager.h>
  54. #include <tools/sch_navigate_tool.h>
  55. #include <font/outline_font.h>
  56. SCH_FIELD::SCH_FIELD( const VECTOR2I& aPos, int aFieldId, SCH_ITEM* aParent,
  57. const wxString& aName ) :
  58. SCH_ITEM( aParent, SCH_FIELD_T ),
  59. EDA_TEXT( schIUScale, wxEmptyString ),
  60. m_id( 0 ),
  61. m_name( aName ),
  62. m_showName( false ),
  63. m_allowAutoPlace( true ),
  64. m_renderCacheValid( false ),
  65. m_lastResolvedColor( COLOR4D::UNSPECIFIED )
  66. {
  67. SetTextPos( aPos );
  68. SetId( aFieldId ); // will also set the layer
  69. SetVisible( false );
  70. }
  71. SCH_FIELD::SCH_FIELD( SCH_ITEM* aParent, int aFieldId, const wxString& aName ) :
  72. SCH_FIELD( VECTOR2I(), aFieldId, aParent, aName )
  73. {
  74. }
  75. SCH_FIELD::SCH_FIELD( const SCH_FIELD& aField ) :
  76. SCH_ITEM( aField ),
  77. EDA_TEXT( aField )
  78. {
  79. m_id = aField.m_id;
  80. m_name = aField.m_name;
  81. m_showName = aField.m_showName;
  82. m_allowAutoPlace = aField.m_allowAutoPlace;
  83. m_renderCache.clear();
  84. for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
  85. {
  86. KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
  87. m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
  88. }
  89. m_renderCacheValid = aField.m_renderCacheValid;
  90. m_renderCachePos = aField.m_renderCachePos;
  91. m_lastResolvedColor = aField.m_lastResolvedColor;
  92. }
  93. SCH_FIELD& SCH_FIELD::operator=( const SCH_FIELD& aField )
  94. {
  95. EDA_TEXT::operator=( aField );
  96. m_id = aField.m_id;
  97. m_name = aField.m_name;
  98. m_showName = aField.m_showName;
  99. m_allowAutoPlace = aField.m_allowAutoPlace;
  100. m_renderCache.clear();
  101. for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
  102. {
  103. KIFONT::OUTLINE_GLYPH* outline_glyph = static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() );
  104. m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline_glyph ) );
  105. }
  106. m_renderCacheValid = aField.m_renderCacheValid;
  107. m_renderCachePos = aField.m_renderCachePos;
  108. m_lastResolvedColor = aField.m_lastResolvedColor;
  109. return *this;
  110. }
  111. EDA_ITEM* SCH_FIELD::Clone() const
  112. {
  113. return new SCH_FIELD( *this );
  114. }
  115. void SCH_FIELD::SetId( int aId )
  116. {
  117. m_id = aId;
  118. if( m_parent && m_parent->Type() == SCH_SHEET_T )
  119. {
  120. switch( m_id )
  121. {
  122. case SHEETNAME: SetLayer( LAYER_SHEETNAME ); break;
  123. case SHEETFILENAME: SetLayer( LAYER_SHEETFILENAME ); break;
  124. default: SetLayer( LAYER_SHEETFIELDS ); break;
  125. }
  126. }
  127. else if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  128. {
  129. switch( m_id )
  130. {
  131. case REFERENCE_FIELD: SetLayer( LAYER_REFERENCEPART ); break;
  132. case VALUE_FIELD: SetLayer( LAYER_VALUEPART ); break;
  133. default: SetLayer( LAYER_FIELDS ); break;
  134. }
  135. }
  136. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  137. {
  138. // We can't use defined IDs for labels because there can be multiple net class
  139. // assignments.
  140. if( GetCanonicalName() == wxT( "Netclass" ) )
  141. SetLayer( LAYER_NETCLASS_REFS );
  142. else if( GetCanonicalName() == wxT( "Intersheetrefs" ) )
  143. SetLayer( LAYER_INTERSHEET_REFS );
  144. else
  145. SetLayer( LAYER_FIELDS );
  146. }
  147. }
  148. wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText,
  149. int aDepth ) const
  150. {
  151. std::function<bool( wxString* )> symbolResolver =
  152. [&]( wxString* token ) -> bool
  153. {
  154. return static_cast<SCH_SYMBOL*>( m_parent )->ResolveTextVar( aPath, token, aDepth + 1 );
  155. };
  156. std::function<bool( wxString* )> sheetResolver =
  157. [&]( wxString* token ) -> bool
  158. {
  159. return static_cast<SCH_SHEET*>( m_parent )->ResolveTextVar( token, aDepth + 1 );
  160. };
  161. std::function<bool( wxString* )> labelResolver =
  162. [&]( wxString* token ) -> bool
  163. {
  164. return static_cast<SCH_LABEL_BASE*>( m_parent )->ResolveTextVar( aPath, token,
  165. aDepth + 1 );
  166. };
  167. wxString text = EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
  168. if( IsNameShown() && aAllowExtraText )
  169. text = GetName() << wxS( ": " ) << text;
  170. if( text == wxS( "~" ) ) // Legacy placeholder for empty string
  171. {
  172. text = wxS( "" );
  173. }
  174. else if( HasTextVars() )
  175. {
  176. if( aDepth < 10 )
  177. {
  178. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  179. text = ExpandTextVars( text, &symbolResolver );
  180. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  181. text = ExpandTextVars( text, &sheetResolver );
  182. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  183. text = ExpandTextVars( text, &labelResolver );
  184. else if( Schematic() )
  185. text = ExpandTextVars( text, &Schematic()->Prj() );
  186. }
  187. }
  188. // WARNING: the IDs of FIELDS and SHEETS overlap, so one must check *both* the
  189. // id and the parent's type.
  190. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  191. {
  192. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  193. if( m_id == REFERENCE_FIELD )
  194. {
  195. // For more than one part per package, we must add the part selection
  196. // A, B, ... or 1, 2, .. to the reference.
  197. if( parentSymbol->GetUnitCount() > 1 )
  198. text << LIB_SYMBOL::SubReference( parentSymbol->GetUnit() );
  199. }
  200. }
  201. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  202. {
  203. if( m_id == SHEETFILENAME && aAllowExtraText )
  204. text = _( "File:" ) + wxS( " " )+ text;
  205. }
  206. return text;
  207. }
  208. wxString SCH_FIELD::GetShownText( bool aAllowExtraText, int aDepth ) const
  209. {
  210. if( SCHEMATIC* schematic = Schematic() )
  211. return GetShownText( &schematic->CurrentSheet(), aAllowExtraText, aDepth );
  212. else
  213. return EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
  214. }
  215. int SCH_FIELD::GetPenWidth() const
  216. {
  217. return GetEffectiveTextPenWidth();
  218. }
  219. KIFONT::FONT* SCH_FIELD::getDrawFont() const
  220. {
  221. KIFONT::FONT* font = EDA_TEXT::GetFont();
  222. if( !font )
  223. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  224. return font;
  225. }
  226. void SCH_FIELD::ClearCaches()
  227. {
  228. ClearRenderCache();
  229. EDA_TEXT::ClearBoundingBoxCache();
  230. }
  231. void SCH_FIELD::ClearRenderCache()
  232. {
  233. EDA_TEXT::ClearRenderCache();
  234. m_renderCacheValid = false;
  235. }
  236. std::vector<std::unique_ptr<KIFONT::GLYPH>>*
  237. SCH_FIELD::GetRenderCache( const wxString& forResolvedText, const VECTOR2I& forPosition,
  238. TEXT_ATTRIBUTES& aAttrs ) const
  239. {
  240. KIFONT::FONT* font = GetFont();
  241. if( !font )
  242. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  243. if( font->IsOutline() )
  244. {
  245. KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
  246. if( m_renderCache.empty() || !m_renderCacheValid )
  247. {
  248. m_renderCache.clear();
  249. outlineFont->GetLinesAsGlyphs( &m_renderCache, forResolvedText, forPosition, aAttrs );
  250. m_renderCachePos = forPosition;
  251. m_renderCacheValid = true;
  252. }
  253. if( m_renderCachePos != forPosition )
  254. {
  255. VECTOR2I delta = forPosition - m_renderCachePos;
  256. for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_renderCache )
  257. static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() )->Move( delta );
  258. m_renderCachePos = forPosition;
  259. }
  260. return &m_renderCache;
  261. }
  262. return nullptr;
  263. }
  264. void SCH_FIELD::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  265. {
  266. wxDC* DC = aSettings->GetPrintDC();
  267. COLOR4D color = aSettings->GetLayerColor( IsForceVisible() ? LAYER_HIDDEN : m_layer );
  268. bool blackAndWhiteMode = GetGRForceBlackPenState();
  269. VECTOR2I textpos;
  270. int penWidth = GetEffectiveTextPenWidth( aSettings->GetDefaultPenWidth() );
  271. if( ( !IsVisible() && !IsForceVisible() ) || GetShownText( true ).IsEmpty() )
  272. return;
  273. COLOR4D bg = aSettings->GetBackgroundColor();
  274. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  275. bg = COLOR4D::WHITE;
  276. if( IsForceVisible() )
  277. bg = aSettings->GetLayerColor( LAYER_HIDDEN );
  278. if( !blackAndWhiteMode && GetTextColor() != COLOR4D::UNSPECIFIED )
  279. color = GetTextColor();
  280. // Calculate the text orientation according to the symbol orientation.
  281. EDA_ANGLE orient = GetTextAngle();
  282. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  283. {
  284. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  285. if( parentSymbol && parentSymbol->GetTransform().y1 ) // Rotate symbol 90 degrees.
  286. {
  287. if( orient == ANGLE_HORIZONTAL )
  288. orient = ANGLE_VERTICAL;
  289. else
  290. orient = ANGLE_HORIZONTAL;
  291. }
  292. if( parentSymbol && parentSymbol->GetDNP() )
  293. {
  294. color.Desaturate();
  295. color = color.Mix( bg, 0.5f );
  296. }
  297. }
  298. KIFONT::FONT* font = GetFont();
  299. if( !font )
  300. font = KIFONT::FONT::GetFont( aSettings->GetDefaultFont(), IsBold(), IsItalic() );
  301. /*
  302. * Calculate the text justification, according to the symbol orientation/mirror.
  303. * This is a bit complicated due to cumulative calculations:
  304. * - numerous cases (mirrored or not, rotation)
  305. * - the GRText function will also recalculate H and V justifications according to the text
  306. * orientation.
  307. * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
  308. * to calculate so the more easily way is to use no justifications (centered text) and use
  309. * GetBoundingBox to know the text coordinate considered as centered
  310. */
  311. textpos = GetBoundingBox().Centre() + aOffset;
  312. GRPrintText( DC, textpos, color, GetShownText( true ), orient, GetTextSize(),
  313. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_CENTER, penWidth, IsItalic(), IsBold(),
  314. font );
  315. }
  316. void SCH_FIELD::ImportValues( const LIB_FIELD& aSource )
  317. {
  318. SetAttributes( aSource );
  319. SetNameShown( aSource.IsNameShown() );
  320. SetCanAutoplace( aSource.CanAutoplace() );
  321. }
  322. void SCH_FIELD::SwapData( SCH_ITEM* aItem )
  323. {
  324. wxCHECK_RET( ( aItem != nullptr ) && ( aItem->Type() == SCH_FIELD_T ),
  325. wxT( "Cannot swap field data with invalid item." ) );
  326. SCH_FIELD* item = (SCH_FIELD*) aItem;
  327. std::swap( m_layer, item->m_layer );
  328. std::swap( m_showName, item->m_showName );
  329. std::swap( m_allowAutoPlace, item->m_allowAutoPlace );
  330. SwapText( *item );
  331. SwapAttributes( *item );
  332. std::swap( m_lastResolvedColor, item->m_lastResolvedColor );
  333. }
  334. COLOR4D SCH_FIELD::GetFieldColor() const
  335. {
  336. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  337. {
  338. m_lastResolvedColor = GetTextColor();
  339. }
  340. else
  341. {
  342. SCH_LABEL_BASE* parentLabel = dynamic_cast<SCH_LABEL_BASE*>( GetParent() );
  343. if( parentLabel && !parentLabel->IsConnectivityDirty() )
  344. m_lastResolvedColor = parentLabel->GetEffectiveNetClass()->GetSchematicColor();
  345. else
  346. m_lastResolvedColor = GetTextColor();
  347. }
  348. return m_lastResolvedColor;
  349. }
  350. EDA_ANGLE SCH_FIELD::GetDrawRotation() const
  351. {
  352. // Calculate the text orientation according to the symbol orientation.
  353. EDA_ANGLE orient = GetTextAngle();
  354. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  355. {
  356. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  357. if( parentSymbol && parentSymbol->GetTransform().y1 ) // Rotate symbol 90 degrees.
  358. {
  359. if( orient.IsHorizontal() )
  360. orient = ANGLE_VERTICAL;
  361. else
  362. orient = ANGLE_HORIZONTAL;
  363. }
  364. }
  365. return orient;
  366. }
  367. const BOX2I SCH_FIELD::GetBoundingBox() const
  368. {
  369. // Calculate the text bounding box:
  370. BOX2I bbox = GetTextBox();
  371. // Calculate the bounding box position relative to the parent:
  372. VECTOR2I origin = GetParentPosition();
  373. VECTOR2I pos = GetTextPos() - origin;
  374. VECTOR2I begin = bbox.GetOrigin() - origin;
  375. VECTOR2I end = bbox.GetEnd() - origin;
  376. RotatePoint( begin, pos, GetTextAngle() );
  377. RotatePoint( end, pos, GetTextAngle() );
  378. // Now, apply the symbol transform (mirror/rot)
  379. TRANSFORM transform;
  380. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  381. {
  382. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  383. // Due to the Y axis direction, we must mirror the bounding box,
  384. // relative to the text position:
  385. MIRROR( begin.y, pos.y );
  386. MIRROR( end.y, pos.y );
  387. transform = parentSymbol->GetTransform();
  388. }
  389. else
  390. {
  391. transform = TRANSFORM( 1, 0, 0, 1 ); // identity transform
  392. }
  393. bbox.SetOrigin( transform.TransformCoordinate( begin ) );
  394. bbox.SetEnd( transform.TransformCoordinate( end ) );
  395. bbox.Move( origin );
  396. bbox.Normalize();
  397. return bbox;
  398. }
  399. bool SCH_FIELD::IsHorizJustifyFlipped() const
  400. {
  401. VECTOR2I render_center = GetBoundingBox().Centre();
  402. VECTOR2I pos = GetPosition();
  403. switch( GetHorizJustify() )
  404. {
  405. case GR_TEXT_H_ALIGN_LEFT:
  406. if( GetDrawRotation().IsVertical() )
  407. return render_center.y > pos.y;
  408. else
  409. return render_center.x < pos.x;
  410. case GR_TEXT_H_ALIGN_RIGHT:
  411. if( GetDrawRotation().IsVertical() )
  412. return render_center.y < pos.y;
  413. else
  414. return render_center.x > pos.x;
  415. default:
  416. return false;
  417. }
  418. }
  419. GR_TEXT_H_ALIGN_T SCH_FIELD::GetEffectiveHorizJustify() const
  420. {
  421. switch( GetHorizJustify() )
  422. {
  423. case GR_TEXT_H_ALIGN_LEFT:
  424. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_RIGHT : GR_TEXT_H_ALIGN_LEFT;
  425. case GR_TEXT_H_ALIGN_RIGHT:
  426. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_LEFT : GR_TEXT_H_ALIGN_RIGHT;
  427. default:
  428. return GR_TEXT_H_ALIGN_CENTER;
  429. }
  430. }
  431. bool SCH_FIELD::IsVertJustifyFlipped() const
  432. {
  433. VECTOR2I render_center = GetBoundingBox().Centre();
  434. VECTOR2I pos = GetPosition();
  435. switch( GetVertJustify() )
  436. {
  437. case GR_TEXT_V_ALIGN_TOP:
  438. if( GetDrawRotation().IsVertical() )
  439. return render_center.x < pos.x;
  440. else
  441. return render_center.y < pos.y;
  442. case GR_TEXT_V_ALIGN_BOTTOM:
  443. if( GetDrawRotation().IsVertical() )
  444. return render_center.x > pos.x;
  445. else
  446. return render_center.y > pos.y;
  447. default:
  448. return false;
  449. }
  450. }
  451. GR_TEXT_V_ALIGN_T SCH_FIELD::GetEffectiveVertJustify() const
  452. {
  453. switch( GetVertJustify() )
  454. {
  455. case GR_TEXT_V_ALIGN_TOP:
  456. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_BOTTOM : GR_TEXT_V_ALIGN_TOP;
  457. case GR_TEXT_V_ALIGN_BOTTOM:
  458. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_TOP : GR_TEXT_V_ALIGN_BOTTOM;
  459. default:
  460. return GR_TEXT_V_ALIGN_CENTER;
  461. }
  462. }
  463. bool SCH_FIELD::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  464. {
  465. bool searchHiddenFields = false;
  466. bool searchAndReplace = false;
  467. bool replaceReferences = false;
  468. try
  469. {
  470. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData ); // downcast
  471. searchHiddenFields = schSearchData.searchAllFields;
  472. searchAndReplace = schSearchData.searchAndReplace;
  473. replaceReferences = schSearchData.replaceReferences;
  474. }
  475. catch( const std::bad_cast& )
  476. {
  477. }
  478. wxString text = UnescapeString( GetText() );
  479. if( !IsVisible() && !searchHiddenFields )
  480. return false;
  481. if( m_parent && m_parent->Type() == SCH_SYMBOL_T && m_id == REFERENCE_FIELD )
  482. {
  483. if( searchAndReplace && !replaceReferences )
  484. return false;
  485. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  486. wxASSERT( aAuxData );
  487. // Take sheet path into account which effects the reference field and the unit for
  488. // symbols with multiple parts.
  489. if( aAuxData )
  490. {
  491. text = parentSymbol->GetRef( (SCH_SHEET_PATH*) aAuxData );
  492. if( SCH_ITEM::Matches( text, aSearchData ) )
  493. return true;
  494. if( parentSymbol->GetUnitCount() > 1 )
  495. text << LIB_SYMBOL::SubReference( parentSymbol->GetUnit() );
  496. }
  497. }
  498. return SCH_ITEM::Matches( text, aSearchData );
  499. }
  500. bool SCH_FIELD::IsReplaceable() const
  501. {
  502. if( m_parent && m_parent->Type() == SCH_SHEET_T )
  503. {
  504. // See comments in SCH_FIELD::Replace(), below.
  505. if( m_id == SHEETFILENAME )
  506. return false;
  507. }
  508. else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  509. {
  510. if( m_id == 0 /* IntersheetRefs */ )
  511. return false;
  512. }
  513. return true;
  514. }
  515. bool SCH_FIELD::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  516. {
  517. bool replaceReferences = false;
  518. try
  519. {
  520. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData );
  521. replaceReferences = schSearchData.replaceReferences;
  522. }
  523. catch( const std::bad_cast& )
  524. {
  525. }
  526. wxString text;
  527. bool isReplaced = false;
  528. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  529. {
  530. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  531. switch( m_id )
  532. {
  533. case REFERENCE_FIELD:
  534. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in refdes." ) );
  535. if( !replaceReferences )
  536. return false;
  537. text = parentSymbol->GetRef( (SCH_SHEET_PATH*) aAuxData );
  538. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  539. if( isReplaced )
  540. parentSymbol->SetRef( (SCH_SHEET_PATH*) aAuxData, text );
  541. break;
  542. case VALUE_FIELD:
  543. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in value field." ) );
  544. text = parentSymbol->GetField( VALUE_FIELD )->GetText();
  545. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  546. if( isReplaced )
  547. parentSymbol->SetValueFieldText( text );
  548. break;
  549. case FOOTPRINT_FIELD:
  550. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in footprint field." ) );
  551. text = parentSymbol->GetField( FOOTPRINT_FIELD )->GetText();
  552. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  553. if( isReplaced )
  554. parentSymbol->SetFootprintFieldText( text );
  555. break;
  556. default:
  557. isReplaced = EDA_TEXT::Replace( aSearchData );
  558. }
  559. }
  560. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  561. {
  562. isReplaced = EDA_TEXT::Replace( aSearchData );
  563. if( m_id == SHEETFILENAME && isReplaced )
  564. {
  565. // If we allowed this we'd have a bunch of work to do here, including warning
  566. // about it not being undoable, checking for recursive hierarchies, reloading
  567. // sheets, etc. See DIALOG_SHEET_PROPERTIES::TransferDataFromWindow().
  568. }
  569. }
  570. else
  571. {
  572. isReplaced = EDA_TEXT::Replace( aSearchData );
  573. }
  574. return isReplaced;
  575. }
  576. void SCH_FIELD::Rotate( const VECTOR2I& aCenter )
  577. {
  578. VECTOR2I pt = GetPosition();
  579. RotatePoint( pt, aCenter, ANGLE_90 );
  580. SetPosition( pt );
  581. }
  582. wxString SCH_FIELD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  583. {
  584. return wxString::Format( "%s '%s'", GetName(), KIUI::EllipsizeMenuText( GetText() ) );
  585. }
  586. void SCH_FIELD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  587. {
  588. wxString msg;
  589. aList.emplace_back( _( "Symbol Field" ), UnescapeString( GetName() ) );
  590. // Don't use GetShownText() here; we want to show the user the variable references
  591. aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  592. aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
  593. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  594. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  595. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  596. switch ( GetHorizJustify() )
  597. {
  598. case GR_TEXT_H_ALIGN_LEFT: msg = _( "Left" ); break;
  599. case GR_TEXT_H_ALIGN_CENTER: msg = _( "Center" ); break;
  600. case GR_TEXT_H_ALIGN_RIGHT: msg = _( "Right" ); break;
  601. }
  602. aList.emplace_back( _( "H Justification" ), msg );
  603. switch ( GetVertJustify() )
  604. {
  605. case GR_TEXT_V_ALIGN_TOP: msg = _( "Top" ); break;
  606. case GR_TEXT_V_ALIGN_CENTER: msg = _( "Center" ); break;
  607. case GR_TEXT_V_ALIGN_BOTTOM: msg = _( "Bottom" ); break;
  608. }
  609. aList.emplace_back( _( "V Justification" ), msg );
  610. }
  611. void SCH_FIELD::DoHypertextAction( EDA_DRAW_FRAME* aFrame ) const
  612. {
  613. constexpr int START_ID = 1;
  614. if( IsHypertext() )
  615. {
  616. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  617. std::vector<std::pair<wxString, wxString>> pages;
  618. wxMenu menu;
  619. wxString href;
  620. label->GetIntersheetRefs( &pages );
  621. for( int i = 0; i < (int) pages.size(); ++i )
  622. {
  623. menu.Append( i + START_ID, wxString::Format( _( "Go to Page %s (%s)" ),
  624. pages[i].first,
  625. pages[i].second ) );
  626. }
  627. menu.AppendSeparator();
  628. menu.Append( 999 + START_ID, _( "Back to Previous Selected Sheet" ) );
  629. int sel = aFrame->GetPopupMenuSelectionFromUser( menu ) - START_ID;
  630. if( sel >= 0 && sel < (int) pages.size() )
  631. href = wxT( "#" ) + pages[ sel ].first;
  632. else if( sel == 999 )
  633. href = SCH_NAVIGATE_TOOL::g_BackLink;
  634. if( !href.IsEmpty() )
  635. {
  636. SCH_NAVIGATE_TOOL* navTool = aFrame->GetToolManager()->GetTool<SCH_NAVIGATE_TOOL>();
  637. navTool->HypertextCommand( href );
  638. }
  639. }
  640. }
  641. wxString SCH_FIELD::GetName( bool aUseDefaultName ) const
  642. {
  643. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  644. {
  645. if( m_id >= 0 && m_id < MANDATORY_FIELDS )
  646. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  647. else if( m_name.IsEmpty() && aUseDefaultName )
  648. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  649. else
  650. return m_name;
  651. }
  652. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  653. {
  654. if( m_id >= 0 && m_id < SHEET_MANDATORY_FIELDS )
  655. return SCH_SHEET::GetDefaultFieldName( m_id );
  656. else if( m_name.IsEmpty() && aUseDefaultName )
  657. return SCH_SHEET::GetDefaultFieldName( m_id );
  658. else
  659. return m_name;
  660. }
  661. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  662. {
  663. return SCH_LABEL_BASE::GetDefaultFieldName( m_name, aUseDefaultName );
  664. }
  665. else
  666. {
  667. wxFAIL_MSG( "Unhandled field owner type." );
  668. return m_name;
  669. }
  670. }
  671. wxString SCH_FIELD::GetCanonicalName() const
  672. {
  673. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  674. {
  675. switch( m_id )
  676. {
  677. case REFERENCE_FIELD: return wxT( "Reference" );
  678. case VALUE_FIELD: return wxT( "Value" );
  679. case FOOTPRINT_FIELD: return wxT( "Footprint" );
  680. case DATASHEET_FIELD: return wxT( "Datasheet" );
  681. default: return m_name;
  682. }
  683. }
  684. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  685. {
  686. switch( m_id )
  687. {
  688. case SHEETNAME: return wxT( "Sheetname" );
  689. case SHEETFILENAME: return wxT( "Sheetfile" );
  690. default: return m_name;
  691. }
  692. }
  693. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  694. {
  695. // These should be stored in canonical format, but just in case:
  696. if( m_name == _( "Net Class" ) || m_name == wxT( "Net Class" ) )
  697. return wxT( "Netclass" );
  698. else if( m_name == _( "Sheet References" )
  699. || m_name == wxT( "Sheet References" )
  700. || m_name == wxT( "Intersheet References" ) )
  701. return wxT( "Intersheetrefs" );
  702. else
  703. return m_name;
  704. }
  705. else
  706. {
  707. if( m_parent )
  708. {
  709. wxFAIL_MSG( wxString::Format( "Unhandled field owner type (id %d, parent type %d).",
  710. m_id, m_parent->Type() ) );
  711. }
  712. return m_name;
  713. }
  714. }
  715. BITMAPS SCH_FIELD::GetMenuImage() const
  716. {
  717. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  718. {
  719. switch( m_id )
  720. {
  721. case REFERENCE_FIELD: return BITMAPS::edit_comp_ref;
  722. case VALUE_FIELD: return BITMAPS::edit_comp_value;
  723. case FOOTPRINT_FIELD: return BITMAPS::edit_comp_footprint;
  724. default: return BITMAPS::text;
  725. }
  726. }
  727. return BITMAPS::text;
  728. }
  729. bool SCH_FIELD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  730. {
  731. // Do not hit test hidden or empty fields.
  732. if( !IsVisible() || GetShownText( true ).IsEmpty() )
  733. return false;
  734. BOX2I rect = GetBoundingBox();
  735. rect.Inflate( aAccuracy );
  736. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  737. {
  738. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  739. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  740. }
  741. return rect.Contains( aPosition );
  742. }
  743. bool SCH_FIELD::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  744. {
  745. // Do not hit test hidden fields.
  746. if( !IsVisible() || GetShownText( true ).IsEmpty() )
  747. return false;
  748. BOX2I rect = aRect;
  749. rect.Inflate( aAccuracy );
  750. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  751. {
  752. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  753. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  754. }
  755. if( aContained )
  756. return rect.Contains( GetBoundingBox() );
  757. return rect.Intersects( GetBoundingBox() );
  758. }
  759. void SCH_FIELD::Plot( PLOTTER* aPlotter, bool aBackground ) const
  760. {
  761. if( GetShownText( true ).IsEmpty() || aBackground )
  762. return;
  763. RENDER_SETTINGS* settings = aPlotter->RenderSettings();
  764. COLOR4D color = settings->GetLayerColor( GetLayer() );
  765. int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
  766. COLOR4D bg = settings->GetBackgroundColor();;
  767. if( bg == COLOR4D::UNSPECIFIED || !aPlotter->GetColorMode() )
  768. bg = COLOR4D::WHITE;
  769. if( aPlotter->GetColorMode() && GetTextColor() != COLOR4D::UNSPECIFIED )
  770. color = GetTextColor();
  771. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  772. // clamp the pen width to be sure the text is readable
  773. penWidth = std::min( penWidth, std::min( GetTextSize().x, GetTextSize().y ) / 4 );
  774. if( !IsVisible() )
  775. return;
  776. // Calculate the text orientation, according to the symbol orientation/mirror
  777. EDA_ANGLE orient = GetTextAngle();
  778. VECTOR2I textpos = GetTextPos();
  779. GR_TEXT_H_ALIGN_T hjustify = GetHorizJustify();
  780. GR_TEXT_V_ALIGN_T vjustify = GetVertJustify();
  781. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  782. {
  783. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  784. if( parentSymbol->GetDNP() )
  785. {
  786. color.Desaturate();
  787. color = color.Mix( bg, 0.5f );
  788. }
  789. if( parentSymbol->GetTransform().y1 ) // Rotate symbol 90 deg.
  790. {
  791. if( orient.IsHorizontal() )
  792. orient = ANGLE_VERTICAL;
  793. else
  794. orient = ANGLE_HORIZONTAL;
  795. }
  796. /*
  797. * Calculate the text justification, according to the symbol orientation/mirror. This is
  798. * a bit complicated due to cumulative calculations:
  799. * - numerous cases (mirrored or not, rotation)
  800. * - the plotter's Text() function will also recalculate H and V justifications according
  801. * to the text orientation
  802. * - when a symbol is mirrored the text is not, and justifications become a nightmare
  803. *
  804. * So the easier way is to use no justifications (centered text) and use GetBoundingBox to
  805. * know the text coordinate considered as centered.
  806. */
  807. hjustify = GR_TEXT_H_ALIGN_CENTER;
  808. vjustify = GR_TEXT_V_ALIGN_CENTER;
  809. textpos = GetBoundingBox().Centre();
  810. }
  811. else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  812. {
  813. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( m_parent );
  814. textpos += label->GetSchematicTextOffset( settings );
  815. }
  816. KIFONT::FONT* font = GetFont();
  817. if( !font )
  818. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  819. TEXT_ATTRIBUTES attrs = GetAttributes();
  820. attrs.m_StrokeWidth = penWidth;
  821. attrs.m_Halign = hjustify;
  822. attrs.m_Valign = vjustify;
  823. attrs.m_Angle = orient;
  824. attrs.m_Multiline = false;
  825. aPlotter->PlotText( textpos, color, GetShownText( true ), attrs, font );
  826. if( IsHypertext() )
  827. {
  828. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  829. std::vector<std::pair<wxString, wxString>> pages;
  830. std::vector<wxString> pageHrefs;
  831. BOX2I bbox = GetBoundingBox();
  832. wxCHECK( label, /* void */ );
  833. label->GetIntersheetRefs( &pages );
  834. for( const std::pair<wxString, wxString>& page : pages )
  835. pageHrefs.push_back( wxT( "#" ) + page.first );
  836. bbox.Offset( label->GetSchematicTextOffset( settings ) );
  837. aPlotter->HyperlinkMenu( bbox, pageHrefs );
  838. }
  839. }
  840. void SCH_FIELD::SetPosition( const VECTOR2I& aPosition )
  841. {
  842. // Actual positions are calculated by the rotation/mirror transform of the parent symbol
  843. // of the field. The inverse transform is used to calculate the position relative to the
  844. // parent symbol.
  845. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  846. {
  847. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  848. VECTOR2I relPos = aPosition - parentSymbol->GetPosition();
  849. relPos = parentSymbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
  850. SetTextPos( relPos + parentSymbol->GetPosition() );
  851. return;
  852. }
  853. SetTextPos( aPosition );
  854. }
  855. VECTOR2I SCH_FIELD::GetPosition() const
  856. {
  857. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  858. {
  859. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  860. VECTOR2I relativePos = GetTextPos() - parentSymbol->GetPosition();
  861. relativePos = parentSymbol->GetTransform().TransformCoordinate( relativePos );
  862. return relativePos + parentSymbol->GetPosition();
  863. }
  864. return GetTextPos();
  865. }
  866. VECTOR2I SCH_FIELD::GetParentPosition() const
  867. {
  868. return m_parent ? m_parent->GetPosition() : VECTOR2I( 0, 0 );
  869. }
  870. bool SCH_FIELD::operator <( const SCH_ITEM& aItem ) const
  871. {
  872. if( Type() != aItem.Type() )
  873. return Type() < aItem.Type();
  874. auto field = static_cast<const SCH_FIELD*>( &aItem );
  875. if( GetId() != field->GetId() )
  876. return GetId() < field->GetId();
  877. if( GetText() != field->GetText() )
  878. return GetText() < field->GetText();
  879. if( GetLibPosition().x != field->GetLibPosition().x )
  880. return GetLibPosition().x < field->GetLibPosition().x;
  881. if( GetLibPosition().y != field->GetLibPosition().y )
  882. return GetLibPosition().y < field->GetLibPosition().y;
  883. return GetName() < field->GetName();
  884. }