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.

569 lines
18 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2022-2024 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 <base_units.h>
  24. #include <pgm_base.h>
  25. #include <sch_edit_frame.h>
  26. #include <plotters/plotter.h>
  27. #include <widgets/msgpanel.h>
  28. #include <bitmaps.h>
  29. #include <string_utils.h>
  30. #include <schematic.h>
  31. #include <settings/color_settings.h>
  32. #include <sch_painter.h>
  33. #include <dialogs/html_message_box.h>
  34. #include <project/project_file.h>
  35. #include <project/net_settings.h>
  36. #include <core/kicad_algo.h>
  37. #include <trigo.h>
  38. #include <lib_textbox.h>
  39. using KIGFX::SCH_RENDER_SETTINGS;
  40. LIB_TEXTBOX::LIB_TEXTBOX( LIB_SYMBOL* aParent, int aLineWidth, FILL_T aFillType,
  41. const wxString& text ) :
  42. LIB_SHAPE( aParent, SHAPE_T::RECTANGLE, aLineWidth, aFillType, LIB_TEXTBOX_T ),
  43. EDA_TEXT( schIUScale, text )
  44. {
  45. SetTextSize( VECTOR2I( schIUScale.MilsToIU( DEFAULT_TEXT_SIZE ),
  46. schIUScale.MilsToIU( DEFAULT_TEXT_SIZE ) ) );
  47. SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  48. SetVertJustify( GR_TEXT_V_ALIGN_TOP );
  49. SetMultilineAllowed( true );
  50. }
  51. LIB_TEXTBOX::LIB_TEXTBOX( const LIB_TEXTBOX& aText ) :
  52. LIB_SHAPE( aText ),
  53. EDA_TEXT( aText )
  54. { }
  55. int LIB_TEXTBOX::GetTextMargin() const
  56. {
  57. return KiROUND( GetTextSize().y * 0.8 );
  58. }
  59. void LIB_TEXTBOX::MirrorHorizontally( const VECTOR2I& center )
  60. {
  61. // Text is NOT really mirrored; it just has its justification flipped
  62. if( GetTextAngle() == ANGLE_HORIZONTAL )
  63. {
  64. switch( GetHorizJustify() )
  65. {
  66. case GR_TEXT_H_ALIGN_LEFT: SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
  67. case GR_TEXT_H_ALIGN_CENTER: break;
  68. case GR_TEXT_H_ALIGN_RIGHT: SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
  69. }
  70. }
  71. }
  72. void LIB_TEXTBOX::MirrorVertically( const VECTOR2I& center )
  73. {
  74. // Text is NOT really mirrored; it just has its justification flipped
  75. if( GetTextAngle() == ANGLE_VERTICAL )
  76. {
  77. switch( GetHorizJustify() )
  78. {
  79. case GR_TEXT_H_ALIGN_LEFT: SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT ); break;
  80. case GR_TEXT_H_ALIGN_CENTER: break;
  81. case GR_TEXT_H_ALIGN_RIGHT: SetHorizJustify( GR_TEXT_H_ALIGN_LEFT ); break;
  82. }
  83. }
  84. }
  85. void LIB_TEXTBOX::Rotate( const VECTOR2I& aCenter, bool aRotateCCW )
  86. {
  87. LIB_SHAPE::Rotate( aCenter, aRotateCCW );
  88. SetTextAngle( GetTextAngle() == ANGLE_VERTICAL ? ANGLE_HORIZONTAL : ANGLE_VERTICAL );
  89. }
  90. VECTOR2I LIB_TEXTBOX::GetDrawPos() const
  91. {
  92. int margin = GetTextMargin();
  93. BOX2I bbox( VECTOR2I( std::min( m_start.x, m_end.x ), std::min( -m_start.y, -m_end.y ) ),
  94. VECTOR2I( abs( m_end.x - m_start.x ), abs( m_end.y - m_start.y ) ) );
  95. VECTOR2I pos( bbox.GetLeft() + margin, bbox.GetBottom() - margin );
  96. if( GetTextAngle() == ANGLE_VERTICAL )
  97. {
  98. switch( GetHorizJustify() )
  99. {
  100. case GR_TEXT_H_ALIGN_LEFT:
  101. pos.y = bbox.GetBottom() - margin;
  102. break;
  103. case GR_TEXT_H_ALIGN_CENTER:
  104. pos.y = ( bbox.GetTop() + bbox.GetBottom() ) / 2;
  105. break;
  106. case GR_TEXT_H_ALIGN_RIGHT:
  107. pos.y = bbox.GetTop() + margin;
  108. break;
  109. }
  110. switch( GetVertJustify() )
  111. {
  112. case GR_TEXT_V_ALIGN_TOP:
  113. pos.x = bbox.GetLeft() + margin;
  114. break;
  115. case GR_TEXT_V_ALIGN_CENTER:
  116. pos.x = ( bbox.GetLeft() + bbox.GetRight() ) / 2;
  117. break;
  118. case GR_TEXT_V_ALIGN_BOTTOM:
  119. pos.x = bbox.GetRight() - margin;
  120. break;
  121. }
  122. }
  123. else
  124. {
  125. switch( GetHorizJustify() )
  126. {
  127. case GR_TEXT_H_ALIGN_LEFT:
  128. pos.x = bbox.GetLeft() + margin;
  129. break;
  130. case GR_TEXT_H_ALIGN_CENTER:
  131. pos.x = ( bbox.GetLeft() + bbox.GetRight() ) / 2;
  132. break;
  133. case GR_TEXT_H_ALIGN_RIGHT:
  134. pos.x = bbox.GetRight() - margin;
  135. break;
  136. }
  137. switch( GetVertJustify() )
  138. {
  139. case GR_TEXT_V_ALIGN_TOP:
  140. pos.y = bbox.GetTop() + margin;
  141. break;
  142. case GR_TEXT_V_ALIGN_CENTER:
  143. pos.y = ( bbox.GetTop() + bbox.GetBottom() ) / 2;
  144. break;
  145. case GR_TEXT_V_ALIGN_BOTTOM:
  146. pos.y = bbox.GetBottom() - margin;
  147. break;
  148. }
  149. }
  150. return pos;
  151. }
  152. int LIB_TEXTBOX::compare( const LIB_ITEM& aOther, int aCompareFlags ) const
  153. {
  154. wxASSERT( aOther.Type() == LIB_TEXTBOX_T );
  155. int retv = LIB_ITEM::compare( aOther, aCompareFlags );
  156. if( retv )
  157. return retv;
  158. const LIB_TEXTBOX* tmp = static_cast<const LIB_TEXTBOX*>( &aOther );
  159. int result = GetText().CmpNoCase( tmp->GetText() );
  160. if( result != 0 )
  161. return result;
  162. if( GetTextWidth() != tmp->GetTextWidth() )
  163. return GetTextWidth() - tmp->GetTextWidth();
  164. if( GetTextHeight() != tmp->GetTextHeight() )
  165. return GetTextHeight() - tmp->GetTextHeight();
  166. if( IsBold() != tmp->IsBold() )
  167. return IsBold() - tmp->IsBold();
  168. if( IsItalic() != tmp->IsItalic() )
  169. return IsItalic() - tmp->IsItalic();
  170. if( GetHorizJustify() != tmp->GetHorizJustify() )
  171. return GetHorizJustify() - tmp->GetHorizJustify();
  172. if( GetTextAngle().AsTenthsOfADegree() != tmp->GetTextAngle().AsTenthsOfADegree() )
  173. return GetTextAngle().AsTenthsOfADegree() - tmp->GetTextAngle().AsTenthsOfADegree();
  174. return EDA_SHAPE::Compare( &static_cast<const LIB_SHAPE&>( aOther ) );
  175. }
  176. KIFONT::FONT* LIB_TEXTBOX::getDrawFont() const
  177. {
  178. KIFONT::FONT* font = EDA_TEXT::GetFont();
  179. if( !font )
  180. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  181. return font;
  182. }
  183. void LIB_TEXTBOX::print( const RENDER_SETTINGS* aSettings, const VECTOR2I& aOffset, void* aData,
  184. const TRANSFORM& aTransform, bool aDimmed )
  185. {
  186. if( IsPrivate() )
  187. return;
  188. bool forceNoFill = static_cast<bool>( aData );
  189. bool blackAndWhiteMode = GetGRForceBlackPenState();
  190. int penWidth = GetEffectivePenWidth( aSettings );
  191. COLOR4D color = GetStroke().GetColor();
  192. LINE_STYLE lineStyle = GetStroke().GetLineStyle();
  193. wxDC* DC = aSettings->GetPrintDC();
  194. VECTOR2I pt1 = aTransform.TransformCoordinate( m_start ) + aOffset;
  195. VECTOR2I pt2 = aTransform.TransformCoordinate( m_end ) + aOffset;
  196. if( !forceNoFill && GetFillMode() == FILL_T::FILLED_WITH_COLOR && !blackAndWhiteMode )
  197. GRFilledRect( DC, pt1, pt2, penWidth, GetFillColor(), GetFillColor() );
  198. if( penWidth > 0 )
  199. {
  200. penWidth = std::max( penWidth, aSettings->GetMinPenWidth() );
  201. if( blackAndWhiteMode || color == COLOR4D::UNSPECIFIED )
  202. color = aSettings->GetLayerColor( LAYER_DEVICE );
  203. COLOR4D bg = aSettings->GetBackgroundColor();
  204. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  205. bg = COLOR4D::WHITE;
  206. if( aDimmed )
  207. {
  208. color.Desaturate( );
  209. color = color.Mix( bg, 0.5f );
  210. }
  211. if( lineStyle == LINE_STYLE::DEFAULT )
  212. lineStyle = LINE_STYLE::SOLID;
  213. if( lineStyle <= LINE_STYLE::FIRST_TYPE )
  214. {
  215. GRRect( DC, pt1, pt2, penWidth, color );
  216. }
  217. else
  218. {
  219. std::vector<SHAPE*> shapes = MakeEffectiveShapes( true );
  220. for( SHAPE* shape : shapes )
  221. {
  222. STROKE_PARAMS::Stroke( shape, lineStyle, penWidth, aSettings,
  223. [&]( const VECTOR2I& a, const VECTOR2I& b )
  224. {
  225. VECTOR2I pts = aTransform.TransformCoordinate( a ) + aOffset;
  226. VECTOR2I pte = aTransform.TransformCoordinate( b ) + aOffset;
  227. GRLine( DC, pts.x, pts.y, pte.x, pte.y, penWidth, color );
  228. } );
  229. }
  230. for( SHAPE* shape : shapes )
  231. delete shape;
  232. }
  233. }
  234. LIB_TEXTBOX text( *this );
  235. color = GetTextColor();
  236. if( blackAndWhiteMode || color == COLOR4D::UNSPECIFIED )
  237. color = aSettings->GetLayerColor( LAYER_DEVICE );
  238. COLOR4D bg = aSettings->GetBackgroundColor();
  239. if( bg == COLOR4D::UNSPECIFIED || GetGRForceBlackPenState() )
  240. bg = COLOR4D::WHITE;
  241. if( aDimmed )
  242. {
  243. color.Desaturate( );
  244. color = color.Mix( bg, 0.5f );
  245. }
  246. penWidth = std::max( GetEffectiveTextPenWidth(), aSettings->GetMinPenWidth() );
  247. if( aTransform.y1 )
  248. {
  249. text.SetTextAngle( text.GetTextAngle() == ANGLE_HORIZONTAL ? ANGLE_VERTICAL
  250. : ANGLE_HORIZONTAL );
  251. }
  252. KIFONT::FONT* font = GetFont();
  253. if( !font )
  254. font = KIFONT::FONT::GetFont( aSettings->GetDefaultFont(), IsBold(), IsItalic() );
  255. // NB: GetDrawPos() will want Symbol Editor (upside-down) coordinates
  256. text.SetStart( VECTOR2I( pt1.x, -pt1.y ) );
  257. text.SetEnd( VECTOR2I( pt2.x, -pt2.y ) );
  258. GRPrintText( DC, text.GetDrawPos(), color, text.GetShownText( true ), text.GetTextAngle(),
  259. text.GetTextSize(), text.GetHorizJustify(), text.GetVertJustify(), penWidth,
  260. text.IsItalic(), text.IsBold(), font, GetFontMetrics() );
  261. }
  262. wxString LIB_TEXTBOX::GetShownText( bool aAllowExtraText, int aDepth ) const
  263. {
  264. wxString text = EDA_TEXT::GetShownText( aAllowExtraText, aDepth );
  265. KIFONT::FONT* font = GetFont();
  266. VECTOR2D size = GetEnd() - GetStart();
  267. int colWidth = GetTextAngle() == ANGLE_HORIZONTAL ? size.x : size.y;
  268. if( !font )
  269. font = KIFONT::FONT::GetFont( GetDefaultFont(), IsBold(), IsItalic() );
  270. colWidth = abs( colWidth ) - GetTextMargin() * 2;
  271. font->LinebreakText( text, colWidth, GetTextSize(), GetTextThickness(), IsBold(), IsItalic() );
  272. return text;
  273. }
  274. bool LIB_TEXTBOX::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
  275. {
  276. if( aAccuracy < schIUScale.MilsToIU( MINIMUM_SELECTION_DISTANCE ) )
  277. aAccuracy = schIUScale.MilsToIU( MINIMUM_SELECTION_DISTANCE );
  278. BOX2I rect = GetBoundingBox();
  279. rect.Inflate( aAccuracy );
  280. return rect.Contains( aPosition );
  281. }
  282. bool LIB_TEXTBOX::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
  283. {
  284. BOX2I rect = aRect;
  285. rect.Inflate( aAccuracy );
  286. if( aContained )
  287. return rect.Contains( GetBoundingBox() );
  288. return rect.Intersects( GetBoundingBox() );
  289. }
  290. wxString LIB_TEXTBOX::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
  291. {
  292. return wxString::Format( _( "Graphic Text Box" ) );
  293. }
  294. BITMAPS LIB_TEXTBOX::GetMenuImage() const
  295. {
  296. return BITMAPS::add_textbox;
  297. }
  298. void LIB_TEXTBOX::Plot( PLOTTER* aPlotter, bool aBackground, const VECTOR2I& aOffset,
  299. const TRANSFORM& aTransform, bool aDimmed ) const
  300. {
  301. wxASSERT( aPlotter != nullptr );
  302. if( IsPrivate() )
  303. return;
  304. if( aBackground )
  305. {
  306. LIB_SHAPE::Plot( aPlotter, aBackground, aOffset, aTransform, aDimmed );
  307. return;
  308. }
  309. RENDER_SETTINGS* renderSettings = aPlotter->RenderSettings();
  310. VECTOR2I start = aTransform.TransformCoordinate( m_start ) + aOffset;
  311. VECTOR2I end = aTransform.TransformCoordinate( m_end ) + aOffset;
  312. COLOR4D bg = renderSettings->GetBackgroundColor();
  313. if( bg == COLOR4D::UNSPECIFIED || !aPlotter->GetColorMode() )
  314. bg = COLOR4D::WHITE;
  315. int penWidth = GetEffectivePenWidth( renderSettings );
  316. COLOR4D color = GetStroke().GetColor();
  317. LINE_STYLE lineStyle = GetStroke().GetLineStyle();
  318. if( penWidth > 0 )
  319. {
  320. if( !aPlotter->GetColorMode() || color == COLOR4D::UNSPECIFIED )
  321. color = renderSettings->GetLayerColor( LAYER_DEVICE );
  322. if( lineStyle == LINE_STYLE::DEFAULT )
  323. lineStyle = LINE_STYLE::SOLID;
  324. if( aDimmed )
  325. {
  326. color.Desaturate( );
  327. color = color.Mix( bg, 0.5f );
  328. }
  329. aPlotter->SetColor( color );
  330. aPlotter->SetDash( penWidth, lineStyle );
  331. aPlotter->Rect( start, end, FILL_T::NO_FILL, penWidth );
  332. aPlotter->SetDash( penWidth, LINE_STYLE::SOLID );
  333. }
  334. KIFONT::FONT* font = GetFont();
  335. if( !font )
  336. font = KIFONT::FONT::GetFont( renderSettings->GetDefaultFont(), IsBold(), IsItalic() );
  337. LIB_TEXTBOX text( *this );
  338. color = GetTextColor();
  339. if( !aPlotter->GetColorMode() || color == COLOR4D::UNSPECIFIED )
  340. color = renderSettings->GetLayerColor( LAYER_DEVICE );
  341. if( aDimmed )
  342. {
  343. color.Desaturate( );
  344. color = color.Mix( bg, 0.5f );
  345. }
  346. penWidth = std::max( GetEffectiveTextPenWidth(), aPlotter->RenderSettings()->GetMinPenWidth() );
  347. if( aTransform.y1 )
  348. {
  349. text.SetTextAngle( text.GetTextAngle() == ANGLE_HORIZONTAL ? ANGLE_VERTICAL
  350. : ANGLE_HORIZONTAL );
  351. }
  352. // NB: GetDrawPos() will want Symbol Editor (upside-down) coordinates
  353. text.SetStart( VECTOR2I( start.x, -start.y ) );
  354. text.SetEnd( VECTOR2I( end.x, -end.y ) );
  355. std::vector<VECTOR2I> positions;
  356. wxArrayString strings_list;
  357. wxStringSplit( GetShownText( true ), strings_list, '\n' );
  358. positions.reserve( strings_list.Count() );
  359. text.GetLinePositions( positions, (int) strings_list.Count() );
  360. TEXT_ATTRIBUTES attrs = text.GetAttributes();
  361. attrs.m_StrokeWidth = penWidth;
  362. attrs.m_Multiline = false;
  363. for( unsigned ii = 0; ii < strings_list.Count(); ii++ )
  364. {
  365. aPlotter->PlotText( positions[ii], color, strings_list.Item( ii ), attrs, font,
  366. GetFontMetrics() );
  367. }
  368. }
  369. void LIB_TEXTBOX::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
  370. {
  371. // Don't use GetShownText() here; we want to show the user the variable references
  372. aList.emplace_back( _( "Text Box" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
  373. aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
  374. wxString textStyle[] = { _( "Normal" ), _( "Italic" ), _( "Bold" ), _( "Bold Italic" ) };
  375. int style = IsBold() && IsItalic() ? 3 : IsBold() ? 2 : IsItalic() ? 1 : 0;
  376. aList.emplace_back( _( "Style" ), textStyle[style] );
  377. aList.emplace_back( _( "Text Size" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
  378. aList.emplace_back( _( "Box Width" ),
  379. aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
  380. aList.emplace_back( _( "Box Height" ),
  381. aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
  382. m_stroke.GetMsgPanelInfo( aFrame, aList );
  383. }
  384. bool LIB_TEXTBOX::operator==( const LIB_ITEM& aOther ) const
  385. {
  386. if( aOther.Type() != LIB_TEXTBOX_T )
  387. return false;
  388. const LIB_TEXTBOX& other = static_cast<const LIB_TEXTBOX&>( aOther );
  389. return LIB_SHAPE::operator==( other ) && EDA_TEXT::operator==( other );
  390. }
  391. double LIB_TEXTBOX::Similarity( const LIB_ITEM& aOther ) const
  392. {
  393. if( m_Uuid == aOther.m_Uuid )
  394. return 1.0;
  395. if( aOther.Type() != LIB_TEXTBOX_T )
  396. return 0.0;
  397. const LIB_TEXTBOX& other = static_cast<const LIB_TEXTBOX&>( aOther );
  398. double similarity = SimilarityBase( other );
  399. similarity *= LIB_SHAPE::Similarity( other );
  400. similarity *= EDA_TEXT::Similarity( other );
  401. return similarity;
  402. }
  403. void LIB_TEXTBOX::ViewGetLayers( int aLayers[], int& aCount ) const
  404. {
  405. aCount = 3;
  406. aLayers[0] = IsPrivate() ? LAYER_PRIVATE_NOTES : LAYER_DEVICE;
  407. aLayers[1] = IsPrivate() ? LAYER_NOTES_BACKGROUND : LAYER_DEVICE_BACKGROUND;
  408. aLayers[2] = LAYER_SELECTION_SHADOWS;
  409. }
  410. static struct LIB_TEXTBOX_DESC
  411. {
  412. LIB_TEXTBOX_DESC()
  413. {
  414. PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
  415. REGISTER_TYPE( LIB_TEXTBOX );
  416. propMgr.AddTypeCast( new TYPE_CAST<LIB_TEXTBOX, LIB_SHAPE> );
  417. propMgr.AddTypeCast( new TYPE_CAST<LIB_TEXTBOX, EDA_SHAPE> );
  418. propMgr.AddTypeCast( new TYPE_CAST<LIB_TEXTBOX, EDA_TEXT> );
  419. propMgr.InheritsAfter( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( LIB_SHAPE ) );
  420. propMgr.InheritsAfter( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ) );
  421. propMgr.InheritsAfter( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ) );
  422. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_SHAPE ), _HKI( "Shape" ) );
  423. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Visible" ) );
  424. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Width" ) );
  425. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );
  426. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Thickness" ) );
  427. propMgr.AddProperty( new PROPERTY<LIB_TEXTBOX, int>( _HKI( "Text Size" ),
  428. &LIB_TEXTBOX::SetLibTextSize, &LIB_TEXTBOX::GetLibTextSize, PROPERTY_DISPLAY::PT_SIZE ),
  429. _HKI( "Text Properties" ) );
  430. propMgr.Mask( TYPE_HASH( LIB_TEXTBOX ), TYPE_HASH( EDA_TEXT ), _HKI( "Orientation" ) );
  431. }
  432. } _LIB_TEXTBOX_DESC;