mirror of
https://gitlab.com/kicad/code/kicad.git
synced 2025-03-30 05:56:55 +00:00
new feature: Schematic Design Blocks
Added to advanced config, default to off. EnableDesignBlocks=1 in kicad_advanced to test Fixes: https://gitlab.com/kicad/code/kicad/-/issues/2263
This commit is contained in:
parent
43177b7554
commit
2c99bc6c6d
common
CMakeLists.txtadvanced_config.cppdesign_block.hdesign_block_info.cppdesign_block_info.hdesign_block_info_impl.cppdesign_block_info_impl.hdesign_block_io.cppdesign_block_io.hdesign_block_lib_table.cpplib_table.keywordslib_tree_model.cpplib_tree_model_adapter.cpppaths.cppproject.cpp
settings
tool
trace_helpers.cppwildcards_and_files_ext.cppeeschema
CMakeLists.txtcross-probing.cppdesign_block_tree_model_adapter.cppdesign_block_tree_model_adapter.hdesign_block_utils.cpp
dialogs
eeschema_config.cppeeschema_helpers.cppeeschema_helpers.heeschema_jobs_handler.cppeeschema_settings.cppeeschema_settings.hfiles-io.cppmenubar.cppsch_base_frame.cppsch_base_frame.hsch_edit_frame.cppsch_edit_frame.hsch_painter.cppsheet.cppsymbol_tree_model_adapter.hsymbol_tree_synchronizing_adapter.htoolbars_sch_editor.cpptools
ee_actions.cppee_actions.hsch_design_block_control.cppsch_design_block_control.hsch_drawing_tools.cppsch_drawing_tools.hsch_edit_tool.cppsch_editor_control.cppsch_editor_control.h
widgets
include
advanced_config.h
core
design_block_lib_table.heda_draw_frame.hframe_type.hid.hlib_tree_model.hlib_tree_model_adapter.hpaths.hproject.hproject
settings
tool
trace_helpers.hwildcards_and_files_ext.hkicad
CMakeLists.txt
dialogs
panel_design_block_lib_table.cpppanel_design_block_lib_table.hpanel_design_block_lib_table_base.cpppanel_design_block_lib_table_base.fbppanel_design_block_lib_table_base.h
kicad.cppmenubar.cpppgm_kicad.htools
pcbnew
@ -14,6 +14,7 @@ include_directories(
|
||||
${CMAKE_SOURCE_DIR}/resources/bitmaps_png
|
||||
${CMAKE_SOURCE_DIR}/3d-viewer
|
||||
${CMAKE_SOURCE_DIR}/pcbnew
|
||||
${CMAKE_SOURCE_DIR}/kicad
|
||||
${INC_AFTER}
|
||||
)
|
||||
|
||||
@ -117,6 +118,11 @@ set( KICOMMON_SRCS
|
||||
|
||||
database/database_lib_settings.cpp
|
||||
|
||||
design_block_lib_table.cpp
|
||||
design_block_io.cpp
|
||||
design_block_info.cpp
|
||||
design_block_info_impl.cpp
|
||||
|
||||
advanced_config.cpp
|
||||
asset_archive.cpp
|
||||
array_axis.cpp
|
||||
@ -173,6 +179,8 @@ set( KICOMMON_SRCS
|
||||
../scripting/python_scripting.cpp
|
||||
|
||||
io/kicad/kicad_io_utils.cpp # needed by richio
|
||||
io/io_base.cpp
|
||||
io/io_utils.cpp
|
||||
)
|
||||
|
||||
if( KICAD_IPC_API )
|
||||
|
@ -99,6 +99,7 @@ static const wxChar UpdateUIEventInterval[] = wxT( "UpdateUIEventInterval" );
|
||||
static const wxChar V3DRT_BevelHeight_um[] = wxT( "V3DRT_BevelHeight_um" );
|
||||
static const wxChar V3DRT_BevelExtentFactor[] = wxT( "V3DRT_BevelExtentFactor" );
|
||||
static const wxChar UseClipper2[] = wxT( "UseClipper2" );
|
||||
static const wxChar EnableDesignBlocks[] = wxT( "EnableDesignBlocks" );
|
||||
static const wxChar EnableGenerators[] = wxT( "EnableGenerators" );
|
||||
static const wxChar EnableGit[] = wxT( "EnableGit" );
|
||||
static const wxChar EnableLibWithText[] = wxT( "EnableLibWithText" );
|
||||
@ -241,6 +242,7 @@ ADVANCED_CFG::ADVANCED_CFG()
|
||||
m_CompactSave = false;
|
||||
m_UpdateUIEventInterval = 0;
|
||||
m_ShowRepairSchematic = false;
|
||||
m_EnableDesignBlocks = false;
|
||||
m_EnableGenerators = false;
|
||||
m_EnableGit = false;
|
||||
m_EnableLibWithText = false;
|
||||
@ -459,6 +461,9 @@ void ADVANCED_CFG::loadSettings( wxConfigBase& aCfg )
|
||||
m_DisambiguationMenuDelay,
|
||||
50, 10000 ) );
|
||||
|
||||
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableDesignBlocks,
|
||||
&m_EnableDesignBlocks, m_EnableDesignBlocks ) );
|
||||
|
||||
configParams.push_back( new PARAM_CFG_BOOL( true, AC_KEYS::EnableGenerators,
|
||||
&m_EnableGenerators, m_EnableGenerators ) );
|
||||
|
||||
|
54
common/design_block.h
Normal file
54
common/design_block.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 Mike Williams <mike@mikebwilliams.com>
|
||||
* Copyright (C) 1992-2024 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 <kicommon.h>
|
||||
#include <lib_id.h>
|
||||
|
||||
|
||||
class DESIGN_BLOCK
|
||||
{
|
||||
public:
|
||||
void SetLibId( const LIB_ID& aName ) { m_lib_id = aName; }
|
||||
const LIB_ID& GetLibId() const { return m_lib_id; }
|
||||
|
||||
wxString GetLibIdAsString() const { return m_lib_id.Format(); }
|
||||
|
||||
wxString GetLibDescription() const { return m_libDescription; }
|
||||
void SetLibDescription( const wxString& aDesc ) { m_libDescription = aDesc; }
|
||||
|
||||
wxString GetKeywords() const { return m_keywords; }
|
||||
void SetKeywords( const wxString& aKeywords ) { m_keywords = aKeywords; }
|
||||
|
||||
wxString GetDocumentationUrl() const { return m_doc_url; }
|
||||
void SetDocumentationUrl( const wxString& aDocumentationUrl ) { m_doc_url = aDocumentationUrl; }
|
||||
|
||||
wxString GetSchematicFile() const { return m_schematicFile; }
|
||||
void SetSchematicFile( const wxString& aFile ) { m_schematicFile = aFile; }
|
||||
|
||||
private:
|
||||
LIB_ID m_lib_id;
|
||||
wxString m_schematicFile; // File name and path for schematic symbol.
|
||||
wxString m_libDescription; // File name and path for documentation file.
|
||||
wxString m_keywords; // Search keywords to find footprint in library.
|
||||
wxString m_doc_url; // URL of external documentation
|
||||
};
|
108
common/design_block_info.cpp
Normal file
108
common/design_block_info.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2024 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* Functions to read design block libraries and fill m_design_blocks by available design blocks names
|
||||
* and their documentation (comments and keywords)
|
||||
*/
|
||||
|
||||
#include <design_block_info.h>
|
||||
#include <fp_lib_table.h>
|
||||
#include <dialogs/html_message_box.h>
|
||||
#include <string_utils.h>
|
||||
#include <kiface_ids.h>
|
||||
#include <kiway.h>
|
||||
#include <lib_id.h>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <kiface_base.h>
|
||||
|
||||
DESIGN_BLOCK_INFO* DESIGN_BLOCK_LIST::GetDesignBlockInfo( const wxString& aLibNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
{
|
||||
if( aDesignBlockName.IsEmpty() )
|
||||
return nullptr;
|
||||
|
||||
for( std::unique_ptr<DESIGN_BLOCK_INFO>& db : m_list )
|
||||
{
|
||||
if( aLibNickname == db->GetLibNickname() && aDesignBlockName == db->GetDesignBlockName() )
|
||||
return db.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_INFO* DESIGN_BLOCK_LIST::GetDesignBlockInfo( const wxString& aDesignBlockName )
|
||||
{
|
||||
if( aDesignBlockName.IsEmpty() )
|
||||
return nullptr;
|
||||
|
||||
LIB_ID dbid;
|
||||
|
||||
wxCHECK_MSG( dbid.Parse( aDesignBlockName ) < 0, nullptr,
|
||||
wxString::Format( wxT( "'%s' is not a valid LIB_ID." ), aDesignBlockName ) );
|
||||
|
||||
return GetDesignBlockInfo( dbid.GetLibNickname(), dbid.GetLibItemName() );
|
||||
}
|
||||
|
||||
|
||||
std::vector<SEARCH_TERM> DESIGN_BLOCK_INFO::GetSearchTerms()
|
||||
{
|
||||
std::vector<SEARCH_TERM> terms;
|
||||
|
||||
terms.emplace_back( SEARCH_TERM( GetName(), 8 ) );
|
||||
|
||||
wxStringTokenizer keywordTokenizer( GetKeywords(), wxS( " " ), wxTOKEN_STRTOK );
|
||||
|
||||
while( keywordTokenizer.HasMoreTokens() )
|
||||
terms.emplace_back( SEARCH_TERM( keywordTokenizer.GetNextToken(), 4 ) );
|
||||
|
||||
// Also include keywords as one long string, just in case
|
||||
terms.emplace_back( SEARCH_TERM( GetKeywords(), 1 ) );
|
||||
terms.emplace_back( SEARCH_TERM( GetDesc(), 1 ) );
|
||||
|
||||
return terms;
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_INFO::InLibrary( const wxString& aLibrary ) const
|
||||
{
|
||||
return aLibrary == m_nickname;
|
||||
}
|
||||
|
||||
|
||||
bool operator<( const DESIGN_BLOCK_INFO& lhs, const DESIGN_BLOCK_INFO& rhs )
|
||||
{
|
||||
int retv = StrNumCmp( lhs.m_nickname, rhs.m_nickname, false );
|
||||
|
||||
if( retv != 0 )
|
||||
return retv < 0;
|
||||
|
||||
// Technically design block names are not case sensitive because the file name is used
|
||||
// as the design block name. On windows this would be problematic because windows does
|
||||
// not support case sensitive file names by default. This should not cause any issues
|
||||
// and allow for a future change to use the name defined in the design block file.
|
||||
return StrNumCmp( lhs.m_dbname, rhs.m_dbname, false ) < 0;
|
||||
}
|
219
common/design_block_info.h
Normal file
219
common/design_block_info.h
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2024 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
|
||||
*/
|
||||
|
||||
/*
|
||||
* @file design_block_info.h
|
||||
*/
|
||||
|
||||
#ifndef DESIGN_BLOCK_INFO_H_
|
||||
#define DESIGN_BLOCK_INFO_H_
|
||||
|
||||
|
||||
#include <kicommon.h>
|
||||
#include <boost/ptr_container/ptr_vector.hpp>
|
||||
#include <import_export.h>
|
||||
#include <ki_exception.h>
|
||||
#include <core/sync_queue.h>
|
||||
#include <lib_tree_item.h>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
|
||||
class DESIGN_BLOCK_LIB_TABLE;
|
||||
class DESIGN_BLOCK_LIST;
|
||||
class DESIGN_BLOCK_LIST_IMPL;
|
||||
class PROGRESS_REPORTER;
|
||||
class wxTopLevelWindow;
|
||||
class KIWAY;
|
||||
class wxTextFile;
|
||||
|
||||
|
||||
/*
|
||||
* Helper class to handle the list of design blocks available in libraries. It stores
|
||||
* design block names, doc and keywords.
|
||||
*
|
||||
* This is a virtual class; its implementation lives in common/design_block_info_impl.cpp.
|
||||
* To get instances of these classes, see DESIGN_BLOCK_LIST::GetInstance().
|
||||
*/
|
||||
class KICOMMON_API DESIGN_BLOCK_INFO : public LIB_TREE_ITEM
|
||||
{
|
||||
public:
|
||||
virtual ~DESIGN_BLOCK_INFO() {}
|
||||
|
||||
// These two accessors do not have to call ensure_loaded(), because constructor
|
||||
// fills in these fields:
|
||||
|
||||
const wxString& GetDesignBlockName() const { return m_dbname; }
|
||||
|
||||
wxString GetLibNickname() const override { return m_nickname; }
|
||||
|
||||
wxString GetName() const override { return m_dbname; }
|
||||
|
||||
LIB_ID GetLIB_ID() const override { return LIB_ID( m_nickname, m_dbname ); }
|
||||
|
||||
wxString GetDesc() override
|
||||
{
|
||||
ensure_loaded();
|
||||
return m_doc;
|
||||
}
|
||||
|
||||
wxString GetKeywords()
|
||||
{
|
||||
ensure_loaded();
|
||||
return m_keywords;
|
||||
}
|
||||
|
||||
std::vector<SEARCH_TERM> GetSearchTerms() override;
|
||||
|
||||
int GetOrderNum()
|
||||
{
|
||||
ensure_loaded();
|
||||
return m_num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if the #DESIGN_BLOCK_INFO object was loaded from \a aLibrary.
|
||||
*
|
||||
* @param aLibrary is the nickname of the library to test.
|
||||
*
|
||||
* @return true if the #DESIGN_BLOCK_INFO object was loaded from \a aLibrary. Otherwise
|
||||
* false.
|
||||
*/
|
||||
bool InLibrary( const wxString& aLibrary ) const;
|
||||
|
||||
/**
|
||||
* Less than comparison operator, intended for sorting DESIGN_BLOCK_INFO objects
|
||||
*/
|
||||
friend bool operator<( const DESIGN_BLOCK_INFO& lhs, const DESIGN_BLOCK_INFO& rhs );
|
||||
|
||||
protected:
|
||||
void ensure_loaded()
|
||||
{
|
||||
if( !m_loaded )
|
||||
load();
|
||||
}
|
||||
|
||||
/// lazily load stuff not filled in by constructor. This may throw IO_ERRORS.
|
||||
virtual void load(){};
|
||||
|
||||
DESIGN_BLOCK_LIST* m_owner; ///< provides access to DESIGN_BLOCK_LIB_TABLE
|
||||
|
||||
bool m_loaded;
|
||||
|
||||
wxString m_nickname; ///< library as known in DESIGN_BLOCK_LIB_TABLE
|
||||
wxString m_dbname; ///< Module name.
|
||||
int m_num; ///< Order number in the display list.
|
||||
wxString m_doc; ///< Design block description.
|
||||
wxString m_keywords; ///< Design block keywords.
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Holds a list of #DESIGN_BLOCK_INFO objects, along with a list of IO_ERRORs or
|
||||
* PARSE_ERRORs that were thrown acquiring the DESIGN_BLOCK_INFOs.
|
||||
*
|
||||
* This is a virtual class; its implementation lives in common/design_block_info_impl.cpp.
|
||||
* To get instances of these classes, see DESIGN_BLOCK_LIST::GetInstance().
|
||||
*/
|
||||
class KICOMMON_API DESIGN_BLOCK_LIST
|
||||
{
|
||||
public:
|
||||
typedef std::vector<std::unique_ptr<DESIGN_BLOCK_INFO>> DBILIST;
|
||||
typedef SYNC_QUEUE<std::unique_ptr<IO_ERROR>> ERRLIST;
|
||||
|
||||
DESIGN_BLOCK_LIST() : m_lib_table( nullptr ) {}
|
||||
|
||||
virtual ~DESIGN_BLOCK_LIST() {}
|
||||
|
||||
virtual void WriteCacheToFile( const wxString& aFilePath ){};
|
||||
virtual void ReadCacheFromFile( const wxString& aFilePath ){};
|
||||
|
||||
/**
|
||||
* @return the number of items stored in list
|
||||
*/
|
||||
unsigned GetCount() const { return m_list.size(); }
|
||||
|
||||
/// Was forced to add this by modview_frame.cpp
|
||||
const DBILIST& GetList() const { return m_list; }
|
||||
|
||||
/**
|
||||
* @return Clears the design block info cache
|
||||
*/
|
||||
void Clear() { m_list.clear(); }
|
||||
|
||||
/**
|
||||
* Get info for a design block by id.
|
||||
*/
|
||||
DESIGN_BLOCK_INFO* GetDesignBlockInfo( const wxString& aDesignBlockName );
|
||||
|
||||
/**
|
||||
* Get info for a design block by libNickname/designBlockName
|
||||
*/
|
||||
DESIGN_BLOCK_INFO* GetDesignBlockInfo( const wxString& aLibNickname,
|
||||
const wxString& aDesignBlockName );
|
||||
|
||||
/**
|
||||
* Get info for a design block by index.
|
||||
*
|
||||
* @param aIdx index of the given item.
|
||||
* @return the aIdx item in list.
|
||||
*/
|
||||
DESIGN_BLOCK_INFO& GetItem( unsigned aIdx ) const { return *m_list[aIdx]; }
|
||||
|
||||
unsigned GetErrorCount() const { return m_errors.size(); }
|
||||
|
||||
std::unique_ptr<IO_ERROR> PopError()
|
||||
{
|
||||
std::unique_ptr<IO_ERROR> error;
|
||||
|
||||
m_errors.pop( error );
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read all the design blocks 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 design blocks
|
||||
* from all known libraries in aTable.
|
||||
* @param aProgressReporter is an optional progress reporter. ReadDesignBlockFiles() will
|
||||
* use 2 phases within the reporter.
|
||||
* @return true if it ran to completion, else false if it aborted after some number of
|
||||
* errors. If true, it does not mean there were no errors, check GetErrorCount()
|
||||
* for that, should be zero to indicate success.
|
||||
*/
|
||||
virtual bool ReadDesignBlockFiles( DESIGN_BLOCK_LIB_TABLE* aTable,
|
||||
const wxString* aNickname = nullptr,
|
||||
PROGRESS_REPORTER* aProgressReporter = nullptr ) = 0;
|
||||
|
||||
DESIGN_BLOCK_LIB_TABLE* GetTable() const { return m_lib_table; }
|
||||
|
||||
protected:
|
||||
DESIGN_BLOCK_LIB_TABLE* m_lib_table = nullptr; ///< no ownership
|
||||
|
||||
DBILIST m_list;
|
||||
ERRLIST m_errors; ///< some can be PARSE_ERRORs also
|
||||
};
|
||||
|
||||
#endif // DESIGN_BLOCK_INFO_H_
|
376
common/design_block_info_impl.cpp
Normal file
376
common/design_block_info_impl.cpp
Normal file
@ -0,0 +1,376 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2024 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 <design_block_info_impl.h>
|
||||
|
||||
#include <design_block.h>
|
||||
#include <design_block_info.h>
|
||||
#include <design_block_lib_table.h>
|
||||
#include <kiway.h>
|
||||
#include <locale_io.h>
|
||||
#include <lib_id.h>
|
||||
#include <progress_reporter.h>
|
||||
#include <string_utils.h>
|
||||
#include <core/thread_pool.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
|
||||
#include <kiplatform/io.h>
|
||||
|
||||
#include <wx/textfile.h>
|
||||
#include <wx/txtstrm.h>
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
|
||||
void DESIGN_BLOCK_INFO_IMPL::load()
|
||||
{
|
||||
DESIGN_BLOCK_LIB_TABLE* dbtable = m_owner->GetTable();
|
||||
|
||||
wxASSERT( dbtable );
|
||||
|
||||
const DESIGN_BLOCK* design_block = dbtable->GetEnumeratedDesignBlock( m_nickname, m_dbname );
|
||||
|
||||
if( design_block )
|
||||
{
|
||||
m_keywords = design_block->GetKeywords();
|
||||
m_doc = design_block->GetLibDescription();
|
||||
}
|
||||
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIST_IMPL::CatchErrors( const 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;
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIST_IMPL::ReadDesignBlockFiles( DESIGN_BLOCK_LIB_TABLE* aTable,
|
||||
const wxString* aNickname,
|
||||
PROGRESS_REPORTER* aProgressReporter )
|
||||
{
|
||||
long long int generatedTimestamp = 0;
|
||||
|
||||
if( !CatchErrors(
|
||||
[&]()
|
||||
{
|
||||
generatedTimestamp = aTable->GenerateTimestamp( aNickname );
|
||||
} ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( generatedTimestamp == m_list_timestamp )
|
||||
return true;
|
||||
|
||||
// Disable KIID generation: not needed for library parts; sometimes very slow
|
||||
KIID_NIL_SET_RESET reset_kiid;
|
||||
|
||||
m_progress_reporter = aProgressReporter;
|
||||
|
||||
if( m_progress_reporter )
|
||||
{
|
||||
m_progress_reporter->SetMaxProgress( m_queue_in.size() );
|
||||
m_progress_reporter->Report( _( "Fetching design_block libraries..." ) );
|
||||
}
|
||||
|
||||
m_cancelled = false;
|
||||
m_lib_table = aTable;
|
||||
|
||||
// Clear data before reading files
|
||||
m_errors.clear();
|
||||
m_list.clear();
|
||||
m_queue_in.clear();
|
||||
m_queue_out.clear();
|
||||
|
||||
if( aNickname )
|
||||
{
|
||||
m_queue_in.push( *aNickname );
|
||||
}
|
||||
else
|
||||
{
|
||||
for( const wxString& nickname : aTable->GetLogicalLibs() )
|
||||
m_queue_in.push( nickname );
|
||||
}
|
||||
|
||||
|
||||
loadLibs();
|
||||
|
||||
if( !m_cancelled )
|
||||
{
|
||||
if( m_progress_reporter )
|
||||
{
|
||||
m_progress_reporter->SetMaxProgress( m_queue_out.size() );
|
||||
m_progress_reporter->AdvancePhase();
|
||||
m_progress_reporter->Report( _( "Loading design_blocks..." ) );
|
||||
}
|
||||
|
||||
loadDesignBlocks();
|
||||
|
||||
if( m_progress_reporter )
|
||||
m_progress_reporter->AdvancePhase();
|
||||
}
|
||||
|
||||
if( m_cancelled )
|
||||
m_list_timestamp = 0; // God knows what we got before we were canceled
|
||||
else
|
||||
m_list_timestamp = generatedTimestamp;
|
||||
|
||||
return m_errors.empty();
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIST_IMPL::loadLibs()
|
||||
{
|
||||
thread_pool& tp = GetKiCadThreadPool();
|
||||
size_t num_returns = m_queue_in.size();
|
||||
std::vector<std::future<size_t>> returns( num_returns );
|
||||
|
||||
auto loader_job =
|
||||
[this]() -> size_t
|
||||
{
|
||||
wxString nickname;
|
||||
size_t retval = 0;
|
||||
|
||||
if( !m_cancelled && m_queue_in.pop( nickname ) )
|
||||
{
|
||||
if( CatchErrors( [this, &nickname]()
|
||||
{
|
||||
m_lib_table->PrefetchLib( nickname );
|
||||
m_queue_out.push( nickname );
|
||||
} ) && m_progress_reporter )
|
||||
{
|
||||
m_progress_reporter->AdvanceProgress();
|
||||
}
|
||||
|
||||
++retval;
|
||||
}
|
||||
|
||||
return retval;
|
||||
};
|
||||
|
||||
for( size_t ii = 0; ii < num_returns; ++ii )
|
||||
returns[ii] = tp.submit( loader_job );
|
||||
|
||||
for( const std::future<size_t>& ret : returns )
|
||||
{
|
||||
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
|
||||
|
||||
while( status != std::future_status::ready )
|
||||
{
|
||||
if( m_progress_reporter && !m_progress_reporter->KeepRefreshing() )
|
||||
m_cancelled = true;
|
||||
|
||||
status = ret.wait_for( std::chrono::milliseconds( 250 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIST_IMPL::loadDesignBlocks()
|
||||
{
|
||||
LOCALE_IO toggle_locale;
|
||||
|
||||
// Parse the design_blocks in parallel. WARNING! This requires changing the locale, which is
|
||||
// GLOBAL. It is only thread safe 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<DESIGN_BLOCK_INFO>> queue_parsed;
|
||||
thread_pool& tp = GetKiCadThreadPool();
|
||||
size_t num_elements = m_queue_out.size();
|
||||
std::vector<std::future<size_t>> returns( num_elements );
|
||||
|
||||
auto db_thread =
|
||||
[ this, &queue_parsed ]() -> size_t
|
||||
{
|
||||
wxString nickname;
|
||||
|
||||
if( m_cancelled || !m_queue_out.pop( nickname ) )
|
||||
return 0;
|
||||
|
||||
wxArrayString dbnames;
|
||||
|
||||
CatchErrors(
|
||||
[&]()
|
||||
{
|
||||
m_lib_table->DesignBlockEnumerate( dbnames, nickname, false );
|
||||
} );
|
||||
|
||||
for( wxString dbname : dbnames )
|
||||
{
|
||||
CatchErrors(
|
||||
[&]()
|
||||
{
|
||||
auto* dbinfo = new DESIGN_BLOCK_INFO_IMPL( this, nickname, dbname );
|
||||
queue_parsed.move_push( std::unique_ptr<DESIGN_BLOCK_INFO>( dbinfo ) );
|
||||
} );
|
||||
|
||||
if( m_cancelled )
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( m_progress_reporter )
|
||||
m_progress_reporter->AdvanceProgress();
|
||||
|
||||
return 1;
|
||||
};
|
||||
|
||||
for( size_t ii = 0; ii < num_elements; ++ii )
|
||||
returns[ii] = tp.submit( db_thread );
|
||||
|
||||
for( const std::future<size_t>& ret : returns )
|
||||
{
|
||||
std::future_status status = ret.wait_for( std::chrono::milliseconds( 250 ) );
|
||||
|
||||
while( status != std::future_status::ready )
|
||||
{
|
||||
if( m_progress_reporter )
|
||||
m_progress_reporter->KeepRefreshing();
|
||||
|
||||
status = ret.wait_for( std::chrono::milliseconds( 250 ) );
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<DESIGN_BLOCK_INFO> dbi;
|
||||
|
||||
while( queue_parsed.pop( dbi ) )
|
||||
m_list.push_back( std::move( dbi ) );
|
||||
|
||||
std::sort( m_list.begin(), m_list.end(),
|
||||
[]( std::unique_ptr<DESIGN_BLOCK_INFO> const& lhs,
|
||||
std::unique_ptr<DESIGN_BLOCK_INFO> const& rhs ) -> bool
|
||||
{
|
||||
return *lhs < *rhs;
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_LIST_IMPL::DESIGN_BLOCK_LIST_IMPL() :
|
||||
m_list_timestamp( 0 ), m_progress_reporter( nullptr ), m_cancelled( false )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIST_IMPL::WriteCacheToFile( const wxString& aFilePath )
|
||||
{
|
||||
wxFileName tmpFileName = wxFileName::CreateTempFileName( aFilePath );
|
||||
wxFFileOutputStream outStream( tmpFileName.GetFullPath() );
|
||||
wxTextOutputStream txtStream( outStream );
|
||||
|
||||
if( !outStream.IsOk() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
txtStream << wxString::Format( wxT( "%lld" ), m_list_timestamp ) << endl;
|
||||
|
||||
for( std::unique_ptr<DESIGN_BLOCK_INFO>& dbinfo : m_list )
|
||||
{
|
||||
txtStream << dbinfo->GetLibNickname() << endl;
|
||||
txtStream << dbinfo->GetName() << endl;
|
||||
txtStream << EscapeString( dbinfo->GetDesc(), CTX_LINE ) << endl;
|
||||
txtStream << EscapeString( dbinfo->GetKeywords(), CTX_LINE ) << endl;
|
||||
txtStream << wxString::Format( wxT( "%d" ), dbinfo->GetOrderNum() ) << endl;
|
||||
}
|
||||
|
||||
txtStream.Flush();
|
||||
outStream.Close();
|
||||
|
||||
// Preserve the permissions of the current file
|
||||
KIPLATFORM::IO::DuplicatePermissions( aFilePath, tmpFileName.GetFullPath() );
|
||||
|
||||
if( !wxRenameFile( tmpFileName.GetFullPath(), aFilePath, true ) )
|
||||
{
|
||||
// cleanup in case rename failed
|
||||
// its also not the end of the world since this is just a cache file
|
||||
wxRemoveFile( tmpFileName.GetFullPath() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIST_IMPL::ReadCacheFromFile( const wxString& aFilePath )
|
||||
{
|
||||
wxTextFile cacheFile( aFilePath );
|
||||
|
||||
m_list_timestamp = 0;
|
||||
m_list.clear();
|
||||
|
||||
try
|
||||
{
|
||||
if( cacheFile.Exists() && cacheFile.Open() )
|
||||
{
|
||||
cacheFile.GetFirstLine().ToLongLong( &m_list_timestamp );
|
||||
|
||||
while( cacheFile.GetCurrentLine() + 6 < cacheFile.GetLineCount() )
|
||||
{
|
||||
wxString libNickname = cacheFile.GetNextLine();
|
||||
wxString name = cacheFile.GetNextLine();
|
||||
wxString desc = UnescapeString( cacheFile.GetNextLine() );
|
||||
wxString keywords = UnescapeString( cacheFile.GetNextLine() );
|
||||
int orderNum = wxAtoi( cacheFile.GetNextLine() );
|
||||
|
||||
DESIGN_BLOCK_INFO_IMPL* dbinfo =
|
||||
new DESIGN_BLOCK_INFO_IMPL( libNickname, name, desc, keywords, orderNum );
|
||||
|
||||
m_list.emplace_back( std::unique_ptr<DESIGN_BLOCK_INFO>( dbinfo ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
// whatever went wrong, invalidate the cache
|
||||
m_list_timestamp = 0;
|
||||
}
|
||||
|
||||
// Sanity check: an empty list is very unlikely to be correct.
|
||||
if( m_list.size() == 0 )
|
||||
m_list_timestamp = 0;
|
||||
|
||||
if( cacheFile.IsOpened() )
|
||||
cacheFile.Close();
|
||||
}
|
112
common/design_block_info_impl.h
Normal file
112
common/design_block_info_impl.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2021 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 DESIGN_BLOCK_INFO_IMPL_H
|
||||
#define DESIGN_BLOCK_INFO_IMPL_H
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <kicommon.h>
|
||||
#include <design_block_info.h>
|
||||
#include <core/sync_queue.h>
|
||||
|
||||
class LOCALE_IO;
|
||||
|
||||
class KICOMMON_API DESIGN_BLOCK_INFO_IMPL : public DESIGN_BLOCK_INFO
|
||||
{
|
||||
public:
|
||||
DESIGN_BLOCK_INFO_IMPL( DESIGN_BLOCK_LIST* aOwner, const wxString& aNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
{
|
||||
m_nickname = aNickname;
|
||||
m_dbname = aDesignBlockName;
|
||||
m_num = 0;
|
||||
|
||||
m_owner = aOwner;
|
||||
m_loaded = false;
|
||||
load();
|
||||
}
|
||||
|
||||
// A constructor for cached items
|
||||
DESIGN_BLOCK_INFO_IMPL( const wxString& aNickname, const wxString& aDesignBlockName,
|
||||
const wxString& aDescription, const wxString& aKeywords, int aOrderNum )
|
||||
{
|
||||
m_nickname = aNickname;
|
||||
m_dbname = aDesignBlockName;
|
||||
m_num = aOrderNum;
|
||||
m_doc = aDescription;
|
||||
m_keywords = aKeywords;
|
||||
|
||||
m_owner = nullptr;
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
|
||||
// A dummy constructor for use as a target in a binary search
|
||||
DESIGN_BLOCK_INFO_IMPL( const wxString& aNickname, const wxString& aDesignBlockName )
|
||||
{
|
||||
m_nickname = aNickname;
|
||||
m_dbname = aDesignBlockName;
|
||||
|
||||
m_owner = nullptr;
|
||||
m_loaded = true;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void load() override;
|
||||
};
|
||||
|
||||
|
||||
class KICOMMON_API DESIGN_BLOCK_LIST_IMPL : public DESIGN_BLOCK_LIST
|
||||
{
|
||||
public:
|
||||
DESIGN_BLOCK_LIST_IMPL();
|
||||
virtual ~DESIGN_BLOCK_LIST_IMPL(){};
|
||||
|
||||
void WriteCacheToFile( const wxString& aFilePath ) override;
|
||||
void ReadCacheFromFile( const wxString& aFilePath ) override;
|
||||
|
||||
bool ReadDesignBlockFiles( DESIGN_BLOCK_LIB_TABLE* aTable, const wxString* aNickname = nullptr,
|
||||
PROGRESS_REPORTER* aProgressReporter = nullptr ) override;
|
||||
|
||||
protected:
|
||||
void loadLibs();
|
||||
void loadDesignBlocks();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Call aFunc, pushing any IO_ERRORs and std::exceptions it throws onto m_errors.
|
||||
*
|
||||
* @return true if no error occurred.
|
||||
*/
|
||||
bool CatchErrors( const std::function<void()>& aFunc );
|
||||
|
||||
SYNC_QUEUE<wxString> m_queue_in;
|
||||
SYNC_QUEUE<wxString> m_queue_out;
|
||||
long long m_list_timestamp;
|
||||
PROGRESS_REPORTER* m_progress_reporter;
|
||||
std::atomic_bool m_cancelled;
|
||||
std::mutex m_join;
|
||||
};
|
||||
|
||||
#endif // DESIGN_BLOCK_INFO_IMPL_H
|
387
common/design_block_io.cpp
Normal file
387
common/design_block_io.cpp
Normal file
@ -0,0 +1,387 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 Mike Williams <mike@mikebwilliams.com>
|
||||
* Copyright (C) 1992-2024 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 <i18n_utility.h>
|
||||
#include <wx/dir.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/translation.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/arrstr.h>
|
||||
#include <wx/datetime.h>
|
||||
#include <wildcards_and_files_ext.h>
|
||||
#include <kiway_player.h>
|
||||
#include <design_block_io.h>
|
||||
#include <design_block.h>
|
||||
#include <ki_exception.h>
|
||||
#include <trace_helpers.h>
|
||||
#include <fstream>
|
||||
|
||||
const wxString DESIGN_BLOCK_IO_MGR::ShowType( DESIGN_BLOCK_FILE_T aFileType )
|
||||
{
|
||||
switch( aFileType )
|
||||
{
|
||||
case KICAD_SEXP: return _( "KiCad" );
|
||||
default: return wxString::Format( _( "UNKNOWN (%d)" ), aFileType );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T
|
||||
DESIGN_BLOCK_IO_MGR::EnumFromStr( const wxString& aFileType )
|
||||
{
|
||||
if( aFileType == _( "KiCad" ) )
|
||||
return DESIGN_BLOCK_FILE_T( KICAD_SEXP );
|
||||
|
||||
return DESIGN_BLOCK_FILE_T( DESIGN_BLOCK_FILE_UNKNOWN );
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_IO* DESIGN_BLOCK_IO_MGR::FindPlugin( DESIGN_BLOCK_FILE_T aFileType )
|
||||
{
|
||||
switch( aFileType )
|
||||
{
|
||||
case KICAD_SEXP: return new DESIGN_BLOCK_IO();
|
||||
default: return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T
|
||||
DESIGN_BLOCK_IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl )
|
||||
{
|
||||
if( IO_RELEASER<DESIGN_BLOCK_IO>( FindPlugin( KICAD_SEXP ) )->CanReadLibrary( aLibPath ) && aCtl != KICTL_NONKICAD_ONLY )
|
||||
return KICAD_SEXP;
|
||||
|
||||
return DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE;
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_IO_MGR::ConvertLibrary( std::map<std::string, UTF8>* aOldFileProps,
|
||||
const wxString& aOldFilePath,
|
||||
const wxString& aNewFilePath )
|
||||
{
|
||||
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T oldFileType =
|
||||
DESIGN_BLOCK_IO_MGR::GuessPluginTypeFromLibPath( aOldFilePath );
|
||||
|
||||
if( oldFileType == DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE )
|
||||
return false;
|
||||
|
||||
|
||||
IO_RELEASER<DESIGN_BLOCK_IO> oldFilePI( DESIGN_BLOCK_IO_MGR::FindPlugin( oldFileType ) );
|
||||
IO_RELEASER<DESIGN_BLOCK_IO> kicadPI(
|
||||
DESIGN_BLOCK_IO_MGR::FindPlugin( DESIGN_BLOCK_IO_MGR::KICAD_SEXP ) );
|
||||
wxArrayString dbNames;
|
||||
wxFileName newFileName( aNewFilePath );
|
||||
|
||||
if( newFileName.HasExt() )
|
||||
{
|
||||
wxString extraDir = newFileName.GetFullName();
|
||||
newFileName.ClearExt();
|
||||
newFileName.SetName( "" );
|
||||
newFileName.AppendDir( extraDir );
|
||||
}
|
||||
|
||||
if( !newFileName.DirExists() && !wxFileName::Mkdir( aNewFilePath, wxS_DIR_DEFAULT ) )
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
bool bestEfforts = false; // throw on first error
|
||||
oldFilePI->DesignBlockEnumerate( dbNames, aOldFilePath, bestEfforts, aOldFileProps );
|
||||
|
||||
for( const wxString& dbName : dbNames )
|
||||
{
|
||||
std::unique_ptr<const DESIGN_BLOCK> db(
|
||||
oldFilePI->GetEnumeratedDesignBlock( aOldFilePath, dbName, aOldFileProps ) );
|
||||
kicadPI->DesignBlockSave( aNewFilePath, db.get() );
|
||||
}
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const DESIGN_BLOCK_IO::IO_FILE_DESC DESIGN_BLOCK_IO::GetLibraryDesc() const
|
||||
{
|
||||
return IO_BASE::IO_FILE_DESC( _HKI( "KiCad Design Block folders" ), {},
|
||||
{ FILEEXT::KiCadDesignBlockLibPathExtension }, false );
|
||||
}
|
||||
|
||||
|
||||
long long DESIGN_BLOCK_IO::GetLibraryTimestamp( const wxString& aLibraryPath ) const
|
||||
{
|
||||
wxFileName fn( aLibraryPath );
|
||||
|
||||
if( fn.IsFileReadable() && fn.GetModificationTime().IsValid() )
|
||||
return fn.GetModificationTime().GetValue().GetValue();
|
||||
else
|
||||
return wxDateTime( 0.0 ).GetValue().GetValue();
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_IO::CreateLibrary( const wxString& aLibraryPath,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
if( wxDir::Exists( aLibraryPath ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Cannot overwrite library path '%s'." ),
|
||||
aLibraryPath.GetData() ) );
|
||||
}
|
||||
|
||||
wxFileName dir;
|
||||
dir.SetPath( aLibraryPath );
|
||||
|
||||
if( !dir.Mkdir() )
|
||||
{
|
||||
THROW_IO_ERROR(
|
||||
wxString::Format( _( "Library path '%s' could not be created.\n\n"
|
||||
"Make sure you have write permissions and try again." ),
|
||||
dir.GetPath() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_IO::DeleteLibrary( const wxString& aLibraryPath,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
wxFileName fn;
|
||||
fn.SetPath( aLibraryPath );
|
||||
|
||||
// Return if there is no library path to delete.
|
||||
if( !fn.DirExists() )
|
||||
return false;
|
||||
|
||||
if( !fn.IsDirWritable() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Insufficient permissions to delete folder '%s'." ),
|
||||
aLibraryPath.GetData() ) );
|
||||
}
|
||||
|
||||
wxDir dir( aLibraryPath );
|
||||
|
||||
// Design block folders should only contain sub-folders for each design block
|
||||
if( dir.HasFiles() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Library folder '%s' has unexpected files." ),
|
||||
aLibraryPath.GetData() ) );
|
||||
}
|
||||
|
||||
// Must delete all sub-directories before deleting the library directory
|
||||
if( dir.HasSubDirs() )
|
||||
{
|
||||
wxArrayString dirs;
|
||||
|
||||
// Get all sub-directories in the library path
|
||||
dir.GetAllFiles( aLibraryPath, &dirs, wxEmptyString, wxDIR_DIRS );
|
||||
|
||||
for( size_t i = 0; i < dirs.GetCount(); i++ )
|
||||
{
|
||||
wxFileName tmp = dirs[i];
|
||||
|
||||
if( tmp.GetExt() != FILEEXT::KiCadDesignBlockLibPathExtension )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Unexpected folder '%s' found in library "
|
||||
"path '%s'." ),
|
||||
dirs[i].GetData(), aLibraryPath.GetData() ) );
|
||||
}
|
||||
}
|
||||
|
||||
for( size_t i = 0; i < dirs.GetCount(); i++ )
|
||||
wxRemoveFile( dirs[i] );
|
||||
}
|
||||
|
||||
wxLogTrace( traceDesignBlocks, wxT( "Removing design block library '%s'." ),
|
||||
aLibraryPath.GetData() );
|
||||
|
||||
// Some of the more elaborate wxRemoveFile() crap puts up its own wxLog dialog
|
||||
// we don't want that. we want bare metal portability with no UI here.
|
||||
if( !wxFileName::Rmdir( aLibraryPath, wxPATH_RMDIR_RECURSIVE ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Design block library '%s' cannot be deleted." ),
|
||||
aLibraryPath.GetData() ) );
|
||||
}
|
||||
|
||||
// For some reason removing a directory in Windows is not immediately updated. This delay
|
||||
// prevents an error when attempting to immediately recreate the same directory when over
|
||||
// writing an existing library.
|
||||
#ifdef __WINDOWS__
|
||||
wxMilliSleep( 250L );
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_IO::DesignBlockEnumerate( wxArrayString& aDesignBlockNames,
|
||||
const wxString& aLibraryPath, bool aBestEfforts,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
// From the starting directory, look for all directories ending in the .block extension
|
||||
wxDir dir( aLibraryPath );
|
||||
|
||||
if( !dir.IsOpened() )
|
||||
return;
|
||||
|
||||
wxString dirname;
|
||||
wxString fileSpec = wxT( "*." ) + wxString( FILEEXT::KiCadDesignBlockPathExtension );
|
||||
bool cont = dir.GetFirst( &dirname, fileSpec, wxDIR_DIRS );
|
||||
|
||||
while( cont )
|
||||
{
|
||||
aDesignBlockNames.Add( dirname.Before( wxT( '.' ) ) );
|
||||
cont = dir.GetNext( &dirname );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK* DESIGN_BLOCK_IO::DesignBlockLoad( const wxString& aLibraryPath,
|
||||
const wxString& aDesignBlockName, bool aKeepUUID,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
DESIGN_BLOCK* newDB = new DESIGN_BLOCK();
|
||||
wxString dbPath = aLibraryPath + wxFileName::GetPathSeparator() +
|
||||
aDesignBlockName + wxT( "." ) + FILEEXT::KiCadDesignBlockPathExtension + wxFileName::GetPathSeparator();
|
||||
wxString dbSchPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension;
|
||||
wxString dbMetadataPath = dbPath + aDesignBlockName + wxT( "." ) + FILEEXT::JsonFileExtension;
|
||||
|
||||
|
||||
|
||||
// Library name needs to be empty for when we fill it in with the correct library nickname
|
||||
// one layer above
|
||||
newDB->SetLibId( LIB_ID( wxEmptyString, aDesignBlockName ) );
|
||||
newDB->SetSchematicFile(
|
||||
// Library path
|
||||
aLibraryPath + wxFileName::GetPathSeparator() +
|
||||
// Design block name (project folder)
|
||||
aDesignBlockName + +wxT( "." ) + FILEEXT::KiCadDesignBlockPathExtension + wxT( "/" ) +
|
||||
// Schematic file
|
||||
aDesignBlockName + wxT( "." ) + FILEEXT::KiCadSchematicFileExtension );
|
||||
|
||||
// Parse the JSON file if it exists
|
||||
if( wxFileExists( dbMetadataPath ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
nlohmann::json dbMetadata;
|
||||
std::ifstream dbMetadataFile( dbMetadataPath.fn_str() );
|
||||
dbMetadataFile >> dbMetadata;
|
||||
|
||||
if( dbMetadata.contains( "description" ) )
|
||||
newDB->SetLibDescription( dbMetadata["description"] );
|
||||
|
||||
if( dbMetadata.contains( "keywords" ) )
|
||||
newDB->SetKeywords( dbMetadata["keywords"] );
|
||||
|
||||
if( dbMetadata.contains( "documentation_url" ) )
|
||||
newDB->SetDocumentationUrl( dbMetadata["documentation_url"] );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
_( "Design block metadata file '%s' could not be read." ), dbMetadataPath ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return newDB;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_IO::DesignBlockSave( const wxString& aLibraryPath,
|
||||
const DESIGN_BLOCK* aDesignBlock,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
// Make sure we have a valid LIB_ID or we can't save the design block
|
||||
if( !aDesignBlock->GetLibId().IsValid() )
|
||||
{
|
||||
THROW_IO_ERROR( _( "Design block does not have a valid library ID." ) );
|
||||
}
|
||||
|
||||
if( !wxFileExists( aDesignBlock->GetSchematicFile() ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Schematic source file '%s' does not exist." ),
|
||||
aDesignBlock->GetSchematicFile() ) );
|
||||
}
|
||||
|
||||
// Create the design block folder
|
||||
wxFileName dbFolder( aLibraryPath + wxFileName::GetPathSeparator()
|
||||
+ aDesignBlock->GetLibId().GetLibItemName() + wxT( "." )
|
||||
+ FILEEXT::KiCadDesignBlockPathExtension
|
||||
+ wxFileName::GetPathSeparator() );
|
||||
|
||||
if( !dbFolder.DirExists() )
|
||||
{
|
||||
if( !dbFolder.Mkdir() )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Design block folder '%s' could not be created." ),
|
||||
dbFolder.GetFullPath().GetData() ) );
|
||||
}
|
||||
}
|
||||
|
||||
// The new schematic file name is based on the design block name, not the source sheet name
|
||||
wxString dbSchematicFile = dbFolder.GetFullPath() + aDesignBlock->GetLibId().GetLibItemName()
|
||||
+ wxT( "." ) + FILEEXT::KiCadSchematicFileExtension;
|
||||
|
||||
// Copy the source sheet file to the design block folder, under the design block name
|
||||
if( !wxCopyFile( aDesignBlock->GetSchematicFile(), dbSchematicFile ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format(
|
||||
_( "Schematic file '%s' could not be saved as design block at '%s'." ),
|
||||
aDesignBlock->GetSchematicFile().GetData(), dbSchematicFile ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_IO::DesignBlockDelete( const wxString& aLibPath, const wxString& aDesignBlockName,
|
||||
const std::map<std::string, UTF8>* aProperties )
|
||||
{
|
||||
wxFileName dbDir = wxFileName( aLibPath + wxFileName::GetPathSeparator() + aDesignBlockName
|
||||
+ wxT( "." ) + FILEEXT::KiCadDesignBlockPathExtension );
|
||||
|
||||
|
||||
if( !dbDir.DirExists() )
|
||||
{
|
||||
THROW_IO_ERROR(
|
||||
wxString::Format( _( "Design block '%s' does not exist." ), dbDir.GetFullName() ) );
|
||||
}
|
||||
|
||||
// Delete the whole design block folder
|
||||
if( !wxFileName::Rmdir( dbDir.GetFullPath(), wxPATH_RMDIR_RECURSIVE ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Design block folder '%s' could not be deleted." ),
|
||||
dbDir.GetFullPath().GetData() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_IO::IsLibraryWritable( const wxString& aLibraryPath )
|
||||
{
|
||||
wxFileName path( aLibraryPath );
|
||||
return path.IsOk() && path.IsDirWritable();
|
||||
}
|
115
common/design_block_io.h
Normal file
115
common/design_block_io.h
Normal file
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2024 Mike Williams <mike@mikebwilliams.com>
|
||||
* Copyright (C) 1992-2024 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 DESIGN_BLOCK_IO_H
|
||||
#define DESIGN_BLOCK_IO_H
|
||||
|
||||
#include <kicommon.h>
|
||||
#include <io/io_base.h>
|
||||
#include <io/io_mgr.h>
|
||||
|
||||
class DESIGN_BLOCK;
|
||||
class DESIGN_BLOCK_IO;
|
||||
class wxArrayString;
|
||||
|
||||
class KICOMMON_API DESIGN_BLOCK_IO_MGR : public IO_MGR
|
||||
{
|
||||
public:
|
||||
enum DESIGN_BLOCK_FILE_T
|
||||
{
|
||||
DESIGN_BLOCK_FILE_UNKNOWN = 0, ///< 0 is not a legal menu id on Mac
|
||||
KICAD_SEXP, ///< S-expression KiCad file format.
|
||||
|
||||
FILE_TYPE_NONE
|
||||
};
|
||||
|
||||
static const wxString ShowType( DESIGN_BLOCK_FILE_T aFileType );
|
||||
static DESIGN_BLOCK_IO* FindPlugin( DESIGN_BLOCK_FILE_T aFileType );
|
||||
static DESIGN_BLOCK_FILE_T EnumFromStr( const wxString& aFileType );
|
||||
static DESIGN_BLOCK_FILE_T GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl = 0 );
|
||||
/**
|
||||
* Convert a design block library to the latest KiCad format
|
||||
*/
|
||||
static bool ConvertLibrary( std::map<std::string, UTF8>* aOldFileProps,
|
||||
const wxString& aOldFilePath, const wxString& aNewFilePath );
|
||||
};
|
||||
|
||||
|
||||
class KICOMMON_API DESIGN_BLOCK_IO : public IO_BASE
|
||||
{
|
||||
public:
|
||||
DESIGN_BLOCK_IO() : IO_BASE( wxS( "KiCad" ) ) {}
|
||||
|
||||
const IO_BASE::IO_FILE_DESC GetLibraryDesc() const override;
|
||||
long long GetLibraryTimestamp( const wxString& aLibraryPath ) const;
|
||||
|
||||
void DesignBlockEnumerate( wxArrayString& aDesignBlockNames, const wxString& aLibraryPath,
|
||||
bool aBestEfforts,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr );
|
||||
|
||||
const DESIGN_BLOCK*
|
||||
GetEnumeratedDesignBlock( const wxString& aLibraryPath, const wxString& aDesignBlockName,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr )
|
||||
{
|
||||
return DesignBlockLoad( aLibraryPath, aDesignBlockName, false, aProperties );
|
||||
}
|
||||
|
||||
bool DesignBlockExists( const wxString& aLibraryPath, const wxString& aDesignBlockName,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr )
|
||||
{
|
||||
return DesignBlockLoad( aLibraryPath, aDesignBlockName, true, aProperties ) != nullptr;
|
||||
}
|
||||
|
||||
DESIGN_BLOCK* ImportDesignBlock( const wxString& aDesignBlockPath,
|
||||
wxString& aDesignBlockNameOut,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CreateLibrary( const wxString& aLibraryPath,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr ) override;
|
||||
|
||||
virtual bool DeleteLibrary( const wxString& aLibraryPath,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr ) override;
|
||||
|
||||
|
||||
bool IsLibraryWritable( const wxString& aLibraryPath ) override;
|
||||
|
||||
DESIGN_BLOCK* DesignBlockLoad( const wxString& aLibraryPath, const wxString& aDesignBlockName,
|
||||
bool aKeepUUID = false,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr );
|
||||
|
||||
void DesignBlockSave( const wxString& aLibraryPath, const DESIGN_BLOCK* aDesignBlock,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr );
|
||||
|
||||
void DesignBlockDelete( const wxString& aLibraryPath, const wxString& aDesignBlockName,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr );
|
||||
|
||||
virtual void PrefetchLib( const wxString& aLibraryPath,
|
||||
const std::map<std::string, UTF8>* aProperties = nullptr )
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
683
common/design_block_lib_table.cpp
Normal file
683
common/design_block_lib_table.cpp
Normal file
@ -0,0 +1,683 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012-2024 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 <kiface_base.h>
|
||||
#include <env_vars.h>
|
||||
#include <design_block_info.h>
|
||||
#include <lib_id.h>
|
||||
#include <lib_table_lexer.h>
|
||||
#include <paths.h>
|
||||
#include <pgm_base.h>
|
||||
#include <search_stack.h>
|
||||
#include <settings/kicad_settings.h>
|
||||
#include <settings/settings_manager.h>
|
||||
#include <systemdirsappend.h>
|
||||
#include <design_block_lib_table.h>
|
||||
#include <design_block.h>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <wx/hash.h>
|
||||
|
||||
#define OPT_SEP '|' ///< options separator character
|
||||
|
||||
/// The global design block library table. This is not dynamically allocated because
|
||||
/// in a multiple project environment we must keep its address constant (since it is
|
||||
/// the fallback table for multiple projects).
|
||||
DESIGN_BLOCK_LIB_TABLE GDesignBlockTable;
|
||||
|
||||
/// The global footprint info table. This is performance-intensive to build so we
|
||||
/// keep a hash-stamped global version. Any deviation from the request vs. stored
|
||||
/// hash will result in it being rebuilt.
|
||||
DESIGN_BLOCK_LIST_IMPL GDesignBlockList;
|
||||
|
||||
|
||||
using namespace LIB_TABLE_T;
|
||||
|
||||
|
||||
static const wxChar global_tbl_name[] = wxT( "design-block-lib-table" );
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIB_TABLE_ROW::operator==( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) const
|
||||
{
|
||||
return LIB_TABLE_ROW::operator==( aRow ) && type == aRow.type;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE_ROW::SetType( const wxString& aType )
|
||||
{
|
||||
type = DESIGN_BLOCK_IO_MGR::EnumFromStr( aType );
|
||||
|
||||
if( DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T( -1 ) == type )
|
||||
type = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
|
||||
|
||||
plugin.reset();
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_LIB_TABLE::DESIGN_BLOCK_LIB_TABLE( DESIGN_BLOCK_LIB_TABLE* aFallBackTable ) :
|
||||
LIB_TABLE( aFallBackTable )
|
||||
{
|
||||
// not copying fall back, simply search aFallBackTable separately
|
||||
// if "nickName not found".
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::Parse( LIB_TABLE_LEXER* in )
|
||||
{
|
||||
T tok;
|
||||
wxString errMsg; // to collect error messages
|
||||
|
||||
// This table may be nested within a larger s-expression, or not.
|
||||
// Allow for parser of that optional containing s-epression to have looked ahead.
|
||||
if( in->CurTok() != T_design_block_lib_table )
|
||||
{
|
||||
in->NeedLEFT();
|
||||
|
||||
if( ( tok = in->NextTok() ) != T_design_block_lib_table )
|
||||
in->Expecting( T_design_block_lib_table );
|
||||
}
|
||||
|
||||
while( ( tok = in->NextTok() ) != T_RIGHT )
|
||||
{
|
||||
std::unique_ptr<DESIGN_BLOCK_LIB_TABLE_ROW> row =
|
||||
std::make_unique<DESIGN_BLOCK_LIB_TABLE_ROW>();
|
||||
|
||||
if( tok == T_EOF )
|
||||
in->Expecting( T_RIGHT );
|
||||
|
||||
if( tok != T_LEFT )
|
||||
in->Expecting( T_LEFT );
|
||||
|
||||
// in case there is a "row integrity" error, tell where later.
|
||||
int lineNum = in->CurLineNumber();
|
||||
tok = in->NextTok();
|
||||
|
||||
// Optionally parse the current version number
|
||||
if( tok == T_version )
|
||||
{
|
||||
in->NeedNUMBER( "version" );
|
||||
m_version = std::stoi( in->CurText() );
|
||||
in->NeedRIGHT();
|
||||
continue;
|
||||
}
|
||||
|
||||
if( tok != T_lib )
|
||||
in->Expecting( T_lib );
|
||||
|
||||
// (name NICKNAME)
|
||||
in->NeedLEFT();
|
||||
|
||||
if( ( tok = in->NextTok() ) != T_name )
|
||||
in->Expecting( T_name );
|
||||
|
||||
in->NeedSYMBOLorNUMBER();
|
||||
|
||||
row->SetNickName( in->FromUTF8() );
|
||||
|
||||
in->NeedRIGHT();
|
||||
|
||||
// After (name), remaining (lib) elements are order independent, and in
|
||||
// some cases optional.
|
||||
bool sawType = false;
|
||||
bool sawOpts = false;
|
||||
bool sawDesc = false;
|
||||
bool sawUri = false;
|
||||
bool sawDisabled = false;
|
||||
|
||||
while( ( tok = in->NextTok() ) != T_RIGHT )
|
||||
{
|
||||
if( tok == T_EOF )
|
||||
in->Unexpected( T_EOF );
|
||||
|
||||
if( tok != T_LEFT )
|
||||
in->Expecting( T_LEFT );
|
||||
|
||||
tok = in->NeedSYMBOLorNUMBER();
|
||||
|
||||
switch( tok )
|
||||
{
|
||||
case T_uri:
|
||||
if( sawUri )
|
||||
in->Duplicate( tok );
|
||||
sawUri = true;
|
||||
in->NeedSYMBOLorNUMBER();
|
||||
row->SetFullURI( in->FromUTF8() );
|
||||
break;
|
||||
|
||||
case T_type:
|
||||
if( sawType )
|
||||
in->Duplicate( tok );
|
||||
sawType = true;
|
||||
in->NeedSYMBOLorNUMBER();
|
||||
row->SetType( in->FromUTF8() );
|
||||
break;
|
||||
|
||||
case T_options:
|
||||
if( sawOpts )
|
||||
in->Duplicate( tok );
|
||||
sawOpts = true;
|
||||
in->NeedSYMBOLorNUMBER();
|
||||
row->SetOptions( in->FromUTF8() );
|
||||
break;
|
||||
|
||||
case T_descr:
|
||||
if( sawDesc )
|
||||
in->Duplicate( tok );
|
||||
sawDesc = true;
|
||||
in->NeedSYMBOLorNUMBER();
|
||||
row->SetDescr( in->FromUTF8() );
|
||||
break;
|
||||
|
||||
case T_disabled:
|
||||
if( sawDisabled )
|
||||
in->Duplicate( tok );
|
||||
sawDisabled = true;
|
||||
row->SetEnabled( false );
|
||||
break;
|
||||
|
||||
case T_hidden:
|
||||
// Hiding design block libraries is not yet supported. Unclear what path can set this
|
||||
// attribute, but clear it on load.
|
||||
row->SetVisible();
|
||||
break;
|
||||
|
||||
default: in->Unexpected( tok );
|
||||
}
|
||||
|
||||
in->NeedRIGHT();
|
||||
}
|
||||
|
||||
if( !sawType )
|
||||
in->Expecting( T_type );
|
||||
|
||||
if( !sawUri )
|
||||
in->Expecting( T_uri );
|
||||
|
||||
// All nickNames within this table fragment must be unique, so we do not use doReplace
|
||||
// in doInsertRow(). (However a fallBack table can have a conflicting nickName and ours
|
||||
// will supercede that one since in FindLib() we search this table before any fall back.)
|
||||
wxString nickname = row->GetNickName(); // store it to be able to used it
|
||||
// after row deletion if an error occurs
|
||||
bool doReplace = false;
|
||||
LIB_TABLE_ROW* tmp = row.release();
|
||||
|
||||
if( !doInsertRow( tmp, doReplace ) )
|
||||
{
|
||||
delete tmp; // The table did not take ownership of the row.
|
||||
|
||||
wxString msg = wxString::Format( _( "Duplicate library nickname '%s' found in "
|
||||
"design block library table file line %d." ),
|
||||
nickname, lineNum );
|
||||
|
||||
if( !errMsg.IsEmpty() )
|
||||
errMsg << '\n';
|
||||
|
||||
errMsg << msg;
|
||||
}
|
||||
}
|
||||
|
||||
if( !errMsg.IsEmpty() )
|
||||
THROW_IO_ERROR( errMsg );
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIB_TABLE::operator==( const DESIGN_BLOCK_LIB_TABLE& aDesignBlockTable ) const
|
||||
{
|
||||
if( m_rows.size() == aDesignBlockTable.m_rows.size() )
|
||||
{
|
||||
for( unsigned i = 0; i < m_rows.size(); ++i )
|
||||
{
|
||||
if( (DESIGN_BLOCK_LIB_TABLE_ROW&) m_rows[i]
|
||||
!= (DESIGN_BLOCK_LIB_TABLE_ROW&) aDesignBlockTable.m_rows[i] )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::Format( OUTPUTFORMATTER* aOutput, int aIndentLevel ) const
|
||||
{
|
||||
aOutput->Print( aIndentLevel, "(design_block_lib_table\n" );
|
||||
aOutput->Print( aIndentLevel + 1, "(version %d)\n", m_version );
|
||||
|
||||
for( LIB_TABLE_ROWS_CITER it = m_rows.begin(); it != m_rows.end(); ++it )
|
||||
it->Format( aOutput, aIndentLevel + 1 );
|
||||
|
||||
aOutput->Print( aIndentLevel, ")\n" );
|
||||
}
|
||||
|
||||
|
||||
long long DESIGN_BLOCK_LIB_TABLE::GenerateTimestamp( const wxString* aNickname )
|
||||
{
|
||||
long long hash = 0;
|
||||
|
||||
if( aNickname )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( *aNickname, true );
|
||||
|
||||
wxCHECK( row && row->plugin, hash );
|
||||
|
||||
return row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) )
|
||||
+ wxHashTable::MakeKey( *aNickname );
|
||||
}
|
||||
|
||||
for( const wxString& nickname : GetLogicalLibs() )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
row = FindRow( nickname, true );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
// Do nothing if not found: just skip.
|
||||
}
|
||||
|
||||
wxCHECK2( row && row->plugin, continue );
|
||||
|
||||
hash += row->plugin->GetLibraryTimestamp( row->GetFullURI( true ) )
|
||||
+ wxHashTable::MakeKey( nickname );
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::DesignBlockEnumerate( wxArrayString& aDesignBlockNames,
|
||||
const wxString& aNickname, bool aBestEfforts )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
row->plugin->DesignBlockEnumerate( aDesignBlockNames, row->GetFullURI( true ), aBestEfforts,
|
||||
row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::PrefetchLib( const wxString& aNickname )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
row->plugin->PrefetchLib( row->GetFullURI( true ), row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* DESIGN_BLOCK_LIB_TABLE::FindRow( const wxString& aNickname,
|
||||
bool aCheckIfEnabled )
|
||||
{
|
||||
DESIGN_BLOCK_LIB_TABLE_ROW* row =
|
||||
static_cast<DESIGN_BLOCK_LIB_TABLE_ROW*>( findRow( aNickname, aCheckIfEnabled ) );
|
||||
|
||||
if( !row )
|
||||
{
|
||||
wxString msg = wxString::Format(
|
||||
_( "design-block-lib-table files contain no library named '%s'." ), aNickname );
|
||||
THROW_IO_ERROR( msg );
|
||||
}
|
||||
|
||||
if( !row->plugin )
|
||||
row->setPlugin( DESIGN_BLOCK_IO_MGR::FindPlugin( row->type ) );
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
static void setLibNickname( DESIGN_BLOCK* aModule, const wxString& aNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
{
|
||||
// The library cannot know its own name, because it might have been renamed or moved.
|
||||
// Therefore design blocks cannot know their own library nickname when residing in
|
||||
// a design block library.
|
||||
// Only at this API layer can we tell the design block about its actual library nickname.
|
||||
if( aModule )
|
||||
{
|
||||
// remove "const"-ness, I really do want to set nickname without
|
||||
// having to copy the LIB_ID and its two strings, twice each.
|
||||
LIB_ID& dbid = (LIB_ID&) aModule->GetLibId();
|
||||
|
||||
// Catch any misbehaving plugin, which should be setting internal design block name properly:
|
||||
wxASSERT( aDesignBlockName == dbid.GetLibItemName().wx_str() );
|
||||
|
||||
// and clearing nickname
|
||||
wxASSERT( !dbid.GetLibNickname().size() );
|
||||
|
||||
dbid.SetLibNickname( aNickname );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const DESIGN_BLOCK*
|
||||
DESIGN_BLOCK_LIB_TABLE::GetEnumeratedDesignBlock( const wxString& aNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
|
||||
return row->plugin->GetEnumeratedDesignBlock( row->GetFullURI( true ), aDesignBlockName,
|
||||
row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIB_TABLE::DesignBlockExists( const wxString& aNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
{
|
||||
try
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
|
||||
return row->plugin->DesignBlockExists( row->GetFullURI( true ), aDesignBlockName,
|
||||
row->GetProperties() );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK* DESIGN_BLOCK_LIB_TABLE::DesignBlockLoad( const wxString& aNickname,
|
||||
const wxString& aDesignBlockName,
|
||||
bool aKeepUUID )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
|
||||
DESIGN_BLOCK* ret = row->plugin->DesignBlockLoad( row->GetFullURI( true ), aDesignBlockName,
|
||||
aKeepUUID, row->GetProperties() );
|
||||
|
||||
setLibNickname( ret, row->GetNickName(), aDesignBlockName );
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_LIB_TABLE::SAVE_T
|
||||
DESIGN_BLOCK_LIB_TABLE::DesignBlockSave( const wxString& aNickname,
|
||||
const DESIGN_BLOCK* aDesignBlock, bool aOverwrite )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
|
||||
if( !aOverwrite )
|
||||
{
|
||||
// Try loading the design block to see if it already exists, caller wants overwrite
|
||||
// protection, which is atypical, not the default.
|
||||
|
||||
wxString DesignBlockname = aDesignBlock->GetLibId().GetLibItemName();
|
||||
|
||||
std::unique_ptr<DESIGN_BLOCK> design_block( row->plugin->DesignBlockLoad(
|
||||
row->GetFullURI( true ), DesignBlockname, row->GetProperties() ) );
|
||||
|
||||
if( design_block.get() )
|
||||
return SAVE_SKIPPED;
|
||||
}
|
||||
|
||||
row->plugin->DesignBlockSave( row->GetFullURI( true ), aDesignBlock, row->GetProperties() );
|
||||
|
||||
return SAVE_OK;
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::DesignBlockDelete( const wxString& aNickname,
|
||||
const wxString& aDesignBlockName )
|
||||
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
return row->plugin->DesignBlockDelete( row->GetFullURI( true ), aDesignBlockName,
|
||||
row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIB_TABLE::IsDesignBlockLibWritable( const wxString& aNickname )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
return row->plugin->IsLibraryWritable( row->GetFullURI( true ) );
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::DesignBlockLibDelete( const wxString& aNickname )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
row->plugin->DeleteLibrary( row->GetFullURI( true ), row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_LIB_TABLE::DesignBlockLibCreate( const wxString& aNickname )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* row = FindRow( aNickname, true );
|
||||
wxASSERT( row->plugin );
|
||||
row->plugin->CreateLibrary( row->GetFullURI( true ), row->GetProperties() );
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK*
|
||||
DESIGN_BLOCK_LIB_TABLE::DesignBlockLoadWithOptionalNickname( const LIB_ID& aDesignBlockId,
|
||||
bool aKeepUUID )
|
||||
{
|
||||
wxString nickname = aDesignBlockId.GetLibNickname();
|
||||
wxString DesignBlockname = aDesignBlockId.GetLibItemName();
|
||||
|
||||
if( nickname.size() )
|
||||
{
|
||||
return DesignBlockLoad( nickname, DesignBlockname, aKeepUUID );
|
||||
}
|
||||
|
||||
// nickname is empty, sequentially search (alphabetically) all libs/nicks for first match:
|
||||
else
|
||||
{
|
||||
std::vector<wxString> nicks = GetLogicalLibs();
|
||||
|
||||
// Search each library going through libraries alphabetically.
|
||||
for( unsigned i = 0; i < nicks.size(); ++i )
|
||||
{
|
||||
// DesignBlockLoad() returns NULL on not found, does not throw exception
|
||||
// unless there's an IO_ERROR.
|
||||
DESIGN_BLOCK* ret = DesignBlockLoad( nicks[i], DesignBlockname, aKeepUUID );
|
||||
|
||||
if( ret )
|
||||
return ret;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const wxString DESIGN_BLOCK_LIB_TABLE::GlobalPathEnvVariableName()
|
||||
{
|
||||
return ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) );
|
||||
}
|
||||
|
||||
|
||||
class PCM_DESIGN_BLOCK_LIB_TRAVERSER final : public wxDirTraverser
|
||||
{
|
||||
public:
|
||||
explicit PCM_DESIGN_BLOCK_LIB_TRAVERSER( const wxString& aPath, DESIGN_BLOCK_LIB_TABLE& aTable,
|
||||
const wxString& aPrefix ) :
|
||||
m_lib_table( aTable ),
|
||||
m_path_prefix( aPath ), m_lib_prefix( aPrefix )
|
||||
{
|
||||
wxFileName f( aPath, wxS( "" ) );
|
||||
m_prefix_dir_count = f.GetDirCount();
|
||||
}
|
||||
|
||||
wxDirTraverseResult OnFile( const wxString& aFilePath ) override { return wxDIR_CONTINUE; }
|
||||
|
||||
wxDirTraverseResult OnDir( const wxString& dirPath ) override
|
||||
{
|
||||
wxFileName dir = wxFileName::DirName( dirPath );
|
||||
|
||||
// consider a directory to be a lib if it's name ends with .pretty and
|
||||
// it is under $KICADn_3RD_PARTY/design_blocks/<pkgid>/ i.e. has nested level of at least +3
|
||||
if( dirPath.EndsWith( wxS( ".pretty" ) ) && dir.GetDirCount() >= m_prefix_dir_count + 3 )
|
||||
{
|
||||
wxString versionedPath = wxString::Format(
|
||||
wxS( "${%s}" ), ENV_VAR::GetVersionedEnvVarName( wxS( "3RD_PARTY" ) ) );
|
||||
|
||||
wxArrayString parts = dir.GetDirs();
|
||||
parts.RemoveAt( 0, m_prefix_dir_count );
|
||||
parts.Insert( versionedPath, 0 );
|
||||
|
||||
wxString libPath = wxJoin( parts, '/' );
|
||||
|
||||
if( !m_lib_table.HasLibraryWithPath( libPath ) )
|
||||
{
|
||||
wxString name = parts.Last().substr( 0, parts.Last().length() - 7 );
|
||||
wxString nickname = wxString::Format( wxS( "%s%s" ), m_lib_prefix, name );
|
||||
|
||||
if( m_lib_table.HasLibrary( nickname ) )
|
||||
{
|
||||
int increment = 1;
|
||||
do
|
||||
{
|
||||
nickname =
|
||||
wxString::Format( wxS( "%s%s_%d" ), m_lib_prefix, name, increment );
|
||||
increment++;
|
||||
} while( m_lib_table.HasLibrary( nickname ) );
|
||||
}
|
||||
|
||||
m_lib_table.InsertRow( new DESIGN_BLOCK_LIB_TABLE_ROW(
|
||||
nickname, libPath, wxT( "KiCad" ), wxEmptyString,
|
||||
_( "Added by Plugin and Content Manager" ) ) );
|
||||
}
|
||||
}
|
||||
|
||||
return wxDIR_CONTINUE;
|
||||
}
|
||||
|
||||
private:
|
||||
DESIGN_BLOCK_LIB_TABLE& m_lib_table;
|
||||
wxString m_path_prefix;
|
||||
wxString m_lib_prefix;
|
||||
size_t m_prefix_dir_count;
|
||||
};
|
||||
|
||||
|
||||
bool DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable( DESIGN_BLOCK_LIB_TABLE& aTable )
|
||||
{
|
||||
bool tableExists = true;
|
||||
wxFileName fn = GetGlobalTableFileName();
|
||||
|
||||
if( !fn.FileExists() )
|
||||
{
|
||||
tableExists = false;
|
||||
|
||||
if( !fn.DirExists() && !fn.Mkdir( 0x777, wxPATH_MKDIR_FULL ) )
|
||||
{
|
||||
THROW_IO_ERROR( wxString::Format( _( "Cannot create global library table path '%s'." ),
|
||||
fn.GetPath() ) );
|
||||
}
|
||||
|
||||
// Attempt to copy the default global file table from the KiCad
|
||||
// template folder to the user's home configuration path.
|
||||
SEARCH_STACK ss;
|
||||
|
||||
SystemDirsAppend( &ss );
|
||||
|
||||
const ENV_VAR_MAP& envVars = Pgm().GetLocalEnvVariables();
|
||||
std::optional<wxString> v =
|
||||
ENV_VAR::GetVersionedEnvVarValue( envVars, wxT( "TEMPLATE_DIR" ) );
|
||||
|
||||
if( v && !v->IsEmpty() )
|
||||
ss.AddPaths( *v, 0 );
|
||||
|
||||
wxString fileName = ss.FindValidPath( global_tbl_name );
|
||||
|
||||
// The fallback is to create an empty global design block table for the user to populate.
|
||||
if( fileName.IsEmpty() || !::wxCopyFile( fileName, fn.GetFullPath(), false ) )
|
||||
{
|
||||
DESIGN_BLOCK_LIB_TABLE emptyTable;
|
||||
|
||||
emptyTable.Save( fn.GetFullPath() );
|
||||
}
|
||||
}
|
||||
|
||||
aTable.clear();
|
||||
aTable.Load( fn.GetFullPath() );
|
||||
|
||||
SETTINGS_MANAGER& mgr = Pgm().GetSettingsManager();
|
||||
KICAD_SETTINGS* settings = mgr.GetAppSettings<KICAD_SETTINGS>();
|
||||
|
||||
const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables();
|
||||
wxString packagesPath;
|
||||
|
||||
if( std::optional<wxString> v = ENV_VAR::GetVersionedEnvVarValue( env, wxT( "3RD_PARTY" ) ) )
|
||||
packagesPath = *v;
|
||||
|
||||
if( settings->m_PcmLibAutoAdd )
|
||||
{
|
||||
// Scan for libraries in PCM packages directory
|
||||
|
||||
wxFileName d( packagesPath, wxS( "" ) );
|
||||
d.AppendDir( wxS( "design_blocks" ) );
|
||||
|
||||
if( d.DirExists() )
|
||||
{
|
||||
PCM_DESIGN_BLOCK_LIB_TRAVERSER traverser( packagesPath, aTable,
|
||||
settings->m_PcmLibPrefix );
|
||||
wxDir dir( d.GetPath() );
|
||||
|
||||
dir.Traverse( traverser );
|
||||
}
|
||||
}
|
||||
|
||||
if( settings->m_PcmLibAutoRemove )
|
||||
{
|
||||
// Remove PCM libraries that no longer exist
|
||||
std::vector<wxString> to_remove;
|
||||
|
||||
for( size_t i = 0; i < aTable.GetCount(); i++ )
|
||||
{
|
||||
LIB_TABLE_ROW& row = aTable.At( i );
|
||||
wxString path = row.GetFullURI( true );
|
||||
|
||||
if( path.StartsWith( packagesPath ) && !wxDir::Exists( path ) )
|
||||
to_remove.push_back( row.GetNickName() );
|
||||
}
|
||||
|
||||
for( const wxString& nickName : to_remove )
|
||||
aTable.RemoveRow( aTable.FindRow( nickName ) );
|
||||
}
|
||||
|
||||
return tableExists;
|
||||
}
|
||||
|
||||
|
||||
wxString DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName()
|
||||
{
|
||||
wxFileName fn;
|
||||
|
||||
fn.SetPath( PATHS::GetUserSettingsPath() );
|
||||
fn.SetName( global_tbl_name );
|
||||
|
||||
return fn.GetFullPath();
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
design_block_lib_table
|
||||
fp_lib_table
|
||||
sym_lib_table
|
||||
version
|
||||
@ -8,4 +9,4 @@ uri
|
||||
options
|
||||
descr
|
||||
disabled
|
||||
hidden
|
||||
hidden
|
||||
|
@ -359,6 +359,23 @@ LIB_TREE_NODE_LIBRARY& LIB_TREE_NODE_ROOT::AddLib( wxString const& aName, wxStri
|
||||
}
|
||||
|
||||
|
||||
void LIB_TREE_NODE_ROOT::RemoveLib( wxString const& aName )
|
||||
{
|
||||
m_Children.erase( std::remove_if( m_Children.begin(), m_Children.end(),
|
||||
[&]( std::unique_ptr<LIB_TREE_NODE>& aNode )
|
||||
{
|
||||
return aNode->m_Name == aName;
|
||||
} ),
|
||||
m_Children.end() );
|
||||
}
|
||||
|
||||
|
||||
void LIB_TREE_NODE_ROOT::Clear()
|
||||
{
|
||||
m_Children.clear();
|
||||
}
|
||||
|
||||
|
||||
void LIB_TREE_NODE_ROOT::UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
|
||||
std::function<bool( LIB_TREE_NODE& aNode )>* aFilter )
|
||||
{
|
||||
|
@ -247,6 +247,12 @@ void LIB_TREE_MODEL_ADAPTER::DoAddLibrary( const wxString& aNodeName, const wxSt
|
||||
}
|
||||
|
||||
|
||||
void LIB_TREE_MODEL_ADAPTER::DoRemoveLibrary( const wxString& aNodeName )
|
||||
{
|
||||
m_tree.RemoveLib( aNodeName );
|
||||
}
|
||||
|
||||
|
||||
void LIB_TREE_MODEL_ADAPTER::UpdateSearchString( const wxString& aSearch, bool aState )
|
||||
{
|
||||
{
|
||||
@ -383,7 +389,7 @@ void LIB_TREE_MODEL_ADAPTER::resortTree()
|
||||
|
||||
void LIB_TREE_MODEL_ADAPTER::PinLibrary( LIB_TREE_NODE* aTreeNode )
|
||||
{
|
||||
m_parent->Prj().PinLibrary( aTreeNode->m_LibId.GetLibNickname(), isSymbolModel() );
|
||||
m_parent->Prj().PinLibrary( aTreeNode->m_LibId.GetLibNickname(), getLibType() );
|
||||
aTreeNode->m_Pinned = true;
|
||||
|
||||
resortTree();
|
||||
@ -393,7 +399,7 @@ void LIB_TREE_MODEL_ADAPTER::PinLibrary( LIB_TREE_NODE* aTreeNode )
|
||||
|
||||
void LIB_TREE_MODEL_ADAPTER::UnpinLibrary( LIB_TREE_NODE* aTreeNode )
|
||||
{
|
||||
m_parent->Prj().UnpinLibrary( aTreeNode->m_LibId.GetLibNickname(), isSymbolModel() );
|
||||
m_parent->Prj().UnpinLibrary( aTreeNode->m_LibId.GetLibNickname(), getLibType() );
|
||||
aTreeNode->m_Pinned = false;
|
||||
|
||||
resortTree();
|
||||
|
@ -106,6 +106,17 @@ wxString PATHS::GetDefaultUserFootprintsPath()
|
||||
}
|
||||
|
||||
|
||||
wxString PATHS::GetDefaultUserDesignBlocksPath()
|
||||
{
|
||||
wxFileName tmp;
|
||||
getUserDocumentPath( tmp );
|
||||
|
||||
tmp.AppendDir( wxT( "blocks" ) );
|
||||
|
||||
return tmp.GetPath();
|
||||
}
|
||||
|
||||
|
||||
wxString PATHS::GetDefaultUser3DModelsPath()
|
||||
{
|
||||
wxFileName tmp;
|
||||
@ -224,6 +235,16 @@ wxString PATHS::GetStockFootprintsPath()
|
||||
}
|
||||
|
||||
|
||||
wxString PATHS::GetStockDesignBlocksPath()
|
||||
{
|
||||
wxString path;
|
||||
|
||||
path = GetStockEDALibraryPath() + wxT( "/blocks" );
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
wxString PATHS::GetStock3dmodelsPath()
|
||||
{
|
||||
wxString path;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <pgm_base.h>
|
||||
#include <confirm.h>
|
||||
#include <core/kicad_algo.h>
|
||||
#include <design_block_lib_table.h>
|
||||
#include <fp_lib_table.h>
|
||||
#include <string_utils.h>
|
||||
#include <kiface_ids.h>
|
||||
@ -167,40 +168,78 @@ const wxString PROJECT::FootprintLibTblName() const
|
||||
}
|
||||
|
||||
|
||||
void PROJECT::PinLibrary( const wxString& aLibrary, bool isSymbolLibrary )
|
||||
const wxString PROJECT::DesignBlockLibTblName() const
|
||||
{
|
||||
return libTableName( wxS( "design-block-lib-table" ) );
|
||||
}
|
||||
|
||||
|
||||
void PROJECT::PinLibrary( const wxString& aLibrary, enum LIB_TYPE_T aLibType )
|
||||
{
|
||||
COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
|
||||
std::vector<wxString>* pinnedLibs = isSymbolLibrary ? &m_projectFile->m_PinnedSymbolLibs
|
||||
: &m_projectFile->m_PinnedFootprintLibs;
|
||||
std::vector<wxString>* pinnedLibsCfg = nullptr;
|
||||
std::vector<wxString>* pinnedLibsFile = nullptr;
|
||||
|
||||
if( !alg::contains( *pinnedLibs, aLibrary ) )
|
||||
pinnedLibs->push_back( aLibrary );
|
||||
switch( aLibType )
|
||||
{
|
||||
case LIB_TYPE_T::SYMBOL_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedSymbolLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_symbol_libs;
|
||||
break;
|
||||
case LIB_TYPE_T::FOOTPRINT_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedFootprintLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_fp_libs;
|
||||
break;
|
||||
case LIB_TYPE_T::DESIGN_BLOCK_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedDesignBlockLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_design_block_libs;
|
||||
break;
|
||||
default:
|
||||
wxFAIL_MSG( "Cannot pin library: invalid library type" );
|
||||
return;
|
||||
}
|
||||
|
||||
if( !alg::contains( *pinnedLibsFile, aLibrary ) )
|
||||
pinnedLibsFile->push_back( aLibrary );
|
||||
|
||||
Pgm().GetSettingsManager().SaveProject();
|
||||
|
||||
pinnedLibs = isSymbolLibrary ? &cfg->m_Session.pinned_symbol_libs
|
||||
: &cfg->m_Session.pinned_fp_libs;
|
||||
|
||||
if( !alg::contains( *pinnedLibs, aLibrary ) )
|
||||
pinnedLibs->push_back( aLibrary );
|
||||
if( !alg::contains( *pinnedLibsCfg, aLibrary ) )
|
||||
pinnedLibsCfg->push_back( aLibrary );
|
||||
|
||||
cfg->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( cfg ) );
|
||||
}
|
||||
|
||||
|
||||
void PROJECT::UnpinLibrary( const wxString& aLibrary, bool isSymbolLibrary )
|
||||
void PROJECT::UnpinLibrary( const wxString& aLibrary, enum LIB_TYPE_T aLibType )
|
||||
{
|
||||
COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
|
||||
std::vector<wxString>* pinnedLibs = isSymbolLibrary ? &m_projectFile->m_PinnedSymbolLibs
|
||||
: &m_projectFile->m_PinnedFootprintLibs;
|
||||
std::vector<wxString>* pinnedLibsCfg = nullptr;
|
||||
std::vector<wxString>* pinnedLibsFile = nullptr;
|
||||
|
||||
alg::delete_matching( *pinnedLibs, aLibrary );
|
||||
switch( aLibType )
|
||||
{
|
||||
case LIB_TYPE_T::SYMBOL_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedSymbolLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_symbol_libs;
|
||||
break;
|
||||
case LIB_TYPE_T::FOOTPRINT_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedFootprintLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_fp_libs;
|
||||
break;
|
||||
case LIB_TYPE_T::DESIGN_BLOCK_LIB:
|
||||
pinnedLibsFile = &m_projectFile->m_PinnedDesignBlockLibs;
|
||||
pinnedLibsCfg = &cfg->m_Session.pinned_design_block_libs;
|
||||
break;
|
||||
default:
|
||||
wxFAIL_MSG( "Cannot unpin library: invalid library type" );
|
||||
return;
|
||||
}
|
||||
|
||||
alg::delete_matching( *pinnedLibsFile, aLibrary );
|
||||
Pgm().GetSettingsManager().SaveProject();
|
||||
|
||||
pinnedLibs = isSymbolLibrary ? &cfg->m_Session.pinned_symbol_libs
|
||||
: &cfg->m_Session.pinned_fp_libs;
|
||||
|
||||
alg::delete_matching( *pinnedLibs, aLibrary );
|
||||
alg::delete_matching( *pinnedLibsCfg, aLibrary );
|
||||
cfg->SaveToFile( Pgm().GetSettingsManager().GetPathForSettingsFile( cfg ) );
|
||||
}
|
||||
|
||||
@ -374,3 +413,39 @@ FP_LIB_TABLE* PROJECT::PcbFootprintLibs( KIWAY& aKiway )
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_LIB_TABLE* PROJECT::DesignBlockLibs()
|
||||
{
|
||||
// This is a lazy loading function, it loads the project specific table when
|
||||
// that table is asked for, not before.
|
||||
|
||||
DESIGN_BLOCK_LIB_TABLE* tbl = (DESIGN_BLOCK_LIB_TABLE*) GetElem( ELEM::DESIGN_BLOCK_LIB_TABLE );
|
||||
|
||||
if( tbl )
|
||||
{
|
||||
wxASSERT( tbl->ProjectElementType() == PROJECT::ELEM::DESIGN_BLOCK_LIB_TABLE );
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
tbl = new DESIGN_BLOCK_LIB_TABLE( &GDesignBlockTable );
|
||||
tbl->Load( DesignBlockLibTblName() );
|
||||
|
||||
SetElem( ELEM::DESIGN_BLOCK_LIB_TABLE, tbl );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
DisplayErrorMessage( nullptr, _( "Error loading project design block library table." ),
|
||||
ioe.What() );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
DisplayErrorMessage( nullptr,
|
||||
_( "Error loading project design block library table." ) );
|
||||
}
|
||||
}
|
||||
|
||||
return tbl;
|
||||
}
|
||||
|
@ -346,6 +346,9 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
|
||||
m_params.emplace_back( new PARAM_LIST<wxString>( "session.pinned_fp_libs",
|
||||
&m_Session.pinned_fp_libs, {} ) );
|
||||
|
||||
m_params.emplace_back( new PARAM_LIST<wxString>( "session.pinned_design_block_libs",
|
||||
&m_Session.pinned_design_block_libs, {} ) );
|
||||
|
||||
m_params.emplace_back( new PARAM<int>( "netclass_panel.sash_pos",
|
||||
&m_NetclassPanel.sash_pos, 160 ) );
|
||||
|
||||
@ -666,6 +669,10 @@ void COMMON_SETTINGS::InitializeEnvironment()
|
||||
path = basePath;
|
||||
path.AppendDir( wxT( "symbols" ) );
|
||||
addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "SYMBOL_DIR" ) ), path.GetFullPath() );
|
||||
|
||||
path = basePath;
|
||||
path.AppendDir( wxT( "blocks" ) );
|
||||
addVar( ENV_VAR::GetVersionedEnvVarName( wxS( "DESIGN_BLOCK_DIR" ) ), path.GetFullPath() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -43,6 +43,9 @@ KICAD_SETTINGS::KICAD_SETTINGS() :
|
||||
m_params.emplace_back(
|
||||
new PARAM_LIST<wxString>( "system.open_projects", &m_OpenProjects, {} ) );
|
||||
|
||||
m_params.emplace_back( new PARAM<wxString>( "system.last_design_block_lib_dir",
|
||||
&m_lastDesignBlockLibDir, "" ) );
|
||||
|
||||
m_params.emplace_back(
|
||||
new PARAM<wxString>( "system.last_update_check_time", &m_lastUpdateCheckTime, "" ) );
|
||||
|
||||
|
@ -1049,7 +1049,11 @@ std::vector<wxString> SETTINGS_MANAGER::GetOpenProjects() const
|
||||
std::vector<wxString> ret;
|
||||
|
||||
for( const std::pair<const wxString, PROJECT*>& pair : m_projects )
|
||||
ret.emplace_back( pair.first );
|
||||
{
|
||||
// Don't save empty projects (these are the default project settings)
|
||||
if( !pair.first.IsEmpty() )
|
||||
ret.emplace_back( pair.first );
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1104,6 +1104,13 @@ TOOL_ACTION ACTIONS::showFootprintLibTable( TOOL_ACTION_ARGS()
|
||||
.Tooltip( _( "Edit the global and project footprint library lists" ) )
|
||||
.Icon( BITMAPS::library_table ) );
|
||||
|
||||
TOOL_ACTION ACTIONS::showDesignBlockLibTable( TOOL_ACTION_ARGS()
|
||||
.Name( "common.SuiteControl.showDesignBLockLibTable" )
|
||||
.Scope( AS_GLOBAL )
|
||||
.FriendlyName( _( "Manage Design Block Libraries..." ) )
|
||||
.Tooltip( _( "Edit the global and project design block library lists" ) )
|
||||
.Icon( BITMAPS::library_table ) );
|
||||
|
||||
TOOL_ACTION ACTIONS::gettingStarted( TOOL_ACTION_ARGS()
|
||||
.Name( "common.SuiteControl.gettingStarted" )
|
||||
.Scope( AS_GLOBAL )
|
||||
|
@ -82,7 +82,8 @@ int LIBRARY_EDITOR_CONTROL::PinLibrary( const TOOL_EVENT& aEvent )
|
||||
|
||||
if( current && !current->m_Pinned )
|
||||
{
|
||||
m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(), false );
|
||||
m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(),
|
||||
PROJECT::LIB_TYPE_T::FOOTPRINT_LIB );
|
||||
current->m_Pinned = true;
|
||||
regenerateLibraryTree();
|
||||
}
|
||||
@ -98,7 +99,8 @@ int LIBRARY_EDITOR_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent )
|
||||
|
||||
if( current && current->m_Pinned )
|
||||
{
|
||||
m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(), false );
|
||||
m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(),
|
||||
PROJECT::LIB_TYPE_T::FOOTPRINT_LIB );
|
||||
current->m_Pinned = false;
|
||||
regenerateLibraryTree();
|
||||
}
|
||||
|
@ -58,6 +58,7 @@ const wxChar* const traceKiCad2Step = wxT( "KICAD2STEP" );
|
||||
const wxChar* const traceUiProfile = wxT( "KICAD_UI_PROFILE" );
|
||||
const wxChar* const traceGit = wxT( "KICAD_GIT" );
|
||||
const wxChar* const traceEagleIo = wxT( "KICAD_EAGLE_IO" );
|
||||
const wxChar* const traceDesignBlocks = wxT( "KICAD_DESIGN_BLOCK" );
|
||||
|
||||
|
||||
wxString dump( const wxArrayString& aArray )
|
||||
|
@ -186,6 +186,9 @@ const std::string FILEEXT::IpcD356FileExtension( "d356" );
|
||||
const std::string FILEEXT::Ipc2581FileExtension( "xml" );
|
||||
const std::string FILEEXT::WorkbookFileExtension( "wbk" );
|
||||
|
||||
const std::string FILEEXT::KiCadDesignBlockLibPathExtension( "blocks" ); // this is a directory
|
||||
const std::string FILEEXT::KiCadDesignBlockPathExtension( "block" ); // this is a directory
|
||||
|
||||
const std::string FILEEXT::PngFileExtension( "png" );
|
||||
const std::string FILEEXT::JpegFileExtension( "jpg" );
|
||||
const std::string FILEEXT::TextFileExtension( "txt" );
|
||||
@ -337,6 +340,20 @@ wxString FILEEXT::KiCadFootprintLibPathWildcard()
|
||||
}
|
||||
|
||||
|
||||
wxString FILEEXT::KiCadDesignBlockPathWildcard()
|
||||
{
|
||||
return _( "KiCad design block path" )
|
||||
+ AddFileExtListToFilter( { KiCadDesignBlockPathExtension } );
|
||||
}
|
||||
|
||||
|
||||
wxString FILEEXT::KiCadDesignBlockLibPathWildcard()
|
||||
{
|
||||
return _( "KiCad design block library paths" )
|
||||
+ AddFileExtListToFilter( { KiCadDesignBlockLibPathExtension } );
|
||||
}
|
||||
|
||||
|
||||
wxString FILEEXT::DrawingSheetFileWildcard()
|
||||
{
|
||||
return _( "Drawing sheet files" )
|
||||
|
@ -272,7 +272,10 @@ set( EESCHEMA_SIM_SRCS
|
||||
)
|
||||
|
||||
set( EESCHEMA_WIDGETS
|
||||
widgets/design_block_pane.cpp
|
||||
widgets/design_block_preview_widget.cpp
|
||||
widgets/hierarchy_pane.cpp
|
||||
widgets/panel_design_block_chooser.cpp
|
||||
widgets/panel_sch_selection_filter_base.cpp
|
||||
widgets/panel_sch_selection_filter.cpp
|
||||
widgets/panel_symbol_chooser.cpp
|
||||
@ -347,6 +350,8 @@ set( EESCHEMA_SRCS
|
||||
bus-wire-junction.cpp
|
||||
connection_graph.cpp
|
||||
cross-probing.cpp
|
||||
design_block_tree_model_adapter.cpp
|
||||
design_block_utils.cpp
|
||||
ee_collectors.cpp
|
||||
eeschema_config.cpp
|
||||
eeschema_helpers.cpp
|
||||
@ -441,6 +446,7 @@ set( EESCHEMA_SRCS
|
||||
tools/ee_selection_tool.cpp
|
||||
tools/rule_area_create_helper.cpp
|
||||
tools/sch_drawing_tools.cpp
|
||||
tools/sch_design_block_control.cpp
|
||||
tools/sch_edit_table_tool.cpp
|
||||
tools/sch_edit_tool.cpp
|
||||
tools/sch_editor_control.cpp
|
||||
|
@ -37,6 +37,7 @@
|
||||
#include <tools/ee_actions.h>
|
||||
#include <tools/sch_editor_control.h>
|
||||
#include <advanced_config.h>
|
||||
#include <widgets/design_block_pane.h>
|
||||
#include <wx/log.h>
|
||||
|
||||
SCH_ITEM* SCH_EDITOR_CONTROL::FindSymbolAndItem( const wxString* aPath, const wxString* aReference,
|
||||
@ -998,6 +999,7 @@ void SCH_EDIT_FRAME::KiwayMailIn( KIWAY_EXPRESS& mail )
|
||||
break;
|
||||
|
||||
case MAIL_RELOAD_LIB:
|
||||
m_designBlocksPane->RefreshLibs();
|
||||
SyncView();
|
||||
break;
|
||||
|
||||
|
199
eeschema/design_block_tree_model_adapter.cpp
Normal file
199
eeschema/design_block_tree_model_adapter.cpp
Normal file
@ -0,0 +1,199 @@
|
||||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2018-2024 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 <pgm_base.h>
|
||||
#include <eda_base_frame.h>
|
||||
#include <core/kicad_algo.h>
|
||||
#include <settings/common_settings.h>
|
||||
#include <project/project_file.h>
|
||||
#include <wx/log.h>
|
||||
#include <wx/tokenzr.h>
|
||||
#include <string_utils.h>
|
||||
#include <eda_pattern_match.h>
|
||||
#include <design_block.h>
|
||||
#include <design_block_lib_table.h>
|
||||
#include <design_block_info.h>
|
||||
#include <footprint_info.h>
|
||||
#include <wx/settings.h>
|
||||
|
||||
#include <design_block_tree_model_adapter.h>
|
||||
#include <tools/sch_design_block_control.h>
|
||||
|
||||
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>
|
||||
DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs )
|
||||
{
|
||||
auto* adapter = new DESIGN_BLOCK_TREE_MODEL_ADAPTER( aParent, aLibs );
|
||||
adapter->m_frame = aParent;
|
||||
return wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>( adapter );
|
||||
}
|
||||
|
||||
|
||||
DESIGN_BLOCK_TREE_MODEL_ADAPTER::DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent,
|
||||
LIB_TABLE* aLibs ) :
|
||||
LIB_TREE_MODEL_ADAPTER( aParent, wxT( "pinned_design_block_libs" ) ),
|
||||
m_libs( (DESIGN_BLOCK_LIB_TABLE*) aLibs )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_TREE_MODEL_ADAPTER::AddLibraries( EDA_BASE_FRAME* aParent )
|
||||
{
|
||||
COMMON_SETTINGS* cfg = Pgm().GetCommonSettings();
|
||||
PROJECT_FILE& project = aParent->Prj().GetProjectFile();
|
||||
|
||||
for( const wxString& libName : m_libs->GetLogicalLibs() )
|
||||
{
|
||||
const DESIGN_BLOCK_LIB_TABLE_ROW* library = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
library = m_libs->FindRow( libName, true );
|
||||
}
|
||||
catch( ... )
|
||||
{
|
||||
// Skip loading this library, if not exists/ not found
|
||||
continue;
|
||||
}
|
||||
bool pinned = alg::contains( cfg->m_Session.pinned_design_block_libs, libName )
|
||||
|| alg::contains( project.m_PinnedDesignBlockLibs, libName );
|
||||
|
||||
DoAddLibrary( libName, library->GetDescr(), getDesignBlocks( aParent, libName ), pinned,
|
||||
true );
|
||||
}
|
||||
|
||||
m_tree.AssignIntrinsicRanks();
|
||||
}
|
||||
|
||||
|
||||
void DESIGN_BLOCK_TREE_MODEL_ADAPTER::ClearLibraries()
|
||||
{
|
||||
m_tree.Clear();
|
||||
}
|
||||
|
||||
|
||||
std::vector<LIB_TREE_ITEM*>
|
||||
DESIGN_BLOCK_TREE_MODEL_ADAPTER::getDesignBlocks( EDA_BASE_FRAME* aParent,
|
||||
const wxString& aLibName )
|
||||
{
|
||||
std::vector<LIB_TREE_ITEM*> libList;
|
||||
|
||||
|
||||
auto fullListStart = GDesignBlockList.GetList().begin();
|
||||
auto fullListEnd = GDesignBlockList.GetList().end();
|
||||
std::unique_ptr<DESIGN_BLOCK_INFO> dummy =
|
||||
std::make_unique<DESIGN_BLOCK_INFO_IMPL>( aLibName, wxEmptyString );
|
||||
|
||||
// List is sorted, so use a binary search to find the range of footnotes for our library
|
||||
auto libBounds = std::equal_range(
|
||||
fullListStart, fullListEnd, dummy,
|
||||
[]( const std::unique_ptr<DESIGN_BLOCK_INFO>& a,
|
||||
const std::unique_ptr<DESIGN_BLOCK_INFO>& b )
|
||||
{
|
||||
return StrNumCmp( a->GetLibNickname(), b->GetLibNickname(), false ) < 0;
|
||||
} );
|
||||
|
||||
for( auto i = libBounds.first; i != libBounds.second; ++i )
|
||||
libList.push_back( i->get() );
|
||||
|
||||
return libList;
|
||||
}
|
||||
|
||||
|
||||
wxString DESIGN_BLOCK_TREE_MODEL_ADAPTER::GenerateInfo( LIB_ID const& aLibId, int aUnit )
|
||||
{
|
||||
const wxString DescriptionFormat = wxT(
|
||||
"<b>__NAME__</b>"
|
||||
"<br>__DESC__"
|
||||
"<hr><table border=0>"
|
||||
"__FIELDS__"
|
||||
"</table>" );
|
||||
|
||||
const wxString KeywordsFormat = wxT(
|
||||
"<tr>"
|
||||
" <td><b>" + _( "Keywords" ) + "</b></td>"
|
||||
" <td>__KEYWORDS__</td>"
|
||||
"</tr>" );
|
||||
|
||||
const wxString DocFormat = wxT(
|
||||
"<tr>"
|
||||
" <td><b>" + _( "Documentation" ) + "</b></td>"
|
||||
" <td><a href=\"__HREF__\">__TEXT__</a></td>"
|
||||
"</tr>" );
|
||||
|
||||
|
||||
if( !aLibId.IsValid() )
|
||||
return wxEmptyString;
|
||||
|
||||
const DESIGN_BLOCK* db = nullptr;
|
||||
|
||||
try
|
||||
{
|
||||
db = m_libs->GetEnumeratedDesignBlock( aLibId.GetLibNickname(), aLibId.GetLibItemName() );
|
||||
}
|
||||
catch( const IO_ERROR& ioe )
|
||||
{
|
||||
wxLogError( _( "Error loading design block %s from library '%s'." ) + wxS( "\n%s" ),
|
||||
aLibId.GetLibItemName().wx_str(), aLibId.GetLibNickname().wx_str(),
|
||||
ioe.What() );
|
||||
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
wxString html = DescriptionFormat;
|
||||
|
||||
if( db )
|
||||
{
|
||||
wxString name = aLibId.GetLibItemName();
|
||||
wxString desc = db->GetLibDescription();
|
||||
wxString keywords = db->GetKeywords();
|
||||
wxString doc = db->GetDocumentationUrl();
|
||||
|
||||
wxString esc_desc = EscapeHTML( UnescapeString( desc ) );
|
||||
|
||||
// Add line breaks
|
||||
esc_desc.Replace( wxS( "\n" ), wxS( "<br>" ) );
|
||||
|
||||
// Add links
|
||||
esc_desc = LinkifyHTML( esc_desc );
|
||||
|
||||
html.Replace( "__DESC__", esc_desc );
|
||||
html.Replace( "__NAME__", EscapeHTML( name ) );
|
||||
|
||||
wxString keywordsHtml = KeywordsFormat;
|
||||
keywordsHtml.Replace( "__KEYWORDS__", EscapeHTML( keywords ) );
|
||||
|
||||
wxString docHtml = DocFormat;
|
||||
docHtml.Replace( "__HREF__", doc );
|
||||
|
||||
if( doc.Length() > 75 )
|
||||
doc = doc.Left( 72 ) + wxT( "..." );
|
||||
|
||||
docHtml.Replace( "__TEXT__", EscapeHTML( doc ) );
|
||||
|
||||
html.Replace( "__FIELDS__", keywordsHtml + docHtml );
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
TOOL_INTERACTIVE* DESIGN_BLOCK_TREE_MODEL_ADAPTER::GetContextMenuTool()
|
||||
{
|
||||
return m_frame->GetToolManager()->GetTool<SCH_DESIGN_BLOCK_CONTROL>();
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user