summaryrefslogtreecommitdiff
path: root/eeschema/dialogs/dialog_choose_component.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'eeschema/dialogs/dialog_choose_component.cpp')
-rw-r--r--eeschema/dialogs/dialog_choose_component.cpp409
1 files changed, 409 insertions, 0 deletions
diff --git a/eeschema/dialogs/dialog_choose_component.cpp b/eeschema/dialogs/dialog_choose_component.cpp
new file mode 100644
index 0000000..3183c1d
--- /dev/null
+++ b/eeschema/dialogs/dialog_choose_component.cpp
@@ -0,0 +1,409 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
+ * Copyright (C) 2014 KiCad Developers, see change_log.txt for contributors.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+#include <dialog_choose_component.h>
+
+#include <set>
+#include <wx/tokenzr.h>
+
+#include <class_library.h>
+#include <component_tree_search_container.h>
+#include <sch_base_frame.h>
+
+// Tree navigation helpers.
+static wxTreeItemId GetPrevItem( const wxTreeCtrl& tree, const wxTreeItemId& item );
+static wxTreeItemId GetNextItem( const wxTreeCtrl& tree, const wxTreeItemId& item );
+
+DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( SCH_BASE_FRAME* aParent, const wxString& aTitle,
+ COMPONENT_TREE_SEARCH_CONTAINER* const aContainer,
+ int aDeMorganConvert )
+ : DIALOG_CHOOSE_COMPONENT_BASE( aParent, wxID_ANY, aTitle ), m_search_container( aContainer )
+{
+ m_parent = aParent;
+ m_deMorganConvert = aDeMorganConvert >= 0 ? aDeMorganConvert : 0;
+ m_external_browser_requested = false;
+ m_received_doubleclick_in_tree = false;
+ m_search_container->SetTree( m_libraryComponentTree );
+ m_searchBox->SetFocus();
+ m_componentDetails->SetEditable( false );
+ m_componentView->SetLayoutDirection( wxLayout_LeftToRight );
+
+ m_libraryComponentTree->ScrollTo( m_libraryComponentTree->GetFocusedItem() );
+
+ // The tree showing libs and component uses a fixed font,
+ // because we want controle the position of some info when drawing the
+ // tree. Using tabs does not work very well (does not work on Windows)
+ wxFont font = wxSystemSettings::GetFont( wxSYS_DEFAULT_GUI_FONT );
+ m_libraryComponentTree->SetFont( wxFont( font.GetPointSize(),
+ wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL ) );
+
+ // We have to call SetSizeHints to fix the minimal size of the dialog
+ // and its widgets.
+ // this line also fixes an issue on Linux Ubuntu using Unity (dialog not shown).
+ GetSizer()->SetSizeHints( this );
+
+ Centre();
+}
+
+
+DIALOG_CHOOSE_COMPONENT::~DIALOG_CHOOSE_COMPONENT()
+{
+ m_search_container->SetTree( NULL );
+}
+
+
+LIB_ALIAS* DIALOG_CHOOSE_COMPONENT::GetSelectedAlias( int* aUnit ) const
+{
+ return m_search_container->GetSelectedAlias( aUnit );
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnSearchBoxChange( wxCommandEvent& aEvent )
+{
+ m_search_container->UpdateSearchTerm( m_searchBox->GetLineText( 0 ) );
+ updateSelection();
+
+ // On Windows, but not on Linux, the focus is given to
+ // the m_libraryComponentTree, after modificatuons.
+ // We want the focus for m_searchBox.
+ //
+ // We cannot call SetFocus on Linux because it changes the current text selection
+ // and the text edit cursor position.
+#ifdef __WINDOWS__
+ m_searchBox->SetFocus();
+#endif
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnSearchBoxEnter( wxCommandEvent& aEvent )
+{
+ EndModal( wxID_OK ); // We are done.
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::selectIfValid( const wxTreeItemId& aTreeId )
+{
+ if( aTreeId.IsOk() && aTreeId != m_libraryComponentTree->GetRootItem() )
+ m_libraryComponentTree->SelectItem( aTreeId );
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnInterceptSearchBoxKey( wxKeyEvent& aKeyStroke )
+{
+ // Cursor up/down and partiallyi cursor are use to do tree navigation operations.
+ // This is done by intercepting some navigational keystrokes that normally would go to
+ // the text search box (which has the focus by default). That way, we are mostly keyboard
+ // operable.
+ // (If the tree has the focus, it can handle that by itself).
+ const wxTreeItemId sel = m_libraryComponentTree->GetSelection();
+
+ switch( aKeyStroke.GetKeyCode() )
+ {
+ case WXK_UP:
+ selectIfValid( GetPrevItem( *m_libraryComponentTree, sel ) );
+ break;
+
+ case WXK_DOWN:
+ selectIfValid( GetNextItem( *m_libraryComponentTree, sel ) );
+ break;
+
+ // The following keys we can only hijack if they are not needed by the textbox itself.
+
+ case WXK_LEFT:
+ if( m_searchBox->GetInsertionPoint() == 0 )
+ m_libraryComponentTree->Collapse( sel );
+ else
+ aKeyStroke.Skip(); // Use for original purpose: move cursor.
+ break;
+
+ case WXK_RIGHT:
+ if( m_searchBox->GetInsertionPoint() >= (long) m_searchBox->GetLineText( 0 ).length() )
+ m_libraryComponentTree->Expand( sel );
+ else
+ aKeyStroke.Skip(); // Use for original purpose: move cursor.
+ break;
+
+ default:
+ aKeyStroke.Skip(); // Any other key: pass on to search box directly.
+ break;
+ }
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxTreeEvent& aEvent )
+{
+ updateSelection();
+}
+
+
+// Test strategy for OnDoubleClickTreeActivation()/OnTreeMouseUp() work around wxWidgets bug:
+// - search for an item.
+// - use the mouse to double-click on an item in the tree.
+// -> The dialog should close, and the component should _not_ be immediately placed
+void DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation( wxTreeEvent& aEvent )
+{
+ if( !updateSelection() )
+ return;
+
+ // Ok, got selection. We don't just end the modal dialog here, but
+ // wait for the MouseUp event to occur. Otherwise something (broken?)
+ // happens: the dialog will close and will deliver the 'MouseUp' event
+ // to the eeschema canvas, that will immediately place the component.
+ m_received_doubleclick_in_tree = true;
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnTreeMouseUp( wxMouseEvent& aMouseEvent )
+{
+ if( m_received_doubleclick_in_tree )
+ EndModal( wxID_OK ); // We are done (see OnDoubleClickTreeSelect)
+ else
+ aMouseEvent.Skip(); // Let upstream handle it.
+}
+
+// Test strategy to see if OnInterceptTreeEnter() works:
+// - search for an item.
+// - click into the tree once to set focus on tree; navigate. Press 'Enter'
+// -> The dialog should close and the component be available to place.
+void DIALOG_CHOOSE_COMPONENT::OnInterceptTreeEnter( wxKeyEvent& aEvent )
+{
+ // We have to do some special handling for double-click on a tree-item because
+ // of some superfluous event delivery bug in wxWidgets (see OnDoubleClickTreeActivation()).
+ // In tree-activation, we assume we got a double-click and need to take special precaution
+ // that the mouse-up event is not delivered to the window one level up by going through
+ // a state-sequence OnDoubleClickTreeActivation() -> OnTreeMouseUp().
+
+ // Pressing 'Enter' within a tree will also call OnDoubleClickTreeActivation(),
+ // but since this is not due to the double-click and we have no way of knowing that it is
+ // not, we need to intercept the 'Enter' key before that to know that it is time to exit.
+ if( aEvent.GetKeyCode() == WXK_RETURN )
+ EndModal( wxID_OK ); // Dialog is done.
+ else
+ aEvent.Skip(); // Let tree handle that key for navigation.
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnStartComponentBrowser( wxMouseEvent& aEvent )
+{
+ m_external_browser_requested = true;
+ EndModal( wxID_OK ); // We are done.
+}
+
+
+bool DIALOG_CHOOSE_COMPONENT::updateSelection()
+{
+ int unit = 0;
+ LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &unit );
+
+ m_componentView->Refresh();
+
+ m_componentDetails->Clear();
+
+ if( selection == NULL )
+ return false;
+
+ m_componentDetails->Freeze();
+ wxFont font_normal = m_componentDetails->GetFont();
+ wxFont font_bold = m_componentDetails->GetFont();
+ font_bold.SetWeight( wxFONTWEIGHT_BOLD );
+
+ wxTextAttr headline_attribute;
+ headline_attribute.SetFont( font_bold );
+ wxTextAttr text_attribute;
+ text_attribute.SetFont( font_normal );
+
+ const wxString name = selection->GetName();
+
+ if ( !name.empty() )
+ {
+ m_componentDetails->SetDefaultStyle( headline_attribute );
+ m_componentDetails->AppendText( name );
+ }
+
+ const wxString description = selection->GetDescription();
+
+ if( !description.empty() )
+ {
+ if ( !m_componentDetails->IsEmpty() )
+ m_componentDetails->AppendText( wxT( "\n\n" ) );
+
+ m_componentDetails->SetDefaultStyle( headline_attribute );
+ m_componentDetails->AppendText( _( "Description\n" ) );
+ m_componentDetails->SetDefaultStyle( text_attribute );
+ m_componentDetails->AppendText( description );
+ }
+
+ const wxString keywords = selection->GetKeyWords();
+
+ if( !keywords.empty() )
+ {
+ if ( !m_componentDetails->IsEmpty() )
+ m_componentDetails->AppendText( wxT( "\n\n" ) );
+
+ m_componentDetails->SetDefaultStyle( headline_attribute );
+ m_componentDetails->AppendText( _( "Keywords\n" ) );
+ m_componentDetails->SetDefaultStyle( text_attribute );
+ m_componentDetails->AppendText( keywords );
+ }
+
+ if ( !selection->IsRoot() )
+ {
+ LIB_PART* root_part = selection->GetPart();
+ const wxString root_component_name( root_part ? root_part->GetName() : _( "Unknown" ) );
+
+ if ( !m_componentDetails->IsEmpty() )
+ m_componentDetails->AppendText( wxT( "\n\n" ) );
+
+ m_componentDetails->SetDefaultStyle( headline_attribute );
+ m_componentDetails->AppendText( _( "Alias of " ) );
+ m_componentDetails->SetDefaultStyle( text_attribute );
+ m_componentDetails->AppendText( root_component_name );
+ }
+
+ m_componentDetails->SetInsertionPoint( 0 ); // scroll up.
+ m_componentDetails->Thaw();
+
+ return true;
+}
+
+
+void DIALOG_CHOOSE_COMPONENT::OnHandlePreviewRepaint( wxPaintEvent& aRepaintEvent )
+{
+ int unit = 0;
+ LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &unit );
+ LIB_PART* part = selection ? selection->GetPart() : NULL;
+
+ // Don't draw anything (not even the background) if we don't have
+ // a part to show
+ if( !part )
+ return;
+
+ if( selection->IsRoot() )
+ {
+ // just show the part directly
+ renderPreview( part, unit );
+ }
+ else
+ {
+ // switch out the name temporarily for the alias name
+ wxString tmp( part->GetName() );
+ part->SetName( selection->GetName() );
+
+ renderPreview( part, unit );
+
+ part->SetName( tmp );
+ }
+}
+
+
+// Render the preview in our m_componentView. If this gets more complicated, we should
+// probably have a derived class from wxPanel; but this keeps things local.
+void DIALOG_CHOOSE_COMPONENT::renderPreview( LIB_PART* aComponent, int aUnit )
+{
+ wxPaintDC dc( m_componentView );
+
+ const wxSize dc_size = dc.GetSize();
+
+ // Avoid rendering when either dimension is zero
+ if( dc_size.x == 0 || dc_size.y == 0 )
+ return;
+
+ GRResetPenAndBrush( &dc );
+
+ EDA_COLOR_T bgcolor = m_parent->GetDrawBgColor();
+
+ dc.SetBackground( bgcolor == BLACK ? *wxBLACK_BRUSH : *wxWHITE_BRUSH );
+ dc.Clear();
+
+ if( aComponent == NULL )
+ return;
+
+ if( aUnit <= 0 )
+ aUnit = 1;
+
+ dc.SetDeviceOrigin( dc_size.x / 2, dc_size.y / 2 );
+
+ // Find joint bounding box for everything we are about to draw.
+ EDA_RECT bBox = aComponent->GetBoundingBox( aUnit, m_deMorganConvert );
+ const double xscale = (double) dc_size.x / bBox.GetWidth();
+ const double yscale = (double) dc_size.y / bBox.GetHeight();
+ const double scale = std::min( xscale, yscale ) * 0.85;
+
+ dc.SetUserScale( scale, scale );
+
+ wxPoint offset = -bBox.Centre();
+
+
+ aComponent->Draw( NULL, &dc, offset, aUnit, m_deMorganConvert, GR_COPY,
+ UNSPECIFIED_COLOR, DefaultTransform, true, true, false );
+}
+
+
+static wxTreeItemId GetPrevItem( const wxTreeCtrl& tree, const wxTreeItemId& item )
+{
+ wxTreeItemId prevItem = tree.GetPrevSibling( item );
+
+ if( !prevItem.IsOk() )
+ {
+ prevItem = tree.GetItemParent( item );
+ }
+ else if( tree.IsExpanded( prevItem ) )
+ {
+ prevItem = tree.GetLastChild( prevItem );
+ }
+
+ return prevItem;
+}
+
+
+static wxTreeItemId GetNextItem( const wxTreeCtrl& tree, const wxTreeItemId& item )
+{
+ wxTreeItemId nextItem;
+
+ if( !item.IsOk() )
+ return nextItem; // item is not valid: return a not valid wxTreeItemId
+
+ if( tree.IsExpanded( item ) )
+ {
+ wxTreeItemIdValue dummy;
+ nextItem = tree.GetFirstChild( item, dummy );
+ }
+ else
+ {
+ wxTreeItemId root_cell= tree.GetRootItem();
+
+ // Walk up levels until we find one that has a next sibling.
+ for ( wxTreeItemId walk = item; walk.IsOk(); walk = tree.GetItemParent( walk ) )
+ {
+ if( walk == root_cell ) // the root cell (not displayed) is reached
+ break; // Exit (calling GetNextSibling( root_cell ) crashes.
+
+ nextItem = tree.GetNextSibling( walk );
+
+ if( nextItem.IsOk() )
+ break;
+ }
+ }
+
+ return nextItem;
+}