2
* This program source code file is part of KiCad, a free EDA CAD application.
4
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
5
* Copyright (C) 2014 KiCad Developers, see change_log.txt for contributors.
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.
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.
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
24
#include <dialog_choose_component.h>
27
#include <boost/foreach.hpp>
28
#include <wx/tokenzr.h>
30
#include <class_library.h>
31
#include <component_tree_search_container.h>
32
#include <sch_base_frame.h>
34
// Tree navigation helpers.
35
static wxTreeItemId GetPrevItem( const wxTreeCtrl& tree, const wxTreeItemId& item );
36
static wxTreeItemId GetNextItem( const wxTreeCtrl& tree, const wxTreeItemId& item );
38
DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( wxWindow* aParent, const wxString& aTitle,
39
COMPONENT_TREE_SEARCH_CONTAINER* aContainer,
40
int aDeMorganConvert )
41
: DIALOG_CHOOSE_COMPONENT_BASE( aParent, wxID_ANY, aTitle ),
42
m_search_container( aContainer ),
43
m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ),
44
m_external_browser_requested( false ),
45
m_received_doubleclick_in_tree( false )
47
m_search_container->SetTree( m_libraryComponentTree );
48
m_searchBox->SetFocus();
49
m_componentDetails->SetEditable( false );
51
#if wxCHECK_VERSION( 3, 0, 0 )
52
m_libraryComponentTree->ScrollTo( m_libraryComponentTree->GetFocusedItem() );
55
// The tree showing libs and component uses a fixed font,
56
// because we want controle the position of some info when drawing the
57
// tree. Using tabs does not work very well (does not work on Windows)
58
wxFont font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
59
m_libraryComponentTree->SetFont( wxFont( font.GetPointSize(),
60
wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ) );
64
DIALOG_CHOOSE_COMPONENT::~DIALOG_CHOOSE_COMPONENT()
66
m_search_container->SetTree( NULL );
70
LIB_ALIAS* DIALOG_CHOOSE_COMPONENT::GetSelectedAlias( int* aUnit ) const
72
return m_search_container->GetSelectedAlias( aUnit );
76
void DIALOG_CHOOSE_COMPONENT::OnSearchBoxChange( wxCommandEvent& aEvent )
78
m_search_container->UpdateSearchTerm( m_searchBox->GetLineText(0) );
83
void DIALOG_CHOOSE_COMPONENT::OnSearchBoxEnter( wxCommandEvent& aEvent )
85
EndModal( wxID_OK ); // We are done.
89
void DIALOG_CHOOSE_COMPONENT::selectIfValid( const wxTreeItemId& aTreeId )
91
if( aTreeId.IsOk() && aTreeId != m_libraryComponentTree->GetRootItem() )
92
m_libraryComponentTree->SelectItem( aTreeId );
96
void DIALOG_CHOOSE_COMPONENT::OnInterceptSearchBoxKey( wxKeyEvent& aKeyStroke )
98
// Cursor up/down and partiallyi cursor are use to do tree navigation operations.
99
// This is done by intercepting some navigational keystrokes that normally would go to
100
// the text search box (which has the focus by default). That way, we are mostly keyboard
102
// (If the tree has the focus, it can handle that by itself).
103
const wxTreeItemId sel = m_libraryComponentTree->GetSelection();
105
switch( aKeyStroke.GetKeyCode() )
108
selectIfValid( GetPrevItem( *m_libraryComponentTree, sel ) );
112
selectIfValid( GetNextItem( *m_libraryComponentTree, sel ) );
115
// The follwoing keys we can only hijack if they are not needed by the textbox itself.
118
if( m_searchBox->GetInsertionPoint() == 0 )
119
m_libraryComponentTree->Collapse( sel );
121
aKeyStroke.Skip(); // Use for original purpose: move cursor.
125
if( m_searchBox->GetInsertionPoint() >= (long) m_searchBox->GetLineText( 0 ).length() )
126
m_libraryComponentTree->Expand( sel );
128
aKeyStroke.Skip(); // Use for original purpose: move cursor.
132
aKeyStroke.Skip(); // Any other key: pass on to search box directly.
138
void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxTreeEvent& aEvent )
144
// Test strategy for OnDoubleClickTreeActivation()/OnTreeMouseUp() work around wxWidgets bug:
145
// - search for an item.
146
// - use the mouse to double-click on an item in the tree.
147
// -> The dialog should close, and the component should _not_ be immediately placed
148
void DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation( wxTreeEvent& aEvent )
150
if( !updateSelection() )
153
// Ok, got selection. We don't just end the modal dialog here, but
154
// wait for the MouseUp event to occur. Otherwise something (broken?)
155
// happens: the dialog will close and will deliver the 'MouseUp' event
156
// to the eeschema canvas, that will immediately place the component.
157
m_received_doubleclick_in_tree = true;
161
void DIALOG_CHOOSE_COMPONENT::OnTreeMouseUp( wxMouseEvent& aMouseEvent )
163
if( m_received_doubleclick_in_tree )
164
EndModal( wxID_OK ); // We are done (see OnDoubleClickTreeSelect)
166
aMouseEvent.Skip(); // Let upstream handle it.
169
// Test strategy to see if OnInterceptTreeEnter() works:
170
// - search for an item.
171
// - click into the tree once to set focus on tree; navigate. Press 'Enter'
172
// -> The dialog should close and the component be available to place.
173
void DIALOG_CHOOSE_COMPONENT::OnInterceptTreeEnter( wxKeyEvent& aEvent )
175
// We have to do some special handling for double-click on a tree-item because
176
// of some superfluous event delivery bug in wxWidgets (see OnDoubleClickTreeActivation()).
177
// In tree-activation, we assume we got a double-click and need to take special precaution
178
// that the mouse-up event is not delivered to the window one level up by going through
179
// a state-sequence OnDoubleClickTreeActivation() -> OnTreeMouseUp().
181
// Pressing 'Enter' within a tree will also call OnDoubleClickTreeActivation(),
182
// but since this is not due to the double-click and we have no way of knowing that it is
183
// not, we need to intercept the 'Enter' key before that to know that it is time to exit.
184
if( aEvent.GetKeyCode() == WXK_RETURN )
185
EndModal( wxID_OK ); // Dialog is done.
187
aEvent.Skip(); // Let tree handle that key for navigation.
191
void DIALOG_CHOOSE_COMPONENT::OnStartComponentBrowser( wxMouseEvent& aEvent )
193
m_external_browser_requested = true;
194
EndModal( wxID_OK ); // We are done.
198
bool DIALOG_CHOOSE_COMPONENT::updateSelection()
201
LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &unit );
203
m_componentView->Refresh();
205
m_componentDetails->Clear();
207
if( selection == NULL )
210
m_componentDetails->Freeze();
211
wxFont font_normal = m_componentDetails->GetFont();
212
wxFont font_bold = m_componentDetails->GetFont();
213
font_bold.SetWeight( wxFONTWEIGHT_BOLD );
215
wxTextAttr headline_attribute;
216
headline_attribute.SetFont(font_bold);
217
wxTextAttr text_attribute;
218
text_attribute.SetFont(font_normal);
220
const wxString description = selection->GetDescription();
222
if( !description.empty() )
224
m_componentDetails->SetDefaultStyle( headline_attribute );
225
m_componentDetails->AppendText( _("Description\n") );
226
m_componentDetails->SetDefaultStyle( text_attribute );
227
m_componentDetails->AppendText( description );
228
m_componentDetails->AppendText( wxT("\n\n") );
231
const wxString keywords = selection->GetKeyWords();
233
if( !keywords.empty() )
235
m_componentDetails->SetDefaultStyle( headline_attribute );
236
m_componentDetails->AppendText( _("Keywords\n") );
237
m_componentDetails->SetDefaultStyle( text_attribute );
238
m_componentDetails->AppendText( keywords );
241
m_componentDetails->SetInsertionPoint( 0 ); // scroll up.
242
m_componentDetails->Thaw();
248
void DIALOG_CHOOSE_COMPONENT::OnHandlePreviewRepaint( wxPaintEvent& aRepaintEvent )
251
LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &unit );
253
renderPreview( selection ? selection->GetComponent() : NULL, unit );
257
// Render the preview in our m_componentView. If this gets more complicated, we should
258
// probably have a derived class from wxPanel; but this keeps things local.
259
void DIALOG_CHOOSE_COMPONENT::renderPreview( LIB_COMPONENT* aComponent, int aUnit )
261
wxPaintDC dc( m_componentView );
262
dc.SetBackground( *wxWHITE_BRUSH );
265
if( aComponent == NULL )
271
const wxSize dc_size = dc.GetSize();
272
dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );
274
// Find joint bounding box for everything we are about to draw.
275
EDA_RECT bBox = aComponent->GetBoundingBox( aUnit, m_deMorganConvert );
276
const double xscale = (double) dc_size.x / bBox.GetWidth();
277
const double yscale = (double) dc_size.y / bBox.GetHeight();
278
const double scale = std::min( xscale, yscale ) * 0.85;
280
dc.SetUserScale( scale, scale );
282
wxPoint offset = bBox.Centre();
286
aComponent->Draw( NULL, &dc, offset, aUnit, m_deMorganConvert, GR_COPY,
287
UNSPECIFIED_COLOR, DefaultTransform, true, true, false );
291
static wxTreeItemId GetPrevItem( const wxTreeCtrl& tree, const wxTreeItemId& item )
293
wxTreeItemId prevItem = tree.GetPrevSibling( item );
295
if( !prevItem.IsOk() )
297
prevItem = tree.GetItemParent( item );
299
else if( tree.IsExpanded( prevItem ) )
301
prevItem = tree.GetLastChild( prevItem );
308
static wxTreeItemId GetNextItem( const wxTreeCtrl& tree, const wxTreeItemId& item )
310
wxTreeItemId nextItem;
312
if( tree.IsExpanded( item ) )
314
wxTreeItemIdValue dummy;
315
nextItem = tree.GetFirstChild( item, dummy );
319
// Walk up levels until we find one that has a next sibling.
320
for ( wxTreeItemId walk = item; walk.IsOk(); walk = tree.GetItemParent( walk ) )
322
nextItem = tree.GetNextSibling( walk );
323
if( nextItem.IsOk() )