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.

499 lines
15 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2023 KiCad Developers, see AUTHORS.txt for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 2
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  19. * or you may search the http://www.gnu.org website for the version 2 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include <common.h>
  24. #include <sch_draw_panel.h>
  25. #include <plotters/plotter.h>
  26. #include <trigo.h>
  27. #include <base_units.h>
  28. #include <widgets/msgpanel.h>
  29. #include <bitmaps.h>
  30. #include <eda_draw_frame.h>
  31. #include <lib_item.h>
  32. #include <general.h>
  33. #include <transform.h>
  34. #include <settings/color_settings.h>
  35. #include <lib_text.h>
  36. #include <default_values.h> // For some default values
  37. #include <string_utils.h>
  38. LIB_TEXT::LIB_TEXT( LIB_SYMBOL* aParent ) :
  39. LIB_ITEM( LIB_TEXT_T, aParent ),
  40. EDA_TEXT( schIUScale, wxEmptyString )
  41. {
  42. SetTextSize( VECTOR2I( schIUScale.MilsToIU( DEFAULT_TEXT_SIZE ),
  43. schIUScale.MilsToIU( DEFAULT_TEXT_SIZE ) ) );
  44. }
  45. void LIB_TEXT::ViewGetLayers( int aLayers[], int& aCount ) const
  46. {
  47. aCount = 2;
  48. aLayers[0] = IsPrivate() ? LAYER_PRIVATE_NOTES : LAYER_DEVICE;
  49. aLayers[1] = LAYER_SELECTION_SHADOWS;
  50. }
  51. bool LIB_TEXT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  52. {
  53. EDA_TEXT tmp_text( *this );
  54. tmp_text.SetTextPos( DefaultTransform.TransformCoordinate( GetTextPos() ) );
  55. /* The text orientation may need to be flipped if the
  56. * transformation matrix causes xy axes to be flipped.
  57. * this simple algo works only for schematic matrix (rot 90 or/and mirror)
  58. */
  59. bool t1 = ( DefaultTransform.x1 != 0 ) ^ ( GetTextAngle() != ANGLE_HORIZONTAL );
  60. tmp_text.SetTextAngle( t1 ? ANGLE_HORIZONTAL : ANGLE_VERTICAL );
  61. return tmp_text.TextHitTest( aPosition, aAccuracy );
  62. }
  63. EDA_ITEM* LIB_TEXT::Clone() const
  64. {
  65. LIB_TEXT* newitem = new LIB_TEXT( nullptr );
  66. newitem->m_parent = m_parent;
  67. newitem->m_unit = m_unit;
  68. newitem->m_convert = m_convert;
  69. newitem->m_private = m_private;
  70. newitem->m_flags = m_flags;
  71. newitem->SetText( GetText() );
  72. newitem->SetAttributes( *this );
  73. return newitem;
  74. }
  75. int LIB_TEXT::compare( const LIB_ITEM& aOther, int aCompareFlags ) const
  76. {
  77. wxASSERT( aOther.Type() == LIB_TEXT_T );
  78. int retv = LIB_ITEM::compare( aOther, aCompareFlags );
  79. if( retv )
  80. return retv;
  81. const LIB_TEXT* tmp = ( LIB_TEXT* ) &aOther;
  82. int result = GetText().CmpNoCase( tmp->GetText() );
  83. if( result != 0 )
  84. return result;
  85. if( GetTextPos().x != tmp->GetTextPos().x )
  86. return GetTextPos().x - tmp->GetTextPos().x;
  87. if( GetTextPos().y != tmp->GetTextPos().y )
  88. return GetTextPos().y - tmp->GetTextPos().y;
  89. if( GetTextWidth() != tmp->GetTextWidth() )
  90. return GetTextWidth() - tmp->GetTextWidth();
  91. if( GetTextHeight() != tmp->GetTextHeight() )
  92. return GetTextHeight() - tmp->GetTextHeight();
  93. return 0;
  94. }
  95. void LIB_TEXT::Offset( const VECTOR2I& aOffset )
  96. {
  97. EDA_TEXT::Offset( aOffset );
  98. }
  99. void LIB_TEXT::MoveTo( const VECTOR2I& newPosition )
  100. {
  101. SetTextPos( newPosition );
  102. }
  103. void LIB_TEXT::NormalizeJustification( bool inverse )
  104. {
  105. if( GetHorizJustify() == GR_TEXT_H_ALIGN_CENTER && GetVertJustify() == GR_TEXT_V_ALIGN_CENTER )
  106. return;
  107. VECTOR2I delta( 0, 0 );
  108. BOX2I bbox = GetTextBox();
  109. if( GetTextAngle().IsHorizontal() )
  110. {
  111. if( GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  112. delta.x = bbox.GetWidth() / 2;
  113. else if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  114. delta.x = - bbox.GetWidth() / 2;
  115. if( GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  116. delta.y = - bbox.GetHeight() / 2;
  117. else if( GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  118. delta.y = bbox.GetHeight() / 2;
  119. }
  120. else
  121. {
  122. if( GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  123. delta.y = bbox.GetWidth() / 2;
  124. else if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  125. delta.y = - bbox.GetWidth() / 2;
  126. if( GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  127. delta.x = + bbox.GetHeight() / 2;
  128. else if( GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  129. delta.x = - bbox.GetHeight() / 2;
  130. }
  131. if( inverse )
  132. SetTextPos( GetTextPos() - delta );
  133. else
  134. SetTextPos( GetTextPos() + delta );
  135. }
  136. void LIB_TEXT::MirrorHorizontal( const VECTOR2I& center )
  137. {
  138. NormalizeJustification( false );
  139. int x = GetTextPos().x;
  140. x -= center.x;
  141. x *= -1;
  142. x += center.x;
  143. if( GetTextAngle().IsHorizontal() )
  144. {
  145. if( GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  146. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  147. else if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  148. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  149. }
  150. else
  151. {
  152. if( GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  153. SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  154. else if( GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  155. SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  156. }
  157. SetTextX( x );
  158. NormalizeJustification( true );
  159. }
  160. void LIB_TEXT::MirrorVertical( const VECTOR2I& center )
  161. {
  162. NormalizeJustification( false );
  163. int y = GetTextPos().y;
  164. y -= center.y;
  165. y *= -1;
  166. y += center.y;
  167. if( GetTextAngle().IsHorizontal() )
  168. {
  169. if( GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  170. SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  171. else if( GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  172. SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  173. }
  174. else
  175. {
  176. if( GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  177. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  178. else if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  179. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  180. }
  181. SetTextY( y );
  182. NormalizeJustification( true );
  183. }
  184. void LIB_TEXT::Rotate( const VECTOR2I& center, bool aRotateCCW )
  185. {
  186. NormalizeJustification( false );
  187. EDA_ANGLE rot_angle = aRotateCCW ? -ANGLE_90 : ANGLE_90;
  188. VECTOR2I pt = GetTextPos();
  189. RotatePoint( pt, center, rot_angle );
  190. SetTextPos( pt );
  191. if( GetTextAngle().IsHorizontal() )
  192. {
  193. SetTextAngle( ANGLE_VERTICAL );
  194. }
  195. else
  196. {
  197. // 180° rotation is a mirror
  198. if( GetHorizJustify() == GR_TEXT_H_ALIGN_LEFT )
  199. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  200. else if( GetHorizJustify() == GR_TEXT_H_ALIGN_RIGHT )
  201. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  202. if( GetVertJustify() == GR_TEXT_V_ALIGN_TOP )
  203. SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  204. else if( GetVertJustify() == GR_TEXT_V_ALIGN_BOTTOM )
  205. SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  206. SetTextAngle( ANGLE_0 );
  207. }
  208. NormalizeJustification( true );
  209. }
  210. void LIB_TEXT::Plot( PLOTTER* plotter, bool aBackground, const VECTOR2I& offset,
  211. const TRANSFORM& aTransform, bool aDimmed ) const
  212. {
  213. wxASSERT( plotter != nullptr );
  214. if( IsPrivate() )
  215. return;
  216. if( aBackground )
  217. return;
  218. RENDER_SETTINGS* settings = plotter->RenderSettings();
  219. BOX2I bBox = GetBoundingBox();
  220. // convert coordinates from draw Y axis to symbol_editor Y axis
  221. bBox.RevertYAxis();
  222. /*
  223. * Calculate the text justification, according to the symbol orientation/mirror. This is
  224. * a bit complicated due to cumulative calculations:
  225. * - numerous cases (mirrored or not, rotation)
  226. * - the plotter's Text() function will also recalculate H and V justifications according
  227. * to the text orientation
  228. * - when a symbol is mirrored the text is not, and justifications become a nightmare
  229. *
  230. * So the easier way is to use no justifications (centered text) and use GetBoundingBox to
  231. * know the text coordinate considered as centered.
  232. */
  233. VECTOR2I txtpos = bBox.Centre();
  234. TEXT_ATTRIBUTES attrs = GetAttributes();
  235. attrs.m_Halign = GR_TEXT_H_ALIGN_CENTER;
  236. attrs.m_Valign = GR_TEXT_V_ALIGN_CENTER;
  237. // The text orientation may need to be flipped if the transformation matrix causes xy
  238. // axes to be flipped.
  239. int t1 = ( aTransform.x1 != 0 ) ^ ( GetTextAngle() != ANGLE_HORIZONTAL );
  240. VECTOR2I pos = aTransform.TransformCoordinate( txtpos ) + offset;
  241. COLOR4D color = GetTextColor();
  242. COLOR4D bg = settings->GetBackgroundColor();
  243. if( !plotter->GetColorMode() || color == COLOR4D::UNSPECIFIED )
  244. color = settings->GetLayerColor( LAYER_DEVICE );
  245. if( !IsVisible() )
  246. bg = settings->GetLayerColor( LAYER_HIDDEN );
  247. else if( bg == COLOR4D::UNSPECIFIED || !plotter->GetColorMode() )
  248. bg = COLOR4D::WHITE;
  249. if( aDimmed )
  250. {
  251. color.Desaturate( );
  252. color = color.Mix( bg, 0.5f );
  253. }
  254. int penWidth = std::max( GetEffectiveTextPenWidth(), settings->GetMinPenWidth() );
  255. KIFONT::FONT* font = GetFont();
  256. if( !font )
  257. font = KIFONT::FONT::GetFont( settings->GetDefaultFont(), IsBold(), IsItalic() );
  258. attrs.m_StrokeWidth = penWidth;
  259. attrs.m_Angle = t1 ? ANGLE_HORIZONTAL : ANGLE_VERTICAL;
  260. plotter->PlotText( pos, color, GetText(), attrs, font );
  261. }
  262. int LIB_TEXT::GetPenWidth() const
  263. {
  264. return GetEffectiveTextPenWidth();
  265. }
  266. KIFONT::FONT* LIB_TEXT::getDrawFont() const
  267. {
  268. KIFONT::FONT* font = EDA_TEXT::GetFont();
  269. if( !font )
  270. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  271. return font;
  272. }
  273. void LIB_TEXT::print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset, void* aData,
  274. const TRANSFORM& aTransform, bool aDimmed )
  275. {
  276. wxDC* DC = aSettings->GetPrintDC();
  277. COLOR4D color = GetTextColor();
  278. bool blackAndWhiteMode = GetGRForceBlackPenState();
  279. int penWidth = std::max( GetEffectiveTextPenWidth(), aSettings->GetDefaultPenWidth() );
  280. if( blackAndWhiteMode || color == COLOR4D::UNSPECIFIED )
  281. color = aSettings->GetLayerColor( LAYER_DEVICE );
  282. COLOR4D bg = aSettings->GetBackgroundColor();
  283. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  284. bg = COLOR4D::WHITE;
  285. if( !IsVisible() )
  286. bg = aSettings->GetLayerColor( LAYER_HIDDEN );
  287. if( aDimmed )
  288. {
  289. color.Desaturate( );
  290. color = color.Mix( bg, 0.5f );
  291. }
  292. // Calculate the text orientation, according to the symbol orientation/mirror (needed when
  293. // draw text in schematic)
  294. EDA_ANGLE orient = GetTextAngle();
  295. if( aTransform.y1 ) // Rotate symbol 90 degrees.
  296. {
  297. if( orient == ANGLE_HORIZONTAL )
  298. orient = ANGLE_VERTICAL;
  299. else
  300. orient = ANGLE_HORIZONTAL;
  301. }
  302. KIFONT::FONT* font = GetFont();
  303. if( !font )
  304. font = KIFONT::FONT::GetFont( aSettings->GetDefaultFont(), IsBold(), IsItalic() );
  305. /*
  306. * Calculate the text justification, according to the symbol orientation/mirror.
  307. * This is a bit complicated due to cumulative calculations:
  308. * - numerous cases (mirrored or not, rotation)
  309. * - the GRText function will also recalculate H and V justifications according to the text
  310. * orientation.
  311. * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
  312. * to calculate so the more easily way is to use no justifications (centered text) and
  313. * use GetBoundingBox to know the text coordinate considered as centered
  314. */
  315. BOX2I bBox = GetBoundingBox();
  316. // convert coordinates from draw Y axis to symbol_editor Y axis:
  317. bBox.RevertYAxis();
  318. VECTOR2I txtpos = bBox.Centre();
  319. // Calculate pos according to mirror/rotation.
  320. txtpos = aTransform.TransformCoordinate( txtpos ) + aOffset;
  321. GRPrintText( DC, txtpos, color, GetShownText( true ), orient, GetTextSize(),
  322. GR_TEXT_H_ALIGN_CENTER, GR_TEXT_V_ALIGN_CENTER, penWidth, IsItalic(), IsBold(),
  323. font );
  324. }
  325. void LIB_TEXT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  326. {
  327. wxString msg;
  328. LIB_ITEM::GetMsgPanelInfo( aFrame, aList );
  329. // Don't use GetShownText() here; we want to show the user the variable references
  330. aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  331. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  332. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  333. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  334. switch ( GetHorizJustify() )
  335. {
  336. case GR_TEXT_H_ALIGN_LEFT: msg = _( "Left" ); break;
  337. case GR_TEXT_H_ALIGN_CENTER: msg = _( "Center" ); break;
  338. case GR_TEXT_H_ALIGN_RIGHT: msg = _( "Right" ); break;
  339. }
  340. aList.emplace_back( _( "H Justification" ), msg );
  341. switch ( GetVertJustify() )
  342. {
  343. case GR_TEXT_V_ALIGN_TOP: msg = _( "Top" ); break;
  344. case GR_TEXT_V_ALIGN_CENTER: msg = _( "Center" ); break;
  345. case GR_TEXT_V_ALIGN_BOTTOM: msg = _( "Bottom" ); break;
  346. }
  347. aList.emplace_back( _( "V Justification" ), msg );
  348. }
  349. const BOX2I LIB_TEXT::GetBoundingBox() const
  350. {
  351. /* Y coordinates for LIB_ITEMS are bottom to top, so we must invert the Y position when
  352. * calling GetTextBox() that works using top to bottom Y axis orientation.
  353. */
  354. BOX2I bbox = GetTextBox( -1, true );
  355. bbox.RevertYAxis();
  356. // We are using now a bottom to top Y axis.
  357. VECTOR2I orig = bbox.GetOrigin();
  358. VECTOR2I end = bbox.GetEnd();
  359. RotatePoint( orig, GetTextPos(), -GetTextAngle() );
  360. RotatePoint( end, GetTextPos(), -GetTextAngle() );
  361. bbox.SetOrigin( orig );
  362. bbox.SetEnd( end );
  363. // We are using now a top to bottom Y axis:
  364. bbox.RevertYAxis();
  365. return bbox;
  366. }
  367. wxString LIB_TEXT::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  368. {
  369. return wxString::Format( _( "Graphic Text '%s'" ), KIUI::EllipsizeMenuText( GetText() ) );
  370. }
  371. BITMAPS LIB_TEXT::GetMenuImage() const
  372. {
  373. return BITMAPS::text;
  374. }
  375. void LIB_TEXT::BeginEdit( const VECTOR2I& aPosition )
  376. {
  377. SetTextPos( aPosition );
  378. }
  379. void LIB_TEXT::CalcEdit( const VECTOR2I& aPosition )
  380. {
  381. SetTextPos( aPosition );
  382. }