diff --git a/common/footprint_info.cpp b/common/footprint_info.cpp index 7ccd0f87de..3ad15516b5 100644 --- a/common/footprint_info.cpp +++ b/common/footprint_info.cpp @@ -51,22 +51,15 @@ #include -FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) +FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aLibNickname, + const wxString& aFootprintName ) { if( aFootprintName.IsEmpty() ) return NULL; - LIB_ID fpid; - - wxCHECK_MSG( fpid.Parse( aFootprintName, LIB_ID::ID_PCB ) < 0, NULL, - wxString::Format( wxT( "\"%s\" is not a valid LIB_ID." ), aFootprintName ) ); - - wxString libNickname = fpid.GetLibNickname(); - wxString footprintName = fpid.GetLibItemName(); - for( auto& fp : m_list ) { - if( libNickname == fp->GetNickname() && footprintName == fp->GetFootprintName() ) + if( aLibNickname == fp->GetNickname() && aFootprintName == fp->GetFootprintName() ) return &*fp; } @@ -74,6 +67,20 @@ FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) } +FOOTPRINT_INFO* FOOTPRINT_LIST::GetModuleInfo( const wxString& aFootprintName ) +{ + if( aFootprintName.IsEmpty() ) + return NULL; + + LIB_ID fpid; + + wxCHECK_MSG( fpid.Parse( aFootprintName, LIB_ID::ID_PCB ) < 0, NULL, + wxString::Format( wxT( "\"%s\" is not a valid LIB_ID." ), aFootprintName ) ); + + return GetModuleInfo( fpid.GetLibNickname(), fpid.GetLibItemName() ); +} + + bool FOOTPRINT_INFO::InLibrary( const wxString& aLibrary ) const { return aLibrary == m_nickname; diff --git a/include/footprint_info.h b/include/footprint_info.h index 2ce3cc538d..ac1870577f 100644 --- a/include/footprint_info.h +++ b/include/footprint_info.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include @@ -62,7 +63,7 @@ class KIWAY; * 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 APIEXPORT FOOTPRINT_INFO +class APIEXPORT FOOTPRINT_INFO : public LIB_TREE_ITEM { friend bool operator<( const FOOTPRINT_INFO& item1, const FOOTPRINT_INFO& item2 ); @@ -84,18 +85,33 @@ public: return m_nickname; } - const wxString& GetDoc() + const wxString& GetName() const override + { + return m_fpname; + } + + LIB_ID GetLibId() const override + { + return LIB_ID( m_nickname, m_fpname ); + } + + wxString GetDescription() override { ensure_loaded(); return m_doc; } - const wxString& GetKeywords() + wxString GetKeywords() { ensure_loaded(); return m_keywords; } + wxString GetSearchText() override + { + return GetKeywords() + wxT( " " ) + GetDescription(); + } + unsigned GetPadCount() { ensure_loaded(); @@ -207,11 +223,14 @@ public: } /** - * 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 + * Get info for a module by id. + */ + FOOTPRINT_INFO* GetModuleInfo( const wxString& aFootprintId ); + + /** + * Get info for a module by libNickname/footprintName */ - FOOTPRINT_INFO* GetModuleInfo( const wxString& aFootprintName ); + FOOTPRINT_INFO* GetModuleInfo( const wxString& aLibNickname, const wxString& aFootprintName ); /** * Get info for a module by index. @@ -223,12 +242,6 @@ public: return *m_list[aIdx]; } - /** - * Add aItem to list - * @param aItem = item to add - */ - void AddItem( FOOTPRINT_INFO* aItem ); - unsigned GetErrorCount() const { return m_errors.size(); diff --git a/include/pcb_base_frame.h b/include/pcb_base_frame.h index 4ef140879b..ac46252bdb 100644 --- a/include/pcb_base_frame.h +++ b/include/pcb_base_frame.h @@ -125,16 +125,6 @@ public: */ MODULE* LoadFootprint( const LIB_ID& aFootprintId ); - /** - * Check to see if a footprint is available - * Note that this is more strict than LoadFootprint as it also checks to see that - * the footprint's library is enabled in the fpTable. - * - * @param aFootprintId - * @return true if \a aFootprintId is available and can be loaded - */ - bool CheckFootprint( const LIB_ID& aFootprintId ); - /** * Function GetBoardBoundingBox * calculates the bounding box containing all board items (or board edge segments). @@ -460,7 +450,7 @@ public: * @param aLibrary = the library name to use, or empty string to search all libraries * @param aUseFootprintViewer = true to allow selection by the footprint viewer */ - MODULE* LoadModuleFromLibrary( const wxString& aLibrary, bool aUseFootprintViewer = true ); + MODULE* SelectFootprintFromLibTree( const wxString& aLibrary, bool aUseFootprintViewer = true ); /** * Adds the given module to the board. diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index f890e6179e..6441877d15 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -54,6 +54,7 @@ set( PCBNEW_DIALOGS dialogs/dialog_block_options.cpp dialogs/dialog_block_options_base.cpp dialogs/dialog_board_setup.cpp + dialogs/dialog_choose_footprint.cpp dialogs/dialog_cleaning_options.cpp dialogs/dialog_cleaning_options_base.cpp dialogs/dialog_copper_zones.cpp @@ -88,8 +89,6 @@ set( PCBNEW_DIALOGS dialogs/dialog_gencad_export_options.cpp dialogs/dialog_gendrill.cpp dialogs/dialog_gendrill_base.cpp - dialogs/dialog_get_footprint.cpp - dialogs/dialog_get_footprint_base.cpp dialogs/dialog_get_footprint_by_name_base.cpp dialogs/dialog_global_deletion.cpp dialogs/dialog_global_deletion_base.cpp @@ -244,6 +243,8 @@ set( PCBNEW_CLASS_SRCS footprint_edit_frame.cpp footprint_libraries_utils.cpp footprint_viewer_frame.cpp + fp_tree_model_adapter.cpp + generate_footprint_info.cpp grid_layer_box_helpers.cpp grid_layer_box_helpers.h highlight.cpp diff --git a/pcbnew/dialogs/dialog_choose_footprint.cpp b/pcbnew/dialogs/dialog_choose_footprint.cpp new file mode 100644 index 0000000000..d29569602a --- /dev/null +++ b/pcbnew/dialogs/dialog_choose_footprint.cpp @@ -0,0 +1,256 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Henner Zeller + * Copyright (C) 2016-2018 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 + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +wxSize DIALOG_CHOOSE_FOOTPRINT::m_last_dlg_size( -1, -1 ); +int DIALOG_CHOOSE_FOOTPRINT::m_h_sash_pos = 0; +int DIALOG_CHOOSE_FOOTPRINT::m_v_sash_pos = 0; + + +DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent, + const wxString& aTitle, + FP_TREE_MODEL_ADAPTER::PTR& aAdapter, + bool aAllowBrowser ) + : DIALOG_SHIM( aParent, wxID_ANY, aTitle, wxDefaultPosition, wxDefaultSize, + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ), + m_browser_button( nullptr ), + m_hsplitter( nullptr ), + m_vsplitter( nullptr ), + m_parent( aParent ), + m_external_browser_requested( false ) +{ + auto sizer = new wxBoxSizer( wxVERTICAL ); + wxHtmlWindow* details = nullptr; + + m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxSP_LIVE_UPDATE | wxSP_3DSASH ); + + m_hsplitter = new wxSplitterWindow( m_vsplitter, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxSP_LIVE_UPDATE | wxSP_3DSASH ); + + //Avoid the splitter window being assigned as the Parent to additional windows + m_hsplitter->SetExtraStyle( wxWS_EX_TRANSIENT ); + + auto detailsPanel = new wxPanel( m_vsplitter ); + auto detailsSizer = new wxBoxSizer( wxVERTICAL ); + detailsPanel->SetSizer( detailsSizer ); + + details = new wxHtmlWindow( detailsPanel, wxID_ANY, wxDefaultPosition, wxDefaultSize, + wxHW_SCROLLBAR_AUTO | wxRAISED_BORDER ); + detailsSizer->Add( details, 1, wxEXPAND | wxLEFT | wxRIGHT, 5 ); + detailsPanel->Layout(); + detailsSizer->Fit( detailsPanel ); + + m_vsplitter->SetSashGravity( 0.5 ); + m_vsplitter->SetMinimumPaneSize( 20 ); + m_vsplitter->SplitHorizontally( m_hsplitter, detailsPanel ); + + sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 ); + + m_tree = new LIB_TREE( m_hsplitter, Prj().PcbFootprintLibs(), aAdapter, + LIB_TREE::WIDGETS::ALL, details ); + + m_hsplitter->SetSashGravity( 0.8 ); + m_hsplitter->SetMinimumPaneSize( 20 ); + m_hsplitter->SplitVertically( m_tree, ConstructRightPanel( m_hsplitter ) ); + + m_dbl_click_timer = new wxTimer( this ); + + auto buttonsSizer = new wxBoxSizer( wxHORIZONTAL ); + + if( aAllowBrowser ) + { + m_browser_button = new wxButton( this, wxID_ANY, _( "Select with Browser" ) ); + buttonsSizer->Add( m_browser_button, 0, wxALL | wxALIGN_CENTER_VERTICAL, 10 ); + } + + auto sdbSizer = new wxStdDialogButtonSizer(); + auto okButton = new wxButton( this, wxID_OK ); + auto cancelButton = new wxButton( this, wxID_CANCEL ); + sdbSizer->AddButton( okButton ); + sdbSizer->AddButton( cancelButton ); + sdbSizer->Realize(); + + buttonsSizer->Add( sdbSizer, 1, wxALL, 5 ); + + sizer->Add( buttonsSizer, 0, wxEXPAND | wxLEFT, 5 ); + SetSizer( sizer ); + + Bind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this, m_dbl_click_timer->GetId() ); + Bind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this ); + Bind( COMPONENT_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this ); + + if( m_browser_button ) + m_browser_button->Bind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser, this ); + + Layout(); + + // We specify the width of the right window (m_symbol_view_panel), because specify + // the width of the left window does not work as expected when SetSashGravity() is called + m_hsplitter->SetSashPosition( m_h_sash_pos ? m_h_sash_pos : HorizPixelsFromDU( 220 ) ); + + if( m_vsplitter ) + m_vsplitter->SetSashPosition( m_v_sash_pos ? m_v_sash_pos : VertPixelsFromDU( 230 ) ); + + if( m_last_dlg_size == wxSize( -1, -1 ) ) + SetSizeInDU( 440, 340 ); + else + SetSize( m_last_dlg_size ); + + SetInitialFocus( m_tree ); + okButton->SetDefault(); +} + + +DIALOG_CHOOSE_FOOTPRINT::~DIALOG_CHOOSE_FOOTPRINT() +{ + Unbind( wxEVT_TIMER, &DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer, this ); + Unbind( COMPONENT_PRESELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected, this ); + Unbind( COMPONENT_SELECTED, &DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected, this ); + + if( m_browser_button ) + m_browser_button->Unbind( wxEVT_COMMAND_BUTTON_CLICKED, &DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser, this ); + + // I am not sure the following two lines are necessary, + // but they will not hurt anyone + m_dbl_click_timer->Stop(); + delete m_dbl_click_timer; + + m_last_dlg_size = GetSize(); + m_h_sash_pos = m_hsplitter->GetSashPosition(); + + if( m_vsplitter ) + m_v_sash_pos = m_vsplitter->GetSashPosition(); +} + + +wxPanel* DIALOG_CHOOSE_FOOTPRINT::ConstructRightPanel( wxWindow* aParent ) +{ + auto panel = new wxPanel( aParent ); + auto sizer = new wxBoxSizer( wxVERTICAL ); + + m_preview_ctrl = new FOOTPRINT_PREVIEW_WIDGET( panel, Kiway() ); + sizer->Add( m_preview_ctrl, 1, wxEXPAND | wxBOTTOM | wxRIGHT, 5 ); + + panel->SetSizer( sizer ); + panel->Layout(); + sizer->Fit( panel ); + + return panel; +} + + +LIB_ID DIALOG_CHOOSE_FOOTPRINT::GetSelectedLibId() const +{ + return m_tree->GetSelectedLibId(); +} + + +void DIALOG_CHOOSE_FOOTPRINT::OnUseBrowser( wxCommandEvent& aEvent ) +{ + m_external_browser_requested = true; + EndQuasiModal( wxID_OK ); +} + + +void DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer( wxTimerEvent& aEvent ) +{ + // Hack handler because of eaten MouseUp event. See + // DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected for the beginning + // of this spaghetti noodle. + + auto state = wxGetMouseState(); + + if( state.LeftIsDown() ) + { + // Mouse hasn't been raised yet, so fire the timer again. Otherwise the + // purpose of this timer is defeated. + m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_FOOTPRINT::DblClickDelay ); + } + else + { + EndQuasiModal( wxID_OK ); + } +} + + +void DIALOG_CHOOSE_FOOTPRINT::OnComponentPreselected( wxCommandEvent& aEvent ) +{ + if( !m_preview_ctrl || !m_preview_ctrl->IsInitialized() ) + return; + + LIB_ID lib_id = m_tree->GetSelectedLibId(); + + if( !lib_id.IsValid() ) + { + m_preview_ctrl->SetStatusText( _( "No footprint selected" ) ); + } + else + { + m_preview_ctrl->ClearStatus(); + m_preview_ctrl->CacheFootprint( lib_id ); + m_preview_ctrl->DisplayFootprint( lib_id ); + } +} + + +void DIALOG_CHOOSE_FOOTPRINT::OnComponentSelected( wxCommandEvent& aEvent ) +{ + if( m_tree->GetSelectedLibId().IsValid() ) + { + // Got a selection. We can't just end the modal dialog here, because + // wx leaks some events back to the parent window (in particular, the + // MouseUp following a double click). + // + // NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. + // This isn't really feasible to bypass without a fully custom + // wxDataViewCtrl implementation, and even then might not be fully + // possible (docs are vague). To get around this, we use a one-shot + // timer to schedule the dialog close. + // + // See DIALOG_CHOOSE_FOOTPRINT::OnCloseTimer for the other end of this + // spaghetti noodle. + m_dbl_click_timer->StartOnce( DIALOG_CHOOSE_FOOTPRINT::DblClickDelay ); + } +} diff --git a/pcbnew/dialogs/dialog_choose_footprint.h b/pcbnew/dialogs/dialog_choose_footprint.h new file mode 100644 index 0000000000..257c050280 --- /dev/null +++ b/pcbnew/dialogs/dialog_choose_footprint.h @@ -0,0 +1,154 @@ +/* -*- c++ -*- + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2014 Henner Zeller + * Copyright (C) 2014-2018 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 DIALOG_CHOOSE_FOOTPRINT_H +#define DIALOG_CHOOSE_FOOTPRINT_H + +#include "dialog_shim.h" +#include +#include +#include + +class wxStaticBitmap; +class wxTextCtrl; +class wxStdDialogButtonSizer; +class wxDataViewCtrl; +class wxHtmlWindow; +class wxHtmlLinkEvent; +class wxPanel; +class wxChoice; +class wxButton; +class wxTimer; + +class PCB_BASE_FRAME; +class LIB_TREE; +class MODULE; + + +/** + * Dialog class to select a footprint from the libraries. This is the master + * View class in a Model-View-Adapter (mediated MVC) architecture. The other + * pieces are in: + * + * - Adapter: CMP_TREE_MODEL_ADAPTER in common/cmp_tree_model_adapter.h + * - Model: CMP_TREE_NODE and descendants in common/cmp_tree_model.h + * + * Because everything is tied together in the adapter class, see that file + * for thorough documentation. A simple example usage follows: + * + * // Create the adapter class + * auto adapter( FP_TREE_MODEL_ADAPTER::Create( Prj().PcbFootprintLibs() ) ); + * + * // Perform any configuration of adapter properties here + * adapter->SetPreselectNode( "LIB_NICKNAME", "FP_NAME", 2 ); + * + * // Initialize model from #FP_LIB_TABLE + * libNicknames = libs->GetLogicalLibs(); + * + * for( auto nickname : libNicknames ) + * { + * adapter->AddLibrary( nickname ); + * } + * + * // Create and display dialog + * DIALOG_CHOOSE_FOOTPRINT dlg( this, title, adapter, 1 ); + * bool selected = ( dlg.ShowModal() != wxID_CANCEL ); + * + * // Receive part + * if( selected ) + * { + * int unit; + * #LIB_ID id = dlg.GetSelectedAlias( &unit ); + * do_something( id, unit ); + * } + * + */ +class DIALOG_CHOOSE_FOOTPRINT : public DIALOG_SHIM +{ +public: + /** + * Create dialog to choose component. + * + * @param aParent a PCB_BASE_FRAME parent window. + * @param aAdapter FP_TREE_MODEL_ADAPTER::PTR. See CMP_TREE_MODEL_ADAPTER + * for documentation. + */ + DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent, const wxString& aTitle, + FP_TREE_MODEL_ADAPTER::PTR& aAdapter, bool aAllowBrowser ); + + ~DIALOG_CHOOSE_FOOTPRINT(); + + /** + * To be called after this dialog returns from ShowModal(). + * + * @return the #LIB_ID of the symbol that has been selected. + */ + LIB_ID GetSelectedLibId() 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; + } + +protected: + static constexpr int DblClickDelay = 100; // milliseconds + + wxPanel* ConstructRightPanel( wxWindow* aParent ); + + void OnCloseTimer( wxTimerEvent& aEvent ); + void OnUseBrowser( wxCommandEvent& aEvent ); + + void OnComponentPreselected( wxCommandEvent& aEvent ); + + /** + * Handle the selection of an item. This is called when either the search + * box or the tree receive an Enter, or the tree receives a double click. + * If the item selected is a category, it is expanded or collapsed; if it + * is a component, the component is picked. + */ + void OnComponentSelected( wxCommandEvent& aEvent ); + + wxTimer* m_dbl_click_timer; + wxButton* m_browser_button; + wxSplitterWindow* m_hsplitter; + wxSplitterWindow* m_vsplitter; + + static int m_h_sash_pos; // remember sash positions during a session + static int m_v_sash_pos; + + FOOTPRINT_PREVIEW_WIDGET* m_preview_ctrl; + LIB_TREE* m_tree; + + PCB_BASE_FRAME* m_parent; + bool m_external_browser_requested; + + // Remember the dialog size during a session + static wxSize m_last_dlg_size; +}; + +#endif /* DIALOG_CHOOSE_FOOTPRINT_H */ diff --git a/pcbnew/dialogs/dialog_get_footprint.cpp b/pcbnew/dialogs/dialog_get_footprint.cpp deleted file mode 100644 index 7edfa41a0d..0000000000 --- a/pcbnew/dialogs/dialog_get_footprint.cpp +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2010-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2018 KiCad Developers, see CHANGELOG.TXT for contributors. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, you may find one here: - * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html - * or you may search the http://www.gnu.org website for the version 2 license, - * or you may write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - -/****************************************************************************/ -/* Show a dialog frame to choose a name from an history list, or a new name */ -/* to select a module */ -/****************************************************************************/ - -static wxArrayString s_HistoryList; -static unsigned s_HistoryMaxCount = 8; // Max number of items displayed in history list - - -DIALOG_GET_FOOTPRINT::DIALOG_GET_FOOTPRINT( PCB_BASE_FRAME* parent, bool aShowBrowseButton ) : - DIALOG_GET_FOOTPRINT_BASE( parent, -1 ), - m_frame( parent ) -{ - - m_Text = wxEmptyString; - m_selectByBrowser = false; - m_selectionIsKeyword = false; - - for( size_t ii = 0; ii < s_HistoryList.size(); ++ii ) - { - LIB_ID fpid; - fpid.Parse( s_HistoryList[ ii ], LIB_ID::ID_PCB ); - - if( m_frame->CheckFootprint( fpid ) ) - m_historyList->Append( s_HistoryList[ ii ] ); - } - - m_buttonBrowse->Show( aShowBrowseButton ); - m_buttonBrowse->Enable( aShowBrowseButton ); - - m_sdbSizerOK->SetDefault(); - - m_textCmpNameCtrl->SetFocus(); - GetSizer()->Fit( this ); - GetSizer()->SetSizeHints( this ); -} - - -void DIALOG_GET_FOOTPRINT::OnHistoryClick( wxCommandEvent& aEvent ) -{ - m_textCmpNameCtrl->SetValue( m_historyList->GetStringSelection() ); -} - - -void DIALOG_GET_FOOTPRINT::Accept( wxCommandEvent& aEvent ) -{ - m_selectionIsKeyword = false; - switch( aEvent.GetId() ) - { - case ID_SEL_BY_LISTBOX: - m_Text = m_historyList->GetStringSelection(); - break; - - case wxID_OK: - if( m_historyList->HasFocus() ) - m_Text = m_historyList->GetStringSelection(); - else - m_Text = m_textCmpNameCtrl->GetValue(); - break; - - case ID_ACCEPT_KEYWORD: - m_selectionIsKeyword = true; - m_Text = m_textCmpNameCtrl->GetValue(); - break; - - case ID_LIST_ALL: - m_Text = wxT( "*" ); - break; - - case ID_BROWSE: - m_Text = wxEmptyString; - m_selectByBrowser = true; - break; - } - - m_Text.Trim( false ); // Remove blanks at beginning - m_Text.Trim( true ); // Remove blanks at end - - // Put an wxID_OK event through the dialog infrastrucutre - aEvent.SetEventType( wxEVT_COMMAND_BUTTON_CLICKED ); - aEvent.SetId( wxID_OK ); - aEvent.Skip(); -} - - -// Return the component name selected by the dialog -wxString DIALOG_GET_FOOTPRINT::GetComponentName( void ) -{ - return m_Text; -} - - -/* Initialize the default component name default choice -*/ -void DIALOG_GET_FOOTPRINT::SetComponentName( const wxString& name ) -{ - if( m_textCmpNameCtrl ) - { - m_textCmpNameCtrl->SetValue( name ); - m_textCmpNameCtrl->SetSelection( -1, -1 ); - } -} - - -/* - * Add the string "aName" to the history list aHistoryList - */ -void AddHistoryComponentName( const wxString& aName ) -{ - // Remove duplicates - for( int ii = s_HistoryList.GetCount() - 1; ii >= 0; --ii ) - { - if( s_HistoryList[ ii ] == aName ) - s_HistoryList.RemoveAt( (size_t) ii ); - } - - // Add the new name at the beginning of the history list - s_HistoryList.Insert( aName, 0 ); - - // Remove extra names - while( s_HistoryList.GetCount() >= s_HistoryMaxCount ) - s_HistoryList.RemoveAt( s_HistoryList.GetCount() - 1 ); -} diff --git a/pcbnew/dialogs/dialog_get_footprint.h b/pcbnew/dialogs/dialog_get_footprint.h deleted file mode 100644 index f35cb98710..0000000000 --- a/pcbnew/dialogs/dialog_get_footprint.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This program source code file is part of KiCad, a free EDA CAD application. - * - * Copyright (C) 2010-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr - * Copyright (C) 1992-2018 KiCad Developers, see CHANGELOG.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 __INCLUDE_DIALOG_GET_COMPONENT_H__ -#define __INCLUDE_DIALOG_GET_COMPONENT_H__ - -#include "dialog_get_footprint_base.h" - -class PCB_BASE_FRAME; - -void AddHistoryComponentName( const wxString& Name ); - - -/* Dialog frame to choose a component name */ -class DIALOG_GET_FOOTPRINT : public DIALOG_GET_FOOTPRINT_BASE -{ -private: - PCB_BASE_FRAME* m_frame; - wxString m_Text; - bool m_selectionIsKeyword; - bool m_selectByBrowser; - -public: - // Constructor and destructor - DIALOG_GET_FOOTPRINT( PCB_BASE_FRAME* parent, bool aShowBrowseButton ); - ~DIALOG_GET_FOOTPRINT() override {}; - - /** - * Function GetComponentName - * @return the selection (name or keyword) - */ - wxString GetComponentName(); - - /** - * Function IsKeyword - * @return true if the returned string is a keyword - */ - bool IsKeyword() - { - return m_selectionIsKeyword; - } - - /** - * Function SelectByBrowser - * @return true if the footprint browser should be shown to select the footprint - */ - bool SelectByBrowser() - { - return m_selectByBrowser; - } - - void SetComponentName( const wxString& name ); - -private: - void OnHistoryClick( wxCommandEvent& aEvent ) override; - void Accept( wxCommandEvent& aEvent ) override; -}; - - -#endif /* __INCLUDE_DIALOG_GET_COMPONENT_H__ */ diff --git a/pcbnew/dialogs/dialog_get_footprint_base.cpp b/pcbnew/dialogs/dialog_get_footprint_base.cpp deleted file mode 100644 index 86fd1c135d..0000000000 --- a/pcbnew/dialogs/dialog_get_footprint_base.cpp +++ /dev/null @@ -1,100 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Apr 20 2018) -// http://www.wxformbuilder.org/ -// -// PLEASE DO *NOT* EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#include "dialog_get_footprint_base.h" - -/////////////////////////////////////////////////////////////////////////// - -DIALOG_GET_FOOTPRINT_BASE::DIALOG_GET_FOOTPRINT_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style ) -{ - this->SetSizeHints( wxDefaultSize, wxDefaultSize ); - - wxBoxSizer* bSizerMain; - bSizerMain = new wxBoxSizer( wxVERTICAL ); - - wxBoxSizer* bSizerUpper; - bSizerUpper = new wxBoxSizer( wxHORIZONTAL ); - - wxBoxSizer* bSizerLeft; - bSizerLeft = new wxBoxSizer( wxVERTICAL ); - - m_staticTextName = new wxStaticText( this, wxID_ANY, _("Name:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextName->Wrap( -1 ); - bSizerLeft->Add( m_staticTextName, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - - m_textCmpNameCtrl = new wxTextCtrl( this, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0 ); - bSizerLeft->Add( m_textCmpNameCtrl, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - m_staticTextHistory = new wxStaticText( this, wxID_ANY, _("History list:"), wxDefaultPosition, wxDefaultSize, 0 ); - m_staticTextHistory->Wrap( -1 ); - bSizerLeft->Add( m_staticTextHistory, 0, wxTOP|wxRIGHT|wxLEFT, 5 ); - - m_historyList = new wxListBox( this, ID_SEL_BY_LISTBOX, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 ); - m_historyList->SetMinSize( wxSize( 200,100 ) ); - - bSizerLeft->Add( m_historyList, 1, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 ); - - - bSizerUpper->Add( bSizerLeft, 1, wxEXPAND, 5 ); - - wxBoxSizer* bSizerRight; - bSizerRight = new wxBoxSizer( wxVERTICAL ); - - m_buttonKW = new wxButton( this, ID_ACCEPT_KEYWORD, _("Search by Keyword"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerRight->Add( m_buttonKW, 0, wxALL|wxEXPAND, 5 ); - - m_buttonList = new wxButton( this, ID_LIST_ALL, _("List All"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerRight->Add( m_buttonList, 0, wxALL|wxEXPAND, 5 ); - - m_buttonBrowse = new wxButton( this, ID_BROWSE, _("Select by Browser"), wxDefaultPosition, wxDefaultSize, 0 ); - bSizerRight->Add( m_buttonBrowse, 0, wxALL|wxEXPAND, 5 ); - - - bSizerUpper->Add( bSizerRight, 0, wxALIGN_CENTER_VERTICAL, 5 ); - - - bSizerMain->Add( bSizerUpper, 1, wxEXPAND, 5 ); - - m_staticline1 = new wxStaticLine( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLI_HORIZONTAL ); - bSizerMain->Add( m_staticline1, 0, wxEXPAND|wxLEFT|wxRIGHT, 5 ); - - m_sdbSizer = new wxStdDialogButtonSizer(); - m_sdbSizerOK = new wxButton( this, wxID_OK ); - m_sdbSizer->AddButton( m_sdbSizerOK ); - m_sdbSizerCancel = new wxButton( this, wxID_CANCEL ); - m_sdbSizer->AddButton( m_sdbSizerCancel ); - m_sdbSizer->Realize(); - - bSizerMain->Add( m_sdbSizer, 0, wxALL|wxEXPAND, 5 ); - - - this->SetSizer( bSizerMain ); - this->Layout(); - bSizerMain->Fit( this ); - - this->Centre( wxBOTH ); - - // Connect Events - m_historyList->Connect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::OnHistoryClick ), NULL, this ); - m_historyList->Connect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonKW->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonList->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonBrowse->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_sdbSizerOK->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); -} - -DIALOG_GET_FOOTPRINT_BASE::~DIALOG_GET_FOOTPRINT_BASE() -{ - // Disconnect Events - m_historyList->Disconnect( wxEVT_COMMAND_LISTBOX_SELECTED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::OnHistoryClick ), NULL, this ); - m_historyList->Disconnect( wxEVT_COMMAND_LISTBOX_DOUBLECLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonKW->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonList->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_buttonBrowse->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - m_sdbSizerOK->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_GET_FOOTPRINT_BASE::Accept ), NULL, this ); - -} diff --git a/pcbnew/dialogs/dialog_get_footprint_base.fbp b/pcbnew/dialogs/dialog_get_footprint_base.fbp deleted file mode 100644 index 3cafce2724..0000000000 --- a/pcbnew/dialogs/dialog_get_footprint_base.fbp +++ /dev/null @@ -1,849 +0,0 @@ - - - - - - C++ - 1 - source_name - 0 - 0 - res - UTF-8 - connect - dialog_get_footprint_base - 1000 - none - - 1 - dialog_get_footprint_base - - . - - 1 - 1 - 1 - 1 - UI - 0 - 0 - - 0 - wxAUI_MGR_DEFAULT - - wxBOTH - - 1 - 1 - impl_virtual - - - - 0 - wxID_ANY - - - DIALOG_GET_FOOTPRINT_BASE - - -1,-1 - wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER - DIALOG_SHIM; dialog_shim.h - Choose Footprint - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bSizerMain - wxVERTICAL - none - - 5 - wxEXPAND - 1 - - - bSizerUpper - wxHORIZONTAL - none - - 5 - wxEXPAND - 1 - - - bSizerLeft - wxVERTICAL - none - - 5 - wxTOP|wxRIGHT|wxLEFT - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - Name: - - 0 - - - 0 - - 1 - m_staticTextName - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - 0 - - 0 - - 1 - m_textCmpNameCtrl - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxTOP|wxRIGHT|wxLEFT - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - History list: - - 0 - - - 0 - - 1 - m_staticTextHistory - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - - - -1 - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT - 1 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - ID_SEL_BY_LISTBOX - - 0 - - - 0 - 200,100 - 1 - m_historyList - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - - - - - - - - - - - OnHistoryClick - Accept - - - - - - - - - - - - - - - - - - - 5 - wxALIGN_CENTER_VERTICAL - 0 - - - bSizerRight - wxVERTICAL - none - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - ID_ACCEPT_KEYWORD - Search by Keyword - - 0 - - - 0 - - 1 - m_buttonKW - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - Accept - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - ID_LIST_ALL - List All - - 0 - - - 0 - - 1 - m_buttonList - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - Accept - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - ID_BROWSE - Select by Browser - - 0 - - - 0 - - 1 - m_buttonBrowse - 1 - - - protected - 1 - - Resizable - 1 - - - - 0 - - - wxFILTER_NONE - wxDefaultValidator - - - - - Accept - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxEXPAND|wxLEFT|wxRIGHT - 0 - - 1 - 1 - 1 - 1 - - - - - - - - 1 - 0 - 1 - - 1 - 0 - Dock - 0 - Left - 1 - - 1 - - 0 - 0 - wxID_ANY - - 0 - - - 0 - - 1 - m_staticline1 - 1 - - - protected - 1 - - Resizable - 1 - - wxLI_HORIZONTAL - - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 5 - wxALL|wxEXPAND - 0 - - 0 - 1 - 0 - 0 - 0 - 1 - 0 - 0 - - m_sdbSizer - protected - - - - - - Accept - - - - - - - - diff --git a/pcbnew/dialogs/dialog_get_footprint_base.h b/pcbnew/dialogs/dialog_get_footprint_base.h deleted file mode 100644 index 3cb17602a1..0000000000 --- a/pcbnew/dialogs/dialog_get_footprint_base.h +++ /dev/null @@ -1,67 +0,0 @@ -/////////////////////////////////////////////////////////////////////////// -// C++ code generated with wxFormBuilder (version Apr 20 2018) -// http://www.wxformbuilder.org/ -// -// PLEASE DO *NOT* EDIT THIS FILE! -/////////////////////////////////////////////////////////////////////////// - -#ifndef __DIALOG_GET_FOOTPRINT_BASE_H__ -#define __DIALOG_GET_FOOTPRINT_BASE_H__ - -#include -#include -#include -#include "dialog_shim.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/////////////////////////////////////////////////////////////////////////// - -#define ID_SEL_BY_LISTBOX 1000 -#define ID_ACCEPT_KEYWORD 1001 -#define ID_LIST_ALL 1002 -#define ID_BROWSE 1003 - -/////////////////////////////////////////////////////////////////////////////// -/// Class DIALOG_GET_FOOTPRINT_BASE -/////////////////////////////////////////////////////////////////////////////// -class DIALOG_GET_FOOTPRINT_BASE : public DIALOG_SHIM -{ - private: - - protected: - wxStaticText* m_staticTextName; - wxTextCtrl* m_textCmpNameCtrl; - wxStaticText* m_staticTextHistory; - wxListBox* m_historyList; - wxButton* m_buttonKW; - wxButton* m_buttonList; - wxButton* m_buttonBrowse; - wxStaticLine* m_staticline1; - wxStdDialogButtonSizer* m_sdbSizer; - wxButton* m_sdbSizerOK; - wxButton* m_sdbSizerCancel; - - // Virtual event handlers, overide them in your derived class - virtual void OnHistoryClick( wxCommandEvent& event ) { event.Skip(); } - virtual void Accept( wxCommandEvent& event ) { event.Skip(); } - - - public: - - DIALOG_GET_FOOTPRINT_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Choose Footprint"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER ); - ~DIALOG_GET_FOOTPRINT_BASE(); - -}; - -#endif //__DIALOG_GET_FOOTPRINT_BASE_H__ diff --git a/pcbnew/footprint_edit_frame.h b/pcbnew/footprint_edit_frame.h index 5523bd63a6..a6186a04cd 100644 --- a/pcbnew/footprint_edit_frame.h +++ b/pcbnew/footprint_edit_frame.h @@ -316,7 +316,7 @@ public: * @return a pointer to a module if this module is selected or NULL otherwise * @param aPcb = the board from modules can be loaded */ - MODULE* SelectFootprint( BOARD* aPcb ); + MODULE* SelectFootprintFromBoard( BOARD* aPcb ); // functions to edit footprint edges diff --git a/pcbnew/footprint_editor_utils.cpp b/pcbnew/footprint_editor_utils.cpp index f4f436d5ba..a430e36ebc 100644 --- a/pcbnew/footprint_editor_utils.cpp +++ b/pcbnew/footprint_editor_utils.cpp @@ -537,7 +537,7 @@ void FOOTPRINT_EDIT_FRAME::Process_Special_Functions( wxCommandEvent& event ) break; } - MODULE* module = LoadModuleFromLibrary( wxEmptyString ); + MODULE* module = SelectFootprintFromLibTree( wxEmptyString ); if( !module ) break; diff --git a/pcbnew/footprint_viewer_frame.cpp b/pcbnew/footprint_viewer_frame.cpp index 1dadf6430c..bf44250ab0 100644 --- a/pcbnew/footprint_viewer_frame.cpp +++ b/pcbnew/footprint_viewer_frame.cpp @@ -785,7 +785,7 @@ void FOOTPRINT_VIEWER_FRAME::SelectCurrentFootprint( wxCommandEvent& event ) { wxString curr_nickname = getCurNickname(); MODULE* oldmodule = GetBoard()->m_Modules; - MODULE* module = LoadModuleFromLibrary( curr_nickname, false ); + MODULE* module = SelectFootprintFromLibTree( curr_nickname, false ); if( module ) { diff --git a/pcbnew/fp_tree_model_adapter.cpp b/pcbnew/fp_tree_model_adapter.cpp new file mode 100644 index 0000000000..5847d6301b --- /dev/null +++ b/pcbnew/fp_tree_model_adapter.cpp @@ -0,0 +1,75 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2018 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include + +#include +#include +#include +#include + +#include "fp_tree_model_adapter.h" + + +FP_TREE_MODEL_ADAPTER::PTR FP_TREE_MODEL_ADAPTER::Create( LIB_TABLE* aLibs ) +{ + return PTR( new FP_TREE_MODEL_ADAPTER( aLibs ) ); +} + + +FP_TREE_MODEL_ADAPTER::FP_TREE_MODEL_ADAPTER( LIB_TABLE* aLibs ) + : m_libs( (FP_LIB_TABLE*) aLibs ) +{} + + +FP_TREE_MODEL_ADAPTER::~FP_TREE_MODEL_ADAPTER() +{} + + +void FP_TREE_MODEL_ADAPTER::AddLibraries( FOOTPRINT_LIST* aFootprintInfoList ) +{ + // Note: FOOTPRINT_INFO list must be sorted! + + wxString currentLib; + std::vector footprints; + + for( auto& footprint : aFootprintInfoList->GetList() ) + { + if( footprint->GetNickname() != currentLib ) + { + if( footprints.size() ) + DoAddLibrary( currentLib, m_libs->GetDescription( currentLib ), footprints ); + + footprints.clear(); + currentLib = footprint->GetNickname(); + } + + footprints.push_back( footprint.get() ); + } + + if( footprints.size() ) + DoAddLibrary( currentLib, m_libs->GetDescription( currentLib ), footprints ); +} + + +wxString FP_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, int aUnit ) +{ + return GenerateFootprintInfo( m_libs, aLibId ); +} diff --git a/pcbnew/fp_tree_model_adapter.h b/pcbnew/fp_tree_model_adapter.h new file mode 100644 index 0000000000..f6bf354699 --- /dev/null +++ b/pcbnew/fp_tree_model_adapter.h @@ -0,0 +1,61 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Chris Pavlina + * Copyright (C) 2014 Henner Zeller + * 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 as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef FP_TREE_MODEL_ADAPTER_H +#define FP_TREE_MODEL_ADAPTER_H + +#include +#include + +class LIB_TABLE; +class FP_LIB_TABLE; + +class FP_TREE_MODEL_ADAPTER : public LIB_TREE_MODEL_ADAPTER +{ +public: + /** + * Destructor. Do NOT delete this class manually; it is reference-counted by wxObject. + */ + ~FP_TREE_MODEL_ADAPTER(); + + /** + * Factory function: create a model adapter in a reference-counting container. + * + * @param aLibs library set from which parts will be loaded + */ + static PTR Create( LIB_TABLE* aLibs ); + + void AddLibraries( FOOTPRINT_LIST* aFootprintInfoList ); + + wxString GenerateInfo( LIB_ID const& aLibId, int aUnit ) override; + +protected: + + /** + * Constructor; takes a set of libraries to be included in the search. + */ + FP_TREE_MODEL_ADAPTER( LIB_TABLE* aLibs ); + +private: + FP_LIB_TABLE* m_libs; +}; + +#endif // FP_TREE_MODEL_ADAPTER_H diff --git a/pcbnew/generate_footprint_info.cpp b/pcbnew/generate_footprint_info.cpp new file mode 100644 index 0000000000..fe6038b90c --- /dev/null +++ b/pcbnew/generate_footprint_info.cpp @@ -0,0 +1,141 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Chris Pavlina + * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include +#include +#include +#include + + +static const wxString DescriptionFormat = + "__NAME__" + "
__DESC__" + "
" + "__FIELDS__" + "
"; + +static const wxString KeywordsFormat = + "" + " " + _( "Keywords" ) + "" + " __KEYWORDS__" + ""; + +static const wxString DocFormat = + "" + " " + _( "Documentation" ) + "" + " __TEXT__" + ""; + + +class FOOTPRINT_INFO_GENERATOR +{ + wxString m_html; + FP_LIB_TABLE* m_fp_lib_table; + LIB_ID const m_lib_id; + MODULE* m_module; + +public: + FOOTPRINT_INFO_GENERATOR( FP_LIB_TABLE* aFpLibTable, LIB_ID const& aLibId ) + : m_html( DescriptionFormat ), + m_fp_lib_table( aFpLibTable ), + m_lib_id( aLibId ), + m_module( nullptr ) + { } + + /** + * Generate the HTML internally. + */ + void GenerateHtml() + { + wxCHECK_RET( m_fp_lib_table, "Footprint library table pointer is not valid" ); + + if( !m_lib_id.IsValid() ) + return; + + try + { + m_module = m_fp_lib_table->FootprintLoad( m_lib_id.GetLibNickname(), + m_lib_id.GetLibItemName() ); + } + catch( const IO_ERROR& ioe ) + { + wxLogError( wxString::Format( _( "Error loading footprint %s from library %s.\n\n%s" ), + m_lib_id.GetLibItemName().wx_str(), + m_lib_id.GetLibNickname().wx_str(), + ioe.What() ) ); + return; + } + + if( m_module ) + { + wxString name = m_lib_id.GetLibItemName(); + wxString desc = m_module->GetDescription(); + wxString keywords = m_module->GetKeywords(); + wxString doc; + + // It is currently common practice to store a documentation link in the description. + int idx = desc.find( wxT( "http:" ) ); + + if( idx >= 0 ) + { + doc = desc.substr( (unsigned) idx ); + + desc = desc.substr( 0, (unsigned) idx ); + desc = desc.Trim( true ); + + if( desc.Last() == ',' ) + desc.RemoveLast( 1 ); + } + + m_html.Replace( "__NAME__", EscapedHTML( name ) ); + m_html.Replace( "__DESC__", EscapedHTML( desc ) ); + + wxString keywordsHtml = KeywordsFormat; + keywordsHtml.Replace( "__KEYWORDS__", EscapedHTML( keywords ) ); + + wxString docHtml = DocFormat; + docHtml.Replace( "__HREF__", EscapedHTML( doc ) ); + + if( doc.Length() > 75 ) + doc = doc.Left( 72 ) + wxT( "..." ); + + docHtml.Replace( "__TEXT__", EscapedHTML( doc ) ); + + m_html.Replace( "__FIELDS__", keywordsHtml + docHtml ); + } + } + + /** + * Return the generated HTML. + */ + wxString GetHtml() + { + return m_html; + } + +}; + + +wxString GenerateFootprintInfo( FP_LIB_TABLE* aSymLibTable, LIB_ID const& aLibId ) +{ + FOOTPRINT_INFO_GENERATOR gen( aSymLibTable, aLibId ); + gen.GenerateHtml(); + return gen.GetHtml(); +} diff --git a/pcbnew/generate_footprint_info.h b/pcbnew/generate_footprint_info.h new file mode 100644 index 0000000000..2f2fac9eb7 --- /dev/null +++ b/pcbnew/generate_footprint_info.h @@ -0,0 +1,35 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2017 Chris Pavlina + * Copyright (C) 2017 KiCad Developers, see AUTHORS.txt for contributors. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#ifndef GENERATE_FOOTPRINT_INFO_H +#define GENERATE_FOOTPRINT_INFO_H + +#include + +class LIB_ID; +class FP_LIB_TABLE; + +/** + * Return an HTML page describing a #LIB_ID in a #FP_LIB_TABLE. This is suitable for inclusion + * in a wxHtmlWindow. + */ +wxString GenerateFootprintInfo( FP_LIB_TABLE* aFpLibTable, LIB_ID const& aLibId ); + +#endif // GENERATE_FOOTPRINT_INFO_H diff --git a/pcbnew/load_select_footprint.cpp b/pcbnew/load_select_footprint.cpp index 135773c510..c39c4ecb12 100644 --- a/pcbnew/load_select_footprint.cpp +++ b/pcbnew/load_select_footprint.cpp @@ -55,17 +55,33 @@ using namespace std::placeholders; #include #include #include -#include +#include #include #include #include +#include "fp_tree_model_adapter.h" -static void DisplayCmpDoc( wxString& aName, void* aData ); +static wxArrayString s_ModuleHistoryList; +static unsigned s_ModuleHistoryMaxCount = 8; + +static void AddModuleToHistory( const wxString& aName ) +{ + // Remove duplicates + for( int ii = s_ModuleHistoryList.GetCount() - 1; ii >= 0; --ii ) + { + if( s_ModuleHistoryList[ ii ] == aName ) + s_ModuleHistoryList.RemoveAt( (size_t) ii ); + } + + // Add the new name at the beginning of the history list + s_ModuleHistoryList.Insert( aName, 0 ); + + // Remove extra names + while( s_ModuleHistoryList.GetCount() >= s_ModuleHistoryMaxCount ) + s_ModuleHistoryList.RemoveAt( s_ModuleHistoryList.GetCount() - 1 ); +} -// 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 ) { @@ -86,7 +102,7 @@ bool FOOTPRINT_EDIT_FRAME::Load_Module_From_BOARD( MODULE* aModule ) if( ! frame->GetBoard() || ! frame->GetBoard()->m_Modules ) return false; - aModule = SelectFootprint( frame->GetBoard() ); + aModule = SelectFootprintFromBoard( frame->GetBoard()); } if( aModule == NULL ) @@ -101,8 +117,6 @@ bool FOOTPRINT_EDIT_FRAME::Load_Module_From_BOARD( MODULE* aModule ) newModule->SetParent( GetBoard() ); newModule->SetLink( aModule->GetTimeStamp() ); - aModule = newModule; - newModule->ClearFlags(); newModule->RunOnChildren( std::bind( &clearModuleItemFlags, _1 ) ); @@ -169,130 +183,82 @@ wxString PCB_BASE_FRAME::SelectFootprintFromLibBrowser() } -MODULE* PCB_BASE_FRAME::LoadModuleFromLibrary( const wxString& aLibrary, bool aUseFootprintViewer ) +MODULE* PCB_BASE_FRAME::SelectFootprintFromLibTree( const wxString& aLibrary, bool aAllowBrowser ) { FP_LIB_TABLE* fpTable = Prj().PcbFootprintLibs(); + wxString moduleName; + LIB_ID fpid; MODULE* module = NULL; - wxString moduleName, keys; - const wxString& libName = aLibrary; - bool allowWildSeach = true; static wxString lastComponentName; - // Ask for a component name or key words - DIALOG_GET_FOOTPRINT dlg( this, aUseFootprintViewer ); + WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 ); + GFootprintList.ReadFootprintFiles( fpTable, aLibrary.length() ? &aLibrary : NULL, &progressReporter ); + progressReporter.Show( false ); + + if( GFootprintList.GetErrorCount() ) + GFootprintList.DisplayErrors( this ); + + auto adapterPtr( FP_TREE_MODEL_ADAPTER::Create( fpTable ) ); + auto adapter = static_cast( adapterPtr.get() ); + + if( !s_ModuleHistoryList.empty() ) + { + std::vector history_list; + + for( auto const& item : s_ModuleHistoryList ) + history_list.push_back( GFootprintList.GetModuleInfo( item ) ); + + adapter->DoAddLibrary( "-- " + _( "Recently Used" ) + " --", wxEmptyString, history_list ); + adapter->SetPreselectNode( history_list[0]->GetLibId(), 0 ); + } + + adapter->AddLibraries( &GFootprintList ); - dlg.SetComponentName( lastComponentName ); + wxString title; + title.Printf( _( "Choose Footprint (%d items loaded)" ), adapter->GetItemCount() ); - if( dlg.ShowModal() == wxID_CANCEL ) + DIALOG_CHOOSE_FOOTPRINT dialog( this, title, adapterPtr, aAllowBrowser ); + + if( dialog.ShowQuasiModal() == wxID_CANCEL ) return NULL; - if( dlg.SelectByBrowser() ) + if( dialog.IsExternalBrowserSelected() ) { // SelectFootprintFromLibBrowser() returns the "full" footprint name, i.e. // / or LIB_ID format "lib_name:fp_name:rev#" moduleName = SelectFootprintFromLibBrowser(); + + if( moduleName.IsEmpty() ) // Cancel command + return NULL; + else + fpid.Parse( moduleName, LIB_ID::ID_PCB ); } else { - moduleName = dlg.GetComponentName(); - } - - if( moduleName.IsEmpty() ) // Cancel command - { - m_canvas->MoveCursorToCrossHair(); - return NULL; - } + fpid = dialog.GetSelectedLibId(); - if( dlg.IsKeyword() || moduleName.Contains( wxT( "?" ) ) || moduleName.Contains( wxT( "*" ) ) ) - { - // While SelectFootprint() can load a library at a time (and stop when a match - // is found), the async loader gives much better feedback and loads the libraries - // in parallel. - // If the footprints are already in the cache, ReadFootprintFiles() will return - // immediately. - WX_PROGRESS_REPORTER progressReporter( this, _( "Loading Footprint Libraries" ), 2 ); - MList.ReadFootprintFiles( fpTable, libName.length() ? &libName : NULL, &progressReporter ); - progressReporter.Show( false ); - - if( MList.GetErrorCount() ) - MList.DisplayErrors( this ); - - if( dlg.IsKeyword() ) // Selection by keywords - { - allowWildSeach = false; - keys = moduleName; - moduleName = SelectFootprint( this, libName, wxEmptyString, keys, fpTable ); - - if( moduleName.IsEmpty() ) // Cancel command - { - m_canvas->MoveCursorToCrossHair(); - return NULL; - } - } - else // Selection wild card - { - allowWildSeach = false; - moduleName = SelectFootprint( this, libName, moduleName, wxEmptyString, fpTable ); - - if( moduleName.IsEmpty() ) - { - m_canvas->MoveCursorToCrossHair(); - return NULL; // Cancel command. - } - } + if( !fpid.IsValid() ) + return NULL; + else + moduleName = fpid.Format(); } - LIB_ID fpid; - - wxCHECK_MSG( fpid.Parse( moduleName, LIB_ID::ID_PCB ) < 0, NULL, - wxString::Format( wxT( "Could not parse LIB_ID \"%s\"." ), moduleName ) ); - try { module = loadFootprint( fpid ); } catch( const IO_ERROR& ioe ) { - wxLogDebug( wxT( "An error occurred attemping to load footprint '%s'.\n\nError: %s" ), - fpid.Format().c_str(), GetChars( ioe.What() ) ); - } - - if( !module && allowWildSeach ) // Search with wild card - { - allowWildSeach = false; - - wxString wildname = wxChar( '*' ) + moduleName + wxChar( '*' ); - moduleName = wildname; - - moduleName = SelectFootprint( this, libName, moduleName, wxEmptyString, fpTable ); - - if( moduleName.IsEmpty() ) - { - m_canvas->MoveCursorToCrossHair(); - return NULL; // Cancel command. - } - else - { - wxCHECK_MSG( fpid.Parse( moduleName, LIB_ID::ID_PCB ) < 0, NULL, - wxString::Format( wxT( "Could not parse LIB_ID \"%s\"." ), moduleName ) ); - - try - { - module = loadFootprint( fpid ); - } - catch( const IO_ERROR& ioe ) - { - wxLogDebug( wxT( "An error occurred attemping to load footprint '%s'.\n\nError: %s" ), - fpid.Format().c_str(), GetChars( ioe.What() ) ); - } - } + wxLogDebug( wxT( "Error loading footprint '%s'.\n\nError: %s" ), + fpid.Format().c_str(), + ioe.What() ); } if( module ) { lastComponentName = moduleName; - AddHistoryComponentName( moduleName ); + AddModuleToHistory( moduleName ); } return module; @@ -352,26 +318,6 @@ MODULE* PCB_BASE_FRAME::LoadFootprint( const LIB_ID& aFootprintId ) } -bool PCB_BASE_FRAME::CheckFootprint( const LIB_ID& aFootprintId ) -{ - const wxString& libNickname = aFootprintId.GetLibNickname(); - const wxString& fpName = aFootprintId.GetLibItemName(); - FP_LIB_TABLE* fpTable = Prj().PcbFootprintLibs(); - - try - { - const FP_LIB_TABLE_ROW* fpTableRow = fpTable->FindRow( aFootprintId.GetLibNickname() ); - - if( fpTableRow && fpTableRow->GetIsEnabled() ) - return fpTable->FootprintLoad( libNickname, fpName ) != nullptr; - } - catch( ... ) - { } - - return false; -} - - MODULE* PCB_BASE_FRAME::loadFootprint( const LIB_ID& aFootprintId ) { FP_LIB_TABLE* fptbl = Prj().PcbFootprintLibs(); @@ -400,131 +346,7 @@ MODULE* PCB_BASE_FRAME::loadFootprint( const LIB_ID& aFootprintId ) } -wxString PCB_BASE_FRAME::SelectFootprint( EDA_DRAW_FRAME* aWindow, - const wxString& aLibraryName, - const wxString& aMask, - const wxString& aKeyWord, - FP_LIB_TABLE* aTable ) -{ - static wxString oldName; // Save the name of the last module loaded. - - wxString fpname; - wxString msg; - wxArrayString libraries; - - std::vector< wxArrayString > rows; - - wxASSERT( aTable != NULL ); - - MList.ReadFootprintFiles( aTable, !aLibraryName ? NULL : &aLibraryName ); - - if( MList.GetErrorCount() ) - MList.DisplayErrors( this ); - - if( MList.GetCount() == 0 ) - { - wxString tmp; - - for( unsigned i = 0; i < libraries.GetCount(); i++ ) - tmp += libraries[i] + wxT( "\n" ); - - msg.Printf( _( "No footprints could be read from library file(s):\n\n%s\nin any of " - "the library search paths. Verify your system is configured properly " - "so the footprint libraries can be found." ), GetChars( tmp ) ); - DisplayError( aWindow, msg ); - return wxEmptyString; - } - - if( !aKeyWord.IsEmpty() ) // Create a list of modules found by keyword. - { - wxString keyword = aKeyWord.Upper(); - - for( unsigned ii = 0; ii < MList.GetCount(); ii++ ) - { - if( KeywordMatch( keyword, MList.GetItem( ii ).GetKeywords().Upper() ) ) - { - wxArrayString cols; - cols.Add( MList.GetItem( ii ).GetFootprintName() ); - cols.Add( MList.GetItem( ii ).GetNickname() ); - rows.push_back( cols ); - } - } - } - else if( !aMask.IsEmpty() ) // Create a list of modules found by pattern - { - for( unsigned ii = 0; ii < MList.GetCount(); ii++ ) - { - const wxString& candidate = MList.GetItem( ii ).GetFootprintName(); - - if( WildCompareString( aMask, candidate, false ) ) - { - wxArrayString cols; - cols.Add( MList.GetItem( ii ).GetFootprintName() ); - cols.Add( MList.GetItem( ii ).GetNickname() ); - rows.push_back( cols ); - } - } - } - else // Create the full list of modules - { - for( unsigned ii = 0; ii < MList.GetCount(); ii++ ) - { - wxArrayString cols; - cols.Add( MList.GetItem( ii ).GetFootprintName() ); - cols.Add( MList.GetItem( ii ).GetNickname() ); - rows.push_back( cols ); - } - } - - if( !rows.empty() ) - { - wxArrayString headers; - - headers.Add( _( "Footprint" ) ); - headers.Add( _( "Library" ) ); - - msg.Printf( _( "Footprints [%d items]" ), (int) rows.size() ); - - EDA_LIST_DIALOG dlg( aWindow, msg, headers, rows, oldName, DisplayCmpDoc ); - - if( dlg.ShowModal() == wxID_OK ) - { - if( !dlg.GetTextSelection( 0 ).IsEmpty() ) - fpname = dlg.GetTextSelection( 1 ) + wxT( ":" ) + dlg.GetTextSelection( 0 ); - - SkipNextLeftButtonReleaseEvent(); - } - } - else - { - DisplayError( aWindow, _( "No footprint found." ) ); - } - - if( fpname != wxEmptyString ) - oldName = fpname; - - wxLogDebug( wxT( "Footprint '%s' was selected." ), GetChars( fpname ) ); - - return fpname; -} - - -static void DisplayCmpDoc( wxString& aName, void* aData ) -{ - FOOTPRINT_INFO* module_info = MList.GetModuleInfo( aName ); - - if( !module_info ) - { - aName.Empty(); - return; - } - - aName = _( "Description: " ) + module_info->GetDoc(); - aName += _( "\nKey words: " ) + module_info->GetKeywords(); -} - - -MODULE* FOOTPRINT_EDIT_FRAME::SelectFootprint( BOARD* aPcb ) +MODULE* FOOTPRINT_EDIT_FRAME::SelectFootprintFromBoard( BOARD* aPcb ) { static wxString oldName; // Save name of last module selected. @@ -576,7 +398,7 @@ MODULE* FOOTPRINT_EDIT_FRAME::SelectFootprint( BOARD* aPcb ) void FOOTPRINT_EDIT_FRAME::OnSaveLibraryAs( wxCommandEvent& aEvent ) { - wxString curLibPath = getLibPath(); + wxString curLibPath; wxString dstLibPath = CreateNewLibrary(); if( !dstLibPath ) diff --git a/pcbnew/onleftclick.cpp b/pcbnew/onleftclick.cpp index e6b9a85167..f5178e84eb 100644 --- a/pcbnew/onleftclick.cpp +++ b/pcbnew/onleftclick.cpp @@ -368,7 +368,7 @@ void PCB_EDIT_FRAME::OnLeftClick( wxDC* aDC, const wxPoint& aPosition ) if( (curr_item == NULL) || (curr_item->GetFlags() == 0) ) { m_canvas->MoveCursorToCrossHair(); - MODULE* module = LoadModuleFromLibrary( wxEmptyString, Prj().PcbFootprintLibs() ); + MODULE* module = SelectFootprintFromLibTree( wxEmptyString, Prj().PcbFootprintLibs()); SetCurItem( (BOARD_ITEM*) module ); diff --git a/pcbnew/tools/pcb_editor_control.cpp b/pcbnew/tools/pcb_editor_control.cpp index d8a4d96972..be2a875533 100644 --- a/pcbnew/tools/pcb_editor_control.cpp +++ b/pcbnew/tools/pcb_editor_control.cpp @@ -467,7 +467,7 @@ int PCB_EDITOR_CONTROL::PlaceModule( const TOOL_EVENT& aEvent ) if( !module ) { // Pick the module to be placed - module = m_frame->LoadModuleFromLibrary( wxEmptyString ); + module = m_frame->SelectFootprintFromLibTree( wxEmptyString ); if( module == NULL ) continue;