diff --git a/CMakeLists.txt b/CMakeLists.txt index 18f2052e1d..0d72dbef5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,8 +101,6 @@ option( BUILD_GITHUB_PLUGIN "Build the GITHUB_PLUGIN for pcbnew." ON ) option( KICAD_SPICE "Build Kicad with internal Spice simulator." OFF ) -option( KICAD_FOOTPRINT_SELECTOR "Build experimental eeschema footprint selector." OFF ) - # Global setting: exports are explicit set( CMAKE_CXX_VISIBILITY_PRESET "hidden" ) set( CMAKE_VISIBILITY_INLINES_HIDDEN ON ) @@ -321,10 +319,6 @@ if( KICAD_SPICE ) add_definitions( -DKICAD_SPICE ) endif() -if( KICAD_FOOTPRINT_SELECTOR ) - add_definitions( -DKICAD_FOOTPRINT_SELECTOR ) -endif() - if( KICAD_USE_SCH_IO_MANAGER ) add_definitions( -DKICAD_USE_SCH_IO_MANAGER ) endif() diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index c9744915c8..ad1cd5dee4 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -171,6 +171,8 @@ set( COMMON_WIDGET_SRCS widgets/widget_hotkey_list.cpp widgets/two_column_tree_list.cpp widgets/footprint_preview_widget.cpp + widgets/footprint_select_widget.cpp + widgets/footprint_choice.cpp widgets/indicator_icon.cpp ) @@ -243,6 +245,8 @@ set( COMMON_SRCS exceptions.cpp executable_names.cpp filter_reader.cpp + footprint_info.cpp + footprint_filter.cpp lib_id.cpp lib_table_keywords.cpp # findkicadhelppath.cpp.notused deprecated, use searchhelpfilefullpath.cpp @@ -351,7 +355,6 @@ set( PCB_COMMON_SRCS eda_text.cpp class_page_info.cpp lset.cpp - footprint_info.cpp ../pcbnew/basepcbframe.cpp ../pcbnew/class_board.cpp ../pcbnew/class_board_connected_item.cpp diff --git a/common/eda_pattern_match.cpp b/common/eda_pattern_match.cpp index b09094e0d4..f2d8bced00 100644 --- a/common/eda_pattern_match.cpp +++ b/common/eda_pattern_match.cpp @@ -34,6 +34,12 @@ bool EDA_PATTERN_MATCH_SUBSTR::SetPattern( const wxString& aPattern ) } +wxString const& EDA_PATTERN_MATCH_SUBSTR::GetPattern() const +{ + return m_pattern; +} + + int EDA_PATTERN_MATCH_SUBSTR::Find( const wxString& aCandidate ) const { int loc = aCandidate.Find( m_pattern ); @@ -75,6 +81,12 @@ bool EDA_PATTERN_MATCH_REGEX::SetPattern( const wxString& aPattern ) } +wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const +{ + return m_pattern; +} + + int EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const { if( m_regex.IsValid() ) @@ -100,6 +112,8 @@ int EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern ) { + m_wildcard_pattern = aPattern; + // Compile the wildcard string to a regular expression wxString regex; regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly @@ -132,6 +146,12 @@ bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern ) } +wxString const& EDA_PATTERN_MATCH_WILDCARD::GetPattern() const +{ + return m_wildcard_pattern; +} + + int EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const { return EDA_PATTERN_MATCH_REGEX::Find( aCandidate ); diff --git a/common/footprint_filter.cpp b/common/footprint_filter.cpp new file mode 100644 index 0000000000..c6929758e4 --- /dev/null +++ b/common/footprint_filter.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 . + */ + +#include +#include +#include + +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() ); + 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; +} diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp index ddf79508a9..8ec1163a0a 100644 --- a/common/footprint_info.cpp +++ b/common/footprint_info.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2011 Jean-Pierre Charras, * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -23,242 +23,31 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ + /** * @file footprint_info.cpp */ -/** - No. concurrent threads doing "http(s) GET". More than 6 is not significantly - faster, less than 6 is likely slower. Main thread is in this count, so if - set to 1 then no temp threads are created. -*/ -#define READER_THREADS 6 - /* * Functions to read footprint libraries and fill m_footprints by available footprints names * and their documentation (comments and keywords) */ -#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include -#include -#include -#include -#include -#include #include -#include - - -/* -static wxString ToHTMLFragment( const IO_ERROR* aDerivative ) -{ - @todo - - 1) change up IO_ERROR so it keeps linenumbers, source file name and - error message in separate strings. - - 2) Add a summarizing virtual member like - virtual wxString What() - to combine all portions of an IO_ERROR's text into a single wxString. - - 3) Do same for PARSE_ERROR. - - 4) Add a "reason or error category" to IO_ERROR and thereby also PARSE_ERROR? - - msg += " - - for( int i=0; i" ); - - for ( unsigned ii = 0; ii < strings_list->GetCount(); ii++ ) - { - msg += wxT( "
  • " ); - msg += strings_list->Item( ii ) + wxT( "
  • " ); - } - - msg += wxT( "" ); - - m_htmlWindow->AppendToPage( msg ); - - delete strings_list; -} -*/ - - -void FOOTPRINT_INFO::load() -{ - FP_LIB_TABLE* fptable = m_owner->GetTable(); - - wxASSERT( fptable ); - - std::unique_ptr 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; - } -} - - -void FOOTPRINT_LIST::loader_job( const wxString* aNicknameList, int aJobZ ) -{ - for( int i=0; iFootprintEnumerate( nickname ); - - for( unsigned ni=0; ni nicknames; - - // do all of them - nicknames = aTable->GetLogicalLibs(); - - // Even though the PLUGIN API implementation is the place for the - // locale toggling, in order to keep LOCAL_IO::C_count at 1 or greater - // for the duration of all helper threads, we increment by one here via instantiation. - // Only done here because of the multi-threaded nature of this code. - // Without this C_count skips in and out of "equal to zero" and causes - // needless locale toggling among the threads, based on which of them - // are in a PLUGIN::FootprintLoad() function. And that is occasionally - // none of them. - LOCALE_IO top_most_nesting; - - // Something which will not invoke a thread copy constructor, one of many ways obviously: - typedef std::vector< std::thread > MYTHREADS; - - MYTHREADS threads; - - unsigned jobz = (nicknames.size() + READER_THREADS - 1) / READER_THREADS; - - // Give each thread JOBZ nicknames to process. The last portion of, or if the entire - // size() is small, I'll do myself. - for( unsigned i=0; i= nicknames.size() ) // on the last iteration of this for(;;) - { - jobz = nicknames.size() - i; - - // Only a little bit to do, I'll do it myself on current thread. - // I am part of the READER_THREADS count. - loader_job( &nicknames[i], jobz ); - } - else - { - // Delegate the job to a temporary thread created here. - threads.push_back( std::thread( &FOOTPRINT_LIST::loader_job, - this, &nicknames[i], jobz ) ); - } - - i += jobz; - } - - // Wait for all the worker threads to complete, it does not matter in what order - // we wait for them as long as a full sweep is made. Think of the great race, - // everyone must finish. - for( unsigned i=0; i FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) @@ -266,19 +55,19 @@ FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) if( aFootprintName.IsEmpty() ) return NULL; - for( FOOTPRINT_INFO& fp : m_list ) + for( auto& fp : m_list ) { LIB_ID fpid; wxCHECK_MSG( fpid.Parse( aFootprintName ) < 0, NULL, - wxString::Format( wxT( "'%s' is not a valid LIB_ID." ), - GetChars( aFootprintName ) ) ); + wxString::Format( + wxT( "'%s' is not a valid LIB_ID." ), GetChars( aFootprintName ) ) ); - wxString libNickname = fpid.GetLibNickname(); + wxString libNickname = fpid.GetLibNickname(); wxString footprintName = fpid.GetLibItemName(); - if( libNickname == fp.GetNickname() && footprintName == fp.GetFootprintName() ) - return &fp; + if( libNickname == fp->GetNickname() && footprintName == fp->GetFootprintName() ) + return &*fp; } return NULL; @@ -304,12 +93,119 @@ void FOOTPRINT_LIST::DisplayErrors( wxTopLevelWindow* aWindow ) wxString msg; - for( unsigned i = 0; i" ) + m_errors[i].Problem() + wxT( "

    " ); + msg += wxT( "

    " ) + error->Problem() + wxT( "

    " ); } dlg.AddHTML_Text( msg ); dlg.ShowModal(); } + + +static std::unique_ptr get_instance_from_id( KIWAY& aKiway, int aId ) +{ + void* ptr = nullptr; + + try + { + KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB ); + + if( !kiface ) + return nullptr; + + ptr = kiface->IfaceOrAddress( aId ); + + if( !ptr ) + return nullptr; + } + catch( ... ) + { + return nullptr; + } + + return std::unique_ptr( (FOOTPRINT_LIST*) ( ptr ) ); +} + + +std::unique_ptr FOOTPRINT_LIST::GetInstance( KIWAY& aKiway ) +{ + return get_instance_from_id( aKiway, KIFACE_NEW_FOOTPRINT_LIST ); +} + + +FOOTPRINT_ASYNC_LOADER::FOOTPRINT_ASYNC_LOADER() : m_list( nullptr ) +{ +} + + +void FOOTPRINT_ASYNC_LOADER::SetList( FOOTPRINT_LIST* aList ) +{ + m_list = aList; +} + + +void FOOTPRINT_ASYNC_LOADER::Start( + FP_LIB_TABLE* aTable, wxString const* aNickname, unsigned aNThreads ) +{ + m_started = true; + + // Capture the FP_LIB_TABLE into m_last_table. Formatting it as a string instead of storing the + // raw data avoids having to pull in the FP-specific parts. + STRING_FORMATTER sof; + aTable->Format( &sof, 0 ); + m_last_table = sof.GetString(); + + m_list->StartWorkers( aTable, aNickname, this, aNThreads ); +} + + +bool FOOTPRINT_ASYNC_LOADER::Join() +{ + if( m_list ) + { + bool rv = m_list->JoinWorkers(); + m_list = nullptr; + return rv; + } + else + return true; +} + + +int FOOTPRINT_ASYNC_LOADER::GetProgress() const +{ + if( !m_started ) + return 0; + else if( m_total_libs == 0 || !m_list ) + return 100; + else + { + int loaded = m_list->CountFinished(); + int prog = ( 100 * loaded ) / m_total_libs; + + if( loaded == m_total_libs ) + return 100; + else if( loaded < m_total_libs && prog >= 100 ) + return 99; + else if( prog <= 0 ) + return 1; + else + return prog; + } +} + + +void FOOTPRINT_ASYNC_LOADER::SetCompletionCallback( std::function aCallback ) +{ + m_completion_cb = aCallback; +} + + +bool FOOTPRINT_ASYNC_LOADER::IsSameTable( FP_LIB_TABLE* aOther ) +{ + STRING_FORMATTER sof; + aOther->Format( &sof, 0 ); + return m_last_table == sof.GetString(); +} diff --git a/common/fp_lib_table.cpp b/common/fp_lib_table.cpp index 4c4f6e203f..504496024d 100644 --- a/common/fp_lib_table.cpp +++ b/common/fp_lib_table.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012-2016 Wayne Stambaugh - * Copyright (C) 2012-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2012-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 @@ -204,6 +204,14 @@ wxArrayString FP_LIB_TABLE::FootprintEnumerate( const wxString& aNickname ) } +void FP_LIB_TABLE::PrefetchLib( const wxString& aNickname ) +{ + const FP_LIB_TABLE_ROW* row = FindRow( aNickname ); + wxASSERT( (PLUGIN*) row->plugin ); + row->plugin->PrefetchLib( row->GetFullURI( true ), row->GetProperties() ); +} + + const FP_LIB_TABLE_ROW* FP_LIB_TABLE::FindRow( const wxString& aNickname ) throw( IO_ERROR ) { diff --git a/common/project.cpp b/common/project.cpp index e55ed94b06..12e36473e7 100644 --- a/common/project.cpp +++ b/common/project.cpp @@ -1,8 +1,7 @@ - /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2014-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 @@ -22,7 +21,6 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - #include #include @@ -34,6 +32,9 @@ #include #include #include +#include +#include +#include PROJECT::PROJECT() @@ -379,3 +380,41 @@ const wxString PROJECT::AbsolutePath( const wxString& aFileName ) const return fn.GetFullPath(); } + + +FP_LIB_TABLE* PROJECT::PcbFootprintLibs( KIWAY& aKiway ) +{ + // This is a lazy loading function, it loads the project specific table when + // that table is asked for, not before. + + FP_LIB_TABLE* tbl = (FP_LIB_TABLE*) GetElem( ELEM_FPTBL ); + + // its gotta be NULL or a FP_LIB_TABLE, or a bug. + wxASSERT( !tbl || dynamic_cast( tbl ) ); + + if( !tbl ) + { + // Stack the project specific FP_LIB_TABLE overlay on top of the global table. + // ~FP_LIB_TABLE() will not touch the fallback table, so multiple projects may + // stack this way, all using the same global fallback table. + KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB ); + if( kiface ) + tbl = (FP_LIB_TABLE*) kiface->IfaceOrAddress( KIFACE_G_FOOTPRINT_TABLE ); + + wxASSERT( tbl ); + SetElem( ELEM_FPTBL, tbl ); + + wxString projectFpLibTableFileName = FootprintLibTblName(); + + try + { + tbl->Load( projectFpLibTableFileName ); + } + catch( const IO_ERROR& ioe ) + { + DisplayError( NULL, ioe.What() ); + } + } + + return tbl; +} diff --git a/common/widgets/footprint_choice.cpp b/common/widgets/footprint_choice.cpp new file mode 100644 index 0000000000..ec547282b4 --- /dev/null +++ b/common/widgets/footprint_choice.cpp @@ -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 . + */ + +#include +#include +#include +#include + +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( 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 ); + } +} diff --git a/common/widgets/footprint_select_widget.cpp b/common/widgets/footprint_select_widget.cpp new file mode 100644 index 0000000000..430c0849ce --- /dev/null +++ b/common/widgets/footprint_select_widget.cpp @@ -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 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/** + * 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& 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( 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( 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( 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 ); +} diff --git a/cvpcb/autosel.cpp b/cvpcb/autosel.cpp index a60583cd51..1646106f84 100644 --- a/cvpcb/autosel.cpp +++ b/cvpcb/autosel.cpp @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -215,7 +215,7 @@ void CVPCB_MAINFRAME::AutomaticFootprintMatching( wxCommandEvent& event ) if( equivItem.m_ComponentValue.CmpNoCase( component->GetValue() ) != 0 ) continue; - const FOOTPRINT_INFO *module = m_FootprintsList.GetModuleInfo( equivItem.m_FootprintFPID ); + const FOOTPRINT_INFO *module = m_FootprintsList->GetModuleInfo( equivItem.m_FootprintFPID ); bool equ_is_unique = true; unsigned next = idx+1; @@ -277,7 +277,7 @@ void CVPCB_MAINFRAME::AutomaticFootprintMatching( wxCommandEvent& event ) { // we do not need to analyze wildcards: single footprint do not // contain them and if there are wildcards it just will not match any - const FOOTPRINT_INFO* module = m_FootprintsList.GetModuleInfo( component->GetFootprintFilters()[0] ); + const FOOTPRINT_INFO* module = m_FootprintsList->GetModuleInfo( component->GetFootprintFilters()[0] ); if( module ) SetNewPkg( component->GetFootprintFilters()[0] ); diff --git a/cvpcb/class_DisplayFootprintsFrame.cpp b/cvpcb/class_DisplayFootprintsFrame.cpp index 892f5e90b8..5a484b3b65 100644 --- a/cvpcb/class_DisplayFootprintsFrame.cpp +++ b/cvpcb/class_DisplayFootprintsFrame.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2015-2016 Wayne Stambaugh - * Copyright (C) 2007-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2007-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 @@ -459,7 +459,7 @@ MODULE* DISPLAY_FOOTPRINTS_FRAME::Get_Module( const wxString& aFootprintName ) wxLogDebug( wxT( "Load footprint <%s> from library <%s>." ), fpname.c_str(), nickname.c_str() ); - footprint = Prj().PcbFootprintLibs()->FootprintLoad( + footprint = Prj().PcbFootprintLibs( Kiway() )->FootprintLoad( FROM_UTF8( nickname.c_str() ), FROM_UTF8( fpname.c_str() ) ); } catch( const IO_ERROR& ioe ) @@ -495,7 +495,7 @@ void DISPLAY_FOOTPRINTS_FRAME::InitDisplay() SetTitle( msg ); const FOOTPRINT_INFO* module_info = - parentframe->m_FootprintsList.GetModuleInfo( footprintName ); + parentframe->m_FootprintsList->GetModuleInfo( footprintName ); const wxChar* libname; diff --git a/cvpcb/class_footprints_listbox.cpp b/cvpcb/class_footprints_listbox.cpp index 4d3f5b0b58..1b0d0320c5 100644 --- a/cvpcb/class_footprints_listbox.cpp +++ b/cvpcb/class_footprints_listbox.cpp @@ -29,12 +29,14 @@ #include #include +#include #include #include #include #include #include +#include FOOTPRINTS_LISTBOX::FOOTPRINTS_LISTBOX( CVPCB_MAINFRAME* parent, @@ -133,62 +135,28 @@ void FOOTPRINTS_LISTBOX::SetFootprints( FOOTPRINT_LIST& aList, const wxString& a wxString msg; wxString oldSelection; - EDA_PATTERN_MATCH_WILDCARD patternFilter; - patternFilter.SetPattern( aFootPrintFilterPattern.Lower() ); // Use case insensitive search + FOOTPRINT_FILTER filter( aList ); + + if( aFilterType & FILTERING_BY_COMPONENT_KEYWORD ) + filter.FilterByFootprintFilters( aComponent->GetFootprintFilters() ); + + if( aFilterType & FILTERING_BY_PIN_COUNT ) + filter.FilterByPinCount( aComponent->GetNetCount() ); + + if( aFilterType & FILTERING_BY_LIBRARY ) + filter.FilterByLibrary( aLibName ); + + if( aFilterType & FILTERING_BY_NAME ) + filter.FilterByPattern( aFootPrintFilterPattern ); if( GetSelection() >= 0 && GetSelection() < (int)m_footprintList.GetCount() ) oldSelection = m_footprintList[ GetSelection() ]; - for( unsigned ii = 0; ii < aList.GetCount(); ii++ ) + for( auto& i: filter ) { - if( aFilterType == UNFILTERED_FP_LIST ) - { - msg.Printf( wxT( "%3d %s:%s" ), int( newList.GetCount() + 1 ), - GetChars( aList.GetItem( ii ).GetNickname() ), - GetChars( aList.GetItem( ii ).GetFootprintName() ) ); - newList.Add( msg ); - continue; - } - - // Filter footprints by selected library - if( (aFilterType & FILTERING_BY_LIBRARY) && !aLibName.IsEmpty() - && !aList.GetItem( ii ).InLibrary( aLibName ) ) - continue; - - // Filter footprints by symbol fp-filters - if( (aFilterType & FILTERING_BY_COMPONENT_KEYWORD) && aComponent - && !aComponent->MatchesFootprintFilters( aList.GetItem( ii ).GetNickname(), aList.GetItem( ii ).GetFootprintName() ) ) - continue; - - // Filter footprints by symbol pin-count - if( (aFilterType & FILTERING_BY_PIN_COUNT) && aComponent - && aComponent->GetNetCount() != aList.GetItem( ii ).GetUniquePadCount() ) - continue; - - // Filter footprints by text-input - if( (aFilterType & FILTERING_BY_NAME ) && !aFootPrintFilterPattern.IsEmpty() ) - { - wxString currname = ""; - - // If the search string contains a ':' character, - // include the library name in the search string - // e.g. LibName:FootprintName - if( aFootPrintFilterPattern.Contains( ":" ) ) - { - currname = aList.GetItem( ii ).GetNickname().Lower() + ":"; - } - - currname += aList.GetItem( ii ).GetFootprintName().Lower(); - - if( patternFilter.Find( currname ) == EDA_PATTERN_NOT_FOUND ) - { - continue; - } - } - - msg.Printf( wxT( "%3d %s:%s" ), int( newList.GetCount() + 1 ), - GetChars( aList.GetItem( ii ).GetNickname() ), - GetChars( aList.GetItem( ii ).GetFootprintName() ) ); + msg.Printf( "%3d %s:%s", int( newList.GetCount() + 1 ), + GetChars( i.GetNickname() ), + GetChars( i.GetFootprintName() ) ); newList.Add( msg ); } @@ -202,6 +170,7 @@ void FOOTPRINTS_LISTBOX::SetFootprints( FOOTPRINT_LIST& aList, const wxString& a if( selection == wxNOT_FOUND ) selection = 0; + wxWindowUpdateLocker freeze( this ); DeleteAllItems(); if( m_footprintList.GetCount() ) diff --git a/cvpcb/cvpcb_mainframe.cpp b/cvpcb/cvpcb_mainframe.cpp index af25c93c2c..fb622768c8 100644 --- a/cvpcb/cvpcb_mainframe.cpp +++ b/cvpcb/cvpcb_mainframe.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2016 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011-2016 Wayne Stambaugh - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -127,6 +127,7 @@ CVPCB_MAINFRAME::CVPCB_MAINFRAME( KIWAY* aKiway, wxWindow* aParent ) : m_skipComponentSelect = false; m_filteringOptions = 0; m_tcFilterString = NULL; + m_FootprintsList = FOOTPRINT_LIST::GetInstance( Kiway() ); /* Name of the document footprint list * usually located in share/modules/footprints_doc @@ -409,7 +410,7 @@ bool CVPCB_MAINFRAME::OpenProjectFiles( const std::vector& aFileSet, i void CVPCB_MAINFRAME::OnEditFootprintLibraryTable( wxCommandEvent& aEvent ) { bool tableChanged = false; - int r = InvokePcbLibTableEditor( this, &GFootprintTable, Prj().PcbFootprintLibs() ); + int r = InvokePcbLibTableEditor( this, &GFootprintTable, Prj().PcbFootprintLibs( Kiway() ) ); if( r & 1 ) { @@ -437,7 +438,7 @@ void CVPCB_MAINFRAME::OnEditFootprintLibraryTable( wxCommandEvent& aEvent ) try { - Prj().PcbFootprintLibs()->Save( fileName ); + Prj().PcbFootprintLibs( Kiway() )->Save( fileName ); tableChanged = true; } catch( const IO_ERROR& ioe ) @@ -455,7 +456,7 @@ void CVPCB_MAINFRAME::OnEditFootprintLibraryTable( wxCommandEvent& aEvent ) { wxBusyCursor dummy; BuildLIBRARY_LISTBOX(); - m_FootprintsList.ReadFootprintFiles( Prj().PcbFootprintLibs() ); + m_FootprintsList->ReadFootprintFiles( Prj().PcbFootprintLibs( Kiway() ) ); } } @@ -482,7 +483,7 @@ void CVPCB_MAINFRAME::OnSelectComponent( wxListEvent& event ) COMPONENT* component = GetSelectedComponent(); libraryName = m_libListBox->GetSelectedLibrary(); - m_footprintListBox->SetFootprints( m_FootprintsList, libraryName, component, + m_footprintListBox->SetFootprints( *m_FootprintsList, libraryName, component, m_currentSearchPattern, m_filteringOptions); refreshAfterComponentSearch (component); @@ -657,7 +658,7 @@ void CVPCB_MAINFRAME::DisplayStatus() { wxString footprintName = GetSelectedFootprint(); - FOOTPRINT_INFO* module = m_FootprintsList.GetModuleInfo( footprintName ); + FOOTPRINT_INFO* module = m_FootprintsList->GetModuleInfo( footprintName ); if( module ) // can be NULL if no netlist loaded { @@ -715,7 +716,7 @@ void CVPCB_MAINFRAME::DisplayStatus() bool CVPCB_MAINFRAME::LoadFootprintFiles() { - FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs(); + FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs( Kiway() ); // Check if there are footprint libraries in the footprint library table. if( !fptbl || !fptbl->GetLogicalLibs().size() ) @@ -728,12 +729,12 @@ bool CVPCB_MAINFRAME::LoadFootprintFiles() { wxBusyCursor dummy; // Let the user know something is happening. - m_FootprintsList.ReadFootprintFiles( fptbl ); + m_FootprintsList->ReadFootprintFiles( fptbl ); } - if( m_FootprintsList.GetErrorCount() ) + if( m_FootprintsList->GetErrorCount() ) { - m_FootprintsList.DisplayErrors( this ); + m_FootprintsList->DisplayErrors( this ); } return true; @@ -862,7 +863,7 @@ void CVPCB_MAINFRAME::BuildFOOTPRINTS_LISTBOX() wxFONTWEIGHT_NORMAL ) ); } - m_footprintListBox->SetFootprints( m_FootprintsList, wxEmptyString, NULL, + m_footprintListBox->SetFootprints( *m_FootprintsList, wxEmptyString, NULL, wxEmptyString, FOOTPRINTS_LISTBOX::UNFILTERED_FP_LIST ); DisplayStatus(); } @@ -921,7 +922,7 @@ void CVPCB_MAINFRAME::BuildLIBRARY_LISTBOX() wxFONTWEIGHT_NORMAL ) ); } - FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs(); + FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs( Kiway() ); if( tbl ) { diff --git a/cvpcb/cvpcb_mainframe.h b/cvpcb/cvpcb_mainframe.h index 5471aa8a1c..2218478ba7 100644 --- a/cvpcb/cvpcb_mainframe.h +++ b/cvpcb/cvpcb_mainframe.h @@ -2,7 +2,7 @@ * 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-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -37,6 +37,7 @@ #include #include #include +#include /* Forward declarations of all top-level window classes. */ @@ -72,7 +73,7 @@ public: wxArrayString m_ModuleLibNames; wxArrayString m_EquFilesNames; wxString m_DocModulesFileName; - FOOTPRINT_LIST m_FootprintsList; + std::unique_ptr m_FootprintsList; protected: int m_undefinedComponentCnt; diff --git a/cvpcb/listview_classes.h b/cvpcb/listview_classes.h index 0cb4290372..1ef078caa4 100644 --- a/cvpcb/listview_classes.h +++ b/cvpcb/listview_classes.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 1992-2012 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -29,7 +29,7 @@ #define CVSTRUCT_H #include - +#include /* Forward declarations of all top-level window classes. */ class CVPCB_MAINFRAME; @@ -90,8 +90,12 @@ private: wxArrayString m_footprintList; public: - // OR'ed mask to manage footprint filtering options - enum FP_FILTER_T + + /** + * 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, diff --git a/cvpcb/readwrite_dlgs.cpp b/cvpcb/readwrite_dlgs.cpp index 4eadc9a1f6..137b891215 100644 --- a/cvpcb/readwrite_dlgs.cpp +++ b/cvpcb/readwrite_dlgs.cpp @@ -7,7 +7,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jean-pierre.charras * Copyright (C) 2011-2016 Wayne Stambaugh - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -26,7 +26,6 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - #include #include #include @@ -212,7 +211,7 @@ bool CVPCB_MAINFRAME::ReadNetListAndLinkFiles( const std::string& aNetlist ) if( component->GetFPID().IsLegacy() ) { // get this first here, it's possibly obsoleted if we get it too soon. - FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs(); + FP_LIB_TABLE* tbl = Prj().PcbFootprintLibs( Kiway() ); int guess = guessNickname( tbl, (LIB_ID*) &component->GetFPID() ); diff --git a/eeschema/dialogs/dialog_choose_component.cpp b/eeschema/dialogs/dialog_choose_component.cpp index dab4e2de23..7940a2cbd9 100644 --- a/eeschema/dialogs/dialog_choose_component.cpp +++ b/eeschema/dialogs/dialog_choose_component.cpp @@ -25,30 +25,31 @@ #include +#include #include #include #include #include #include -#include -#include -#include +#include +#include #include #include #include -#include +#include #include -#include +#include +#include #include #include #include -#include -#include -#include -#include #include +#include +#include +#include +#include // Tree navigation helpers. static wxDataViewItem GetPrevItem( const wxDataViewCtrl& ctrl, const wxDataViewItem& item ); @@ -56,21 +57,25 @@ static wxDataViewItem GetNextItem( const wxDataViewCtrl& ctrl, const wxDataViewI static wxDataViewItem GetPrevSibling( const wxDataViewCtrl& ctrl, const wxDataViewItem& item ); static wxDataViewItem GetNextSibling( const wxDataViewCtrl& ctrl, const wxDataViewItem& item ); -DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( - SCH_BASE_FRAME* aParent, const wxString& aTitle, - CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, int aDeMorganConvert ): - DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, - wxSize( 800, 650 ), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ), - m_parent( aParent ), - m_adapter( aAdapter ), - m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ), - m_external_browser_requested( false ) +FOOTPRINT_ASYNC_LOADER DIALOG_CHOOSE_COMPONENT::m_fp_loader; +std::unique_ptr DIALOG_CHOOSE_COMPONENT::m_fp_list; + +DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( SCH_BASE_FRAME* aParent, const wxString& aTitle, + CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, int aDeMorganConvert, bool aAllowFieldEdits ) + : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxSize( 800, 650 ), + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ), + m_parent( aParent ), + m_adapter( aAdapter ), + m_deMorganConvert( aDeMorganConvert >= 0 ? aDeMorganConvert : 0 ), + m_allow_field_edits( aAllowFieldEdits ), + m_external_browser_requested( false ) { wxBusyCursor busy_while_loading; auto sizer = new wxBoxSizer( wxVERTICAL ); - auto splitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE ); + auto splitter = new wxSplitterWindow( + this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE ); auto left_panel = ConstructLeftPanel( splitter ); auto right_panel = ConstructRightPanel( splitter ); auto buttons = new wxStdDialogButtonSizer(); @@ -84,22 +89,28 @@ DIALOG_CHOOSE_COMPONENT::DIALOG_CHOOSE_COMPONENT( buttons->AddButton( new wxButton( this, wxID_CANCEL ) ); buttons->Realize(); - sizer->Add( splitter, 1, wxEXPAND | wxALL, 5 ); - sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 ); + sizer->Add( splitter, 1, wxEXPAND | wxALL, 5 ); + sizer->Add( buttons, 0, wxEXPAND | wxBOTTOM, 10 ); SetSizer( sizer ); Bind( wxEVT_INIT_DIALOG, &DIALOG_CHOOSE_COMPONENT::OnInitDialog, this ); - Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this ); + Bind( wxEVT_TIMER, &DIALOG_CHOOSE_COMPONENT::OnCloseTimer, this, m_dbl_click_timer->GetId() ); - m_query_ctrl->Bind( wxEVT_TEXT, &DIALOG_CHOOSE_COMPONENT::OnQueryText, this ); - m_query_ctrl->Bind( wxEVT_TEXT_ENTER, &DIALOG_CHOOSE_COMPONENT::OnQueryEnter, this ); - m_query_ctrl->Bind( wxEVT_CHAR_HOOK, &DIALOG_CHOOSE_COMPONENT::OnQueryCharHook, this ); - m_tree_ctrl->Bind( wxEVT_DATAVIEW_ITEM_ACTIVATED, &DIALOG_CHOOSE_COMPONENT::OnTreeActivate, this ); - m_tree_ctrl->Bind( wxEVT_DATAVIEW_SELECTION_CHANGED, &DIALOG_CHOOSE_COMPONENT::OnTreeSelect, this ); + m_query_ctrl->Bind( wxEVT_TEXT, &DIALOG_CHOOSE_COMPONENT::OnQueryText, this ); + m_query_ctrl->Bind( wxEVT_TEXT_ENTER, &DIALOG_CHOOSE_COMPONENT::OnQueryEnter, this ); + m_query_ctrl->Bind( wxEVT_CHAR_HOOK, &DIALOG_CHOOSE_COMPONENT::OnQueryCharHook, this ); + m_tree_ctrl->Bind( + wxEVT_DATAVIEW_ITEM_ACTIVATED, &DIALOG_CHOOSE_COMPONENT::OnTreeActivate, this ); + m_tree_ctrl->Bind( + wxEVT_DATAVIEW_SELECTION_CHANGED, &DIALOG_CHOOSE_COMPONENT::OnTreeSelect, this ); m_details_ctrl->Bind( wxEVT_HTML_LINK_CLICKED, &DIALOG_CHOOSE_COMPONENT::OnDetailsLink, this ); m_sch_view_ctrl->Bind( wxEVT_LEFT_DCLICK, &DIALOG_CHOOSE_COMPONENT::OnSchViewDClick, this ); m_sch_view_ctrl->Bind( wxEVT_PAINT, &DIALOG_CHOOSE_COMPONENT::OnSchViewPaint, this ); + if( m_fp_sel_ctrl ) + m_fp_sel_ctrl->Bind( + EVT_FOOTPRINT_SELECTED, &DIALOG_CHOOSE_COMPONENT::OnFootprintSelected, this ); + Layout(); } @@ -121,31 +132,27 @@ wxPanel* DIALOG_CHOOSE_COMPONENT::ConstructLeftPanel( wxWindow* aParent ) auto sizer = new wxBoxSizer( wxVERTICAL ); auto search_sizer = new wxBoxSizer( wxHORIZONTAL ); - m_query_ctrl = new wxTextCtrl( panel, wxID_ANY, - wxEmptyString, wxDefaultPosition, wxDefaultSize, - wxTE_PROCESS_ENTER ); + m_query_ctrl = new wxTextCtrl( + panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER ); - m_tree_ctrl = new wxDataViewCtrl( panel, wxID_ANY, - wxDefaultPosition, wxDefaultSize, - wxDV_SINGLE ); + m_tree_ctrl = + new wxDataViewCtrl( panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxDV_SINGLE ); m_adapter->AttachTo( m_tree_ctrl ); - m_details_ctrl = new wxHtmlWindow( panel, wxID_ANY, - wxDefaultPosition, wxSize( 320,240 ), + m_details_ctrl = new wxHtmlWindow( panel, wxID_ANY, wxDefaultPosition, wxSize( 320, 240 ), wxHW_SCROLLBAR_AUTO | wxSUNKEN_BORDER ); - // Additional visual cue for GTK, which hides the placeholder text on focus +// Additional visual cue for GTK, which hides the placeholder text on focus #ifdef __WXGTK__ - search_sizer->Add( - new wxStaticBitmap( - panel, wxID_ANY, wxArtProvider::GetBitmap( wxART_FIND, wxART_FRAME_ICON ) ), - 0, wxALIGN_CENTER | wxALL, 5 ); + search_sizer->Add( new wxStaticBitmap( panel, wxID_ANY, + wxArtProvider::GetBitmap( wxART_FIND, wxART_FRAME_ICON ) ), + 0, wxALIGN_CENTER | wxALL, 5 ); #endif search_sizer->Add( m_query_ctrl, 1, wxALIGN_CENTER | wxALL | wxEXPAND, 5 ); - sizer->Add( search_sizer, 0, wxEXPAND, 5 ); - sizer->Add( m_tree_ctrl, 1, wxALL | wxEXPAND, 5 ); + sizer->Add( search_sizer, 0, wxEXPAND, 5 ); + sizer->Add( m_tree_ctrl, 1, wxALL | wxEXPAND, 5 ); sizer->Add( m_details_ctrl, 1, wxALL | wxEXPAND, 5 ); panel->SetSizer( sizer ); @@ -160,23 +167,25 @@ wxPanel* DIALOG_CHOOSE_COMPONENT::ConstructRightPanel( wxWindow* aParent ) auto panel = new wxPanel( aParent ); auto sizer = new wxBoxSizer( wxVERTICAL ); - m_sch_view_ctrl = new wxPanel( panel, wxID_ANY, - wxDefaultPosition, wxSize( -1, -1 ), + m_sch_view_ctrl = new wxPanel( panel, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ), wxFULL_REPAINT_ON_RESIZE | wxSUNKEN_BORDER | wxTAB_TRAVERSAL ); m_sch_view_ctrl->SetLayoutDirection( wxLayout_LeftToRight ); - m_fp_sel_ctrl = new wxChoice( panel, wxID_ANY ); - m_fp_sel_ctrl->SetSelection( 0 ); + if( m_allow_field_edits ) + m_fp_sel_ctrl = new FOOTPRINT_SELECT_WIDGET( panel, m_fp_loader, m_fp_list, true ); + else + m_fp_sel_ctrl = nullptr; m_fp_view_ctrl = new FOOTPRINT_PREVIEW_WIDGET( panel, Kiway() ); - sizer->Add( m_sch_view_ctrl, 1, wxEXPAND | wxALL, 5 ); - sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxALL, 5 ); - sizer->Add( m_fp_view_ctrl, 1, wxEXPAND | wxALL, 5 ); -#ifndef KICAD_FOOTPRINT_SELECTOR - m_fp_sel_ctrl->Hide(); -#endif + sizer->Add( m_sch_view_ctrl, 1, wxEXPAND | wxALL, 5 ); + + if( m_fp_sel_ctrl ) + sizer->Add( m_fp_sel_ctrl, 0, wxEXPAND | wxALL, 5 ); + + sizer->Add( m_fp_view_ctrl, 1, wxEXPAND | wxALL, 5 ); + panel->SetSizer( sizer ); panel->Layout(); @@ -199,6 +208,12 @@ void DIALOG_CHOOSE_COMPONENT::OnInitDialog( wxInitDialogEvent& aEvent ) // This hides the GAL panel and shows the status label m_fp_view_ctrl->SetStatusText( wxEmptyString ); } + + if( m_fp_sel_ctrl ) + m_fp_sel_ctrl->Load( Kiway(), Prj() ); + + // There may be a part preselected in the model. Make sure it is displayed. + PostSelectEvent(); } @@ -206,13 +221,19 @@ LIB_ALIAS* DIALOG_CHOOSE_COMPONENT::GetSelectedAlias( int* aUnit ) const { auto sel = m_tree_ctrl->GetSelection(); - if( aUnit && m_adapter->GetUnitFor( sel ) ) + if( aUnit ) *aUnit = m_adapter->GetUnitFor( sel ); return m_adapter->GetAliasFor( sel ); } +std::vector> DIALOG_CHOOSE_COMPONENT::GetFields() const +{ + return m_field_edits; +} + + void DIALOG_CHOOSE_COMPONENT::OnQueryText( wxCommandEvent& aEvent ) { m_adapter->UpdateSearchString( m_query_ctrl->GetLineText( 0 ) ); @@ -254,16 +275,12 @@ void DIALOG_CHOOSE_COMPONENT::OnQueryCharHook( wxKeyEvent& aKeyStroke ) switch( aKeyStroke.GetKeyCode() ) { - case WXK_UP: - SelectIfValid( GetPrevItem( *m_tree_ctrl, sel ) ); - break; + case WXK_UP: SelectIfValid( GetPrevItem( *m_tree_ctrl, sel ) ); break; - case WXK_DOWN: - SelectIfValid( GetNextItem( *m_tree_ctrl, sel ) ); - break; + case WXK_DOWN: SelectIfValid( GetNextItem( *m_tree_ctrl, sel ) ); break; default: - aKeyStroke.Skip(); // Any other key: pass on to search box directly. + aKeyStroke.Skip(); // Any other key: pass on to search box directly. break; } } @@ -271,8 +288,8 @@ void DIALOG_CHOOSE_COMPONENT::OnQueryCharHook( wxKeyEvent& aKeyStroke ) void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxDataViewEvent& aEvent ) { - auto sel = m_tree_ctrl->GetSelection(); - int unit = m_adapter->GetUnitFor( sel ); + auto sel = m_tree_ctrl->GetSelection(); + int unit = m_adapter->GetUnitFor( sel ); LIB_ALIAS* alias = m_adapter->GetAliasFor( sel ); m_sch_view_ctrl->Refresh(); @@ -281,6 +298,7 @@ void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxDataViewEvent& aEvent ) { m_details_ctrl->SetPage( GenerateAliasInfo( alias, unit ) ); ShowFootprintFor( alias ); + PopulateFootprintSelector( alias ); } else { @@ -288,6 +306,8 @@ void DIALOG_CHOOSE_COMPONENT::OnTreeSelect( wxDataViewEvent& aEvent ) if( m_fp_view_ctrl->IsInitialized() ) m_fp_view_ctrl->SetStatusText( wxEmptyString ); + + PopulateFootprintSelector( nullptr ); } } @@ -314,7 +334,7 @@ void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent ) } else { - EndModal( wxID_OK ); + EndQuasiModal( wxID_OK ); } } @@ -322,7 +342,7 @@ void DIALOG_CHOOSE_COMPONENT::OnCloseTimer( wxTimerEvent& aEvent ) void DIALOG_CHOOSE_COMPONENT::OnSchViewDClick( wxMouseEvent& aEvent ) { m_external_browser_requested = true; - EndModal( wxID_OK ); + EndQuasiModal( wxID_OK ); } @@ -331,32 +351,62 @@ void DIALOG_CHOOSE_COMPONENT::ShowFootprintFor( LIB_ALIAS* aAlias ) if( !m_fp_view_ctrl->IsInitialized() ) return; - LIB_FIELDS fields; - aAlias->GetPart()->GetFields( fields ); + LIB_FIELD* fp_field = aAlias->GetPart()->GetField( FOOTPRINT ); + wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" ); - for( auto const & field: fields ) + ShowFootprint( fp_name ); +} + + +void DIALOG_CHOOSE_COMPONENT::ShowFootprint( wxString const& aName ) +{ + if( aName == wxEmptyString ) { - if( field.GetId() != FOOTPRINT ) - continue; - wxString fpname = field.GetFullText(); - if( fpname == wxEmptyString ) - { - m_fp_view_ctrl->SetStatusText( _( "No footprint specified" ) ); - } - else - { - m_fp_view_ctrl->ClearStatus(); - m_fp_view_ctrl->CacheFootprint( LIB_ID( fpname ) ); - m_fp_view_ctrl->DisplayFootprint( LIB_ID( fpname ) ); - } - break; + m_fp_view_ctrl->SetStatusText( _( "No footprint specified" ) ); + } + else + { + LIB_ID lib_id( aName ); + + m_fp_view_ctrl->ClearStatus(); + m_fp_view_ctrl->CacheFootprint( lib_id ); + m_fp_view_ctrl->DisplayFootprint( lib_id ); + } +} + + +void DIALOG_CHOOSE_COMPONENT::PopulateFootprintSelector( LIB_ALIAS* aAlias ) +{ + if( !m_fp_sel_ctrl ) + return; + + m_fp_sel_ctrl->ClearFilters(); + + if( aAlias ) + { + LIB_PINS temp_pins; + LIB_FIELD* fp_field = aAlias->GetPart()->GetField( FOOTPRINT ); + wxString fp_name = fp_field ? fp_field->GetFullText() : wxString( "" ); + + aAlias->GetPart()->GetPins( temp_pins ); + + m_fp_sel_ctrl->FilterByPinCount( temp_pins.size() ); + m_fp_sel_ctrl->FilterByFootprintFilters( aAlias->GetPart()->GetFootPrints(), true ); + m_fp_sel_ctrl->SetDefaultFootprint( fp_name ); + m_fp_sel_ctrl->UpdateList(); + m_fp_sel_ctrl->Enable(); + } + else + { + m_fp_sel_ctrl->UpdateList(); + m_fp_sel_ctrl->Disable(); } } void DIALOG_CHOOSE_COMPONENT::OnDetailsLink( wxHtmlLinkEvent& aEvent ) { - const wxHtmlLinkInfo & info = aEvent.GetLinkInfo(); + const wxHtmlLinkInfo& info = aEvent.GetLinkInfo(); ::wxLaunchDefaultBrowser( info.GetHref() ); } @@ -365,9 +415,9 @@ void DIALOG_CHOOSE_COMPONENT::OnSchViewPaint( wxPaintEvent& aEvent ) { auto sel = m_tree_ctrl->GetSelection(); - int unit = m_adapter->GetUnitFor( sel ); + int unit = m_adapter->GetUnitFor( sel ); LIB_ALIAS* alias = m_adapter->GetAliasFor( sel ); - LIB_PART* part = alias ? alias->GetPart() : nullptr; + LIB_PART* part = alias ? alias->GetPart() : nullptr; // Don't draw anything (not even the background) if we don't have // a part to show @@ -392,6 +442,21 @@ void DIALOG_CHOOSE_COMPONENT::OnSchViewPaint( wxPaintEvent& aEvent ) } +void DIALOG_CHOOSE_COMPONENT::OnFootprintSelected( wxCommandEvent& aEvent ) +{ + m_fp_override = aEvent.GetString(); + + m_field_edits.erase( + std::remove_if( m_field_edits.begin(), m_field_edits.end(), + []( std::pair const& i ) { return i.first == FOOTPRINT; } ), + m_field_edits.end() ); + + m_field_edits.push_back( std::make_pair( FOOTPRINT, m_fp_override ) ); + + ShowFootprint( m_fp_override ); +} + + void DIALOG_CHOOSE_COMPONENT::RenderPreview( LIB_PART* aComponent, int aUnit ) { wxPaintDC dc( m_sch_view_ctrl ); @@ -418,10 +483,10 @@ void DIALOG_CHOOSE_COMPONENT::RenderPreview( LIB_PART* aComponent, int aUnit ) 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->GetUnitBoundingBox( aUnit, m_deMorganConvert ); + EDA_RECT bBox = aComponent->GetUnitBoundingBox( 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; + const double scale = std::min( xscale, yscale ) * 0.85; dc.SetUserScale( scale, scale ); @@ -503,7 +568,7 @@ static wxDataViewItem GetNextItem( const wxDataViewCtrl& tree, const wxDataViewI else { // Walk up levels until we find one that has a next sibling. - for ( wxDataViewItem walk = item; walk.IsOk(); walk = tree.GetModel()->GetParent( walk ) ) + for( wxDataViewItem walk = item; walk.IsOk(); walk = tree.GetModel()->GetParent( walk ) ) { nextItem = GetNextSibling( tree, walk ); @@ -519,8 +584,8 @@ static wxDataViewItem GetNextItem( const wxDataViewCtrl& tree, const wxDataViewI static wxDataViewItem GetPrevSibling( const wxDataViewCtrl& tree, const wxDataViewItem& item ) { wxDataViewItemArray siblings; - wxDataViewItem invalid; - wxDataViewItem parent = tree.GetModel()->GetParent( item ); + wxDataViewItem invalid; + wxDataViewItem parent = tree.GetModel()->GetParent( item ); tree.GetModel()->GetChildren( parent, siblings ); @@ -542,8 +607,8 @@ static wxDataViewItem GetPrevSibling( const wxDataViewCtrl& tree, const wxDataVi static wxDataViewItem GetNextSibling( const wxDataViewCtrl& tree, const wxDataViewItem& item ) { wxDataViewItemArray siblings; - wxDataViewItem invalid; - wxDataViewItem parent = tree.GetModel()->GetParent( item ); + wxDataViewItem invalid; + wxDataViewItem parent = tree.GetModel()->GetParent( item ); tree.GetModel()->GetChildren( parent, siblings ); diff --git a/eeschema/dialogs/dialog_choose_component.h b/eeschema/dialogs/dialog_choose_component.h index 6af8869ac3..84f9f5cf2f 100644 --- a/eeschema/dialogs/dialog_choose_component.h +++ b/eeschema/dialogs/dialog_choose_component.h @@ -27,6 +27,7 @@ #include "dialog_shim.h" #include +#include class wxStaticBitmap; class wxTextCtrl; @@ -40,6 +41,7 @@ class wxButton; class wxTimer; class FOOTPRINT_PREVIEW_WIDGET; +class FOOTPRINT_SELECT_WIDGET; class LIB_ALIAS; class LIB_PART; class SCH_BASE_FRAME; @@ -82,7 +84,6 @@ class SCH_BASE_FRAME; class DIALOG_CHOOSE_COMPONENT : public DIALOG_SHIM { public: - /** * Create dialog to choose component. * @@ -92,30 +93,44 @@ public: * for documentation. * @param aDeMorganConvert preferred deMorgan conversion * (TODO: should happen in dialog) + * @param aAllowFieldEdits if false, all functions that allow the user to edit + * fields (currently just footprint selection) will not be available. */ DIALOG_CHOOSE_COMPONENT( SCH_BASE_FRAME* aParent, const wxString& aTitle, - CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, - int aDeMorganConvert ); + CMP_TREE_MODEL_ADAPTER::PTR& aAdapter, int aDeMorganConvert, bool aAllowFieldEdits ); ~DIALOG_CHOOSE_COMPONENT(); /** Function GetSelectedAlias * To be called after this dialog returns from ShowModal(). * + * For multi-unit components, if the user selects the component itself + * rather than picking an individual unit, 0 will be returned in aUnit. + * Beware that this is an invalid unit number - this should be replaced + * with whatever default is desired (usually 1). + * * @param aUnit if not NULL, the selected unit is filled in here. * @return the alias that has been selected, or NULL if there is none. */ LIB_ALIAS* GetSelectedAlias( int* aUnit ) const; + /** + * Get a list of fields edited by the user. + * @return vector of pairs; each.first = field ID, each.second = new value + */ + std::vector> GetFields() const; + /** Function IsExternalBrowserSelected * * @return true, iff the user pressed the thumbnail view of the component to * launch the component browser. */ - bool IsExternalBrowserSelected() const { return m_external_browser_requested; } + bool IsExternalBrowserSelected() const + { + return m_external_browser_requested; + } protected: - static constexpr int DblClickDelay = 100; // milliseconds wxPanel* ConstructLeftPanel( wxWindow* aParent ); @@ -123,6 +138,7 @@ protected: void OnInitDialog( wxInitDialogEvent& aEvent ); void OnCloseTimer( wxTimerEvent& aEvent ); + void OnProgressTimer( wxTimerEvent& aEvent ); void OnQueryText( wxCommandEvent& aEvent ); void OnQueryEnter( wxCommandEvent& aEvent ); @@ -136,11 +152,25 @@ protected: void OnSchViewDClick( wxMouseEvent& aEvent ); void OnSchViewPaint( wxPaintEvent& aEvent ); + void OnFootprintSelected( wxCommandEvent& aEvent ); + /** * Look up the footprint for a given alias and display it. */ void ShowFootprintFor( LIB_ALIAS* aAlias ); + /** + * Display the given footprint by name. + */ + void ShowFootprint( wxString const& aName ); + + /** + * Populate the footprint selector for a given alias. + * + * @param aAlias alias, or null to clear + */ + void PopulateFootprintSelector( LIB_ALIAS* aAlias ); + /** * If a wxDataViewitem is valid, select it and post a selection event. */ @@ -170,15 +200,20 @@ protected: wxDataViewCtrl* m_tree_ctrl; wxHtmlWindow* m_details_ctrl; wxPanel* m_sch_view_ctrl; - wxChoice* m_fp_sel_ctrl; + FOOTPRINT_SELECT_WIDGET* m_fp_sel_ctrl; FOOTPRINT_PREVIEW_WIDGET* m_fp_view_ctrl; SCH_BASE_FRAME* m_parent; CMP_TREE_MODEL_ADAPTER::PTR m_adapter; - int m_deMorganConvert; - bool m_external_browser_requested; + int m_deMorganConvert; + bool m_allow_field_edits; + bool m_external_browser_requested; + wxString m_fp_override; + static FOOTPRINT_ASYNC_LOADER m_fp_loader; + static std::unique_ptr m_fp_list; + std::vector> m_field_edits; }; #endif /* DIALOG_CHOOSE_COMPONENT_H */ diff --git a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp index e1a25a872c..381640da5a 100644 --- a/eeschema/dialogs/dialog_edit_component_in_schematic.cpp +++ b/eeschema/dialogs/dialog_edit_component_in_schematic.cpp @@ -281,14 +281,14 @@ void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnTestChipName( wxCommandEvent& event ) void DIALOG_EDIT_COMPONENT_IN_SCHEMATIC::OnSelectChipName( wxCommandEvent& event ) { - wxArrayString dummy; - int dummyunit = 1; - wxString chipname = m_parent->SelectComponentFromLibrary( NULL, dummy, dummyunit, - true, NULL, NULL ); - if( chipname.IsEmpty() ) + SCH_BASE_FRAME::HISTORY_LIST dummy; + + auto sel = m_parent->SelectComponentFromLibrary( NULL, dummy, true, 0, 0 ); + + if( sel.Name.IsEmpty() ) return; - chipnameTextCtrl->SetValue( chipname ); + chipnameTextCtrl->SetValue( sel.Name ); } diff --git a/eeschema/getpart.cpp b/eeschema/getpart.cpp index 22a9eaa520..42f1962cca 100644 --- a/eeschema/getpart.cpp +++ b/eeschema/getpart.cpp @@ -28,6 +28,7 @@ * @brief functions to get and place library components. */ +#include #include #include #include @@ -50,9 +51,10 @@ #include -wxString SCH_BASE_FRAME::SelectComponentFromLibBrowser( const SCHLIB_FILTER* aFilter, - LIB_ALIAS* aPreselectedAlias, - int* aUnit, int* aConvert ) +SCH_BASE_FRAME::COMPONENT_SELECTION SCH_BASE_FRAME::SelectComponentFromLibBrowser( + const SCHLIB_FILTER* aFilter, + LIB_ALIAS* aPreselectedAlias, + int aUnit, int aConvert ) { // Close any open non-modal Lib browser, and open a new one, in "modal" mode: LIB_VIEW_FRAME* viewlibFrame = (LIB_VIEW_FRAME*) Kiway().Player( FRAME_SCH_VIEWER, false ); @@ -71,38 +73,36 @@ wxString SCH_BASE_FRAME::SelectComponentFromLibBrowser( const SCHLIB_FILTER* aFi viewlibFrame->SetSelectedComponent( aPreselectedAlias->GetName() ); } - if( aUnit && *aUnit > 0 ) - viewlibFrame->SetUnit( *aUnit ); + if( aUnit > 0 ) + viewlibFrame->SetUnit( aUnit ); - if( aConvert && *aConvert > 0 ) - viewlibFrame->SetConvert( *aConvert ); + if( aConvert > 0 ) + viewlibFrame->SetConvert( aConvert ); viewlibFrame->Refresh(); - wxString cmpname; + COMPONENT_SELECTION sel; - if( viewlibFrame->ShowModal( &cmpname, this ) ) + if( viewlibFrame->ShowModal( &sel.Name, this ) ) { - if( aUnit ) - *aUnit = viewlibFrame->GetUnit(); - - if( aConvert ) - *aConvert = viewlibFrame->GetConvert(); + sel.Unit = viewlibFrame->GetUnit(); + sel.Convert = viewlibFrame->GetConvert(); } viewlibFrame->Destroy(); - return cmpname; + return sel; } -wxString SCH_BASE_FRAME::SelectComponentFromLibrary( const SCHLIB_FILTER* aFilter, - wxArrayString& aHistoryList, - int& aHistoryLastUnit, - bool aUseLibBrowser, - int* aUnit, - int* aConvert, - const wxString& aHighlight ) +SCH_BASE_FRAME::COMPONENT_SELECTION SCH_BASE_FRAME::SelectComponentFromLibrary( + const SCHLIB_FILTER* aFilter, + std::vector& aHistoryList, + bool aUseLibBrowser, + int aUnit, + int aConvert, + const wxString& aHighlight, + bool aAllowFields ) { wxString dialogTitle; PART_LIBS* libs = Prj().SchLibs(); @@ -141,56 +141,68 @@ wxString SCH_BASE_FRAME::SelectComponentFromLibrary( const SCHLIB_FILTER* aFilte if( !aHistoryList.empty() ) { - adapter->AddAliasList( "-- " + _( "History" ) + " --", aHistoryList, NULL ); - adapter->SetPreselectNode( aHistoryList[0], aHistoryLastUnit ); + wxArrayString history_list; + + for( auto const& i : aHistoryList ) + history_list.push_back( i.Name ); + + adapter->AddAliasList( "-- " + _( "History" ) + " --", history_list, NULL ); + adapter->SetPreselectNode( aHistoryList[0].Name, aHistoryList[0].Unit ); } if( !aHighlight.IsEmpty() ) adapter->SetPreselectNode( aHighlight, /* aUnit */ 0 ); - const int deMorgan = aConvert ? *aConvert : 1; dialogTitle.Printf( _( "Choose Component (%d items loaded)" ), adapter->GetComponentsCount() ); - DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, deMorgan ); + DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, aConvert, aAllowFields ); - if( dlg.ShowModal() == wxID_CANCEL ) - return wxEmptyString; + if( dlg.ShowQuasiModal() == wxID_CANCEL ) + return COMPONENT_SELECTION(); + + COMPONENT_SELECTION sel; + LIB_ALIAS* const alias = dlg.GetSelectedAlias( &sel.Unit ); + + if( alias->GetPart()->IsMulti() && sel.Unit == 0 ) + sel.Unit = 1; + + sel.Fields = dlg.GetFields(); - wxString cmpName; - LIB_ALIAS* const alias = dlg.GetSelectedAlias( aUnit ); if ( alias ) - cmpName = alias->GetName(); + sel.Name = alias->GetName(); if( dlg.IsExternalBrowserSelected() ) // User requested component browser. - cmpName = SelectComponentFromLibBrowser( aFilter, alias, aUnit, aConvert); + sel = SelectComponentFromLibBrowser( aFilter, alias, sel.Unit, sel.Convert ); - if( !cmpName.empty() ) + if( !sel.Name.empty() ) { - AddHistoryComponentName( aHistoryList, cmpName ); + aHistoryList.erase( + std::remove_if( + aHistoryList.begin(), + aHistoryList.end(), + [ &sel ]( COMPONENT_SELECTION const& i ) { return i.Name == sel.Name; } ), + aHistoryList.end() ); - if ( aUnit ) - aHistoryLastUnit = *aUnit; + aHistoryList.insert( aHistoryList.begin(), sel ); } - return cmpName; + return sel; } -SCH_COMPONENT* SCH_EDIT_FRAME::Load_Component( wxDC* aDC, - const SCHLIB_FILTER* aFilter, - wxArrayString& aHistoryList, - int& aHistoryLastUnit, - bool aUseLibBrowser ) +SCH_COMPONENT* SCH_EDIT_FRAME::Load_Component( + wxDC* aDC, + const SCHLIB_FILTER* aFilter, + SCH_BASE_FRAME::HISTORY_LIST& aHistoryList, + bool aUseLibBrowser ) { - int unit = 1; - int convert = 1; SetRepeatItem( NULL ); m_canvas->SetIgnoreMouseEvents( true ); - wxString name = SelectComponentFromLibrary( aFilter, aHistoryList, aHistoryLastUnit, - aUseLibBrowser, &unit, &convert ); + auto sel = SelectComponentFromLibrary( aFilter, aHistoryList, + aUseLibBrowser, 1, 1 ); - if( name.IsEmpty() ) + if( sel.Name.IsEmpty() ) { m_canvas->SetIgnoreMouseEvents( false ); m_canvas->MoveCursorToCrossHair(); @@ -205,19 +217,19 @@ SCH_COMPONENT* SCH_EDIT_FRAME::Load_Component( wxDC* aDC, if( aFilter ) libsource = aFilter->GetLibSource(); - LIB_PART* part = Prj().SchLibs()->FindLibPart( LIB_ID( wxEmptyString, name ), libsource ); + LIB_PART* part = Prj().SchLibs()->FindLibPart( LIB_ID( wxEmptyString, sel.Name ), libsource ); if( !part ) { wxString msg = wxString::Format( _( "Failed to find part '%s' in library" ), - GetChars( name ) + GetChars( sel.Name ) ); wxMessageBox( msg ); return NULL; } - SCH_COMPONENT* component = new SCH_COMPONENT( *part, m_CurrentSheet, unit, convert, + SCH_COMPONENT* component = new SCH_COMPONENT( *part, m_CurrentSheet, sel.Unit, sel.Convert, GetCrossHairPosition(), true ); // Set the m_ChipName value, from component name in lib, for aliases @@ -225,14 +237,23 @@ SCH_COMPONENT* SCH_EDIT_FRAME::Load_Component( wxDC* aDC, // alias exists because its root component was found LIB_ID libId; - libId.SetLibItemName( name, false ); + libId.SetLibItemName( sel.Name, false ); component->SetLibId( libId ); // Be sure the link to the corresponding LIB_PART is OK: component->Resolve( Prj().SchLibs() ); + // Set any fields that have been modified + for( auto const& i : sel.Fields ) + { + auto field = component->GetField( i.first ); + + if( field ) + field->SetText( i.second ); + } + // Set the component value that can differ from component name in lib, for aliases - component->GetField( VALUE )->SetText( name ); + component->GetField( VALUE )->SetText( sel.Name ); MSG_PANEL_ITEMS items; diff --git a/eeschema/libedit.cpp b/eeschema/libedit.cpp index 2f454c3ace..fc9aa63e68 100644 --- a/eeschema/libedit.cpp +++ b/eeschema/libedit.cpp @@ -105,7 +105,6 @@ bool LIB_EDIT_FRAME::LoadComponentFromCurrentLib( LIB_ALIAS* aLibEntry ) void LIB_EDIT_FRAME::LoadOneLibraryPart( wxCommandEvent& event ) { - wxString cmp_name; LIB_ALIAS* libEntry = NULL; m_canvas->EndMouseCapture( ID_NO_TOOL_SELECTED, m_canvas->GetDefaultCursor() ); @@ -130,14 +129,13 @@ void LIB_EDIT_FRAME::LoadOneLibraryPart( wxCommandEvent& event ) LIB_PART* current_part = GetCurPart(); wxString part_name = current_part ? current_part->GetName() : wxString( wxEmptyString ); - wxArrayString dummyHistoryList; - int dummyLastUnit; + SCH_BASE_FRAME::HISTORY_LIST dummyHistoryList; SCHLIB_FILTER filter; filter.LoadFrom( lib->GetName() ); - cmp_name = SelectComponentFromLibrary( &filter, dummyHistoryList, dummyLastUnit, - true, NULL, NULL, part_name ); + auto sel = SelectComponentFromLibrary( &filter, dummyHistoryList, + true, 0, 0, part_name, false ); - if( cmp_name.IsEmpty() ) + if( sel.Name.IsEmpty() ) return; GetScreen()->ClrModify(); @@ -148,14 +146,14 @@ void LIB_EDIT_FRAME::LoadOneLibraryPart( wxCommandEvent& event ) m_aliasName.Empty(); // Load the new library component - libEntry = lib->FindAlias( cmp_name ); + libEntry = lib->FindAlias( sel.Name ); PART_LIB* searchLib = lib; if( !libEntry ) { // Not found in the active library: search inside the full list // (can happen when using Viewlib to load a component) - libEntry = Prj().SchLibs()->FindLibraryAlias( LIB_ID( wxEmptyString, cmp_name ) ); + libEntry = Prj().SchLibs()->FindLibraryAlias( LIB_ID( wxEmptyString, sel.Name ) ); if( libEntry ) { @@ -175,7 +173,7 @@ void LIB_EDIT_FRAME::LoadOneLibraryPart( wxCommandEvent& event ) if( !libEntry ) { wxString msg = wxString::Format( _( "Part name '%s' not found in library '%s'" ), - GetChars( cmp_name ), + GetChars( sel.Name ), GetChars( searchLib->GetName() ) ); DisplayError( this, msg ); return; @@ -540,9 +538,9 @@ void LIB_EDIT_FRAME::DeleteOnePart( wxCommandEvent& event ) wxString dialogTitle; dialogTitle.Printf( _( "Delete Component (%u items loaded)" ), adapter->GetComponentsCount() ); - DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, m_convert ); + DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, m_convert, false ); - if( dlg.ShowModal() == wxID_CANCEL ) + if( dlg.ShowQuasiModal() == wxID_CANCEL ) { return; } diff --git a/eeschema/onleftclick.cpp b/eeschema/onleftclick.cpp index 9aec70733c..5721d6c4d1 100644 --- a/eeschema/onleftclick.cpp +++ b/eeschema/onleftclick.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2011 Wayne Stambaugh - * Copyright (C) 1992-2015 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -53,11 +53,8 @@ // TODO(hzeller): These pairs of elmenets should be represented by an object, but don't want // to refactor too much right now to not get in the way with other code changes. -static wxArrayString s_CmpNameList; -static int s_CmpLastUnit; - -static wxArrayString s_PowerNameList; -static int s_LastPowerUnit; +static SCH_BASE_FRAME::HISTORY_LIST s_CmpNameList; +static SCH_BASE_FRAME::HISTORY_LIST s_PowerNameList; void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) @@ -306,7 +303,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) if( (item == NULL) || (item->GetFlags() == 0) ) { GetScreen()->SetCurItem( Load_Component( aDC, NULL, - s_CmpNameList, s_CmpLastUnit, true ) ); + s_CmpNameList, true ) ); m_canvas->SetAutoPanRequest( true ); } else @@ -321,7 +318,7 @@ void SCH_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) SCHLIB_FILTER filter; filter.FilterPowerParts( true ); GetScreen()->SetCurItem( Load_Component( aDC, &filter, - s_PowerNameList, s_LastPowerUnit, false ) ); + s_PowerNameList, false ) ); m_canvas->SetAutoPanRequest( true ); } else diff --git a/eeschema/sch_base_frame.h b/eeschema/sch_base_frame.h index b5a5accbff..7757f48f78 100644 --- a/eeschema/sch_base_frame.h +++ b/eeschema/sch_base_frame.h @@ -122,31 +122,55 @@ public: void UpdateStatusBar() override; + + struct COMPONENT_SELECTION + { + wxString Name; + int Unit; + int Convert; + + std::vector> Fields; + + COMPONENT_SELECTION(): + Name(""), + Unit(1), + Convert(1) + {} + }; + + typedef std::vector HISTORY_LIST; + /** * Function SelectComponentFromLib * Calls the library viewer to select component to import into schematic. * if the library viewer is currently running, it is closed and reopened * in modal mode. + * + * aAllowFields chooses whether or not features that permit the user to edit + * fields (e.g. footprint selection) should be enabled. This should be false + * when they would have no effect, for example loading a part into libedit. + * * @param aFilter is a SCHLIB_FILTER filter to pass the allowed library names * and/or the library name to load the component from and/or some other filter * if NULL, no filtering. - * @param aHistoryList list of previously loaded components - * @param aHistoryLastUnit remembering last unit in last component. + * @param aHistoryList list of previously loaded components - will be edited * @param aUseLibBrowser bool to call the library viewer to select the component - * @param aUnit a pointer to int to return the selected unit (if any) - * @param aConvert a pointer to int to return the selected De Morgan shape (if any) + * @param aUnit preselected unit + * @param aConvert preselected De Morgan shape * @param aHighlight name of component to highlight in the list. * highlights none if there isn't one by that name + * @param aAllowFields whether to allow field editing in the dialog * - * @return the component name + * @return the selected component */ - wxString SelectComponentFromLibrary( const SCHLIB_FILTER* aFilter, - wxArrayString& aHistoryList, - int& aHistoryLastUnit, - bool aUseLibBrowser, - int* aUnit, - int* aConvert, - const wxString& aHighlight = wxEmptyString ); + COMPONENT_SELECTION SelectComponentFromLibrary( + const SCHLIB_FILTER* aFilter, + std::vector& aHistoryList, + bool aUseLibBrowser, + int aUnit, + int aConvert, + const wxString& aHighlight = wxEmptyString, + bool aAllowFields = true ); protected: @@ -158,15 +182,14 @@ protected: * @param aFilter is a filter to pass the allowed library names * and/or some other filter * @param aPreselectedAlias Preselected component alias. NULL if none. - * @param aUnit Pointer to Unit-number. Input is the pre-selected unit, output - * is the finally selected unit by the user. Can be NULL. - * @param aConvert Pointer to deMorgan conversion. Input is what is pre-selected, - * output is the finally selected deMorgan type by the user. - * @return the component name + * @param aUnit preselected unit + * @param aConvert preselected deMorgan conversion + * @return the selected component */ - wxString SelectComponentFromLibBrowser( const SCHLIB_FILTER* aFilter, - LIB_ALIAS* aPreselectedAlias, - int* aUnit, int* aConvert ); + COMPONENT_SELECTION SelectComponentFromLibBrowser( + const SCHLIB_FILTER* aFilter, + LIB_ALIAS* aPreselectedAlias, + int aUnit, int aConvert ); /** * Function OnOpenLibraryViewer diff --git a/eeschema/schframe.h b/eeschema/schframe.h index 3562908994..4fe4a6546b 100644 --- a/eeschema/schframe.h +++ b/eeschema/schframe.h @@ -3,7 +3,7 @@ * * Copyright (C) 2015 Jean-Pierre Charras, jp.charras wanadoo.fr * Copyright (C) 2008-2015 Wayne Stambaugh - * Copyright (C) 2004-2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2004-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 @@ -1093,17 +1093,15 @@ private: * to load the component from and/or some other filters * if NULL, no filtering. * @param aHistoryList list remembering recently used component names. - * @param aHistoryLastUnit remembering last unit in last component. * @param aUseLibBrowser is the flag to determine if the library browser should be launched. * @return a pointer the SCH_COMPONENT object selected or NULL if no component was selected. * (TODO(hzeller): This really should be a class doing history, but didn't * want to change too much while other refactoring is going on) */ - SCH_COMPONENT* Load_Component( wxDC* aDC, - const SCHLIB_FILTER* aFilter, - wxArrayString& aHistoryList, - int& aHistoryLastUnit, - bool aUseLibBrowser ); + SCH_COMPONENT* Load_Component( wxDC* aDC, + const SCHLIB_FILTER* aFilter, + SCH_BASE_FRAME::HISTORY_LIST& aHistoryList, + bool aUseLibBrowser ); /** * Function EditComponent diff --git a/eeschema/viewlibs.cpp b/eeschema/viewlibs.cpp index 06ded6082c..4badd6b3a8 100644 --- a/eeschema/viewlibs.cpp +++ b/eeschema/viewlibs.cpp @@ -59,9 +59,9 @@ void LIB_VIEW_FRAME::OnSelectSymbol( wxCommandEvent& aEvent ) dialogTitle.Printf( _( "Choose Component (%d items loaded)" ), adapter->GetComponentsCount() ); - DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, m_convert ); + DIALOG_CHOOSE_COMPONENT dlg( this, dialogTitle, adapter, m_convert, false ); - if( dlg.ShowModal() == wxID_CANCEL ) + if( dlg.ShowQuasiModal() == wxID_CANCEL ) return; /// @todo: The unit selection gets reset to 1 by SetSelectedComponent() so the unit diff --git a/include/eda_pattern_match.h b/include/eda_pattern_match.h index 6bca8acd97..367c5602b5 100644 --- a/include/eda_pattern_match.h +++ b/include/eda_pattern_match.h @@ -1,8 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2015 Chris Pavlina - * Copyright (C) 2015 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2015-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 @@ -52,6 +51,11 @@ public: */ virtual bool SetPattern( const wxString& aPattern ) = 0; + /** + * Return the pattern passed to SetPattern(). + */ + virtual wxString const& GetPattern() const = 0; + /** * Return the location of a match iff a given candidate string matches the set pattern. * Otherwise, return EDA_PATTERN_NOT_FOUND. @@ -67,6 +71,7 @@ class EDA_PATTERN_MATCH_SUBSTR : public EDA_PATTERN_MATCH { public: virtual bool SetPattern( const wxString& aPattern ) override; + virtual wxString const& GetPattern() const override; virtual int Find( const wxString& aCandidate ) const override; protected: @@ -81,6 +86,7 @@ class EDA_PATTERN_MATCH_REGEX : public EDA_PATTERN_MATCH { public: virtual bool SetPattern( const wxString& aPattern ) override; + virtual wxString const& GetPattern() const override; virtual int Find( const wxString& aCandidate ) const override; protected: @@ -93,7 +99,11 @@ class EDA_PATTERN_MATCH_WILDCARD : public EDA_PATTERN_MATCH_REGEX { public: virtual bool SetPattern( const wxString& aPattern ) override; + virtual wxString const& GetPattern() const override; virtual int Find( const wxString& aCandidate ) const override; + +protected: + wxString m_wildcard_pattern; }; diff --git a/include/footprint_filter.h b/include/footprint_filter.h new file mode 100644 index 0000000000..7a08dfa7b8 --- /dev/null +++ b/include/footprint_filter.h @@ -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 . + */ + +#ifndef FOOTPRINT_FILTER_H +#define FOOTPRINT_FILTER_H + +#include +#include +#include + + +/** + * 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 + { + 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> m_footprint_filters; +}; + +#endif // FOOTPRINT_FILTER_H diff --git a/include/footprint_info.h b/include/footprint_info.h index 2559752f06..ecdaaaa0e5 100644 --- a/include/footprint_info.h +++ b/include/footprint_info.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2011 Jean-Pierre Charras, - * Copyright (C) 1992-2011 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -32,47 +32,55 @@ #include +#include +#include #include #include +#include + +#include +#include +#include -#define USE_FPI_LAZY 0 // 1:yes lazy, 0:no early +#define USE_FPI_LAZY 0 // 1:yes lazy, 0:no early class FP_LIB_TABLE; class FOOTPRINT_LIST; +class FOOTPRINT_LIST_IMPL; +class FOOTPRINT_ASYNC_LOADER; class wxTopLevelWindow; +class KIWAY; /* - * Class FOOTPRINT_INFO - * is a helper class to handle the list of footprints available in libraries. It stores - * footprint names, doc and keywords + * Helper class to handle the list of footprints available in libraries. It stores + * footprint names, doc and keywords. + * + * This is a virtual class; its implementation lives in pcbnew/footprint_info_impl.cpp. + * To get instances of these classes, see FOOTPRINT_LIST::GetInstance(). */ -class FOOTPRINT_INFO +class APIEXPORT FOOTPRINT_INFO { friend bool operator<( const FOOTPRINT_INFO& item1, const FOOTPRINT_INFO& item2 ); public: + virtual ~FOOTPRINT_INFO() + { + } // These two accessors do not have to call ensure_loaded(), because constructor // fills in these fields: - const wxString& GetFootprintName() const { return m_fpname; } - const wxString& GetNickname() const { return m_nickname; } - - FOOTPRINT_INFO( 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 ) + const wxString& GetFootprintName() const { -#if !USE_FPI_LAZY - load(); -#endif + return m_fpname; + } + + const wxString& GetNickname() const + { + return m_nickname; } const wxString& GetDoc() @@ -106,8 +114,7 @@ public: } /** - * Function InLibrary - * tests if the #FOOTPRINT_INFO object was loaded from \a aLibrary. + * Test if the #FOOTPRINT_INFO object was loaded from \a aLibrary. * * @param aLibrary is the nickname of the library to test. * @@ -116,8 +123,7 @@ public: */ bool InLibrary( const wxString& aLibrary ) const; -private: - +protected: void ensure_loaded() { if( !m_loaded ) @@ -125,19 +131,19 @@ private: } /// lazily load stuff not filled in by constructor. This may throw IO_ERRORS. - void load(); + virtual void load() = 0; - FOOTPRINT_LIST* m_owner; ///< provides access to FP_LIB_TABLE + FOOTPRINT_LIST* m_owner; ///< provides access to FP_LIB_TABLE - bool m_loaded; + bool m_loaded; - wxString m_nickname; ///< library as known in FP_LIB_TABLE - wxString m_fpname; ///< Module name. - int m_num; ///< Order number in the display list. - int m_pad_count; ///< Number of pads - int m_unique_pad_count; ///< Number of unique pads - wxString m_doc; ///< Footprint description. - wxString m_keywords; ///< Footprint keywords. + wxString m_nickname; ///< library as known in FP_LIB_TABLE + wxString m_fpname; ///< Module name. + int m_num; ///< Order number in the display list. + int m_pad_count; ///< Number of pads + int m_unique_pad_count; ///< Number of unique pads + wxString m_doc; ///< Footprint description. + wxString m_keywords; ///< Footprint keywords. }; @@ -154,89 +160,91 @@ inline bool operator<( const FOOTPRINT_INFO& item1, const FOOTPRINT_INFO& item2 /** - * Class FOOTPRINT_LIST - * holds a list of FOOTPRINT_INFO objects, along with a list of IO_ERRORs or + * Holds a list of FOOTPRINT_INFO objects, along with a list of IO_ERRORs or * PARSE_ERRORs that were thrown acquiring the FOOTPRINT_INFOs. + * + * This is a virtual class; its implementation lives in pcbnew/footprint_info_impl.cpp. + * To get instances of these classes, see FOOTPRINT_LIST::GetInstance(). */ -class FOOTPRINT_LIST +class APIEXPORT FOOTPRINT_LIST { - FP_LIB_TABLE* m_lib_table; ///< no ownership - volatile int m_error_count; ///< thread safe to read. + friend class FOOTPRINT_ASYNC_LOADER; - typedef boost::ptr_vector< FOOTPRINT_INFO > FPILIST; - typedef boost::ptr_vector< IO_ERROR > ERRLIST; +protected: + FP_LIB_TABLE* m_lib_table; ///< no ownership + + typedef std::vector> FPILIST; + typedef SYNC_QUEUE> ERRLIST; FPILIST m_list; - ERRLIST m_errors; ///< some can be PARSE_ERRORs also + ERRLIST m_errors; ///< some can be PARSE_ERRORs also - MUTEX m_errors_lock; - MUTEX m_list_lock; - - /** - * Function loader_job - * loads footprints from @a aNicknameList and calls AddItem() on to help fill - * m_list. - * - * @param aNicknameList is a wxString[] holding libraries to load all footprints from. - * @param aJobZ is the size of the job, i.e. the count of nicknames. - */ - void loader_job( const wxString* aNicknameList, int aJobZ ); - - void addItem( FOOTPRINT_INFO* aItem ) - { - // m_list is not thread safe, and this function is called from - // worker threads, lock m_list. - MUTLOCK lock( m_list_lock ); - - m_list.push_back( aItem ); - } + MUTEX m_list_lock; public: + FOOTPRINT_LIST() : m_lib_table( 0 ) + { + } - FOOTPRINT_LIST() : - m_lib_table( 0 ), - m_error_count( 0 ) + virtual ~FOOTPRINT_LIST() { } /** - * Function GetCount * @return the number of items stored in list */ - unsigned GetCount() const { return m_list.size(); } + unsigned GetCount() const + { + return m_list.size(); + } /// Was forced to add this by modview_frame.cpp - const FPILIST& GetList() const { return m_list; } + const FPILIST& GetList() const + { + return m_list; + } /** - * Function GetModuleInfo + * Get info for a module by name. * @param aFootprintName = the footprint name inside the FOOTPRINT_INFO of interest. * @return FOOTPRINT_INF* - the item stored in list if found */ FOOTPRINT_INFO* GetModuleInfo( const wxString& aFootprintName ); /** - * Function GetItem + * Get info for a module by index. * @param aIdx = index of the given item * @return the aIdx item in list */ - FOOTPRINT_INFO& GetItem( unsigned aIdx ) { return m_list[aIdx]; } + FOOTPRINT_INFO& GetItem( unsigned aIdx ) + { + return *m_list[aIdx]; + } /** - * Function AddItem - * add aItem in list + * Add aItem to list * @param aItem = item to add */ void AddItem( FOOTPRINT_INFO* aItem ); - unsigned GetErrorCount() const { return m_errors.size(); } + unsigned GetErrorCount() const + { + return m_errors.size(); + } - const IO_ERROR* GetError( unsigned aIdx ) const { return &m_errors[aIdx]; } + std::unique_ptr PopError() + { + auto item = m_errors.pop(); + + if( item ) + return std::move( *item ); + else + return std::unique_ptr(); + } /** - * Function ReadFootprintFiles - * reads all the footprints provided by the combination of aTable and aNickname. + * Read all the footprints provided by the combination of aTable and aNickname. * * @param aTable defines all the libraries. * @param aNickname is the library to read from, or if NULL means read all @@ -245,11 +253,133 @@ public: * some number of errors. If true, it does not mean there were no errors, check * GetErrorCount() for that, should be zero to indicate success. */ - bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ); + virtual bool ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ) = 0; void DisplayErrors( wxTopLevelWindow* aCaller = NULL ); - FP_LIB_TABLE* GetTable() const { return m_lib_table; } + FP_LIB_TABLE* GetTable() const + { + return m_lib_table; + } + + /** + * Factory function to return a new FOOTPRINT_LIST via Kiway. NOT guaranteed + * to succeed; will return null if the kiface is not available. + * + * @param aKiway - active kiway instance + */ + static std::unique_ptr GetInstance( KIWAY& aKiway ); + +protected: + /** + * Launch worker threads to load footprints. Part of the + * FOOTPRINT_ASYNC_LOADER implementation. + */ + virtual void StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname, + FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads ) = 0; + + /** + * Join worker threads. Part of the FOOTPRINT_ASYNC_LOADER implementation. + */ + virtual bool JoinWorkers() = 0; + + + /** + * Return the number of libraries finished (successfully or otherwise). + */ + virtual size_t CountFinished() = 0; }; -#endif // FOOTPRINT_INFO_H_ + +/** + * This class can be used to populate a FOOTPRINT_LIST asynchronously. + * Constructing one, calling .Start(), then waiting until it reports completion + * is equivalent to calling FOOTPRINT_LIST::ReadFootprintFiles(). + */ +class APIEXPORT FOOTPRINT_ASYNC_LOADER +{ + friend class FOOTPRINT_LIST; + friend class FOOTPRINT_LIST_IMPL; + + FOOTPRINT_LIST* m_list; + std::function m_completion_cb; + std::string m_last_table; + + bool m_started; ///< True if Start() has been called - does not reset + int m_total_libs; + +public: + /** + * Construct an asynchronous loader. + */ + FOOTPRINT_ASYNC_LOADER(); + + /** + * Assign a FOOTPRINT_LIST to the loader. This does not take ownership of + * the list. + */ + void SetList( FOOTPRINT_LIST* aList ); + + /** + * Launch the worker threads. + * @param aTable defines all the libraries. + * @param aNickname is the library to read from, or if NULL means read all + * footprints from all known libraries in aTable. + * @param aNThreads is the number of worker threads. + */ + void Start( FP_LIB_TABLE* aTable, wxString const* aNickname = nullptr, + unsigned aNThreads = DEFAULT_THREADS ); + + /** + * Wait until the worker threads are finished, and then perform any required + * single-threaded finishing on the list. This must be called before using + * the list, even if the completion callback was used! + * + * It is safe to call this method from a thread, but it is not safe to use + * the list from ANY thread until it completes. It is recommended to call + * this from the main thread because of this. + * + * It is safe to call this multiple times, but after the first it will + * always return true. + * + * @return true if no errors occurred + */ + bool Join(); + + /** + * Get the current completion percentage. 0 and 100 are reserved values: + * 0 will only be returned if Start() has not yet been called, and 100 + * will only be returned if totally complete (i.e. rounding errors will + * never cause a 100% progress despite not being complete). + * + * If there are no libraries at all, returns 100 (as loading zero libraries + * is always complete). + * + * Threadsafe. + */ + int GetProgress() const; + + /** + * Set a callback to receive notice when loading is complete. + * + * Callback MUST be threadsafe, and must be set before calling Start + * if you want to use it (it is safe not to set it at all). + */ + void SetCompletionCallback( std::function aCallback ); + + /** + * Return true if the given table is the same as the last table loaded. + * Useful for checking if the table has been modified and needs to be + * reloaded. + */ + bool IsSameTable( FP_LIB_TABLE* aOther ); + + /** + * Default number of worker threads. Determined empirically (by dickelbeck): + * More than 6 is not significantly faster, less than 6 is likely slower. + */ + static constexpr unsigned DEFAULT_THREADS = 6; +}; + + +#endif // FOOTPRINT_INFO_H_ diff --git a/include/fp_lib_table.h b/include/fp_lib_table.h index 9162aae4f4..bb87ed4bc7 100644 --- a/include/fp_lib_table.h +++ b/include/fp_lib_table.h @@ -3,7 +3,7 @@ * * Copyright (C) 2010-2012 SoftPLC Corporation, Dick Hollenbeck * Copyright (C) 2012-2016 Wayne Stambaugh - * Copyright (C) 2012-2016 KiCad Developers, see change_log.txt for contributors. + * Copyright (C) 2012-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 @@ -145,6 +145,19 @@ public: */ wxArrayString FootprintEnumerate( const wxString& aNickname ); + /** + * Function PrefetchLib + * If possible, prefetches the specified library (e.g. performing downloads). Does not parse. + * Threadsafe. + * + * This is a no-op for libraries that cannot be prefetched. + * + * @param aNickname is a locator for the library; it is a name in LIB_TABLE_ROW. + * + * @throw IO_ERROR if there is an error prefetching the library. + */ + void PrefetchLib( const wxString& aNickname ); + /** * Function FootprintLoad * diff --git a/include/kiface_ids.h b/include/kiface_ids.h new file mode 100644 index 0000000000..bea9129439 --- /dev/null +++ b/include/kiface_ids.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 diff --git a/include/project.h b/include/project.h index e3fd29230b..491abe4a2e 100644 --- a/include/project.h +++ b/include/project.h @@ -1,7 +1,7 @@ /* * This program source code file is part of KiCad, a free EDA CAD application. * - * Copyright (C) 2014 KiCad Developers, see CHANGELOG.TXT for contributors. + * Copyright (C) 2014-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 @@ -20,7 +20,6 @@ * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ - #ifndef PROJECT_H_ #define PROJECT_H_ @@ -39,6 +38,7 @@ class FP_LIB_TABLE; class PART_LIBS; class SEARCH_STACK; class S3D_CACHE; +class KIWAY; #define VTBL_ENTRY virtual @@ -235,6 +235,11 @@ public: */ VTBL_ENTRY const wxString AbsolutePath( const wxString& aFileName ) const; + /** + * Return the table of footprint libraries. Requires an active Kiway as + * this is fetched from pcbnew. + */ + VTBL_ENTRY FP_LIB_TABLE* PcbFootprintLibs( KIWAY& aKiway ); //-------------------------------------------------------- @@ -250,7 +255,10 @@ public: // functions can get linked into the KIFACE that needs them, and only there. // In fact, the other KIFACEs don't even know they exist. #if defined(PCBNEW) || defined(CVPCB) - // These are all prefaced with "Pcb" + /** + * Return the table of footprint libraries without Kiway, only from within + * pcbnew. + */ FP_LIB_TABLE* PcbFootprintLibs(); /** diff --git a/include/sync_queue.h b/include/sync_queue.h new file mode 100644 index 0000000000..55772f6e40 --- /dev/null +++ b/include/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 . + */ + +#ifndef SYNC_QUEUE_H +#define SYNC_QUEUE_H + +#include +#include +#include + +/** + * 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 class SYNC_QUEUE +{ + typedef std::lock_guard GUARD; + + std::queue 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 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 diff --git a/include/widgets/footprint_choice.h b/include/widgets/footprint_choice.h new file mode 100644 index 0000000000..a2427f6fcb --- /dev/null +++ b/include/widgets/footprint_choice.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 . + */ + +#ifndef FOOTPRINT_CHOICE_H +#define FOOTPRINT_CHOICE_H + +#include + +/** + * 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 diff --git a/include/widgets/footprint_select_widget.h b/include/widgets/footprint_select_widget.h new file mode 100644 index 0000000000..e5bbbaa3f1 --- /dev/null +++ b/include/widgets/footprint_select_widget.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 . + */ + +#ifndef FOOTPRINT_SELECT_WIDGET_H +#define FOOTPRINT_SELECT_WIDGET_H + +#include +#include +#include +#include +#include + +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& 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 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& 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 diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index c0ce9ad19e..20769c6a42 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -238,6 +238,7 @@ set( PCBNEW_CLASS_SRCS edtxtmod.cpp event_handlers_tracks_vias_sizes.cpp files.cpp + footprint_info_impl.cpp globaleditpad.cpp highlight.cpp hotkeys.cpp diff --git a/pcbnew/footprint_info_impl.cpp b/pcbnew/footprint_info_impl.cpp new file mode 100644 index 0000000000..108a191bae --- /dev/null +++ b/pcbnew/footprint_info_impl.cpp @@ -0,0 +1,230 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 Jean-Pierre Charras, + * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck + * 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 . + */ + + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +void FOOTPRINT_INFO_IMPL::load() +{ + FP_LIB_TABLE* fptable = m_owner->GetTable(); + + wxASSERT( fptable ); + + std::unique_ptr 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 aFunc ) +{ + try + { + aFunc(); + } + catch( const IO_ERROR& ioe ) + { + m_errors.move_push( std::make_unique( 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( 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> queue_parsed; + std::vector 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( 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 const& lhs, + std::unique_ptr 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(); +} diff --git a/pcbnew/footprint_info_impl.h b/pcbnew/footprint_info_impl.h new file mode 100644 index 0000000000..a2ec466b49 --- /dev/null +++ b/pcbnew/footprint_info_impl.h @@ -0,0 +1,94 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2011 Jean-Pierre Charras, + * 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 . + */ + +#ifndef FOOTPRINT_INFO_IMPL_H +#define FOOTPRINT_INFO_IMPL_H + +#include +#include +#include +#include +#include + +#include +#include + +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 m_threads; + SYNC_QUEUE m_queue_in; + SYNC_QUEUE 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 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 diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp index cefc620e0f..f95529f97c 100644 --- a/pcbnew/github/github_plugin.cpp +++ b/pcbnew/github/github_plugin.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2015 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-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 @@ -165,6 +165,18 @@ wxArrayString GITHUB_PLUGIN::FootprintEnumerate( } +void GITHUB_PLUGIN::PrefetchLib( + const wxString& aLibraryPath, const PROPERTIES* aProperties ) +{ + if( m_lib_path != aLibraryPath ) + { + m_zip_image.clear(); + } + + remoteGetZip( aLibraryPath ); +} + + MODULE* GITHUB_PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties ) { @@ -370,9 +382,14 @@ void GITHUB_PLUGIN::cacheLib( const wxString& aLibraryPath, const PROPERTIES* aP { delete m_gh_cache; m_gh_cache = 0; - m_pretty_dir.clear(); + if( !m_lib_path.empty() ) + { + // Library path wasn't empty before - it's been changed. Flush out the prefetch cache. + m_zip_image.clear(); + } + if( aProperties ) { UTF8 pretty_dir; @@ -516,6 +533,9 @@ void GITHUB_PLUGIN::remoteGetZip( const wxString& aRepoURL ) throw( IO_ERROR ) { std::string zip_url; + if( !m_zip_image.empty() ) + return; + if( !repoURL_zipURL( aRepoURL, &zip_url ) ) { wxString msg = wxString::Format( _( "Unable to parse URL:\n'%s'" ), GetChars( aRepoURL ) ); diff --git a/pcbnew/github/github_plugin.h b/pcbnew/github/github_plugin.h index f795a9f66f..3ec8f42d65 100644 --- a/pcbnew/github/github_plugin.h +++ b/pcbnew/github/github_plugin.h @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-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 @@ -170,6 +170,9 @@ public: wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL ) override; + void PrefetchLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties = NULL ) override; + MODULE* FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties ) override; @@ -215,7 +218,8 @@ protected: /** * Function remoteGetZip * fetches a zip file image from a github repo synchronously. The byte image - * is received into the m_input_stream. + * is received into the m_input_stream. If the image has already been stored, + * do nothing. */ void remoteGetZip( const wxString& aRepoURL ) throw( IO_ERROR ); diff --git a/pcbnew/io_mgr.h b/pcbnew/io_mgr.h index 87e217a045..6dff80ed39 100644 --- a/pcbnew/io_mgr.h +++ b/pcbnew/io_mgr.h @@ -5,7 +5,7 @@ * This program source code file is part of KICAD, a free EDA CAD application. * * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2016 Kicad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-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 @@ -282,6 +282,28 @@ public: virtual wxArrayString FootprintEnumerate( const wxString& aLibraryPath, const PROPERTIES* aProperties = NULL ); + /** + * Function PrefetchLib + * If possible, prefetches the specified library (e.g. performing downloads). Does not parse. + * Threadsafe. + * + * This is a no-op for libraries that cannot be prefetched. + * + * Plugins that cannot prefetch need not override this; a default no-op is provided. + * + * @param aLibraryPath is a locator for the "library", usually a directory, file, + * or URL containing several footprints. + * + * @param aProperties is an associative array that can be used to tell the + * plugin anything needed about how to perform with respect to @a aLibraryPath. + * The caller continues to own this object (plugin may not delete it), and + * plugins should expect it to be optionally NULL. + * + * @throw IO_ERROR if there is an error prefetching the library. + */ + virtual void PrefetchLib( const wxString& aLibraryPath, + const PROPERTIES* aProperties = NULL ); + /** * Function FootprintLoad * loads a footprint having @a aFootprintName from the @a aLibraryPath containing diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp index 6dac6db801..f1955c2960 100644 --- a/pcbnew/loadcmp.cpp +++ b/pcbnew/loadcmp.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@ujf-grenoble.fr * Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors. + * 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 @@ -54,6 +54,7 @@ using namespace std::placeholders; #include #include #include +#include #include #include #include @@ -62,7 +63,9 @@ using namespace std::placeholders; static void DisplayCmpDoc( wxString& aName, void* aData ); -static FOOTPRINT_LIST MList; +// Use the _IMPL class directly here because this is static - don't want to yank +// a static through kiface. +static FOOTPRINT_LIST_IMPL MList; static void clearModuleItemFlags( BOARD_ITEM* aItem ) { diff --git a/pcbnew/modview_frame.cpp b/pcbnew/modview_frame.cpp index 7cb36959a7..3dee456ae8 100644 --- a/pcbnew/modview_frame.cpp +++ b/pcbnew/modview_frame.cpp @@ -3,7 +3,7 @@ * * Copyright (C) 2012-2015 Jean-Pierre Charras, jp.charras at wanadoo.fr * Copyright (C) 2008-2016 Wayne Stambaugh - * Copyright (C) 2004-2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2004-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 @@ -60,6 +60,7 @@ #include "tools/pcb_actions.h" #include +#include using namespace std::placeholders; @@ -387,21 +388,21 @@ void FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList() return; } - FOOTPRINT_LIST fp_info_list; + auto fp_info_list( FOOTPRINT_LIST::GetInstance( Kiway() ) ); wxString nickname = getCurNickname(); - fp_info_list.ReadFootprintFiles( Prj().PcbFootprintLibs(), !nickname ? NULL : &nickname ); + fp_info_list->ReadFootprintFiles( Prj().PcbFootprintLibs(), !nickname ? NULL : &nickname ); - if( fp_info_list.GetErrorCount() ) + if( fp_info_list->GetErrorCount() ) { - fp_info_list.DisplayErrors( this ); + fp_info_list->DisplayErrors( this ); return; } - for( const FOOTPRINT_INFO& footprint : fp_info_list.GetList() ) + for( auto& footprint : fp_info_list->GetList() ) { - m_footprintList->Append( footprint.GetFootprintName() ); + m_footprintList->Append( footprint->GetFootprintName() ); } int index = m_footprintList->FindString( getCurFootprintName() ); diff --git a/pcbnew/pcb_netlist.cpp b/pcbnew/pcb_netlist.cpp index cb54855dfc..cc4acc1ccf 100644 --- a/pcbnew/pcb_netlist.cpp +++ b/pcbnew/pcb_netlist.cpp @@ -73,38 +73,6 @@ const COMPONENT_NET& COMPONENT::GetNet( const wxString& aPinName ) } -bool COMPONENT::MatchesFootprintFilters( const wxString& aLibraryName, const wxString& aFootprintName ) const -{ - if( m_footprintFilters.GetCount() == 0 ) - return true; - - // The matching is case insensitive - wxString name = ""; - - EDA_PATTERN_MATCH_WILDCARD patternFilter; - - for( unsigned ii = 0; ii < m_footprintFilters.GetCount(); ii++ ) - { - // If the filter contains a ':' character, include the library name in the pattern - if( m_footprintFilters[ii].Contains( ":" ) ) - { - name = aLibraryName.Lower() + ":"; - } - - name += aFootprintName.Lower(); - - patternFilter.SetPattern( m_footprintFilters[ii].Lower() ); - - if( patternFilter.Find( name ) != EDA_PATTERN_NOT_FOUND ) - { - return true; - } - } - - return false; -} - - void COMPONENT::Format( OUTPUTFORMATTER* aOut, int aNestLevel, int aCtl ) { int nl = aNestLevel; diff --git a/pcbnew/pcb_netlist.h b/pcbnew/pcb_netlist.h index 25b79f52c9..e6d4046853 100644 --- a/pcbnew/pcb_netlist.h +++ b/pcbnew/pcb_netlist.h @@ -174,14 +174,6 @@ public: const wxArrayString& GetFootprintFilters() const { return m_footprintFilters; } - /** - * Function MatchesFootprintFilters - * - * @return true if \a aFootprintName matches any of the footprint filters or no footprint - * filters are defined. - */ - bool MatchesFootprintFilters( const wxString& aLibraryName, const wxString& aFootprintName ) const; - MODULE* GetModule( bool aRelease = false ) { return ( aRelease ) ? m_footprint.release() : m_footprint.get(); diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp index 65d4a2d95e..b17a68ac16 100644 --- a/pcbnew/pcbnew.cpp +++ b/pcbnew/pcbnew.cpp @@ -35,8 +35,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -58,6 +60,7 @@ #include #include #include +#include #include extern bool IsWxPythonLoaded(); @@ -170,7 +173,17 @@ static struct IFACE : public KIFACE_I */ void* IfaceOrAddress( int aDataId ) override { - return NULL; + switch( aDataId ) + { + case KIFACE_NEW_FOOTPRINT_LIST: + return (void*) static_cast( new FOOTPRINT_LIST_IMPL() ); + + case KIFACE_G_FOOTPRINT_TABLE: + return (void*) new FP_LIB_TABLE( &GFootprintTable ); + + default: + return nullptr; + } } } kiface( "pcbnew", KIWAY::FACE_PCB ); diff --git a/pcbnew/plugin.cpp b/pcbnew/plugin.cpp index 5d9364712f..89557da172 100644 --- a/pcbnew/plugin.cpp +++ b/pcbnew/plugin.cpp @@ -2,7 +2,7 @@ * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2011-2012 SoftPLC Corporation, Dick Hollenbeck - * Copyright (C) 2016 KiCad Developers, see AUTHORS.txt for contributors. + * Copyright (C) 2016-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 @@ -66,6 +66,13 @@ wxArrayString PLUGIN::FootprintEnumerate( const wxString& aLibraryPath, const PR } +void PLUGIN::PrefetchLib( const wxString& aLibraryPath, const PROPERTIES* aProperties ) +{ + (void) aLibraryPath; + (void) aProperties; +} + + MODULE* PLUGIN::FootprintLoad( const wxString& aLibraryPath, const wxString& aFootprintName, const PROPERTIES* aProperties ) {