kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #28907
Re: [PATCH] Add footprint select dropdown to component chooser, serious refactoring
Hi again,
Updated patch unless this doesn't apply cleanly anymore after b47a6e4
On Wed, Mar 22, 2017 at 09:09:01PM -0400, Chris Pavlina wrote:
> Hi,
>
> Footprint selection in the component chooser is now working - here is a
> patch. I'd like to merge this, but it required serious refactoring to
> make everything work cleanly, so I'm posting to the list. Wayne, please
> have a look when you get a chance.
>
> There are a couple known issues, but IMO they aren't merge-stoppers. The
> sooner I get this merged the sooner I can get actual feedback, and the
> smoother integration of any changes that are made can be.
>
> Here's a summary (yes, the *summary* is big):
>
> - DIALOG_CHOOSE_COMPONENT changes:
> + Add FOOTPRINT_SELECT_WIDGET.
> + Add support for DIALOG_CHOOSE_COMPONENT to pass arbitrary field value
> overrides to the caller. This is of course to allow setting the footprint;
> in the future it could be used to allow more field edits.
> + Add an option to hide everything that can edit fields, for use when
> that doesn't make sense (libedit, etc).
>
> - Add FOOTPRINT_SELECT_WIDGET
> This is an adapter widget that combines into one FOOTPRINT_CHOICE view:
> + Footprint listings from FOOTPRINT_LIST
> + Filtering from FOOTPRINT_FILTER
> + Loading progress display from FOOTPRINT_ASYNC_LOADER and wxGauge
>
> It presents as a status progress bar that transforms in-place to a selection
> dropdown when the footprints finish loading. The GUI remains fully interactive
> as the footprints load; the user can even exit and reopen the dialog and it
> will continue to load in the background.
>
> - Add FOOTPRINT_CHOICE widget
> This is a customized wxComboCtrl with some extra features:
> + Greying out of library name for readability
> + List separators
>
> - Add FOOTPRINT_FILTER class
> Provides a reusable filtered view of a FOOTPRINT_LIST, fully iterable
>
> + Make cvpcb use FOOTPRINT_FILTER instead of providing its own filter
> code.
>
> - Seriously rework FOOTPRINT_INFO
> + Add partially asynchronous loading via the new FP_LIB_TABLE::PrefetchLib.
> A FOOTPRINT_ASYNC_LOADER class is added that can spawn loader threads and
> provide progress updates to the GUI while they work.
> + Completely rewrite footprint loader worker threads. They are now a
> queue-driven pool of workers rather than each loading a fixed number
> of libs (more efficient, a bit faster) and the main thread does no
> work, so it can return.
> + Make FOOTPRINT_INFO available to the world, by making them virtual base
> classes, putting the real implementation in
> pcbnew/footprint_info_impl.h/cpp, and adding a factory function to
> create an instance from anywhere via Kiface.
>
> - Add FP_LIB_TABLE::PrefetchLib
> This pulls everything that is async-safe (download from github, but not
> parsing due to a threadsafety issue) into a separate loader so the user
> can continue interacting as footprints download.
>
> Parsing itself remains synchronous, but the time it takes is tiny
> compared to downloading.
>
> - Allow access to the global fp_lib_table from anywhere via kiface
> (IfaceOrAddress()). Most methods to manipulate the table are still not
> compiled in everywhere (they have seriously large dependencies), but the
> table can be fetched as an opaque object.
>
> - Add a SYNC_QUEUE template class providing a std::queue wrapper with locking,
> for ease of passing things to and from worker threads.
>
> - Minor changes:
> + Add EDA_PATTERN_MATCH::GetPattern()
> + Create a type to represent component history items instead of just storing
> a list of strings and a unit. We'll need to track footprints too.
> + Make DIALOG_CHOOSE_COMPONENT quasimodal so it can summon the footprint
> picker.
> + Add kiface_ids.h for storing arbitrary IDs used in kiface. This is used
> for KifaceOrAddress(), as I'm the first person to actually use that method
> + Remove KICAD_FOOTPRINT_SELECTOR build option, no reason for it to be
> optional now.
>
> ===========
> Known issues, planned improvements:
>
> - History items don't remember their selected footprints.
> There are some implementation issues and UI/UX issues I still have to work
> out.
>
> - eeschema and cvpcb/pcbnew have separate footprint caches, so the first time
> you open one of the latter, it'll fetch the footprints even if eeschema
> already did.
>
> - FOOTPRINT_ASYNC_LOADER can also be used in cvpcb so cvpcb doesn't freeze
> as the footprints load! :)
>
> - Footprint picker isn't hidden in standalone mode, but it's useless.
>
> --
> Chris
>From f35678eb5d8f969a69c95ee60a360b7674678185 Mon Sep 17 00:00:00 2001
From: Chris Pavlina <pavlina.chris@xxxxxxxxx>
Date: Wed, 22 Mar 2017 20:59:25 -0400
Subject: [PATCH] Add footprint select dropdown to component chooser, serious
refactoring
- DIALOG_CHOOSE_COMPONENT has footprint select widget
- FOOTPRINT_SELECT_WIDGET
- FOOTPRINT_CHOICE widget (customized wxComboCtrl)
- FOOTPRINT_FILTER class
- FOOTPRINT_INFO rework:
- FOOTPRINT_ASYNC_LOADER to load without freezing UI
- Rewrite loader threads as queue-driven thread pool
- Make FOOTPRINT_INFO available via kiway
- FP_LIB_TABLE::PrefetchLib
- Access to global fp-lib-table via kiway
- SYNC_QUEUE threadsafe queue template
- Remove KICAD_FOOTPRINT_SELECTOR build option
---
CMakeLists.txt | 6 -
common/CMakeLists.txt | 5 +-
common/eda_pattern_match.cpp | 20 ++
common/footprint_filter.cpp | 230 ++++++++++++++
common/footprint_info.cpp | 330 +++++++--------------
common/fp_lib_table.cpp | 10 +-
common/project.cpp | 45 ++-
common/widgets/footprint_choice.cpp | 262 ++++++++++++++++
common/widgets/footprint_select_widget.cpp | 304 +++++++++++++++++++
cvpcb/autosel.cpp | 6 +-
cvpcb/class_DisplayFootprintsFrame.cpp | 6 +-
cvpcb/class_footprints_listbox.cpp | 69 ++---
cvpcb/cvpcb_mainframe.cpp | 25 +-
cvpcb/cvpcb_mainframe.h | 5 +-
cvpcb/listview_classes.h | 12 +-
cvpcb/readwrite_dlgs.cpp | 5 +-
eeschema/dialogs/dialog_choose_component.cpp | 247 +++++++++------
eeschema/dialogs/dialog_choose_component.h | 55 +++-
.../dialogs/dialog_edit_component_in_schematic.cpp | 12 +-
eeschema/getpart.cpp | 127 ++++----
eeschema/libedit.cpp | 20 +-
eeschema/onleftclick.cpp | 13 +-
eeschema/sch_base_frame.h | 63 ++--
eeschema/schframe.h | 12 +-
eeschema/viewlibs.cpp | 4 +-
include/eda_pattern_match.h | 14 +-
include/footprint_filter.h | 148 +++++++++
include/footprint_info.h | 288 +++++++++++++-----
include/fp_lib_table.h | 15 +-
include/kiface_ids.h | 49 +++
include/project.h | 14 +-
include/sync_queue.h | 113 +++++++
include/widgets/footprint_choice.h | 113 +++++++
include/widgets/footprint_select_widget.h | 161 ++++++++++
pcbnew/CMakeLists.txt | 1 +
pcbnew/footprint_info_impl.cpp | 230 ++++++++++++++
pcbnew/footprint_info_impl.h | 94 ++++++
pcbnew/github/github_plugin.cpp | 24 +-
pcbnew/github/github_plugin.h | 8 +-
pcbnew/io_mgr.h | 24 +-
pcbnew/loadcmp.cpp | 7 +-
pcbnew/modview_frame.cpp | 15 +-
pcbnew/pcb_netlist.cpp | 32 --
pcbnew/pcb_netlist.h | 8 -
pcbnew/pcbnew.cpp | 15 +-
pcbnew/plugin.cpp | 9 +-
46 files changed, 2622 insertions(+), 653 deletions(-)
create mode 100644 common/footprint_filter.cpp
create mode 100644 common/widgets/footprint_choice.cpp
create mode 100644 common/widgets/footprint_select_widget.cpp
create mode 100644 include/footprint_filter.h
create mode 100644 include/kiface_ids.h
create mode 100644 include/sync_queue.h
create mode 100644 include/widgets/footprint_choice.h
create mode 100644 include/widgets/footprint_select_widget.h
create mode 100644 pcbnew/footprint_info_impl.cpp
create mode 100644 pcbnew/footprint_info_impl.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18f2052e1..0d72dbef5 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 c9744915c..ad1cd5dee 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 b09094e0d..f2d8bced0 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 000000000..c6929758e
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <footprint_filter.h>
+#include <make_unique.h>
+#include <stdexcept>
+
+using FOOTPRINT_FILTER_IT = FOOTPRINT_FILTER::ITERATOR;
+
+
+FOOTPRINT_FILTER::ITERATOR::ITERATOR() : m_pos( 0 ), m_filter( nullptr )
+{
+}
+
+
+FOOTPRINT_FILTER::ITERATOR::ITERATOR( FOOTPRINT_FILTER_IT const& aOther )
+ : m_pos( aOther.m_pos ), m_filter( aOther.m_filter )
+{
+}
+
+
+FOOTPRINT_FILTER::ITERATOR::ITERATOR( FOOTPRINT_FILTER& aFilter )
+ : m_pos( (size_t) -1 ), m_filter( &aFilter )
+{
+ increment();
+}
+
+
+void FOOTPRINT_FILTER_IT::increment()
+{
+ bool found = false;
+
+ if( !m_filter || !m_filter->m_list || m_filter->m_list->GetCount() == 0 )
+ {
+ m_pos = 0;
+ return;
+ }
+
+ auto filter_type = m_filter->m_filter_type;
+ auto list = m_filter->m_list;
+ auto& lib_name = m_filter->m_lib_name;
+ auto& filter_pattern = m_filter->m_filter_pattern;
+ auto& filter = m_filter->m_filter;
+
+ for( ++m_pos; m_pos < list->GetCount() && !found; ++m_pos )
+ {
+ found = true;
+
+ if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_LIBRARY ) && !lib_name.IsEmpty()
+ && !list->GetItem( m_pos ).InLibrary( lib_name ) )
+ found = false;
+
+ if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_COMPONENT_KEYWORD )
+ && !FootprintFilterMatch( list->GetItem( m_pos ) ) )
+ found = false;
+
+ if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_PIN_COUNT )
+ && !PinCountMatch( list->GetItem( m_pos ) ) )
+ found = false;
+
+ if( ( filter_type & FOOTPRINT_FILTER::FILTERING_BY_NAME ) && !filter_pattern.IsEmpty() )
+ {
+ wxString currname;
+
+ // If the search string contains a ':' character,
+ // include the library name in the search string
+ // e.g. LibName:FootprintName
+ if( filter_pattern.Contains( ":" ) )
+ currname = list->GetItem( m_pos ).GetNickname().Lower() + ":";
+
+ currname += list->GetItem( m_pos ).GetFootprintName().Lower();
+
+ if( filter.Find( currname ) == EDA_PATTERN_NOT_FOUND )
+ found = false;
+ }
+
+ if( filter_type == FOOTPRINT_FILTER::UNFILTERED_FP_LIST )
+ {
+ // override
+ found = true;
+ }
+ }
+
+ // for loop will stop one past the correct item
+ if( found )
+ --m_pos;
+}
+
+
+bool FOOTPRINT_FILTER_IT::equal( FOOTPRINT_FILTER_IT const& aOther ) const
+{
+ // Invalid iterators are always equal
+ return ( m_pos == aOther.m_pos ) && ( m_filter == aOther.m_filter || m_pos == (size_t) -1 );
+}
+
+
+FOOTPRINT_INFO& FOOTPRINT_FILTER_IT::dereference() const
+{
+ if( m_filter && m_filter->m_list && m_pos < m_filter->m_list->GetCount() )
+ return m_filter->m_list->GetItem( m_pos );
+ else
+ throw std::out_of_range( "Attempt to dereference past FOOTPRINT_FILTER::end()" );
+}
+
+
+bool FOOTPRINT_FILTER_IT::FootprintFilterMatch( FOOTPRINT_INFO& aItem )
+{
+ if( m_filter->m_footprint_filters.empty() )
+ return true;
+
+ // The matching is case insensitive
+ wxString name = "";
+
+ EDA_PATTERN_MATCH_WILDCARD patternFilter;
+
+ for( auto const& each_filter : m_filter->m_footprint_filters )
+ {
+ // If the filter contains a ':' character, include the library name in the pattern
+ if( each_filter->GetPattern().Contains( ":" ) )
+ {
+ name = aItem.GetNickname().Lower() + ":";
+ }
+
+ name += aItem.GetFootprintName().Lower();
+
+ if( each_filter->Find( name ) != EDA_PATTERN_NOT_FOUND )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+bool FOOTPRINT_FILTER_IT::PinCountMatch( FOOTPRINT_INFO& aItem )
+{
+ return (unsigned) m_filter->m_pin_count == aItem.GetUniquePadCount();
+}
+
+
+FOOTPRINT_FILTER::FOOTPRINT_FILTER( FOOTPRINT_LIST& aList ) : FOOTPRINT_FILTER()
+{
+ SetList( aList );
+}
+
+
+FOOTPRINT_FILTER::FOOTPRINT_FILTER()
+ : m_list( nullptr ), m_pin_count( -1 ), m_filter_type( UNFILTERED_FP_LIST )
+{
+}
+
+
+void FOOTPRINT_FILTER::SetList( FOOTPRINT_LIST& aList )
+{
+ m_list = &aList;
+}
+
+
+void FOOTPRINT_FILTER::ClearFilters()
+{
+ m_filter_type = UNFILTERED_FP_LIST;
+}
+
+
+void FOOTPRINT_FILTER::FilterByLibrary( wxString const& aLibName )
+{
+ m_lib_name = aLibName;
+ m_filter_type |= FILTERING_BY_LIBRARY;
+}
+
+
+void FOOTPRINT_FILTER::FilterByPinCount( int aPinCount )
+{
+ m_pin_count = aPinCount;
+ m_filter_type |= FILTERING_BY_PIN_COUNT;
+}
+
+
+void FOOTPRINT_FILTER::FilterByFootprintFilters( wxArrayString const& aFilters )
+{
+ m_footprint_filters.clear();
+
+ for( auto const& each_pattern : aFilters )
+ {
+ m_footprint_filters.push_back( std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
+ wxASSERT( m_footprint_filters.back()->SetPattern( each_pattern.Lower() ) );
+ }
+
+ m_filter_type |= FILTERING_BY_COMPONENT_KEYWORD;
+}
+
+
+void FOOTPRINT_FILTER::FilterByPattern( wxString const& aPattern )
+{
+ m_filter_pattern = aPattern;
+ m_filter.SetPattern( aPattern.Lower() );
+ m_filter_type |= FILTERING_BY_NAME;
+}
+
+
+FOOTPRINT_FILTER_IT FOOTPRINT_FILTER::begin()
+{
+ return FOOTPRINT_FILTER_IT( *this );
+}
+
+
+FOOTPRINT_FILTER_IT FOOTPRINT_FILTER::end()
+{
+ FOOTPRINT_FILTER_IT end_it( *this );
+ end_it.m_pos = m_list->GetCount();
+ return end_it;
+}
diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp
index ddf79508a..8ec1163a0 100644
--- a/common/footprint_info.cpp
+++ b/common/footprint_info.cpp
@@ -3,7 +3,7 @@
*
* Copyright (C) 2011 Jean-Pierre Charras, <jp.charras@xxxxxxxxxx>
* Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
- * 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,293 +23,189 @@
* 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 <fctsys.h>
+#include <class_module.h>
#include <common.h>
-#include <macros.h>
-#include <pgm_base.h>
-#include <wildcards_and_files_ext.h>
+#include <fctsys.h>
#include <footprint_info.h>
-#include <io_mgr.h>
#include <fp_lib_table.h>
+#include <html_messagebox.h>
+#include <io_mgr.h>
+#include <kiface_ids.h>
+#include <kiway.h>
#include <lib_id.h>
-#include <class_module.h>
+#include <macros.h>
+#include <pgm_base.h>
#include <thread>
-#include <html_messagebox.h>
+#include <wildcards_and_files_ext.h>
-/*
-static wxString ToHTMLFragment( const IO_ERROR* aDerivative )
+FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName )
{
- @todo
+ if( aFootprintName.IsEmpty() )
+ return NULL;
+
+ for( auto& fp : m_list )
+ {
+ LIB_ID fpid;
- 1) change up IO_ERROR so it keeps linenumbers, source file name and
- error message in separate strings.
+ wxCHECK_MSG( fpid.Parse( aFootprintName ) < 0, NULL,
+ wxString::Format(
+ wxT( "'%s' is not a valid LIB_ID." ), GetChars( aFootprintName ) ) );
- 2) Add a summarizing virtual member like
- virtual wxString What()
- to combine all portions of an IO_ERROR's text into a single wxString.
+ wxString libNickname = fpid.GetLibNickname();
+ wxString footprintName = fpid.GetLibItemName();
- 3) Do same for PARSE_ERROR.
+ if( libNickname == fp->GetNickname() && footprintName == fp->GetFootprintName() )
+ return &*fp;
+ }
- 4) Add a "reason or error category" to IO_ERROR and thereby also PARSE_ERROR?
+ return NULL;
+}
- msg += "
- for( int i=0; i<aCount; ++i )
- {
+bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const
+{
+ return aLibrary == m_nickname;
+}
- wxArrayString* sl = wxStringSplit( aList, wxChar( '\n' ) );
+void FOOTPRINT_LIST::DisplayErrors( wxTopLevelWindow* aWindow )
+{
+ // @todo: go to a more HTML !<table>! ? centric output, possibly with
+ // recommendations for remedy of errors. Add numeric error codes
+ // to PARSE_ERROR, and switch on them for remedies, etc. Full
+ // access is provided to everything in every exception!
+ HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) );
- delete sl;
- }
+ dlg.MessageSet( _( "Errors were encountered loading footprints:" ) );
- wxString msg = wxT( "<ul>" );
+ wxString msg;
- for ( unsigned ii = 0; ii < strings_list->GetCount(); ii++ )
+ while( auto error = PopError() )
{
- msg += wxT( "<li>" );
- msg += strings_list->Item( ii ) + wxT( "</li>" );
+ msg += wxT( "<p>" ) + error->Problem() + wxT( "</p>" );
}
- msg += wxT( "</ul>" );
-
- m_htmlWindow->AppendToPage( msg );
+ dlg.AddHTML_Text( msg );
- delete strings_list;
+ dlg.ShowModal();
}
-*/
-void FOOTPRINT_INFO::load()
+static std::unique_ptr<FOOTPRINT_LIST> get_instance_from_id( KIWAY& aKiway, int aId )
{
- FP_LIB_TABLE* fptable = m_owner->GetTable();
+ void* ptr = nullptr;
- wxASSERT( fptable );
+ try
+ {
+ KIFACE* kiface = aKiway.KiFACE( KIWAY::FACE_PCB );
- std::unique_ptr<MODULE> footprint( fptable->FootprintLoad( m_nickname, m_fpname ) );
+ if( !kiface )
+ return nullptr;
- if( footprint.get() == NULL ) // Should happen only with malformed/broken libraries
- {
- m_pad_count = 0;
- m_unique_pad_count = 0;
+ ptr = kiface->IfaceOrAddress( aId );
+
+ if( !ptr )
+ return nullptr;
}
- else
+ catch( ... )
{
- 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;
+ return nullptr;
}
+
+ return std::unique_ptr<FOOTPRINT_LIST>( (FOOTPRINT_LIST*) ( ptr ) );
}
-void FOOTPRINT_LIST::loader_job( const wxString* aNicknameList, int aJobZ )
+std::unique_ptr<FOOTPRINT_LIST> FOOTPRINT_LIST::GetInstance( KIWAY& aKiway )
{
- for( int i=0; i<aJobZ; ++i )
- {
- const wxString& nickname = aNicknameList[i];
-
- try
- {
- wxArrayString fpnames = m_lib_table->FootprintEnumerate( nickname );
-
- for( unsigned ni=0; ni<fpnames.GetCount(); ++ni )
- {
- FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO( this, nickname, fpnames[ni] );
-
- addItem( fpinfo );
- }
- }
- catch( const PARSE_ERROR& pe )
- {
- // m_errors.push_back is not thread safe, lock its MUTEX.
- MUTLOCK lock( m_errors_lock );
-
- ++m_error_count; // modify only under lock
- m_errors.push_back( new IO_ERROR( pe ) );
- }
- catch( const IO_ERROR& ioe )
- {
- MUTLOCK lock( m_errors_lock );
-
- ++m_error_count;
- m_errors.push_back( new IO_ERROR( ioe ) );
- }
-
- // Catch anything unexpected and map it into the expected.
- // Likely even more important since this function runs on GUI-less
- // worker threads.
- 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 )
- {
- MUTLOCK lock( m_errors_lock );
-
- ++m_error_count;
- m_errors.push_back( new IO_ERROR( ioe ) );
- }
- }
- }
+ return get_instance_from_id( aKiway, KIFACE_NEW_FOOTPRINT_LIST );
}
-bool FOOTPRINT_LIST::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname )
+FOOTPRINT_ASYNC_LOADER::FOOTPRINT_ASYNC_LOADER() : m_list( nullptr )
{
- bool retv = true;
-
- m_lib_table = aTable;
-
- // Clear data before reading files
- m_error_count = 0;
- m_errors.clear();
- m_list.clear();
-
- if( aNickname )
- // single footprint
- loader_job( aNickname, 1 );
- else
- {
- std::vector< wxString > 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(); )
- {
- if( i + jobz >= 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<threads.size(); ++i )
- {
- threads[i].join();
- }
-
- m_list.sort();
- }
+}
- // The result of this function can be a blend of successes and failures, whose
- // mix is given by the Count()s of the two lists. The return value indicates whether
- // an abort occurred, even true does not necessarily mean full success, although
- // false definitely means failure.
- return retv;
+void FOOTPRINT_ASYNC_LOADER::SetList( FOOTPRINT_LIST* aList )
+{
+ m_list = aList;
}
-FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName )
+void FOOTPRINT_ASYNC_LOADER::Start(
+ FP_LIB_TABLE* aTable, wxString const* aNickname, unsigned aNThreads )
{
- if( aFootprintName.IsEmpty() )
- return NULL;
+ m_started = true;
- for( FOOTPRINT_INFO& fp : m_list )
- {
- LIB_ID fpid;
+ // 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();
- wxCHECK_MSG( fpid.Parse( aFootprintName ) < 0, NULL,
- wxString::Format( wxT( "'%s' is not a valid LIB_ID." ),
- GetChars( aFootprintName ) ) );
+ m_list->StartWorkers( aTable, aNickname, this, aNThreads );
+}
- wxString libNickname = fpid.GetLibNickname();
- wxString footprintName = fpid.GetLibItemName();
- if( libNickname == fp.GetNickname() && footprintName == fp.GetFootprintName() )
- return &fp;
+bool FOOTPRINT_ASYNC_LOADER::Join()
+{
+ if( m_list )
+ {
+ bool rv = m_list->JoinWorkers();
+ m_list = nullptr;
+ return rv;
}
-
- return NULL;
+ else
+ return true;
}
-bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const
+int FOOTPRINT_ASYNC_LOADER::GetProgress() const
{
- return aLibrary == m_nickname;
+ 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_LIST::DisplayErrors( wxTopLevelWindow* aWindow )
+void FOOTPRINT_ASYNC_LOADER::SetCompletionCallback( std::function<void()> aCallback )
{
- // @todo: go to a more HTML !<table>! ? centric output, possibly with
- // recommendations for remedy of errors. Add numeric error codes
- // to PARSE_ERROR, and switch on them for remedies, etc. Full
- // access is provided to everything in every exception!
-
- HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) );
-
- dlg.MessageSet( _( "Errors were encountered loading footprints:" ) );
-
- wxString msg;
-
- for( unsigned i = 0; i<m_errors.size(); ++i )
- {
- msg += wxT( "<p>" ) + m_errors[i].Problem() + wxT( "</p>" );
- }
+ m_completion_cb = aCallback;
+}
- dlg.AddHTML_Text( msg );
- dlg.ShowModal();
+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 4c4f6e203..504496024 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 <dick@xxxxxxxxxxx>
* Copyright (C) 2012-2016 Wayne Stambaugh <stambaughw@xxxxxxxxx>
- * 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 e55ed94b0..12e36473e 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 <wx/stdpaths.h>
#include <fctsys.h>
@@ -34,6 +32,9 @@
#include <kicad_string.h>
#include <config_params.h>
#include <wildcards_and_files_ext.h>
+#include <fp_lib_table.h>
+#include <kiway.h>
+#include <kiface_ids.h>
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<FP_LIB_TABLE*>( 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 000000000..ec547282b
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <functional>
+#include <widgets/footprint_choice.h>
+#include <wx/dc.h>
+#include <wx/pen.h>
+
+wxDEFINE_EVENT( EVT_INTERACTIVE_CHOICE, wxCommandEvent );
+
+
+wxColour FOOTPRINT_CHOICE::m_grey( 0x808080 );
+
+
+FOOTPRINT_CHOICE::FOOTPRINT_CHOICE( wxWindow* aParent, int aId )
+ : wxOwnerDrawnComboBox( aParent, aId, wxEmptyString, wxDefaultPosition, wxDefaultSize,
+ /* n */ 0, /* choices */ nullptr, wxCB_READONLY ),
+ m_last_selection( 0 )
+{
+}
+
+
+FOOTPRINT_CHOICE::~FOOTPRINT_CHOICE()
+{
+}
+
+
+void FOOTPRINT_CHOICE::DoSetPopupControl( wxComboPopup* aPopup )
+{
+ using namespace std::placeholders;
+ wxOwnerDrawnComboBox::DoSetPopupControl( aPopup );
+
+ // Bind events to intercept selections, so the separator can be made nonselectable.
+
+ GetVListBoxComboPopup()->Bind( wxEVT_MOTION, &FOOTPRINT_CHOICE::TryVetoMouse, this );
+ GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DOWN, &FOOTPRINT_CHOICE::TryVetoMouse, this );
+ GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::TryVetoMouse, this );
+ GetVListBoxComboPopup()->Bind( wxEVT_LEFT_UP, &FOOTPRINT_CHOICE::OnMouseUp, this );
+ GetVListBoxComboPopup()->Bind( wxEVT_LEFT_DCLICK, &FOOTPRINT_CHOICE::TryVetoMouse, this );
+ GetVListBoxComboPopup()->Bind(
+ wxEVT_LISTBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, true ) );
+ Bind( wxEVT_COMBOBOX, std::bind( &FOOTPRINT_CHOICE::TryVetoSelect, this, _1, false ) );
+ GetVListBoxComboPopup()->Bind(
+ wxEVT_CHAR_HOOK, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, true ) );
+ GetVListBoxComboPopup()->Bind( wxEVT_CHAR_HOOK, &FOOTPRINT_CHOICE::OnKeyUp, this );
+ Bind( wxEVT_KEY_DOWN, std::bind( &FOOTPRINT_CHOICE::TrySkipSeparator, this, _1, false ) );
+}
+
+
+void FOOTPRINT_CHOICE::OnDrawItem( wxDC& aDC, wxRect const& aRect, int aItem, int aFlags ) const
+{
+ wxString text = SafeGetString( aItem );
+
+ if( text == wxEmptyString )
+ {
+ wxPen pen( m_grey, 1, wxPENSTYLE_SOLID );
+
+ aDC.SetPen( pen );
+ aDC.DrawLine( aRect.x, aRect.y + aRect.height / 2, aRect.x + aRect.width,
+ aRect.y + aRect.height / 2 );
+ }
+ else
+ {
+ wxCoord x, y;
+
+ if( aFlags & wxODCB_PAINTING_CONTROL )
+ {
+ x = aRect.x + GetMargins().x;
+ y = ( aRect.height - aDC.GetCharHeight() ) / 2 + aRect.y;
+ }
+ else
+ {
+ x = aRect.x + 2;
+ y = aRect.y;
+ }
+
+ // If this item has a footprint and that footprint has a ":" delimiter, find the
+ // library component, then find that in the display string and grey it out.
+
+ size_t start_grey = 0;
+ size_t end_grey = 0;
+
+ wxString lib = static_cast<wxStringClientData*>( GetClientObject( aItem ) )->GetData();
+ size_t colon_index = lib.rfind( ':' );
+
+ if( colon_index != wxString::npos )
+ {
+ wxString library_part = lib.SubString( 0, colon_index );
+ size_t library_index = text.rfind( library_part );
+
+ if( library_index != wxString::npos )
+ {
+ start_grey = library_index;
+ end_grey = start_grey + library_part.Length();
+ }
+ }
+
+ if( start_grey != end_grey && !( aFlags & wxODCB_PAINTING_SELECTED ) )
+ {
+ x = DrawTextFragment( aDC, x, y, text.SubString( 0, start_grey - 1 ) );
+
+ wxColour standard_color = aDC.GetTextForeground();
+
+ aDC.SetTextForeground( m_grey );
+ x = DrawTextFragment( aDC, x, y, text.SubString( start_grey, end_grey - 1 ) );
+
+ aDC.SetTextForeground( standard_color );
+ x = DrawTextFragment( aDC, x, y, text.SubString( end_grey, text.Length() - 1 ) );
+ }
+ else
+ {
+ aDC.DrawText( text, x, y );
+ }
+ }
+}
+
+wxCoord FOOTPRINT_CHOICE::OnMeasureItem( size_t aItem ) const
+{
+ if( SafeGetString( aItem ) == "" )
+ return 11;
+ else
+ return wxOwnerDrawnComboBox::OnMeasureItem( aItem );
+}
+
+
+wxCoord FOOTPRINT_CHOICE::OnMeasureItemWidth( size_t aItem ) const
+{
+ if( SafeGetString( aItem ) == "" )
+ return GetTextRect().GetWidth() - 2;
+ else
+ return wxOwnerDrawnComboBox::OnMeasureItemWidth( aItem );
+}
+
+
+wxCoord FOOTPRINT_CHOICE::DrawTextFragment( wxDC& aDC, wxCoord x, wxCoord y, wxString const& aText )
+{
+ aDC.DrawText( aText, x, y );
+ return x + aDC.GetTextExtent( aText ).GetWidth();
+}
+
+
+void FOOTPRINT_CHOICE::TryVetoMouse( wxMouseEvent& aEvent )
+{
+ int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y );
+
+ if( SafeGetString( item ) != "" )
+ aEvent.Skip();
+}
+
+
+void FOOTPRINT_CHOICE::OnMouseUp( wxMouseEvent& aEvent )
+{
+ int item = GetVListBoxComboPopup()->VirtualHitTest( aEvent.GetPosition().y );
+
+ wxCommandEvent evt( EVT_INTERACTIVE_CHOICE );
+ evt.SetInt( item );
+ wxPostEvent( this, evt );
+
+ aEvent.Skip();
+}
+
+
+void FOOTPRINT_CHOICE::OnKeyUp( wxKeyEvent& aEvent )
+{
+ int item = GetSelectionEither( true );
+
+ if( aEvent.GetKeyCode() == WXK_RETURN )
+ {
+ wxCommandEvent evt( EVT_INTERACTIVE_CHOICE );
+ evt.SetInt( item );
+ wxPostEvent( this, evt );
+ }
+
+ aEvent.Skip();
+}
+
+
+void FOOTPRINT_CHOICE::TryVetoSelect( wxCommandEvent& aEvent, bool aInner )
+{
+ int sel = GetSelectionEither( aInner );
+
+ if( sel >= 0 && sel < (int) GetCount() )
+ {
+ wxString text = SafeGetString( sel );
+
+ if( text == "" )
+ SetSelectionEither( aInner, m_last_selection );
+ else
+ {
+ m_last_selection = sel;
+ aEvent.Skip();
+ }
+ }
+}
+
+
+void FOOTPRINT_CHOICE::TrySkipSeparator( wxKeyEvent& aEvent, bool aInner )
+{
+ int key = aEvent.GetKeyCode();
+ int sel = GetSelectionEither( aInner );
+ int new_sel = sel;
+
+ if( key == WXK_UP && SafeGetString( sel - 1 ) == wxEmptyString )
+ {
+ new_sel = sel - 2;
+ }
+ else if( key == WXK_DOWN && SafeGetString( sel + 1 ) == wxEmptyString )
+ {
+ new_sel = sel + 2;
+ }
+
+ if( new_sel != sel )
+ SetSelectionEither( aInner, new_sel );
+ else
+ aEvent.Skip();
+}
+
+
+wxString FOOTPRINT_CHOICE::SafeGetString( int aItem ) const
+{
+ if( aItem >= 0 && aItem < (int) GetCount() )
+ return GetVListBoxComboPopup()->GetString( aItem );
+ else
+ return wxEmptyString;
+}
+
+
+int FOOTPRINT_CHOICE::GetSelectionEither( bool aInner ) const
+{
+ if( aInner )
+ return GetVListBoxComboPopup()->wxVListBox::GetSelection();
+ else
+ return GetSelection();
+}
+
+
+void FOOTPRINT_CHOICE::SetSelectionEither( bool aInner, int aSel )
+{
+ if( aSel >= 0 && aSel < (int) GetCount() )
+ {
+ if( aInner )
+ return GetVListBoxComboPopup()->wxVListBox::SetSelection( aSel );
+ else
+ return SetSelection( aSel );
+ }
+}
diff --git a/common/widgets/footprint_select_widget.cpp b/common/widgets/footprint_select_widget.cpp
new file mode 100644
index 000000000..430c0849c
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <dialog_shim.h>
+#include <kiway.h>
+#include <kiway_player.h>
+#include <make_unique.h>
+#include <project.h>
+#include <widgets/footprint_choice.h>
+#include <widgets/footprint_select_widget.h>
+
+#include <functional>
+#include <wx/combo.h>
+#include <wx/gauge.h>
+#include <wx/odcombo.h>
+#include <wx/simplebook.h>
+#include <wx/sizer.h>
+#include <wx/timer.h>
+#include <wx/utils.h>
+#include <wx/wupdlock.h>
+
+
+/**
+ * Fixed positions for standard items in the list
+ */
+enum
+{
+ POS_DEFAULT,
+ POS_OTHER,
+ POS_SEPARATOR
+};
+
+
+/**
+ * Page numbers in the wxSimplebook
+ */
+enum
+{
+ PAGE_PROGRESS,
+ PAGE_SELECT
+};
+
+
+wxDEFINE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent );
+
+
+FOOTPRINT_SELECT_WIDGET::FOOTPRINT_SELECT_WIDGET( wxWindow* aParent,
+ FOOTPRINT_ASYNC_LOADER& aLoader, std::unique_ptr<FOOTPRINT_LIST>& aFpList, bool aUpdate,
+ int aMaxItems )
+ : wxPanel( aParent ),
+ m_kiway( nullptr ),
+ m_update( aUpdate ),
+ m_finished_loading( false ),
+ m_max_items( aMaxItems ),
+ m_last_item( 0 ),
+ m_fp_loader( aLoader ),
+ m_fp_list( aFpList )
+{
+ m_sizer = new wxBoxSizer( wxVERTICAL );
+ m_progress_timer = std::make_unique<wxTimer>( this );
+ m_book = new wxSimplebook( this, wxID_ANY );
+ m_progress_ctrl = new wxGauge( m_book, wxID_ANY, 100 );
+ m_fp_sel_ctrl = new FOOTPRINT_CHOICE( m_book, wxID_ANY );
+
+ m_book->SetEffect( wxSHOW_EFFECT_BLEND );
+ m_book->AddPage( m_progress_ctrl, "", true );
+ m_book->AddPage( m_fp_sel_ctrl, "", false );
+ m_sizer->Add( m_book, 1, wxEXPAND | wxALL, 5 );
+
+ SetSizer( m_sizer );
+ Layout();
+ m_sizer->Fit( this );
+
+ Bind( wxEVT_TIMER, &FOOTPRINT_SELECT_WIDGET::OnProgressTimer, this, m_progress_timer->GetId() );
+ m_fp_sel_ctrl->Bind( wxEVT_COMBOBOX, &FOOTPRINT_SELECT_WIDGET::OnComboBox, this );
+ m_fp_sel_ctrl->Bind(
+ EVT_INTERACTIVE_CHOICE, &FOOTPRINT_SELECT_WIDGET::OnComboInteractive, this );
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::Load( KIWAY& aKiway, PROJECT& aProject )
+{
+ m_kiway = &aKiway;
+ auto fp_lib_table = aProject.PcbFootprintLibs( aKiway );
+
+ if( m_fp_loader.GetProgress() == 0 || !m_fp_loader.IsSameTable( fp_lib_table ) )
+ {
+ m_fp_list = FOOTPRINT_LIST::GetInstance( aKiway );
+ m_fp_loader.SetList( &*m_fp_list );
+ m_fp_loader.Start( fp_lib_table );
+ }
+
+ m_progress_timer->Start( 200 );
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::OnProgressTimer( wxTimerEvent& aEvent )
+{
+ int prog = m_fp_loader.GetProgress();
+ m_progress_ctrl->SetValue( prog );
+
+ if( prog == 100 )
+ {
+ wxBusyCursor busy;
+
+ m_fp_loader.Join();
+ m_fp_filter.SetList( *m_fp_list );
+ m_progress_timer->Stop();
+
+ m_book->SetSelection( PAGE_SELECT );
+ m_finished_loading = true;
+
+ if( m_update )
+ UpdateList();
+ }
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::OnComboBox( wxCommandEvent& aEvent )
+{
+ wxCommandEvent evt( EVT_FOOTPRINT_SELECTED );
+ int sel = m_fp_sel_ctrl->GetSelection();
+
+ switch( sel )
+ {
+ case wxNOT_FOUND: return;
+
+ case POS_SEPARATOR:
+ // User somehow managed to select the separator. This should not be
+ // possible, but just in case... deselect it
+ m_fp_sel_ctrl->SetSelection( m_last_item );
+ break;
+
+ case POS_OTHER:
+ // When POS_OTHER is selected, a dialog should be shown. However, we don't want to
+ // do this ALL the time, as some times (e.g. when moving around with the arrow keys)
+ // it could be very annoying. Therefore showing the picker is done from the custom
+ // "interactive select" event on FOOTPRINT_CHOICE, which only fires for more direct
+ // choice actions.
+ break;
+
+ default:
+ {
+ wxStringClientData* clientdata =
+ static_cast<wxStringClientData*>( m_fp_sel_ctrl->GetClientObject( sel ) );
+ wxASSERT( clientdata );
+
+ evt.SetString( clientdata->GetData() );
+ wxPostEvent( this, evt );
+ }
+ }
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::OnComboInteractive( wxCommandEvent& aEvent )
+{
+ if( aEvent.GetInt() == POS_OTHER && !m_fp_sel_ctrl->IsPopupShown() )
+ {
+ DoOther();
+ }
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::DoOther()
+{
+ wxCommandEvent evt( EVT_FOOTPRINT_SELECTED );
+
+ wxString fpname = ShowPicker();
+ m_other_footprint = fpname;
+ UpdateList();
+ m_fp_sel_ctrl->SetSelection( POS_OTHER );
+ m_last_item = POS_OTHER;
+
+ evt.SetString( m_other_footprint );
+ wxPostEvent( this, evt );
+}
+
+
+wxString FOOTPRINT_SELECT_WIDGET::ShowPicker()
+{
+ wxString fpname;
+ wxWindow* parent = ::wxGetTopLevelParent( this );
+ DIALOG_SHIM* dsparent = dynamic_cast<DIALOG_SHIM*>( parent );
+
+ // Only quasimodal dialogs can launch modal kiface dialogs. Otherwise the
+ // event loop goes all silly.
+ wxASSERT( !dsparent || dsparent->IsQuasiModal() );
+
+ auto frame = m_kiway->Player( FRAME_PCB_MODULE_VIEWER_MODAL, true );
+
+ if( !frame->ShowModal( &fpname, parent ) )
+ {
+ fpname = wxEmptyString;
+ }
+
+ frame->Destroy();
+
+ return fpname;
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::ClearFilters()
+{
+ m_fp_filter.ClearFilters();
+ m_default_footprint.Clear();
+ m_other_footprint.Clear();
+ m_zero_filter = false;
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::FilterByPinCount( int aPinCount )
+{
+ m_fp_filter.FilterByPinCount( aPinCount );
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::FilterByFootprintFilters(
+ wxArrayString const& aFilters, bool aZeroFilters )
+{
+ if( aZeroFilters && aFilters.size() == 0 )
+ m_zero_filter = true;
+ else
+ m_zero_filter = false;
+
+ m_fp_filter.FilterByFootprintFilters( aFilters );
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::SetDefaultFootprint( wxString const& aFp )
+{
+ m_default_footprint = aFp;
+}
+
+
+bool FOOTPRINT_SELECT_WIDGET::UpdateList()
+{
+ int n_items = 0;
+
+ if( !m_fp_list || !m_finished_loading )
+ return false;
+
+ wxWindowUpdateLocker lock( m_fp_sel_ctrl );
+ m_fp_sel_ctrl->Clear();
+
+ // Be careful adding items! "Default" must occupy POS_DEFAULT,
+ // "Other" must occupy POS_OTHER, and the separator must occupy POS_SEPARATOR.
+
+ m_fp_sel_ctrl->Append( m_default_footprint.IsEmpty() ?
+ _( "No default footprint" ) :
+ "[" + _( "Default" ) + "] " + m_default_footprint,
+ new wxStringClientData( m_default_footprint ) );
+
+ m_fp_sel_ctrl->Append( m_other_footprint.IsEmpty() ?
+ _( "Other..." ) :
+ "[" + _( "Other..." ) + "] " + m_other_footprint,
+ new wxStringClientData( m_other_footprint ) );
+
+ m_fp_sel_ctrl->Append( "", new wxStringClientData( "" ) );
+
+ if( !m_zero_filter )
+ {
+ for( auto& fpinfo : m_fp_filter )
+ {
+ wxString display_name( fpinfo.GetNickname() + ":" + fpinfo.GetFootprintName() );
+
+ m_fp_sel_ctrl->Append( display_name, new wxStringClientData( display_name ) );
+ ++n_items;
+
+ if( n_items >= m_max_items )
+ break;
+ }
+ }
+
+ SelectDefault();
+ return true;
+}
+
+
+void FOOTPRINT_SELECT_WIDGET::SelectDefault()
+{
+ m_fp_sel_ctrl->SetSelection( POS_DEFAULT );
+}
+
+
+bool FOOTPRINT_SELECT_WIDGET::Enable( bool aEnable )
+{
+ return m_fp_sel_ctrl->Enable( aEnable );
+}
diff --git a/cvpcb/autosel.cpp b/cvpcb/autosel.cpp
index a60583cd5..1646106f8 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 892f5e90b..5a484b3b6 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 <stambaughw@xxxxxxxxxxx>
- * 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 4d3f5b0b5..1b0d0320c 100644
--- a/cvpcb/class_footprints_listbox.cpp
+++ b/cvpcb/class_footprints_listbox.cpp
@@ -29,12 +29,14 @@
#include <fctsys.h>
#include <wxstruct.h>
+#include <wx/wupdlock.h>
#include <cvpcb.h>
#include <cvpcb_mainframe.h>
#include <listview_classes.h>
#include <cvpcb_id.h>
#include <eda_pattern_match.h>
+#include <footprint_filter.h>
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( GetSelection() >= 0 && GetSelection() < (int)m_footprintList.GetCount() )
- oldSelection = m_footprintList[ GetSelection() ];
-
- for( unsigned ii = 0; ii < aList.GetCount(); ii++ )
- {
- 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;
- }
+ if( aFilterType & FILTERING_BY_COMPONENT_KEYWORD )
+ filter.FilterByFootprintFilters( aComponent->GetFootprintFilters() );
- // Filter footprints by selected library
- if( (aFilterType & FILTERING_BY_LIBRARY) && !aLibName.IsEmpty()
- && !aList.GetItem( ii ).InLibrary( aLibName ) )
- continue;
+ if( aFilterType & FILTERING_BY_PIN_COUNT )
+ filter.FilterByPinCount( aComponent->GetNetCount() );
- // Filter footprints by symbol fp-filters
- if( (aFilterType & FILTERING_BY_COMPONENT_KEYWORD) && aComponent
- && !aComponent->MatchesFootprintFilters( aList.GetItem( ii ).GetNickname(), aList.GetItem( ii ).GetFootprintName() ) )
- continue;
+ if( aFilterType & FILTERING_BY_LIBRARY )
+ filter.FilterByLibrary( aLibName );
- // Filter footprints by symbol pin-count
- if( (aFilterType & FILTERING_BY_PIN_COUNT) && aComponent
- && aComponent->GetNetCount() != aList.GetItem( ii ).GetUniquePadCount() )
- continue;
+ if( aFilterType & FILTERING_BY_NAME )
+ filter.FilterByPattern( aFootPrintFilterPattern );
- // 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;
- }
- }
+ if( GetSelection() >= 0 && GetSelection() < (int)m_footprintList.GetCount() )
+ oldSelection = m_footprintList[ GetSelection() ];
- msg.Printf( wxT( "%3d %s:%s" ), int( newList.GetCount() + 1 ),
- GetChars( aList.GetItem( ii ).GetNickname() ),
- GetChars( aList.GetItem( ii ).GetFootprintName() ) );
+ for( auto& i: filter )
+ {
+ 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 af25c93c2..fb622768c 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 <stambaughw@xxxxxxxxxxx>
- * 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<wxString>& 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 5471aa8a1..2218478ba 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 <wxBasePcbFrame.h>
#include <config_params.h>
#include <autosel.h>
+#include <memory>
/* 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<FOOTPRINT_LIST> m_FootprintsList;
protected:
int m_undefinedComponentCnt;
diff --git a/cvpcb/listview_classes.h b/cvpcb/listview_classes.h
index 0cb429037..1ef078caa 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 <wx/listctrl.h>
-
+#include <footprint_filter.h>
/* 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 4eadc9a1f..137b89121 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 <stambaughw@xxxxxxxxxxx>
- * 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 <fctsys.h>
#include <kiway.h>
#include <common.h>
@@ -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 dab4e2de2..7940a2cbd 100644
--- a/eeschema/dialogs/dialog_choose_component.cpp
+++ b/eeschema/dialogs/dialog_choose_component.cpp
@@ -25,30 +25,31 @@
#include <dialog_choose_component.h>
+#include <algorithm>
#include <set>
#include <wx/tokenzr.h>
#include <wx/utils.h>
#include <wx/artprov.h>
#include <wx/bitmap.h>
-#include <wx/statbmp.h>
-#include <wx/textctrl.h>
-#include <wx/sizer.h>
+#include <wx/button.h>
+#include <wx/choice.h>
#include <wx/dataview.h>
#include <wx/html/htmlwin.h>
#include <wx/panel.h>
-#include <wx/choice.h>
+#include <wx/sizer.h>
#include <wx/splitter.h>
-#include <wx/button.h>
+#include <wx/statbmp.h>
+#include <wx/textctrl.h>
#include <wx/timer.h>
#include <wx/utils.h>
#include <class_library.h>
+#include <generate_alias_info.h>
#include <sch_base_frame.h>
-#include <widgets/footprint_preview_widget.h>
-#include <widgets/two_column_tree_list.h>
#include <template_fieldnames.h>
-#include <generate_alias_info.h>
+#include <widgets/footprint_preview_widget.h>
+#include <widgets/footprint_select_widget.h>
// 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<FOOTPRINT_LIST> 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 );
-
- 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 );
+ 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_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<std::pair<int, wxString>> 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( "" );
+
+ ShowFootprint( fp_name );
+}
- for( auto const & field: fields )
+
+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<int, wxString> 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 6af8869ac..211f97f29 100644
--- a/eeschema/dialogs/dialog_choose_component.h
+++ b/eeschema/dialogs/dialog_choose_component.h
@@ -27,6 +27,7 @@
#include "dialog_shim.h"
#include <cmp_tree_model_adapter.h>
+#include <footprint_info.h>
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,46 @@ 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();
~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<std::pair<int, wxString>> 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 +140,7 @@ protected:
void OnInitDialog( wxInitDialogEvent& aEvent );
void OnCloseTimer( wxTimerEvent& aEvent );
+ void OnProgressTimer( wxTimerEvent& aEvent );
void OnQueryText( wxCommandEvent& aEvent );
void OnQueryEnter( wxCommandEvent& aEvent );
@@ -136,12 +154,26 @@ 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.
*/
void SelectIfValid( const wxDataViewItem& aTreeId );
@@ -170,15 +202,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<FOOTPRINT_LIST> m_fp_list;
+ std::vector<std::pair<int, wxString>> 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 e1a25a872..381640da5 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 22a9eaa52..42f1962cc 100644
--- a/eeschema/getpart.cpp
+++ b/eeschema/getpart.cpp
@@ -28,6 +28,7 @@
* @brief functions to get and place library components.
*/
+#include <algorithm>
#include <fctsys.h>
#include <pgm_base.h>
#include <kiway.h>
@@ -50,9 +51,10 @@
#include <dialog_get_component.h>
-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<COMPONENT_SELECTION>& 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.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;
- if( dlg.ShowModal() == wxID_CANCEL )
- return wxEmptyString;
+ 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 );
-
- if ( aUnit )
- aHistoryLastUnit = *aUnit;
+ aHistoryList.erase(
+ std::remove_if(
+ aHistoryList.begin(),
+ aHistoryList.end(),
+ [ &sel ]( COMPONENT_SELECTION const& i ) { return i.Name == sel.Name; } ),
+ aHistoryList.end() );
+
+ 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 2f454c3ac..fc9aa63e6 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 9aec70733..5721d6c4d 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 <stambaughw@xxxxxxxxxxx>
- * 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 b5a5accbf..7757f48f7 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<std::pair<int, wxString>> Fields;
+
+ COMPONENT_SELECTION():
+ Name(""),
+ Unit(1),
+ Convert(1)
+ {}
+ };
+
+ typedef std::vector<COMPONENT_SELECTION> 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<COMPONENT_SELECTION>& 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 356290899..4fe4a6546 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 <stambaughw@xxxxxxxxxxx>
- * 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 06ded6082..4badd6b3a 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 6bca8acd9..367c5602b 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 <pavlina.chris@xxxxxxxxx>
- * 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
@@ -53,6 +52,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 000000000..7a08dfa7b
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOOTPRINT_FILTER_H
+#define FOOTPRINT_FILTER_H
+
+#include <boost/iterator/iterator_facade.hpp>
+#include <eda_pattern_match.h>
+#include <footprint_info.h>
+
+
+/**
+ * Footprint display filter. Takes a list of footprints and filtering settings,
+ * and provides an iterable view of the filtered data.
+ */
+class FOOTPRINT_FILTER
+{
+public:
+ /**
+ * Construct a filter.
+ *
+ * @param aList - unfiltered list of footprints
+ */
+ FOOTPRINT_FILTER( FOOTPRINT_LIST& aList );
+
+ /**
+ * Construct a filter without assigning a footprint list. The filter MUST NOT
+ * be iterated over until SetList() is called.
+ */
+ FOOTPRINT_FILTER();
+
+ /**
+ * Set the list to filter.
+ */
+ void SetList( FOOTPRINT_LIST& aList );
+
+ /**
+ * Clear all filter criteria.
+ */
+ void ClearFilters();
+
+ /**
+ * Add library name to filter criteria.
+ */
+ void FilterByLibrary( wxString const& aLibName );
+
+ /**
+ * Set a pin count to filter by.
+ */
+ void FilterByPinCount( int aPinCount );
+
+ /**
+ * Set a list of footprint filters to filter by.
+ */
+ void FilterByFootprintFilters( wxArrayString const& aFilters );
+
+ /**
+ * Add a pattern to filter by name, including wildcards and optionally a colon-delimited
+ * library name.
+ */
+ void FilterByPattern( wxString const& aPattern );
+
+ /**
+ * Inner iterator class returned by begin() and end().
+ */
+ class ITERATOR
+ : public boost::iterator_facade<ITERATOR, FOOTPRINT_INFO, boost::forward_traversal_tag>
+ {
+ public:
+ ITERATOR();
+ ITERATOR( ITERATOR const& aOther );
+ ITERATOR( FOOTPRINT_FILTER& aFilter );
+
+ private:
+ friend class boost::iterator_core_access;
+ friend class FOOTPRINT_FILTER;
+
+ void increment();
+ bool equal( ITERATOR const& aOther ) const;
+ FOOTPRINT_INFO& dereference() const;
+
+ size_t m_pos;
+ FOOTPRINT_FILTER* m_filter;
+
+ /**
+ * Check if the stored component matches an item by footprint filter.
+ */
+ bool FootprintFilterMatch( FOOTPRINT_INFO& aItem );
+
+ /**
+ * Check if the stored component matches an item by pin count.
+ */
+ bool PinCountMatch( FOOTPRINT_INFO& aItem );
+ };
+
+ /**
+ * Get an iterator to the beginning of the filtered view.
+ */
+ ITERATOR begin();
+
+ /**
+ * Get an iterator to the end of the filtered view. The end iterator is
+ * invalid and may not be dereferenced, only compared against.
+ */
+ ITERATOR end();
+
+private:
+ /**
+ * Filter setting constants. The filter type is a bitwise OR of these flags,
+ * and only footprints matching all selected filter types are shown.
+ */
+ enum FP_FILTER_T : int
+ {
+ UNFILTERED_FP_LIST = 0,
+ FILTERING_BY_COMPONENT_KEYWORD = 0x0001,
+ FILTERING_BY_PIN_COUNT = 0x0002,
+ FILTERING_BY_LIBRARY = 0x0004,
+ FILTERING_BY_NAME = 0x0008
+ };
+
+ FOOTPRINT_LIST* m_list;
+
+ wxString m_lib_name;
+ wxString m_filter_pattern;
+ int m_pin_count;
+ int m_filter_type;
+ EDA_PATTERN_MATCH_WILDCARD m_filter;
+
+ std::vector<std::unique_ptr<EDA_PATTERN_MATCH>> m_footprint_filters;
+};
+
+#endif // FOOTPRINT_FILTER_H
diff --git a/include/footprint_info.h b/include/footprint_info.h
index 2559752f0..ecdaaaa0e 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, <jp.charras@xxxxxxxxxx>
- * 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 <boost/ptr_container/ptr_vector.hpp>
+#include <import_export.h>
+#include <ki_exception.h>
#include <ki_mutex.h>
#include <kicad_string.h>
+#include <sync_queue.h>
+#include <atomic>
+#include <functional>
+#include <memory>
-#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; }
+ const wxString& GetFootprintName() const
+ {
+ return m_fpname;
+ }
- 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& GetNickname() const
{
-#if !USE_FPI_LAZY
- load();
-#endif
+ 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;
+
+protected:
+ FP_LIB_TABLE* m_lib_table; ///< no ownership
- typedef boost::ptr_vector< FOOTPRINT_INFO > FPILIST;
- typedef boost::ptr_vector< IO_ERROR > ERRLIST;
+ typedef std::vector<std::unique_ptr<FOOTPRINT_INFO>> FPILIST;
+ typedef SYNC_QUEUE<std::unique_ptr<IO_ERROR>> 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;
+ 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 )
+public:
+ FOOTPRINT_LIST() : m_lib_table( 0 )
{
- // 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 );
}
-
-public:
-
- 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();
+ }
+
+ std::unique_ptr<IO_ERROR> PopError()
+ {
+ auto item = m_errors.pop();
- const IO_ERROR* GetError( unsigned aIdx ) const { return &m_errors[aIdx]; }
+ if( item )
+ return std::move( *item );
+ else
+ return std::unique_ptr<IO_ERROR>();
+ }
/**
- * 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<FOOTPRINT_LIST> 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<void()> 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<void()> 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 9162aae4f..bb87ed4bc 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 <dick@xxxxxxxxxxx>
* Copyright (C) 2012-2016 Wayne Stambaugh <stambaughw@xxxxxxxxxxx>
- * 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
@@ -146,6 +146,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
*
* loads a footprint having @a aFootprintName from the library given by @a aNickname.
diff --git a/include/kiface_ids.h b/include/kiface_ids.h
new file mode 100644
index 000000000..bea912943
--- /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 e3fd29230..491abe4a2 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 );
//-----</Cross Module API>---------------------------------------------------
@@ -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 000000000..55772f6e4
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SYNC_QUEUE_H
+#define SYNC_QUEUE_H
+
+#include <boost/optional.hpp>
+#include <mutex>
+#include <queue>
+
+/**
+ * Synchronized, locking queue. Safe for multiple producer/multiple consumer environments with
+ * nontrivial data (though bear in mind data needs to be copied in and out).
+ */
+template <typename T> class SYNC_QUEUE
+{
+ typedef std::lock_guard<std::mutex> GUARD;
+
+ std::queue<T> m_queue;
+ mutable std::mutex m_mutex;
+
+public:
+ SYNC_QUEUE()
+ {
+ }
+
+ /**
+ * Push a value onto the queue.
+ */
+ void push( T const& aValue )
+ {
+ GUARD guard( m_mutex );
+ m_queue.push( aValue );
+ }
+
+ /**
+ * Move a value onto the queue. Useful for e.g. unique_ptr.
+ */
+ void move_push( T&& aValue )
+ {
+ GUARD guard( m_mutex );
+ m_queue.push( std::move( aValue ) );
+ }
+
+ /**
+ * Pop a value off the queue if there is one, returning it. If the queue is empty,
+ * return boost::none instead.
+ */
+ boost::optional<T> pop()
+ {
+ GUARD guard( m_mutex );
+
+ if( m_queue.empty() )
+ {
+ return boost::none;
+ }
+ else
+ {
+ T val = std::move( m_queue.front() );
+ m_queue.pop();
+ return std::move( val );
+ }
+ }
+
+ /**
+ * Return true iff the queue is empty.
+ */
+ bool empty() const
+ {
+ GUARD guard( m_mutex );
+ return m_queue.empty();
+ }
+
+ /**
+ * Return the size of the queue.
+ */
+ size_t size() const
+ {
+ GUARD guard( m_mutex );
+ return m_queue.size();
+ }
+
+ /**
+ * Clear the queue.
+ */
+ void clear()
+ {
+ GUARD guard( m_mutex );
+
+ while( !m_queue.empty() )
+ {
+ m_queue.pop();
+ }
+ }
+};
+
+#endif // SYNC_QUEUE_H
diff --git a/include/widgets/footprint_choice.h b/include/widgets/footprint_choice.h
new file mode 100644
index 000000000..a2427f6fc
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOOTPRINT_CHOICE_H
+#define FOOTPRINT_CHOICE_H
+
+#include <wx/odcombo.h>
+
+/**
+ * Event thrown when an item is selected "interactively". This includes direct clicks
+ * and presses of the Enter key, but not arrow key motion. Integer data will be the
+ * item selected.
+ */
+wxDECLARE_EVENT( EVT_INTERACTIVE_CHOICE, wxCommandEvent );
+
+
+/**
+ * Customized combo box for footprint selection. This provides the following features:
+ *
+ * - library name is greyed out for readability when lib:footprint format is found in
+ * the item text
+ * - empty items are displayed as nonselectable separators
+ *
+ * Multiple separators in a row is undefined behavior; it is likely to result in errors
+ * such as the ability to select separators. Separators ARE valid at the top and bottom.
+ *
+ * For any items containing footprints, the "lib:footprint" name should be attached to
+ * the item as a wxStringClientData.
+ */
+class FOOTPRINT_CHOICE : public wxOwnerDrawnComboBox
+{
+public:
+ FOOTPRINT_CHOICE( wxWindow* aParent, int aId );
+
+ virtual ~FOOTPRINT_CHOICE();
+
+protected:
+ virtual void DoSetPopupControl( wxComboPopup* aPopup ) override;
+ virtual void OnDrawItem( wxDC& aDC, wxRect const& aRect, int aItem, int aFlags ) const override;
+ virtual wxCoord OnMeasureItem( size_t aItem ) const override;
+ virtual wxCoord OnMeasureItemWidth( size_t aItem ) const override;
+
+ /**
+ * Draw a fragment of text, then return the next x coordinate to continue drawing.
+ */
+ static wxCoord DrawTextFragment( wxDC& aDC, wxCoord x, wxCoord y, wxString const& aText );
+
+ /// Veto a mouseover event if in the separator
+ void TryVetoMouse( wxMouseEvent& aEvent );
+
+ /**
+ * Veto a select event for the separator
+ *
+ * @param aInner - true if event was called for the inner list (ie the popup)
+ */
+ void TryVetoSelect( wxCommandEvent& aEvent, bool aInner );
+
+ /**
+ * Mouse up on an item in the list.
+ */
+ void OnMouseUp( wxMouseEvent& aEvent );
+
+ /**
+ * Key up on an item in the list.
+ */
+ void OnKeyUp( wxKeyEvent& aEvent );
+
+ /**
+ * For arrow key events, skip over separators.
+ *
+ * @param aInner - true if event was called for the inner list (ie the popup)
+ */
+ void TrySkipSeparator( wxKeyEvent& aEvent, bool aInner );
+
+ /**
+ * Safely get a string for an item, returning wxEmptyString if the item doesn't exist.
+ */
+ wxString SafeGetString( int aItem ) const;
+
+ /**
+ * Get selection from either the outer (combo box) or inner (popup) list.
+ */
+ int GetSelectionEither( bool aInner ) const;
+
+ /**
+ * Safely set selection for either the outer (combo box) or inner (popup) list, doing nothing
+ * for invalid selections.
+ */
+ void SetSelectionEither( bool aInner, int aSel );
+
+ static wxColour m_grey;
+
+private:
+ int m_last_selection;
+};
+
+#endif // FOOTPRINT_CHOICE_H
diff --git a/include/widgets/footprint_select_widget.h b/include/widgets/footprint_select_widget.h
new file mode 100644
index 000000000..e5bbbaa3f
--- /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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOOTPRINT_SELECT_WIDGET_H
+#define FOOTPRINT_SELECT_WIDGET_H
+
+#include <footprint_filter.h>
+#include <footprint_info.h>
+#include <vector>
+#include <wx/panel.h>
+#include <wx/wx.h>
+
+class KIWAY;
+class PROJECT;
+class FOOTPRINT_CHOICE;
+class wxGauge;
+class wxMenu;
+class wxTimer;
+class wxTimerEvent;
+class wxWindow;
+class wxSimplebook;
+
+/**
+ * This event is fired when a footprint is selected. The string data of the
+ * event will contain the footprint name.
+ */
+wxDECLARE_EVENT( EVT_FOOTPRINT_SELECTED, wxCommandEvent );
+
+class FOOTPRINT_SELECT_WIDGET : public wxPanel
+{
+public:
+ /**
+ * Construct a footprint selector widget.
+ *
+ * This requires references to an external footprint loader, and an external
+ * unique_ptr-to-FOOTPRINT_LIST. The latter will be populated with a
+ * FOOTPRINT_LIST instance the first time Load() is called.
+ *
+ * The reason for this is that footprint loading tends to be very expensive,
+ * especially when using online libraries. The caller is expected to keep
+ * these objects around (e.g. they may be statics on the dialog this
+ * FOOTPRINT_SELECT_WIDGET is created in) so footprints do not have to be
+ * loaded more than once.
+ *
+ * @param aParent - parent window
+ * @param aLoader - FOOTPRINT_ASYNC_LOADER instance
+ * @param aFpList - FOOTPRINT_LIST container
+ * @param aUpdate - whether to call UpdateList() automatically when finished loading
+ * @param aMaxItems - maximum number of filter items to display, in addition to
+ * Default and Other
+ */
+ FOOTPRINT_SELECT_WIDGET( wxWindow* aParent, FOOTPRINT_ASYNC_LOADER& aLoader,
+ std::unique_ptr<FOOTPRINT_LIST>& aFpList, bool aUpdate = true, int aMaxItems = 10 );
+
+ virtual ~FOOTPRINT_SELECT_WIDGET()
+ {
+ }
+
+ /**
+ * Start loading. This function returns immediately; footprints will
+ * continue to load in the background.
+ *
+ * @param aKiway - active kiway instance. This is cached for use when "Other"
+ * is selected.
+ * @param aProject - current project
+ */
+ void Load( KIWAY& aKiway, PROJECT& aProject );
+
+ /**
+ * Clear all filters. Does not update the list.
+ */
+ void ClearFilters();
+
+ /**
+ * Filter by pin count. Does not update the list.
+ */
+ void FilterByPinCount( int aPinCount );
+
+ /**
+ * Filter by footprint filter list. Does not update the list.
+ *
+ * @param aZeroFilters - if true, zero filters = zero footprints. If false, zero filters =
+ * not filtering.
+ */
+ void FilterByFootprintFilters( wxArrayString const& aFilters, bool aZeroFilters );
+
+ /**
+ * Set the default footprint for a part. This will be listed at the
+ * top. May be an empty string.
+ */
+ void SetDefaultFootprint( wxString const& aFp );
+
+ /**
+ * Update the contents of the list to match the filters. Has no effect if
+ * the footprint list has not been loaded yet. The "default" footprint will be
+ * selected.
+ *
+ * @return true if the footprint list has been loaded (and the list was updated)
+ */
+ bool UpdateList();
+
+ /**
+ * Set current selection to the default footprint
+ */
+ void SelectDefault();
+
+ /**
+ * Enable or disable the control for input
+ */
+ virtual bool Enable( bool aEnable = true ) override;
+
+private:
+ KIWAY* m_kiway;
+ wxGauge* m_progress_ctrl;
+ FOOTPRINT_CHOICE* m_fp_sel_ctrl;
+ wxSizer* m_sizer;
+ wxSimplebook* m_book;
+
+ std::unique_ptr<wxTimer> m_progress_timer;
+
+ bool m_update;
+ bool m_finished_loading;
+ int m_max_items;
+ wxString m_default_footprint;
+ wxString m_other_footprint;
+ int m_last_item;
+
+ FOOTPRINT_ASYNC_LOADER& m_fp_loader;
+ std::unique_ptr<FOOTPRINT_LIST>& m_fp_list;
+ FOOTPRINT_FILTER m_fp_filter;
+ bool m_zero_filter;
+
+ void OnProgressTimer( wxTimerEvent& aEvent );
+ void OnComboBox( wxCommandEvent& aEvent );
+ void OnComboInteractive( wxCommandEvent& aEvent );
+
+ /// Show the component picker and return the selected component. Used by DoOther()
+ wxString ShowPicker();
+
+ /// Handle activation of the "Other..." item
+ void DoOther();
+};
+
+
+#endif // FOOTPRINT_SELECT_WIDGET
diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index c0ce9ad19..20769c6a4 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 000000000..108a191ba
--- /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, <jp.charras@xxxxxxxxxx>
+ * Copyright (C) 2013-2016 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
+ * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include <footprint_info_impl.h>
+
+#include <class_module.h>
+#include <common.h>
+#include <fctsys.h>
+#include <footprint_info.h>
+#include <fp_lib_table.h>
+#include <html_messagebox.h>
+#include <io_mgr.h>
+#include <kiface_ids.h>
+#include <kiway.h>
+#include <lib_id.h>
+#include <macros.h>
+#include <make_unique.h>
+#include <pgm_base.h>
+#include <wildcards_and_files_ext.h>
+
+#include <thread>
+
+
+void FOOTPRINT_INFO_IMPL::load()
+{
+ FP_LIB_TABLE* fptable = m_owner->GetTable();
+
+ wxASSERT( fptable );
+
+ std::unique_ptr<MODULE> footprint( fptable->FootprintLoad( m_nickname, m_fpname ) );
+
+ if( footprint.get() == NULL ) // Should happen only with malformed/broken libraries
+ {
+ m_pad_count = 0;
+ m_unique_pad_count = 0;
+ }
+ else
+ {
+ m_pad_count = footprint->GetPadCount( DO_NOT_INCLUDE_NPTH );
+ m_unique_pad_count = footprint->GetUniquePadCount( DO_NOT_INCLUDE_NPTH );
+ m_keywords = footprint->GetKeywords();
+ m_doc = footprint->GetDescription();
+
+ // tell ensure_loaded() I'm loaded.
+ m_loaded = true;
+ }
+}
+
+
+bool FOOTPRINT_LIST_IMPL::CatchErrors( std::function<void()> aFunc )
+{
+ try
+ {
+ aFunc();
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
+ return false;
+ }
+ catch( const std::exception& se )
+ {
+ // This is a round about way to do this, but who knows what THROW_IO_ERROR()
+ // may be tricked out to do someday, keep it in the game.
+ try
+ {
+ THROW_IO_ERROR( se.what() );
+ }
+ catch( const IO_ERROR& ioe )
+ {
+ m_errors.move_push( std::make_unique<IO_ERROR>( ioe ) );
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+void FOOTPRINT_LIST_IMPL::loader_job()
+{
+ while( auto const nickname = m_queue_in.pop() )
+ {
+ CatchErrors( [this, &nickname]() {
+ m_lib_table->PrefetchLib( *nickname );
+ m_queue_out.push( *nickname );
+ } );
+
+ m_count_finished.fetch_add( 1 );
+ }
+
+ if( !m_first_to_finish.exchange( true ) )
+ {
+ // yay, we're first to finish!
+ if( m_loader->m_completion_cb )
+ {
+ m_loader->m_completion_cb();
+ }
+ }
+}
+
+
+bool FOOTPRINT_LIST_IMPL::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname )
+{
+ FOOTPRINT_ASYNC_LOADER loader;
+
+ loader.SetList( this );
+ loader.Start( aTable, aNickname );
+ return loader.Join();
+}
+
+
+void FOOTPRINT_LIST_IMPL::StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
+ FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads )
+{
+ m_loader = aLoader;
+ m_lib_table = aTable;
+
+ // Clear data before reading files
+ m_first_to_finish.store( false );
+ m_count_finished.store( 0 );
+ m_errors.clear();
+ m_list.clear();
+ m_threads.clear();
+ m_queue_in.clear();
+ m_queue_out.clear();
+
+ if( aNickname )
+ m_queue_in.push( *aNickname );
+ else
+ {
+ for( auto const& nickname : aTable->GetLogicalLibs() )
+ m_queue_in.push( nickname );
+ }
+
+ m_loader->m_total_libs = m_queue_in.size();
+
+ for( unsigned i = 0; i < aNThreads; ++i )
+ {
+ m_threads.push_back( std::thread( &FOOTPRINT_LIST_IMPL::loader_job, this ) );
+ }
+}
+
+bool FOOTPRINT_LIST_IMPL::JoinWorkers()
+{
+ for( auto& i : m_threads )
+ i.join();
+
+ m_threads.clear();
+ m_queue_in.clear();
+
+ LOCALE_IO toggle_locale;
+
+ // Parse the footprints in parallel. WARNING! This requires changing the locale, which is
+ // GLOBAL. It is only threadsafe to construct the LOCALE_IO before the threads are created,
+ // destroy it after they finish, and block the main (GUI) thread while they work. Any deviation
+ // from this will cause nasal demons.
+ //
+ // TODO: blast LOCALE_IO into the sun
+
+ SYNC_QUEUE<std::unique_ptr<FOOTPRINT_INFO>> queue_parsed;
+ std::vector<std::thread> threads;
+
+ for( size_t i = 0; i < std::thread::hardware_concurrency() + 1; ++i )
+ {
+ threads.push_back( std::thread( [this, &queue_parsed]() {
+ while( auto nickname = this->m_queue_out.pop() )
+ {
+ CatchErrors( [this, &queue_parsed, &nickname]() {
+ wxArrayString fpnames = this->m_lib_table->FootprintEnumerate( *nickname );
+
+ for( auto const& fpname : fpnames )
+ {
+ FOOTPRINT_INFO* fpinfo = new FOOTPRINT_INFO_IMPL( this, *nickname, fpname );
+ queue_parsed.move_push( std::unique_ptr<FOOTPRINT_INFO>( fpinfo ) );
+ }
+ } );
+ }
+ } ) );
+ }
+
+ for( auto& thr : threads )
+ thr.join();
+
+ while( auto fpi = queue_parsed.pop() )
+ m_list.push_back( std::move( *fpi ) );
+
+ std::sort( m_list.begin(), m_list.end(),
+ []( std::unique_ptr<FOOTPRINT_INFO> const& lhs,
+ std::unique_ptr<FOOTPRINT_INFO> const& rhs ) -> bool { return *lhs < *rhs; } );
+
+ return m_errors.empty();
+}
+
+
+size_t FOOTPRINT_LIST_IMPL::CountFinished()
+{
+ return m_count_finished.load();
+}
+
+
+FOOTPRINT_LIST_IMPL::FOOTPRINT_LIST_IMPL() : m_loader( nullptr )
+{
+}
+
+
+FOOTPRINT_LIST_IMPL::~FOOTPRINT_LIST_IMPL()
+{
+ for( auto& i : m_threads )
+ i.join();
+}
diff --git a/pcbnew/footprint_info_impl.h b/pcbnew/footprint_info_impl.h
new file mode 100644
index 000000000..a2ec466b4
--- /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, <jp.charras@xxxxxxxxxx>
+ * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation, either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef FOOTPRINT_INFO_IMPL_H
+#define FOOTPRINT_INFO_IMPL_H
+
+#include <atomic>
+#include <functional>
+#include <memory>
+#include <thread>
+#include <vector>
+
+#include <footprint_info.h>
+#include <sync_queue.h>
+
+class LOCALE_IO;
+
+class FOOTPRINT_INFO_IMPL : public FOOTPRINT_INFO
+{
+public:
+ FOOTPRINT_INFO_IMPL(
+ FOOTPRINT_LIST* aOwner, const wxString& aNickname, const wxString& aFootprintName )
+ {
+ m_owner = aOwner;
+ m_loaded = false;
+ m_nickname = aNickname;
+ m_fpname = aFootprintName;
+ m_num = 0;
+ m_pad_count = 0;
+ m_unique_pad_count = 0;
+#if !USE_FPI_LAZY
+ load();
+#endif
+ }
+
+protected:
+ virtual void load() override;
+};
+
+
+class FOOTPRINT_LIST_IMPL : public FOOTPRINT_LIST
+{
+ FOOTPRINT_ASYNC_LOADER* m_loader;
+ std::vector<std::thread> m_threads;
+ SYNC_QUEUE<wxString> m_queue_in;
+ SYNC_QUEUE<wxString> m_queue_out;
+ std::atomic_size_t m_count_finished;
+ std::atomic_bool m_first_to_finish;
+
+ /**
+ * Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors.
+ *
+ * @return true if no error occurred.
+ */
+ bool CatchErrors( std::function<void()> aFunc );
+
+protected:
+ virtual void StartWorkers( FP_LIB_TABLE* aTable, wxString const* aNickname,
+ FOOTPRINT_ASYNC_LOADER* aLoader, unsigned aNThreads ) override;
+ virtual bool JoinWorkers() override;
+ virtual size_t CountFinished() override;
+
+ /**
+ * Function loader_job
+ * loads footprints from m_queue_in.
+ */
+ void loader_job();
+
+public:
+ FOOTPRINT_LIST_IMPL();
+ virtual ~FOOTPRINT_LIST_IMPL();
+
+ virtual bool ReadFootprintFiles(
+ FP_LIB_TABLE* aTable, const wxString* aNickname = NULL ) override;
+};
+
+#endif // FOOTPRINT_INFO_IMPL_H
diff --git a/pcbnew/github/github_plugin.cpp b/pcbnew/github/github_plugin.cpp
index cefc620e0..f95529f97 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 <dick@xxxxxxxxxxx>
- * 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 f795a9f66..3ec8f42d6 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 <dick@xxxxxxxxxxx>
- * 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 87e217a04..6dff80ed3 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 <dick@xxxxxxxxxxx>
- * 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
@@ -283,6 +283,28 @@ public:
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
* a library format that this PLUGIN knows about.
diff --git a/pcbnew/loadcmp.cpp b/pcbnew/loadcmp.cpp
index 6dac6db80..f1955c296 100644
--- a/pcbnew/loadcmp.cpp
+++ b/pcbnew/loadcmp.cpp
@@ -3,7 +3,7 @@
*
* Copyright (C) 2012 Jean-Pierre Charras, jean-pierre.charras@xxxxxxxxxxxxxxx
* Copyright (C) 2012 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
- * 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 <pcbnew.h>
#include <module_editor_frame.h>
#include <footprint_info.h>
+#include <footprint_info_impl.h>
#include <dialog_get_component.h>
#include <modview_frame.h>
#include <wildcards_and_files_ext.h>
@@ -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 7cb36959a..3dee456ae 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 <stambaughw@xxxxxxxxxxx>
- * 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 <functional>
+#include <memory>
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 cb54855df..cc4acc1cc 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 25b79f52c..e6d404685 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 65d4a2d95..b17a68ac1 100644
--- a/pcbnew/pcbnew.cpp
+++ b/pcbnew/pcbnew.cpp
@@ -35,8 +35,10 @@
#include <fctsys.h>
#include <pgm_base.h>
#include <kiface_i.h>
+#include <kiface_ids.h>
#include <confirm.h>
#include <macros.h>
+#include <make_unique.h>
#include <class_drawpanel.h>
#include <wxPcbStruct.h>
#include <eda_dde.h>
@@ -58,6 +60,7 @@
#include <modview_frame.h>
#include <footprint_wizard_frame.h>
#include <footprint_preview_panel.h>
+#include <footprint_info_impl.h>
#include <gl_context_mgr.h>
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<FOOTPRINT_LIST*>( 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 5d9364712..89557da17 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 <dick@xxxxxxxxxxx>
- * 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 )
{
--
2.12.0
Follow ups
References