KiCad PCB EDA Suite
footprint_choice.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) 2017 KiCad Developers, see AUTHORS.txt for contributors.
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include <functional>
22 #include <wx/dc.h>
23 #include <wx/pen.h>
24 
25 wxDEFINE_EVENT( EVT_INTERACTIVE_CHOICE, wxCommandEvent );
26 
27 
28 wxColour FOOTPRINT_CHOICE::m_grey( 0x808080 );
29 
30 
31 FOOTPRINT_CHOICE::FOOTPRINT_CHOICE( wxWindow* aParent, int aId )
32  : wxOwnerDrawnComboBox( aParent, aId, wxEmptyString, wxDefaultPosition, wxDefaultSize,
33  /* n */ 0, /* choices */ nullptr, wxCB_READONLY ),
34  m_last_selection( 0 )
35 {
36 }
37 
38 
40 {
41 }
42 
43 
44 void FOOTPRINT_CHOICE::DoSetPopupControl( wxComboPopup* aPopup )
45 {
46  using namespace std::placeholders;
47  wxOwnerDrawnComboBox::DoSetPopupControl( aPopup );
48 
49  // Bind events to intercept selections, so the separator can be made nonselectable.
50 
51  GetVListBoxComboPopup()->Bind( wxEVT_MOTION, &FOOTPRINT_CHOICE::TryVetoMouse, this );
52  GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DOWN, &FOOTPRINT_CHOICE::TryVetoMouse, this );
53  GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::TryVetoMouse, this );
54  GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::OnMouseUp, this );
55  GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DCLICK, &FOOTPRINT_CHOICE::TryVetoMouse, this );
56  GetVListBoxComboPopup()->Bind(
57  wxEVT_LISTBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, true ) );
58  Bind( wxEVT_COMBOBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, false ) );
59  GetVListBoxComboPopup()->Bind(
60  wxEVT_CHAR_HOOK, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, true ) );
61  GetVListBoxComboPopup()->Bind( wxEVT_CHAR_HOOK, &FOOTPRINT_CHOICE::OnKeyUp, this );
62  Bind( wxEVT_KEY_DOWN, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, false ) );
63 }
64 
65 
66 void FOOTPRINT_CHOICE::OnDrawItem( wxDC& aDC, wxRect const& aRect, int aItem, int aFlags ) const
67 {
68  wxString text = SafeGetString( aItem );
69 
70  if( text == wxEmptyString )
71  {
72  wxPen pen( m_grey, 1, wxPENSTYLE_SOLID );
73 
74  aDC.SetPen( pen );
75  aDC.DrawLine( aRect.x, aRect.y + aRect.height / 2, aRect.x + aRect.width,
76  aRect.y + aRect.height / 2 );
77  }
78  else
79  {
80  wxCoord x, y;
81 
82  if( aFlags & wxODCB_PAINTING_CONTROL )
83  {
84  x = aRect.x + GetMargins().x;
85  y = ( aRect.height - aDC.GetCharHeight() ) / 2 + aRect.y;
86  }
87  else
88  {
89  x = aRect.x + 2;
90  y = aRect.y;
91  }
92 
93  // If this item has a footprint and that footprint has a ":" delimiter, find the
94  // library component, then find that in the display string and grey it out.
95 
96  size_t start_grey = 0;
97  size_t end_grey = 0;
98 
99  wxString lib = static_cast<wxStringClientData*>( GetClientObject( aItem ) )->GetData();
100  size_t colon_index = lib.rfind( ':' );
101 
102  if( colon_index != wxString::npos )
103  {
104  wxString library_part = lib.SubString( 0, colon_index );
105  size_t library_index = text.rfind( library_part );
106 
107  if( library_index != wxString::npos )
108  {
109  start_grey = library_index;
110  end_grey = start_grey + library_part.Length();
111  }
112  }
113 
114  if( start_grey != end_grey && !( aFlags & wxODCB_PAINTING_SELECTED ) )
115  {
116  x = DrawTextFragment( aDC, x, y, text.SubString( 0, start_grey - 1 ) );
117 
118  wxColour standard_color = aDC.GetTextForeground();
119 
120  aDC.SetTextForeground( m_grey );
121  x = DrawTextFragment( aDC, x, y, text.SubString( start_grey, end_grey - 1 ) );
122 
123  aDC.SetTextForeground( standard_color );
124  x = DrawTextFragment( aDC, x, y, text.SubString( end_grey, text.Length() - 1 ) );
125  }
126  else
127  {
128  aDC.DrawText( text, x, y );
129  }
130  }
131 }
132 
133 wxCoord FOOTPRINT_CHOICE::OnMeasureItem( size_t aItem ) const
134 {
135  if( SafeGetString( aItem ) == "" )
136  return 11;
137  else
138  return wxOwnerDrawnComboBox::OnMeasureItem( aItem );
139 }
140 
141 
142 wxCoord FOOTPRINT_CHOICE::OnMeasureItemWidth( size_t aItem ) const
143 {
144  if( SafeGetString( aItem ) == "" )
145  return GetTextRect().GetWidth() - 2;
146  else
147  return wxOwnerDrawnComboBox::OnMeasureItemWidth( aItem );
148 }
149 
150 
151 wxCoord FOOTPRINT_CHOICE::DrawTextFragment( wxDC& aDC, wxCoord x, wxCoord y, wxString const& aText )
152 {
153  aDC.DrawText( aText, x, y );
154  return x + aDC.GetTextExtent( aText ).GetWidth();
155 }
156 
157 
158 void FOOTPRINT_CHOICE::TryVetoMouse( wxMouseEvent& aEvent )
159 {
160  int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y );
161 
162  if( SafeGetString( item ) != "" )
163  aEvent.Skip();
164 }
165 
166 
167 void FOOTPRINT_CHOICE::OnMouseUp( wxMouseEvent& aEvent )
168 {
169  int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y );
170 
171  wxCommandEvent evt( EVT_INTERACTIVE_CHOICE );
172  evt.SetInt( item );
173  wxPostEvent( this, evt );
174 
175  aEvent.Skip();
176 }
177 
178 
179 void FOOTPRINT_CHOICE::OnKeyUp( wxKeyEvent& aEvent )
180 {
181  int item = GetSelectionEither( true );
182 
183  if( aEvent.GetKeyCode() == WXK_RETURN )
184  {
185  wxCommandEvent evt( EVT_INTERACTIVE_CHOICE );
186  evt.SetInt( item );
187  wxPostEvent( this, evt );
188  }
189 
190  aEvent.Skip();
191 }
192 
193 
194 void FOOTPRINT_CHOICE::TryVetoSelect( wxCommandEvent& aEvent, bool aInner )
195 {
196  int sel = GetSelectionEither( aInner );
197 
198  if( sel >= 0 && sel < (int) GetCount() )
199  {
200  wxString text = SafeGetString( sel );
201 
202  if( text == "" )
204  else
205  {
206  m_last_selection = sel;
207  aEvent.Skip();
208  }
209  }
210 }
211 
212 
213 void FOOTPRINT_CHOICE::TrySkipSeparator( wxKeyEvent& aEvent, bool aInner )
214 {
215  int key = aEvent.GetKeyCode();
216  int sel = GetSelectionEither( aInner );
217  int new_sel = sel;
218 
219  if( key == WXK_UP && SafeGetString( sel - 1 ) == wxEmptyString )
220  {
221  new_sel = sel - 2;
222  }
223  else if( key == WXK_DOWN && SafeGetString( sel + 1 ) == wxEmptyString )
224  {
225  new_sel = sel + 2;
226  }
227 
228  if( new_sel != sel )
229  SetSelectionEither( aInner, new_sel );
230  else
231  aEvent.Skip();
232 }
233 
234 
235 wxString FOOTPRINT_CHOICE::SafeGetString( int aItem ) const
236 {
237  if( aItem >= 0 && aItem < (int) GetCount() )
238  return GetVListBoxComboPopup()->GetString( aItem );
239  else
240  return wxEmptyString;
241 }
242 
243 
244 int FOOTPRINT_CHOICE::GetSelectionEither( bool aInner ) const
245 {
246  if( aInner )
247  return GetVListBoxComboPopup()->wxVListBox::GetSelection();
248  else
249  return GetSelection();
250 }
251 
252 
253 void FOOTPRINT_CHOICE::SetSelectionEither( bool aInner, int aSel )
254 {
255  if( aSel >= 0 && aSel < (int) GetCount() )
256  {
257  if( aInner )
258  return GetVListBoxComboPopup()->wxVListBox::SetSelection( aSel );
259  else
260  return SetSelection( aSel );
261  }
262 }
virtual wxCoord OnMeasureItem(size_t aItem) const override
void TrySkipSeparator(wxKeyEvent &aEvent, bool aInner)
For arrow key events, skip over separators.
virtual void DoSetPopupControl(wxComboPopup *aPopup) override
virtual ~FOOTPRINT_CHOICE()
FOOTPRINT_CHOICE(wxWindow *aParent, int aId)
void TryVetoMouse(wxMouseEvent &aEvent)
Veto a mouseover event if in the separator.
virtual wxCoord OnMeasureItemWidth(size_t aItem) const override
virtual void OnDrawItem(wxDC &aDC, wxRect const &aRect, int aItem, int aFlags) const override
void TryVetoSelect(wxCommandEvent &aEvent, bool aInner)
Veto a select event for the separator.
void OnMouseUp(wxMouseEvent &aEvent)
Mouse up on an item in the list.
int GetSelectionEither(bool aInner) const
Get selection from either the outer (combo box) or inner (popup) list.
static wxCoord DrawTextFragment(wxDC &aDC, wxCoord x, wxCoord y, wxString const &aText)
Draw a fragment of text, then return the next x coordinate to continue drawing.
void OnKeyUp(wxKeyEvent &aEvent)
Key up on an item in the list.
void SetSelectionEither(bool aInner, int aSel)
Safely set selection for either the outer (combo box) or inner (popup) list, doing nothing for invali...
wxString SafeGetString(int aItem) const
Safely get a string for an item, returning wxEmptyString if the item doesn't exist.
static wxColour m_grey
wxDEFINE_EVENT(EVT_INTERACTIVE_CHOICE, wxCommandEvent)