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.

1687 lines
50 KiB

5 months ago
2 years ago
2 years ago
  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 The 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 <wx/log.h>
  25. #include <wx/menu.h>
  26. #include <advanced_config.h>
  27. #include <base_units.h>
  28. #include <common.h> // for ExpandTextVars
  29. #include <sch_edit_frame.h>
  30. #include <plotters/plotter.h>
  31. #include <bitmaps.h>
  32. #include <kiway.h>
  33. #include <symbol_library.h>
  34. #include <settings/color_settings.h>
  35. #include <string_utils.h>
  36. #include <geometry/geometry_utils.h>
  37. #include <trace_helpers.h>
  38. #include <tool/tool_manager.h>
  39. #include <tools/sch_navigate_tool.h>
  40. #include <font/outline_font.h>
  41. #include "sim/sim_lib_mgr.h"
  42. static const std::vector<KICAD_T> labelTypes = { SCH_LABEL_LOCATE_ANY_T };
  43. SCH_FIELD::SCH_FIELD() :
  44. SCH_ITEM( nullptr, SCH_FIELD_T ),
  45. EDA_TEXT( schIUScale, wxEmptyString ),
  46. m_id( FIELD_T::USER ),
  47. m_ordinal( 0 ),
  48. m_showName( false ),
  49. m_allowAutoPlace( true ),
  50. m_isGeneratedField( false ),
  51. m_autoAdded( false ),
  52. m_showInChooser( true ),
  53. m_renderCacheValid( false ),
  54. m_lastResolvedColor( COLOR4D::UNSPECIFIED )
  55. {
  56. }
  57. SCH_FIELD::SCH_FIELD( SCH_ITEM* aParent, FIELD_T aFieldId, const wxString& aName ) :
  58. SCH_FIELD()
  59. {
  60. m_parent = aParent;
  61. if( !aName.IsEmpty() )
  62. SetName( aName );
  63. else
  64. SetName( GetDefaultFieldName( aFieldId, DO_TRANSLATE ) );
  65. setId( aFieldId ); // will also set the layer
  66. SetVisible( true );
  67. if( aParent && aParent->Schematic() )
  68. {
  69. SCHEMATIC_SETTINGS& settings = aParent->Schematic()->Settings();
  70. SetTextSize( VECTOR2I( settings.m_DefaultTextSize, settings.m_DefaultTextSize ) );
  71. }
  72. if( aFieldId == FIELD_T::USER && aParent )
  73. {
  74. if( aParent->Type() == SCH_SYMBOL_T )
  75. m_ordinal = static_cast<SCH_SYMBOL*>( aParent )->GetNextFieldOrdinal();
  76. else if( aParent->Type() == LIB_SYMBOL_T )
  77. m_ordinal = static_cast<LIB_SYMBOL*>( aParent )->GetNextFieldOrdinal();
  78. else if( aParent->Type() == SCH_SHEET_T )
  79. m_ordinal = static_cast<SCH_SHEET*>( aParent )->GetNextFieldOrdinal();
  80. else if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( aParent ) )
  81. m_ordinal = label->GetNextFieldOrdinal();
  82. }
  83. }
  84. SCH_FIELD::SCH_FIELD( SCH_ITEM* aParent, SCH_TEXT* aText ) :
  85. SCH_FIELD( aParent, FIELD_T::USER, wxEmptyString )
  86. {
  87. SCH_ITEM::operator=( *aText );
  88. EDA_TEXT::operator=( *aText );
  89. }
  90. SCH_FIELD::SCH_FIELD( const SCH_FIELD& aField ) :
  91. SCH_ITEM( aField ),
  92. EDA_TEXT( aField )
  93. {
  94. m_private = aField.m_private;
  95. setId( aField.m_id ); // will also set the layer
  96. m_ordinal = aField.m_ordinal;
  97. m_name = aField.m_name;
  98. m_showName = aField.m_showName;
  99. m_allowAutoPlace = aField.m_allowAutoPlace;
  100. m_isGeneratedField = aField.m_isGeneratedField;
  101. m_autoAdded = aField.m_autoAdded;
  102. m_showInChooser = aField.m_showInChooser;
  103. m_renderCache.clear();
  104. for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
  105. {
  106. if( KIFONT::OUTLINE_GLYPH* outline = dynamic_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() ) )
  107. m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline ) );
  108. else if( KIFONT::STROKE_GLYPH* stroke = dynamic_cast<KIFONT::STROKE_GLYPH*>( glyph.get() ) )
  109. m_renderCache.emplace_back( std::make_unique<KIFONT::STROKE_GLYPH>( *stroke ) );
  110. }
  111. m_renderCacheValid = aField.m_renderCacheValid;
  112. m_renderCachePos = aField.m_renderCachePos;
  113. m_lastResolvedColor = aField.m_lastResolvedColor;
  114. }
  115. SCH_FIELD& SCH_FIELD::operator=( const SCH_FIELD& aField )
  116. {
  117. EDA_TEXT::operator=( aField );
  118. m_private = aField.m_private;
  119. setId( aField.m_id ); // will also set the layer
  120. m_ordinal = aField.m_ordinal;
  121. m_name = aField.m_name;
  122. m_showName = aField.m_showName;
  123. m_allowAutoPlace = aField.m_allowAutoPlace;
  124. m_isGeneratedField = aField.m_isGeneratedField;
  125. m_renderCache.clear();
  126. for( const std::unique_ptr<KIFONT::GLYPH>& glyph : aField.m_renderCache )
  127. {
  128. if( KIFONT::OUTLINE_GLYPH* outline = dynamic_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() ) )
  129. m_renderCache.emplace_back( std::make_unique<KIFONT::OUTLINE_GLYPH>( *outline ) );
  130. else if( KIFONT::STROKE_GLYPH* stroke = dynamic_cast<KIFONT::STROKE_GLYPH*>( glyph.get() ) )
  131. m_renderCache.emplace_back( std::make_unique<KIFONT::STROKE_GLYPH>( *stroke ) );
  132. }
  133. m_renderCacheValid = aField.m_renderCacheValid;
  134. m_renderCachePos = aField.m_renderCachePos;
  135. m_lastResolvedColor = aField.m_lastResolvedColor;
  136. return *this;
  137. }
  138. EDA_ITEM* SCH_FIELD::Clone() const
  139. {
  140. return new SCH_FIELD( *this );
  141. }
  142. void SCH_FIELD::Copy( SCH_FIELD* aTarget ) const
  143. {
  144. *aTarget = *this;
  145. }
  146. void SCH_FIELD::setId( FIELD_T aId )
  147. {
  148. m_id = aId;
  149. SetLayer( GetDefaultLayer() );
  150. }
  151. wxString SCH_FIELD::GetShownName() const
  152. {
  153. return m_isGeneratedField ? GetGeneratedFieldDisplayName( GetName() ) : GetName();
  154. }
  155. wxString SCH_FIELD::GetShownText( const SCH_SHEET_PATH* aPath, bool aAllowExtraText,
  156. int aDepth ) const
  157. {
  158. std::function<bool( wxString* )> libSymbolResolver =
  159. [&]( wxString* token ) -> bool
  160. {
  161. LIB_SYMBOL* symbol = static_cast<LIB_SYMBOL*>( m_parent );
  162. return symbol->ResolveTextVar( token, aDepth + 1 );
  163. };
  164. std::function<bool( wxString* )> symbolResolver =
  165. [&]( wxString* token ) -> bool
  166. {
  167. SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( m_parent );
  168. return symbol->ResolveTextVar( aPath, token, aDepth + 1 );
  169. };
  170. std::function<bool( wxString* )> schematicResolver =
  171. [&]( wxString* token ) -> bool
  172. {
  173. if( !aPath )
  174. return false;
  175. if( SCHEMATIC* schematic = Schematic() )
  176. return schematic->ResolveTextVar( aPath, token, aDepth + 1 );
  177. return false;
  178. };
  179. std::function<bool( wxString* )> sheetResolver =
  180. [&]( wxString* token ) -> bool
  181. {
  182. if( !aPath )
  183. return false;
  184. SCH_SHEET* sheet = static_cast<SCH_SHEET*>( m_parent );
  185. SCHEMATIC* schematic = Schematic();
  186. SCH_SHEET_PATH path = *aPath;
  187. path.push_back( sheet );
  188. bool retval = sheet->ResolveTextVar( &path, token, aDepth + 1 );
  189. if( schematic )
  190. retval |= schematic->ResolveTextVar( &path, token, aDepth + 1 );
  191. return retval;
  192. };
  193. std::function<bool( wxString* )> labelResolver =
  194. [&]( wxString* token ) -> bool
  195. {
  196. if( !aPath )
  197. return false;
  198. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  199. return label->ResolveTextVar( aPath, token, aDepth + 1 );
  200. };
  201. wxString text = EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
  202. if( IsNameShown() && aAllowExtraText )
  203. text = GetShownName() << wxS( ": " ) << text;
  204. if( HasTextVars() )
  205. {
  206. while( text.Contains( wxT( "${" ) ) && aDepth++ <= ADVANCED_CFG::GetCfg().m_ResolveTextRecursionDepth )
  207. {
  208. if( m_parent && m_parent->Type() == LIB_SYMBOL_T )
  209. text = ExpandTextVars( text, &libSymbolResolver );
  210. else if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  211. text = ExpandTextVars( text, &symbolResolver );
  212. else if( m_parent && m_parent->Type() == SCH_SHEET_T )
  213. text = ExpandTextVars( text, &sheetResolver );
  214. else if( m_parent && m_parent->IsType( labelTypes ) )
  215. text = ExpandTextVars( text, &labelResolver );
  216. else if( Schematic() )
  217. {
  218. text = ExpandTextVars( text, &Schematic()->Project() );
  219. text = ExpandTextVars( text, &schematicResolver );
  220. }
  221. }
  222. }
  223. if( m_id == FIELD_T::REFERENCE && aPath )
  224. {
  225. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  226. // For more than one part per package, we must add the part selection
  227. // A, B, ... or 1, 2, .. to the reference.
  228. if( parentSymbol && parentSymbol->GetUnitCount() > 1 )
  229. text << parentSymbol->SubReference( parentSymbol->GetUnitSelection( aPath ) );
  230. }
  231. if( m_id == FIELD_T::SHEET_FILENAME && aAllowExtraText && !IsNameShown() )
  232. text = _( "File:" ) + wxS( " " ) + text;
  233. if( text.Contains( wxT( "@{" ) ) )
  234. text = EvaluateText( text );
  235. return text;
  236. }
  237. wxString SCH_FIELD::GetShownText( bool aAllowExtraText, int aDepth ) const
  238. {
  239. if( SCHEMATIC* schematic = Schematic() )
  240. return GetShownText( &schematic->CurrentSheet(), aAllowExtraText, aDepth );
  241. else
  242. return GetShownText( nullptr, aAllowExtraText, aDepth );
  243. }
  244. wxString SCH_FIELD::GetFullText( int unit ) const
  245. {
  246. if( GetId() != FIELD_T::REFERENCE )
  247. return GetText();
  248. wxString text = GetText();
  249. text << wxT( "?" );
  250. if( GetParentSymbol() && GetParentSymbol()->IsMultiUnit() )
  251. text << LIB_SYMBOL::LetterSubReference( unit, 'A' );
  252. return text;
  253. }
  254. int SCH_FIELD::GetPenWidth() const
  255. {
  256. return GetEffectiveTextPenWidth();
  257. }
  258. KIFONT::FONT* SCH_FIELD::GetDrawFont( const RENDER_SETTINGS* aSettings ) const
  259. {
  260. KIFONT::FONT* font = EDA_TEXT::GetFont();
  261. if( !font )
  262. font = KIFONT::FONT::GetFont( GetDefaultFont( aSettings ), IsBold(), IsItalic() );
  263. return font;
  264. }
  265. void SCH_FIELD::ClearCaches()
  266. {
  267. ClearRenderCache();
  268. EDA_TEXT::ClearBoundingBoxCache();
  269. }
  270. void SCH_FIELD::ClearRenderCache()
  271. {
  272. EDA_TEXT::ClearRenderCache();
  273. m_renderCacheValid = false;
  274. }
  275. std::vector<std::unique_ptr<KIFONT::GLYPH>>*
  276. SCH_FIELD::GetRenderCache( const wxString& forResolvedText, const VECTOR2I& forPosition,
  277. TEXT_ATTRIBUTES& aAttrs ) const
  278. {
  279. KIFONT::FONT* font = GetDrawFont( nullptr );
  280. if( font->IsOutline() )
  281. {
  282. KIFONT::OUTLINE_FONT* outlineFont = static_cast<KIFONT::OUTLINE_FONT*>( font );
  283. if( m_renderCache.empty() || !m_renderCacheValid )
  284. {
  285. m_renderCache.clear();
  286. outlineFont->GetLinesAsGlyphs( &m_renderCache, forResolvedText, forPosition, aAttrs,
  287. GetFontMetrics() );
  288. m_renderCachePos = forPosition;
  289. m_renderCacheValid = true;
  290. }
  291. if( m_renderCachePos != forPosition )
  292. {
  293. VECTOR2I delta = forPosition - m_renderCachePos;
  294. for( std::unique_ptr<KIFONT::GLYPH>& glyph : m_renderCache )
  295. {
  296. if( glyph->IsOutline() )
  297. static_cast<KIFONT::OUTLINE_GLYPH*>( glyph.get() )->Move( delta );
  298. else
  299. static_cast<KIFONT::STROKE_GLYPH*>( glyph.get() )->Move( delta );
  300. }
  301. m_renderCachePos = forPosition;
  302. }
  303. return &m_renderCache;
  304. }
  305. return nullptr;
  306. }
  307. void SCH_FIELD::ImportValues( const SCH_FIELD& aSource )
  308. {
  309. SetAttributes( aSource );
  310. SetVisible( aSource.IsVisible() );
  311. SetNameShown( aSource.IsNameShown() );
  312. SetCanAutoplace( aSource.CanAutoplace() );
  313. }
  314. void SCH_FIELD::swapData( SCH_ITEM* aItem )
  315. {
  316. wxCHECK_RET( aItem && aItem->Type() == SCH_FIELD_T, wxT( "Cannot swap with invalid item." ) );
  317. SCH_FIELD* item = static_cast<SCH_FIELD*>( aItem );
  318. std::swap( m_showName, item->m_showName );
  319. std::swap( m_allowAutoPlace, item->m_allowAutoPlace );
  320. std::swap( m_isGeneratedField, item->m_isGeneratedField );
  321. SwapText( *item );
  322. SwapAttributes( *item );
  323. std::swap( m_lastResolvedColor, item->m_lastResolvedColor );
  324. }
  325. COLOR4D SCH_FIELD::GetFieldColor() const
  326. {
  327. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  328. {
  329. m_lastResolvedColor = GetTextColor();
  330. }
  331. else
  332. {
  333. SCH_LABEL_BASE* parentLabel = dynamic_cast<SCH_LABEL_BASE*>( GetParent() );
  334. if( parentLabel && !parentLabel->IsConnectivityDirty() )
  335. m_lastResolvedColor = parentLabel->GetEffectiveNetClass()->GetSchematicColor();
  336. else
  337. m_lastResolvedColor = GetTextColor();
  338. }
  339. return m_lastResolvedColor;
  340. }
  341. std::vector<int> SCH_FIELD::ViewGetLayers() const
  342. {
  343. return { GetDefaultLayer(), LAYER_SELECTION_SHADOWS };
  344. }
  345. SCH_LAYER_ID SCH_FIELD::GetDefaultLayer() const
  346. {
  347. if( m_parent && m_parent->Type() == SCH_LABEL_T )
  348. {
  349. if( GetCanonicalName() == wxT( "Netclass" )
  350. || GetCanonicalName() == wxT( "Component Class" ) )
  351. {
  352. return LAYER_NETCLASS_REFS;
  353. }
  354. }
  355. switch( m_id )
  356. {
  357. case FIELD_T::REFERENCE: return LAYER_REFERENCEPART;
  358. case FIELD_T::VALUE: return LAYER_VALUEPART;
  359. case FIELD_T::SHEET_NAME: return LAYER_SHEETNAME;
  360. case FIELD_T::SHEET_FILENAME: return LAYER_SHEETFILENAME;
  361. case FIELD_T::SHEET_USER: return LAYER_SHEETFIELDS;
  362. case FIELD_T::INTERSHEET_REFS: return LAYER_INTERSHEET_REFS;
  363. default: return LAYER_FIELDS;
  364. }
  365. }
  366. EDA_ANGLE SCH_FIELD::GetDrawRotation() const
  367. {
  368. // Calculate the text orientation according to the symbol orientation.
  369. EDA_ANGLE orient = GetTextAngle();
  370. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  371. {
  372. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  373. if( parentSymbol && parentSymbol->GetTransform().y1 ) // Rotate symbol 90 degrees.
  374. {
  375. if( orient.IsHorizontal() )
  376. orient = ANGLE_VERTICAL;
  377. else
  378. orient = ANGLE_HORIZONTAL;
  379. }
  380. }
  381. return orient;
  382. }
  383. const BOX2I SCH_FIELD::GetBoundingBox() const
  384. {
  385. BOX2I bbox = GetTextBox( nullptr );
  386. // Calculate the bounding box position relative to the parent:
  387. VECTOR2I origin = GetParentPosition();
  388. VECTOR2I pos = GetTextPos() - origin;
  389. VECTOR2I begin = bbox.GetOrigin() - origin;
  390. VECTOR2I end = bbox.GetEnd() - origin;
  391. RotatePoint( begin, pos, GetTextAngle() );
  392. RotatePoint( end, pos, GetTextAngle() );
  393. // Now, apply the symbol transform (mirror/rot)
  394. TRANSFORM transform;
  395. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  396. transform = static_cast<SCH_SYMBOL*>( m_parent )->GetTransform();
  397. bbox.SetOrigin( transform.TransformCoordinate( begin ) );
  398. bbox.SetEnd( transform.TransformCoordinate( end ) );
  399. bbox.Move( origin );
  400. bbox.Normalize();
  401. return bbox;
  402. }
  403. bool SCH_FIELD::IsHorizJustifyFlipped() const
  404. {
  405. VECTOR2I render_center = GetBoundingBox().Centre();
  406. VECTOR2I pos = GetPosition();
  407. switch( GetHorizJustify() )
  408. {
  409. case GR_TEXT_H_ALIGN_LEFT:
  410. if( GetDrawRotation().IsVertical() )
  411. return render_center.y > pos.y;
  412. else
  413. return render_center.x < pos.x;
  414. case GR_TEXT_H_ALIGN_RIGHT:
  415. if( GetDrawRotation().IsVertical() )
  416. return render_center.y < pos.y;
  417. else
  418. return render_center.x > pos.x;
  419. default:
  420. return false;
  421. }
  422. }
  423. void SCH_FIELD::SetEffectiveHorizJustify( GR_TEXT_H_ALIGN_T aJustify )
  424. {
  425. GR_TEXT_H_ALIGN_T actualJustify;
  426. switch( aJustify )
  427. {
  428. case GR_TEXT_H_ALIGN_LEFT:
  429. actualJustify = IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_RIGHT : GR_TEXT_H_ALIGN_LEFT;
  430. break;
  431. case GR_TEXT_H_ALIGN_RIGHT:
  432. actualJustify = IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_LEFT : GR_TEXT_H_ALIGN_RIGHT;
  433. break;
  434. default:
  435. actualJustify = aJustify;
  436. }
  437. SetHorizJustify( actualJustify );
  438. }
  439. GR_TEXT_H_ALIGN_T SCH_FIELD::GetEffectiveHorizJustify() const
  440. {
  441. switch( GetHorizJustify() )
  442. {
  443. case GR_TEXT_H_ALIGN_LEFT:
  444. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_RIGHT : GR_TEXT_H_ALIGN_LEFT;
  445. case GR_TEXT_H_ALIGN_RIGHT:
  446. return IsHorizJustifyFlipped() ? GR_TEXT_H_ALIGN_LEFT : GR_TEXT_H_ALIGN_RIGHT;
  447. default:
  448. return GR_TEXT_H_ALIGN_CENTER;
  449. }
  450. }
  451. bool SCH_FIELD::IsVertJustifyFlipped() const
  452. {
  453. VECTOR2I render_center = GetBoundingBox().Centre();
  454. VECTOR2I pos = GetPosition();
  455. switch( GetVertJustify() )
  456. {
  457. case GR_TEXT_V_ALIGN_TOP:
  458. if( GetDrawRotation().IsVertical() )
  459. return render_center.x < pos.x;
  460. else
  461. return render_center.y < pos.y;
  462. case GR_TEXT_V_ALIGN_BOTTOM:
  463. if( GetDrawRotation().IsVertical() )
  464. return render_center.x > pos.x;
  465. else
  466. return render_center.y > pos.y;
  467. default:
  468. return false;
  469. }
  470. }
  471. void SCH_FIELD::SetEffectiveVertJustify( GR_TEXT_V_ALIGN_T aJustify )
  472. {
  473. GR_TEXT_V_ALIGN_T actualJustify;
  474. switch( aJustify )
  475. {
  476. case GR_TEXT_V_ALIGN_TOP:
  477. actualJustify = IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_BOTTOM : GR_TEXT_V_ALIGN_TOP;
  478. break;
  479. case GR_TEXT_V_ALIGN_BOTTOM:
  480. actualJustify = IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_TOP : GR_TEXT_V_ALIGN_BOTTOM;
  481. break;
  482. default:
  483. actualJustify = aJustify;
  484. }
  485. SetVertJustify( actualJustify );
  486. }
  487. GR_TEXT_V_ALIGN_T SCH_FIELD::GetEffectiveVertJustify() const
  488. {
  489. switch( GetVertJustify() )
  490. {
  491. case GR_TEXT_V_ALIGN_TOP:
  492. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_BOTTOM : GR_TEXT_V_ALIGN_TOP;
  493. case GR_TEXT_V_ALIGN_BOTTOM:
  494. return IsVertJustifyFlipped() ? GR_TEXT_V_ALIGN_TOP : GR_TEXT_V_ALIGN_BOTTOM;
  495. default:
  496. return GR_TEXT_V_ALIGN_CENTER;
  497. }
  498. }
  499. bool SCH_FIELD::Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const
  500. {
  501. bool searchHiddenFields = aSearchData.searchAllFields;
  502. bool searchAndReplace = aSearchData.searchAndReplace;
  503. bool replaceReferences = false;
  504. try
  505. {
  506. // downcast
  507. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData );
  508. replaceReferences = schSearchData.replaceReferences;
  509. }
  510. catch( const std::bad_cast& )
  511. {
  512. }
  513. wxString text = UnescapeString( GetText() );
  514. if( !IsVisible() && !searchHiddenFields )
  515. return false;
  516. if( m_id == FIELD_T::REFERENCE )
  517. {
  518. if( searchAndReplace && !replaceReferences )
  519. return false;
  520. SCH_SYMBOL* parentSymbol = dyn_cast<SCH_SYMBOL*>( m_parent );
  521. // The parent might be a LIB_SYMBOL, in which case, we don't
  522. // have a sheet path to resolve the reference.
  523. if( !parentSymbol )
  524. return false;
  525. if( parentSymbol->Matches( aSearchData, aAuxData ) )
  526. return true;
  527. wxASSERT( aAuxData );
  528. // Take sheet path into account which effects the reference field and the unit for
  529. // symbols with multiple parts.
  530. if( aAuxData )
  531. {
  532. SCH_SHEET_PATH* sheet = (SCH_SHEET_PATH*) aAuxData;
  533. text = parentSymbol->GetRef( sheet );
  534. if( SCH_ITEM::Matches( text, aSearchData ) )
  535. return true;
  536. if( parentSymbol->GetUnitCount() > 1 )
  537. text << parentSymbol->SubReference( parentSymbol->GetUnitSelection( sheet ) );
  538. }
  539. }
  540. return SCH_ITEM::Matches( text, aSearchData );
  541. }
  542. void SCH_FIELD::OnScintillaCharAdded( SCINTILLA_TRICKS* aScintillaTricks,
  543. wxStyledTextEvent &aEvent ) const
  544. {
  545. SCH_ITEM* parent = dynamic_cast<SCH_ITEM*>( GetParent() );
  546. SCHEMATIC* schematic = parent ? parent->Schematic() : nullptr;
  547. if( !schematic )
  548. return;
  549. wxStyledTextCtrl* scintilla = aScintillaTricks->Scintilla();
  550. int key = aEvent.GetKey();
  551. wxArrayString autocompleteTokens;
  552. int pos = scintilla->GetCurrentPos();
  553. int start = scintilla->WordStartPosition( pos, true );
  554. wxString partial;
  555. // Multi-line fields are not allowed. So remove '\n' if entered.
  556. if( key == '\n' )
  557. {
  558. wxString text = scintilla->GetText();
  559. int currpos = scintilla->GetCurrentPos();
  560. text.Replace( wxS( "\n" ), wxS( "" ) );
  561. scintilla->SetText( text );
  562. scintilla->GotoPos( currpos-1 );
  563. return;
  564. }
  565. auto textVarRef =
  566. [&]( int pt )
  567. {
  568. return pt >= 2
  569. && scintilla->GetCharAt( pt - 2 ) == '$'
  570. && scintilla->GetCharAt( pt - 1 ) == '{';
  571. };
  572. // Check for cross-reference
  573. if( start > 1 && scintilla->GetCharAt( start - 1 ) == ':' )
  574. {
  575. int refStart = scintilla->WordStartPosition( start - 1, true );
  576. if( textVarRef( refStart ) )
  577. {
  578. partial = scintilla->GetRange( start, pos );
  579. wxString ref = scintilla->GetRange( refStart, start - 1 );
  580. if( ref == wxS( "OP" ) )
  581. {
  582. // SPICE operating points use ':' syntax for ports
  583. if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( parent ) )
  584. {
  585. NULL_REPORTER devnull;
  586. SCH_SHEET_PATH& sheet = schematic->CurrentSheet();
  587. SIM_LIB_MGR mgr( &schematic->Project() );
  588. std::vector<EMBEDDED_FILES*> embeddedFilesStack;
  589. embeddedFilesStack.push_back( schematic->GetEmbeddedFiles() );
  590. if( EMBEDDED_FILES* symbolEmbeddedFiles = symbol->GetEmbeddedFiles() )
  591. embeddedFilesStack.push_back( symbolEmbeddedFiles );
  592. mgr.SetFilesStack( std::move( embeddedFilesStack ) );
  593. SIM_MODEL& model = mgr.CreateModel( &sheet, *symbol, true, 0, devnull ).model;
  594. for( wxString pin : model.GetPinNames() )
  595. {
  596. if( pin.StartsWith( '<' ) && pin.EndsWith( '>' ) )
  597. autocompleteTokens.push_back( pin.Mid( 1, pin.Length() - 2 ) );
  598. else
  599. autocompleteTokens.push_back( pin );
  600. }
  601. // add the synthetic port for power measurements
  602. autocompleteTokens.push_back( wxT( "power" ) );
  603. }
  604. }
  605. else
  606. {
  607. SCH_REFERENCE_LIST refs;
  608. SCH_SYMBOL* refSymbol = nullptr;
  609. schematic->Hierarchy().GetSymbols( refs );
  610. for( size_t jj = 0; jj < refs.GetCount(); jj++ )
  611. {
  612. if( refs[ jj ].GetSymbol()->GetRef( &refs[ jj ].GetSheetPath(), true ) == ref )
  613. {
  614. refSymbol = refs[ jj ].GetSymbol();
  615. break;
  616. }
  617. }
  618. if( refSymbol )
  619. refSymbol->GetContextualTextVars( &autocompleteTokens );
  620. }
  621. }
  622. }
  623. else if( textVarRef( start ) )
  624. {
  625. partial = scintilla->GetTextRange( start, pos );
  626. SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( parent );
  627. SCH_SHEET* sheet = dynamic_cast<SCH_SHEET*>( parent );
  628. SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( parent );
  629. if( symbol )
  630. {
  631. symbol->GetContextualTextVars( &autocompleteTokens );
  632. if( schematic->CurrentSheet().Last() )
  633. schematic->CurrentSheet().Last()->GetContextualTextVars( &autocompleteTokens );
  634. }
  635. if( sheet )
  636. sheet->GetContextualTextVars( &autocompleteTokens );
  637. if( label )
  638. label->GetContextualTextVars( &autocompleteTokens );
  639. for( std::pair<wxString, wxString> entry : schematic->Project().GetTextVars() )
  640. autocompleteTokens.push_back( entry.first );
  641. }
  642. aScintillaTricks->DoAutocomplete( partial, autocompleteTokens );
  643. scintilla->SetFocus();
  644. }
  645. bool SCH_FIELD::IsReplaceable() const
  646. {
  647. // See comments in SCH_FIELD::Replace(), below.
  648. if( m_id == FIELD_T::SHEET_FILENAME || m_id == FIELD_T::INTERSHEET_REFS )
  649. return false;
  650. return true;
  651. }
  652. bool SCH_FIELD::Replace( const EDA_SEARCH_DATA& aSearchData, void* aAuxData )
  653. {
  654. bool replaceReferences = false;
  655. try
  656. {
  657. const SCH_SEARCH_DATA& schSearchData = dynamic_cast<const SCH_SEARCH_DATA&>( aSearchData );
  658. replaceReferences = schSearchData.replaceReferences;
  659. }
  660. catch( const std::bad_cast& )
  661. {
  662. }
  663. wxString text;
  664. bool isReplaced = false;
  665. if( m_id == FIELD_T::REFERENCE && m_parent && m_parent->Type() == SCH_SYMBOL_T )
  666. {
  667. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  668. if( !replaceReferences )
  669. return false;
  670. wxCHECK_MSG( aAuxData, false, wxT( "Need sheetpath to replace in refdes." ) );
  671. text = parentSymbol->GetRef( (SCH_SHEET_PATH*) aAuxData );
  672. isReplaced = EDA_ITEM::Replace( aSearchData, text );
  673. if( isReplaced )
  674. parentSymbol->SetRef( (SCH_SHEET_PATH*) aAuxData, text );
  675. }
  676. else
  677. {
  678. isReplaced = EDA_TEXT::Replace( aSearchData );
  679. if( m_id == FIELD_T::SHEET_FILENAME && isReplaced )
  680. {
  681. // If we allowed this we'd have a bunch of work to do here, including warning
  682. // about it not being undoable, checking for recursive hierarchies, reloading
  683. // sheets, etc. See DIALOG_SHEET_PROPERTIES::TransferDataFromWindow().
  684. }
  685. }
  686. return isReplaced;
  687. }
  688. void SCH_FIELD::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
  689. {
  690. const GR_TEXT_H_ALIGN_T horizJustify = GetHorizJustify();
  691. if( GetTextAngle().IsVertical() )
  692. {
  693. switch( horizJustify )
  694. {
  695. case GR_TEXT_H_ALIGN_LEFT:
  696. if( aRotateCCW )
  697. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  698. break;
  699. case GR_TEXT_H_ALIGN_RIGHT:
  700. if( aRotateCCW )
  701. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  702. break;
  703. case GR_TEXT_H_ALIGN_CENTER:
  704. case GR_TEXT_H_ALIGN_INDETERMINATE:
  705. break;
  706. }
  707. SetTextAngle( ANGLE_HORIZONTAL );
  708. }
  709. else if( GetTextAngle().IsHorizontal() )
  710. {
  711. switch( horizJustify )
  712. {
  713. case GR_TEXT_H_ALIGN_LEFT:
  714. if( !aRotateCCW )
  715. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  716. break;
  717. case GR_TEXT_H_ALIGN_RIGHT:
  718. if( !aRotateCCW )
  719. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  720. break;
  721. case GR_TEXT_H_ALIGN_CENTER:
  722. case GR_TEXT_H_ALIGN_INDETERMINATE:
  723. break;
  724. }
  725. SetTextAngle( ANGLE_VERTICAL );
  726. }
  727. else
  728. {
  729. wxFAIL_MSG( wxString::Format( wxT( "SCH_FIELD text angle is not horizontal or vertical: %f" ),
  730. GetTextAngle().AsDegrees() ) );
  731. }
  732. VECTOR2I pt = GetPosition();
  733. RotatePoint( pt, aCenter, aRotateCCW ? ANGLE_90 : ANGLE_270 );
  734. SetPosition( pt );
  735. }
  736. void SCH_FIELD::MirrorHorizontally( int aCenter )
  737. {
  738. int x = GetTextPos().x;
  739. x -= aCenter;
  740. x *= -1;
  741. x += aCenter;
  742. SetTextX( x );
  743. }
  744. void SCH_FIELD::MirrorVertically( int aCenter )
  745. {
  746. int y = GetTextPos().y;
  747. y -= aCenter;
  748. y *= -1;
  749. y += aCenter;
  750. SetTextY( y );
  751. }
  752. void SCH_FIELD::BeginEdit( const VECTOR2I& aPosition )
  753. {
  754. SetTextPos( aPosition );
  755. }
  756. void SCH_FIELD::CalcEdit( const VECTOR2I& aPosition )
  757. {
  758. SetTextPos( aPosition );
  759. }
  760. wxString SCH_FIELD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
  761. {
  762. return wxString::Format( _( "Field %s '%s'" ),
  763. UnescapeString( GetName() ),
  764. aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
  765. }
  766. void SCH_FIELD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  767. {
  768. wxString msg;
  769. aList.emplace_back( _( "Symbol Field" ), UnescapeString( GetName() ) );
  770. // Don't use GetShownText() here; we want to show the user the variable references
  771. aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  772. aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
  773. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  774. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  775. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  776. switch ( GetHorizJustify() )
  777. {
  778. case GR_TEXT_H_ALIGN_LEFT: msg = _( "Left" ); break;
  779. case GR_TEXT_H_ALIGN_CENTER: msg = _( "Center" ); break;
  780. case GR_TEXT_H_ALIGN_RIGHT: msg = _( "Right" ); break;
  781. case GR_TEXT_H_ALIGN_INDETERMINATE: msg = INDETERMINATE_STATE; break;
  782. }
  783. aList.emplace_back( _( "H Justification" ), msg );
  784. switch ( GetVertJustify() )
  785. {
  786. case GR_TEXT_V_ALIGN_TOP: msg = _( "Top" ); break;
  787. case GR_TEXT_V_ALIGN_CENTER: msg = _( "Center" ); break;
  788. case GR_TEXT_V_ALIGN_BOTTOM: msg = _( "Bottom" ); break;
  789. case GR_TEXT_V_ALIGN_INDETERMINATE: msg = INDETERMINATE_STATE; break;
  790. }
  791. aList.emplace_back( _( "V Justification" ), msg );
  792. }
  793. bool SCH_FIELD::IsHypertext() const
  794. {
  795. if( m_id == FIELD_T::INTERSHEET_REFS )
  796. return true;
  797. if( m_name == SIM_LIBRARY::LIBRARY_FIELD )
  798. return true;
  799. return IsURL( GetShownText( false ) );
  800. }
  801. void SCH_FIELD::DoHypertextAction( EDA_DRAW_FRAME* aFrame ) const
  802. {
  803. constexpr int START_ID = 1;
  804. if( IsHypertext() )
  805. {
  806. wxString href;
  807. if( m_id == FIELD_T::INTERSHEET_REFS )
  808. {
  809. SCH_LABEL_BASE* label = static_cast<SCH_LABEL_BASE*>( m_parent );
  810. SCH_SHEET_PATH* sheet = &label->Schematic()->CurrentSheet();
  811. wxMenu menu;
  812. std::vector<std::pair<wxString, wxString>> pages;
  813. label->GetIntersheetRefs( sheet, &pages );
  814. for( int i = 0; i < (int) pages.size(); ++i )
  815. {
  816. menu.Append( i + START_ID, wxString::Format( _( "Go to Page %s (%s)" ),
  817. pages[i].first,
  818. pages[i].second ) );
  819. }
  820. menu.AppendSeparator();
  821. menu.Append( 999 + START_ID, _( "Back to Previous Selected Sheet" ) );
  822. int sel = aFrame->GetPopupMenuSelectionFromUser( menu ) - START_ID;
  823. if( sel >= 0 && sel < (int) pages.size() )
  824. href = wxT( "#" ) + pages[ sel ].first;
  825. else if( sel == 999 )
  826. href = SCH_NAVIGATE_TOOL::g_BackLink;
  827. }
  828. else if( IsURL( GetShownText( false ) ) || m_name == SIM_LIBRARY::LIBRARY_FIELD )
  829. {
  830. href = GetShownText( false );
  831. }
  832. if( !href.IsEmpty() )
  833. {
  834. SCH_NAVIGATE_TOOL* navTool = aFrame->GetToolManager()->GetTool<SCH_NAVIGATE_TOOL>();
  835. navTool->HypertextCommand( href );
  836. }
  837. }
  838. }
  839. void SCH_FIELD::SetName( const wxString& aName )
  840. {
  841. m_name = aName;
  842. m_isGeneratedField = ::IsGeneratedField( aName );
  843. if( m_isGeneratedField )
  844. EDA_TEXT::SetText( aName );
  845. }
  846. void SCH_FIELD::SetText( const wxString& aText )
  847. {
  848. // Don't allow modification of text value of generated fields.
  849. if( m_isGeneratedField )
  850. return;
  851. // Mandatory fields should not have leading or trailing whitespace.
  852. if( IsMandatory() )
  853. EDA_TEXT::SetText( aText.Strip( wxString::both ) );
  854. else
  855. EDA_TEXT::SetText( aText );
  856. }
  857. wxString SCH_FIELD::GetName( bool aUseDefaultName ) const
  858. {
  859. if( m_parent && m_parent->IsType( labelTypes ) )
  860. return SCH_LABEL_BASE::GetDefaultFieldName( m_name, aUseDefaultName );
  861. if( IsMandatory() )
  862. return GetCanonicalFieldName( m_id );
  863. else if( m_name.IsEmpty() && aUseDefaultName )
  864. return GetDefaultFieldName( m_id, !DO_TRANSLATE );
  865. else
  866. return m_name;
  867. }
  868. wxString SCH_FIELD::GetCanonicalName() const
  869. {
  870. if( m_parent && m_parent->IsType( labelTypes ) )
  871. {
  872. // These should be stored in canonical format, but just in case:
  873. if( m_name == _( "Net Class" ) || m_name == wxT( "Net Class" ) )
  874. return wxT( "Netclass" );
  875. }
  876. if( IsMandatory() )
  877. return GetCanonicalFieldName( m_id );
  878. return m_name;
  879. }
  880. BITMAPS SCH_FIELD::GetMenuImage() const
  881. {
  882. if( m_parent && ( m_parent->Type() == SCH_SYMBOL_T || m_parent->Type() == LIB_SYMBOL_T ) )
  883. {
  884. switch( m_id )
  885. {
  886. case FIELD_T::REFERENCE: return BITMAPS::edit_comp_ref;
  887. case FIELD_T::VALUE: return BITMAPS::edit_comp_value;
  888. case FIELD_T::FOOTPRINT: return BITMAPS::edit_comp_footprint;
  889. default: return BITMAPS::text;
  890. }
  891. }
  892. return BITMAPS::text;
  893. }
  894. bool SCH_FIELD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  895. {
  896. if( GetShownText( true ).IsEmpty() )
  897. return false;
  898. BOX2I rect = GetBoundingBox();
  899. // Text in symbol editor can have additional chars (ie: reference designators U? or U?A)
  900. if( m_parent && m_parent->Type() == LIB_SYMBOL_T )
  901. {
  902. SCH_FIELD temp( *this );
  903. temp.SetText( GetFullText() );
  904. rect = temp.GetBoundingBox();
  905. }
  906. rect.Inflate( aAccuracy );
  907. if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  908. {
  909. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  910. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  911. }
  912. return rect.Contains( aPosition );
  913. }
  914. bool SCH_FIELD::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  915. {
  916. if( GetShownText( true ).IsEmpty() )
  917. return false;
  918. if( m_flags & (STRUCT_DELETED | SKIP_STRUCT ) )
  919. return false;
  920. BOX2I rect = aRect;
  921. rect.Inflate( aAccuracy );
  922. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  923. {
  924. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  925. rect.Offset( label->GetSchematicTextOffset( nullptr ) );
  926. }
  927. if( aContained )
  928. return rect.Contains( GetBoundingBox() );
  929. return rect.Intersects( GetBoundingBox() );
  930. }
  931. bool SCH_FIELD::HitTest( const SHAPE_LINE_CHAIN& aPoly, bool aContained ) const
  932. {
  933. if( GetShownText( true ).IsEmpty() )
  934. return false;
  935. if( m_flags & (STRUCT_DELETED | SKIP_STRUCT ) )
  936. return false;
  937. BOX2I bbox = GetBoundingBox();
  938. if( GetParent() && GetParent()->Type() == SCH_GLOBAL_LABEL_T )
  939. {
  940. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( GetParent() );
  941. bbox.Offset( label->GetSchematicTextOffset( nullptr ) );
  942. }
  943. return KIGEOM::BoxHitTest( aPoly, bbox, aContained );
  944. }
  945. void SCH_FIELD::Plot( PLOTTER* aPlotter, bool aBackground, const SCH_PLOT_OPTS& aPlotOpts,
  946. int aUnit, int aBodyStyle, const VECTOR2I& aOffset, bool aDimmed )
  947. {
  948. wxString text;
  949. if( Schematic() )
  950. text = GetShownText( &Schematic()->CurrentSheet(), true );
  951. else
  952. text = GetShownText( true );
  953. if( ( !IsVisible() && !IsForceVisible() ) || text.IsEmpty() || aBackground )
  954. return;
  955. SCH_RENDER_SETTINGS* renderSettings = getRenderSettings( aPlotter );
  956. COLOR4D color = renderSettings->GetLayerColor( GetLayer() );
  957. int penWidth = GetEffectiveTextPenWidth( renderSettings->GetDefaultPenWidth() );
  958. COLOR4D bg = renderSettings->GetBackgroundColor();;
  959. if( bg == COLOR4D::UNSPECIFIED || !aPlotter->GetColorMode() )
  960. bg = COLOR4D::WHITE;
  961. if( aPlotter->GetColorMode() && GetTextColor() != COLOR4D::UNSPECIFIED )
  962. color = GetTextColor();
  963. if( aDimmed )
  964. {
  965. color.Desaturate( );
  966. color = color.Mix( bg, 0.5f );
  967. }
  968. penWidth = std::max( penWidth, renderSettings->GetMinPenWidth() );
  969. // clamp the pen width to be sure the text is readable
  970. penWidth = std::min( penWidth, std::min( GetTextSize().x, GetTextSize().y ) / 4 );
  971. if( !IsVisible() && !renderSettings->m_ShowHiddenFields )
  972. return;
  973. // Calculate the text orientation, according to the symbol orientation/mirror
  974. EDA_ANGLE orient = GetTextAngle();
  975. VECTOR2I textpos = GetTextPos();
  976. GR_TEXT_H_ALIGN_T hjustify = GetHorizJustify();
  977. GR_TEXT_V_ALIGN_T vjustify = GetVertJustify();
  978. if( renderSettings->m_Transform.y1 ) // Rotate symbol 90 deg.
  979. {
  980. if( orient.IsHorizontal() )
  981. orient = ANGLE_VERTICAL;
  982. else
  983. orient = ANGLE_HORIZONTAL;
  984. }
  985. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  986. {
  987. /*
  988. * Calculate the text justification, according to the symbol orientation/mirror. This is
  989. * a bit complicated due to cumulative calculations:
  990. * - numerous cases (mirrored or not, rotation)
  991. * - the plotter's Text() function will also recalculate H and V justifications according
  992. * to the text orientation
  993. * - when a symbol is mirrored the text is not, and justifications become a nightmare
  994. *
  995. * So the easier way is to use no justifications (centered text) and use GetBoundingBox
  996. * to know the text coordinate considered as centered.
  997. */
  998. hjustify = GR_TEXT_H_ALIGN_CENTER;
  999. vjustify = GR_TEXT_V_ALIGN_CENTER;
  1000. textpos = GetBoundingBox().Centre();
  1001. }
  1002. else if( m_parent && m_parent->Type() == SCH_GLOBAL_LABEL_T )
  1003. {
  1004. SCH_GLOBALLABEL* label = static_cast<SCH_GLOBALLABEL*>( m_parent );
  1005. textpos += label->GetSchematicTextOffset( renderSettings );
  1006. }
  1007. else if( m_parent && m_parent->Type() == SCH_DIRECTIVE_LABEL_T )
  1008. {
  1009. SCH_DIRECTIVE_LABEL* label = static_cast<SCH_DIRECTIVE_LABEL*>( m_parent );
  1010. std::shared_ptr<NETCLASS> nc = label->GetEffectiveNetClass();
  1011. if( nc && ( nc->GetSchematicColor() != COLOR4D::UNSPECIFIED ) && aPlotter->GetColorMode() )
  1012. color = nc->GetSchematicColor();
  1013. }
  1014. KIFONT::FONT* font = GetDrawFont( renderSettings );
  1015. TEXT_ATTRIBUTES attrs = GetAttributes();
  1016. attrs.m_StrokeWidth = penWidth;
  1017. attrs.m_Halign = hjustify;
  1018. attrs.m_Valign = vjustify;
  1019. attrs.m_Angle = orient;
  1020. attrs.m_Multiline = false;
  1021. aPlotter->PlotText( textpos, color, text, attrs, font, GetFontMetrics() );
  1022. if( m_id == FIELD_T::INTERSHEET_REFS && Schematic() )
  1023. {
  1024. if( SCH_LABEL_BASE* label = dynamic_cast<SCH_LABEL_BASE*>( m_parent ) )
  1025. {
  1026. std::vector<std::pair<wxString, wxString>> pages;
  1027. std::vector<wxString> pageHrefs;
  1028. label->GetIntersheetRefs( &Schematic()->CurrentSheet(), &pages );
  1029. for( const auto& [ pageNumber, sheetName ] : pages )
  1030. pageHrefs.push_back( wxT( "#" ) + pageNumber );
  1031. BOX2I bbox = GetBoundingBox();
  1032. bbox.Offset( label->GetSchematicTextOffset( renderSettings ) );
  1033. aPlotter->HyperlinkMenu( bbox, pageHrefs );
  1034. }
  1035. }
  1036. }
  1037. void SCH_FIELD::SetPosition( const VECTOR2I& aPosition )
  1038. {
  1039. // Actual positions are calculated by the rotation/mirror transform of the parent symbol
  1040. // of the field. The inverse transform is used to calculate the position relative to the
  1041. // parent symbol.
  1042. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  1043. {
  1044. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  1045. VECTOR2I relPos = aPosition - parentSymbol->GetPosition();
  1046. relPos = parentSymbol->GetTransform().InverseTransform().TransformCoordinate( relPos );
  1047. SetTextPos( relPos + parentSymbol->GetPosition() );
  1048. return;
  1049. }
  1050. SetTextPos( aPosition );
  1051. }
  1052. VECTOR2I SCH_FIELD::GetPosition() const
  1053. {
  1054. if( m_parent && m_parent->Type() == SCH_SYMBOL_T )
  1055. {
  1056. SCH_SYMBOL* parentSymbol = static_cast<SCH_SYMBOL*>( m_parent );
  1057. VECTOR2I relativePos = GetTextPos() - parentSymbol->GetPosition();
  1058. relativePos = parentSymbol->GetTransform().TransformCoordinate( relativePos );
  1059. return relativePos + parentSymbol->GetPosition();
  1060. }
  1061. return GetTextPos();
  1062. }
  1063. VECTOR2I SCH_FIELD::GetParentPosition() const
  1064. {
  1065. return m_parent ? m_parent->GetPosition() : VECTOR2I( 0, 0 );
  1066. }
  1067. bool SCH_FIELD::IsMandatory() const
  1068. {
  1069. return m_id == FIELD_T::REFERENCE
  1070. || m_id == FIELD_T::VALUE
  1071. || m_id == FIELD_T::FOOTPRINT
  1072. || m_id == FIELD_T::DATASHEET
  1073. || m_id == FIELD_T::DESCRIPTION
  1074. || m_id == FIELD_T::SHEET_NAME
  1075. || m_id == FIELD_T::SHEET_FILENAME
  1076. || m_id == FIELD_T::INTERSHEET_REFS;
  1077. }
  1078. bool SCH_FIELD::operator<( const SCH_ITEM& aItem ) const
  1079. {
  1080. if( Type() != aItem.Type() )
  1081. return Type() < aItem.Type();
  1082. auto field = static_cast<const SCH_FIELD*>( &aItem );
  1083. if( GetId() != field->GetId() )
  1084. return GetId() < field->GetId();
  1085. if( GetText() != field->GetText() )
  1086. return GetText() < field->GetText();
  1087. if( GetLibPosition().x != field->GetLibPosition().x )
  1088. return GetLibPosition().x < field->GetLibPosition().x;
  1089. if( GetLibPosition().y != field->GetLibPosition().y )
  1090. return GetLibPosition().y < field->GetLibPosition().y;
  1091. return GetName() < field->GetName();
  1092. }
  1093. bool SCH_FIELD::operator==(const SCH_ITEM& aOther) const
  1094. {
  1095. if( Type() != aOther.Type() )
  1096. return false;
  1097. const SCH_FIELD& field = static_cast<const SCH_FIELD&>( aOther );
  1098. return *this == field;
  1099. }
  1100. bool SCH_FIELD::operator==( const SCH_FIELD& aOther ) const
  1101. {
  1102. // Identical fields of different symbols are not equal.
  1103. if( !GetParentSymbol() || !aOther.GetParentSymbol()
  1104. || GetParentSymbol()->m_Uuid != aOther.GetParentSymbol()->m_Uuid )
  1105. {
  1106. return false;
  1107. }
  1108. if( IsMandatory() != aOther.IsMandatory() )
  1109. return false;
  1110. if( IsMandatory() )
  1111. {
  1112. if( GetId() != aOther.GetId() )
  1113. return false;
  1114. }
  1115. else
  1116. {
  1117. if( GetOrdinal() != aOther.GetOrdinal() )
  1118. return false;
  1119. }
  1120. if( GetPosition() != aOther.GetPosition() )
  1121. return false;
  1122. if( IsGeneratedField() != aOther.IsGeneratedField() )
  1123. return false;
  1124. if( IsNameShown() != aOther.IsNameShown() )
  1125. return false;
  1126. if( CanAutoplace() != aOther.CanAutoplace() )
  1127. return false;
  1128. return EDA_TEXT::operator==( aOther );
  1129. }
  1130. double SCH_FIELD::Similarity( const SCH_ITEM& aOther ) const
  1131. {
  1132. if( Type() != aOther.Type() )
  1133. return 0.0;
  1134. if( m_Uuid == aOther.m_Uuid )
  1135. return 1.0;
  1136. const SCH_FIELD& field = static_cast<const SCH_FIELD&>( aOther );
  1137. double similarity = 0.99; // The UUIDs are different, so we start with non-identity
  1138. if( GetId() != field.GetId() )
  1139. {
  1140. // We don't allow swapping of mandatory fields, so these cannot be the same item
  1141. if( IsMandatory() || field.IsMandatory() )
  1142. return 0.0;
  1143. else
  1144. similarity *= 0.5;
  1145. }
  1146. similarity *= SimilarityBase( aOther );
  1147. similarity *= EDA_TEXT::Similarity( field );
  1148. if( GetPosition() != field.GetPosition() )
  1149. similarity *= 0.5;
  1150. if( IsGeneratedField() != field.IsGeneratedField() )
  1151. similarity *= 0.5;
  1152. if( IsNameShown() != field.IsNameShown() )
  1153. similarity *= 0.5;
  1154. if( CanAutoplace() != field.CanAutoplace() )
  1155. similarity *= 0.5;
  1156. return similarity;
  1157. }
  1158. int SCH_FIELD::compare( const SCH_ITEM& aOther, int aCompareFlags ) const
  1159. {
  1160. wxASSERT( aOther.Type() == SCH_FIELD_T );
  1161. int compareFlags = aCompareFlags;
  1162. // For ERC tests, the field position has no matter, so do not test it
  1163. if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::ERC )
  1164. compareFlags |= SCH_ITEM::COMPARE_FLAGS::SKIP_TST_POS;
  1165. int retv = SCH_ITEM::compare( aOther, compareFlags );
  1166. if( retv )
  1167. return retv;
  1168. const SCH_FIELD* tmp = static_cast<const SCH_FIELD*>( &aOther );
  1169. // Equality test will vary depending whether or not the field is mandatory. Otherwise,
  1170. // sorting is done by ordinal.
  1171. if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::EQUALITY )
  1172. {
  1173. // Mandatory fields have fixed ordinals and their names can vary due to translated field
  1174. // names. Optional fields have fixed names and their ordinals can vary.
  1175. if( IsMandatory() )
  1176. {
  1177. if( m_id != tmp->m_id )
  1178. return (int) m_id - (int) tmp->m_id;
  1179. }
  1180. else
  1181. {
  1182. retv = m_name.Cmp( tmp->m_name );
  1183. if( retv )
  1184. return retv;
  1185. }
  1186. }
  1187. else // assume we're sorting
  1188. {
  1189. if( m_id != tmp->m_id )
  1190. return (int) m_id - (int) tmp->m_id;
  1191. }
  1192. bool ignoreFieldText = false;
  1193. if( m_id == FIELD_T::REFERENCE && !( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::EQUALITY ) )
  1194. ignoreFieldText = true;
  1195. if( m_id == FIELD_T::VALUE && ( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::ERC ) )
  1196. ignoreFieldText = true;
  1197. if( !ignoreFieldText )
  1198. {
  1199. retv = GetText().CmpNoCase( tmp->GetText() );
  1200. if( retv != 0 )
  1201. return retv;
  1202. }
  1203. if( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::EQUALITY )
  1204. {
  1205. if( GetTextPos().x != tmp->GetTextPos().x )
  1206. return GetTextPos().x - tmp->GetTextPos().x;
  1207. if( GetTextPos().y != tmp->GetTextPos().y )
  1208. return GetTextPos().y - tmp->GetTextPos().y;
  1209. }
  1210. // For ERC tests, the field size has no matter, so do not test it
  1211. if( !( aCompareFlags & SCH_ITEM::COMPARE_FLAGS::ERC ) )
  1212. {
  1213. if( GetTextWidth() != tmp->GetTextWidth() )
  1214. return GetTextWidth() - tmp->GetTextWidth();
  1215. if( GetTextHeight() != tmp->GetTextHeight() )
  1216. return GetTextHeight() - tmp->GetTextHeight();
  1217. }
  1218. return 0;
  1219. }
  1220. static struct SCH_FIELD_DESC
  1221. {
  1222. SCH_FIELD_DESC()
  1223. {
  1224. // These are defined in EDA_TEXT as well but initialization order is
  1225. // not defined, so this needs to be conditional. Defining in both
  1226. // places leads to duplicate symbols.
  1227. auto& h_inst = ENUM_MAP<GR_TEXT_H_ALIGN_T>::Instance();
  1228. if( h_inst.Choices().GetCount() == 0)
  1229. {
  1230. h_inst.Map( GR_TEXT_H_ALIGN_LEFT, _( "Left" ) );
  1231. h_inst.Map( GR_TEXT_H_ALIGN_CENTER, _( "Center" ) );
  1232. h_inst.Map( GR_TEXT_H_ALIGN_RIGHT, _( "Right" ) );
  1233. }
  1234. auto& v_inst = ENUM_MAP<GR_TEXT_V_ALIGN_T>::Instance();
  1235. if( v_inst.Choices().GetCount() == 0)
  1236. {
  1237. v_inst.Map( GR_TEXT_V_ALIGN_TOP, _( "Top" ) );
  1238. v_inst.Map( GR_TEXT_V_ALIGN_CENTER, _( "Center" ) );
  1239. v_inst.Map( GR_TEXT_V_ALIGN_BOTTOM, _( "Bottom" ) );
  1240. }
  1241. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  1242. REGISTER_TYPE( SCH_FIELD );
  1243. propMgr.AddTypeCast( new TYPE_CAST<SCH_FIELD, SCH_ITEM> );
  1244. propMgr.AddTypeCast( new TYPE_CAST<SCH_FIELD, EDA_TEXT> );
  1245. propMgr.InheritsAfter( TYPE_HASH( SCH_FIELD ), TYPE_HASH( SCH_ITEM ) );
  1246. propMgr.InheritsAfter( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ) );
  1247. const wxString textProps = _HKI( "Text Properties" );
  1248. auto horiz = new PROPERTY_ENUM<SCH_FIELD, GR_TEXT_H_ALIGN_T>(
  1249. _HKI( "Horizontal Justification" ), &SCH_FIELD::SetEffectiveHorizJustify,
  1250. &SCH_FIELD::GetEffectiveHorizJustify );
  1251. propMgr.ReplaceProperty( TYPE_HASH( EDA_TEXT ), _HKI( "Horizontal Justification" ), horiz,
  1252. textProps );
  1253. auto vert = new PROPERTY_ENUM<SCH_FIELD, GR_TEXT_V_ALIGN_T>(
  1254. _HKI( "Vertical Justification" ), &SCH_FIELD::SetEffectiveVertJustify,
  1255. &SCH_FIELD::GetEffectiveVertJustify );
  1256. propMgr.ReplaceProperty( TYPE_HASH( EDA_TEXT ), _HKI( "Vertical Justification" ), vert,
  1257. textProps );
  1258. propMgr.AddProperty( new PROPERTY<SCH_FIELD, bool>( _HKI( "Show Field Name" ),
  1259. &SCH_FIELD::SetNameShown, &SCH_FIELD::IsNameShown ) );
  1260. propMgr.AddProperty( new PROPERTY<SCH_FIELD, bool>( _HKI( "Allow Autoplacement" ),
  1261. &SCH_FIELD::SetCanAutoplace, &SCH_FIELD::CanAutoplace ) );
  1262. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Hyperlink" ) );
  1263. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Thickness" ) );
  1264. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Mirrored" ) );
  1265. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Width" ) );
  1266. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );
  1267. propMgr.AddProperty( new PROPERTY<SCH_FIELD, int>( _HKI( "Text Size" ),
  1268. &SCH_FIELD::SetSchTextSize, &SCH_FIELD::GetSchTextSize, PROPERTY_DISPLAY::PT_SIZE ),
  1269. _HKI( "Text Properties" ) );
  1270. propMgr.Mask( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Orientation" ) );
  1271. auto isNotGeneratedField =
  1272. []( INSPECTABLE* aItem ) -> bool
  1273. {
  1274. if( SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem ) )
  1275. return !field->IsGeneratedField();
  1276. return true;
  1277. };
  1278. propMgr.OverrideWriteability( TYPE_HASH( SCH_FIELD ), TYPE_HASH( EDA_TEXT ), _HKI( "Text" ),
  1279. isNotGeneratedField );
  1280. auto isNonMandatoryField =
  1281. []( INSPECTABLE* aItem ) -> bool
  1282. {
  1283. if( SCH_FIELD* field = dynamic_cast<SCH_FIELD*>( aItem ) )
  1284. return !field->IsMandatory();
  1285. return false;
  1286. };
  1287. propMgr.OverrideAvailability( TYPE_HASH( SCH_FIELD ), TYPE_HASH( SCH_ITEM ),
  1288. _HKI( "Private" ), isNonMandatoryField );
  1289. }
  1290. } _SCH_FIELD_DESC;
  1291. DECLARE_ENUM_TO_WXANY( GR_TEXT_H_ALIGN_T )
  1292. DECLARE_ENUM_TO_WXANY( GR_TEXT_V_ALIGN_T )