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.

1111 lines
33 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, int aDepth,
  149. bool aAllowExtraText ) const
  150. {
  151. std::function<bool( wxString* )> symbolResolver =
  152. [&]( wxString* token ) -> bool
  153. {
  154. return static_cast<SCH_SYMBOL*>( m_parent )->ResolveTextVar( token, aDepth + 1, aPath );
  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( token,
  165. aDepth + 1 );
  166. };
  167. wxString text = EDA_TEXT::GetShownText();
  168. if( IsNameShown() )
  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. int SCH_FIELD::GetPenWidth() const
  209. {
  210. return GetEffectiveTextPenWidth();
  211. }
  212. KIFONT::FONT* SCH_FIELD::getDrawFont() const
  213. {
  214. KIFONT::FONT* font = EDA_TEXT::GetFont();
  215. if( !font )
  216. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  217. return font;
  218. }
  219. void SCH_FIELD::ClearCaches()
  220. {
  221. ClearRenderCache();
  222. EDA_TEXT::ClearBoundingBoxCache();
  223. }
  224. void SCH_FIELD::ClearRenderCache()
  225. {
  226. EDA_TEXT::ClearRenderCache();
  227. m_renderCacheValid = false;
  228. }
  229. std::vector<std::unique_ptr<KIFONT::GLYPH>>*
  230. SCH_FIELD::GetRenderCache( const wxString& forResolvedText, const VECTOR2I& forPosition,
  231. TEXT_ATTRIBUTES& aAttrs ) const
  232. {
  233. KIFONT::FONT* font = GetFont();
  234. if( !font )
  235. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  236. if( font->IsOutline() )
  237. {
  238. KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
  239. if( m_renderCache.empty() || !m_renderCacheValid )
  240. {
  241. m_renderCache.clear();
  242. outlineFont->GetLinesAsGlyphs( &m_renderCache, forResolvedText, forPosition, aAttrs );
  243. m_renderCachePos = forPosition;
  244. m_renderCacheValid = true;
  245. }
  246. if( m_renderCachePos != forPosition )
  247. {
  248. VECTOR2I delta = forPosition - m_renderCachePos;
  249. for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_renderCache )
  250. static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() )->Move( delta );
  251. m_renderCachePos = forPosition;
  252. }
  253. return &m_renderCache;
  254. }
  255. return nullptr;
  256. }
  257. void SCH_FIELD::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  258. {
  259. wxDC* DC = aSettings->GetPrintDC();
  260. COLOR4D color = aSettings->GetLayerColor( IsForceVisible() ? LAYER_HIDDEN : m_layer );
  261. bool blackAndWhiteMode = GetGRForceBlackPenState();
  262. VECTOR2I textpos;
  263. int penWidth = GetEffectiveTextPenWidth( aSettings->GetDefaultPenWidth() );
  264. if( ( !IsVisible() && !IsForceVisible() ) || GetShownText().IsEmpty() )
  265. return;
  266. COLOR4D bg = aSettings->GetBackgroundColor();
  267. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  268. bg = COLOR4D::WHITE;
  269. if( IsForceVisible() )
  270. bg = aSettings->GetLayerColor( LAYER_HIDDEN );
  271. if( !blackAndWhiteMode && GetTextColor() != COLOR4D::UNSPECIFIED )
  272. color = GetTextColor();
  273. // Calculate the text orientation according to the symbol orientation.
  274. EDA_ANGLE orient = GetTextAngle();
  275. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  276. {
  277. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  278. if( parentSymbol && parentSymbol->GetTransform().y1 ) // Rotate symbol 90 degrees.
  279. {
  280. if( orient == ANGLE_HORIZONTAL )
  281. orient = ANGLE_VERTICAL;
  282. else
  283. orient = ANGLE_HORIZONTAL;
  284. }
  285. if( parentSymbol && parentSymbol->GetDNP() )
  286. {
  287. color.Desaturate();
  288. color = color.Mix( bg, 0.5f );
  289. }
  290. }
  291. KIFONT::FONT* font = GetFont();
  292. if( !font )
  293. font = KIFONT::FONT::GetFont( aSettings->GetDefaultFont(), IsBold(), IsItalic() );
  294. /*
  295. * Calculate the text justification, according to the symbol orientation/mirror.
  296. * This is a bit complicated due to cumulative calculations:
  297. * - numerous cases (mirrored or not, rotation)
  298. * - the GRText function will also recalculate H and V justifications according to the text
  299. * orientation.
  300. * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
  301. * to calculate so the more easily way is to use no justifications (centered text) and use
  302. * GetBoundingBox to know the text coordinate considered as centered
  303. */
  304. textpos = GetBoundingBox().Centre() + aOffset;
  305. GRPrintText( DC, textpos, color, GetShownText(), orient, GetTextSize(), GR_TEXT_H_ALIGN_CENTER,
  306. GR_TEXT_V_ALIGN_CENTER, penWidth, IsItalic(), IsBold(), font );
  307. }
  308. void SCH_FIELD::ImportValues( const LIB_FIELD& aSource )
  309. {
  310. SetAttributes( aSource );
  311. SetNameShown( aSource.IsNameShown() );
  312. SetCanAutoplace( aSource.CanAutoplace() );
  313. }
  314. void SCH_FIELD::SwapData( SCH_ITEM* aItem )
  315. {
  316. wxCHECK_RET( ( aItem != nullptr ) && ( aItem->Type() == SCH_FIELD_T ),
  317. wxT( "Cannot swap field data with invalid item." ) );
  318. SCH_FIELD* item = (SCH_FIELD*) aItem;
  319. std::swap( m_layer, item->m_layer );
  320. std::swap( m_showName, item->m_showName );
  321. std::swap( m_allowAutoPlace, item->m_allowAutoPlace );
  322. SwapText( *item );
  323. SwapAttributes( *item );
  324. std::swap( m_lastResolvedColor, item->m_lastResolvedColor );
  325. }
  326. COLOR4D SCH_FIELD::GetFieldColor() const
  327. {
  328. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  329. {
  330. m_lastResolvedColor = GetTextColor();
  331. }
  332. else
  333. {
  334. SCH_LABEL_BASE* parentLabel = dynamic_cast<SCH_LABEL_BASE*>( GetParent() );
  335. if( parentLabel && !parentLabel->IsConnectivityDirty() )
  336. m_lastResolvedColor = parentLabel->GetEffectiveNetClass()->GetSchematicColor();
  337. else
  338. m_lastResolvedColor = GetTextColor();
  339. }
  340. return m_lastResolvedColor;
  341. }
  342. EDA_ANGLE SCH_FIELD::GetDrawRotation() const
  343. {
  344. // Calculate the text orientation according to the symbol orientation.
  345. EDA_ANGLE orient = GetTextAngle();
  346. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  347. {
  348. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  349. if( parentSymbol && parentSymbol->GetTransform().y1 ) // Rotate symbol 90 degrees.
  350. {
  351. if( orient.IsHorizontal() )
  352. orient = ANGLE_VERTICAL;
  353. else
  354. orient = ANGLE_HORIZONTAL;
  355. }
  356. }
  357. return orient;
  358. }
  359. const BOX2I SCH_FIELD::GetBoundingBox() const
  360. {
  361. // Calculate the text bounding box:
  362. BOX2I bbox = GetTextBox();
  363. // Calculate the bounding box position relative to the parent:
  364. VECTOR2I origin = GetParentPosition();
  365. VECTOR2I pos = GetTextPos() - origin;
  366. VECTOR2I begin = bbox.GetOrigin() - origin;
  367. VECTOR2I end = bbox.GetEnd() - origin;
  368. RotatePoint( begin, pos, GetTextAngle() );
  369. RotatePoint( end, pos, GetTextAngle() );
  370. // Now, apply the symbol transform (mirror/rot)
  371. TRANSFORM transform;
  372. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  373. {
  374. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  375. // Due to the Y axis direction, we must mirror the bounding box,
  376. // relative to the text position:
  377. MIRROR( begin.y, pos.y );
  378. MIRROR( end.y, pos.y );
  379. transform = parentSymbol->GetTransform();
  380. }
  381. else
  382. {
  383. transform = TRANSFORM( 1, 0, 0, 1 ); // identity transform
  384. }
  385. bbox.SetOrigin( transform.TransformCoordinate( begin ) );
  386. bbox.SetEnd( transform.TransformCoordinate( end ) );
  387. bbox.Move( origin );
  388. bbox.Normalize();
  389. return bbox;
  390. }
  391. bool SCH_FIELD::IsHorizJustifyFlipped() const
  392. {
  393. VECTOR2I render_center = GetBoundingBox().Centre();
  394. VECTOR2I pos = GetPosition();
  395. switch( GetHorizJustify() )
  396. {
  397. case GR_TEXT_H_ALIGN_LEFT:
  398. if( GetDrawRotation().IsVertical() )
  399. return render_center.y > pos.y;
  400. else
  401. return render_center.x < pos.x;
  402. case GR_TEXT_H_ALIGN_RIGHT:
  403. if( GetDrawRotation().IsVertical() )
  404. return render_center.y < pos.y;
  405. else
  406. return render_center.x > pos.x;
  407. default:
  408. return false;
  409. }
  410. }
  411. GR_TEXT_H_ALIGN_T SCH_FIELD::GetEffectiveHorizJustify() const
  412. {
  413. switch( GetHorizJustify() )
  414. {
  415. case GR_TEXT_H_ALIGN_LEFT:
  416. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_RIGHT : GR_TEXT_H_ALIGN_LEFT;
  417. case GR_TEXT_H_ALIGN_RIGHT:
  418. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_LEFT : GR_TEXT_H_ALIGN_RIGHT;
  419. default:
  420. return GR_TEXT_H_ALIGN_CENTER;
  421. }
  422. }
  423. bool SCH_FIELD::IsVertJustifyFlipped() const
  424. {
  425. VECTOR2I render_center = GetBoundingBox().Centre();
  426. VECTOR2I pos = GetPosition();
  427. switch( GetVertJustify() )
  428. {
  429. case GR_TEXT_V_ALIGN_TOP:
  430. if( GetDrawRotation().IsVertical() )
  431. return render_center.x < pos.x;
  432. else
  433. return render_center.y < pos.y;
  434. case GR_TEXT_V_ALIGN_BOTTOM:
  435. if( GetDrawRotation().IsVertical() )
  436. return render_center.x > pos.x;
  437. else
  438. return render_center.y > pos.y;
  439. default:
  440. return false;
  441. }
  442. }
  443. GR_TEXT_V_ALIGN_T SCH_FIELD::GetEffectiveVertJustify() const
  444. {
  445. switch( GetVertJustify() )
  446. {
  447. case GR_TEXT_V_ALIGN_TOP:
  448. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_BOTTOM : GR_TEXT_V_ALIGN_TOP;
  449. case GR_TEXT_V_ALIGN_BOTTOM:
  450. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_TOP : GR_TEXT_V_ALIGN_BOTTOM;
  451. default:
  452. return GR_TEXT_V_ALIGN_CENTER;
  453. }
  454. }
  455. bool SCH_FIELD::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  456. {
  457. bool searchHiddenFields = false;
  458. bool searchAndReplace = false;
  459. bool replaceReferences = false;
  460. try
  461. {
  462. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData ); // downcast
  463. searchHiddenFields = schSearchData.searchAllFields;
  464. searchAndReplace = schSearchData.searchAndReplace;
  465. replaceReferences = schSearchData.replaceReferences;
  466. }
  467. catch( const std::bad_cast& )
  468. {
  469. }
  470. wxString text = GetShownText();
  471. if( !IsVisible() && !searchHiddenFields )
  472. return false;
  473. if( m_parent && m_parent->Type() == SCH_SYMBOL_T && m_id == REFERENCE_FIELD )
  474. {
  475. if( searchAndReplace && !replaceReferences )
  476. return false;
  477. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  478. wxASSERT( aAuxData );
  479. // Take sheet path into account which effects the reference field and the unit for
  480. // symbols with multiple parts.
  481. if( aAuxData )
  482. {
  483. text = parentSymbol->GetRef((SCH_SHEET_PATH*) aAuxData );
  484. if( SCH_ITEM::Matches( text, aSearchData ) )
  485. return true;
  486. if( parentSymbol->GetUnitCount() > 1 )
  487. text << LIB_SYMBOL::SubReference( parentSymbol->GetUnit() );
  488. }
  489. }
  490. return SCH_ITEM::Matches( text, aSearchData );
  491. }
  492. bool SCH_FIELD::IsReplaceable() const
  493. {
  494. if( m_parent && m_parent->Type() == SCH_SHEET_T )
  495. {
  496. // See comments in SCH_FIELD::Replace(), below.
  497. if( m_id == SHEETFILENAME )
  498. return false;
  499. }
  500. else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  501. {
  502. if( m_id == 0 /* IntersheetRefs */ )
  503. return false;
  504. }
  505. return true;
  506. }
  507. bool SCH_FIELD::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  508. {
  509. bool replaceReferences = false;
  510. try
  511. {
  512. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData );
  513. replaceReferences = schSearchData.replaceReferences;
  514. }
  515. catch( const std::bad_cast& )
  516. {
  517. }
  518. wxString text;
  519. bool resolve = false; // Replace in source text, not shown text
  520. bool isReplaced = false;
  521. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  522. {
  523. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  524. switch( m_id )
  525. {
  526. case REFERENCE_FIELD:
  527. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in refdes." ) );
  528. if( !replaceReferences )
  529. return false;
  530. text = parentSymbol->GetRef( (SCH_SHEET_PATH*) aAuxData );
  531. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  532. if( isReplaced )
  533. parentSymbol->SetRef( (SCH_SHEET_PATH*) aAuxData, text );
  534. break;
  535. case VALUE_FIELD:
  536. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in value field." ) );
  537. text = parentSymbol->GetValueFieldText( resolve );
  538. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  539. if( isReplaced )
  540. parentSymbol->SetValueFieldText( text );
  541. break;
  542. case FOOTPRINT_FIELD:
  543. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in footprint field." ) );
  544. text = parentSymbol->GetFootprintFieldText( resolve );
  545. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  546. if( isReplaced )
  547. parentSymbol->SetFootprintFieldText( text );
  548. break;
  549. default:
  550. isReplaced = EDA_TEXT::Replace( aSearchData );
  551. }
  552. }
  553. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  554. {
  555. isReplaced = EDA_TEXT::Replace( aSearchData );
  556. if( m_id == SHEETFILENAME && isReplaced )
  557. {
  558. // If we allowed this we'd have a bunch of work to do here, including warning
  559. // about it not being undoable, checking for recursive hierarchies, reloading
  560. // sheets, etc. See DIALOG_SHEET_PROPERTIES::TransferDataFromWindow().
  561. }
  562. }
  563. else
  564. {
  565. isReplaced = EDA_TEXT::Replace( aSearchData );
  566. }
  567. return isReplaced;
  568. }
  569. void SCH_FIELD::Rotate( const VECTOR2I& aCenter )
  570. {
  571. VECTOR2I pt = GetPosition();
  572. RotatePoint( pt, aCenter, ANGLE_90 );
  573. SetPosition( pt );
  574. }
  575. wxString SCH_FIELD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  576. {
  577. return wxString::Format( "%s '%s'", GetName(), KIUI::EllipsizeMenuText( GetShownText() ) );
  578. }
  579. void SCH_FIELD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  580. {
  581. wxString msg;
  582. aList.emplace_back( _( "Symbol Field" ), GetName() );
  583. // Don't use GetShownText() here; we want to show the user the variable references
  584. aList.emplace_back( _( "Text" ), UnescapeString( GetText() ) );
  585. aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
  586. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  587. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  588. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  589. switch ( GetHorizJustify() )
  590. {
  591. case GR_TEXT_H_ALIGN_LEFT: msg = _( "Left" ); break;
  592. case GR_TEXT_H_ALIGN_CENTER: msg = _( "Center" ); break;
  593. case GR_TEXT_H_ALIGN_RIGHT: msg = _( "Right" ); break;
  594. }
  595. aList.emplace_back( _( "H Justification" ), msg );
  596. switch ( GetVertJustify() )
  597. {
  598. case GR_TEXT_V_ALIGN_TOP: msg = _( "Top" ); break;
  599. case GR_TEXT_V_ALIGN_CENTER: msg = _( "Center" ); break;
  600. case GR_TEXT_V_ALIGN_BOTTOM: msg = _( "Bottom" ); break;
  601. }
  602. aList.emplace_back( _( "V Justification" ), msg );
  603. }
  604. void SCH_FIELD::DoHypertextAction( EDA_DRAW_FRAME* aFrame ) const
  605. {
  606. constexpr int START_ID = 1;
  607. if( IsHypertext() )
  608. {
  609. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  610. std::vector<std::pair<wxString, wxString>> pages;
  611. wxMenu menu;
  612. wxString href;
  613. label->GetIntersheetRefs( &pages );
  614. for( int i = 0; i < (int) pages.size(); ++i )
  615. {
  616. menu.Append( i + START_ID, wxString::Format( _( "Go to Page %s (%s)" ),
  617. pages[i].first,
  618. pages[i].second ) );
  619. }
  620. menu.AppendSeparator();
  621. menu.Append( 999 + START_ID, _( "Back to Previous Selected Sheet" ) );
  622. int sel = aFrame->GetPopupMenuSelectionFromUser( menu ) - START_ID;
  623. if( sel >= 0 && sel < (int) pages.size() )
  624. href = wxT( "#" ) + pages[ sel ].first;
  625. else if( sel == 999 )
  626. href = SCH_NAVIGATE_TOOL::g_BackLink;
  627. if( !href.IsEmpty() )
  628. {
  629. SCH_NAVIGATE_TOOL* navTool = aFrame->GetToolManager()->GetTool<SCH_NAVIGATE_TOOL>();
  630. navTool->HypertextCommand( href );
  631. }
  632. }
  633. }
  634. wxString SCH_FIELD::GetName( bool aUseDefaultName ) const
  635. {
  636. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  637. {
  638. if( m_id >= 0 && m_id < MANDATORY_FIELDS )
  639. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  640. else if( m_name.IsEmpty() && aUseDefaultName )
  641. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  642. else
  643. return m_name;
  644. }
  645. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  646. {
  647. if( m_id >= 0 && m_id < SHEET_MANDATORY_FIELDS )
  648. return SCH_SHEET::GetDefaultFieldName( m_id );
  649. else if( m_name.IsEmpty() && aUseDefaultName )
  650. return SCH_SHEET::GetDefaultFieldName( m_id );
  651. else
  652. return m_name;
  653. }
  654. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  655. {
  656. return SCH_LABEL_BASE::GetDefaultFieldName( m_name, aUseDefaultName );
  657. }
  658. else
  659. {
  660. wxFAIL_MSG( "Unhandled field owner type." );
  661. return m_name;
  662. }
  663. }
  664. wxString SCH_FIELD::GetCanonicalName() const
  665. {
  666. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  667. {
  668. switch( m_id )
  669. {
  670. case REFERENCE_FIELD: return wxT( "Reference" );
  671. case VALUE_FIELD: return wxT( "Value" );
  672. case FOOTPRINT_FIELD: return wxT( "Footprint" );
  673. case DATASHEET_FIELD: return wxT( "Datasheet" );
  674. default: return m_name;
  675. }
  676. }
  677. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  678. {
  679. switch( m_id )
  680. {
  681. case SHEETNAME: return wxT( "Sheetname" );
  682. case SHEETFILENAME: return wxT( "Sheetfile" );
  683. default: return m_name;
  684. }
  685. }
  686. else if( m_parent && m_parent->IsType( { SCH_LABEL_LOCATE_ANY_T } ) )
  687. {
  688. // These should be stored in canonical format, but just in case:
  689. if( m_name == _( "Net Class" ) || m_name == wxT( "Net Class" ) )
  690. return wxT( "Netclass" );
  691. else if( m_name == _( "Sheet References" )
  692. || m_name == wxT( "Sheet References" )
  693. || m_name == wxT( "Intersheet References" ) )
  694. return wxT( "Intersheetrefs" );
  695. else
  696. return m_name;
  697. }
  698. else
  699. {
  700. if( m_parent )
  701. {
  702. wxFAIL_MSG( wxString::Format( "Unhandled field owner type (id %d, parent type %d).",
  703. m_id, m_parent->Type() ) );
  704. }
  705. return m_name;
  706. }
  707. }
  708. BITMAPS SCH_FIELD::GetMenuImage() const
  709. {
  710. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  711. {
  712. switch( m_id )
  713. {
  714. case REFERENCE_FIELD: return BITMAPS::edit_comp_ref;
  715. case VALUE_FIELD: return BITMAPS::edit_comp_value;
  716. case FOOTPRINT_FIELD: return BITMAPS::edit_comp_footprint;
  717. default: return BITMAPS::text;
  718. }
  719. }
  720. return BITMAPS::text;
  721. }
  722. bool SCH_FIELD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  723. {
  724. // Do not hit test hidden or empty fields.
  725. if( !IsVisible() || GetShownText().IsEmpty() )
  726. return false;
  727. BOX2I rect = GetBoundingBox();
  728. rect.Inflate( aAccuracy );
  729. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  730. {
  731. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  732. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  733. }
  734. return rect.Contains( aPosition );
  735. }
  736. bool SCH_FIELD::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  737. {
  738. // Do not hit test hidden fields.
  739. if( !IsVisible() || GetShownText().IsEmpty() )
  740. return false;
  741. BOX2I rect = aRect;
  742. rect.Inflate( aAccuracy );
  743. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  744. {
  745. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  746. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  747. }
  748. if( aContained )
  749. return rect.Contains( GetBoundingBox() );
  750. return rect.Intersects( GetBoundingBox() );
  751. }
  752. void SCH_FIELD::Plot( PLOTTER* aPlotter, bool aBackground ) const
  753. {
  754. if( GetShownText().IsEmpty() || aBackground )
  755. return;
  756. RENDER_SETTINGS* settings = aPlotter->RenderSettings();
  757. COLOR4D color = settings->GetLayerColor( GetLayer() );
  758. int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
  759. COLOR4D bg = settings->GetBackgroundColor();;
  760. if( bg == COLOR4D::UNSPECIFIED || !aPlotter->GetColorMode() )
  761. bg = COLOR4D::WHITE;
  762. if( aPlotter->GetColorMode() && GetTextColor() != COLOR4D::UNSPECIFIED )
  763. color = GetTextColor();
  764. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  765. // clamp the pen width to be sure the text is readable
  766. penWidth = std::min( penWidth, std::min( GetTextSize().x, GetTextSize().y ) / 4 );
  767. if( !IsVisible() )
  768. return;
  769. // Calculate the text orientation, according to the symbol orientation/mirror
  770. EDA_ANGLE orient = GetTextAngle();
  771. VECTOR2I textpos = GetTextPos();
  772. GR_TEXT_H_ALIGN_T hjustify = GetHorizJustify();
  773. GR_TEXT_V_ALIGN_T vjustify = GetVertJustify();
  774. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  775. {
  776. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  777. if( parentSymbol->GetDNP() )
  778. {
  779. color.Desaturate();
  780. color = color.Mix( bg, 0.5f );
  781. }
  782. if( parentSymbol->GetTransform().y1 ) // Rotate symbol 90 deg.
  783. {
  784. if( orient.IsHorizontal() )
  785. orient = ANGLE_VERTICAL;
  786. else
  787. orient = ANGLE_HORIZONTAL;
  788. }
  789. /*
  790. * Calculate the text justification, according to the symbol orientation/mirror. This is
  791. * a bit complicated due to cumulative calculations:
  792. * - numerous cases (mirrored or not, rotation)
  793. * - the plotter's Text() function will also recalculate H and V justifications according
  794. * to the text orientation
  795. * - when a symbol is mirrored the text is not, and justifications become a nightmare
  796. *
  797. * So the easier way is to use no justifications (centered text) and use GetBoundingBox to
  798. * know the text coordinate considered as centered.
  799. */
  800. hjustify = GR_TEXT_H_ALIGN_CENTER;
  801. vjustify = GR_TEXT_V_ALIGN_CENTER;
  802. textpos = GetBoundingBox().Centre();
  803. }
  804. else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  805. {
  806. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( m_parent );
  807. textpos += label->GetSchematicTextOffset( settings );
  808. }
  809. KIFONT::FONT* font = GetFont();
  810. if( !font )
  811. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  812. TEXT_ATTRIBUTES attrs = GetAttributes();
  813. attrs.m_StrokeWidth = penWidth;
  814. attrs.m_Halign = hjustify;
  815. attrs.m_Valign = vjustify;
  816. attrs.m_Angle = orient;
  817. attrs.m_Multiline = false;
  818. aPlotter->PlotText( textpos, color, GetShownText(), attrs, font );
  819. if( IsHypertext() )
  820. {
  821. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  822. std::vector<std::pair<wxString, wxString>> pages;
  823. std::vector<wxString> pageHrefs;
  824. BOX2I bbox = GetBoundingBox();
  825. wxCHECK( label, /* void */ );
  826. label->GetIntersheetRefs( &pages );
  827. for( const std::pair<wxString, wxString>& page : pages )
  828. pageHrefs.push_back( wxT( "#" ) + page.first );
  829. bbox.Offset( label->GetSchematicTextOffset( settings ) );
  830. aPlotter->HyperlinkMenu( bbox, pageHrefs );
  831. }
  832. }
  833. void SCH_FIELD::SetPosition( const VECTOR2I& aPosition )
  834. {
  835. // Actual positions are calculated by the rotation/mirror transform of the parent symbol
  836. // of the field. The inverse transform is used to calculate the position relative to the
  837. // parent symbol.
  838. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  839. {
  840. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  841. VECTOR2I relPos = aPosition - parentSymbol->GetPosition();
  842. relPos = parentSymbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
  843. SetTextPos( relPos + parentSymbol->GetPosition() );
  844. return;
  845. }
  846. SetTextPos( aPosition );
  847. }
  848. VECTOR2I SCH_FIELD::GetPosition() const
  849. {
  850. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  851. {
  852. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  853. VECTOR2I relativePos = GetTextPos() - parentSymbol->GetPosition();
  854. relativePos = parentSymbol->GetTransform().TransformCoordinate( relativePos );
  855. return relativePos + parentSymbol->GetPosition();
  856. }
  857. return GetTextPos();
  858. }
  859. VECTOR2I SCH_FIELD::GetParentPosition() const
  860. {
  861. return m_parent ? m_parent->GetPosition() : VECTOR2I( 0, 0 );
  862. }
  863. bool SCH_FIELD::operator <( const SCH_ITEM& aItem ) const
  864. {
  865. if( Type() != aItem.Type() )
  866. return Type() < aItem.Type();
  867. auto field = static_cast<const SCH_FIELD*>( &aItem );
  868. if( GetId() != field->GetId() )
  869. return GetId() < field->GetId();
  870. if( GetText() != field->GetText() )
  871. return GetText() < field->GetText();
  872. if( GetLibPosition().x != field->GetLibPosition().x )
  873. return GetLibPosition().x < field->GetLibPosition().x;
  874. if( GetLibPosition().y != field->GetLibPosition().y )
  875. return GetLibPosition().y < field->GetLibPosition().y;
  876. return GetName() < field->GetName();
  877. }