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.

523 lines
13 KiB

6 years ago
6 years ago
6 years ago
6 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2018 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2004-2022 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 <eda_item.h>
  25. #include <string_utils.h>
  26. #include <sch_draw_panel.h>
  27. #include <eda_draw_frame.h>
  28. #include <plotters/plotter.h>
  29. #include <trigo.h>
  30. #include <base_units.h>
  31. #include <widgets/msgpanel.h>
  32. #include <bitmaps.h>
  33. #include <general.h>
  34. #include <lib_symbol.h>
  35. #include <transform.h>
  36. #include <lib_field.h>
  37. #include <template_fieldnames.h>
  38. #include <settings/color_settings.h>
  39. LIB_FIELD::LIB_FIELD( LIB_SYMBOL* aParent, int aId ) :
  40. LIB_ITEM( LIB_FIELD_T, aParent )
  41. {
  42. Init( aId );
  43. }
  44. LIB_FIELD::LIB_FIELD( int aId ) :
  45. LIB_ITEM( LIB_FIELD_T, nullptr )
  46. {
  47. Init( aId );
  48. }
  49. LIB_FIELD::LIB_FIELD( int aId, const wxString& aName ) :
  50. LIB_ITEM( LIB_FIELD_T, nullptr )
  51. {
  52. Init( aId );
  53. m_name = aName;
  54. }
  55. LIB_FIELD::~LIB_FIELD()
  56. {
  57. }
  58. LIB_FIELD& LIB_FIELD::operator=( const LIB_FIELD& field )
  59. {
  60. m_id = field.m_id;
  61. m_name = field.m_name;
  62. m_parent = field.m_parent;
  63. SetText( field.GetText() );
  64. SetAttributes( field );
  65. return *this;
  66. }
  67. void LIB_FIELD::Init( int aId )
  68. {
  69. wxCHECK2( aId >= 0, aId = MANDATORY_FIELDS );
  70. m_id = aId;
  71. SetTextAngle( ANGLE_HORIZONTAL ); // constructor already did this.
  72. // Fields in RAM must always have names, because we are trying to get less dependent on
  73. // field ids and more dependent on names. Plus assumptions are made in the field editors.
  74. m_name = TEMPLATE_FIELDNAME::GetDefaultFieldName( aId );
  75. // By contrast, VALUE and REFERENCE are are always constructed as initially visible, and
  76. // template fieldsnames' initial visibility is controlled by the template fieldname config.
  77. if( aId == DATASHEET_FIELD || aId == FOOTPRINT_FIELD )
  78. SetVisible( false );
  79. }
  80. void LIB_FIELD::SetId( int aId )
  81. {
  82. wxCHECK2( aId >= 0, aId = MANDATORY_FIELDS );
  83. m_id = aId;
  84. }
  85. int LIB_FIELD::GetPenWidth() const
  86. {
  87. return GetEffectiveTextPenWidth();
  88. }
  89. KIFONT::FONT* LIB_FIELD::GetDrawFont() const
  90. {
  91. KIFONT::FONT* font = EDA_TEXT::GetFont();
  92. if( !font )
  93. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  94. return font;
  95. }
  96. void LIB_FIELD::print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset, void* aData,
  97. const TRANSFORM& aTransform )
  98. {
  99. wxDC* DC = aSettings->GetPrintDC();
  100. COLOR4D color = aSettings->GetLayerColor( IsVisible() ? GetDefaultLayer() : LAYER_HIDDEN );
  101. bool blackAndWhiteMode = GetGRForceBlackPenState();
  102. int penWidth = GetEffectivePenWidth( aSettings );
  103. VECTOR2I text_pos = aTransform.TransformCoordinate( GetTextPos() ) + aOffset;
  104. wxString text = aData ? *static_cast<wxString*>( aData ) : GetText();
  105. if( !blackAndWhiteMode && GetTextColor() != COLOR4D::UNSPECIFIED )
  106. color = GetTextColor();
  107. GRPrintText( DC, text_pos, color, text, GetTextAngle(), GetTextSize(), GetHorizJustify(),
  108. GetVertJustify(), penWidth, IsItalic(), IsBold(), GetDrawFont() );
  109. }
  110. bool LIB_FIELD::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  111. {
  112. // Because HitTest is mainly used to select the field return false if it is empty
  113. if( GetText().IsEmpty() )
  114. return false;
  115. // Build a temporary copy of the text for hit testing
  116. EDA_TEXT tmp_text( *this );
  117. // Reference designator text has one or 2 additional character (displays U? or U?A)
  118. if( m_id == REFERENCE_FIELD )
  119. {
  120. const LIB_SYMBOL* parent = dynamic_cast<const LIB_SYMBOL*>( m_parent );
  121. wxString extended_text = tmp_text.GetText();
  122. extended_text.Append('?');
  123. if ( parent && parent->GetUnitCount() > 1 )
  124. extended_text.Append('A');
  125. tmp_text.SetText( extended_text );
  126. }
  127. tmp_text.SetTextPos( DefaultTransform.TransformCoordinate( GetTextPos() ) );
  128. // The text orientation may need to be flipped if the transformation matrix causes xy axes
  129. // to be flipped. This simple algo works only for schematic matrix (rot 90 or/and mirror)
  130. bool t1 = ( DefaultTransform.x1 != 0 ) ^ ( GetTextAngle() != ANGLE_HORIZONTAL );
  131. tmp_text.SetTextAngle( t1 ? ANGLE_HORIZONTAL : ANGLE_VERTICAL );
  132. return tmp_text.TextHitTest( aPosition, aAccuracy );
  133. }
  134. EDA_ITEM* LIB_FIELD::Clone() const
  135. {
  136. LIB_FIELD* newfield = new LIB_FIELD( m_id );
  137. Copy( newfield );
  138. return (EDA_ITEM*) newfield;
  139. }
  140. void LIB_FIELD::Copy( LIB_FIELD* aTarget ) const
  141. {
  142. aTarget->m_name = m_name;
  143. aTarget->CopyText( *this );
  144. aTarget->SetAttributes( *this );
  145. aTarget->SetParent( m_parent );
  146. }
  147. int LIB_FIELD::compare( const LIB_ITEM& aOther, int aCompareFlags ) const
  148. {
  149. wxASSERT( aOther.Type() == LIB_FIELD_T );
  150. int retv = LIB_ITEM::compare( aOther, aCompareFlags );
  151. if( retv )
  152. return retv;
  153. const LIB_FIELD* tmp = ( LIB_FIELD* ) &aOther;
  154. // Equality test will vary depending whether or not the field is mandatory. Otherwise,
  155. // sorting is done by ordinal.
  156. if( aCompareFlags & LIB_ITEM::COMPARE_FLAGS::EQUALITY )
  157. {
  158. // Mandatory fields have fixed ordinals and their names can vary due to translated field
  159. // names. Optional fields have fixed names and their ordinals can vary.
  160. if( IsMandatory() )
  161. {
  162. if( m_id != tmp->m_id )
  163. return m_id - tmp->m_id;
  164. }
  165. else
  166. {
  167. retv = m_name.Cmp( tmp->m_name );
  168. if( retv )
  169. return retv;
  170. }
  171. }
  172. else
  173. {
  174. if( m_id != tmp->m_id )
  175. return m_id - tmp->m_id;
  176. }
  177. retv = GetText().CmpNoCase( tmp->GetText() );
  178. if( retv != 0 )
  179. return retv;
  180. if( GetTextPos().x != tmp->GetTextPos().x )
  181. return GetTextPos().x - tmp->GetTextPos().x;
  182. if( GetTextPos().y != tmp->GetTextPos().y )
  183. return GetTextPos().y - tmp->GetTextPos().y;
  184. if( GetTextWidth() != tmp->GetTextWidth() )
  185. return GetTextWidth() - tmp->GetTextWidth();
  186. if( GetTextHeight() != tmp->GetTextHeight() )
  187. return GetTextHeight() - tmp->GetTextHeight();
  188. return 0;
  189. }
  190. void LIB_FIELD::Offset( const VECTOR2I& aOffset )
  191. {
  192. EDA_TEXT::Offset( aOffset );
  193. }
  194. void LIB_FIELD::MoveTo( const VECTOR2I& newPosition )
  195. {
  196. EDA_TEXT::SetTextPos( newPosition );
  197. }
  198. void LIB_FIELD::MirrorHorizontal( const VECTOR2I& center )
  199. {
  200. int x = GetTextPos().x;
  201. x -= center.x;
  202. x *= -1;
  203. x += center.x;
  204. SetTextX( x );
  205. }
  206. void LIB_FIELD::MirrorVertical( const VECTOR2I& center )
  207. {
  208. int y = GetTextPos().y;
  209. y -= center.y;
  210. y *= -1;
  211. y += center.y;
  212. SetTextY( y );
  213. }
  214. void LIB_FIELD::Rotate( const VECTOR2I& center, bool aRotateCCW )
  215. {
  216. EDA_ANGLE rot_angle = aRotateCCW ? -ANGLE_90 : ANGLE_90;
  217. VECTOR2I pt = GetTextPos();
  218. RotatePoint( pt, center, rot_angle );
  219. SetTextPos( pt );
  220. SetTextAngle( GetTextAngle() != ANGLE_HORIZONTAL ? ANGLE_HORIZONTAL : ANGLE_VERTICAL );
  221. }
  222. void LIB_FIELD::Plot( PLOTTER* aPlotter, bool aBackground, const VECTOR2I& aOffset,
  223. const TRANSFORM& aTransform ) const
  224. {
  225. if( GetText().IsEmpty() || aBackground )
  226. return;
  227. // Calculate the text orientation, according to the symbol orientation/mirror.
  228. EDA_ANGLE orient = GetTextAngle();
  229. if( aTransform.y1 ) // Rotate symbol 90 deg.
  230. {
  231. if( orient.IsHorizontal() )
  232. orient = ANGLE_VERTICAL;
  233. else
  234. orient = ANGLE_HORIZONTAL;
  235. }
  236. EDA_RECT bbox = GetBoundingBox();
  237. bbox.RevertYAxis();
  238. GR_TEXT_H_ALIGN_T hjustify = GR_TEXT_H_ALIGN_CENTER;
  239. GR_TEXT_V_ALIGN_T vjustify = GR_TEXT_V_ALIGN_CENTER;
  240. VECTOR2I textpos = aTransform.TransformCoordinate( bbox.Centre() ) + aOffset;
  241. COLOR4D color;
  242. if( aPlotter->GetColorMode() )
  243. {
  244. if( GetTextColor() != COLOR4D::UNSPECIFIED )
  245. color = GetTextColor();
  246. else
  247. color = aPlotter->RenderSettings()->GetLayerColor( GetDefaultLayer() );
  248. }
  249. else
  250. {
  251. color = COLOR4D::BLACK;
  252. }
  253. int penWidth = GetEffectivePenWidth( aPlotter->RenderSettings() );
  254. aPlotter->Text( textpos, color, GetShownText(), orient, GetTextSize(), hjustify, vjustify,
  255. penWidth, IsItalic(), IsBold(), false, GetDrawFont() );
  256. }
  257. wxString LIB_FIELD::GetFullText( int unit ) const
  258. {
  259. if( m_id != REFERENCE_FIELD )
  260. return GetText();
  261. wxString text = GetText();
  262. text << wxT( "?" );
  263. wxCHECK( GetParent(), text );
  264. if( GetParent()->IsMulti() )
  265. text << LIB_SYMBOL::SubReference( unit );
  266. return text;
  267. }
  268. const EDA_RECT LIB_FIELD::GetBoundingBox() const
  269. {
  270. /* Y coordinates for LIB_ITEMS are bottom to top, so we must invert the Y position when
  271. * calling GetTextBox() that works using top to bottom Y axis orientation.
  272. */
  273. EDA_RECT rect = GetTextBox( -1, true );
  274. rect.RevertYAxis();
  275. // We are using now a bottom to top Y axis.
  276. VECTOR2I orig = rect.GetOrigin();
  277. VECTOR2I end = rect.GetEnd();
  278. RotatePoint( orig, GetTextPos(), -GetTextAngle() );
  279. RotatePoint( end, GetTextPos(), -GetTextAngle() );
  280. rect.SetOrigin( orig );
  281. rect.SetEnd( end );
  282. // We are using now a top to bottom Y axis:
  283. rect.RevertYAxis();
  284. return rect;
  285. }
  286. void LIB_FIELD::ViewGetLayers( int aLayers[], int& aCount ) const
  287. {
  288. aCount = 2;
  289. switch( m_id )
  290. {
  291. case REFERENCE_FIELD: aLayers[0] = LAYER_REFERENCEPART; break;
  292. case VALUE_FIELD: aLayers[0] = LAYER_VALUEPART; break;
  293. default: aLayers[0] = LAYER_FIELDS; break;
  294. }
  295. aLayers[1] = LAYER_SELECTION_SHADOWS;
  296. }
  297. SCH_LAYER_ID LIB_FIELD::GetDefaultLayer() const
  298. {
  299. switch( m_id )
  300. {
  301. case REFERENCE_FIELD: return LAYER_REFERENCEPART;
  302. case VALUE_FIELD: return LAYER_VALUEPART;
  303. default: return LAYER_FIELDS;
  304. }
  305. }
  306. wxString LIB_FIELD::GetName( bool aUseDefaultName ) const
  307. {
  308. if( m_name.IsEmpty() && aUseDefaultName )
  309. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  310. return m_name;
  311. }
  312. wxString LIB_FIELD::GetCanonicalName() const
  313. {
  314. switch( m_id )
  315. {
  316. case REFERENCE_FIELD:
  317. case VALUE_FIELD:
  318. case FOOTPRINT_FIELD:
  319. case DATASHEET_FIELD:
  320. return TEMPLATE_FIELDNAME::GetDefaultFieldName( m_id );
  321. }
  322. return m_name;
  323. }
  324. void LIB_FIELD::SetName( const wxString& aName )
  325. {
  326. // Mandatory field names are fixed.
  327. if( IsMandatory() )
  328. {
  329. wxFAIL_MSG( "trying to set a MANDATORY_FIELD's name\n" );
  330. return;
  331. }
  332. if( m_name != aName )
  333. {
  334. m_name = aName;
  335. SetModified();
  336. }
  337. }
  338. wxString LIB_FIELD::GetSelectMenuText( EDA_UNITS aUnits ) const
  339. {
  340. return wxString::Format( "%s '%s'", GetName(), ShortenedShownText() );
  341. }
  342. void LIB_FIELD::BeginEdit( const VECTOR2I& aPosition )
  343. {
  344. SetTextPos( aPosition );
  345. }
  346. void LIB_FIELD::CalcEdit( const VECTOR2I& aPosition )
  347. {
  348. SetTextPos( aPosition );
  349. }
  350. void LIB_FIELD::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  351. {
  352. wxString msg;
  353. LIB_ITEM::GetMsgPanelInfo( aFrame, aList );
  354. aList.emplace_back( _( "Field" ), GetName() );
  355. // Don't use GetShownText() here; we want to show the user the variable references
  356. aList.emplace_back( _( "Text" ), UnescapeString( GetText() ) );
  357. aList.emplace_back( _( "Visible" ), IsVisible() ? _( "Yes" ) : _( "No" ) );
  358. aList.emplace_back( _( "Font" ), GetDrawFont()->GetName() );
  359. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  360. aList.emplace_back( _( "Text Size" ), MessageTextFromValue( aFrame->GetUserUnits(),
  361. GetTextWidth() ) );
  362. switch ( GetHorizJustify() )
  363. {
  364. case GR_TEXT_H_ALIGN_LEFT: msg = _( "Left" ); break;
  365. case GR_TEXT_H_ALIGN_CENTER: msg = _( "Center" ); break;
  366. case GR_TEXT_H_ALIGN_RIGHT: msg = _( "Right" ); break;
  367. }
  368. aList.emplace_back( _( "H Justification" ), msg );
  369. switch ( GetVertJustify() )
  370. {
  371. case GR_TEXT_V_ALIGN_TOP: msg = _( "Top" ); break;
  372. case GR_TEXT_V_ALIGN_CENTER: msg = _( "Center" ); break;
  373. case GR_TEXT_V_ALIGN_BOTTOM: msg = _( "Bottom" ); break;
  374. }
  375. aList.emplace_back( _( "V Justification" ), msg );
  376. }
  377. BITMAPS LIB_FIELD::GetMenuImage() const
  378. {
  379. return BITMAPS::move;
  380. }
  381. bool LIB_FIELD::IsMandatory() const
  382. {
  383. return m_id >= 0 && m_id < MANDATORY_FIELDS;
  384. }