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.

520 lines
15 KiB

14 years ago
18 years ago
18 years ago
18 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2015 Wayne Stambaugh <stambaughw@gmail.com>
  6. * Copyright (C) 1992-2022 KiCad Developers, see AUTHORS.txt for contributors.
  7. *
  8. * This program is free software; you can redistribute it and/or
  9. * modify it under the terms of the GNU General Public License
  10. * as published by the Free Software Foundation; either version 2
  11. * of the License, or (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, you may find one here:
  20. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  21. * or you may search the http://www.gnu.org website for the version 2 license,
  22. * or you may write to the Free Software Foundation, Inc.,
  23. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  24. */
  25. #include <pgm_base.h>
  26. #include <sch_edit_frame.h>
  27. #include <plotters/plotter.h>
  28. #include <widgets/msgpanel.h>
  29. #include <bitmaps.h>
  30. #include <string_utils.h>
  31. #include <sch_text.h>
  32. #include <schematic.h>
  33. #include <settings/color_settings.h>
  34. #include <sch_painter.h>
  35. #include <default_values.h>
  36. #include <wx/debug.h>
  37. #include <wx/log.h>
  38. #include <dialogs/html_message_box.h>
  39. #include <project/project_file.h>
  40. #include <project/net_settings.h>
  41. #include <core/mirror.h>
  42. #include <core/kicad_algo.h>
  43. #include <trigo.h>
  44. using KIGFX::SCH_RENDER_SETTINGS;
  45. TEXT_SPIN_STYLE TEXT_SPIN_STYLE::RotateCW()
  46. {
  47. SPIN newSpin = m_spin;
  48. switch( m_spin )
  49. {
  50. case TEXT_SPIN_STYLE::LEFT: newSpin = TEXT_SPIN_STYLE::UP; break;
  51. case TEXT_SPIN_STYLE::UP: newSpin = TEXT_SPIN_STYLE::RIGHT; break;
  52. case TEXT_SPIN_STYLE::RIGHT: newSpin = TEXT_SPIN_STYLE::BOTTOM; break;
  53. case TEXT_SPIN_STYLE::BOTTOM: newSpin = TEXT_SPIN_STYLE::LEFT; break;
  54. }
  55. return TEXT_SPIN_STYLE( newSpin );
  56. }
  57. TEXT_SPIN_STYLE TEXT_SPIN_STYLE::RotateCCW()
  58. {
  59. SPIN newSpin = m_spin;
  60. switch( m_spin )
  61. {
  62. case TEXT_SPIN_STYLE::LEFT: newSpin = TEXT_SPIN_STYLE::BOTTOM; break;
  63. case TEXT_SPIN_STYLE::BOTTOM: newSpin = TEXT_SPIN_STYLE::RIGHT; break;
  64. case TEXT_SPIN_STYLE::RIGHT: newSpin = TEXT_SPIN_STYLE::UP; break;
  65. case TEXT_SPIN_STYLE::UP: newSpin = TEXT_SPIN_STYLE::LEFT; break;
  66. }
  67. return TEXT_SPIN_STYLE( newSpin );
  68. }
  69. TEXT_SPIN_STYLE TEXT_SPIN_STYLE::MirrorX()
  70. {
  71. SPIN newSpin = m_spin;
  72. switch( m_spin )
  73. {
  74. case TEXT_SPIN_STYLE::UP: newSpin = TEXT_SPIN_STYLE::BOTTOM; break;
  75. case TEXT_SPIN_STYLE::BOTTOM: newSpin = TEXT_SPIN_STYLE::UP; break;
  76. case TEXT_SPIN_STYLE::LEFT: break;
  77. case TEXT_SPIN_STYLE::RIGHT: break;
  78. }
  79. return TEXT_SPIN_STYLE( newSpin );
  80. }
  81. TEXT_SPIN_STYLE TEXT_SPIN_STYLE::MirrorY()
  82. {
  83. SPIN newSpin = m_spin;
  84. switch( m_spin )
  85. {
  86. case TEXT_SPIN_STYLE::LEFT: newSpin = TEXT_SPIN_STYLE::RIGHT; break;
  87. case TEXT_SPIN_STYLE::RIGHT: newSpin = TEXT_SPIN_STYLE::LEFT; break;
  88. case TEXT_SPIN_STYLE::UP: break;
  89. case TEXT_SPIN_STYLE::BOTTOM: break;
  90. }
  91. return TEXT_SPIN_STYLE( newSpin );
  92. }
  93. SCH_TEXT::SCH_TEXT( const VECTOR2I& pos, const wxString& text, KICAD_T aType ) :
  94. SCH_ITEM( nullptr, aType ),
  95. EDA_TEXT( text )
  96. {
  97. m_layer = LAYER_NOTES;
  98. SetTextPos( pos );
  99. SetTextSpinStyle( TEXT_SPIN_STYLE::LEFT );
  100. SetMultilineAllowed( true );
  101. }
  102. SCH_TEXT::SCH_TEXT( const SCH_TEXT& aText ) :
  103. SCH_ITEM( aText ),
  104. EDA_TEXT( aText ),
  105. m_spin_style( aText.m_spin_style )
  106. { }
  107. VECTOR2I SCH_TEXT::GetSchematicTextOffset( const RENDER_SETTINGS* aSettings ) const
  108. {
  109. return VECTOR2I( 0, 0 );
  110. }
  111. void SCH_TEXT::MirrorHorizontally( int aCenter )
  112. {
  113. // Text is NOT really mirrored; it is moved to a suitable horizontal position
  114. SetTextSpinStyle( GetTextSpinStyle().MirrorY() );
  115. SetTextX( MIRRORVAL( GetTextPos().x, aCenter ) );
  116. }
  117. void SCH_TEXT::MirrorVertically( int aCenter )
  118. {
  119. // Text is NOT really mirrored; it is moved to a suitable vertical position
  120. SetTextSpinStyle( GetTextSpinStyle().MirrorX() );
  121. SetTextY( MIRRORVAL( GetTextPos().y, aCenter ) );
  122. }
  123. void SCH_TEXT::Rotate( const VECTOR2I& aCenter )
  124. {
  125. VECTOR2I pt = GetTextPos();
  126. RotatePoint( pt, aCenter, ANGLE_90 );
  127. VECTOR2I offset = pt - GetTextPos();
  128. Rotate90( false );
  129. SetTextPos( GetTextPos() + offset );
  130. }
  131. void SCH_TEXT::Rotate90( bool aClockwise )
  132. {
  133. if( aClockwise )
  134. SetTextSpinStyle( GetTextSpinStyle().RotateCW() );
  135. else
  136. SetTextSpinStyle( GetTextSpinStyle().RotateCCW() );
  137. }
  138. void SCH_TEXT::MirrorSpinStyle( bool aLeftRight )
  139. {
  140. if( aLeftRight )
  141. SetTextSpinStyle( GetTextSpinStyle().MirrorY() );
  142. else
  143. SetTextSpinStyle( GetTextSpinStyle().MirrorX() );
  144. }
  145. void SCH_TEXT::SetTextSpinStyle( TEXT_SPIN_STYLE aSpinStyle )
  146. {
  147. m_spin_style = aSpinStyle;
  148. // Assume "Right" and Left" mean which side of the anchor the text will be on
  149. // Thus we want to left justify text up against the anchor if we are on the right
  150. switch( aSpinStyle )
  151. {
  152. default:
  153. wxFAIL_MSG( "Bad spin style" );
  154. m_spin_style = TEXT_SPIN_STYLE::RIGHT; // Handle the error spin style by resetting
  155. KI_FALLTHROUGH;
  156. case TEXT_SPIN_STYLE::RIGHT: // Horiz Normal Orientation
  157. SetTextAngle( ANGLE_HORIZONTAL );
  158. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  159. break;
  160. case TEXT_SPIN_STYLE::UP: // Vert Orientation UP
  161. SetTextAngle( ANGLE_VERTICAL );
  162. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  163. break;
  164. case TEXT_SPIN_STYLE::LEFT: // Horiz Orientation - Right justified
  165. SetTextAngle( ANGLE_HORIZONTAL );
  166. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  167. break;
  168. case TEXT_SPIN_STYLE::BOTTOM: // Vert Orientation BOTTOM
  169. SetTextAngle( ANGLE_VERTICAL );
  170. SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  171. break;
  172. }
  173. SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
  174. }
  175. void SCH_TEXT::SwapData( SCH_ITEM* aItem )
  176. {
  177. SCH_TEXT* item = static_cast<SCH_TEXT*>( aItem );
  178. std::swap( m_layer, item->m_layer );
  179. std::swap( m_spin_style, item->m_spin_style );
  180. SwapText( *item );
  181. SwapAttributes( *item );
  182. }
  183. bool SCH_TEXT::operator<( const SCH_ITEM& aItem ) const
  184. {
  185. if( Type() != aItem.Type() )
  186. return Type() < aItem.Type();
  187. auto other = static_cast<const SCH_TEXT*>( &aItem );
  188. if( GetLayer() != other->GetLayer() )
  189. return GetLayer() < other->GetLayer();
  190. if( GetPosition().x != other->GetPosition().x )
  191. return GetPosition().x < other->GetPosition().x;
  192. if( GetPosition().y != other->GetPosition().y )
  193. return GetPosition().y < other->GetPosition().y;
  194. return GetText() < other->GetText();
  195. }
  196. int SCH_TEXT::GetTextOffset( const RENDER_SETTINGS* aSettings ) const
  197. {
  198. double ratio;
  199. if( aSettings )
  200. ratio = static_cast<const SCH_RENDER_SETTINGS*>( aSettings )->m_TextOffsetRatio;
  201. else if( Schematic() )
  202. ratio = Schematic()->Settings().m_TextOffsetRatio;
  203. else
  204. ratio = DEFAULT_TEXT_OFFSET_RATIO; // For previews (such as in Preferences), etc.
  205. return KiROUND( ratio * GetTextSize().y );
  206. }
  207. int SCH_TEXT::GetPenWidth() const
  208. {
  209. return GetEffectiveTextPenWidth();
  210. }
  211. KIFONT::FONT* SCH_TEXT::GetDrawFont() const
  212. {
  213. KIFONT::FONT* font = EDA_TEXT::GetFont();
  214. if( !font )
  215. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  216. return font;
  217. }
  218. void SCH_TEXT::Print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset )
  219. {
  220. COLOR4D color = GetTextColor();
  221. bool blackAndWhiteMode = GetGRForceBlackPenState();
  222. VECTOR2I text_offset = aOffset + GetSchematicTextOffset( aSettings );
  223. if( blackAndWhiteMode || color == COLOR4D::UNSPECIFIED )
  224. color = aSettings->GetLayerColor( m_layer );
  225. // Adjust text drawn in an outline font to more closely mimic the positioning of
  226. // SCH_FIELD text.
  227. if( GetDrawFont()->IsOutline() )
  228. {
  229. EDA_RECT firstLineBBox = GetTextBox( 0 );
  230. int sizeDiff = firstLineBBox.GetHeight() - GetTextSize().y;
  231. int adjust = KiROUND( sizeDiff * 0.4 );
  232. VECTOR2I adjust_offset( 0, - adjust );
  233. RotatePoint( adjust_offset, GetDrawRotation() );
  234. text_offset += adjust_offset;
  235. }
  236. EDA_TEXT::Print( aSettings, text_offset, color );
  237. }
  238. const EDA_RECT SCH_TEXT::GetBoundingBox() const
  239. {
  240. EDA_RECT rect = GetTextBox();
  241. if( !GetTextAngle().IsZero() ) // Rotate rect.
  242. {
  243. VECTOR2I pos = rect.GetOrigin();
  244. VECTOR2I end = rect.GetEnd();
  245. RotatePoint( pos, GetTextPos(), GetTextAngle() );
  246. RotatePoint( end, GetTextPos(), GetTextAngle() );
  247. rect.SetOrigin( pos );
  248. rect.SetEnd( end );
  249. }
  250. rect.Normalize();
  251. return rect;
  252. }
  253. wxString SCH_TEXT::GetShownText( int aDepth ) const
  254. {
  255. std::function<bool( wxString* )> textResolver =
  256. [&]( wxString* token ) -> bool
  257. {
  258. if( token->Contains( ':' ) )
  259. {
  260. if( Schematic()->ResolveCrossReference( token, aDepth ) )
  261. return true;
  262. }
  263. else
  264. {
  265. SCHEMATIC* schematic = Schematic();
  266. SCH_SHEET* sheet = schematic ? schematic->CurrentSheet().Last() : nullptr;
  267. if( sheet && sheet->ResolveTextVar( token, aDepth + 1 ) )
  268. return true;
  269. }
  270. return false;
  271. };
  272. std::function<bool( wxString* )> schematicTextResolver =
  273. [&]( wxString* token ) -> bool
  274. {
  275. return Schematic()->ResolveTextVar( token, aDepth + 1 );
  276. };
  277. wxString text = EDA_TEXT::GetShownText();
  278. if( text == "~" ) // Legacy placeholder for empty string
  279. {
  280. text = "";
  281. }
  282. else if( HasTextVars() )
  283. {
  284. PROJECT* project = nullptr;
  285. if( Schematic() )
  286. project = &Schematic()->Prj();
  287. if( aDepth < 10 )
  288. text = ExpandTextVars( text, &textResolver, &schematicTextResolver, project );
  289. }
  290. return text;
  291. }
  292. wxString SCH_TEXT::GetSelectMenuText( EDA_UNITS aUnits ) const
  293. {
  294. return wxString::Format( _( "Graphic Text '%s'" ), ShortenedShownText() );
  295. }
  296. BITMAPS SCH_TEXT::GetMenuImage() const
  297. {
  298. return BITMAPS::text;
  299. }
  300. bool SCH_TEXT::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  301. {
  302. EDA_RECT bBox = GetBoundingBox();
  303. bBox.Inflate( aAccuracy );
  304. return bBox.Contains( aPosition );
  305. }
  306. bool SCH_TEXT::HitTest( const EDA_RECT& aRect, bool aContained, int aAccuracy ) const
  307. {
  308. EDA_RECT bBox = GetBoundingBox();
  309. bBox.Inflate( aAccuracy );
  310. if( aContained )
  311. return aRect.Contains( bBox );
  312. return aRect.Intersects( bBox );
  313. }
  314. void SCH_TEXT::ViewGetLayers( int aLayers[], int& aCount ) const
  315. {
  316. aCount = 2;
  317. aLayers[0] = m_layer;
  318. aLayers[1] = LAYER_SELECTION_SHADOWS;
  319. }
  320. void SCH_TEXT::Plot( PLOTTER* aPlotter, bool aBackground ) const
  321. {
  322. if( aBackground )
  323. return;
  324. static std::vector<VECTOR2I> s_poly;
  325. RENDER_SETTINGS* settings = aPlotter->RenderSettings();
  326. SCH_CONNECTION* connection = Connection();
  327. int layer = ( connection && connection->IsBus() ) ? LAYER_BUS : m_layer;
  328. COLOR4D color = GetTextColor();
  329. int penWidth = GetEffectiveTextPenWidth( settings->GetDefaultPenWidth() );
  330. KIFONT::FONT* font = GetDrawFont();
  331. VECTOR2I text_offset = GetSchematicTextOffset( aPlotter->RenderSettings() );
  332. if( !aPlotter->GetColorMode() || color == COLOR4D::UNSPECIFIED )
  333. color = settings->GetLayerColor( layer );
  334. penWidth = std::max( penWidth, settings->GetMinPenWidth() );
  335. aPlotter->SetCurrentLineWidth( penWidth );
  336. // Adjust text drawn in an outline font to more closely mimic the positioning of
  337. // SCH_FIELD text.
  338. if( GetDrawFont()->IsOutline() )
  339. {
  340. EDA_RECT firstLineBBox = GetTextBox( 0 );
  341. int sizeDiff = firstLineBBox.GetHeight() - GetTextSize().y;
  342. int adjust = KiROUND( sizeDiff * 0.4 );
  343. VECTOR2I adjust_offset( 0, - adjust );
  344. RotatePoint( adjust_offset, GetDrawRotation() );
  345. text_offset += adjust_offset;
  346. }
  347. std::vector<VECTOR2I> positions;
  348. wxArrayString strings_list;
  349. wxStringSplit( GetShownText(), strings_list, '\n' );
  350. positions.reserve( strings_list.Count() );
  351. GetLinePositions( positions, (int) strings_list.Count() );
  352. for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
  353. {
  354. VECTOR2I textpos = positions[ii] + text_offset;
  355. wxString& txt = strings_list.Item( ii );
  356. aPlotter->Text( textpos, color, txt, GetTextAngle(), GetTextSize(), GetHorizJustify(),
  357. GetVertJustify(), penWidth, IsItalic(), IsBold(), false, font );
  358. }
  359. }
  360. void SCH_TEXT::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  361. {
  362. wxString msg;
  363. // Don't use GetShownText() here; we want to show the user the variable references
  364. aList.emplace_back( _( "Graphic Text" ), UnescapeString( ShortenedText() ) );
  365. aList.emplace_back( _( "Font" ), GetDrawFont()->GetName() );
  366. wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
  367. int style = IsBold() && IsItalic() ? 3 : IsBold() ? 2 : IsItalic() ? 1 : 0;
  368. aList.emplace_back( _( "Style" ), textStyle[style] );
  369. aList.emplace_back( _( "Text Size" ), MessageTextFromValue( aFrame->GetUserUnits(),
  370. GetTextWidth() ) );
  371. switch( GetTextSpinStyle() )
  372. {
  373. case TEXT_SPIN_STYLE::LEFT: msg = _( "Align right" ); break;
  374. case TEXT_SPIN_STYLE::UP: msg = _( "Align bottom" ); break;
  375. case TEXT_SPIN_STYLE::RIGHT: msg = _( "Align left" ); break;
  376. case TEXT_SPIN_STYLE::BOTTOM: msg = _( "Align top" ); break;
  377. default: msg = wxT( "???" ); break;
  378. }
  379. aList.emplace_back( _( "Justification" ), msg );
  380. }
  381. #if defined(DEBUG)
  382. void SCH_TEXT::Show( int nestLevel, std::ostream& os ) const
  383. {
  384. // XML output:
  385. wxString s = GetClass();
  386. NestedSpace( nestLevel, os ) << '<' << s.Lower().mb_str()
  387. << " layer=\"" << m_layer << '"'
  388. << '>'
  389. << TO_UTF8( GetText() )
  390. << "</" << s.Lower().mb_str() << ">\n";
  391. }
  392. #endif