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.

422 lines
12 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2021 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( wxEmptyString )
  41. {
  42. SetTextSize( wxSize( Mils2iu( DEFAULT_TEXT_SIZE ), Mils2iu( DEFAULT_TEXT_SIZE ) ) );
  43. }
  44. void LIB_TEXT::ViewGetLayers( int aLayers[], int& aCount ) const
  45. {
  46. aCount = 2;
  47. aLayers[0] = LAYER_DEVICE;
  48. aLayers[1] = LAYER_SELECTION_SHADOWS;
  49. }
  50. bool LIB_TEXT::HitTest( const wxPoint& aPosition, int aAccuracy ) const
  51. {
  52. EDA_TEXT tmp_text( *this );
  53. tmp_text.SetTextPos( DefaultTransform.TransformCoordinate( GetTextPos() ) );
  54. /* The text orientation may need to be flipped if the
  55. * transformation matrix causes xy axes to be flipped.
  56. * this simple algo works only for schematic matrix (rot 90 or/and mirror)
  57. */
  58. bool t1 = ( DefaultTransform.x1 != 0 ) ^ ( GetTextAngle() != 0 );
  59. tmp_text.SetTextAngle( t1 ? TEXT_ANGLE_HORIZ : TEXT_ANGLE_VERT );
  60. return tmp_text.TextHitTest( aPosition, aAccuracy );
  61. }
  62. EDA_ITEM* LIB_TEXT::Clone() const
  63. {
  64. LIB_TEXT* newitem = new LIB_TEXT( nullptr );
  65. newitem->m_unit = m_unit;
  66. newitem->m_convert = m_convert;
  67. newitem->m_flags = m_flags;
  68. newitem->SetText( GetText() );
  69. newitem->SetEffects( *this );
  70. return newitem;
  71. }
  72. int LIB_TEXT::compare( const LIB_ITEM& aOther, LIB_ITEM::COMPARE_FLAGS aCompareFlags ) const
  73. {
  74. wxASSERT( aOther.Type() == LIB_TEXT_T );
  75. int retv = LIB_ITEM::compare( aOther, aCompareFlags );
  76. if( retv )
  77. return retv;
  78. const LIB_TEXT* tmp = ( LIB_TEXT* ) &aOther;
  79. int result = GetText().CmpNoCase( tmp->GetText() );
  80. if( result != 0 )
  81. return result;
  82. if( GetTextPos().x != tmp->GetTextPos().x )
  83. return GetTextPos().x - tmp->GetTextPos().x;
  84. if( GetTextPos().y != tmp->GetTextPos().y )
  85. return GetTextPos().y - tmp->GetTextPos().y;
  86. if( GetTextWidth() != tmp->GetTextWidth() )
  87. return GetTextWidth() - tmp->GetTextWidth();
  88. if( GetTextHeight() != tmp->GetTextHeight() )
  89. return GetTextHeight() - tmp->GetTextHeight();
  90. return 0;
  91. }
  92. void LIB_TEXT::Offset( const wxPoint& aOffset )
  93. {
  94. EDA_TEXT::Offset( aOffset );
  95. }
  96. void LIB_TEXT::MoveTo( const wxPoint& newPosition )
  97. {
  98. SetTextPos( newPosition );
  99. }
  100. void LIB_TEXT::NormalizeJustification( bool inverse )
  101. {
  102. wxPoint delta( 0, 0 );
  103. EDA_RECT bbox = GetTextBox();
  104. if( GetTextAngle() == 0.0 )
  105. {
  106. if( GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  107. delta.x = bbox.GetWidth() / 2;
  108. else if( GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  109. delta.x = - bbox.GetWidth() / 2;
  110. if( GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  111. delta.y = - bbox.GetHeight() / 2;
  112. else if( GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  113. delta.y = bbox.GetHeight() / 2;
  114. }
  115. else
  116. {
  117. if( GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  118. delta.y = bbox.GetWidth() / 2;
  119. else if( GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  120. delta.y = - bbox.GetWidth() / 2;
  121. if( GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  122. delta.x = + bbox.GetHeight() / 2;
  123. else if( GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  124. delta.x = - bbox.GetHeight() / 2;
  125. }
  126. if( inverse )
  127. SetTextPos( GetTextPos() - delta );
  128. else
  129. SetTextPos( GetTextPos() + delta );
  130. }
  131. void LIB_TEXT::MirrorHorizontal( const wxPoint& center )
  132. {
  133. NormalizeJustification( false );
  134. int x = GetTextPos().x;
  135. x -= center.x;
  136. x *= -1;
  137. x += center.x;
  138. if( GetTextAngle() == 0.0 )
  139. {
  140. if( GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  141. SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  142. else if( GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  143. SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  144. }
  145. else
  146. {
  147. if( GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  148. SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  149. else if( GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  150. SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  151. }
  152. SetTextX( x );
  153. NormalizeJustification( true );
  154. }
  155. void LIB_TEXT::MirrorVertical( const wxPoint& center )
  156. {
  157. NormalizeJustification( false );
  158. int y = GetTextPos().y;
  159. y -= center.y;
  160. y *= -1;
  161. y += center.y;
  162. if( GetTextAngle() == 0.0 )
  163. {
  164. if( GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  165. SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  166. else if( GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  167. SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  168. }
  169. else
  170. {
  171. if( GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  172. SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  173. else if( GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  174. SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  175. }
  176. SetTextY( y );
  177. NormalizeJustification( true );
  178. }
  179. void LIB_TEXT::Rotate( const wxPoint& center, bool aRotateCCW )
  180. {
  181. NormalizeJustification( false );
  182. int rot_angle = aRotateCCW ? -900 : 900;
  183. wxPoint pt = GetTextPos();
  184. RotatePoint( &pt, center, rot_angle );
  185. SetTextPos( pt );
  186. if( GetTextAngle() == 0.0 )
  187. {
  188. SetTextAngle( 900 );
  189. }
  190. else
  191. {
  192. // 180º of rotation is a mirror
  193. if( GetHorizJustify() == GR_TEXT_HJUSTIFY_LEFT )
  194. SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT );
  195. else if( GetHorizJustify() == GR_TEXT_HJUSTIFY_RIGHT )
  196. SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT );
  197. if( GetVertJustify() == GR_TEXT_VJUSTIFY_TOP )
  198. SetVertJustify( GR_TEXT_VJUSTIFY_BOTTOM );
  199. else if( GetVertJustify() == GR_TEXT_VJUSTIFY_BOTTOM )
  200. SetVertJustify( GR_TEXT_VJUSTIFY_TOP );
  201. SetTextAngle( 0 );
  202. }
  203. NormalizeJustification( true );
  204. }
  205. void LIB_TEXT::Plot( PLOTTER* plotter, const wxPoint& offset, bool fill,
  206. const TRANSFORM& aTransform ) const
  207. {
  208. wxASSERT( plotter != nullptr );
  209. EDA_RECT bBox = GetBoundingBox();
  210. // convert coordinates from draw Y axis to symbol_editor Y axis
  211. bBox.RevertYAxis();
  212. wxPoint txtpos = bBox.Centre();
  213. // The text orientation may need to be flipped if the transformation matrix causes xy
  214. // axes to be flipped.
  215. int t1 = ( aTransform.x1 != 0 ) ^ ( GetTextAngle() != 0 );
  216. wxPoint pos = aTransform.TransformCoordinate( txtpos ) + offset;
  217. // Get color
  218. COLOR4D color;
  219. if( plotter->GetColorMode() ) // Used normal color or selected color
  220. color = plotter->RenderSettings()->GetLayerColor( LAYER_DEVICE );
  221. else
  222. color = COLOR4D::BLACK;
  223. RENDER_SETTINGS* settings = plotter->RenderSettings();
  224. int penWidth = std::max( GetEffectiveTextPenWidth(), settings->GetMinPenWidth() );
  225. plotter->Text( pos, color, GetText(), t1 ? TEXT_ANGLE_HORIZ : TEXT_ANGLE_VERT, GetTextSize(),
  226. GR_TEXT_HJUSTIFY_CENTER, GR_TEXT_VJUSTIFY_CENTER, penWidth, IsItalic(),
  227. IsBold() );
  228. }
  229. int LIB_TEXT::GetPenWidth() const
  230. {
  231. return GetEffectiveTextPenWidth();
  232. }
  233. void LIB_TEXT::print( const RENDER_SETTINGS* aSettings, const wxPoint& aOffset, void* aData,
  234. const TRANSFORM& aTransform )
  235. {
  236. wxDC* DC = aSettings->GetPrintDC();
  237. COLOR4D color = aSettings->GetLayerColor( LAYER_DEVICE );
  238. int penWidth = std::max( GetEffectiveTextPenWidth(), aSettings->GetDefaultPenWidth() );
  239. // Calculate the text orientation, according to the symbol orientation/mirror (needed when
  240. // draw text in schematic)
  241. int orient = (int) GetTextAngle();
  242. if( aTransform.y1 ) // Rotate symbol 90 degrees.
  243. {
  244. if( orient == TEXT_ANGLE_HORIZ )
  245. orient = TEXT_ANGLE_VERT;
  246. else
  247. orient = TEXT_ANGLE_HORIZ;
  248. }
  249. /*
  250. * Calculate the text justification, according to the symbol orientation/mirror.
  251. * This is a bit complicated due to cumulative calculations:
  252. * - numerous cases (mirrored or not, rotation)
  253. * - the GRText function will also recalculate H and V justifications according to the text
  254. * orientation.
  255. * - When a symbol is mirrored, the text is not mirrored and justifications are complicated
  256. * to calculate so the more easily way is to use no justifications (centered text) and
  257. * use GetBoundingBox to know the text coordinate considered as centered
  258. */
  259. EDA_RECT bBox = GetBoundingBox();
  260. // convert coordinates from draw Y axis to symbol_editor Y axis:
  261. bBox.RevertYAxis();
  262. wxPoint txtpos = bBox.Centre();
  263. // Calculate pos according to mirror/rotation.
  264. txtpos = aTransform.TransformCoordinate( txtpos ) + aOffset;
  265. GRText( DC, txtpos, color, GetShownText(), orient, GetTextSize(), GR_TEXT_HJUSTIFY_CENTER,
  266. GR_TEXT_VJUSTIFY_CENTER, penWidth, IsItalic(), IsBold() );
  267. }
  268. void LIB_TEXT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  269. {
  270. wxString msg;
  271. LIB_ITEM::GetMsgPanelInfo( aFrame, aList );
  272. // Don't use GetShownText() here; we want to show the user the variable references
  273. aList.emplace_back( _( "Text" ), UnescapeString( GetText() ) );
  274. aList.emplace_back( _( "Style" ), GetTextStyleName() );
  275. aList.emplace_back( _( "Text Size" ), MessageTextFromValue( aFrame->GetUserUnits(),
  276. GetTextWidth() ) );
  277. switch ( GetHorizJustify() )
  278. {
  279. case GR_TEXT_HJUSTIFY_LEFT: msg = _( "Left" ); break;
  280. case GR_TEXT_HJUSTIFY_CENTER: msg = _( "Center" ); break;
  281. case GR_TEXT_HJUSTIFY_RIGHT: msg = _( "Right" ); break;
  282. }
  283. aList.emplace_back( _( "H Justification" ), msg );
  284. switch ( GetVertJustify() )
  285. {
  286. case GR_TEXT_VJUSTIFY_TOP: msg = _( "Top" ); break;
  287. case GR_TEXT_VJUSTIFY_CENTER: msg = _( "Center" ); break;
  288. case GR_TEXT_VJUSTIFY_BOTTOM: msg = _( "Bottom" ); break;
  289. }
  290. aList.emplace_back( _( "V Justification" ), msg );
  291. }
  292. const EDA_RECT LIB_TEXT::GetBoundingBox() const
  293. {
  294. /* Y coordinates for LIB_ITEMS are bottom to top, so we must invert the Y position when
  295. * calling GetTextBox() that works using top to bottom Y axis orientation.
  296. */
  297. EDA_RECT rect = GetTextBox( -1, true );
  298. rect.RevertYAxis();
  299. // We are using now a bottom to top Y axis.
  300. wxPoint orig = rect.GetOrigin();
  301. wxPoint end = rect.GetEnd();
  302. RotatePoint( &orig, GetTextPos(), -GetTextAngle() );
  303. RotatePoint( &end, GetTextPos(), -GetTextAngle() );
  304. rect.SetOrigin( orig );
  305. rect.SetEnd( end );
  306. // We are using now a top to bottom Y axis:
  307. rect.RevertYAxis();
  308. return rect;
  309. }
  310. wxString LIB_TEXT::GetSelectMenuText( EDA_UNITS aUnits ) const
  311. {
  312. return wxString::Format( _( "Graphic Text '%s'" ), ShortenedShownText() );
  313. }
  314. BITMAPS LIB_TEXT::GetMenuImage() const
  315. {
  316. return BITMAPS::text;
  317. }
  318. void LIB_TEXT::BeginEdit( const wxPoint& aPosition )
  319. {
  320. SetTextPos( aPosition );
  321. }
  322. void LIB_TEXT::CalcEdit( const wxPoint& aPosition )
  323. {
  324. SetTextPos( aPosition );
  325. }