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.

1981 lines
66 KiB

14 years ago
14 years ago
12 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
5 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
16 years ago
16 years ago
14 years ago
16 years ago
16 years ago
14 years ago
16 years ago
16 years ago
16 years ago
14 years ago
  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2019 Jean-Pierre Charras, jp.charras at wanadoo.fr
  5. * Copyright (C) 2013 Dick Hollenbeck, dick@softplc.com
  6. * Copyright (C) 2008-2013 Wayne Stambaugh <stambaughw@verizon.net>
  7. * Copyright (C) 1992-2020 KiCad Developers, see AUTHORS.txt for contributors.
  8. *
  9. * This program is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU General Public License
  11. * as published by the Free Software Foundation; either version 2
  12. * of the License, or (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, you may find one here:
  21. * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  22. * or you may search the http://www.gnu.org website for the version 2 license,
  23. * or you may write to the Free Software Foundation, Inc.,
  24. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  25. */
  26. #include <base_units.h>
  27. #include <bitmaps.h>
  28. #include <board_commit.h>
  29. #include <class_board.h>
  30. #include <class_module.h>
  31. #include <confirm.h>
  32. #include <convert_basic_shapes_to_polygon.h> // for enum RECT_CHAMFER_POSITIONS definition
  33. #include <dialog_pad_properties.h>
  34. #include <gal/graphics_abstraction_layer.h>
  35. #include <html_messagebox.h>
  36. #include <macros.h>
  37. #include <pcb_base_frame.h>
  38. #include <pcb_painter.h>
  39. #include <pcbnew_settings.h>
  40. #include <settings/color_settings.h>
  41. #include <view/view_controls.h>
  42. #include <widgets/net_selector.h>
  43. #include <tool/tool_manager.h>
  44. #include <tools/pad_tool.h>
  45. #include <advanced_config.h> // for pad property feature management
  46. // list of pad shapes, ordered like the pad shape wxChoice in dialog.
  47. static PAD_SHAPE_T code_shape[] =
  48. {
  49. PAD_SHAPE_CIRCLE,
  50. PAD_SHAPE_OVAL,
  51. PAD_SHAPE_RECT,
  52. PAD_SHAPE_TRAPEZOID,
  53. PAD_SHAPE_ROUNDRECT,
  54. PAD_SHAPE_CHAMFERED_RECT,
  55. PAD_SHAPE_CHAMFERED_RECT, // choice = CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT
  56. PAD_SHAPE_CUSTOM, // choice = CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR
  57. PAD_SHAPE_CUSTOM // choice = PAD_SHAPE_CUSTOM_RECT_ANCHOR
  58. };
  59. // the ordered index of the pad shape wxChoice in dialog.
  60. // keep it consistent with code_shape[] and dialog strings
  61. enum CODE_CHOICE
  62. {
  63. CHOICE_SHAPE_CIRCLE = 0,
  64. CHOICE_SHAPE_OVAL,
  65. CHOICE_SHAPE_RECT,
  66. CHOICE_SHAPE_TRAPEZOID,
  67. CHOICE_SHAPE_ROUNDRECT,
  68. CHOICE_SHAPE_CHAMFERED_RECT,
  69. CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT,
  70. CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR,
  71. CHOICE_SHAPE_CUSTOM_RECT_ANCHOR
  72. };
  73. static PAD_ATTR_T code_type[] =
  74. {
  75. PAD_ATTRIB_STANDARD,
  76. PAD_ATTRIB_SMD,
  77. PAD_ATTRIB_CONN,
  78. PAD_ATTRIB_HOLE_NOT_PLATED,
  79. PAD_ATTRIB_SMD // Aperture pad :type SMD with no copper layers,
  80. // only on tech layers (usually only on paste layer
  81. };
  82. // Default mask layers setup for pads according to the pad type
  83. static const LSET std_pad_layers[] =
  84. {
  85. D_PAD::StandardMask(), // PAD_ATTRIB_STANDARD:
  86. D_PAD::SMDMask(), // PAD_ATTRIB_SMD:
  87. D_PAD::ConnSMDMask(), // PAD_ATTRIB_CONN:
  88. D_PAD::UnplatedHoleMask(), // PAD_ATTRIB_HOLE_NOT_PLATED:
  89. D_PAD::ApertureMask()
  90. };
  91. void PCB_BASE_FRAME::InstallPadOptionsFrame( D_PAD* aPad )
  92. {
  93. DIALOG_PAD_PROPERTIES dlg( this, aPad );
  94. if( dlg.ShowQuasiModal() == wxID_OK ) // QuasiModal required for NET_SELECTOR
  95. {
  96. // aPad can be NULL, if the dialog is called from the footprint editor
  97. // to set the default pad setup
  98. if( aPad )
  99. {
  100. PAD_TOOL* padTools = m_toolManager->GetTool<PAD_TOOL>();
  101. if( padTools )
  102. padTools->SetLastPadName( aPad->GetName() );
  103. }
  104. }
  105. }
  106. DIALOG_PAD_PROPERTIES::DIALOG_PAD_PROPERTIES( PCB_BASE_FRAME* aParent, D_PAD* aPad ) :
  107. DIALOG_PAD_PROPERTIES_BASE( aParent ),
  108. m_parent( aParent ),
  109. m_canUpdate( false ),
  110. m_posX( aParent, m_posXLabel, m_posXCtrl, m_posXUnits ),
  111. m_posY( aParent, m_posYLabel, m_posYCtrl, m_posYUnits ),
  112. m_sizeX( aParent, m_sizeXLabel, m_sizeXCtrl, m_sizeXUnits, true ),
  113. m_sizeY( aParent, m_sizeYLabel, m_sizeYCtrl, m_sizeYUnits, true ),
  114. m_offsetX( aParent, m_offsetXLabel, m_offsetXCtrl, m_offsetXUnits, true ),
  115. m_offsetY( aParent, m_offsetYLabel, m_offsetYCtrl, m_offsetYUnits, true ),
  116. m_padToDie( aParent, m_padToDieLabel, m_padToDieCtrl, m_padToDieUnits, true ),
  117. m_trapDelta( aParent, m_trapDeltaLabel, m_trapDeltaCtrl, m_trapDeltaUnits, true ),
  118. m_cornerRadius( aParent, m_cornerRadiusLabel, m_tcCornerRadius, m_cornerRadiusUnits, true ),
  119. m_holeX( aParent, m_holeXLabel, m_holeXCtrl, m_holeXUnits, true ),
  120. m_holeY( aParent, m_holeYLabel, m_holeYCtrl, m_holeYUnits, true ),
  121. m_OrientValidator( 1, &m_OrientValue ),
  122. m_clearance( aParent, m_clearanceLabel, m_clearanceCtrl, m_clearanceUnits, true ),
  123. m_maskClearance( aParent, m_maskClearanceLabel, m_maskClearanceCtrl, m_maskClearanceUnits, true ),
  124. m_pasteClearance( aParent, m_pasteClearanceLabel, m_pasteClearanceCtrl, m_pasteClearanceUnits, true ),
  125. m_spokeWidth( aParent, m_spokeWidthLabel, m_spokeWidthCtrl, m_spokeWidthUnits, true ),
  126. m_thermalGap( aParent, m_thermalGapLabel, m_thermalGapCtrl, m_thermalGapUnits, true )
  127. {
  128. m_currentPad = aPad; // aPad can be NULL, if the dialog is called
  129. // from the footprint editor to set default pad setup
  130. m_board = m_parent->GetBoard();
  131. m_PadNetSelector->SetBoard( m_board );
  132. m_PadNetSelector->SetNetInfo( &m_board->GetNetInfo() );
  133. m_OrientValidator.SetRange( -360.0, 360.0 );
  134. m_orientation->SetValidator( m_OrientValidator );
  135. m_OrientValidator.SetWindow( m_orientation );
  136. m_cbShowPadOutline->SetValue( m_sketchPreview );
  137. m_FlippedWarningIcon->SetBitmap( KiBitmap( dialog_warning_xpm ) );
  138. m_nonCopperWarningIcon->SetBitmap( KiBitmap( dialog_warning_xpm ) );
  139. m_padMaster = &m_parent->GetDesignSettings().m_Pad_Master;
  140. m_dummyPad = new D_PAD( (MODULE*) NULL );
  141. if( aPad )
  142. {
  143. *m_dummyPad = *aPad;
  144. m_dummyPad->ClearFlags( SELECTED|BRIGHTENED );
  145. }
  146. else // We are editing a "master" pad, i.e. a template to create new pads
  147. *m_dummyPad = *m_padMaster;
  148. initValues();
  149. wxFont infoFont = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
  150. infoFont.SetSymbolicSize( wxFONTSIZE_SMALL );
  151. m_techLayersLabel->SetFont( infoFont );
  152. m_parentInfoLine1->SetFont( infoFont );
  153. m_parentInfoLine2->SetFont( infoFont );
  154. infoFont.SetStyle( wxFONTSTYLE_ITALIC );
  155. m_nonCopperNote->SetFont( infoFont );
  156. m_staticTextInfoPaste->SetFont( infoFont );
  157. m_staticTextInfoNegVal->SetFont( infoFont );
  158. m_staticTextInfoPosValue->SetFont( infoFont );
  159. m_staticTextPrimitiveListWarning->SetFont( infoFont );
  160. // Usually, TransferDataToWindow is called by OnInitDialog
  161. // calling it here fixes all widget sizes so FinishDialogSettings can safely fix minsizes
  162. TransferDataToWindow();
  163. // Initialize canvas to be able to display the dummy pad:
  164. prepareCanvas();
  165. SetInitialFocus( m_PadNumCtrl );
  166. m_sdbSizerOK->SetDefault();
  167. m_canUpdate = true;
  168. m_PadNetSelector->Connect( NET_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES::OnValuesChanged ), NULL, this );
  169. // Now all widgets have the size fixed, call FinishDialogSettings
  170. FinishDialogSettings();
  171. wxUpdateUIEvent dummy;
  172. OnUpdateUI( dummy );
  173. }
  174. DIALOG_PAD_PROPERTIES::~DIALOG_PAD_PROPERTIES()
  175. {
  176. m_PadNetSelector->Disconnect( NET_SELECTED, wxCommandEventHandler( DIALOG_PAD_PROPERTIES::OnValuesChanged ), NULL, this );
  177. delete m_dummyPad;
  178. delete m_axisOrigin;
  179. }
  180. bool DIALOG_PAD_PROPERTIES::m_sketchPreview = false; // Stores the pad draw option during a session
  181. void DIALOG_PAD_PROPERTIES::OnInitDialog( wxInitDialogEvent& event )
  182. {
  183. m_selectedColor = COLOR4D( 1.0, 1.0, 1.0, 0.7 );
  184. // Needed on some WM to be sure the pad is redrawn according to the final size
  185. // of the canvas, with the right zoom factor
  186. redraw();
  187. }
  188. void DIALOG_PAD_PROPERTIES::OnCancel( wxCommandEvent& event )
  189. {
  190. // Mandatory to avoid m_panelShowPadGal trying to draw something
  191. // in a non valid context during closing process:
  192. m_padPreviewGAL->StopDrawing();
  193. // Now call default handler for wxID_CANCEL command event
  194. event.Skip();
  195. }
  196. void DIALOG_PAD_PROPERTIES::enablePrimitivePage( bool aEnable )
  197. {
  198. // Enable or disable the widgets in page managing custom shape primitives
  199. m_listCtrlPrimitives->Enable( aEnable );
  200. m_buttonDel->Enable( aEnable );
  201. m_buttonEditShape->Enable( aEnable );
  202. m_buttonAddShape->Enable( aEnable );
  203. m_buttonDup->Enable( aEnable );
  204. m_buttonGeometry->Enable( aEnable );
  205. }
  206. void DIALOG_PAD_PROPERTIES::prepareCanvas()
  207. {
  208. // Initialize the canvas to display the pad
  209. #ifdef __WXMAC__
  210. // Cairo renderer doesn't handle Retina displays
  211. EDA_DRAW_PANEL_GAL::GAL_TYPE backend = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
  212. #else
  213. EDA_DRAW_PANEL_GAL::GAL_TYPE backend = EDA_DRAW_PANEL_GAL::GAL_TYPE_CAIRO;
  214. #endif
  215. m_padPreviewGAL = new PCB_DRAW_PANEL_GAL( this, -1, wxDefaultPosition, wxDefaultSize,
  216. m_parent->GetGalDisplayOptions(), backend );
  217. m_padPreviewSizer->Add( m_padPreviewGAL, 12, wxEXPAND | wxALL, 5 );
  218. // Show the X and Y axis. It is usefull because pad shape can have an offset
  219. // or be a complex shape.
  220. KIGFX::COLOR4D axis_color = LIGHTBLUE;
  221. m_axisOrigin = new KIGFX::ORIGIN_VIEWITEM( axis_color, KIGFX::ORIGIN_VIEWITEM::CROSS,
  222. Millimeter2iu( 0.2 ),
  223. VECTOR2D( m_dummyPad->GetPosition() ) );
  224. m_axisOrigin->SetDrawAtZero( true );
  225. m_padPreviewGAL->UpdateColors();
  226. m_padPreviewGAL->SetStealsFocus( false );
  227. m_padPreviewGAL->GetViewControls()->ApplySettings(
  228. m_parent->GetCanvas()->GetViewControls()->GetSettings() );
  229. m_padPreviewGAL->Show();
  230. KIGFX::VIEW* view = m_padPreviewGAL->GetView();
  231. // fix the pad render mode (filled/not filled)
  232. auto settings = static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
  233. bool sketchMode = m_cbShowPadOutline->IsChecked();
  234. settings->SetSketchMode( LAYER_PADS_TH, sketchMode );
  235. settings->SetSketchMode( LAYER_PAD_FR, sketchMode );
  236. settings->SetSketchMode( LAYER_PAD_BK, sketchMode );
  237. settings->SetSketchModeGraphicItems( sketchMode );
  238. // gives a non null grid size (0.001mm) because GAL layer does not like a 0 size grid:
  239. double gridsize = 0.001 * IU_PER_MM;
  240. view->GetGAL()->SetGridSize( VECTOR2D( gridsize, gridsize ) );
  241. // And do not show the grid:
  242. view->GetGAL()->SetGridVisibility( false );
  243. view->Add( m_dummyPad );
  244. view->Add( m_axisOrigin );
  245. m_padPreviewGAL->StartDrawing();
  246. Connect( wxEVT_SIZE, wxSizeEventHandler( DIALOG_PAD_PROPERTIES::OnResize ) );
  247. }
  248. void DIALOG_PAD_PROPERTIES::updateRoundRectCornerValues()
  249. {
  250. // Note: use m_tcCornerSizeRatio->ChangeValue() to avoid generating a wxEVT_TEXT event
  251. if( m_dummyPad->GetShape() == PAD_SHAPE_ROUNDRECT ||
  252. m_dummyPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
  253. {
  254. wxString ratio = wxString::Format( "%.1f", m_dummyPad->GetRoundRectRadiusRatio() * 100 );
  255. m_tcCornerSizeRatio->ChangeValue( ratio );
  256. m_tcCornerSizeRatio1->ChangeValue( ratio );
  257. m_cornerRadius.ChangeValue( m_dummyPad->GetRoundRectCornerRadius() );
  258. ratio = wxString::Format( "%.1f", m_dummyPad->GetChamferRectRatio() * 100 );
  259. m_tcChamferRatio->ChangeValue( ratio );
  260. m_tcChamferRatio1->ChangeValue( ratio );
  261. }
  262. }
  263. void DIALOG_PAD_PROPERTIES::onCornerRadiusChange( wxCommandEvent& event )
  264. {
  265. if( m_dummyPad->GetShape() != PAD_SHAPE_ROUNDRECT &&
  266. m_dummyPad->GetShape() != PAD_SHAPE_CHAMFERED_RECT )
  267. return;
  268. double rrRadius = m_cornerRadius.GetValue();
  269. if( rrRadius < 0.0 )
  270. {
  271. rrRadius = 0.0;
  272. m_tcCornerRadius->ChangeValue( wxString::Format( "%.1f", rrRadius ) );
  273. }
  274. transferDataToPad( m_dummyPad );
  275. m_dummyPad->SetRoundRectCornerRadius( rrRadius );
  276. auto ratio = wxString::Format( "%.1f", m_dummyPad->GetRoundRectRadiusRatio() * 100 );
  277. m_tcCornerSizeRatio->ChangeValue( ratio );
  278. m_tcCornerSizeRatio1->ChangeValue( ratio );
  279. redraw();
  280. }
  281. void DIALOG_PAD_PROPERTIES::onCornerSizePercentChange( wxCommandEvent& event )
  282. {
  283. if( m_dummyPad->GetShape() != PAD_SHAPE_ROUNDRECT &&
  284. m_dummyPad->GetShape() != PAD_SHAPE_CHAMFERED_RECT )
  285. {
  286. return;
  287. }
  288. wxObject* ctrl = event.GetEventObject();
  289. wxString value = event.GetString();
  290. bool changed = false;
  291. if( ctrl == m_tcCornerSizeRatio || ctrl == m_tcCornerSizeRatio1 )
  292. {
  293. double ratioPercent;
  294. if( value.ToDouble( &ratioPercent ) )
  295. {
  296. // Clamp ratioPercent to acceptable value (0.0 to 50.0)
  297. if( ratioPercent < 0.0 )
  298. {
  299. ratioPercent = 0.0;
  300. value.Printf( "%.1f", ratioPercent );
  301. m_tcCornerSizeRatio->ChangeValue( value );
  302. m_tcCornerSizeRatio1->ChangeValue( value );
  303. }
  304. if( ratioPercent > 50.0 )
  305. {
  306. ratioPercent = 0.5;
  307. value.Printf( "%.1f", ratioPercent*100.0 );
  308. m_tcCornerSizeRatio->ChangeValue( value );
  309. m_tcCornerSizeRatio1->ChangeValue( value );
  310. }
  311. if( ctrl == m_tcCornerSizeRatio )
  312. m_tcCornerSizeRatio1->ChangeValue( value );
  313. else
  314. m_tcCornerSizeRatio->ChangeValue( value );
  315. changed = true;
  316. }
  317. }
  318. else if( ctrl == m_tcChamferRatio || ctrl == m_tcChamferRatio1 )
  319. {
  320. double ratioPercent;
  321. if( value.ToDouble( &ratioPercent ) )
  322. {
  323. // Clamp ratioPercent to acceptable value (0.0 to 50.0)
  324. if( ratioPercent < 0.0 )
  325. {
  326. ratioPercent = 0.0;
  327. value.Printf( "%.1f", ratioPercent );
  328. m_tcChamferRatio->ChangeValue( value );
  329. m_tcChamferRatio1->ChangeValue( value );
  330. }
  331. if( ratioPercent > 50.0 )
  332. {
  333. ratioPercent = 0.5;
  334. value.Printf( "%.1f", ratioPercent*100.0 );
  335. m_tcChamferRatio->ChangeValue( value );
  336. m_tcChamferRatio1->ChangeValue( value );
  337. }
  338. if( ctrl == m_tcChamferRatio )
  339. m_tcChamferRatio1->ChangeValue( value );
  340. else
  341. m_tcChamferRatio->ChangeValue( value );
  342. changed = true;
  343. }
  344. }
  345. if( changed )
  346. {
  347. transferDataToPad( m_dummyPad );
  348. m_cornerRadius.ChangeValue( m_dummyPad->GetRoundRectCornerRadius() );
  349. redraw();
  350. }
  351. }
  352. void DIALOG_PAD_PROPERTIES::initValues()
  353. {
  354. wxString msg;
  355. double angle;
  356. // Disable pad net name wxTextCtrl if the caller is the footprint editor
  357. // because nets are living only in the board managed by the board editor
  358. m_canEditNetName = m_parent->IsType( FRAME_PCB_EDITOR );
  359. // Setup layers names from board
  360. // Should be made first, before calling m_rbCopperLayersSel->SetSelection()
  361. m_rbCopperLayersSel->SetString( 0, m_board->GetLayerName( F_Cu ) );
  362. m_rbCopperLayersSel->SetString( 1, m_board->GetLayerName( B_Cu ) );
  363. m_PadLayerAdhCmp->SetLabel( m_board->GetLayerName( F_Adhes ) );
  364. m_PadLayerAdhCu->SetLabel( m_board->GetLayerName( B_Adhes ) );
  365. m_PadLayerPateCmp->SetLabel( m_board->GetLayerName( F_Paste ) );
  366. m_PadLayerPateCu->SetLabel( m_board->GetLayerName( B_Paste ) );
  367. m_PadLayerSilkCmp->SetLabel( m_board->GetLayerName( F_SilkS ) );
  368. m_PadLayerSilkCu->SetLabel( m_board->GetLayerName( B_SilkS ) );
  369. m_PadLayerMaskCmp->SetLabel( m_board->GetLayerName( F_Mask ) );
  370. m_PadLayerMaskCu->SetLabel( m_board->GetLayerName( B_Mask ) );
  371. m_PadLayerECO1->SetLabel( m_board->GetLayerName( Eco1_User ) );
  372. m_PadLayerECO2->SetLabel( m_board->GetLayerName( Eco2_User ) );
  373. m_PadLayerDraft->SetLabel( m_board->GetLayerName( Dwgs_User ) );
  374. m_isFlipped = false;
  375. if( m_currentPad )
  376. {
  377. m_isFlipped = m_currentPad->IsFlipped();
  378. // Diplay parent footprint info
  379. MODULE* footprint = m_currentPad->GetParent();
  380. wxString msg1, msg2;
  381. if( footprint )
  382. {
  383. wxString side = footprint->IsFlipped() ? _( "back side (mirrored)" ) : _( "front side" );
  384. msg1.Printf( _("Footprint %s (%s),"), footprint->GetReference(), footprint->GetValue() );
  385. msg2.Printf( _("%s, rotated %.1f deg"), side, footprint->GetOrientation() / 10.0 );
  386. }
  387. m_parentInfoLine1->SetLabel( msg1 );
  388. m_parentInfoLine2->SetLabel( msg2 );
  389. }
  390. if( m_isFlipped )
  391. {
  392. wxPoint pt = m_dummyPad->GetOffset();
  393. pt.y = -pt.y;
  394. m_dummyPad->SetOffset( pt );
  395. wxSize sz = m_dummyPad->GetDelta();
  396. sz.y = -sz.y;
  397. m_dummyPad->SetDelta( sz );
  398. // flip pad's layers
  399. m_dummyPad->SetLayerSet( FlipLayerMask( m_dummyPad->GetLayerSet() ) );
  400. // flip custom pad shapes
  401. m_dummyPad->FlipPrimitives();
  402. }
  403. m_primitives = m_dummyPad->GetPrimitives();
  404. m_FlippedWarningSizer->Show( m_isFlipped );
  405. m_PadNumCtrl->SetValue( m_dummyPad->GetName() );
  406. m_PadNetSelector->SetSelectedNetcode( m_dummyPad->GetNetCode() );
  407. // Display current pad parameters units:
  408. m_posX.SetValue( m_dummyPad->GetPosition().x );
  409. m_posY.SetValue( m_dummyPad->GetPosition().y );
  410. m_holeX.SetValue( m_dummyPad->GetDrillSize().x );
  411. m_holeY.SetValue( m_dummyPad->GetDrillSize().y );
  412. m_sizeX.SetValue( m_dummyPad->GetSize().x );
  413. m_sizeY.SetValue( m_dummyPad->GetSize().y );
  414. m_offsetShapeOpt->SetValue( m_dummyPad->GetOffset() != wxPoint() );
  415. m_offsetX.SetValue( m_dummyPad->GetOffset().x );
  416. m_offsetY.SetValue( m_dummyPad->GetOffset().y );
  417. if( m_dummyPad->GetDelta().x )
  418. {
  419. m_trapDelta.SetValue( m_dummyPad->GetDelta().x );
  420. m_trapAxisCtrl->SetSelection( 0 );
  421. }
  422. else
  423. {
  424. m_trapDelta.SetValue( m_dummyPad->GetDelta().y );
  425. m_trapAxisCtrl->SetSelection( 1 );
  426. }
  427. m_padToDieOpt->SetValue( m_dummyPad->GetPadToDieLength() != 0 );
  428. m_padToDie.SetValue( m_dummyPad->GetPadToDieLength() );
  429. m_clearance.SetValue( m_dummyPad->GetLocalClearance() );
  430. m_maskClearance.SetValue( m_dummyPad->GetLocalSolderMaskMargin() );
  431. m_spokeWidth.SetValue( m_dummyPad->GetThermalWidth() );
  432. m_thermalGap.SetValue( m_dummyPad->GetThermalGap() );
  433. m_pasteClearance.SetValue( m_dummyPad->GetLocalSolderPasteMargin() );
  434. // Prefer "-0" to "0" for normally negative values
  435. if( m_dummyPad->GetLocalSolderPasteMargin() == 0 )
  436. m_pasteClearanceCtrl->SetValue( wxT( "-" ) + m_pasteClearanceCtrl->GetValue() );
  437. msg.Printf( wxT( "%f" ), m_dummyPad->GetLocalSolderPasteMarginRatio() * 100.0 );
  438. if( m_dummyPad->GetLocalSolderPasteMarginRatio() == 0.0 && msg[0] == '0' )
  439. // Sometimes Printf adds a sign if the value is small
  440. m_SolderPasteMarginRatioCtrl->SetValue( wxT( "-" ) + msg );
  441. else
  442. m_SolderPasteMarginRatioCtrl->SetValue( msg );
  443. switch( m_dummyPad->GetZoneConnection() )
  444. {
  445. default:
  446. case ZONE_CONNECTION::INHERITED: m_ZoneConnectionChoice->SetSelection( 0 ); break;
  447. case ZONE_CONNECTION::FULL: m_ZoneConnectionChoice->SetSelection( 1 ); break;
  448. case ZONE_CONNECTION::THERMAL: m_ZoneConnectionChoice->SetSelection( 2 ); break;
  449. case ZONE_CONNECTION::NONE: m_ZoneConnectionChoice->SetSelection( 3 ); break;
  450. }
  451. if( m_dummyPad->GetCustomShapeInZoneOpt() == CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL )
  452. m_ZoneCustomPadShape->SetSelection( 1 );
  453. else
  454. m_ZoneCustomPadShape->SetSelection( 0 );
  455. if( m_currentPad )
  456. {
  457. angle = m_currentPad->GetOrientation();
  458. MODULE* footprint = m_currentPad->GetParent();
  459. if( footprint )
  460. angle -= footprint->GetOrientation();
  461. if( m_isFlipped )
  462. angle = -angle;
  463. m_dummyPad->SetOrientation( angle );
  464. }
  465. angle = m_dummyPad->GetOrientation();
  466. NORMALIZE_ANGLE_180( angle ); // ? normalizing is in D_PAD::SetOrientation()
  467. // Set layers used by this pad: :
  468. setPadLayersList( m_dummyPad->GetLayerSet() );
  469. // Pad Orient
  470. // Note: use ChangeValue() instead of SetValue() so that we don't generate events
  471. m_orientation->ChangeValue( StringFromValue( EDA_UNITS::DEGREES, angle ) );
  472. switch( m_dummyPad->GetShape() )
  473. {
  474. default:
  475. case PAD_SHAPE_CIRCLE: m_PadShape->SetSelection( CHOICE_SHAPE_CIRCLE ); break;
  476. case PAD_SHAPE_OVAL: m_PadShape->SetSelection( CHOICE_SHAPE_OVAL ); break;
  477. case PAD_SHAPE_RECT: m_PadShape->SetSelection( CHOICE_SHAPE_RECT ); break;
  478. case PAD_SHAPE_TRAPEZOID: m_PadShape->SetSelection( CHOICE_SHAPE_TRAPEZOID ); break;
  479. case PAD_SHAPE_ROUNDRECT: m_PadShape->SetSelection( CHOICE_SHAPE_ROUNDRECT ); break;
  480. case PAD_SHAPE_CHAMFERED_RECT:
  481. if( m_dummyPad->GetRoundRectRadiusRatio() > 0.0 )
  482. m_PadShape->SetSelection( CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT );
  483. else
  484. m_PadShape->SetSelection( CHOICE_SHAPE_CHAMFERED_RECT );
  485. break;
  486. case PAD_SHAPE_CUSTOM:
  487. if( m_dummyPad->GetAnchorPadShape() == PAD_SHAPE_RECT )
  488. m_PadShape->SetSelection( CHOICE_SHAPE_CUSTOM_RECT_ANCHOR );
  489. else
  490. m_PadShape->SetSelection( CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR );
  491. break;
  492. }
  493. m_cbTopLeft->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_TOP_LEFT) );
  494. m_cbTopLeft1->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_TOP_LEFT) );
  495. m_cbTopRight->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_TOP_RIGHT) );
  496. m_cbTopRight1->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_TOP_RIGHT) );
  497. m_cbBottomLeft->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_BOTTOM_LEFT) );
  498. m_cbBottomLeft1->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_BOTTOM_LEFT) );
  499. m_cbBottomRight->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_BOTTOM_RIGHT) );
  500. m_cbBottomRight1->SetValue( (m_dummyPad->GetChamferPositions() & RECT_CHAMFER_BOTTOM_RIGHT) );
  501. updateRoundRectCornerValues();
  502. enablePrimitivePage( PAD_SHAPE_CUSTOM == m_dummyPad->GetShape() );
  503. // Type of pad selection
  504. bool aperture = m_dummyPad->GetAttribute() == PAD_ATTRIB_CONN && m_dummyPad->IsAperturePad();
  505. if( aperture )
  506. {
  507. m_PadType->SetSelection( 4 );
  508. }
  509. else
  510. {
  511. switch( m_dummyPad->GetAttribute() )
  512. {
  513. case PAD_ATTRIB_STANDARD: m_PadType->SetSelection( 0 ); break;
  514. case PAD_ATTRIB_SMD: m_PadType->SetSelection( 1 ); break;
  515. case PAD_ATTRIB_CONN: m_PadType->SetSelection( 2 ); break;
  516. case PAD_ATTRIB_HOLE_NOT_PLATED: m_PadType->SetSelection( 3 ); break;
  517. }
  518. }
  519. switch( m_dummyPad->GetProperty() )
  520. {
  521. case PAD_PROP_NONE: m_choiceFabProperty->SetSelection( 0 ); break;
  522. case PAD_PROP_BGA: m_choiceFabProperty->SetSelection( 1 ); break;
  523. case PAD_PROP_FIDUCIAL_LOCAL: m_choiceFabProperty->SetSelection( 2 ); break;
  524. case PAD_PROP_FIDUCIAL_GLBL: m_choiceFabProperty->SetSelection( 3 ); break;
  525. case PAD_PROP_TESTPOINT: m_choiceFabProperty->SetSelection( 4 ); break;
  526. case PAD_PROP_HEATSINK: m_choiceFabProperty->SetSelection( 5 ); break;
  527. case PAD_PROP_CASTELLATED: m_choiceFabProperty->SetSelection( 6 ); break;
  528. }
  529. // Ensure the pad property is compatible with the pad type
  530. if( m_dummyPad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
  531. {
  532. m_choiceFabProperty->SetSelection( 0 );
  533. m_choiceFabProperty->Enable( false );
  534. }
  535. if( m_dummyPad->GetDrillShape() != PAD_DRILL_SHAPE_OBLONG )
  536. m_holeShapeCtrl->SetSelection( 0 );
  537. else
  538. m_holeShapeCtrl->SetSelection( 1 );
  539. // Update some dialog widgets state (Enable/disable options):
  540. wxCommandEvent cmd_event;
  541. setPadLayersList( m_dummyPad->GetLayerSet() );
  542. OnPadShapeSelection( cmd_event );
  543. OnOffsetCheckbox( cmd_event );
  544. // Update basic shapes list
  545. displayPrimitivesList();
  546. }
  547. // A small helper function, to display coordinates:
  548. static wxString formatCoord( EDA_UNITS aUnits, wxPoint aCoord )
  549. {
  550. return wxString::Format( "(X:%s Y:%s)",
  551. MessageTextFromValue( aUnits, aCoord.x, true ),
  552. MessageTextFromValue( aUnits, aCoord.y, true ) );
  553. }
  554. void DIALOG_PAD_PROPERTIES::displayPrimitivesList()
  555. {
  556. m_listCtrlPrimitives->ClearAll();
  557. wxListItem itemCol;
  558. itemCol.SetImage(-1);
  559. for( int ii = 0; ii < 5; ++ii )
  560. m_listCtrlPrimitives->InsertColumn(ii, itemCol);
  561. wxString bs_info[5];
  562. for( unsigned ii = 0; ii < m_primitives.size(); ++ii )
  563. {
  564. const std::shared_ptr<DRAWSEGMENT>& primitive = m_primitives[ii];
  565. for( wxString& s : bs_info )
  566. s.Empty();
  567. bs_info[4] = _( "width " ) + MessageTextFromValue( m_units, primitive->GetWidth(), true );
  568. switch( primitive->GetShape() )
  569. {
  570. case S_SEGMENT: // usual segment : line with rounded ends
  571. bs_info[0] = _( "Segment" );
  572. bs_info[1] = _( "from " ) + formatCoord( m_units, primitive->GetStart() );
  573. bs_info[2] = _( "to " ) + formatCoord( m_units, primitive->GetEnd() );
  574. break;
  575. case S_CURVE: // Bezier segment
  576. bs_info[0] = _( "Bezier" );
  577. bs_info[1] = _( "from " ) + formatCoord( m_units, primitive->GetStart() );
  578. bs_info[2] = _( "to " ) + formatCoord( m_units, primitive->GetEnd() );
  579. break;
  580. case S_ARC: // Arc with rounded ends
  581. bs_info[0] = _( "Arc" );
  582. bs_info[1] = _( "center " ) + formatCoord( m_units, primitive->GetCenter() );
  583. bs_info[2] = _( "start " ) + formatCoord( m_units, primitive->GetArcStart() );
  584. bs_info[3] = _( "angle " ) + FormatAngle( primitive->GetAngle() );
  585. break;
  586. case S_CIRCLE: // ring or circle
  587. if( primitive->GetWidth() )
  588. bs_info[0] = _( "ring" );
  589. else
  590. bs_info[0] = _( "circle" );
  591. bs_info[1] = formatCoord( m_units, primitive->GetStart() );
  592. bs_info[2] = _( "radius " ) + MessageTextFromValue( m_units, primitive->GetRadius(), true );
  593. break;
  594. case S_POLYGON: // polygon
  595. bs_info[0] = "Polygon";
  596. bs_info[1] = wxString::Format( _( "corners count %d" ),
  597. (int) primitive->GetPolyShape().Outline( 0 ).PointCount() );
  598. break;
  599. default:
  600. bs_info[0] = "Unknown primitive";
  601. break;
  602. }
  603. long tmp = m_listCtrlPrimitives->InsertItem( ii, bs_info[0] );
  604. m_listCtrlPrimitives->SetItemData( tmp, ii );
  605. for( int jj = 0, col = 0; jj < 5; ++jj )
  606. m_listCtrlPrimitives->SetItem( tmp, col++, bs_info[jj] );
  607. }
  608. // Now columns are filled, ensure correct width of columns
  609. for( unsigned ii = 0; ii < 5; ++ii )
  610. m_listCtrlPrimitives->SetColumnWidth( ii, wxLIST_AUTOSIZE );
  611. }
  612. void DIALOG_PAD_PROPERTIES::OnResize( wxSizeEvent& event )
  613. {
  614. redraw();
  615. event.Skip();
  616. }
  617. void DIALOG_PAD_PROPERTIES::onChangePadMode( wxCommandEvent& event )
  618. {
  619. m_sketchPreview = m_cbShowPadOutline->GetValue();
  620. KIGFX::VIEW* view = m_padPreviewGAL->GetView();
  621. // fix the pad render mode (filled/not filled)
  622. KIGFX::PCB_RENDER_SETTINGS* settings =
  623. static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
  624. settings->SetSketchMode( LAYER_PADS_TH, m_sketchPreview );
  625. settings->SetSketchMode( LAYER_PAD_FR, m_sketchPreview );
  626. settings->SetSketchMode( LAYER_PAD_BK, m_sketchPreview );
  627. settings->SetSketchModeGraphicItems( m_sketchPreview );
  628. redraw();
  629. }
  630. void DIALOG_PAD_PROPERTIES::OnPadShapeSelection( wxCommandEvent& event )
  631. {
  632. switch( m_PadShape->GetSelection() )
  633. {
  634. case CHOICE_SHAPE_CIRCLE:
  635. case CHOICE_SHAPE_OVAL:
  636. case CHOICE_SHAPE_RECT:
  637. m_shapePropsBook->SetSelection( 0 );
  638. break;
  639. case CHOICE_SHAPE_TRAPEZOID:
  640. m_shapePropsBook->SetSelection( 1 );
  641. break;
  642. case CHOICE_SHAPE_ROUNDRECT:
  643. {
  644. m_shapePropsBook->SetSelection( 2 );
  645. // A reasonable default (from IPC-7351C)
  646. if( m_dummyPad->GetRoundRectRadiusRatio() == 0.0 )
  647. m_tcCornerSizeRatio->SetValue( "25" );
  648. }
  649. break;
  650. case CHOICE_SHAPE_CHAMFERED_RECT:
  651. m_shapePropsBook->SetSelection( 3 );
  652. // A reasonable default is all corners chamfered.
  653. if( !m_cbTopLeft->GetValue() && !m_cbTopRight->GetValue()
  654. && !m_cbBottomLeft->GetValue() && !m_cbBottomRight->GetValue() )
  655. {
  656. m_cbTopLeft->SetValue( true );
  657. m_cbTopRight->SetValue( true );
  658. m_cbBottomLeft->SetValue( true );
  659. m_cbBottomRight->SetValue( true );
  660. }
  661. break;
  662. case CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT:
  663. {
  664. m_shapePropsBook->SetSelection( 4 );
  665. // Reasonable defaults (corner radius from IPC-7351C)
  666. if( m_dummyPad->GetRoundRectRadiusRatio() == 0.0 )
  667. m_tcCornerSizeRatio->SetValue( "25" );
  668. if( m_dummyPad->GetChamferRectRatio() == 0.0 )
  669. m_tcChamferRatio1->ChangeValue( "20" );
  670. }
  671. break;
  672. case CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR: // PAD_SHAPE_CUSTOM, circular anchor
  673. case CHOICE_SHAPE_CUSTOM_RECT_ANCHOR: // PAD_SHAPE_CUSTOM, rect anchor
  674. m_shapePropsBook->SetSelection( 0 );
  675. break;
  676. }
  677. // Readjust props book size
  678. wxSize size = m_shapePropsBook->GetSize();
  679. size.y = m_shapePropsBook->GetPage( m_shapePropsBook->GetSelection() )->GetBestSize().y;
  680. m_shapePropsBook->SetMaxSize( size );
  681. m_sizeY.Enable( m_PadShape->GetSelection() != CHOICE_SHAPE_CIRCLE
  682. && m_PadShape->GetSelection() != CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR );
  683. m_offsetShapeOpt->Enable( m_PadShape->GetSelection() != CHOICE_SHAPE_CIRCLE
  684. && m_PadShape->GetSelection() != CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR
  685. && m_PadShape->GetSelection() != CHOICE_SHAPE_CUSTOM_RECT_ANCHOR );
  686. if( !m_offsetShapeOpt->IsEnabled() )
  687. m_offsetShapeOpt->SetValue( false );
  688. // Show/hide controls depending on m_offsetShapeOpt being enabled
  689. m_offsetCtrls->Show( m_offsetShapeOpt->GetValue() );
  690. m_offsetShapeOptLabel->Show( m_offsetShapeOpt->GetValue() );
  691. bool is_custom = m_PadShape->GetSelection() == CHOICE_SHAPE_CUSTOM_CIRC_ANCHOR
  692. || m_PadShape->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR;
  693. enablePrimitivePage( is_custom );
  694. m_staticTextcps->Enable( is_custom );
  695. m_ZoneCustomPadShape->Enable( is_custom );
  696. transferDataToPad( m_dummyPad );
  697. updateRoundRectCornerValues();
  698. for( size_t i = 0; i < m_notebook->GetPageCount(); ++i )
  699. m_notebook->GetPage( i )->Layout();
  700. // Resize the dialog if its height is too small to show all widgets:
  701. if( m_MainSizer->GetSize().y < m_MainSizer->GetMinSize().y )
  702. m_MainSizer->SetSizeHints( this );
  703. redraw();
  704. }
  705. void DIALOG_PAD_PROPERTIES::OnDrillShapeSelected( wxCommandEvent& event )
  706. {
  707. transferDataToPad( m_dummyPad );
  708. redraw();
  709. }
  710. void DIALOG_PAD_PROPERTIES::PadOrientEvent( wxCommandEvent& event )
  711. {
  712. transferDataToPad( m_dummyPad );
  713. redraw();
  714. }
  715. void DIALOG_PAD_PROPERTIES::PadTypeSelected( wxCommandEvent& event )
  716. {
  717. int ii = m_PadType->GetSelection();
  718. if( (unsigned)ii >= arrayDim( code_type ) ) // catches < 0 also
  719. ii = 0;
  720. bool hasHole, hasConnection, hasProperty;
  721. switch( ii )
  722. {
  723. default:
  724. case 0: /* PTH */ hasHole = true; hasConnection = true; hasProperty = true; break;
  725. case 1: /* SMD */ hasHole = false; hasConnection = true; hasProperty = true; break;
  726. case 2: /* CONN */ hasHole = false; hasConnection = true; hasProperty = true; break;
  727. case 3: /* NPTH */ hasHole = true; hasConnection = false; hasProperty = false; break;
  728. case 4: /* Aperture */ hasHole = false; hasConnection = false; hasProperty = true; break;
  729. }
  730. LSET layer_mask = std_pad_layers[ii];
  731. setPadLayersList( layer_mask );
  732. if( !hasHole )
  733. {
  734. m_holeX.SetValue( 0 );
  735. m_holeY.SetValue( 0 );
  736. }
  737. else if ( m_holeX.GetValue() == 0 && m_currentPad )
  738. {
  739. m_holeX.SetValue( m_currentPad->GetDrillSize().x );
  740. m_holeY.SetValue( m_currentPad->GetDrillSize().y );
  741. }
  742. if( !hasConnection )
  743. {
  744. m_PadNumCtrl->SetValue( wxEmptyString );
  745. m_PadNetSelector->SetSelectedNetcode( 0 );
  746. m_padToDieOpt->SetValue( false );
  747. }
  748. else if( m_PadNumCtrl->GetValue().IsEmpty() && m_currentPad )
  749. {
  750. m_PadNumCtrl->SetValue( m_currentPad->GetName() );
  751. m_PadNetSelector->SetSelectedNetcode( m_currentPad->GetNetCode() );
  752. }
  753. if( !hasProperty )
  754. m_choiceFabProperty->SetSelection( 0 );
  755. m_choiceFabProperty->Enable( hasProperty );
  756. transferDataToPad( m_dummyPad );
  757. redraw();
  758. }
  759. void DIALOG_PAD_PROPERTIES::OnUpdateUI( wxUpdateUIEvent& event )
  760. {
  761. int ii = m_PadType->GetSelection();
  762. if( (unsigned)ii >= arrayDim( code_type ) ) // catches < 0 also
  763. ii = 0;
  764. bool hasHole, hasConnection;
  765. switch( ii )
  766. {
  767. default:
  768. case 0: /* PTH */ hasHole = true; hasConnection = true; break;
  769. case 1: /* SMD */ hasHole = false; hasConnection = true; break;
  770. case 2: /* CONN */ hasHole = false; hasConnection = true; break;
  771. case 3: /* NPTH */ hasHole = true; hasConnection = false; break;
  772. case 4: /* Aperture */ hasHole = false; hasConnection = false; break;
  773. }
  774. // Enable/disable hole controls
  775. m_holeShapeLabel->Enable( hasHole );
  776. m_holeShapeCtrl->Enable( hasHole );
  777. m_holeX.Enable( hasHole );
  778. m_holeY.Enable( hasHole && m_holeShapeCtrl->GetSelection() == 1 );
  779. // Enable/disable Pad number, net and pad length-to-die
  780. m_PadNumText->Enable( hasConnection );
  781. m_PadNumCtrl->Enable( hasConnection );
  782. m_PadNameText->Enable( hasConnection );
  783. m_PadNetSelector->Enable( hasConnection && m_canEditNetName && m_currentPad );
  784. m_padToDieOpt->Enable( hasConnection );
  785. if( !m_padToDieOpt->IsEnabled() )
  786. m_padToDieOpt->SetValue( false );
  787. // We can show/hide this here because it doesn't require the layout to be refreshed.
  788. // All the others have to be done in their event handlers because doing a layout here
  789. // causes infinite looping on MSW.
  790. m_padToDie.Show( m_padToDieOpt->GetValue() );
  791. // Enable/disable Copper Layers control
  792. m_rbCopperLayersSel->Enable( ii != 4 );
  793. }
  794. void DIALOG_PAD_PROPERTIES::setPadLayersList( LSET layer_mask )
  795. {
  796. LSET cu_set = layer_mask & LSET::AllCuMask();
  797. if( cu_set == LSET( F_Cu ) )
  798. m_rbCopperLayersSel->SetSelection( 0 );
  799. else if( cu_set == LSET( B_Cu ) )
  800. m_rbCopperLayersSel->SetSelection( 1 );
  801. else if( cu_set.any() )
  802. m_rbCopperLayersSel->SetSelection( 2 );
  803. else
  804. m_rbCopperLayersSel->SetSelection( 3 );
  805. m_PadLayerAdhCmp->SetValue( layer_mask[F_Adhes] );
  806. m_PadLayerAdhCu->SetValue( layer_mask[B_Adhes] );
  807. m_PadLayerPateCmp->SetValue( layer_mask[F_Paste] );
  808. m_PadLayerPateCu->SetValue( layer_mask[B_Paste] );
  809. m_PadLayerSilkCmp->SetValue( layer_mask[F_SilkS] );
  810. m_PadLayerSilkCu->SetValue( layer_mask[B_SilkS] );
  811. m_PadLayerMaskCmp->SetValue( layer_mask[F_Mask] );
  812. m_PadLayerMaskCu->SetValue( layer_mask[B_Mask] );
  813. m_PadLayerECO1->SetValue( layer_mask[Eco1_User] );
  814. m_PadLayerECO2->SetValue( layer_mask[Eco2_User] );
  815. m_PadLayerDraft->SetValue( layer_mask[Dwgs_User] );
  816. }
  817. // Called when select/deselect a layer.
  818. void DIALOG_PAD_PROPERTIES::OnSetLayers( wxCommandEvent& event )
  819. {
  820. transferDataToPad( m_dummyPad );
  821. redraw();
  822. }
  823. // test if all values are acceptable for the pad
  824. bool DIALOG_PAD_PROPERTIES::padValuesOK()
  825. {
  826. bool error = transferDataToPad( m_dummyPad );
  827. bool skip_tstoffset = false; // the offset prm is not always tested
  828. wxArrayString error_msgs;
  829. wxString msg;
  830. // Test for incorrect values
  831. if( (m_dummyPad->GetSize().x <= 0) ||
  832. ((m_dummyPad->GetSize().y <= 0) && (m_dummyPad->GetShape() != PAD_SHAPE_CIRCLE)) )
  833. {
  834. error_msgs.Add( _( "Pad size must be greater than zero" ) );
  835. }
  836. if( (m_dummyPad->GetSize().x < m_dummyPad->GetDrillSize().x) ||
  837. (m_dummyPad->GetSize().y < m_dummyPad->GetDrillSize().y) )
  838. {
  839. error_msgs.Add( _( "Incorrect value for pad drill: pad drill bigger than pad size" ) );
  840. skip_tstoffset = true; // offset prm will be not tested because if the drill value
  841. // is incorrect the offset prm is always seen as incorrect, even if it is 0
  842. }
  843. if( m_dummyPad->GetLocalClearance() < 0 )
  844. {
  845. error_msgs.Add( _( "Pad local clearance must be zero or greater than zero" ) );
  846. }
  847. // Some pads need a negative solder mask clearance (mainly for BGA with small pads)
  848. // However the negative solder mask clearance must not create negative mask size
  849. // Therefore test for minimal acceptable negative value
  850. // Hovewer, a negative value can give strange result with custom shapes, so it is not
  851. // allowed for custom pad shape
  852. if( m_dummyPad->GetLocalSolderMaskMargin() < 0 )
  853. {
  854. if( m_dummyPad->GetShape() == PAD_SHAPE_CUSTOM )
  855. error_msgs.Add( _( "Pad local solder mask clearance must be zero or greater than zero" ) );
  856. else
  857. {
  858. int min_smClearance = -std::min( m_dummyPad->GetSize().x, m_dummyPad->GetSize().y )/2;
  859. if( m_dummyPad->GetLocalSolderMaskMargin() <= min_smClearance )
  860. {
  861. error_msgs.Add( wxString::Format(
  862. _( "Pad local solder mask clearance must be greater than %s" ),
  863. StringFromValue( GetUserUnits(), min_smClearance, true, true ) ) );
  864. }
  865. }
  866. }
  867. // Some pads need a positive solder paste clearance (mainly for BGA with small pads)
  868. // Hovewer, a positive value can create issues if the resulting shape is too big.
  869. // (like a solder paste creating a solder paste area on a neighbour pad or on the solder mask)
  870. // So we could ask for user to confirm the choice
  871. // Currently there are no test
  872. LSET padlayers_mask = m_dummyPad->GetLayerSet();
  873. if( padlayers_mask == 0 )
  874. error_msgs.Add( _( "Error: pad has no layer" ) );
  875. if( !padlayers_mask[F_Cu] && !padlayers_mask[B_Cu] )
  876. {
  877. if( m_dummyPad->GetDrillSize().x || m_dummyPad->GetDrillSize().y )
  878. {
  879. // Note: he message is shown in an HTML window
  880. msg = _( "Error: the pad is not on a copper layer and has a hole" );
  881. if( m_dummyPad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
  882. {
  883. msg += wxT( "<br><br><i>" );
  884. msg += _( "For NPTH pad, set pad size value to pad drill value,"
  885. " if you do not want this pad plotted in gerber files" );
  886. }
  887. error_msgs.Add( msg );
  888. }
  889. }
  890. if( !skip_tstoffset )
  891. {
  892. wxPoint max_size;
  893. max_size.x = std::abs( m_dummyPad->GetOffset().x );
  894. max_size.y = std::abs( m_dummyPad->GetOffset().y );
  895. max_size.x += m_dummyPad->GetDrillSize().x / 2;
  896. max_size.y += m_dummyPad->GetDrillSize().y / 2;
  897. if( ( m_dummyPad->GetSize().x / 2 < max_size.x ) ||
  898. ( m_dummyPad->GetSize().y / 2 < max_size.y ) )
  899. {
  900. error_msgs.Add( _( "Incorrect value for pad offset" ) );
  901. }
  902. }
  903. if( error )
  904. error_msgs.Add( _( "Too large value for pad delta size" ) );
  905. switch( m_dummyPad->GetAttribute() )
  906. {
  907. case PAD_ATTRIB_HOLE_NOT_PLATED: // Not plated, but through hole, a hole is expected
  908. case PAD_ATTRIB_STANDARD : // Pad through hole, a hole is also expected
  909. if( m_dummyPad->GetDrillSize().x <= 0 ||
  910. ( m_dummyPad->GetDrillSize().y <= 0 && m_dummyPad->GetDrillShape() == PAD_DRILL_SHAPE_OBLONG ) )
  911. error_msgs.Add( _( "Error: Through hole pad: drill diameter set to 0" ) );
  912. break;
  913. case PAD_ATTRIB_CONN: // Connector pads are smd pads, just they do not have solder paste.
  914. if( padlayers_mask[B_Paste] || padlayers_mask[F_Paste] )
  915. error_msgs.Add( _( "Error: Connector pads are not on the solder paste layer\n"
  916. "Use SMD pads instead" ) );
  917. KI_FALLTHROUGH;
  918. case PAD_ATTRIB_SMD: // SMD and Connector pads (One external copper layer only)
  919. {
  920. LSET innerlayers_mask = padlayers_mask & LSET::InternalCuMask();
  921. if( ( padlayers_mask[F_Cu] && padlayers_mask[B_Cu] ) ||
  922. innerlayers_mask.count() != 0 )
  923. error_msgs.Add( _( "Error: only one external copper layer allowed for SMD or Connector pads" ) );
  924. }
  925. break;
  926. }
  927. if( m_dummyPad->GetProperty() != PAD_PROP_NONE &&
  928. m_dummyPad->GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED )
  929. error_msgs.Add( _( "Property cannot be set for NPTH" ) );
  930. if( m_dummyPad->GetProperty() == PAD_PROP_CASTELLATED &&
  931. m_dummyPad->GetAttribute() != PAD_ATTRIB_STANDARD )
  932. error_msgs.Add( _( "Castellated property can be set only for PTH" ) );
  933. if( m_dummyPad->GetProperty() == PAD_PROP_BGA &&
  934. m_dummyPad->GetAttribute() != PAD_ATTRIB_SMD )
  935. error_msgs.Add( _( "BGA property can be set only for SMD pads" ) );
  936. if( m_dummyPad->GetShape() == PAD_SHAPE_ROUNDRECT ||
  937. m_dummyPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
  938. {
  939. wxASSERT( m_tcCornerSizeRatio->GetValue() == m_tcCornerSizeRatio1->GetValue() );
  940. wxString value = m_tcCornerSizeRatio->GetValue();
  941. double rrRadiusRatioPercent;
  942. if( !value.ToDouble( &rrRadiusRatioPercent ) )
  943. error_msgs.Add( _( "Incorrect corner size value" ) );
  944. else
  945. {
  946. if( rrRadiusRatioPercent < 0.0 )
  947. error_msgs.Add( _( "Incorrect (negative) corner size value" ) );
  948. else if( rrRadiusRatioPercent > 50.0 )
  949. error_msgs.Add( _( "Corner size value must be smaller than 50%" ) );
  950. }
  951. }
  952. if( m_dummyPad->GetShape() == PAD_SHAPE_CUSTOM )
  953. {
  954. SHAPE_POLY_SET mergedPolygon;
  955. m_dummyPad->MergePrimitivesAsPolygon( &mergedPolygon );
  956. if( mergedPolygon.OutlineCount() > 1 )
  957. error_msgs.Add( _( "Incorrect pad shape: the shape must be equivalent to only one polygon" ) );
  958. }
  959. if( error_msgs.GetCount() )
  960. {
  961. HTML_MESSAGE_BOX dlg( this, _("Pad setup errors list" ) );
  962. dlg.ListSet( error_msgs );
  963. dlg.ShowModal();
  964. }
  965. return error_msgs.GetCount() == 0;
  966. }
  967. void DIALOG_PAD_PROPERTIES::redraw()
  968. {
  969. if( !m_canUpdate )
  970. return;
  971. KIGFX::VIEW* view = m_padPreviewGAL->GetView();
  972. m_padPreviewGAL->StopDrawing();
  973. // The layer used to place primitive items selected when editing custom pad shapes
  974. // we use here a layer never used in a pad:
  975. #define SELECTED_ITEMS_LAYER Dwgs_User
  976. view->SetTopLayer( SELECTED_ITEMS_LAYER );
  977. KIGFX::PCB_RENDER_SETTINGS* settings =
  978. static_cast<KIGFX::PCB_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
  979. settings->SetLayerColor( SELECTED_ITEMS_LAYER, m_selectedColor );
  980. view->Update( m_dummyPad );
  981. // delete previous items if highlight list
  982. while( m_highlight.size() )
  983. {
  984. delete m_highlight.back(); // the dtor also removes item from view
  985. m_highlight.pop_back();
  986. }
  987. // highlight selected primitives:
  988. long select = m_listCtrlPrimitives->GetFirstSelected();
  989. while( select >= 0 )
  990. {
  991. DRAWSEGMENT* dummySegment = (DRAWSEGMENT*) m_primitives[select]->Clone();
  992. dummySegment->SetLayer( SELECTED_ITEMS_LAYER );
  993. dummySegment->Rotate( wxPoint( 0, 0), m_dummyPad->GetOrientation() );
  994. dummySegment->Move( m_dummyPad->GetPosition() );
  995. view->Add( dummySegment );
  996. m_highlight.push_back( dummySegment );
  997. select = m_listCtrlPrimitives->GetNextSelected( select );
  998. }
  999. BOX2I bbox = m_dummyPad->ViewBBox();
  1000. if( bbox.GetSize().x > 0 && bbox.GetSize().y > 0 )
  1001. {
  1002. // The origin always goes in the middle of the canvas; we want offsetting the pad
  1003. // shape to move the pad, not the hole
  1004. bbox.Move( -m_dummyPad->GetPosition() );
  1005. int maxXExtent = std::max( abs( bbox.GetLeft() ), abs( bbox.GetRight() ) );
  1006. int maxYExtent = std::max( abs( bbox.GetTop() ), abs( bbox.GetBottom() ) );
  1007. // Don't blow up the GAL on too-large numbers
  1008. if( maxXExtent > INT_MAX / 4 )
  1009. maxXExtent = INT_MAX / 4;
  1010. if( maxYExtent > INT_MAX / 4 )
  1011. maxYExtent = INT_MAX / 4;
  1012. BOX2D viewBox( m_dummyPad->GetPosition(), {0, 0} );
  1013. BOX2D canvasBox( m_dummyPad->GetPosition(), {0, 0} );
  1014. viewBox.Inflate( maxXExtent * 1.4, maxYExtent * 1.4 ); // add a margin
  1015. canvasBox.Inflate( maxXExtent * 2.0, maxYExtent * 2.0 );
  1016. view->SetBoundary( canvasBox );
  1017. // Autozoom
  1018. view->SetViewport( viewBox );
  1019. m_padPreviewGAL->StartDrawing();
  1020. m_padPreviewGAL->Refresh();
  1021. }
  1022. }
  1023. bool DIALOG_PAD_PROPERTIES::TransferDataToWindow()
  1024. {
  1025. if( !wxDialog::TransferDataToWindow() )
  1026. return false;
  1027. if( !m_panelGeneral->TransferDataToWindow() )
  1028. return false;
  1029. if( !m_localSettingsPanel->TransferDataToWindow() )
  1030. return false;
  1031. return true;
  1032. }
  1033. bool DIALOG_PAD_PROPERTIES::TransferDataFromWindow()
  1034. {
  1035. BOARD_COMMIT commit( m_parent );
  1036. if( !wxDialog::TransferDataFromWindow() )
  1037. return false;
  1038. if( !m_panelGeneral->TransferDataFromWindow() )
  1039. return false;
  1040. if( !m_localSettingsPanel->TransferDataFromWindow() )
  1041. return false;
  1042. if( !padValuesOK() )
  1043. return false;
  1044. int isign = m_isFlipped ? -1 : 1;
  1045. transferDataToPad( m_padMaster );
  1046. // m_padMaster is a pattern: ensure there is no net for this pad:
  1047. m_padMaster->SetNetCode( NETINFO_LIST::UNCONNECTED );
  1048. if( !m_currentPad ) // Set current Pad parameters
  1049. return true;
  1050. commit.Modify( m_currentPad );
  1051. // redraw the area where the pad was, without pad (delete pad on screen)
  1052. m_currentPad->SetFlags( DO_NOT_DRAW );
  1053. m_parent->GetCanvas()->Refresh();
  1054. m_currentPad->ClearFlags( DO_NOT_DRAW );
  1055. // Update values
  1056. m_currentPad->SetShape( m_padMaster->GetShape() );
  1057. m_currentPad->SetAttribute( m_padMaster->GetAttribute() );
  1058. m_currentPad->SetPosition( m_padMaster->GetPosition() );
  1059. wxSize size;
  1060. MODULE* footprint = m_currentPad->GetParent();
  1061. if( footprint )
  1062. {
  1063. footprint->SetLastEditTime();
  1064. // compute the pos 0 value, i.e. pad position for footprint with orientation = 0
  1065. // i.e. relative to footprint origin (footprint position)
  1066. wxPoint pt = m_currentPad->GetPosition() - footprint->GetPosition();
  1067. RotatePoint( &pt, -footprint->GetOrientation() );
  1068. m_currentPad->SetPos0( pt );
  1069. m_currentPad->SetOrientation( m_padMaster->GetOrientation() * isign
  1070. + footprint->GetOrientation() );
  1071. }
  1072. m_currentPad->SetSize( m_padMaster->GetSize() );
  1073. size = m_padMaster->GetDelta();
  1074. size.y *= isign;
  1075. m_currentPad->SetDelta( size );
  1076. m_currentPad->SetDrillSize( m_padMaster->GetDrillSize() );
  1077. m_currentPad->SetDrillShape( m_padMaster->GetDrillShape() );
  1078. wxPoint offset = m_padMaster->GetOffset();
  1079. offset.y *= isign;
  1080. m_currentPad->SetOffset( offset );
  1081. m_currentPad->SetPadToDieLength( m_padMaster->GetPadToDieLength() );
  1082. if( m_padMaster->GetShape() != PAD_SHAPE_CUSTOM )
  1083. m_padMaster->DeletePrimitivesList();
  1084. m_currentPad->SetAnchorPadShape( m_padMaster->GetAnchorPadShape() );
  1085. m_currentPad->SetPrimitives( m_padMaster->GetPrimitives() );
  1086. if( m_isFlipped )
  1087. {
  1088. m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) );
  1089. m_currentPad->FlipPrimitives();
  1090. }
  1091. m_currentPad->SetLayerSet( m_padMaster->GetLayerSet() );
  1092. if( m_isFlipped )
  1093. {
  1094. m_currentPad->SetLayerSet( FlipLayerMask( m_currentPad->GetLayerSet() ) );
  1095. }
  1096. m_currentPad->SetName( m_padMaster->GetName() );
  1097. int padNetcode = NETINFO_LIST::UNCONNECTED;
  1098. // For PAD_ATTRIB_HOLE_NOT_PLATED, ensure there is no net name selected
  1099. if( m_padMaster->GetAttribute() != PAD_ATTRIB_HOLE_NOT_PLATED )
  1100. padNetcode = m_PadNetSelector->GetSelectedNetcode();
  1101. m_currentPad->SetNetCode( padNetcode );
  1102. m_currentPad->SetLocalClearance( m_padMaster->GetLocalClearance() );
  1103. m_currentPad->SetLocalSolderMaskMargin( m_padMaster->GetLocalSolderMaskMargin() );
  1104. m_currentPad->SetLocalSolderPasteMargin( m_padMaster->GetLocalSolderPasteMargin() );
  1105. m_currentPad->SetLocalSolderPasteMarginRatio( m_padMaster->GetLocalSolderPasteMarginRatio() );
  1106. m_currentPad->SetThermalWidth( m_padMaster->GetThermalWidth() );
  1107. m_currentPad->SetThermalGap( m_padMaster->GetThermalGap() );
  1108. m_currentPad->SetRoundRectRadiusRatio( m_padMaster->GetRoundRectRadiusRatio() );
  1109. m_currentPad->SetChamferRectRatio( m_padMaster->GetChamferRectRatio() );
  1110. m_currentPad->SetChamferPositions( m_padMaster->GetChamferPositions() );
  1111. m_currentPad->SetZoneConnection( m_padMaster->GetEffectiveZoneConnection() );
  1112. // rounded rect pads with radius ratio = 0 are in fact rect pads.
  1113. // So set the right shape (and perhaps issues with a radius = 0)
  1114. if( m_currentPad->GetShape() == PAD_SHAPE_ROUNDRECT &&
  1115. m_currentPad->GetRoundRectRadiusRatio() == 0.0 )
  1116. {
  1117. m_currentPad->SetShape( PAD_SHAPE_RECT );
  1118. }
  1119. // Set the fabrication property:
  1120. m_currentPad->SetProperty( getSelectedProperty() );
  1121. // define the way the clearance area is defined in zones
  1122. m_currentPad->SetCustomShapeInZoneOpt( m_padMaster->GetCustomShapeInZoneOpt() );
  1123. if( footprint )
  1124. footprint->CalculateBoundingBox();
  1125. m_parent->SetMsgPanel( m_currentPad );
  1126. // redraw the area where the pad was
  1127. m_parent->GetCanvas()->Refresh();
  1128. commit.Push( _( "Modify pad" ) );
  1129. return true;
  1130. }
  1131. PAD_PROP_T DIALOG_PAD_PROPERTIES::getSelectedProperty()
  1132. {
  1133. PAD_PROP_T prop = PAD_PROP_NONE;
  1134. switch( m_choiceFabProperty->GetSelection() )
  1135. {
  1136. case 0: prop = PAD_PROP_NONE; break;
  1137. case 1: prop = PAD_PROP_BGA; break;
  1138. case 2: prop = PAD_PROP_FIDUCIAL_LOCAL; break;
  1139. case 3: prop = PAD_PROP_FIDUCIAL_GLBL; break;
  1140. case 4: prop = PAD_PROP_TESTPOINT; break;
  1141. case 5: prop = PAD_PROP_HEATSINK; break;
  1142. case 6: prop = PAD_PROP_CASTELLATED; break;
  1143. }
  1144. return prop;
  1145. }
  1146. bool DIALOG_PAD_PROPERTIES::transferDataToPad( D_PAD* aPad )
  1147. {
  1148. wxString msg;
  1149. if( !Validate() )
  1150. return true;
  1151. if( !m_panelGeneral->Validate() )
  1152. return true;
  1153. if( !m_localSettingsPanel->Validate() )
  1154. return true;
  1155. if( !m_spokeWidth.Validate( 0, INT_MAX ) )
  1156. return false;
  1157. m_OrientValidator.TransferFromWindow();
  1158. aPad->SetAttribute( code_type[m_PadType->GetSelection()] );
  1159. aPad->SetShape( code_shape[m_PadShape->GetSelection()] );
  1160. aPad->SetAnchorPadShape( m_PadShape->GetSelection() == CHOICE_SHAPE_CUSTOM_RECT_ANCHOR ?
  1161. PAD_SHAPE_RECT : PAD_SHAPE_CIRCLE );
  1162. if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  1163. aPad->SetPrimitives( m_primitives );
  1164. // Read pad clearances values:
  1165. aPad->SetLocalClearance( m_clearance.GetValue() );
  1166. aPad->SetLocalSolderMaskMargin( m_maskClearance.GetValue() );
  1167. aPad->SetLocalSolderPasteMargin( m_pasteClearance.GetValue() );
  1168. aPad->SetThermalWidth( m_spokeWidth.GetValue() );
  1169. aPad->SetThermalGap( m_thermalGap.GetValue() );
  1170. double dtmp = 0.0;
  1171. msg = m_SolderPasteMarginRatioCtrl->GetValue();
  1172. msg.ToDouble( &dtmp );
  1173. // A -50% margin ratio means no paste on a pad, the ratio must be >= -50%
  1174. if( dtmp < -50.0 )
  1175. dtmp = -50.0;
  1176. // A margin ratio is always <= 0
  1177. // 0 means use full pad copper area
  1178. if( dtmp > 0.0 )
  1179. dtmp = 0.0;
  1180. aPad->SetLocalSolderPasteMarginRatio( dtmp / 100 );
  1181. switch( m_ZoneConnectionChoice->GetSelection() )
  1182. {
  1183. default:
  1184. case 0: aPad->SetZoneConnection( ZONE_CONNECTION::INHERITED ); break;
  1185. case 1: aPad->SetZoneConnection( ZONE_CONNECTION::FULL ); break;
  1186. case 2: aPad->SetZoneConnection( ZONE_CONNECTION::THERMAL ); break;
  1187. case 3: aPad->SetZoneConnection( ZONE_CONNECTION::NONE ); break;
  1188. }
  1189. aPad->SetPosition( wxPoint( m_posX.GetValue(), m_posY.GetValue() ) );
  1190. aPad->SetPos0( wxPoint( m_posX.GetValue(), m_posY.GetValue() ) );
  1191. if( m_holeShapeCtrl->GetSelection() == 0 )
  1192. {
  1193. aPad->SetDrillShape( PAD_DRILL_SHAPE_CIRCLE );
  1194. aPad->SetDrillSize( wxSize( m_holeX.GetValue(), m_holeX.GetValue() ) );
  1195. }
  1196. else
  1197. {
  1198. aPad->SetDrillShape( PAD_DRILL_SHAPE_OBLONG );
  1199. aPad->SetDrillSize( wxSize( m_holeX.GetValue(), m_holeY.GetValue() ) );
  1200. }
  1201. if( aPad->GetShape() == PAD_SHAPE_CIRCLE )
  1202. aPad->SetSize( wxSize( m_sizeX.GetValue(), m_sizeX.GetValue() ) );
  1203. else
  1204. aPad->SetSize( wxSize( m_sizeX.GetValue(), m_sizeY.GetValue() ) );
  1205. // For a trapezoid, test delta value (be sure delta is not too large for pad size)
  1206. // remember DeltaSize.x is the Y size variation
  1207. bool error = false;
  1208. wxSize delta( 0, 0 );
  1209. if( aPad->GetShape() == PAD_SHAPE_TRAPEZOID )
  1210. {
  1211. // For a trapezoid, only one of delta.x or delta.y is not 0, depending on axis.
  1212. if( m_trapAxisCtrl->GetSelection() == 0 )
  1213. delta.x = m_trapDelta.GetValue();
  1214. else
  1215. delta.y = m_trapDelta.GetValue();
  1216. if( delta.x < 0 && delta.x <= -aPad->GetSize().y )
  1217. {
  1218. delta.x = -aPad->GetSize().y + 2;
  1219. error = true;
  1220. }
  1221. if( delta.x > 0 && delta.x >= aPad->GetSize().y )
  1222. {
  1223. delta.x = aPad->GetSize().y - 2;
  1224. error = true;
  1225. }
  1226. if( delta.y < 0 && delta.y <= -aPad->GetSize().x )
  1227. {
  1228. delta.y = -aPad->GetSize().x + 2;
  1229. error = true;
  1230. }
  1231. if( delta.y > 0 && delta.y >= aPad->GetSize().x )
  1232. {
  1233. delta.y = aPad->GetSize().x - 2;
  1234. error = true;
  1235. }
  1236. }
  1237. aPad->SetDelta( delta );
  1238. if( m_offsetShapeOpt->GetValue() )
  1239. aPad->SetOffset( wxPoint( m_offsetX.GetValue(), m_offsetY.GetValue() ) );
  1240. else
  1241. aPad->SetOffset( wxPoint() );
  1242. // Read pad length die
  1243. if( m_padToDieOpt->GetValue() )
  1244. aPad->SetPadToDieLength( m_padToDie.GetValue() );
  1245. else
  1246. aPad->SetPadToDieLength( 0 );
  1247. aPad->SetOrientation( m_OrientValue * 10.0 );
  1248. aPad->SetName( m_PadNumCtrl->GetValue() );
  1249. aPad->SetNetCode( m_PadNetSelector->GetSelectedNetcode() );
  1250. int chamfers = 0;
  1251. if( m_PadShape->GetSelection() == CHOICE_SHAPE_CHAMFERED_RECT )
  1252. {
  1253. if( m_cbTopLeft->GetValue() )
  1254. chamfers |= RECT_CHAMFER_TOP_LEFT;
  1255. if( m_cbTopRight->GetValue() )
  1256. chamfers |= RECT_CHAMFER_TOP_RIGHT;
  1257. if( m_cbBottomLeft->GetValue() )
  1258. chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
  1259. if( m_cbBottomRight->GetValue() )
  1260. chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
  1261. }
  1262. else if( m_PadShape->GetSelection() == CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT )
  1263. {
  1264. if( m_cbTopLeft1->GetValue() )
  1265. chamfers |= RECT_CHAMFER_TOP_LEFT;
  1266. if( m_cbTopRight1->GetValue() )
  1267. chamfers |= RECT_CHAMFER_TOP_RIGHT;
  1268. if( m_cbBottomLeft1->GetValue() )
  1269. chamfers |= RECT_CHAMFER_BOTTOM_LEFT;
  1270. if( m_cbBottomRight1->GetValue() )
  1271. chamfers |= RECT_CHAMFER_BOTTOM_RIGHT;
  1272. }
  1273. aPad->SetChamferPositions( chamfers );
  1274. if( aPad->GetShape() == PAD_SHAPE_CUSTOM )
  1275. {
  1276. // The pad custom has a "anchor pad" (a basic shape: round or rect pad)
  1277. // that is the minimal area of this pad, and is usefull to ensure a hole
  1278. // diameter is acceptable, and is used in Gerber files as flashed area
  1279. // reference
  1280. if( aPad->GetAnchorPadShape() == PAD_SHAPE_CIRCLE )
  1281. aPad->SetSize( wxSize( m_sizeX.GetValue(), m_sizeX.GetValue() ) );
  1282. // define the way the clearance area is defined in zones
  1283. aPad->SetCustomShapeInZoneOpt( m_ZoneCustomPadShape->GetSelection() == 0 ?
  1284. CUST_PAD_SHAPE_IN_ZONE_OUTLINE :
  1285. CUST_PAD_SHAPE_IN_ZONE_CONVEXHULL );
  1286. }
  1287. switch( aPad->GetAttribute() )
  1288. {
  1289. case PAD_ATTRIB_STANDARD:
  1290. break;
  1291. case PAD_ATTRIB_CONN:
  1292. case PAD_ATTRIB_SMD:
  1293. // SMD and PAD_ATTRIB_CONN has no hole.
  1294. // basically, SMD and PAD_ATTRIB_CONN are same type of pads
  1295. // PAD_ATTRIB_CONN has just a default non technical layers that differs from SMD
  1296. // and are intended to be used in virtual edge board connectors
  1297. // However we can accept a non null offset,
  1298. // mainly to allow complex pads build from a set of basic pad shapes
  1299. aPad->SetDrillSize( wxSize( 0, 0 ) );
  1300. break;
  1301. case PAD_ATTRIB_HOLE_NOT_PLATED:
  1302. // Mechanical purpose only:
  1303. // no net name, no pad name allowed
  1304. aPad->SetName( wxEmptyString );
  1305. aPad->SetNetCode( NETINFO_LIST::UNCONNECTED );
  1306. break;
  1307. default:
  1308. DisplayError( NULL, wxT( "Error: unknown pad type" ) );
  1309. break;
  1310. }
  1311. if( aPad->GetShape() == PAD_SHAPE_ROUNDRECT )
  1312. {
  1313. double ratioPercent;
  1314. m_tcCornerSizeRatio->GetValue().ToDouble( &ratioPercent );
  1315. aPad->SetRoundRectRadiusRatio( ratioPercent / 100.0 );
  1316. }
  1317. else if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT
  1318. && m_PadShape->GetSelection() == CHOICE_SHAPE_CHAMFERED_ROUNDED_RECT )
  1319. {
  1320. double ratioPercent;
  1321. m_tcCornerSizeRatio1->GetValue().ToDouble( &ratioPercent );
  1322. aPad->SetRoundRectRadiusRatio( ratioPercent / 100.0 );
  1323. }
  1324. if( aPad->GetShape() == PAD_SHAPE_CHAMFERED_RECT )
  1325. {
  1326. double ratioPercent;
  1327. m_tcChamferRatio->GetValue().ToDouble( &ratioPercent );
  1328. aPad->SetChamferRectRatio( ratioPercent / 100.0 );
  1329. }
  1330. aPad->SetProperty( getSelectedProperty() );
  1331. LSET padLayerMask;
  1332. switch( m_rbCopperLayersSel->GetSelection() )
  1333. {
  1334. case 0: padLayerMask.set( F_Cu ); break;
  1335. case 1: padLayerMask.set( B_Cu ); break;
  1336. case 2: padLayerMask |= LSET::AllCuMask(); break;
  1337. case 3: break; // No copper layers
  1338. }
  1339. if( m_PadLayerAdhCmp->GetValue() )
  1340. padLayerMask.set( F_Adhes );
  1341. if( m_PadLayerAdhCu->GetValue() )
  1342. padLayerMask.set( B_Adhes );
  1343. if( m_PadLayerPateCmp->GetValue() )
  1344. padLayerMask.set( F_Paste );
  1345. if( m_PadLayerPateCu->GetValue() )
  1346. padLayerMask.set( B_Paste );
  1347. if( m_PadLayerSilkCmp->GetValue() )
  1348. padLayerMask.set( F_SilkS );
  1349. if( m_PadLayerSilkCu->GetValue() )
  1350. padLayerMask.set( B_SilkS );
  1351. if( m_PadLayerMaskCmp->GetValue() )
  1352. padLayerMask.set( F_Mask );
  1353. if( m_PadLayerMaskCu->GetValue() )
  1354. padLayerMask.set( B_Mask );
  1355. if( m_PadLayerECO1->GetValue() )
  1356. padLayerMask.set( Eco1_User );
  1357. if( m_PadLayerECO2->GetValue() )
  1358. padLayerMask.set( Eco2_User );
  1359. if( m_PadLayerDraft->GetValue() )
  1360. padLayerMask.set( Dwgs_User );
  1361. aPad->SetLayerSet( padLayerMask );
  1362. return error;
  1363. }
  1364. void DIALOG_PAD_PROPERTIES::OnOffsetCheckbox( wxCommandEvent& event )
  1365. {
  1366. if( m_offsetShapeOpt->GetValue() && m_currentPad )
  1367. {
  1368. m_offsetX.SetValue( m_currentPad->GetOffset().x );
  1369. m_offsetY.SetValue( m_currentPad->GetOffset().y );
  1370. }
  1371. // Show/hide controls depending on m_offsetShapeOpt being enabled
  1372. m_offsetCtrls->Show( m_offsetShapeOpt->GetValue() );
  1373. m_offsetShapeOptLabel->Show( m_offsetShapeOpt->GetValue() );
  1374. for( size_t i = 0; i < m_notebook->GetPageCount(); ++i )
  1375. m_notebook->GetPage( i )->Layout();
  1376. OnValuesChanged( event );
  1377. }
  1378. void DIALOG_PAD_PROPERTIES::OnPadToDieCheckbox( wxCommandEvent& event )
  1379. {
  1380. if( m_padToDieOpt->GetValue() && m_currentPad )
  1381. m_padToDie.SetValue( m_currentPad->GetPadToDieLength() );
  1382. OnValuesChanged( event );
  1383. }
  1384. void DIALOG_PAD_PROPERTIES::OnValuesChanged( wxCommandEvent& event )
  1385. {
  1386. if( m_canUpdate )
  1387. {
  1388. transferDataToPad( m_dummyPad );
  1389. // If the pad size has changed, update the displayed values
  1390. // for rounded rect pads
  1391. updateRoundRectCornerValues();
  1392. redraw();
  1393. }
  1394. }
  1395. void DIALOG_PAD_PROPERTIES::editPrimitive()
  1396. {
  1397. long select = m_listCtrlPrimitives->GetFirstSelected();
  1398. if( select < 0 )
  1399. {
  1400. wxMessageBox( _( "No shape selected" ) );
  1401. return;
  1402. }
  1403. std::shared_ptr<DRAWSEGMENT>& shape = m_primitives[select];
  1404. if( shape->GetShape() == S_POLYGON )
  1405. {
  1406. DIALOG_PAD_PRIMITIVE_POLY_PROPS dlg( this, m_parent, shape.get() );
  1407. if( dlg.ShowModal() != wxID_OK )
  1408. return;
  1409. dlg.TransferDataFromWindow();
  1410. }
  1411. else
  1412. {
  1413. DIALOG_PAD_PRIMITIVES_PROPERTIES dlg( this, m_parent, shape.get() );
  1414. if( dlg.ShowModal() != wxID_OK )
  1415. return;
  1416. dlg.TransferDataFromWindow();
  1417. }
  1418. displayPrimitivesList();
  1419. if( m_canUpdate )
  1420. {
  1421. transferDataToPad( m_dummyPad );
  1422. redraw();
  1423. }
  1424. }
  1425. void DIALOG_PAD_PROPERTIES::OnPrimitiveSelection( wxListEvent& event )
  1426. {
  1427. // Called on a double click on the basic shapes list
  1428. // To Do: highligth the primitive(s) currently selected.
  1429. redraw();
  1430. }
  1431. /// Called on a double click on the basic shapes list
  1432. void DIALOG_PAD_PROPERTIES::onPrimitiveDClick( wxMouseEvent& event )
  1433. {
  1434. editPrimitive();
  1435. }
  1436. // Called on a click on basic shapes list panel button
  1437. void DIALOG_PAD_PROPERTIES::onEditPrimitive( wxCommandEvent& event )
  1438. {
  1439. editPrimitive();
  1440. }
  1441. // Called on a click on basic shapes list panel button
  1442. void DIALOG_PAD_PROPERTIES::onDeletePrimitive( wxCommandEvent& event )
  1443. {
  1444. long select = m_listCtrlPrimitives->GetFirstSelected();
  1445. if( select < 0 )
  1446. return;
  1447. // Multiple selections are allowed. get them and remove corresponding shapes
  1448. std::vector<long> indexes;
  1449. indexes.push_back( select );
  1450. while( ( select = m_listCtrlPrimitives->GetNextSelected( select ) ) >= 0 )
  1451. indexes.push_back( select );
  1452. // Erase all select shapes
  1453. for( unsigned ii = indexes.size(); ii > 0; --ii )
  1454. m_primitives.erase( m_primitives.begin() + indexes[ii-1] );
  1455. displayPrimitivesList();
  1456. if( m_canUpdate )
  1457. {
  1458. transferDataToPad( m_dummyPad );
  1459. redraw();
  1460. }
  1461. }
  1462. void DIALOG_PAD_PROPERTIES::onAddPrimitive( wxCommandEvent& event )
  1463. {
  1464. // Ask user for shape type
  1465. wxString shapelist[] = { _( "Segment" ), _( "Arc" ), _( "Bezier" ),
  1466. _( "Ring/Circle" ), _( "Polygon" ) };
  1467. int type = wxGetSingleChoiceIndex( _( "Shape type:" ), _( "Add Primitive" ),
  1468. arrayDim( shapelist ), shapelist, 0, this );
  1469. // User pressed cancel
  1470. if( type == -1 )
  1471. return;
  1472. STROKE_T listtype[] = { S_SEGMENT, S_ARC, S_CURVE, S_CIRCLE, S_POLYGON };
  1473. DRAWSEGMENT* primitive = new DRAWSEGMENT();
  1474. primitive->SetShape( listtype[type] );
  1475. primitive->SetWidth( m_board->GetDesignSettings().GetLineThickness( F_Cu ) );
  1476. if( listtype[type] == S_POLYGON )
  1477. {
  1478. DIALOG_PAD_PRIMITIVE_POLY_PROPS dlg( this, m_parent, primitive );
  1479. if( dlg.ShowModal() != wxID_OK )
  1480. return;
  1481. }
  1482. else
  1483. {
  1484. DIALOG_PAD_PRIMITIVES_PROPERTIES dlg( this, m_parent, primitive );
  1485. if( dlg.ShowModal() != wxID_OK )
  1486. return;
  1487. }
  1488. m_primitives.emplace_back( primitive );
  1489. displayPrimitivesList();
  1490. if( m_canUpdate )
  1491. {
  1492. transferDataToPad( m_dummyPad );
  1493. redraw();
  1494. }
  1495. }
  1496. void DIALOG_PAD_PROPERTIES::onGeometryTransform( wxCommandEvent& event )
  1497. {
  1498. long select = m_listCtrlPrimitives->GetFirstSelected();
  1499. if( select < 0 )
  1500. {
  1501. wxMessageBox( _( "No shape selected" ) );
  1502. return;
  1503. }
  1504. // Multiple selections are allowed. Build selected shapes list
  1505. std::vector<std::shared_ptr<DRAWSEGMENT>> shapeList;
  1506. shapeList.emplace_back( m_primitives[select] );
  1507. while( ( select = m_listCtrlPrimitives->GetNextSelected( select ) ) >= 0 )
  1508. shapeList.emplace_back( m_primitives[select] );
  1509. DIALOG_PAD_PRIMITIVES_TRANSFORM dlg( this, m_parent, shapeList, false );
  1510. if( dlg.ShowModal() != wxID_OK )
  1511. return;
  1512. dlg.Transform();
  1513. displayPrimitivesList();
  1514. if( m_canUpdate )
  1515. {
  1516. transferDataToPad( m_dummyPad );
  1517. redraw();
  1518. }
  1519. }
  1520. void DIALOG_PAD_PROPERTIES::onDuplicatePrimitive( wxCommandEvent& event )
  1521. {
  1522. long select = m_listCtrlPrimitives->GetFirstSelected();
  1523. if( select < 0 )
  1524. {
  1525. wxMessageBox( _( "No shape selected" ) );
  1526. return;
  1527. }
  1528. // Multiple selections are allowed. Build selected shapes list
  1529. std::vector<std::shared_ptr<DRAWSEGMENT>> shapeList;
  1530. shapeList.emplace_back( m_primitives[select] );
  1531. while( ( select = m_listCtrlPrimitives->GetNextSelected( select ) ) >= 0 )
  1532. shapeList.emplace_back( m_primitives[select] );
  1533. DIALOG_PAD_PRIMITIVES_TRANSFORM dlg( this, m_parent, shapeList, true );
  1534. if( dlg.ShowModal() != wxID_OK )
  1535. return;
  1536. // Transfer new settings
  1537. // save duplicates to a separate vector to avoid m_primitives reallocation,
  1538. // as shapeList contains pointers to its elements
  1539. std::vector<std::shared_ptr<DRAWSEGMENT>> duplicates;
  1540. dlg.Transform( &duplicates, dlg.GetDuplicateCount() );
  1541. std::move( duplicates.begin(), duplicates.end(), std::back_inserter( m_primitives ) );
  1542. displayPrimitivesList();
  1543. if( m_canUpdate )
  1544. {
  1545. transferDataToPad( m_dummyPad );
  1546. redraw();
  1547. }
  1548. }