2014-02-14 08:05:04 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2014 Henner Zeller <h.zeller@acm.org>
|
2017-02-05 19:18:29 +00:00
|
|
|
* Copyright (C) 2016-2017 KiCad Developers, see AUTHORS.txt for contributors.
|
2014-02-14 08:05:04 +00:00
|
|
|
*
|
|
|
|
* 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
|
|
|
|
*/
|
2017-02-19 02:39:55 +00:00
|
|
|
|
2014-02-14 08:05:04 +00:00
|
|
|
#include <dialog_choose_component.h>
|
|
|
|
|
|
|
|
#include <set>
|
|
|
|
#include <wx/tokenzr.h>
|
2017-02-19 02:39:55 +00:00
|
|
|
#include <wx/utils.h>
|
2014-02-14 08:05:04 +00:00
|
|
|
|
|
|
|
#include <class_library.h>
|
|
|
|
#include <component_tree_search_container.h>
|
|
|
|
#include <sch_base_frame.h>
|
2017-02-19 02:39:55 +00:00
|
|
|
#include <widgets/footprint_preview_panel.h>
|
|
|
|
#include <widgets/two_column_tree_list.h>
|
2017-02-19 14:00:02 +00:00
|
|
|
#include <template_fieldnames.h>
|
|
|
|
#include <generate_alias_info.h>
|
2017-02-24 01:29:03 +00:00
|
|
|
#include <make_unique.h>
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-14 17:16:59 +00:00
|
|
|
// Tree navigation helpers.
|
2017-02-19 02:39:55 +00:00
|
|
|
static wxTreeListItem GetPrevItem( const wxTreeListCtrl& tree, const wxTreeListItem& item );
|
|
|
|
static wxTreeListItem GetNextItem( const wxTreeListCtrl& tree, const wxTreeListItem& item );
|
|
|
|
static wxTreeListItem GetPrevSibling( const wxTreeListCtrl& tree, const wxTreeListItem& item );
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-09-26 10:35:11 +00:00
|
|
|
DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( SCH_BASE_FRAME* aParent, const wxString& aTitle,
|
2014-11-19 14:22:47 +00:00
|
|
|
COMPONENT_TREE_SEARCH_CONTAINER* const aContainer,
|
2014-02-18 21:41:27 +00:00
|
|
|
int aDeMorganConvert )
|
2017-02-21 23:09:34 +00:00
|
|
|
: DIALOG_CHOOSE_COMPONENT_BASE( aParent, wxID_ANY, aTitle ), m_search_container( aContainer ),
|
|
|
|
m_footprintPreviewPanel( nullptr )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2014-09-26 10:35:11 +00:00
|
|
|
m_parent = aParent;
|
2014-11-19 14:22:47 +00:00
|
|
|
m_deMorganConvert = aDeMorganConvert >= 0 ? aDeMorganConvert : 0;
|
|
|
|
m_external_browser_requested = false;
|
2014-02-14 08:05:04 +00:00
|
|
|
m_search_container->SetTree( m_libraryComponentTree );
|
2016-03-22 18:53:50 +00:00
|
|
|
m_componentView->SetLayoutDirection( wxLayout_LeftToRight );
|
2017-02-24 01:29:03 +00:00
|
|
|
m_dbl_click_timer = std::make_unique<wxTimer>( this );
|
2014-02-27 08:05:28 +00:00
|
|
|
|
2017-02-24 19:27:49 +00:00
|
|
|
// Search box styling. wxSearchBox can handle this, but it's buggy...
|
|
|
|
m_searchBox->SetHint( _( "Search" ) );
|
|
|
|
#if defined( __WXMAC__ ) || defined( __WINDOWS__ )
|
|
|
|
{
|
|
|
|
// On Linux, the "Search" hint disappears when the dialog is focused,
|
|
|
|
// meaning it's not present initially when the dialog opens. To ensure
|
|
|
|
// the box is understood, a search icon is also provided.
|
|
|
|
//
|
|
|
|
// The icon provided by wx is ugly on macOS and Windows, *plus* these
|
|
|
|
// ports display the "Search" hint in the empty field even when the
|
|
|
|
// field is focused. Therefore, the icon is not required on these
|
|
|
|
// platforms.
|
|
|
|
m_searchBoxIcon->Hide();
|
|
|
|
m_searchBoxIcon->GetParent()->Layout();
|
|
|
|
}
|
|
|
|
#endif // __WXMAC__
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
// Initialize footprint preview through Kiway
|
2017-02-19 12:53:02 +00:00
|
|
|
m_footprintPreviewPanel =
|
|
|
|
FOOTPRINT_PREVIEW_PANEL::InstallOnPanel( Kiway(), m_footprintView, true );
|
2014-02-22 12:39:59 +00:00
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
if( m_footprintPreviewPanel )
|
|
|
|
{
|
|
|
|
// This hides the GAL panel and shows the status label
|
|
|
|
m_footprintPreviewPanel->SetStatusText( wxEmptyString );
|
|
|
|
}
|
2014-11-19 14:22:47 +00:00
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
#ifndef KICAD_FOOTPRINT_SELECTOR
|
|
|
|
// Footprint chooser isn't implemented yet or isn't selected, don't show it.
|
|
|
|
m_chooseFootprint->Hide();
|
2017-02-24 19:27:49 +00:00
|
|
|
m_chooseFootprint->GetParent()->Layout();
|
2017-02-19 02:39:55 +00:00
|
|
|
#endif
|
2015-03-30 19:34:41 +00:00
|
|
|
|
2017-02-24 01:29:03 +00:00
|
|
|
Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this );
|
2017-02-19 02:39:55 +00:00
|
|
|
Layout();
|
2014-11-19 14:22:47 +00:00
|
|
|
Centre();
|
2017-02-21 23:09:34 +00:00
|
|
|
|
|
|
|
// Per warning in component_tree_search_container.h, this must be called
|
|
|
|
// near the end of the constructor.
|
|
|
|
m_search_container->UpdateSearchTerm( wxEmptyString );
|
2017-02-19 15:01:45 +00:00
|
|
|
updateSelection();
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-24 10:52:08 +00:00
|
|
|
DIALOG_CHOOSE_COMPONENT::~DIALOG_CHOOSE_COMPONENT()
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2014-02-24 10:52:08 +00:00
|
|
|
m_search_container->SetTree( NULL );
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2017-01-22 22:29:16 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnInitDialog( wxInitDialogEvent& event )
|
|
|
|
{
|
|
|
|
m_searchBox->SetFocus();
|
|
|
|
}
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2014-02-24 10:52:08 +00:00
|
|
|
LIB_ALIAS* DIALOG_CHOOSE_COMPONENT::GetSelectedAlias( int* aUnit ) const
|
|
|
|
{
|
|
|
|
return m_search_container->GetSelectedAlias( aUnit );
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnSearchBoxChange( wxCommandEvent& aEvent )
|
|
|
|
{
|
2014-10-09 00:49:29 +00:00
|
|
|
m_search_container->UpdateSearchTerm( m_searchBox->GetLineText( 0 ) );
|
2014-02-14 08:05:04 +00:00
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnSearchBoxEnter( wxCommandEvent& aEvent )
|
|
|
|
{
|
|
|
|
EndModal( wxID_OK ); // We are done.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::selectIfValid( const wxTreeListItem& aTreeId )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2014-02-18 21:41:27 +00:00
|
|
|
if( aTreeId.IsOk() && aTreeId != m_libraryComponentTree->GetRootItem() )
|
2017-02-19 02:39:55 +00:00
|
|
|
m_libraryComponentTree->Select( aTreeId );
|
2017-02-19 15:01:45 +00:00
|
|
|
|
|
|
|
updateSelection();
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnInterceptSearchBoxKey( wxKeyEvent& aKeyStroke )
|
|
|
|
{
|
2014-02-18 21:41:27 +00:00
|
|
|
// 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).
|
2017-02-19 02:39:55 +00:00
|
|
|
const wxTreeListItem sel = m_libraryComponentTree->GetSelection();
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
switch( aKeyStroke.GetKeyCode() )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
|
|
|
case WXK_UP:
|
2014-02-18 21:41:27 +00:00
|
|
|
selectIfValid( GetPrevItem( *m_libraryComponentTree, sel ) );
|
2014-02-14 08:05:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case WXK_DOWN:
|
2014-02-18 21:41:27 +00:00
|
|
|
selectIfValid( GetNextItem( *m_libraryComponentTree, sel ) );
|
|
|
|
break;
|
|
|
|
|
2014-10-09 00:49:29 +00:00
|
|
|
// The following keys we can only hijack if they are not needed by the textbox itself.
|
2014-02-18 21:41:27 +00:00
|
|
|
|
|
|
|
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.
|
2014-02-14 08:05:04 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2014-02-18 21:41:27 +00:00
|
|
|
aKeyStroke.Skip(); // Any other key: pass on to search box directly.
|
2014-02-14 08:05:04 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxTreeListEvent& aEvent )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
|
|
|
updateSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation( wxTreeListEvent& aEvent )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2017-02-24 01:59:15 +00:00
|
|
|
if( updateSelection() )
|
|
|
|
{
|
|
|
|
// 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.
|
|
|
|
//
|
|
|
|
// NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp.
|
|
|
|
// This isn't really feasible to bypass without a fully custom
|
|
|
|
// wxDataViewCtrl implementation, and even then might not be fully
|
|
|
|
// possible (docs are vague). To get around this, we use a one-shot
|
|
|
|
// timer to schedule the dialog close.
|
|
|
|
//
|
|
|
|
// See DIALOG_CHOOSE_COMPONENT::OnCloseTimer for the other end of this
|
|
|
|
// spaghetti noodle.
|
|
|
|
m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_COMPONENT::DblClickDelay );
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-24 01:29:03 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2017-02-24 01:29:03 +00:00
|
|
|
// Hack handler because of eaten MouseUp event. See
|
|
|
|
// DIALOG_CHOOSE_COMPONENT::OnDoubleClickTreeActivation for the beginning
|
|
|
|
// of this spaghetti noodle.
|
|
|
|
|
|
|
|
auto state = wxGetMouseState();
|
|
|
|
|
|
|
|
if( state.LeftIsDown() )
|
|
|
|
{
|
|
|
|
// Mouse hasn't been raised yet, so fire the timer again. Otherwise the
|
|
|
|
// purpose of this timer is defeated.
|
|
|
|
m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_COMPONENT::DblClickDelay );
|
|
|
|
}
|
2017-02-24 01:59:15 +00:00
|
|
|
else
|
2017-02-24 01:29:03 +00:00
|
|
|
{
|
|
|
|
EndModal( wxID_OK );
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 01:59:15 +00:00
|
|
|
|
2014-02-24 01:27:07 +00:00
|
|
|
// 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.
|
2014-02-24 10:52:08 +00:00
|
|
|
if( aEvent.GetKeyCode() == WXK_RETURN )
|
2014-02-24 01:27:07 +00:00
|
|
|
EndModal( wxID_OK ); // Dialog is done.
|
|
|
|
else
|
|
|
|
aEvent.Skip(); // Let tree handle that key for navigation.
|
|
|
|
}
|
|
|
|
|
2014-02-14 08:05:04 +00:00
|
|
|
|
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnStartComponentBrowser( wxMouseEvent& aEvent )
|
|
|
|
{
|
|
|
|
m_external_browser_requested = true;
|
|
|
|
EndModal( wxID_OK ); // We are done.
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
bool DIALOG_CHOOSE_COMPONENT::updateSelection()
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2014-02-18 21:41:27 +00:00
|
|
|
int unit = 0;
|
|
|
|
LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &unit );
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-19 16:44:48 +00:00
|
|
|
m_componentView->Refresh();
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
if( selection == NULL )
|
2017-02-19 02:39:55 +00:00
|
|
|
{
|
|
|
|
if( m_footprintPreviewPanel )
|
|
|
|
{
|
|
|
|
m_footprintPreviewPanel->SetStatusText( wxEmptyString );
|
|
|
|
}
|
|
|
|
|
2017-02-19 14:00:02 +00:00
|
|
|
m_componentDetails->SetPage( wxEmptyString );
|
2017-02-19 02:39:55 +00:00
|
|
|
|
2017-02-19 14:00:02 +00:00
|
|
|
return false;
|
2014-10-09 00:49:29 +00:00
|
|
|
}
|
|
|
|
|
2017-02-19 20:11:35 +00:00
|
|
|
m_componentDetails->SetPage( GenerateAliasInfo( selection, unit ) );
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
updateFootprint();
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::updateFootprint()
|
|
|
|
{
|
|
|
|
if( !m_footprintPreviewPanel )
|
|
|
|
return;
|
|
|
|
|
|
|
|
int dummy_unit = 0;
|
|
|
|
LIB_ALIAS* selection = m_search_container->GetSelectedAlias( &dummy_unit );
|
|
|
|
|
|
|
|
if( !selection )
|
|
|
|
return;
|
|
|
|
|
|
|
|
LIB_FIELDS fields;
|
|
|
|
selection->GetPart()->GetFields( fields );
|
|
|
|
|
|
|
|
for( auto const & field: fields )
|
|
|
|
{
|
|
|
|
if( field.GetId() != FOOTPRINT )
|
|
|
|
continue;
|
|
|
|
wxString fpname = field.GetFullText();
|
|
|
|
if( fpname == wxEmptyString )
|
|
|
|
{
|
|
|
|
m_footprintPreviewPanel->SetStatusText( _( "No footprint specified" ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_footprintPreviewPanel->ClearStatus();
|
|
|
|
m_footprintPreviewPanel->CacheFootprint( LIB_ID( fpname ) );
|
|
|
|
m_footprintPreviewPanel->DisplayFootprint( LIB_ID( fpname ) );
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnDatasheetClick( wxHtmlLinkEvent& aEvent )
|
|
|
|
{
|
|
|
|
const wxHtmlLinkInfo & info = aEvent.GetLinkInfo();
|
|
|
|
::wxLaunchDefaultBrowser( info.GetHref() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::OnHandlePreviewRepaint( wxPaintEvent& aRepaintEvent )
|
|
|
|
{
|
|
|
|
int unit = 0;
|
2014-10-09 00:49:29 +00:00
|
|
|
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;
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2014-10-09 00:49:29 +00:00
|
|
|
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 );
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
|
|
|
|
// 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.
|
2014-10-09 00:49:29 +00:00
|
|
|
void DIALOG_CHOOSE_COMPONENT::renderPreview( LIB_PART* aComponent, int aUnit )
|
2014-02-18 21:41:27 +00:00
|
|
|
{
|
|
|
|
wxPaintDC dc( m_componentView );
|
2015-08-30 14:46:34 +00:00
|
|
|
|
|
|
|
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 );
|
|
|
|
|
2017-02-20 16:57:41 +00:00
|
|
|
COLOR4D bgColor = m_parent->GetDrawBgColor();
|
2014-10-09 00:49:29 +00:00
|
|
|
|
2017-02-20 16:57:41 +00:00
|
|
|
dc.SetBackground( wxBrush( bgColor.ToColour() ) );
|
2014-02-18 21:41:27 +00:00
|
|
|
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.
|
2016-09-30 05:31:48 +00:00
|
|
|
EDA_RECT bBox = aComponent->GetUnitBoundingBox( aUnit, m_deMorganConvert );
|
2014-02-18 21:41:27 +00:00
|
|
|
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 );
|
|
|
|
|
2015-06-26 13:41:56 +00:00
|
|
|
wxPoint offset = -bBox.Centre();
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2017-02-19 18:40:26 +00:00
|
|
|
auto opts = PART_DRAW_OPTIONS::Default();
|
2017-02-19 18:43:50 +00:00
|
|
|
opts.draw_hidden_fields = false;
|
2017-02-19 18:40:26 +00:00
|
|
|
aComponent->Draw( NULL, &dc, offset, aUnit, m_deMorganConvert, opts );
|
2014-02-18 21:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
static wxTreeListItem GetPrevItem( const wxTreeListCtrl& tree, const wxTreeListItem& item )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2017-02-19 02:39:55 +00:00
|
|
|
wxTreeListItem prevItem = GetPrevSibling( tree, item );
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
if( !prevItem.IsOk() )
|
|
|
|
{
|
|
|
|
prevItem = tree.GetItemParent( item );
|
|
|
|
}
|
|
|
|
else if( tree.IsExpanded( prevItem ) )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2017-02-19 02:39:55 +00:00
|
|
|
// wxTreeListCtrl has no .GetLastChild. Simulate it
|
|
|
|
prevItem = tree.GetFirstChild( prevItem );
|
|
|
|
wxTreeListItem next;
|
|
|
|
do
|
|
|
|
{
|
|
|
|
next = tree.GetNextSibling( prevItem );
|
|
|
|
if( next.IsOk() )
|
|
|
|
{
|
|
|
|
prevItem = next;
|
|
|
|
}
|
|
|
|
} while( next.IsOk() );
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 17:16:59 +00:00
|
|
|
return prevItem;
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
static wxTreeListItem GetNextItem( const wxTreeListCtrl& tree, const wxTreeListItem& item )
|
2014-02-14 17:16:59 +00:00
|
|
|
{
|
2017-02-19 02:39:55 +00:00
|
|
|
wxTreeListItem nextItem;
|
2014-02-14 08:05:04 +00:00
|
|
|
|
2015-08-06 09:17:23 +00:00
|
|
|
if( !item.IsOk() )
|
2017-02-19 02:39:55 +00:00
|
|
|
return nextItem; // item is not valid: return a not valid wxTreeListItem
|
2015-08-06 09:17:23 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
if( tree.IsExpanded( item ) )
|
2014-02-14 08:05:04 +00:00
|
|
|
{
|
2017-02-19 02:39:55 +00:00
|
|
|
nextItem = tree.GetFirstChild( item );
|
2014-02-18 21:41:27 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-02-19 02:39:55 +00:00
|
|
|
wxTreeListItem root_cell= tree.GetRootItem();
|
2015-08-06 09:17:23 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
// Walk up levels until we find one that has a next sibling.
|
2017-02-19 02:39:55 +00:00
|
|
|
for ( wxTreeListItem walk = item; walk.IsOk(); walk = tree.GetItemParent( walk ) )
|
2014-02-18 21:41:27 +00:00
|
|
|
{
|
2015-08-06 09:17:23 +00:00
|
|
|
if( walk == root_cell ) // the root cell (not displayed) is reached
|
|
|
|
break; // Exit (calling GetNextSibling( root_cell ) crashes.
|
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
nextItem = tree.GetNextSibling( walk );
|
2014-10-09 00:49:29 +00:00
|
|
|
|
2014-02-18 21:41:27 +00:00
|
|
|
if( nextItem.IsOk() )
|
|
|
|
break;
|
|
|
|
}
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 17:16:59 +00:00
|
|
|
return nextItem;
|
2014-02-14 08:05:04 +00:00
|
|
|
}
|
2017-02-19 02:39:55 +00:00
|
|
|
|
2017-02-19 14:00:02 +00:00
|
|
|
|
2017-02-19 02:39:55 +00:00
|
|
|
static wxTreeListItem GetPrevSibling( const wxTreeListCtrl& tree, const wxTreeListItem& item )
|
|
|
|
{
|
|
|
|
// Why wxTreeListCtrl has no GetPrevSibling when it does have GetNextSibling
|
|
|
|
// is beyond me. wxTreeCtrl has this.
|
|
|
|
|
|
|
|
wxTreeListItem last_item;
|
|
|
|
wxTreeListItem parent = tree.GetItemParent( item );
|
|
|
|
|
|
|
|
if( !parent.IsOk() )
|
|
|
|
return last_item; // invalid signifies not found
|
|
|
|
|
|
|
|
last_item = tree.GetFirstChild( parent );
|
|
|
|
while( last_item.IsOk() )
|
|
|
|
{
|
|
|
|
wxTreeListItem next_item = tree.GetNextSibling( last_item );
|
|
|
|
if( next_item == item )
|
|
|
|
{
|
|
|
|
return last_item;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
last_item = next_item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return last_item;
|
|
|
|
}
|