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.

443 lines
16 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2020 Jon Evans <jon@craftyjon.com>
  5. * Copyright (C) 2020-2023 KiCad Developers, see AUTHORS.txt for contributors.
  6. *
  7. * This program is free software: you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License as published by the
  9. * Free Software Foundation, either version 3 of the License, or (at your
  10. * option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful, but
  13. * WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. #include <widgets/bitmap_button.h>
  21. #include <widgets/font_choice.h>
  22. #include <board.h>
  23. #include <board_commit.h>
  24. #include <pcb_dimension.h>
  25. #include <pcb_base_edit_frame.h>
  26. #include <pcb_layer_box_selector.h>
  27. #include <widgets/unit_binder.h>
  28. #include <wx/msgdlg.h>
  29. #include "dialog_dimension_properties.h"
  30. DIALOG_DIMENSION_PROPERTIES::DIALOG_DIMENSION_PROPERTIES( PCB_BASE_EDIT_FRAME* aParent,
  31. BOARD_ITEM* aItem ) :
  32. DIALOG_DIMENSION_PROPERTIES_BASE( aParent ),
  33. m_frame( aParent ),
  34. m_cbLayerActual( m_cbLayer ),
  35. m_txtValueActual( m_txtValue ),
  36. m_textWidth( aParent, m_lblTextWidth, m_txtTextWidth, m_lblTextWidthUnits ),
  37. m_textHeight( aParent, m_lblTextHeight, m_txtTextHeight, m_lblTextHeightUnits ),
  38. m_textThickness( aParent, m_lblTextThickness, m_txtTextThickness, m_lblTextThicknessUnits ),
  39. m_textPosX( aParent, m_lblTextPosX, m_txtTextPosX, m_lblTextPosXUnits ),
  40. m_textPosY( aParent, m_lblTextPosY, m_txtTextPosY, m_lblTextPosYUnits ),
  41. m_orientation( aParent, m_lblTextOrientation, m_cbTextOrientation, nullptr ),
  42. m_lineThickness( aParent, m_lblLineThickness, m_txtLineThickness, m_lblLineThicknessUnits ),
  43. m_arrowLength( aParent, m_lblArrowLength, m_txtArrowLength, m_lblArrowLengthUnits ),
  44. m_extensionOffset( aParent, m_lblExtensionOffset, m_txtExtensionOffset, m_lblExtensionOffsetUnits )
  45. {
  46. wxASSERT( BaseType( aItem->Type() ) == PCB_DIMENSION_T );
  47. m_dimension = static_cast<PCB_DIMENSION_BASE*>( aItem );
  48. m_previewDimension = static_cast<PCB_DIMENSION_BASE*>( m_dimension->Clone() );
  49. m_previewDimension->SetParent( m_frame->GetBoard() );
  50. switch( m_dimension->Type() )
  51. {
  52. case PCB_DIM_LEADER_T:
  53. // Hide the main format controls and keep the leader controls shown
  54. m_sizerFormat->GetStaticBox()->Hide();
  55. m_sizerCenter->GetStaticBox()->Hide();
  56. m_cbLayerActual = m_cbLeaderLayer;
  57. m_txtValueActual = m_txtLeaderValue;
  58. // Remove a fewings from text format
  59. m_lblTextPositionMode->Hide();
  60. m_cbTextPositionMode->Hide();
  61. break;
  62. case PCB_DIM_CENTER_T:
  63. m_sizerLeader->GetStaticBox()->Hide();
  64. m_sizerFormat->GetStaticBox()->Hide();
  65. m_sizerText->GetStaticBox()->Hide();
  66. m_lblArrowLength->Hide();
  67. m_txtArrowLength->Hide();
  68. m_lblArrowLengthUnits->Hide();
  69. m_lblExtensionOffset->Hide();
  70. m_txtExtensionOffset->Hide();
  71. m_lblExtensionOffsetUnits->Hide();
  72. m_cbLayerActual = m_cbCenterLayer;
  73. break;
  74. default:
  75. m_sizerLeader->GetStaticBox()->Hide();
  76. m_sizerCenter->GetStaticBox()->Hide();
  77. break;
  78. }
  79. m_separator0->SetIsSeparator();
  80. m_bold->SetIsCheckButton();
  81. m_bold->SetBitmap( KiBitmap( BITMAPS::text_bold ) );
  82. m_italic->SetIsCheckButton();
  83. m_italic->SetBitmap( KiBitmap( BITMAPS::text_italic ) );
  84. m_separator1->SetIsSeparator();
  85. m_alignLeft->SetIsRadioButton();
  86. m_alignLeft->SetBitmap( KiBitmap( BITMAPS::text_align_left ) );
  87. m_alignCenter->SetIsRadioButton();
  88. m_alignCenter->SetBitmap( KiBitmap( BITMAPS::text_align_center ) );
  89. m_alignRight->SetIsRadioButton();
  90. m_alignRight->SetBitmap( KiBitmap( BITMAPS::text_align_right ) );
  91. m_separator2->SetIsSeparator();
  92. m_mirrored->SetIsCheckButton();
  93. m_mirrored->SetBitmap( KiBitmap( BITMAPS::text_mirrored ) );
  94. m_separator3->SetIsSeparator();
  95. // Fix the size after hiding/showing some of the properties
  96. Layout();
  97. // Configure display origin transforms
  98. m_textPosX.SetCoordType( ORIGIN_TRANSFORMS::ABS_X_COORD );
  99. m_textPosY.SetCoordType( ORIGIN_TRANSFORMS::ABS_Y_COORD );
  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_frame->GetBoard()->IsLayerEnabled( m_dimension->GetLayer() ) )
  103. m_cbLayerActual->ShowNonActivatedLayers( true );
  104. m_cbLayerActual->SetLayersHotkeys( false );
  105. m_cbLayerActual->SetBoardFrame( aParent );
  106. m_cbLayerActual->Resync();
  107. m_orientation.SetUnits( EDA_UNITS::DEGREES );
  108. m_orientation.SetPrecision( 3 );
  109. // Set predefined rotations in combo dropdown, according to the locale floating point
  110. // separator notation
  111. double rot_list[] = { 0.0, 90.0, -90.0, 180.0 };
  112. for( size_t ii = 0; ii < m_cbTextOrientation->GetCount() && ii < 4; ++ii )
  113. m_cbTextOrientation->SetString( ii, wxString::Format( "%.1f", rot_list[ii] ) );
  114. m_cbOverrideValue->Bind( wxEVT_CHECKBOX,
  115. [&]( wxCommandEvent& evt )
  116. {
  117. m_txtValue->Enable( m_cbOverrideValue->GetValue() );
  118. if( !m_cbOverrideValue->GetValue() )
  119. m_txtValue->SetValue( m_dimension->GetValueText() );
  120. } );
  121. auto updateEventHandler =
  122. [&]( wxCommandEvent& evt )
  123. {
  124. if( !m_cbOverrideValue->GetValue() )
  125. m_txtValue->ChangeValue( m_dimension->GetValueText() );
  126. updatePreviewText();
  127. };
  128. // No need to use m_txtValueActual here since we don't have previewing for leaders
  129. m_txtValue->Bind( wxEVT_TEXT, updateEventHandler );
  130. m_txtPrefix->Bind( wxEVT_TEXT, updateEventHandler );
  131. m_txtSuffix->Bind( wxEVT_TEXT, updateEventHandler );
  132. m_cbUnits->Bind( wxEVT_CHOICE, updateEventHandler );
  133. m_cbUnitsFormat->Bind( wxEVT_CHOICE, updateEventHandler );
  134. m_cbPrecision->Bind( wxEVT_CHOICE, updateEventHandler );
  135. m_cbSuppressZeroes->Bind( wxEVT_CHECKBOX, updateEventHandler );
  136. m_cbTextPositionMode->Bind( wxEVT_CHOICE,
  137. [&]( wxCommandEvent& aEvt )
  138. {
  139. // manual mode
  140. bool allowPositioning = ( m_cbTextPositionMode->GetSelection() == 2 );
  141. m_txtTextPosX->Enable( allowPositioning );
  142. m_txtTextPosY->Enable( allowPositioning );
  143. } );
  144. m_cbKeepAligned->Bind( wxEVT_CHECKBOX,
  145. [&]( wxCommandEvent& aEvt )
  146. {
  147. m_cbTextOrientation->Enable( !m_cbKeepAligned->GetValue() );
  148. } );
  149. SetupStandardButtons();
  150. finishDialogSettings();
  151. }
  152. DIALOG_DIMENSION_PROPERTIES::~DIALOG_DIMENSION_PROPERTIES()
  153. {
  154. delete m_previewDimension;
  155. }
  156. bool DIALOG_DIMENSION_PROPERTIES::TransferDataToWindow()
  157. {
  158. BOARD* board = m_frame->GetBoard();
  159. m_txtValue->Enable( m_dimension->GetOverrideTextEnabled() );
  160. m_cbOverrideValue->SetValue( m_dimension->GetOverrideTextEnabled() );
  161. switch( m_dimension->GetUnitsMode() )
  162. {
  163. case DIM_UNITS_MODE::INCHES: m_cbUnits->SetSelection( 0 ); break;
  164. case DIM_UNITS_MODE::MILS: m_cbUnits->SetSelection( 1 ); break;
  165. case DIM_UNITS_MODE::MILLIMETRES: m_cbUnits->SetSelection( 2 ); break;
  166. case DIM_UNITS_MODE::AUTOMATIC: m_cbUnits->SetSelection( 3 ); break;
  167. }
  168. m_cbUnitsFormat->SetSelection( static_cast<int>( m_dimension->GetUnitsFormat() ) );
  169. m_cbPrecision->SetSelection( static_cast<int>( m_dimension->GetPrecision() ) );
  170. m_txtPrefix->SetValue( board->ConvertKIIDsToCrossReferences( m_dimension->GetPrefix() ) );
  171. m_txtSuffix->SetValue( board->ConvertKIIDsToCrossReferences( m_dimension->GetSuffix() ) );
  172. if( m_cbLayerActual->SetLayerSelection( m_dimension->GetLayer() ) < 0 )
  173. {
  174. wxMessageBox( _( "This item was on a non-existing layer.\n"
  175. "It has been moved to the first defined layer." ) );
  176. m_cbLayerActual->SetSelection( 0 );
  177. }
  178. m_cbSuppressZeroes->SetValue( m_dimension->GetSuppressZeroes() );
  179. m_fontCtrl->SetFontSelection( m_dimension->GetFont() );
  180. m_textWidth.SetValue( m_dimension->GetTextSize().x );
  181. m_textHeight.SetValue( m_dimension->GetTextSize().y );
  182. m_textThickness.SetValue( m_dimension->GetTextThickness() );
  183. m_textPosX.SetValue( m_dimension->GetTextPos().x );
  184. m_textPosY.SetValue( m_dimension->GetTextPos().y );
  185. m_cbTextPositionMode->SetSelection( static_cast<int>( m_dimension->GetTextPositionMode() ) );
  186. if( m_dimension->GetTextPositionMode() != DIM_TEXT_POSITION::MANUAL )
  187. {
  188. m_txtTextPosX->Disable();
  189. m_txtTextPosY->Disable();
  190. }
  191. EDA_ANGLE orientation = m_dimension->GetTextAngle();
  192. m_orientation.SetAngleValue( orientation.Normalize180() );
  193. m_cbTextOrientation->Enable( !m_dimension->GetKeepTextAligned() );
  194. m_cbKeepAligned->SetValue( m_dimension->GetKeepTextAligned() );
  195. m_bold->Check( m_dimension->IsBold() );
  196. m_italic->Check( m_dimension->IsItalic() );
  197. switch ( m_dimension->GetHorizJustify() )
  198. {
  199. case GR_TEXT_H_ALIGN_LEFT: m_alignLeft->Check( true ); break;
  200. case GR_TEXT_H_ALIGN_CENTER: m_alignCenter->Check( true ); break;
  201. case GR_TEXT_H_ALIGN_RIGHT: m_alignRight->Check( true ); break;
  202. }
  203. m_mirrored->Check( m_dimension->IsMirrored() );
  204. m_lineThickness.SetValue( m_dimension->GetLineThickness() );
  205. m_arrowLength.SetValue( m_dimension->GetArrowLength() );
  206. m_extensionOffset.SetValue( m_dimension->GetExtensionOffset() );
  207. // Do this last; it depends on the other settings
  208. if( m_dimension->GetOverrideTextEnabled() )
  209. {
  210. wxString txt = board->ConvertKIIDsToCrossReferences( m_dimension->GetOverrideText() );
  211. m_txtValueActual->SetValue( txt );
  212. }
  213. else
  214. {
  215. m_txtValueActual->SetValue( m_dimension->GetValueText() );
  216. }
  217. if( m_dimension->Type() == PCB_DIM_LEADER_T )
  218. {
  219. PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( m_dimension );
  220. m_cbTextFrame->SetSelection( static_cast<int>( leader->GetTextBorder() ) );
  221. }
  222. return DIALOG_DIMENSION_PROPERTIES_BASE::TransferDataToWindow();
  223. }
  224. bool DIALOG_DIMENSION_PROPERTIES::TransferDataFromWindow()
  225. {
  226. if( !DIALOG_DIMENSION_PROPERTIES_BASE::TransferDataFromWindow() )
  227. return false;
  228. BOARD_COMMIT commit( m_frame );
  229. commit.Modify( m_dimension );
  230. // If no other command in progress, prepare undo command
  231. // (for a command in progress, will be made later, at the completion of command)
  232. bool pushCommit = ( m_dimension->GetEditFlags() == 0 );
  233. /* set flag in edit to force undo/redo/abort proper operation,
  234. * and avoid new calls to SaveCopyInUndoList for the same dimension
  235. * this can occurs when a dimension is moved, and then rotated, edited ..
  236. */
  237. if( !pushCommit )
  238. m_dimension->SetFlags( IN_EDIT );
  239. updateDimensionFromDialog( m_dimension );
  240. if( pushCommit )
  241. commit.Push( _( "Change dimension properties" ) );
  242. return true;
  243. }
  244. void DIALOG_DIMENSION_PROPERTIES::onFontSelected( wxCommandEvent & aEvent )
  245. {
  246. if( KIFONT::FONT::IsStroke( aEvent.GetString() ) )
  247. {
  248. m_textThickness.Show( true );
  249. int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
  250. int thickness = m_textThickness.GetValue();
  251. m_bold->Check( abs( thickness - GetPenSizeForBold( textSize ) )
  252. < abs( thickness - GetPenSizeForNormal( textSize ) ) );
  253. }
  254. else
  255. {
  256. m_textThickness.Show( false );
  257. }
  258. }
  259. void DIALOG_DIMENSION_PROPERTIES::onBoldToggle( wxCommandEvent & aEvent )
  260. {
  261. int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
  262. if( aEvent.IsChecked() )
  263. m_textThickness.ChangeValue( GetPenSizeForBold( textSize ) );
  264. else
  265. m_textThickness.ChangeValue( GetPenSizeForNormal( textSize ) );
  266. aEvent.Skip();
  267. }
  268. void DIALOG_DIMENSION_PROPERTIES::onAlignButton( wxCommandEvent& aEvent )
  269. {
  270. for( BITMAP_BUTTON* btn : { m_alignLeft, m_alignCenter, m_alignRight } )
  271. {
  272. if( btn->IsChecked() && btn != aEvent.GetEventObject() )
  273. btn->Check( false );
  274. }
  275. }
  276. void DIALOG_DIMENSION_PROPERTIES::onThickness( wxCommandEvent& event )
  277. {
  278. int textSize = std::min( m_textWidth.GetValue(), m_textHeight.GetValue() );
  279. int thickness = m_textThickness.GetValue();
  280. m_bold->Check( abs( thickness - GetPenSizeForBold( textSize ) )
  281. < abs( thickness - GetPenSizeForNormal( textSize ) ) );
  282. }
  283. void DIALOG_DIMENSION_PROPERTIES::updateDimensionFromDialog( PCB_DIMENSION_BASE* aTarget )
  284. {
  285. BOARD* board = m_frame->GetBoard();
  286. aTarget->SetOverrideTextEnabled( m_cbOverrideValue->GetValue() );
  287. if( m_cbOverrideValue->GetValue() )
  288. {
  289. wxString txt = board->ConvertCrossReferencesToKIIDs( m_txtValueActual->GetValue() );
  290. aTarget->SetOverrideText( txt );
  291. }
  292. aTarget->SetPrefix( board->ConvertCrossReferencesToKIIDs( m_txtPrefix->GetValue() ) );
  293. aTarget->SetSuffix( board->ConvertCrossReferencesToKIIDs( m_txtSuffix->GetValue() ) );
  294. aTarget->SetLayer( static_cast<PCB_LAYER_ID>( m_cbLayerActual->GetLayerSelection() ) );
  295. switch( m_cbUnits->GetSelection() )
  296. {
  297. case 0: aTarget->SetUnitsMode( DIM_UNITS_MODE::INCHES ); break;
  298. case 1: aTarget->SetUnitsMode( DIM_UNITS_MODE::MILS ); break;
  299. case 2: aTarget->SetUnitsMode( DIM_UNITS_MODE::MILLIMETRES ); break;
  300. case 3: aTarget->SetUnitsMode( DIM_UNITS_MODE::AUTOMATIC ); break;
  301. }
  302. aTarget->SetUnitsFormat( static_cast<DIM_UNITS_FORMAT>( m_cbUnitsFormat->GetSelection() ) );
  303. aTarget->SetPrecision( static_cast<DIM_PRECISION>( m_cbPrecision->GetSelection() ) );
  304. aTarget->SetSuppressZeroes( m_cbSuppressZeroes->GetValue() );
  305. DIM_TEXT_POSITION tpm = static_cast<DIM_TEXT_POSITION>( m_cbTextPositionMode->GetSelection() );
  306. aTarget->SetTextPositionMode( tpm );
  307. if( tpm == DIM_TEXT_POSITION::MANUAL )
  308. {
  309. VECTOR2I pos( m_textPosX.GetValue(), m_textPosY.GetValue() );
  310. aTarget->SetTextPos( pos );
  311. }
  312. aTarget->SetKeepTextAligned( m_cbKeepAligned->GetValue() );
  313. aTarget->SetTextAngle( m_orientation.GetAngleValue().Normalize() );
  314. aTarget->SetTextWidth( m_textWidth.GetValue() );
  315. aTarget->SetTextHeight( m_textHeight.GetValue() );
  316. aTarget->SetTextThickness( m_textThickness.GetValue() );
  317. if( m_fontCtrl->HaveFontSelection() )
  318. aTarget->SetFont( m_fontCtrl->GetFontSelection( m_bold->IsChecked(), m_italic->IsChecked() ) );
  319. aTarget->SetBold( m_bold->IsChecked() );
  320. aTarget->SetItalic( m_italic->IsChecked() );
  321. if( m_alignLeft->IsChecked() )
  322. aTarget->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
  323. else if( m_alignCenter->IsChecked() )
  324. aTarget->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
  325. else
  326. aTarget->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
  327. aTarget->SetMirrored( m_mirrored->IsChecked() );
  328. aTarget->SetLineThickness( m_lineThickness.GetValue() );
  329. aTarget->SetArrowLength( m_arrowLength.GetValue() );
  330. aTarget->SetExtensionOffset( m_extensionOffset.GetValue() );
  331. if( aTarget->Type() == PCB_DIM_LEADER_T )
  332. {
  333. PCB_DIM_LEADER* leader = static_cast<PCB_DIM_LEADER*>( aTarget );
  334. leader->SetTextBorder( static_cast<DIM_TEXT_BORDER>( m_cbTextFrame->GetSelection()));
  335. }
  336. aTarget->Update();
  337. }
  338. void DIALOG_DIMENSION_PROPERTIES::updatePreviewText()
  339. {
  340. updateDimensionFromDialog( m_previewDimension );
  341. m_staticTextPreview->SetLabel( m_previewDimension->GetShownText() );
  342. }