Browse Source
Add footprint select dropdown to component chooser, serious refactoring
Add footprint select dropdown to component chooser, serious refactoring
- DIALOG_CHOOSE_COMPONENT has footprint select widget - FOOTPRINT_SELECT_WIDGET - FOOTPRINT_CHOICE widget (customized wxComboCtrl) - FOOTPRINT_FILTER class - FOOTPRINT_INFO rework: - FOOTPRINT_ASYNC_LOADER to load without freezing UI - Rewrite loader threads as queue-driven thread pool - Make FOOTPRINT_INFO available via kiway - FP_LIB_TABLE::PrefetchLib - Access to global fp-lib-table via kiway - SYNC_QUEUE threadsafe queue template - Remove KICAD_FOOTPRINT_SELECTOR build optionpull/3/merge
46 changed files with 2620 additions and 653 deletions
-
6CMakeLists.txt
-
5common/CMakeLists.txt
-
20common/eda_pattern_match.cpp
-
230common/footprint_filter.cpp
-
330common/footprint_info.cpp
-
10common/fp_lib_table.cpp
-
45common/project.cpp
-
262common/widgets/footprint_choice.cpp
-
304common/widgets/footprint_select_widget.cpp
-
6cvpcb/autosel.cpp
-
6cvpcb/class_DisplayFootprintsFrame.cpp
-
69cvpcb/class_footprints_listbox.cpp
-
25cvpcb/cvpcb_mainframe.cpp
-
5cvpcb/cvpcb_mainframe.h
-
12cvpcb/listview_classes.h
-
5cvpcb/readwrite_dlgs.cpp
-
247eeschema/dialogs/dialog_choose_component.cpp
-
53eeschema/dialogs/dialog_choose_component.h
-
12eeschema/dialogs/dialog_edit_component_in_schematic.cpp
-
127eeschema/getpart.cpp
-
20eeschema/libedit.cpp
-
13eeschema/onleftclick.cpp
-
63eeschema/sch_base_frame.h
-
12eeschema/schframe.h
-
4eeschema/viewlibs.cpp
-
14include/eda_pattern_match.h
-
148include/footprint_filter.h
-
288include/footprint_info.h
-
15include/fp_lib_table.h
-
49include/kiface_ids.h
-
14include/project.h
-
113include/sync_queue.h
-
113include/widgets/footprint_choice.h
-
161include/widgets/footprint_select_widget.h
-
1pcbnew/CMakeLists.txt
-
230pcbnew/footprint_info_impl.cpp
-
94pcbnew/footprint_info_impl.h
-
24pcbnew/github/github_plugin.cpp
-
8pcbnew/github/github_plugin.h
-
24pcbnew/io_mgr.h
-
7pcbnew/loadcmp.cpp
-
15pcbnew/modview_frame.cpp
-
32pcbnew/pcb_netlist.cpp
-
8pcbnew/pcb_netlist.h
-
15pcbnew/pcbnew.cpp
-
9pcbnew/plugin.cpp
@ -0,0 +1,230 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <footprint_filter.h>
|
|||
#include <make_unique.h>
|
|||
#include <stdexcept>
|
|||
|
|||
using FOOTPRINT_FILTER_IT = FOOTPRINT_FILTER::ITERATOR; |
|||
|
|||
|
|||
FOOTPRINT_FILTER::ITERATOR::ITERATOR() : m_pos( 0 ), m_filter( nullptr ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER::ITERATOR::ITERATOR( FOOTPRINT_FILTER_IT const& aOther ) |
|||
: m_pos( aOther.m_pos ), m_filter( aOther.m_filter ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER::ITERATOR::ITERATOR( FOOTPRINT_FILTER& aFilter ) |
|||
: m_pos( (size_t) -1 ), m_filter( &aFilter ) |
|||
{ |
|||
increment(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER_IT::increment() |
|||
{ |
|||
bool found = false; |
|||
|
|||
if( !m_filter || !m_filter->m_list || m_filter->m_list->GetCount() == 0 ) |
|||
{ |
|||
m_pos = 0; |
|||
return; |
|||
} |
|||
|
|||
auto filter_type = m_filter->m_filter_type; |
|||
auto list = m_filter->m_list; |
|||
auto& lib_name = m_filter->m_lib_name; |
|||
auto& filter_pattern = m_filter->m_filter_pattern; |
|||
auto& filter = m_filter->m_filter; |
|||
|
|||
for( ++m_pos; m_pos < list->GetCount() && !found; ++m_pos ) |
|||
{ |
|||
found = true; |
|||
|
|||
if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_LIBRARY ) && !lib_name.IsEmpty() |
|||
&& !list->GetItem( m_pos ).InLibrary( lib_name ) ) |
|||
found = false; |
|||
|
|||
if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_COMPONENT_KEYWORD ) |
|||
&& !FootprintFilterMatch( list->GetItem( m_pos ) ) ) |
|||
found = false; |
|||
|
|||
if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_PIN_COUNT ) |
|||
&& !PinCountMatch( list->GetItem( m_pos ) ) ) |
|||
found = false; |
|||
|
|||
if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_NAME ) && !filter_pattern.IsEmpty() ) |
|||
{ |
|||
wxString currname; |
|||
|
|||
// If the search string contains a ':' character,
|
|||
// include the library name in the search string
|
|||
// e.g. LibName:FootprintName
|
|||
if( filter_pattern.Contains( ":" ) ) |
|||
currname = list->GetItem( m_pos ).GetNickname().Lower() + ":"; |
|||
|
|||
currname += list->GetItem( m_pos ).GetFootprintName().Lower(); |
|||
|
|||
if( filter.Find( currname ) == EDA_PATTERN_NOT_FOUND ) |
|||
found = false; |
|||
} |
|||
|
|||
if( filter_type == FOOTPRINT_FILTER::UNFILTERED_FP_LIST ) |
|||
{ |
|||
// override
|
|||
found = true; |
|||
} |
|||
} |
|||
|
|||
// for loop will stop one past the correct item
|
|||
if( found ) |
|||
--m_pos; |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_FILTER_IT::equal( FOOTPRINT_FILTER_IT const& aOther ) const |
|||
{ |
|||
// Invalid iterators are always equal
|
|||
return ( m_pos == aOther.m_pos ) && ( m_filter == aOther.m_filter || m_pos == (size_t) -1 ); |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_INFO& FOOTPRINT_FILTER_IT::dereference() const |
|||
{ |
|||
if( m_filter && m_filter->m_list && m_pos < m_filter->m_list->GetCount() ) |
|||
return m_filter->m_list->GetItem( m_pos ); |
|||
else |
|||
throw std::out_of_range( "Attempt to dereference past FOOTPRINT_FILTER::end()" ); |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_FILTER_IT::FootprintFilterMatch( FOOTPRINT_INFO& aItem ) |
|||
{ |
|||
if( m_filter->m_footprint_filters.empty() ) |
|||
return true; |
|||
|
|||
// The matching is case insensitive
|
|||
wxString name = ""; |
|||
|
|||
EDA_PATTERN_MATCH_WILDCARD patternFilter; |
|||
|
|||
for( auto const& each_filter : m_filter->m_footprint_filters ) |
|||
{ |
|||
// If the filter contains a ':' character, include the library name in the pattern
|
|||
if( each_filter->GetPattern().Contains( ":" ) ) |
|||
{ |
|||
name = aItem.GetNickname().Lower() + ":"; |
|||
} |
|||
|
|||
name += aItem.GetFootprintName().Lower(); |
|||
|
|||
if( each_filter->Find( name ) != EDA_PATTERN_NOT_FOUND ) |
|||
{ |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_FILTER_IT::PinCountMatch( FOOTPRINT_INFO& aItem ) |
|||
{ |
|||
return (unsigned) m_filter->m_pin_count == aItem.GetUniquePadCount(); |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER::FOOTPRINT_FILTER( FOOTPRINT_LIST& aList ) : FOOTPRINT_FILTER() |
|||
{ |
|||
SetList( aList ); |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER::FOOTPRINT_FILTER() |
|||
: m_list( nullptr ), m_pin_count( -1 ), m_filter_type( UNFILTERED_FP_LIST ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::SetList( FOOTPRINT_LIST& aList ) |
|||
{ |
|||
m_list = &aList; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::ClearFilters() |
|||
{ |
|||
m_filter_type = UNFILTERED_FP_LIST; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::FilterByLibrary( wxString const& aLibName ) |
|||
{ |
|||
m_lib_name = aLibName; |
|||
m_filter_type |= FILTERING_BY_LIBRARY; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::FilterByPinCount( int aPinCount ) |
|||
{ |
|||
m_pin_count = aPinCount; |
|||
m_filter_type |= FILTERING_BY_PIN_COUNT; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::FilterByFootprintFilters( wxArrayString const& aFilters ) |
|||
{ |
|||
m_footprint_filters.clear(); |
|||
|
|||
for( auto const& each_pattern : aFilters ) |
|||
{ |
|||
m_footprint_filters.push_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() ); |
|||
wxASSERT( m_footprint_filters.back()->SetPattern( each_pattern.Lower() ) ); |
|||
} |
|||
|
|||
m_filter_type |= FILTERING_BY_COMPONENT_KEYWORD; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_FILTER::FilterByPattern( wxString const& aPattern ) |
|||
{ |
|||
m_filter_pattern = aPattern; |
|||
m_filter.SetPattern( aPattern.Lower() ); |
|||
m_filter_type |= FILTERING_BY_NAME; |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER_IT FOOTPRINT_FILTER::begin() |
|||
{ |
|||
return FOOTPRINT_FILTER_IT( *this ); |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_FILTER_IT FOOTPRINT_FILTER::end() |
|||
{ |
|||
FOOTPRINT_FILTER_IT end_it( *this ); |
|||
end_it.m_pos = m_list->GetCount(); |
|||
return end_it; |
|||
} |
@ -0,0 +1,262 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <functional>
|
|||
#include <widgets/footprint_choice.h>
|
|||
#include <wx/dc.h>
|
|||
#include <wx/pen.h>
|
|||
|
|||
wxDEFINE_EVENT( EVT_INTERACTIVE_CHOICE, wxCommandEvent ); |
|||
|
|||
|
|||
wxColour FOOTPRINT_CHOICE::m_grey( 0x808080 ); |
|||
|
|||
|
|||
FOOTPRINT_CHOICE::FOOTPRINT_CHOICE( wxWindow* aParent, int aId ) |
|||
: wxOwnerDrawnComboBox( aParent, aId, wxEmptyString, wxDefaultPosition, wxDefaultSize, |
|||
/* n */ 0, /* choices */ nullptr, wxCB_READONLY ), |
|||
m_last_selection( 0 ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_CHOICE::~FOOTPRINT_CHOICE() |
|||
{ |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::DoSetPopupControl( wxComboPopup* aPopup ) |
|||
{ |
|||
using namespace std::placeholders; |
|||
wxOwnerDrawnComboBox::DoSetPopupControl( aPopup ); |
|||
|
|||
// Bind events to intercept selections, so the separator can be made nonselectable.
|
|||
|
|||
GetVListBoxComboPopup()->Bind( wxEVT_MOTION, &FOOTPRINT_CHOICE::TryVetoMouse, this ); |
|||
GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DOWN, &FOOTPRINT_CHOICE::TryVetoMouse, this ); |
|||
GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::TryVetoMouse, this ); |
|||
GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::OnMouseUp, this ); |
|||
GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DCLICK, &FOOTPRINT_CHOICE::TryVetoMouse, this ); |
|||
GetVListBoxComboPopup()->Bind( |
|||
wxEVT_LISTBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, true ) ); |
|||
Bind( wxEVT_COMBOBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, false ) ); |
|||
GetVListBoxComboPopup()->Bind( |
|||
wxEVT_CHAR_HOOK, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, true ) ); |
|||
GetVListBoxComboPopup()->Bind( wxEVT_CHAR_HOOK, &FOOTPRINT_CHOICE::OnKeyUp, this ); |
|||
Bind( wxEVT_KEY_DOWN, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, false ) ); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::OnDrawItem( wxDC& aDC, wxRect const& aRect, int aItem, int aFlags ) const |
|||
{ |
|||
wxString text = SafeGetString( aItem ); |
|||
|
|||
if( text == wxEmptyString ) |
|||
{ |
|||
wxPen pen( m_grey, 1, wxPENSTYLE_SOLID ); |
|||
|
|||
aDC.SetPen( pen ); |
|||
aDC.DrawLine( aRect.x, aRect.y + aRect.height / 2, aRect.x + aRect.width, |
|||
aRect.y + aRect.height / 2 ); |
|||
} |
|||
else |
|||
{ |
|||
wxCoord x, y; |
|||
|
|||
if( aFlags & wxODCB_PAINTING_CONTROL ) |
|||
{ |
|||
x = aRect.x + GetMargins().x; |
|||
y = ( aRect.height - aDC.GetCharHeight() ) / 2 + aRect.y; |
|||
} |
|||
else |
|||
{ |
|||
x = aRect.x + 2; |
|||
y = aRect.y; |
|||
} |
|||
|
|||
// If this item has a footprint and that footprint has a ":" delimiter, find the
|
|||
// library component, then find that in the display string and grey it out.
|
|||
|
|||
size_t start_grey = 0; |
|||
size_t end_grey = 0; |
|||
|
|||
wxString lib = static_cast<wxStringClientData*>( GetClientObject( aItem ) )->GetData(); |
|||
size_t colon_index = lib.rfind( ':' ); |
|||
|
|||
if( colon_index != wxString::npos ) |
|||
{ |
|||
wxString library_part = lib.SubString( 0, colon_index ); |
|||
size_t library_index = text.rfind( library_part ); |
|||
|
|||
if( library_index != wxString::npos ) |
|||
{ |
|||
start_grey = library_index; |
|||
end_grey = start_grey + library_part.Length(); |
|||
} |
|||
} |
|||
|
|||
if( start_grey != end_grey && !( aFlags & wxODCB_PAINTING_SELECTED ) ) |
|||
{ |
|||
x = DrawTextFragment( aDC, x, y, text.SubString( 0, start_grey - 1 ) ); |
|||
|
|||
wxColour standard_color = aDC.GetTextForeground(); |
|||
|
|||
aDC.SetTextForeground( m_grey ); |
|||
x = DrawTextFragment( aDC, x, y, text.SubString( start_grey, end_grey - 1 ) ); |
|||
|
|||
aDC.SetTextForeground( standard_color ); |
|||
x = DrawTextFragment( aDC, x, y, text.SubString( end_grey, text.Length() - 1 ) ); |
|||
} |
|||
else |
|||
{ |
|||
aDC.DrawText( text, x, y ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
wxCoord FOOTPRINT_CHOICE::OnMeasureItem( size_t aItem ) const |
|||
{ |
|||
if( SafeGetString( aItem ) == "" ) |
|||
return 11; |
|||
else |
|||
return wxOwnerDrawnComboBox::OnMeasureItem( aItem ); |
|||
} |
|||
|
|||
|
|||
wxCoord FOOTPRINT_CHOICE::OnMeasureItemWidth( size_t aItem ) const |
|||
{ |
|||
if( SafeGetString( aItem ) == "" ) |
|||
return GetTextRect().GetWidth() - 2; |
|||
else |
|||
return wxOwnerDrawnComboBox::OnMeasureItemWidth( aItem ); |
|||
} |
|||
|
|||
|
|||
wxCoord FOOTPRINT_CHOICE::DrawTextFragment( wxDC& aDC, wxCoord x, wxCoord y, wxString const& aText ) |
|||
{ |
|||
aDC.DrawText( aText, x, y ); |
|||
return x + aDC.GetTextExtent( aText ).GetWidth(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::TryVetoMouse( wxMouseEvent& aEvent ) |
|||
{ |
|||
int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y ); |
|||
|
|||
if( SafeGetString( item ) != "" ) |
|||
aEvent.Skip(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::OnMouseUp( wxMouseEvent& aEvent ) |
|||
{ |
|||
int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y ); |
|||
|
|||
wxCommandEvent evt( EVT_INTERACTIVE_CHOICE ); |
|||
evt.SetInt( item ); |
|||
wxPostEvent( this, evt ); |
|||
|
|||
aEvent.Skip(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::OnKeyUp( wxKeyEvent& aEvent ) |
|||
{ |
|||
int item = GetSelectionEither( true ); |
|||
|
|||
if( aEvent.GetKeyCode() == WXK_RETURN ) |
|||
{ |
|||
wxCommandEvent evt( EVT_INTERACTIVE_CHOICE ); |
|||
evt.SetInt( item ); |
|||
wxPostEvent( this, evt ); |
|||
} |
|||
|
|||
aEvent.Skip(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::TryVetoSelect( wxCommandEvent& aEvent, bool aInner ) |
|||
{ |
|||
int sel = GetSelectionEither( aInner ); |
|||
|
|||
if( sel >= 0 && sel < (int) GetCount() ) |
|||
{ |
|||
wxString text = SafeGetString( sel ); |
|||
|
|||
if( text == "" ) |
|||
SetSelectionEither( aInner, m_last_selection ); |
|||
else |
|||
{ |
|||
m_last_selection = sel; |
|||
aEvent.Skip(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::TrySkipSeparator( wxKeyEvent& aEvent, bool aInner ) |
|||
{ |
|||
int key = aEvent.GetKeyCode(); |
|||
int sel = GetSelectionEither( aInner ); |
|||
int new_sel = sel; |
|||
|
|||
if( key == WXK_UP && SafeGetString( sel - 1 ) == wxEmptyString ) |
|||
{ |
|||
new_sel = sel - 2; |
|||
} |
|||
else if( key == WXK_DOWN && SafeGetString( sel + 1 ) == wxEmptyString ) |
|||
{ |
|||
new_sel = sel + 2; |
|||
} |
|||
|
|||
if( new_sel != sel ) |
|||
SetSelectionEither( aInner, new_sel ); |
|||
else |
|||
aEvent.Skip(); |
|||
} |
|||
|
|||
|
|||
wxString FOOTPRINT_CHOICE::SafeGetString( int aItem ) const |
|||
{ |
|||
if( aItem >= 0 && aItem < (int) GetCount() ) |
|||
return GetVListBoxComboPopup()->GetString( aItem ); |
|||
else |
|||
return wxEmptyString; |
|||
} |
|||
|
|||
|
|||
int FOOTPRINT_CHOICE::GetSelectionEither( bool aInner ) const |
|||
{ |
|||
if( aInner ) |
|||
return GetVListBoxComboPopup()->wxVListBox::GetSelection(); |
|||
else |
|||
return GetSelection(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_CHOICE::SetSelectionEither( bool aInner, int aSel ) |
|||
{ |
|||
if( aSel >= 0 && aSel < (int) GetCount() ) |
|||
{ |
|||
if( aInner ) |
|||
return GetVListBoxComboPopup()->wxVListBox::SetSelection( aSel ); |
|||
else |
|||
return SetSelection( aSel ); |
|||
} |
|||
} |
@ -0,0 +1,304 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
#include <dialog_shim.h>
|
|||
#include <kiway.h>
|
|||
#include <kiway_player.h>
|
|||
#include <make_unique.h>
|
|||
#include <project.h>
|
|||
#include <widgets/footprint_choice.h>
|
|||
#include <widgets/footprint_select_widget.h>
|
|||
|
|||
#include <functional>
|
|||
#include <wx/combo.h>
|
|||
#include <wx/gauge.h>
|
|||
#include <wx/odcombo.h>
|
|||
#include <wx/simplebook.h>
|
|||
#include <wx/sizer.h>
|
|||
#include <wx/timer.h>
|
|||
#include <wx/utils.h>
|
|||
#include <wx/wupdlock.h>
|
|||
|
|||
|
|||
/**
|
|||
* Fixed positions for standard items in the list |
|||
*/ |
|||
enum |
|||
{ |
|||
POS_DEFAULT, |
|||
POS_OTHER, |
|||
POS_SEPARATOR |
|||
}; |
|||
|
|||
|
|||
/**
|
|||
* Page numbers in the wxSimplebook |
|||
*/ |
|||
enum |
|||
{ |
|||
PAGE_PROGRESS, |
|||
PAGE_SELECT |
|||
}; |
|||
|
|||
|
|||
wxDEFINE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent ); |
|||
|
|||
|
|||
FOOTPRINT_SELECT_WIDGET::FOOTPRINT_SELECT_WIDGET( wxWindow* aParent, |
|||
FOOTPRINT_ASYNC_LOADER& aLoader, std::unique_ptr<FOOTPRINT_LIST>& aFpList, bool aUpdate, |
|||
int aMaxItems ) |
|||
: wxPanel( aParent ), |
|||
m_kiway( nullptr ), |
|||
m_update( aUpdate ), |
|||
m_finished_loading( false ), |
|||
m_max_items( aMaxItems ), |
|||
m_last_item( 0 ), |
|||
m_fp_loader( aLoader ), |
|||
m_fp_list( aFpList ) |
|||
{ |
|||
m_sizer = new wxBoxSizer( wxVERTICAL ); |
|||
m_progress_timer = std::make_unique<wxTimer>( this ); |
|||
m_book = new wxSimplebook( this, wxID_ANY ); |
|||
m_progress_ctrl = new wxGauge( m_book, wxID_ANY, 100 ); |
|||
m_fp_sel_ctrl = new FOOTPRINT_CHOICE( m_book, wxID_ANY ); |
|||
|
|||
m_book->SetEffect( wxSHOW_EFFECT_BLEND ); |
|||
m_book->AddPage( m_progress_ctrl, "", true ); |
|||
m_book->AddPage( m_fp_sel_ctrl, "", false ); |
|||
m_sizer->Add( m_book, 1, wxEXPAND | wxALL, 5 ); |
|||
|
|||
SetSizer( m_sizer ); |
|||
Layout(); |
|||
m_sizer->Fit( this ); |
|||
|
|||
Bind( wxEVT_TIMER, &FOOTPRINT_SELECT_WIDGET::OnProgressTimer, this, m_progress_timer->GetId() ); |
|||
m_fp_sel_ctrl->Bind( wxEVT_COMBOBOX, &FOOTPRINT_SELECT_WIDGET::OnComboBox, this ); |
|||
m_fp_sel_ctrl->Bind( |
|||
EVT_INTERACTIVE_CHOICE, &FOOTPRINT_SELECT_WIDGET::OnComboInteractive, this ); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::Load( KIWAY& aKiway, PROJECT& aProject ) |
|||
{ |
|||
m_kiway = &aKiway; |
|||
auto fp_lib_table = aProject.PcbFootprintLibs( aKiway ); |
|||
|
|||
if( m_fp_loader.GetProgress() == 0 || !m_fp_loader.IsSameTable( fp_lib_table ) ) |
|||
{ |
|||
m_fp_list = FOOTPRINT_LIST::GetInstance( aKiway ); |
|||
m_fp_loader.SetList( &*m_fp_list ); |
|||
m_fp_loader.Start( fp_lib_table ); |
|||
} |
|||
|
|||
m_progress_timer->Start( 200 ); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::OnProgressTimer( wxTimerEvent& aEvent ) |
|||
{ |
|||
int prog = m_fp_loader.GetProgress(); |
|||
m_progress_ctrl->SetValue( prog ); |
|||
|
|||
if( prog == 100 ) |
|||
{ |
|||
wxBusyCursor busy; |
|||
|
|||
m_fp_loader.Join(); |
|||
m_fp_filter.SetList( *m_fp_list ); |
|||
m_progress_timer->Stop(); |
|||
|
|||
m_book->SetSelection( PAGE_SELECT ); |
|||
m_finished_loading = true; |
|||
|
|||
if( m_update ) |
|||
UpdateList(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::OnComboBox( wxCommandEvent& aEvent ) |
|||
{ |
|||
wxCommandEvent evt( EVT_FOOTPRINT_SELECTED ); |
|||
int sel = m_fp_sel_ctrl->GetSelection(); |
|||
|
|||
switch( sel ) |
|||
{ |
|||
case wxNOT_FOUND: return; |
|||
|
|||
case POS_SEPARATOR: |
|||
// User somehow managed to select the separator. This should not be
|
|||
// possible, but just in case... deselect it
|
|||
m_fp_sel_ctrl->SetSelection( m_last_item ); |
|||
break; |
|||
|
|||
case POS_OTHER: |
|||
// When POS_OTHER is selected, a dialog should be shown. However, we don't want to
|
|||
// do this ALL the time, as some times (e.g. when moving around with the arrow keys)
|
|||
// it could be very annoying. Therefore showing the picker is done from the custom
|
|||
// "interactive select" event on FOOTPRINT_CHOICE, which only fires for more direct
|
|||
// choice actions.
|
|||
break; |
|||
|
|||
default: |
|||
{ |
|||
wxStringClientData* clientdata = |
|||
static_cast<wxStringClientData*>( m_fp_sel_ctrl->GetClientObject( sel ) ); |
|||
wxASSERT( clientdata ); |
|||
|
|||
evt.SetString( clientdata->GetData() ); |
|||
wxPostEvent( this, evt ); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::OnComboInteractive( wxCommandEvent& aEvent ) |
|||
{ |
|||
if( aEvent.GetInt() == POS_OTHER && !m_fp_sel_ctrl->IsPopupShown() ) |
|||
{ |
|||
DoOther(); |
|||
} |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::DoOther() |
|||
{ |
|||
wxCommandEvent evt( EVT_FOOTPRINT_SELECTED ); |
|||
|
|||
wxString fpname = ShowPicker(); |
|||
m_other_footprint = fpname; |
|||
UpdateList(); |
|||
m_fp_sel_ctrl->SetSelection( POS_OTHER ); |
|||
m_last_item = POS_OTHER; |
|||
|
|||
evt.SetString( m_other_footprint ); |
|||
wxPostEvent( this, evt ); |
|||
} |
|||
|
|||
|
|||
wxString FOOTPRINT_SELECT_WIDGET::ShowPicker() |
|||
{ |
|||
wxString fpname; |
|||
wxWindow* parent = ::wxGetTopLevelParent( this ); |
|||
DIALOG_SHIM* dsparent = dynamic_cast<DIALOG_SHIM*>( parent ); |
|||
|
|||
// Only quasimodal dialogs can launch modal kiface dialogs. Otherwise the
|
|||
// event loop goes all silly.
|
|||
wxASSERT( !dsparent || dsparent->IsQuasiModal() ); |
|||
|
|||
auto frame = m_kiway->Player( FRAME_PCB_MODULE_VIEWER_MODAL, true ); |
|||
|
|||
if( !frame->ShowModal( &fpname, parent ) ) |
|||
{ |
|||
fpname = wxEmptyString; |
|||
} |
|||
|
|||
frame->Destroy(); |
|||
|
|||
return fpname; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::ClearFilters() |
|||
{ |
|||
m_fp_filter.ClearFilters(); |
|||
m_default_footprint.Clear(); |
|||
m_other_footprint.Clear(); |
|||
m_zero_filter = false; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::FilterByPinCount( int aPinCount ) |
|||
{ |
|||
m_fp_filter.FilterByPinCount( aPinCount ); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::FilterByFootprintFilters( |
|||
wxArrayString const& aFilters, bool aZeroFilters ) |
|||
{ |
|||
if( aZeroFilters && aFilters.size() == 0 ) |
|||
m_zero_filter = true; |
|||
else |
|||
m_zero_filter = false; |
|||
|
|||
m_fp_filter.FilterByFootprintFilters( aFilters ); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::SetDefaultFootprint( wxString const& aFp ) |
|||
{ |
|||
m_default_footprint = aFp; |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_SELECT_WIDGET::UpdateList() |
|||
{ |
|||
int n_items = 0; |
|||
|
|||
if( !m_fp_list || !m_finished_loading ) |
|||
return false; |
|||
|
|||
wxWindowUpdateLocker lock( m_fp_sel_ctrl ); |
|||
m_fp_sel_ctrl->Clear(); |
|||
|
|||
// Be careful adding items! "Default" must occupy POS_DEFAULT,
|
|||
// "Other" must occupy POS_OTHER, and the separator must occupy POS_SEPARATOR.
|
|||
|
|||
m_fp_sel_ctrl->Append( m_default_footprint.IsEmpty() ? |
|||
_( "No default footprint" ) : |
|||
"[" + _( "Default" ) + "] " + m_default_footprint, |
|||
new wxStringClientData( m_default_footprint ) ); |
|||
|
|||
m_fp_sel_ctrl->Append( m_other_footprint.IsEmpty() ? |
|||
_( "Other..." ) : |
|||
"[" + _( "Other..." ) + "] " + m_other_footprint, |
|||
new wxStringClientData( m_other_footprint ) ); |
|||
|
|||
m_fp_sel_ctrl->Append( "", new wxStringClientData( "" ) ); |
|||
|
|||
if( !m_zero_filter ) |
|||
{ |
|||
for( auto& fpinfo : m_fp_filter ) |
|||
{ |
|||
wxString display_name( fpinfo.GetNickname() + ":" + fpinfo.GetFootprintName() ); |
|||
|
|||
m_fp_sel_ctrl->Append( display_name, new wxStringClientData( display_name ) ); |
|||
++n_items; |
|||
|
|||
if( n_items >= m_max_items ) |
|||
break; |
|||
} |
|||
} |
|||
|
|||
SelectDefault(); |
|||
return true; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_SELECT_WIDGET::SelectDefault() |
|||
{ |
|||
m_fp_sel_ctrl->SetSelection( POS_DEFAULT ); |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_SELECT_WIDGET::Enable( bool aEnable ) |
|||
{ |
|||
return m_fp_sel_ctrl->Enable( aEnable ); |
|||
} |
@ -0,0 +1,148 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef FOOTPRINT_FILTER_H |
|||
#define FOOTPRINT_FILTER_H |
|||
|
|||
#include <boost/iterator/iterator_facade.hpp> |
|||
#include <eda_pattern_match.h> |
|||
#include <footprint_info.h> |
|||
|
|||
|
|||
/** |
|||
* Footprint display filter. Takes a list of footprints and filtering settings, |
|||
* and provides an iterable view of the filtered data. |
|||
*/ |
|||
class FOOTPRINT_FILTER |
|||
{ |
|||
public: |
|||
/** |
|||
* Construct a filter. |
|||
* |
|||
* @param aList - unfiltered list of footprints |
|||
*/ |
|||
FOOTPRINT_FILTER( FOOTPRINT_LIST& aList ); |
|||
|
|||
/** |
|||
* Construct a filter without assigning a footprint list. The filter MUST NOT |
|||
* be iterated over until SetList() is called. |
|||
*/ |
|||
FOOTPRINT_FILTER(); |
|||
|
|||
/** |
|||
* Set the list to filter. |
|||
*/ |
|||
void SetList( FOOTPRINT_LIST& aList ); |
|||
|
|||
/** |
|||
* Clear all filter criteria. |
|||
*/ |
|||
void ClearFilters(); |
|||
|
|||
/** |
|||
* Add library name to filter criteria. |
|||
*/ |
|||
void FilterByLibrary( wxString const& aLibName ); |
|||
|
|||
/** |
|||
* Set a pin count to filter by. |
|||
*/ |
|||
void FilterByPinCount( int aPinCount ); |
|||
|
|||
/** |
|||
* Set a list of footprint filters to filter by. |
|||
*/ |
|||
void FilterByFootprintFilters( wxArrayString const& aFilters ); |
|||
|
|||
/** |
|||
* Add a pattern to filter by name, including wildcards and optionally a colon-delimited |
|||
* library name. |
|||
*/ |
|||
void FilterByPattern( wxString const& aPattern ); |
|||
|
|||
/** |
|||
* Inner iterator class returned by begin() and end(). |
|||
*/ |
|||
class ITERATOR |
|||
: public boost::iterator_facade<ITERATOR, FOOTPRINT_INFO, boost::forward_traversal_tag> |
|||
{ |
|||
public: |
|||
ITERATOR(); |
|||
ITERATOR( ITERATOR const& aOther ); |
|||
ITERATOR( FOOTPRINT_FILTER& aFilter ); |
|||
|
|||
private: |
|||
friend class boost::iterator_core_access; |
|||
friend class FOOTPRINT_FILTER; |
|||
|
|||
void increment(); |
|||
bool equal( ITERATOR const& aOther ) const; |
|||
FOOTPRINT_INFO& dereference() const; |
|||
|
|||
size_t m_pos; |
|||
FOOTPRINT_FILTER* m_filter; |
|||
|
|||
/** |
|||
* Check if the stored component matches an item by footprint filter. |
|||
*/ |
|||
bool FootprintFilterMatch( FOOTPRINT_INFO& aItem ); |
|||
|
|||
/** |
|||
* Check if the stored component matches an item by pin count. |
|||
*/ |
|||
bool PinCountMatch( FOOTPRINT_INFO& aItem ); |
|||
}; |
|||
|
|||
/** |
|||
* Get an iterator to the beginning of the filtered view. |
|||
*/ |
|||
ITERATOR begin(); |
|||
|
|||
/** |
|||
* Get an iterator to the end of the filtered view. The end iterator is |
|||
* invalid and may not be dereferenced, only compared against. |
|||
*/ |
|||
ITERATOR end(); |
|||
|
|||
private: |
|||
/** |
|||
* Filter setting constants. The filter type is a bitwise OR of these flags, |
|||
* and only footprints matching all selected filter types are shown. |
|||
*/ |
|||
enum FP_FILTER_T : int |
|||
{ |
|||
UNFILTERED_FP_LIST = 0, |
|||
FILTERING_BY_COMPONENT_KEYWORD = 0x0001, |
|||
FILTERING_BY_PIN_COUNT = 0x0002, |
|||
FILTERING_BY_LIBRARY = 0x0004, |
|||
FILTERING_BY_NAME = 0x0008 |
|||
}; |
|||
|
|||
FOOTPRINT_LIST* m_list; |
|||
|
|||
wxString m_lib_name; |
|||
wxString m_filter_pattern; |
|||
int m_pin_count; |
|||
int m_filter_type; |
|||
EDA_PATTERN_MATCH_WILDCARD m_filter; |
|||
|
|||
std::vector<std::unique_ptr<EDA_PATTERN_MATCH>> m_footprint_filters; |
|||
}; |
|||
|
|||
#endif // FOOTPRINT_FILTER_H |
@ -0,0 +1,49 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 |
|||
*/ |
|||
|
|||
#ifndef KIFACE_IDS_H |
|||
#define KIFACE_IDS_H |
|||
|
|||
/** |
|||
* IDs of objects that may be returned by KIFACE::IfaceOrAddress. |
|||
*/ |
|||
enum KIFACE_ADDR_ID : int |
|||
{ |
|||
INVALID, |
|||
|
|||
/** |
|||
* Return a new instance of FOOTPRINT_LIST from pcbnew. |
|||
* Type is FOOTPRINT_LIST* |
|||
* Caller takes ownership |
|||
*/ |
|||
KIFACE_NEW_FOOTPRINT_LIST, |
|||
|
|||
/** |
|||
* Return a new FP_LIB_TABLE copying the global table. |
|||
* Type is FP_LIB_TABLE* |
|||
* Caller takes ownership |
|||
*/ |
|||
KIFACE_G_FOOTPRINT_TABLE, ///< |
|||
}; |
|||
|
|||
#endif // KIFACE_IDS |
@ -0,0 +1,113 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef SYNC_QUEUE_H |
|||
#define SYNC_QUEUE_H |
|||
|
|||
#include <boost/optional.hpp> |
|||
#include <mutex> |
|||
#include <queue> |
|||
|
|||
/** |
|||
* Synchronized, locking queue. Safe for multiple producer/multiple consumer environments with |
|||
* nontrivial data (though bear in mind data needs to be copied in and out). |
|||
*/ |
|||
template <typename T> class SYNC_QUEUE |
|||
{ |
|||
typedef std::lock_guard<std::mutex> GUARD; |
|||
|
|||
std::queue<T> m_queue; |
|||
mutable std::mutex m_mutex; |
|||
|
|||
public: |
|||
SYNC_QUEUE() |
|||
{ |
|||
} |
|||
|
|||
/** |
|||
* Push a value onto the queue. |
|||
*/ |
|||
void push( T const& aValue ) |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
m_queue.push( aValue ); |
|||
} |
|||
|
|||
/** |
|||
* Move a value onto the queue. Useful for e.g. unique_ptr. |
|||
*/ |
|||
void move_push( T&& aValue ) |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
m_queue.push( std::move( aValue ) ); |
|||
} |
|||
|
|||
/** |
|||
* Pop a value off the queue if there is one, returning it. If the queue is empty, |
|||
* return boost::none instead. |
|||
*/ |
|||
boost::optional<T> pop() |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
|
|||
if( m_queue.empty() ) |
|||
{ |
|||
return boost::none; |
|||
} |
|||
else |
|||
{ |
|||
T val = std::move( m_queue.front() ); |
|||
m_queue.pop(); |
|||
return std::move( val ); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* Return true iff the queue is empty. |
|||
*/ |
|||
bool empty() const |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
return m_queue.empty(); |
|||
} |
|||
|
|||
/** |
|||
* Return the size of the queue. |
|||
*/ |
|||
size_t size() const |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
return m_queue.size(); |
|||
} |
|||
|
|||
/** |
|||
* Clear the queue. |
|||
*/ |
|||
void clear() |
|||
{ |
|||
GUARD guard( m_mutex ); |
|||
|
|||
while( !m_queue.empty() ) |
|||
{ |
|||
m_queue.pop(); |
|||
} |
|||
} |
|||
}; |
|||
|
|||
#endif // SYNC_QUEUE_H |
@ -0,0 +1,113 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef FOOTPRINT_CHOICE_H |
|||
#define FOOTPRINT_CHOICE_H |
|||
|
|||
#include <wx/odcombo.h> |
|||
|
|||
/** |
|||
* Event thrown when an item is selected "interactively". This includes direct clicks |
|||
* and presses of the Enter key, but not arrow key motion. Integer data will be the |
|||
* item selected. |
|||
*/ |
|||
wxDECLARE_EVENT( EVT_INTERACTIVE_CHOICE, wxCommandEvent ); |
|||
|
|||
|
|||
/** |
|||
* Customized combo box for footprint selection. This provides the following features: |
|||
* |
|||
* - library name is greyed out for readability when lib:footprint format is found in |
|||
* the item text |
|||
* - empty items are displayed as nonselectable separators |
|||
* |
|||
* Multiple separators in a row is undefined behavior; it is likely to result in errors |
|||
* such as the ability to select separators. Separators ARE valid at the top and bottom. |
|||
* |
|||
* For any items containing footprints, the "lib:footprint" name should be attached to |
|||
* the item as a wxStringClientData. |
|||
*/ |
|||
class FOOTPRINT_CHOICE : public wxOwnerDrawnComboBox |
|||
{ |
|||
public: |
|||
FOOTPRINT_CHOICE( wxWindow* aParent, int aId ); |
|||
|
|||
virtual ~FOOTPRINT_CHOICE(); |
|||
|
|||
protected: |
|||
virtual void DoSetPopupControl( wxComboPopup* aPopup ) override; |
|||
virtual void OnDrawItem( wxDC& aDC, wxRect const& aRect, int aItem, int aFlags ) const override; |
|||
virtual wxCoord OnMeasureItem( size_t aItem ) const override; |
|||
virtual wxCoord OnMeasureItemWidth( size_t aItem ) const override; |
|||
|
|||
/** |
|||
* Draw a fragment of text, then return the next x coordinate to continue drawing. |
|||
*/ |
|||
static wxCoord DrawTextFragment( wxDC& aDC, wxCoord x, wxCoord y, wxString const& aText ); |
|||
|
|||
/// Veto a mouseover event if in the separator |
|||
void TryVetoMouse( wxMouseEvent& aEvent ); |
|||
|
|||
/** |
|||
* Veto a select event for the separator |
|||
* |
|||
* @param aInner - true if event was called for the inner list (ie the popup) |
|||
*/ |
|||
void TryVetoSelect( wxCommandEvent& aEvent, bool aInner ); |
|||
|
|||
/** |
|||
* Mouse up on an item in the list. |
|||
*/ |
|||
void OnMouseUp( wxMouseEvent& aEvent ); |
|||
|
|||
/** |
|||
* Key up on an item in the list. |
|||
*/ |
|||
void OnKeyUp( wxKeyEvent& aEvent ); |
|||
|
|||
/** |
|||
* For arrow key events, skip over separators. |
|||
* |
|||
* @param aInner - true if event was called for the inner list (ie the popup) |
|||
*/ |
|||
void TrySkipSeparator( wxKeyEvent& aEvent, bool aInner ); |
|||
|
|||
/** |
|||
* Safely get a string for an item, returning wxEmptyString if the item doesn't exist. |
|||
*/ |
|||
wxString SafeGetString( int aItem ) const; |
|||
|
|||
/** |
|||
* Get selection from either the outer (combo box) or inner (popup) list. |
|||
*/ |
|||
int GetSelectionEither( bool aInner ) const; |
|||
|
|||
/** |
|||
* Safely set selection for either the outer (combo box) or inner (popup) list, doing nothing |
|||
* for invalid selections. |
|||
*/ |
|||
void SetSelectionEither( bool aInner, int aSel ); |
|||
|
|||
static wxColour m_grey; |
|||
|
|||
private: |
|||
int m_last_selection; |
|||
}; |
|||
|
|||
#endif // FOOTPRINT_CHOICE_H |
@ -0,0 +1,161 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef FOOTPRINT_SELECT_WIDGET_H |
|||
#define FOOTPRINT_SELECT_WIDGET_H |
|||
|
|||
#include <footprint_filter.h> |
|||
#include <footprint_info.h> |
|||
#include <vector> |
|||
#include <wx/panel.h> |
|||
#include <wx/wx.h> |
|||
|
|||
class KIWAY; |
|||
class PROJECT; |
|||
class FOOTPRINT_CHOICE; |
|||
class wxGauge; |
|||
class wxMenu; |
|||
class wxTimer; |
|||
class wxTimerEvent; |
|||
class wxWindow; |
|||
class wxSimplebook; |
|||
|
|||
/** |
|||
* This event is fired when a footprint is selected. The string data of the |
|||
* event will contain the footprint name. |
|||
*/ |
|||
wxDECLARE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent ); |
|||
|
|||
class FOOTPRINT_SELECT_WIDGET : public wxPanel |
|||
{ |
|||
public: |
|||
/** |
|||
* Construct a footprint selector widget. |
|||
* |
|||
* This requires references to an external footprint loader, and an external |
|||
* unique_ptr-to-FOOTPRINT_LIST. The latter will be populated with a |
|||
* FOOTPRINT_LIST instance the first time Load() is called. |
|||
* |
|||
* The reason for this is that footprint loading tends to be very expensive, |
|||
* especially when using online libraries. The caller is expected to keep |
|||
* these objects around (e.g. they may be statics on the dialog this |
|||
* FOOTPRINT_SELECT_WIDGET is created in) so footprints do not have to be |
|||
* loaded more than once. |
|||
* |
|||
* @param aParent - parent window |
|||
* @param aLoader - FOOTPRINT_ASYNC_LOADER instance |
|||
* @param aFpList - FOOTPRINT_LIST container |
|||
* @param aUpdate - whether to call UpdateList() automatically when finished loading |
|||
* @param aMaxItems - maximum number of filter items to display, in addition to |
|||
* Default and Other |
|||
*/ |
|||
FOOTPRINT_SELECT_WIDGET( wxWindow* aParent, FOOTPRINT_ASYNC_LOADER& aLoader, |
|||
std::unique_ptr<FOOTPRINT_LIST>& aFpList, bool aUpdate = true, int aMaxItems = 10 ); |
|||
|
|||
virtual ~FOOTPRINT_SELECT_WIDGET() |
|||
{ |
|||
} |
|||
|
|||
/** |
|||
* Start loading. This function returns immediately; footprints will |
|||
* continue to load in the background. |
|||
* |
|||
* @param aKiway - active kiway instance. This is cached for use when "Other" |
|||
* is selected. |
|||
* @param aProject - current project |
|||
*/ |
|||
void Load( KIWAY& aKiway, PROJECT& aProject ); |
|||
|
|||
/** |
|||
* Clear all filters. Does not update the list. |
|||
*/ |
|||
void ClearFilters(); |
|||
|
|||
/** |
|||
* Filter by pin count. Does not update the list. |
|||
*/ |
|||
void FilterByPinCount( int aPinCount ); |
|||
|
|||
/** |
|||
* Filter by footprint filter list. Does not update the list. |
|||
* |
|||
* @param aZeroFilters - if true, zero filters = zero footprints. If false, zero filters = |
|||
* not filtering. |
|||
*/ |
|||
void FilterByFootprintFilters( wxArrayString const& aFilters, bool aZeroFilters ); |
|||
|
|||
/** |
|||
* Set the default footprint for a part. This will be listed at the |
|||
* top. May be an empty string. |
|||
*/ |
|||
void SetDefaultFootprint( wxString const& aFp ); |
|||
|
|||
/** |
|||
* Update the contents of the list to match the filters. Has no effect if |
|||
* the footprint list has not been loaded yet. The "default" footprint will be |
|||
* selected. |
|||
* |
|||
* @return true if the footprint list has been loaded (and the list was updated) |
|||
*/ |
|||
bool UpdateList(); |
|||
|
|||
/** |
|||
* Set current selection to the default footprint |
|||
*/ |
|||
void SelectDefault(); |
|||
|
|||
/** |
|||
* Enable or disable the control for input |
|||
*/ |
|||
virtual bool Enable( bool aEnable = true ) override; |
|||
|
|||
private: |
|||
KIWAY* m_kiway; |
|||
wxGauge* m_progress_ctrl; |
|||
FOOTPRINT_CHOICE* m_fp_sel_ctrl; |
|||
wxSizer* m_sizer; |
|||
wxSimplebook* m_book; |
|||
|
|||
std::unique_ptr<wxTimer> m_progress_timer; |
|||
|
|||
bool m_update; |
|||
bool m_finished_loading; |
|||
int m_max_items; |
|||
wxString m_default_footprint; |
|||
wxString m_other_footprint; |
|||
int m_last_item; |
|||
|
|||
FOOTPRINT_ASYNC_LOADER& m_fp_loader; |
|||
std::unique_ptr<FOOTPRINT_LIST>& m_fp_list; |
|||
FOOTPRINT_FILTER m_fp_filter; |
|||
bool m_zero_filter; |
|||
|
|||
void OnProgressTimer( wxTimerEvent& aEvent ); |
|||
void OnComboBox( wxCommandEvent& aEvent ); |
|||
void OnComboInteractive( wxCommandEvent& aEvent ); |
|||
|
|||
/// Show the component picker and return the selected component. Used by DoOther() |
|||
wxString ShowPicker(); |
|||
|
|||
/// Handle activation of the "Other..." item |
|||
void DoOther(); |
|||
}; |
|||
|
|||
|
|||
#endif // FOOTPRINT_SELECT_WIDGET |
@ -0,0 +1,230 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr> |
|||
* Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@softplc.com> |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
|
|||
|
|||
#include <footprint_info_impl.h>
|
|||
|
|||
#include <class_module.h>
|
|||
#include <common.h>
|
|||
#include <fctsys.h>
|
|||
#include <footprint_info.h>
|
|||
#include <fp_lib_table.h>
|
|||
#include <html_messagebox.h>
|
|||
#include <io_mgr.h>
|
|||
#include <kiface_ids.h>
|
|||
#include <kiway.h>
|
|||
#include <lib_id.h>
|
|||
#include <macros.h>
|
|||
#include <make_unique.h>
|
|||
#include <pgm_base.h>
|
|||
#include <wildcards_and_files_ext.h>
|
|||
|
|||
#include <thread>
|
|||
|
|||
|
|||
void FOOTPRINT_INFO_IMPL::load() |
|||
{ |
|||
FP_LIB_TABLE* fptable = m_owner->GetTable(); |
|||
|
|||
wxASSERT( fptable ); |
|||
|
|||
std::unique_ptr<MODULE> footprint( fptable->FootprintLoad( m_nickname, m_fpname ) ); |
|||
|
|||
if( footprint.get() == NULL ) // Should happen only with malformed/broken libraries
|
|||
{ |
|||
m_pad_count = 0; |
|||
m_unique_pad_count = 0; |
|||
} |
|||
else |
|||
{ |
|||
m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH ); |
|||
m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH ); |
|||
m_keywords = footprint->GetKeywords(); |
|||
m_doc = footprint->GetDescription(); |
|||
|
|||
// tell ensure_loaded() I'm loaded.
|
|||
m_loaded = true; |
|||
} |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_LIST_IMPL::CatchErrors( std::function<void()> aFunc ) |
|||
{ |
|||
try |
|||
{ |
|||
aFunc(); |
|||
} |
|||
catch( const IO_ERROR& ioe ) |
|||
{ |
|||
m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) ); |
|||
return false; |
|||
} |
|||
catch( const std::exception& se ) |
|||
{ |
|||
// This is a round about way to do this, but who knows what THROW_IO_ERROR()
|
|||
// may be tricked out to do someday, keep it in the game.
|
|||
try |
|||
{ |
|||
THROW_IO_ERROR( se.what() ); |
|||
} |
|||
catch( const IO_ERROR& ioe ) |
|||
{ |
|||
m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) ); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_LIST_IMPL::loader_job() |
|||
{ |
|||
while( auto const nickname = m_queue_in.pop() ) |
|||
{ |
|||
CatchErrors( [this, &nickname]() { |
|||
m_lib_table->PrefetchLib( *nickname ); |
|||
m_queue_out.push( *nickname ); |
|||
} ); |
|||
|
|||
m_count_finished.fetch_add( 1 ); |
|||
} |
|||
|
|||
if( !m_first_to_finish.exchange( true ) ) |
|||
{ |
|||
// yay, we're first to finish!
|
|||
if( m_loader->m_completion_cb ) |
|||
{ |
|||
m_loader->m_completion_cb(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname ) |
|||
{ |
|||
FOOTPRINT_ASYNC_LOADER loader; |
|||
|
|||
loader.SetList( this ); |
|||
loader.Start( aTable, aNickname ); |
|||
return loader.Join(); |
|||
} |
|||
|
|||
|
|||
void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname, |
|||
FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads ) |
|||
{ |
|||
m_loader = aLoader; |
|||
m_lib_table = aTable; |
|||
|
|||
// Clear data before reading files
|
|||
m_first_to_finish.store( false ); |
|||
m_count_finished.store( 0 ); |
|||
m_errors.clear(); |
|||
m_list.clear(); |
|||
m_threads.clear(); |
|||
m_queue_in.clear(); |
|||
m_queue_out.clear(); |
|||
|
|||
if( aNickname ) |
|||
m_queue_in.push( *aNickname ); |
|||
else |
|||
{ |
|||
for( auto const& nickname : aTable->GetLogicalLibs() ) |
|||
m_queue_in.push( nickname ); |
|||
} |
|||
|
|||
m_loader->m_total_libs = m_queue_in.size(); |
|||
|
|||
for( unsigned i = 0; i < aNThreads; ++i ) |
|||
{ |
|||
m_threads.push_back( std::thread( &FOOTPRINT_LIST_IMPL::loader_job, this ) ); |
|||
} |
|||
} |
|||
|
|||
bool FOOTPRINT_LIST_IMPL::JoinWorkers() |
|||
{ |
|||
for( auto& i : m_threads ) |
|||
i.join(); |
|||
|
|||
m_threads.clear(); |
|||
m_queue_in.clear(); |
|||
|
|||
LOCALE_IO toggle_locale; |
|||
|
|||
// Parse the footprints in parallel. WARNING! This requires changing the locale, which is
|
|||
// GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
|
|||
// destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
|
|||
// from this will cause nasal demons.
|
|||
//
|
|||
// TODO: blast LOCALE_IO into the sun
|
|||
|
|||
SYNC_QUEUE<std::unique_ptr<FOOTPRINT_INFO>> queue_parsed; |
|||
std::vector<std::thread> threads; |
|||
|
|||
for( size_t i = 0; i < std::thread::hardware_concurrency() + 1; ++i ) |
|||
{ |
|||
threads.push_back( std::thread( [this, &queue_parsed]() { |
|||
while( auto nickname = this->m_queue_out.pop() ) |
|||
{ |
|||
CatchErrors( [this, &queue_parsed, &nickname]() { |
|||
wxArrayString fpnames = this->m_lib_table->FootprintEnumerate( *nickname ); |
|||
|
|||
for( auto const& fpname : fpnames ) |
|||
{ |
|||
FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, *nickname, fpname ); |
|||
queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) ); |
|||
} |
|||
} ); |
|||
} |
|||
} ) ); |
|||
} |
|||
|
|||
for( auto& thr : threads ) |
|||
thr.join(); |
|||
|
|||
while( auto fpi = queue_parsed.pop() ) |
|||
m_list.push_back( std::move( *fpi ) ); |
|||
|
|||
std::sort( m_list.begin(), m_list.end(), |
|||
[]( std::unique_ptr<FOOTPRINT_INFO> const& lhs, |
|||
std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool { return *lhs < *rhs; } ); |
|||
|
|||
return m_errors.empty(); |
|||
} |
|||
|
|||
|
|||
size_t FOOTPRINT_LIST_IMPL::CountFinished() |
|||
{ |
|||
return m_count_finished.load(); |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() : m_loader( nullptr ) |
|||
{ |
|||
} |
|||
|
|||
|
|||
FOOTPRINT_LIST_IMPL::~FOOTPRINT_LIST_IMPL() |
|||
{ |
|||
for( auto& i : m_threads ) |
|||
i.join(); |
|||
} |
@ -0,0 +1,94 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@wanadoo.fr> |
|||
* Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.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 3 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, see <http://www.gnu.org/licenses/>. |
|||
*/ |
|||
|
|||
#ifndef FOOTPRINT_INFO_IMPL_H |
|||
#define FOOTPRINT_INFO_IMPL_H |
|||
|
|||
#include <atomic> |
|||
#include <functional> |
|||
#include <memory> |
|||
#include <thread> |
|||
#include <vector> |
|||
|
|||
#include <footprint_info.h> |
|||
#include <sync_queue.h> |
|||
|
|||
class LOCALE_IO; |
|||
|
|||
class FOOTPRINT_INFO_IMPL : public FOOTPRINT_INFO |
|||
{ |
|||
public: |
|||
FOOTPRINT_INFO_IMPL( |
|||
FOOTPRINT_LIST* aOwner, const wxString& aNickname, const wxString& aFootprintName ) |
|||
{ |
|||
m_owner = aOwner; |
|||
m_loaded = false; |
|||
m_nickname = aNickname; |
|||
m_fpname = aFootprintName; |
|||
m_num = 0; |
|||
m_pad_count = 0; |
|||
m_unique_pad_count = 0; |
|||
#if !USE_FPI_LAZY |
|||
load(); |
|||
#endif |
|||
} |
|||
|
|||
protected: |
|||
virtual void load() override; |
|||
}; |
|||
|
|||
|
|||
class FOOTPRINT_LIST_IMPL : public FOOTPRINT_LIST |
|||
{ |
|||
FOOTPRINT_ASYNC_LOADER* m_loader; |
|||
std::vector<std::thread> m_threads; |
|||
SYNC_QUEUE<wxString> m_queue_in; |
|||
SYNC_QUEUE<wxString> m_queue_out; |
|||
std::atomic_size_t m_count_finished; |
|||
std::atomic_bool m_first_to_finish; |
|||
|
|||
/** |
|||
* Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors. |
|||
* |
|||
* @return true if no error occurred. |
|||
*/ |
|||
bool CatchErrors( std::function<void()> aFunc ); |
|||
|
|||
protected: |
|||
virtual void StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname, |
|||
FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads ) override; |
|||
virtual bool JoinWorkers() override; |
|||
virtual size_t CountFinished() override; |
|||
|
|||
/** |
|||
* Function loader_job |
|||
* loads footprints from m_queue_in. |
|||
*/ |
|||
void loader_job(); |
|||
|
|||
public: |
|||
FOOTPRINT_LIST_IMPL(); |
|||
virtual ~FOOTPRINT_LIST_IMPL(); |
|||
|
|||
virtual bool ReadFootprintFiles( |
|||
FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ) override; |
|||
}; |
|||
|
|||
#endif // FOOTPRINT_INFO_IMPL_H |
Write
Preview
Loading…
Cancel
Save
Reference in new issue