KiCad PCB EDA Suite
dialog_move_exact.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2014 John Beard, john.j.beard@gmail.com
5  * Copyright (C) 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 
26 #include <math/util.h> // for KiROUND
27 #include <widgets/tab_traversal.h>
28 #include <pcb_edit_frame.h>
29 
30 // initialise statics
32 
33 
35  double& aRotate, ROTATION_ANCHOR& aAnchor,
36  const EDA_RECT& aBbox ) :
37  DIALOG_MOVE_EXACT_BASE( aParent ),
38  m_translation( aTranslate ),
39  m_rotation( aRotate ),
40  m_rotationAnchor( aAnchor ),
41  m_bbox( aBbox ),
42  m_moveX( aParent, m_xLabel, m_xEntry, m_xUnit ),
43  m_moveY( aParent, m_yLabel, m_yEntry, m_yUnit ),
44  m_rotate( aParent, m_rotLabel, m_rotEntry, m_rotUnit )
45 {
46  // We can't set the tab order through wxWidgets due to shortcomings in their mnemonics
47  // implementation on MSW
48  m_tabOrder = {
49  m_xEntry,
50  m_yEntry,
51  m_rotEntry,
55  };
56 
58 
59  m_menuIDs.push_back( aAnchor );
61 
62  if( aParent->IsType( FRAME_PCB_EDITOR ) )
64 
66 
67  // and set up the entries according to the saved options
68  m_polarCoords->SetValue( m_options.polarCoords );
71 
74  m_anchorOptions->SetSelection( std::min( m_options.entryAnchorSelection, m_menuIDs.size() ) );
75 
76  m_stdButtonsOK->SetDefault();
77 
79 }
80 
81 
83 {
84  wxArrayString menuItems;
85 
86  for( auto anchorID : m_menuIDs )
87  {
88  switch( anchorID )
89  {
91  menuItems.push_back( _( "Rotate around item anchor" ) );
92  break;
94  menuItems.push_back( _( "Rotate around selection center" ) );
95  break;
97  menuItems.push_back( _( "Rotate around local coordinates origin" ) );
98  break;
100  menuItems.push_back( _( "Rotate around drill/place origin" ) );
101  break;
102  }
103  }
104 
105  m_anchorOptions->Set( menuItems );
106 }
107 
108 
109 void DIALOG_MOVE_EXACT::ToPolarDeg( double x, double y, double& r, double& q )
110 {
111  // convert to polar coordinates
112  r = hypot( x, y );
113 
114  q = ( r != 0) ? RAD2DEG( atan2( y, x ) ) : 0;
115 }
116 
117 
119 {
120  if( polar )
121  {
122  const int r = m_moveX.GetValue();
123  const double q = m_moveY.GetValue();
124 
125  val.x = r * cos( DEG2RAD( q / 10.0 ) );
126  val.y = r * sin( DEG2RAD( q / 10.0 ) );
127  }
128  else
129  {
130  // direct read
131  val.x = m_moveX.GetValue();
132  val.y = m_moveY.GetValue();
133  }
134 
135  // no validation to do here, but in future, you could return false here
136  return true;
137 }
138 
139 
140 void DIALOG_MOVE_EXACT::OnPolarChanged( wxCommandEvent& event )
141 {
142  bool newPolar = m_polarCoords->IsChecked();
143  wxPoint val;
144 
145  // get the value as previously stored
146  GetTranslationInIU( val, !newPolar );
147 
148  // now switch the controls to the new representations
149  updateDialogControls( newPolar );
150 
151  if( newPolar )
152  {
153  // convert to polar coordinates
154  double r, q;
155  ToPolarDeg( val.x, val.y, r, q );
156 
157  m_moveX.SetValue( KiROUND( r / 10.0) * 10 );
158  m_moveY.SetValue( q * 10 );
159  }
160  else
161  {
162  // vector is already in Cartesian, so just render out
163  // note - round off the last decimal place (10nm) to prevent
164  // (some) rounding causing errors when round-tripping
165  // you can never eliminate entirely, however
166  m_moveX.SetValue( KiROUND( val.x / 10.0 ) * 10 );
167  m_moveY.SetValue( KiROUND( val.y / 10.0 ) * 10 );
168  }
169 
170 }
171 
172 
174 {
175  if( aPolar )
176  {
177  m_moveX.SetLabel( _( "Distance:" ) ); // Polar radius
178  m_moveY.SetLabel( _( "Angle:" ) ); // Polar theta or angle
180  }
181  else
182  {
183  m_moveX.SetLabel( _( "Move X:" ) );
184  m_moveY.SetLabel( _( "Move Y:" ) );
186  }
187 
188  Layout();
189 }
190 
191 
192 void DIALOG_MOVE_EXACT::OnClear( wxCommandEvent& event )
193 {
194  wxObject* obj = event.GetEventObject();
195 
196  if( obj == m_clearX )
197  {
198  m_moveX.SetValue( 0 );
199  }
200  else if( obj == m_clearY )
201  {
202  m_moveY.SetValue( 0 );
203  }
204  else if( obj == m_clearRot )
205  {
206  m_rotate.SetValue( 0 );
207  }
208 
209  // Keep m_stdButtonsOK focused to allow enter key actiavte the OK button
210  m_stdButtonsOK->SetFocus();
211 }
212 
213 
215 {
216  // for the output, we only deliver a Cartesian vector
217  bool ok = GetTranslationInIU( m_translation, m_polarCoords->IsChecked() );
219  m_rotationAnchor = m_menuIDs[ m_anchorOptions->GetSelection() ];
220 
221  if( ok )
222  {
223  // save the settings
224  m_options.polarCoords = m_polarCoords->GetValue();
228  m_options.entryAnchorSelection = (size_t) std::max( m_anchorOptions->GetSelection(), 0 );
229  return true;
230  }
231 
232  return false;
233 }
234 
235 
236 void DIALOG_MOVE_EXACT::OnTextFocusLost( wxFocusEvent& event )
237 {
238  wxTextCtrl* obj = static_cast<wxTextCtrl*>( event.GetEventObject() );
239 
240  if( obj->GetValue().IsEmpty() )
241  obj->SetValue( "0" );
242 
243  event.Skip();
244 }
245 
246 
247 void DIALOG_MOVE_EXACT::OnTextChanged( wxCommandEvent& event )
248 {
249 
250  double delta_x = m_moveX.GetValue();
251  double delta_y = m_moveY.GetValue();
252  double max_border = std::numeric_limits<int>::max() * 0.7071;
253 
254  if( m_bbox.GetLeft() + delta_x < -max_border ||
255  m_bbox.GetRight() + delta_x > max_border ||
256  m_bbox.GetTop() + delta_y < -max_border ||
257  m_bbox.GetBottom() + delta_y > max_border )
258  {
259  const wxString invalid_length = _( "Invalid movement values. Movement would place selection "
260  "outside of the maximum board area." );
261 
262  m_xEntry->SetToolTip( invalid_length );
263  m_xEntry->SetForegroundColour( *wxRED );
264  m_yEntry->SetToolTip( invalid_length );
265  m_yEntry->SetForegroundColour( *wxRED );
266  m_stdButtons->GetAffirmativeButton()->Disable();
267  }
268  else
269  {
270  m_xEntry->SetToolTip( "" );
271  m_xEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
272  m_yEntry->SetToolTip( "" );
273  m_yEntry->SetForegroundColour( wxSystemSettings::GetColour( wxSYS_COLOUR_WINDOWTEXT ) );
274  m_stdButtons->GetAffirmativeButton()->Enable();
275  event.Skip();
276  }
277 
278 }
Functions for manipulating tab traversal in forms and dialogs.
void OnTextChanged(wxCommandEvent &event) override
int GetTop() const
Definition: eda_rect.h:123
ROTATION_ANCHOR & m_rotationAnchor
std::vector< wxWindow * > m_tabOrder
Definition: dialog_shim.h:210
std::vector< ROTATION_ANCHOR > m_menuIDs
int GetLeft() const
Definition: eda_rect.h:122
Class DIALOG_MOVE_EXACT_BASE.
double RAD2DEG(double rad)
Definition: trigo.h:211
void FinishDialogSettings()
In all dialogs, we must call the same functions to fix minimal dlg size, the default position and per...
int GetBottom() const
Definition: eda_rect.h:124
EDA_UNITS GetUserUnits() const
Definition: dialog_shim.h:132
void OnPolarChanged(wxCommandEvent &event) override
const EDA_RECT & m_bbox
bool GetTranslationInIU(wxPoint &val, bool polar)
Get the (Cartesian) translation described by the text entries.
int GetRight() const
Definition: eda_rect.h:121
ROTATION_ANCHOR
void OnTextFocusLost(wxFocusEvent &event) override
void OnClear(wxCommandEvent &event) override
void updateDialogControls(bool aPolar)
void SetLabel(const wxString &aLabel)
bool IsType(FRAME_T aType) const
double DEG2RAD(double deg)
Definition: trigo.h:210
#define _(s)
Definition: 3d_actions.cpp:31
DIALOG_MOVE_EXACT(PCB_BASE_FRAME *aParent, wxPoint &aTranslate, double &aRotate, ROTATION_ANCHOR &aAnchor, const EDA_RECT &aBbox)
virtual void SetValue(int aValue)
Function SetValue Sets new value (in Internal Units) for the text field, taking care of units convers...
bool TransferDataFromWindow() override
EDA_RECT handles the component boundary box.
Definition: eda_rect.h:44
constexpr ret_type KiROUND(fp_type v)
Round a floating point number to an integer using "round halfway cases away from zero".
Definition: util.h:61
void ToPolarDeg(double x, double y, double &r, double &q)
Convert a given Cartesian point into a polar representation.
virtual long long int GetValue()
Function GetValue Returns the current value in Internal Units.
static MOVE_EXACT_OPTIONS m_options
wxStdDialogButtonSizer * m_stdButtons
PCB_BASE_FRAME basic PCB main window class for Pcbnew, Gerbview, and CvPcb footprint viewer.
virtual void SetUnits(EDA_UNITS aUnits, bool aUseMils=false)
Function SetUnits Normally not needed (as the UNIT_BINDER inherits from the parent frame),...
Definition: unit_binder.cpp:68