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.

421 lines
14 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2004-2018 Jean-Pierre Charras jp.charras at wanadoo.fr
  5. * Copyright (C) 2010-2018 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 <fctsys.h>
  25. #include <gr_basic.h>
  26. #include <class_drawpanel.h>
  27. #include <pcbnew.h>
  28. #include <pcb_edit_frame.h>
  29. #include <draw_graphic_text.h>
  30. #include <confirm.h>
  31. #include <wx/valnum.h>
  32. #include <pcb_layer_box_selector.h>
  33. #include <board_commit.h>
  34. #include <widgets/unit_binder.h>
  35. #include <class_board.h>
  36. #include <class_pcb_text.h>
  37. #include <class_text_mod.h>
  38. #include <class_module.h>
  39. #include <class_dimension.h>
  40. #include <dialog_text_properties.h>
  41. /**
  42. * DIALOG_PCB_TEXT_PROPERTIES, derived from DIALOG_PCB_TEXT_PROPERTIES_BASE
  43. * @see dialog_dialog_pcb_text_properties_base.h and
  44. * dialog_dialog_pcb_text_properties_base.cpp, automatically created by
  45. * wxFormBuilder.
  46. */
  47. DIALOG_TEXT_PROPERTIES::DIALOG_TEXT_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent, BOARD_ITEM* aItem,
  48. wxDC* aDC ) :
  49. DIALOG_TEXT_PROPERTIES_BASE( aParent ),
  50. m_Parent( aParent ), m_DC( aDC ), m_item( aItem ),
  51. m_edaText( nullptr ), m_modText( nullptr ), m_pcbText( nullptr ),
  52. m_textWidth( aParent, m_SizeXLabel, m_SizeXCtrl, m_SizeXUnits, true ),
  53. m_textHeight( aParent, m_SizeYLabel, m_SizeYCtrl, m_SizeYUnits, true ),
  54. m_thickness( aParent, m_ThicknessLabel, m_ThicknessCtrl, m_ThicknessUnits, true ),
  55. m_posX( aParent, m_PositionXLabel, m_PositionXCtrl, m_PositionXUnits ),
  56. m_posY( aParent, m_PositionYLabel, m_PositionYCtrl, m_PositionYUnits ),
  57. m_OrientValidator( 1, &m_OrientValue )
  58. {
  59. wxString title;
  60. if( m_item->Type() == PCB_DIMENSION_T )
  61. {
  62. title = _( "Dimension Text Properties" );
  63. DIMENSION* dimension = (DIMENSION*) m_item;
  64. m_edaText = &dimension->Text();
  65. m_pcbText = &dimension->Text();
  66. SetInitialFocus( m_DimensionText );
  67. m_SingleLineSizer->Show( false );
  68. m_MultiLineSizer->Show( false );
  69. m_KeepUpright->Show( false );
  70. m_statusLine->Show( false );
  71. }
  72. else if( m_item->Type() == PCB_MODULE_TEXT_T )
  73. {
  74. title = _( "Footprint Text Properties" );
  75. m_modText = (TEXTE_MODULE*) m_item;
  76. m_edaText = static_cast<EDA_TEXT*>( m_modText );
  77. switch( m_modText->GetType() )
  78. {
  79. case TEXTE_MODULE::TEXT_is_REFERENCE: m_TextLabel->SetLabel( _( "Reference:" ) ); break;
  80. case TEXTE_MODULE::TEXT_is_VALUE: m_TextLabel->SetLabel( _( "Value:" ) ); break;
  81. case TEXTE_MODULE::TEXT_is_DIVERS: m_TextLabel->SetLabel( _( "Text:" ) ); break;
  82. }
  83. SetInitialFocus( m_SingleLineText );
  84. m_MultiLineSizer->Show( false );
  85. m_DimensionTextSizer->Show( false );
  86. }
  87. else
  88. {
  89. title = _( "Text Properties" );
  90. m_pcbText = (TEXTE_PCB*) aItem;
  91. m_edaText = static_cast<EDA_TEXT*>( m_pcbText );
  92. SetInitialFocus( m_MultiLineText );
  93. m_SingleLineSizer->Show( false );
  94. m_DimensionTextSizer->Show( false );
  95. m_KeepUpright->Show( false );
  96. m_statusLine->Show( false );
  97. }
  98. SetTitle( title );
  99. m_hash_key = title;
  100. // Configure the layers list selector. Note that footprints are built outside the current
  101. // board and so we may need to show all layers if the text is on an unactivated layer.
  102. if( !m_Parent->GetBoard()->IsLayerEnabled( m_item->GetLayer() ) )
  103. m_LayerSelectionCtrl->ShowNonActivatedLayers( true );
  104. m_LayerSelectionCtrl->SetLayersHotkeys( false );
  105. m_LayerSelectionCtrl->SetNotAllowedLayerSet( LSET::ForbiddenTextLayers() );
  106. m_LayerSelectionCtrl->SetBoardFrame( m_Parent );
  107. m_LayerSelectionCtrl->Resync();
  108. m_OrientValue = 0.0;
  109. m_OrientValidator.SetRange( -360.0, 360.0 );
  110. m_OrientCtrl->SetValidator( m_OrientValidator );
  111. m_OrientValidator.SetWindow( m_OrientCtrl );
  112. // Set font sizes
  113. wxFont infoFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
  114. infoFont.SetSymbolicSize( wxFONTSIZE_SMALL );
  115. m_statusLine->SetFont( infoFont );
  116. m_sdbSizerOK->SetDefault();
  117. // wxTextCtrls fail to generate wxEVT_CHAR events when the wxTE_MULTILINE flag is set,
  118. // so we have to listen to wxEVT_CHAR_HOOK events instead.
  119. m_MultiLineText->Connect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXT_PROPERTIES::OnCharHook ), NULL, this );
  120. FinishDialogSettings();
  121. }
  122. DIALOG_TEXT_PROPERTIES::~DIALOG_TEXT_PROPERTIES()
  123. {
  124. m_MultiLineText->Disconnect( wxEVT_CHAR_HOOK, wxKeyEventHandler( DIALOG_TEXT_PROPERTIES::OnCharHook ), NULL, this );
  125. }
  126. /**
  127. * Routine for main window class to launch text properties dialog.
  128. */
  129. void PCB_BASE_EDIT_FRAME::InstallTextOptionsFrame( BOARD_ITEM* aText, wxDC* aDC )
  130. {
  131. m_canvas->SetIgnoreMouseEvents( true );
  132. #ifndef __WXMAC__
  133. DIALOG_TEXT_PROPERTIES dlg( this, aText, aDC );
  134. #else
  135. // Avoid "writes" in the dialog, creates errors with WxOverlay and NSView
  136. // Raising an Exception - Fixes #891347
  137. DIALOG_TEXT_PROPERTIES dlg( this, aText, NULL );
  138. #endif
  139. dlg.ShowModal();
  140. m_canvas->MoveCursorToCrossHair();
  141. m_canvas->SetIgnoreMouseEvents( false );
  142. }
  143. void DIALOG_TEXT_PROPERTIES::OnCharHook( wxKeyEvent& aEvent )
  144. {
  145. if( aEvent.GetKeyCode() == WXK_TAB )
  146. {
  147. int flags = 0;
  148. if( !aEvent.ShiftDown() )
  149. flags |= wxNavigationKeyEvent::IsForward;
  150. if( aEvent.ControlDown() )
  151. flags |= wxNavigationKeyEvent::WinChange;
  152. NavigateIn( flags );
  153. }
  154. else if( aEvent.GetKeyCode() == WXK_RETURN && aEvent.ShiftDown() )
  155. {
  156. TransferDataFromWindow();
  157. EndModal( wxID_OK );
  158. }
  159. else
  160. {
  161. aEvent.Skip();
  162. }
  163. }
  164. void DIALOG_TEXT_PROPERTIES::OnDimensionTextChange( wxCommandEvent& event )
  165. {
  166. EDA_UNITS_T units = UNSCALED_UNITS;
  167. bool useMils;
  168. FetchUnitsFromString( m_DimensionText->GetValue(), units, useMils );
  169. if( units != UNSCALED_UNITS )
  170. m_DimensionUnitsOpt->SetSelection( units == MILLIMETRES ? 2 : useMils ? 1 : 0 );
  171. }
  172. void DIALOG_TEXT_PROPERTIES::OnDimensionUnitsChange( wxCommandEvent& event )
  173. {
  174. DIMENSION* dimension = (DIMENSION*) m_item;
  175. EDA_UNITS_T units;
  176. bool useMils;
  177. // Get default units in case dimension text doesn't contain units.
  178. dimension->GetUnits( units, useMils );
  179. double value = ValueFromString( units, m_DimensionText->GetValue(), useMils );
  180. switch( event.GetSelection() )
  181. {
  182. case 0: units = INCHES; useMils = false; break;
  183. case 1: units = INCHES; useMils = true; break;
  184. case 2: units = MILLIMETRES; useMils = false; break;
  185. default: break;
  186. }
  187. m_DimensionText->SetValue( StringFromValue( units, value, true, useMils ) );
  188. }
  189. bool DIALOG_TEXT_PROPERTIES::TransferDataToWindow()
  190. {
  191. if( m_SingleLineText->IsShown() )
  192. {
  193. m_SingleLineText->SetValue( m_edaText->GetText() );
  194. if( m_modText && m_modText->GetType() == TEXTE_MODULE::TEXT_is_REFERENCE )
  195. SelectReferenceNumber( static_cast<wxTextEntry*>( m_SingleLineText ) );
  196. else
  197. m_SingleLineText->SetSelection( -1, -1 );
  198. }
  199. else if( m_MultiLineText->IsShown() )
  200. {
  201. m_MultiLineText->SetValue( m_edaText->GetText() );
  202. m_MultiLineText->SetSelection( -1, -1 );
  203. }
  204. else if (m_DimensionText->IsShown() )
  205. {
  206. m_DimensionText->SetValue( m_edaText->GetText() );
  207. m_DimensionText->SetSelection( -1, -1 );
  208. DIMENSION* dimension = (DIMENSION*) m_item;
  209. EDA_UNITS_T units;
  210. bool useMils;
  211. dimension->GetUnits( units, useMils );
  212. m_DimensionUnitsOpt->SetSelection( units == MILLIMETRES ? 2 : useMils ? 1 : 0 );
  213. }
  214. if( m_item->Type() == PCB_MODULE_TEXT_T )
  215. {
  216. MODULE* module = dynamic_cast<MODULE*>( m_modText->GetParent() );
  217. wxString msg;
  218. if( module )
  219. {
  220. msg.Printf( _("Footprint %s (%s), %s, rotated %.1f deg"),
  221. module->GetReference(),
  222. module->GetValue(),
  223. module->IsFlipped() ? _( "back side (mirrored)" ) : _( "front side" ),
  224. module->GetOrientation() / 10.0 );
  225. }
  226. m_statusLine->SetLabel( msg );
  227. }
  228. else
  229. {
  230. m_statusLine->Show( false );
  231. }
  232. m_LayerSelectionCtrl->SetLayerSelection( m_item->GetLayer() );
  233. m_textWidth.SetValue( m_edaText->GetTextSize().x );
  234. m_textHeight.SetValue( m_edaText->GetTextSize().y );
  235. m_thickness.SetValue( m_edaText->GetThickness() );
  236. m_posX.SetValue( m_edaText->GetTextPos().x );
  237. m_posY.SetValue( m_edaText->GetTextPos().y );
  238. m_Visible->SetValue( m_edaText->IsVisible() );
  239. m_Italic->SetValue( m_edaText->IsItalic() );
  240. EDA_TEXT_HJUSTIFY_T hJustify = m_edaText->GetHorizJustify();
  241. m_JustifyChoice->SetSelection( (int) hJustify + 1 );
  242. m_OrientValue = m_edaText->GetTextAngleDegrees();
  243. m_Mirrored->SetValue( m_edaText->IsMirrored() );
  244. if( m_modText )
  245. m_KeepUpright->SetValue( m_modText->IsKeepUpright() );
  246. return DIALOG_TEXT_PROPERTIES_BASE::TransferDataToWindow();
  247. }
  248. bool DIALOG_TEXT_PROPERTIES::TransferDataFromWindow()
  249. {
  250. if( !DIALOG_TEXT_PROPERTIES_BASE::TransferDataFromWindow() )
  251. return false;
  252. // Test for acceptable layer.
  253. // Incorrect layer can happen for old boards, having texts on edge cut layer for instance
  254. if( m_LayerSelectionCtrl->GetLayerSelection() < 0 )
  255. {
  256. wxMessageBox( _( "No layer selected, Please select the text layer" ) );
  257. return false;
  258. }
  259. if( !m_textWidth.Validate( TEXTS_MIN_SIZE, TEXTS_MAX_SIZE )
  260. || !m_textHeight.Validate( TEXTS_MIN_SIZE, TEXTS_MAX_SIZE ) )
  261. return false;
  262. BOARD_COMMIT commit( m_Parent );
  263. commit.Modify( m_item );
  264. // If no other command in progress, prepare undo command
  265. // (for a command in progress, will be made later, at the completion of command)
  266. int mask = EDA_ITEM_ALL_FLAGS - ( SELECTED | HIGHLIGHTED | BRIGHTENED );
  267. bool pushCommit = ( m_item->GetFlags() & mask ) == 0;
  268. /* set flag in edit to force undo/redo/abort proper operation,
  269. * and avoid new calls to SaveCopyInUndoList for the same text
  270. * this can occurs when a text is moved, and then rotated, edited ..
  271. */
  272. if( !pushCommit )
  273. m_item->SetFlags( IN_EDIT );
  274. #ifndef USE_WX_OVERLAY
  275. // Erase old text on screen if context is available
  276. if( m_DC )
  277. {
  278. m_item->Draw( m_Parent->GetCanvas(), m_DC, GR_XOR );
  279. }
  280. #endif
  281. // Set the new text content
  282. if( m_SingleLineText->IsShown() )
  283. {
  284. if( !m_SingleLineText->GetValue().IsEmpty() )
  285. m_edaText->SetText( m_SingleLineText->GetValue() );
  286. }
  287. else if( m_MultiLineText->IsShown() )
  288. {
  289. if( !m_MultiLineText->GetValue().IsEmpty() )
  290. m_edaText->SetText( m_MultiLineText->GetValue() );
  291. }
  292. else if( m_DimensionText->IsShown() )
  293. {
  294. if( !m_DimensionText->GetValue().IsEmpty() )
  295. m_edaText->SetText( m_DimensionText->GetValue() );
  296. DIMENSION* dimension = (DIMENSION*) m_item;
  297. switch( m_DimensionUnitsOpt->GetSelection() )
  298. {
  299. case 0: dimension->SetUnits( INCHES, false ); break;
  300. case 1: dimension->SetUnits( INCHES, true ); break;
  301. case 2: dimension->SetUnits( MILLIMETRES, false ); break;
  302. default: break;
  303. }
  304. }
  305. m_item->SetLayer( ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() ) );
  306. m_edaText->SetTextSize( wxSize( m_textWidth.GetValue(), m_textHeight.GetValue() ) );
  307. m_edaText->SetThickness( m_thickness.GetValue() );
  308. m_edaText->SetTextPos( wxPoint( m_posX.GetValue(), m_posY.GetValue() ) );
  309. if( m_modText )
  310. m_modText->SetLocalCoord();
  311. // Test for acceptable values for thickness and size and clamp if fails
  312. int maxthickness = Clamp_Text_PenSize( m_edaText->GetThickness(), m_edaText->GetTextSize() );
  313. if( m_edaText->GetThickness() > maxthickness )
  314. {
  315. DisplayError( this, _( "The text thickness is too large for the text size.\n"
  316. "It will be clamped." ) );
  317. m_edaText->SetThickness( maxthickness );
  318. }
  319. m_edaText->SetVisible( m_Visible->GetValue() );
  320. m_edaText->SetItalic( m_Italic->GetValue() );
  321. m_edaText->SetTextAngle( KiROUND( m_OrientValue * 10.0 ) );
  322. m_edaText->SetMirrored( m_Mirrored->GetValue() );
  323. if( m_modText )
  324. m_modText->SetKeepUpright( m_KeepUpright->GetValue() );
  325. switch( m_JustifyChoice->GetSelection() )
  326. {
  327. case 0: m_edaText->SetHorizJustify( GR_TEXT_HJUSTIFY_LEFT ); break;
  328. case 1: m_edaText->SetHorizJustify( GR_TEXT_HJUSTIFY_CENTER ); break;
  329. case 2: m_edaText->SetHorizJustify( GR_TEXT_HJUSTIFY_RIGHT ); break;
  330. default: break;
  331. }
  332. #ifndef USE_WX_OVERLAY
  333. // Finally, display new text if there is a context to do so
  334. if( m_DC )
  335. {
  336. m_item->Draw( m_Parent->GetCanvas(), m_DC, GR_OR );
  337. }
  338. #else
  339. m_Parent->Refresh();
  340. #endif
  341. if( pushCommit )
  342. commit.Push( _( "Change text properties" ) );
  343. return true;
  344. }