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:
Mike Williams 2024-02-21 11:35:09 -05:00
parent 43177b7554
commit 2c99bc6c6d
89 changed files with 8262 additions and 237 deletions

View File

@ -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 )

View File

@ -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
View 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
};

View 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
View 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_

View 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();
}

View 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
View 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
View 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

View 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();
}

View File

@ -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

View File

@ -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 )
{

View File

@ -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();

View File

@ -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;

View File

@ -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;
}

View File

@ -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() );
}

View File

@ -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, "" ) );

View File

@ -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;
}

View File

@ -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 )

View File

@ -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();
}

View File

@ -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 )

View File

@ -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" )

View File

@ -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

View File

@ -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;

View 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>();
}

View File

@ -0,0 +1,62 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014-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/>.
*/
#ifndef DESIGN_BLOCK_TREE_MODEL_ADAPTER_H
#define DESIGN_BLOCK_TREE_MODEL_ADAPTER_H
#include <lib_tree_model_adapter.h>
class LIB_TABLE;
class DESIGN_BLOCK_LIB_TABLE;
class DESIGN_BLOCK_TREE_MODEL_ADAPTER : public LIB_TREE_MODEL_ADAPTER
{
public:
/**
* Factory function: create a model adapter in a reference-counting container.
*
* @param aLibs library set from which parts will be loaded
*/
static wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> Create( EDA_BASE_FRAME* aParent,
LIB_TABLE* aLibs );
void AddLibraries( EDA_BASE_FRAME* aParent );
void ClearLibraries();
wxString GenerateInfo( LIB_ID const& aLibId, int aUnit ) override;
TOOL_INTERACTIVE* GetContextMenuTool() override;
protected:
/**
* Constructor; takes a set of libraries to be included in the search.
*/
DESIGN_BLOCK_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs );
std::vector<LIB_TREE_ITEM*> getDesignBlocks( EDA_BASE_FRAME* aParent,
const wxString& aLibName );
PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB; }
protected:
DESIGN_BLOCK_LIB_TABLE* m_libs;
EDA_BASE_FRAME* m_frame;
};
#endif // DESIGN_BLOCK_TREE_MODEL_ADAPTER_H

View File

@ -0,0 +1,522 @@
/*
* 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
*/
#include <pgm_base.h>
#include <kiway.h>
#include <design_block.h>
#include <design_block_lib_table.h>
#include <design_block_pane.h>
#include <sch_edit_frame.h>
#include <wx/choicdlg.h>
#include <wx/msgdlg.h>
#include <wx/textdlg.h>
#include <wildcards_and_files_ext.h>
#include <paths.h>
#include <env_paths.h>
#include <common.h>
#include <kidialog.h>
#include <confirm.h>
#include <tool/tool_manager.h>
#include <ee_selection_tool.h>
DESIGN_BLOCK_LIB_TABLE* SCH_EDIT_FRAME::selectDesignBlockLibTable( bool aOptional )
{
// If no project is loaded, always work with the global table
if( Prj().IsNullProject() )
{
DESIGN_BLOCK_LIB_TABLE* ret = &GDesignBlockTable;
if( aOptional )
{
wxMessageDialog dlg( this, _( "Add the library to the global library table?" ),
_( "Add To Global Library Table" ), wxYES_NO );
if( dlg.ShowModal() != wxID_OK )
ret = nullptr;
}
return ret;
}
wxArrayString libTableNames;
libTableNames.Add( _( "Global" ) );
libTableNames.Add( _( "Project" ) );
wxSingleChoiceDialog dlg( this, _( "Choose the Library Table to add the library to:" ),
_( "Add To Library Table" ), libTableNames );
if( aOptional )
{
dlg.FindWindow( wxID_CANCEL )->SetLabel( _( "Skip" ) );
dlg.FindWindow( wxID_OK )->SetLabel( _( "Add" ) );
}
if( dlg.ShowModal() != wxID_OK )
return nullptr;
switch( dlg.GetSelection() )
{
case 0: return &GDesignBlockTable;
case 1: return Prj().DesignBlockLibs();
default: return nullptr;
}
}
wxString SCH_EDIT_FRAME::CreateNewDesignBlockLibrary( const wxString& aLibName,
const wxString& aProposedName )
{
return createNewDesignBlockLibrary( aLibName, aProposedName, selectDesignBlockLibTable() );
}
wxString SCH_EDIT_FRAME::createNewDesignBlockLibrary( const wxString& aLibName,
const wxString& aProposedName,
DESIGN_BLOCK_LIB_TABLE* aTable )
{
if( aTable == nullptr )
return wxEmptyString;
wxString initialPath = aProposedName.IsEmpty() ? Prj().GetProjectPath() : aProposedName;
wxFileName fn;
bool doAdd = false;
bool isGlobal = ( aTable == &GDesignBlockTable );
if( aLibName.IsEmpty() )
{
fn = initialPath;
if( !LibraryFileBrowser( false, fn, FILEEXT::KiCadDesignBlockLibPathWildcard(),
FILEEXT::KiCadDesignBlockLibPathExtension, false, isGlobal,
PATHS::GetDefaultUserFootprintsPath() ) )
{
return wxEmptyString;
}
doAdd = true;
}
else
{
fn = EnsureFileExtension( aLibName, FILEEXT::KiCadDesignBlockLibPathExtension );
if( !fn.IsAbsolute() )
{
fn.SetName( aLibName );
fn.MakeAbsolute( initialPath );
}
}
// We can save libs only using DESIGN_BLOCK_IO_MGR::KICAD_SEXP format (.pretty libraries)
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T piType = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
wxString libPath = fn.GetFullPath();
try
{
IO_RELEASER<DESIGN_BLOCK_IO> pi( DESIGN_BLOCK_IO_MGR::FindPlugin( piType ) );
bool writable = false;
bool exists = false;
try
{
writable = pi->IsLibraryWritable( libPath );
exists = fn.Exists();
}
catch( const IO_ERROR& )
{
// best efforts....
}
if( exists )
{
if( !writable )
{
wxString msg = wxString::Format( _( "Library %s is read only." ), libPath );
ShowInfoBarError( msg );
return wxEmptyString;
}
else
{
wxString msg = wxString::Format( _( "Library %s already exists." ), libPath );
KIDIALOG dlg( this, msg, _( "Confirmation" ), wxOK | wxCANCEL | wxICON_WARNING );
dlg.SetOKLabel( _( "Overwrite" ) );
dlg.DoNotShowCheckbox( __FILE__, __LINE__ );
if( dlg.ShowModal() == wxID_CANCEL )
return wxEmptyString;
pi->DeleteLibrary( libPath );
}
}
pi->CreateLibrary( libPath );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
return wxEmptyString;
}
if( doAdd )
AddDesignBlockLibrary( libPath, aTable );
return libPath;
}
bool SCH_EDIT_FRAME::AddDesignBlockLibrary( const wxString& aFilename,
DESIGN_BLOCK_LIB_TABLE* aTable )
{
if( aTable == nullptr )
aTable = selectDesignBlockLibTable();
if( aTable == nullptr )
return wxEmptyString;
bool isGlobal = ( aTable == &GDesignBlockTable );
wxFileName fn( aFilename );
if( aFilename.IsEmpty() )
{
if( !LibraryFileBrowser( true, fn, FILEEXT::KiCadFootprintLibPathWildcard(),
FILEEXT::KiCadFootprintLibPathExtension, true, isGlobal,
PATHS::GetDefaultUserFootprintsPath() ) )
{
return false;
}
}
wxString libPath = fn.GetFullPath();
wxString libName = fn.GetName();
if( libName.IsEmpty() )
return false;
// Open a dialog to ask for a description
wxString description = wxGetTextFromUser( _( "Enter a description for the library:" ),
_( "Library Description" ), wxEmptyString, this );
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T lib_type =
DESIGN_BLOCK_IO_MGR::GuessPluginTypeFromLibPath( libPath );
if( lib_type == DESIGN_BLOCK_IO_MGR::FILE_TYPE_NONE )
lib_type = DESIGN_BLOCK_IO_MGR::KICAD_SEXP;
wxString type = DESIGN_BLOCK_IO_MGR::ShowType( lib_type );
// KiCad lib is our default guess. So it might not have the .blocks extension
// In this case, the extension is part of the library name
if( lib_type == DESIGN_BLOCK_IO_MGR::KICAD_SEXP
&& fn.GetExt() != FILEEXT::KiCadDesignBlockLibPathExtension )
libName = fn.GetFullName();
// try to use path normalized to an environmental variable or project path
wxString normalizedPath = NormalizePath( libPath, &Pgm().GetLocalEnvVariables(), &Prj() );
try
{
DESIGN_BLOCK_LIB_TABLE_ROW* row = new DESIGN_BLOCK_LIB_TABLE_ROW(
libName, normalizedPath, type, wxEmptyString, description );
aTable->InsertRow( row );
if( isGlobal )
GDesignBlockTable.Save( DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName() );
else
Prj().DesignBlockLibs()->Save( Prj().DesignBlockLibTblName() );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
return false;
}
LIB_ID libID( libName, wxEmptyString );
m_designBlocksPane->RefreshLibs();
m_designBlocksPane->SelectLibId( libID );
return true;
}
void SCH_EDIT_FRAME::SaveSheetAsDesignBlock( const wxString& aLibraryName )
{
// Make sure the user has selected a library to save into
if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
{
DisplayError( this, _( "Please select a library to save the design block to." ) );
return;
}
// Just block all attempts to create design blocks with nested sheets at this point
std::vector<SCH_ITEM*> sheets;
GetScreen()->GetSheets( &sheets );
if( !sheets.empty() )
{
DisplayError( this, _( "Design blocks with nested sheets are not supported." ) );
return;
}
// Ask the user for the design block name
wxFileName fn = wxFileNameFromPath( GetScreen()->GetFileName() );
wxString name = wxGetTextFromUser( _( "Enter a name for the design block:" ),
_( "Design Block Name" ), fn.GetName(), this );
if( name.IsEmpty() )
return;
// Save a temporary copy of the schematic file, as the plugin is just going to move it
wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
if( !saveSchematicFile( GetCurrentSheet().Last(), tempFile ) )
{
DisplayError( this, _( "Error saving temporary schematic file to create design block." ) );
wxRemoveFile( tempFile );
return;
}
DESIGN_BLOCK blk;
blk.SetSchematicFile( tempFile );
blk.SetLibId( LIB_ID( aLibraryName, name ) );
try
{
Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
}
// Clean up the temporary file
wxRemoveFile( tempFile );
m_designBlocksPane->RefreshLibs();
m_designBlocksPane->SelectLibId( blk.GetLibId() );
}
void SCH_EDIT_FRAME::SaveSelectionAsDesignBlock( const wxString& aLibraryName )
{
// Make sure the user has selected a library to save into
if( m_designBlocksPane->GetSelectedLibId().GetLibNickname().empty() )
{
DisplayError( this, _( "Please select a library to save the design block to." ) );
return;
}
// Get all selected items
EE_SELECTION selection = m_toolManager->GetTool<EE_SELECTION_TOOL>()->GetSelection();
if( selection.Empty() )
{
DisplayError( this, _( "Please select some items to save as a design block." ) );
return;
}
// Just block all attempts to create design blocks with nested sheets at this point
if( selection.HasType( SCH_SHEET_T ) )
{
DisplayError( this, _( "Design blocks with nested sheets are not supported." ) );
return;
}
// Ask the user for the design block name
wxString name = wxGetTextFromUser( _( "Enter a name for the design block:" ),
_( "Design Block Name" ), wxEmptyString, this );
if( name.IsEmpty() )
return;
// Create a temperorary screen
SCH_SCREEN* tempScreen = new SCH_SCREEN( m_schematic );
// Copy the selected items to the temporary screen
for( EDA_ITEM* item : selection )
{
EDA_ITEM* copy = item->Clone();
tempScreen->Append( static_cast<SCH_ITEM*>( copy ) );
}
// Create a sheet for the temporary screen
SCH_SHEET* tempSheet = new SCH_SHEET( m_schematic );
tempSheet->SetScreen( tempScreen );
// Save a temporary copy of the schematic file, as the plugin is just going to move it
wxString tempFile = wxFileName::CreateTempFileName( "design_block" );
if( !saveSchematicFile( tempSheet, tempFile ) )
{
DisplayError( this, _( "Error saving temporary schematic file to create design block." ) );
wxRemoveFile( tempFile );
return;
}
// Create a design block
DESIGN_BLOCK blk;
blk.SetSchematicFile( tempFile );
blk.SetLibId( LIB_ID( aLibraryName, name ) );
try
{
// Actually save it to disk
Prj().DesignBlockLibs()->DesignBlockSave( aLibraryName, &blk );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
}
// Clean up the temporaries
wxRemoveFile( tempFile );
// This will also delete the screen
delete tempSheet;
m_designBlocksPane->RefreshLibs();
m_designBlocksPane->SelectLibId( blk.GetLibId() );
}
bool SCH_EDIT_FRAME::DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm )
{
if( aLibName.IsEmpty() )
{
DisplayError( this, _( "Please select a library to delete." ) );
return false;
}
if( !Prj().DesignBlockLibs()->IsDesignBlockLibWritable( aLibName ) )
{
wxString msg = wxString::Format( _( "Library '%s' is read only." ), aLibName );
ShowInfoBarError( msg );
return false;
}
// Confirmation
wxString msg = wxString::Format( _( "Delete design block library '%s' from disk? This will "
"delete all design blocks within the library." ),
aLibName.GetData() );
if( aConfirm && !IsOK( this, msg ) )
return false;
try
{
Prj().DesignBlockLibs()->DesignBlockLibDelete( aLibName );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
return false;
}
msg.Printf( _( "Design block library '%s' deleted" ), aLibName.GetData() );
SetStatusText( msg );
m_designBlocksPane->RefreshLibs();
return true;
}
bool SCH_EDIT_FRAME::DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm )
{
if( !aLibId.IsValid() )
return false;
wxString libname = aLibId.GetLibNickname();
wxString dbname = aLibId.GetLibItemName();
if( !Prj().DesignBlockLibs()->IsDesignBlockLibWritable( libname ) )
{
wxString msg = wxString::Format( _( "Library '%s' is read only." ), libname );
ShowInfoBarError( msg );
return false;
}
// Confirmation
wxString msg = wxString::Format( _( "Delete design block '%s' in library '%s' from disk?" ),
dbname.GetData(), libname.GetData() );
if( aConfirm && !IsOK( this, msg ) )
return false;
try
{
Prj().DesignBlockLibs()->DesignBlockDelete( libname, dbname );
}
catch( const IO_ERROR& ioe )
{
DisplayError( this, ioe.What() );
return false;
}
msg.Printf( _( "Design block '%s' deleted from library '%s'" ), dbname.GetData(),
libname.GetData() );
SetStatusText( msg );
m_designBlocksPane->RefreshLibs();
return true;
}
DESIGN_BLOCK* SchGetDesignBlock( const LIB_ID& aLibId, DESIGN_BLOCK_LIB_TABLE* aLibTable,
wxWindow* aParent, bool aShowErrorMsg )
{
wxCHECK_MSG( aLibTable, nullptr, wxS( "Invalid design block library table." ) );
DESIGN_BLOCK* designBlock = nullptr;
try
{
designBlock = aLibTable->DesignBlockLoadWithOptionalNickname( aLibId, true );
}
catch( const IO_ERROR& ioe )
{
if( aShowErrorMsg )
{
wxString msg = wxString::Format( _( "Error loading designblock %s from library '%s'." ),
aLibId.GetLibItemName().wx_str(),
aLibId.GetLibNickname().wx_str() );
DisplayErrorMessage( aParent, msg, ioe.What() );
}
}
return designBlock;
}
DESIGN_BLOCK* SCH_EDIT_FRAME::GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib,
bool aShowErrorMsg )
{
return SchGetDesignBlock( aLibId, Prj().DesignBlockLibs(), this, aShowErrorMsg );
}
void SCH_EDIT_FRAME::UpdateDesignBlockOptions()
{
m_designBlocksPane->UpdateCheckboxes();
}

View File

@ -178,26 +178,10 @@ bool DIALOG_SHEET_PROPERTIES::TransferDataToWindow()
m_borderSwatch->SetSwatchBackground( canvas );
m_backgroundSwatch->SetSwatchBackground( canvas );
SCH_SHEET_LIST hierarchy = m_frame->Schematic().GetFullHierarchy();
SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
instance.push_back( m_sheet );
wxString pageNumber;
if( m_sheet->IsNew() )
{
// Don't try to be too clever when assigning the next availabe page number. Just use
// the number of sheets plus one.
pageNumber.Printf( wxT( "%d" ), static_cast<int>( hierarchy.size() ) + 1 );
instance.SetPageNumber( pageNumber );
}
else
{
pageNumber = instance.GetPageNumber();
}
m_pageNumberTextCtrl->ChangeValue( pageNumber );
m_pageNumberTextCtrl->ChangeValue( instance.GetPageNumber() );
m_cbExcludeFromSim->SetValue( m_sheet->GetExcludedFromSim() );
m_cbExcludeFromBom->SetValue( m_sheet->GetExcludedFromBOM() );

View File

@ -34,6 +34,7 @@
#include <sch_edit_frame.h>
#include <sch_painter.h>
#include <schematic.h>
#include <widgets/design_block_pane.h>
#include <widgets/hierarchy_pane.h>
#include <widgets/sch_search_pane.h>
#include <widgets/panel_sch_selection_filter.h>
@ -304,6 +305,17 @@ void SCH_EDIT_FRAME::SaveSettings( APP_SETTINGS_BASE* aCfg )
cfg->m_AuiPanels.net_nav_panel_float_pos = netNavigatorPane.floating_pos;
cfg->m_AuiPanels.net_nav_panel_float_size = netNavigatorPane.floating_size;
}
wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() );
cfg->m_AuiPanels.design_blocks_show = designBlocksPane.IsShown();
if( designBlocksPane.IsDocked() )
cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x;
else
{
cfg->m_AuiPanels.design_blocks_panel_float_height = designBlocksPane.floating_size.y;
cfg->m_AuiPanels.design_blocks_panel_float_width = designBlocksPane.floating_size.x;
}
}
}

View File

@ -60,7 +60,7 @@ SETTINGS_MANAGER* EESCHEMA_HELPERS::GetSettingsManager()
}
PROJECT* EESCHEMA_HELPERS::GetDefaultProject()
PROJECT* EESCHEMA_HELPERS::GetDefaultProject( bool aSetActive )
{
// For some reasons, LoadProject() needs a C locale, so ensure we have the right locale
// This is mainly when running QA Python tests
@ -70,7 +70,7 @@ PROJECT* EESCHEMA_HELPERS::GetDefaultProject()
if( !project )
{
GetSettingsManager()->LoadProject( "" );
GetSettingsManager()->LoadProject( "", aSetActive );
project = GetSettingsManager()->GetProject( "" );
}
@ -78,20 +78,9 @@ PROJECT* EESCHEMA_HELPERS::GetDefaultProject()
}
SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( wxString& aFileName, bool aSetActive )
{
if( aFileName.EndsWith( FILEEXT::KiCadSchematicFileExtension ) )
return LoadSchematic( aFileName, SCH_IO_MGR::SCH_KICAD, aSetActive );
else if( aFileName.EndsWith( FILEEXT::LegacySchematicFileExtension ) )
return LoadSchematic( aFileName, SCH_IO_MGR::SCH_LEGACY, aSetActive );
// as fall back for any other kind use the legacy format
return LoadSchematic( aFileName, SCH_IO_MGR::SCH_LEGACY, aSetActive );
}
SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( wxString& aFileName, SCH_IO_MGR::SCH_FILE_T aFormat,
bool aSetActive )
SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( const wxString& aFileName,
SCH_IO_MGR::SCH_FILE_T aFormat, bool aSetActive,
bool aForceDefaultProject )
{
wxFileName pro = aFileName;
pro.SetExt( FILEEXT::ProjectFileExtension );
@ -104,23 +93,26 @@ SCHEMATIC* EESCHEMA_HELPERS::LoadSchematic( wxString& aFileName, SCH_IO_MGR::SCH
PROJECT* project = GetSettingsManager()->GetProject( projectPath );
if( !project )
if( !aForceDefaultProject )
{
if( wxFileExists( projectPath ) )
if( !project )
{
GetSettingsManager()->LoadProject( projectPath, aSetActive );
project = GetSettingsManager()->GetProject( projectPath );
if( wxFileExists( projectPath ) )
{
GetSettingsManager()->LoadProject( projectPath, aSetActive );
project = GetSettingsManager()->GetProject( projectPath );
}
}
else if( s_SchEditFrame && project == &GetSettingsManager()->Prj() )
{
// Project is already loaded? Then so is the board
return &s_SchEditFrame->Schematic();
}
}
else if( s_SchEditFrame && project == &GetSettingsManager()->Prj() )
{
// Project is already loaded? Then so is the board
return &s_SchEditFrame->Schematic();
}
// Board cannot be loaded without a project, so create the default project
if( !project )
project = GetDefaultProject();
if( !project || aForceDefaultProject )
project = GetDefaultProject( aSetActive );
IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( aFormat ) );

View File

@ -43,15 +43,13 @@ class EESCHEMA_HELPERS
public:
static SETTINGS_MANAGER* GetSettingsManager();
static void SetSchEditFrame( SCH_EDIT_FRAME* aSchEditFrame );
static PROJECT* GetDefaultProject();
static SCHEMATIC* LoadSchematic( wxString& aFileName, bool aSetActive );
static SCHEMATIC* LoadSchematic( wxString& aFileName, SCH_IO_MGR::SCH_FILE_T aFormat,
bool aSetActive );
static PROJECT* GetDefaultProject( bool aSetActive );
static SCHEMATIC* LoadSchematic( const wxString& aFileName, SCH_IO_MGR::SCH_FILE_T aFormat,
bool aSetActive, bool aForceDefaultProject );
private:
static SCH_EDIT_FRAME* s_SchEditFrame;
static SETTINGS_MANAGER* s_SettingsManager;
};
#endif
#endif

View File

@ -149,7 +149,8 @@ int EESCHEMA_JOBS_HANDLER::JobExportPlot( JOB* aJob )
if( !aPlotJob )
return CLI::EXIT_CODES::ERR_UNKNOWN;
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aPlotJob->m_filename, SCH_IO_MGR::SCH_KICAD, true );
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aPlotJob->m_filename, SCH_IO_MGR::SCH_KICAD,
true, false );
if( sch == nullptr )
{
@ -247,7 +248,8 @@ int EESCHEMA_JOBS_HANDLER::JobExportNetlist( JOB* aJob )
if( !aNetJob )
return CLI::EXIT_CODES::ERR_UNKNOWN;
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aNetJob->m_filename, SCH_IO_MGR::SCH_KICAD, true );
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aNetJob->m_filename, SCH_IO_MGR::SCH_KICAD,
true, false );
if( sch == nullptr )
{
@ -358,7 +360,8 @@ int EESCHEMA_JOBS_HANDLER::JobExportBom( JOB* aJob )
if( !aBomJob )
return CLI::EXIT_CODES::ERR_UNKNOWN;
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aBomJob->m_filename, SCH_IO_MGR::SCH_KICAD, true );
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aBomJob->m_filename, SCH_IO_MGR::SCH_KICAD,
true, false );
if( sch == nullptr )
{
@ -616,7 +619,8 @@ int EESCHEMA_JOBS_HANDLER::JobExportPythonBom( JOB* aJob )
if( !aNetJob )
return CLI::EXIT_CODES::ERR_UNKNOWN;
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aNetJob->m_filename, SCH_IO_MGR::SCH_KICAD, true );
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( aNetJob->m_filename, SCH_IO_MGR::SCH_KICAD,
true, false );
if( sch == nullptr )
{
@ -963,7 +967,8 @@ int EESCHEMA_JOBS_HANDLER::JobSchErc( JOB* aJob )
if( !ercJob )
return CLI::EXIT_CODES::ERR_UNKNOWN;
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( ercJob->m_filename, SCH_IO_MGR::SCH_KICAD, true );
SCHEMATIC* sch = EESCHEMA_HELPERS::LoadSchematic( ercJob->m_filename, SCH_IO_MGR::SCH_KICAD,
true, false );
if( sch == nullptr )
{

View File

@ -126,6 +126,28 @@ const wxAuiPaneInfo& defaultSchSelectionFilterPaneInfo( wxWindow* aWindow )
}
const wxAuiPaneInfo& defaultDesignBlocksPaneInfo( wxWindow* aWindow )
{
static wxAuiPaneInfo paneInfo;
paneInfo.Name( EDA_DRAW_FRAME::DesignBlocksPaneName() )
.Caption( _( "Design Blocks" ) )
.CaptionVisible( true )
.PaneBorder( true )
.Right().Layer( 3 ).Position( 2 )
.TopDockable( false )
.BottomDockable( false )
.CloseButton( false )
.MinSize( aWindow->FromDIP( wxSize( 240, 60 ) ) )
.BestSize( aWindow->FromDIP( wxSize( 300, 200 ) ) )
.FloatingSize( aWindow->FromDIP( wxSize( 800, 600 ) ) )
.FloatingPosition( aWindow->FromDIP( wxPoint( 50, 200 ) ) )
.Show( true );
return paneInfo;
}
EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
APP_SETTINGS_BASE( "eeschema", eeschemaSchemaVersion ),
m_Appearance(),
@ -240,6 +262,18 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "aui.hierarchy_panel_float_height",
&m_AuiPanels.hierarchy_panel_float_height, -1 ) );
m_params.emplace_back( new PARAM<bool>( "aui.design_blocks_show",
&m_AuiPanels.design_blocks_show, false ) );
m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_docked_width",
&m_AuiPanels.design_blocks_panel_docked_width, -1 ) );
m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_float_width",
&m_AuiPanels.design_blocks_panel_float_width, -1 ) );
m_params.emplace_back( new PARAM<int>( "aui.design_blocks_panel_float_height",
&m_AuiPanels.design_blocks_panel_float_height, -1 ) );
m_params.emplace_back( new PARAM<bool>( "aui.schematic_hierarchy_float",
&m_AuiPanels.schematic_hierarchy_float, false ) );
@ -613,6 +647,24 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_params.emplace_back( new PARAM<bool>( "symbol_chooser.place_all_units",
&m_SymChooserPanel.place_all_units, true ) );
m_params.emplace_back( new PARAM<int>( "design_block_chooser.sash_pos_v",
&m_DesignBlockChooserPanel.sash_pos_v, -1 ) );
m_params.emplace_back( new PARAM<int>( "design_block_chooser.width",
&m_DesignBlockChooserPanel.width, -1 ) );
m_params.emplace_back( new PARAM<int>( "design_block_chooser.height",
&m_DesignBlockChooserPanel.height, -1 ) );
m_params.emplace_back( new PARAM<int>( "design_block_chooser.sort_mode",
&m_DesignBlockChooserPanel.sort_mode, 0 ) );
m_params.emplace_back( new PARAM<bool>( "design_block_chooser.repeated_placement",
&m_DesignBlockChooserPanel.repeated_placement, false ) );
m_params.emplace_back( new PARAM<bool>( "design_block_chooser.place_as_sheet",
&m_DesignBlockChooserPanel.place_as_sheet, false ) );
m_params.emplace_back( new PARAM<bool>( "import_graphics.interactive_placement",
&m_ImportGraphics.interactive_placement, true ) );

View File

@ -35,6 +35,7 @@ using KIGFX::COLOR4D;
extern const wxAuiPaneInfo& defaultNetNavigatorPaneInfo();
extern const wxAuiPaneInfo& defaultPropertiesPaneInfo( wxWindow* aWindow );
extern const wxAuiPaneInfo& defaultSchSelectionFilterPaneInfo( wxWindow* aWindow );
extern const wxAuiPaneInfo& defaultDesignBlocksPaneInfo( wxWindow* aWindow );
@ -101,6 +102,10 @@ public:
int properties_panel_width;
float properties_splitter;
bool show_properties;
bool design_blocks_show;
int design_blocks_panel_docked_width;
int design_blocks_panel_float_width;
int design_blocks_panel_float_height;
};
struct AUTOPLACE_FIELDS
@ -269,6 +274,18 @@ public:
bool place_all_units;
};
struct PANEL_DESIGN_BLOCK_CHOOSER
{
int sash_pos_h;
int sash_pos_v;
int width;
int height;
int sort_mode;
bool repeated_placement;
bool place_as_sheet;
bool keep_annotations;
};
struct DIALOG_IMPORT_GRAPHICS
{
bool interactive_placement;
@ -359,6 +376,8 @@ public:
PANEL_SYM_CHOOSER m_SymChooserPanel;
PANEL_DESIGN_BLOCK_CHOOSER m_DesignBlockChooserPanel;
DIALOG_IMPORT_GRAPHICS m_ImportGraphics;
SELECTION m_Selection;

View File

@ -614,93 +614,6 @@ bool SCH_EDIT_FRAME::OpenProjectFiles( const std::vector<wxString>& aFileSet, in
}
bool SCH_EDIT_FRAME::AppendSchematic()
{
SCH_SCREEN* screen = GetScreen();
wxCHECK( screen, false );
// open file chooser dialog
wxString path = wxPathOnly( Prj().GetProjectFullName() );
wxFileDialog dlg( this, _( "Insert Schematic" ), path, wxEmptyString,
FILEEXT::KiCadSchematicFileWildcard(), wxFD_OPEN | wxFD_FILE_MUST_EXIST );
if( dlg.ShowModal() == wxID_CANCEL )
return false;
return AddSheetAndUpdateDisplay( dlg.GetPath() );
}
bool SCH_EDIT_FRAME::AddSheetAndUpdateDisplay( const wxString aFullFileName )
{
SCH_COMMIT commit( m_toolManager );
EE_SELECTION_TOOL* selectionTool = m_toolManager->GetTool<EE_SELECTION_TOOL>();
selectionTool->ClearSelection();
// Mark all existing items on the screen so we don't select them after appending
for( EDA_ITEM* item : GetScreen()->Items() )
item->SetFlags( SKIP_STRUCT );
if( !LoadSheetFromFile( GetCurrentSheet().Last(), &GetCurrentSheet(), aFullFileName ) )
return false;
initScreenZoom();
SetSheetNumberAndCount();
SyncView();
OnModify();
HardRedraw(); // Full reinit of the current screen and the display.
bool selected = false;
// Select all new items
for( EDA_ITEM* item : GetScreen()->Items() )
{
if( !item->HasFlag( SKIP_STRUCT ) )
{
commit.Added( item, GetScreen() );
selectionTool->AddItemToSel( item, true );
selected = true;
if( item->Type() == SCH_LINE_T )
item->SetFlags( STARTPOINT | ENDPOINT );
}
else
item->ClearFlags( SKIP_STRUCT );
}
if( selected )
m_toolManager->ProcessEvent( EVENTS::SelectedEvent );
// Start moving selection, cancel undoes the insertion
if( !m_toolManager->RunSynchronousAction( EE_ACTIONS::move, &commit ) )
commit.Revert();
else
commit.Push( _( "Import Schematic Sheet Content..." ) );
UpdateHierarchyNavigator();
return true;
}
void SCH_EDIT_FRAME::OnAppendProject( wxCommandEvent& event )
{
if( GetScreen() && GetScreen()->IsModified() )
{
wxString msg = _( "This operation cannot be undone.\n\n"
"Do you want to save the current document before proceeding?" );
if( IsOK( this, msg ) )
SaveProject();
}
AppendSchematic();
}
void SCH_EDIT_FRAME::OnImportProject( wxCommandEvent& aEvent )
{
if( Schematic().RootScreen() && !Schematic().RootScreen()->Items().empty() )
@ -840,6 +753,19 @@ bool SCH_EDIT_FRAME::saveSchematicFile( SCH_SHEET* aSheet, const wxString& aSave
// Write through symlinks, don't replace them
WX_FILENAME::ResolvePossibleSymlinks( schematicFileName );
if( !schematicFileName.DirExists() )
{
if( !wxMkdir( schematicFileName.GetPath() ) )
{
msg.Printf( _( "Error saving schematic file '%s'.\n%s" ),
schematicFileName.GetFullPath(),
"Could not create directory: %s" + schematicFileName.GetPath() );
DisplayError( this, msg );
return false;
}
}
if( !IsWritable( schematicFileName ) )
return false;

View File

@ -94,13 +94,6 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
fileMenu->AppendSeparator();
fileMenu->Add( _( "Insert Schematic Sheet Content..." ),
_( "Append schematic sheet content from another project to the current sheet" ),
ID_APPEND_PROJECT,
BITMAPS::add_document );
fileMenu->AppendSeparator();
// Import submenu
ACTION_MENU* submenuImport = new ACTION_MENU( false, selTool );
submenuImport->SetTitle( _( "Import" ) );
@ -207,6 +200,9 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
if( ADVANCED_CFG::GetCfg().m_IncrementalConnectivity )
viewMenu->Add( EE_ACTIONS::showNetNavigator, ACTION_MENU::CHECK );
if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks )
viewMenu->Add( ACTIONS::showLibraryTree, ACTION_MENU::CHECK, _( "Show Design Blocks" ) );
viewMenu->AppendSeparator();
viewMenu->Add( EE_ACTIONS::navigateBack );
viewMenu->Add( EE_ACTIONS::navigateUp );
@ -258,6 +254,7 @@ void SCH_EDIT_FRAME::doReCreateMenuBar()
placeMenu->Add( EE_ACTIONS::drawSheet );
placeMenu->Add( EE_ACTIONS::placeSheetPin );
placeMenu->Add( EE_ACTIONS::syncAllSheetsPins );
placeMenu->Add( EE_ACTIONS::importSheet );
placeMenu->AppendSeparator();
placeMenu->Add( EE_ACTIONS::placeSchematicText );

View File

@ -40,8 +40,10 @@
#include <preview_items/selection_area.h>
#include <project_sch.h>
#include <symbol_library.h>
#include <sch_base_frame.h>
#include <symbol_lib_table.h>
#include <sch_base_frame.h>
#include <design_block.h>
#include <design_block_lib_table.h>
#include <tool/action_toolbar.h>
#include <tool/tool_manager.h>
#include <tool/tool_dispatcher.h>
@ -55,6 +57,7 @@
#include <navlib/nl_schematic_plugin.h>
LIB_SYMBOL* SchGetLibSymbol( const LIB_ID& aLibId, SYMBOL_LIB_TABLE* aLibTable,
SYMBOL_LIB* aCacheLib, wxWindow* aParent, bool aShowErrorMsg )
{

View File

@ -146,6 +146,7 @@ public:
void UpdateStatusBar() override;
/**
* Call the library viewer to select symbol to import into schematic.
* If the library viewer is currently running, it is closed and reopened in modal mode.

View File

@ -34,6 +34,7 @@
#include <dialogs/dialog_schematic_find.h>
#include <dialogs/dialog_book_reporter.h>
#include <dialogs/dialog_symbol_fields_table.h>
#include <widgets/design_block_pane.h>
#include <eeschema_id.h>
#include <executable_names.h>
#include <gal/graphics_abstraction_layer.h>
@ -77,6 +78,7 @@
#include <tools/ee_grid_helper.h>
#include <tools/ee_inspection_tool.h>
#include <tools/ee_point_editor.h>
#include <tools/sch_design_block_control.h>
#include <tools/sch_drawing_tools.h>
#include <tools/sch_edit_tool.h>
#include <tools/sch_edit_table_tool.h>
@ -119,7 +121,6 @@ BEGIN_EVENT_TABLE( SCH_EDIT_FRAME, SCH_BASE_FRAME )
EVT_MENU_RANGE( ID_FILE1, ID_FILEMAX, SCH_EDIT_FRAME::OnLoadFile )
EVT_MENU( ID_FILE_LIST_CLEAR, SCH_EDIT_FRAME::OnClearFileHistory )
EVT_MENU( ID_APPEND_PROJECT, SCH_EDIT_FRAME::OnAppendProject )
EVT_MENU( ID_IMPORT_NON_KICAD_SCH, SCH_EDIT_FRAME::OnImportProject )
EVT_MENU( wxID_EXIT, SCH_EDIT_FRAME::OnExit )
@ -141,7 +142,8 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_diffSymbolDialog( nullptr ),
m_symbolFieldsTableDialog( nullptr ),
m_netNavigator( nullptr ),
m_highlightedConnChanged( false )
m_highlightedConnChanged( false ),
m_designBlocksPane( nullptr )
{
m_maximizeByDefault = true;
m_schematic = new SCHEMATIC( nullptr );
@ -208,6 +210,7 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_propertiesPanel->SetSplitterProportion( eeconfig()->m_AuiPanels.properties_splitter );
m_selectionFilterPanel = new PANEL_SCH_SELECTION_FILTER( this );
m_designBlocksPane = new DESIGN_BLOCK_PANE( this, nullptr, m_designBlockHistoryList );
m_auimgr.SetManagedWindow( this );
@ -236,6 +239,8 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
m_auimgr.AddPane( m_propertiesPanel, defaultPropertiesPaneInfo( this ) );
m_auimgr.AddPane( m_selectionFilterPanel, defaultSchSelectionFilterPaneInfo( this ) );
m_auimgr.AddPane( m_designBlocksPane, defaultDesignBlocksPaneInfo( this ) );
m_auimgr.AddPane( createHighlightedNetNavigator(), defaultNetNavigatorPaneInfo() );
m_auimgr.AddPane( m_optionsToolBar, EDA_PANE().VToolbar().Name( wxS( "OptToolbar" ) )
@ -274,11 +279,13 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
wxAuiPaneInfo& netNavigatorPane = m_auimgr.GetPane( NetNavigatorPaneName() );
wxAuiPaneInfo& propertiesPane = m_auimgr.GetPane( PropertiesPaneName() );
wxAuiPaneInfo& selectionFilterPane = m_auimgr.GetPane( wxS( "SelectionFilter" ) );
wxAuiPaneInfo& designBlocksPane = m_auimgr.GetPane( DesignBlocksPaneName() );
EESCHEMA_SETTINGS* cfg = eeconfig();
hierarchy_pane.Show( cfg->m_AuiPanels.show_schematic_hierarchy );
netNavigatorPane.Show( cfg->m_AuiPanels.show_net_nav_panel );
propertiesPane.Show( cfg->m_AuiPanels.show_properties );
designBlocksPane.Show( cfg->m_AuiPanels.design_blocks_show );
updateSelectionFilterVisbility();
// The selection filter doesn't need to grow in the vertical direction when docked
@ -326,6 +333,10 @@ SCH_EDIT_FRAME::SCH_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
if( cfg->m_AuiPanels.float_net_nav_panel )
netNavigatorPane.Float();
if( cfg->m_AuiPanels.design_blocks_show )
SetAuiPaneSize( m_auimgr, designBlocksPane,
cfg->m_AuiPanels.design_blocks_panel_docked_width, -1 );
if( cfg->m_AuiPanels.hierarchy_panel_docked_width > 0 )
{
// If the net navigator is not show, let the hierarchy navigator take all of the vertical
@ -522,6 +533,7 @@ void SCH_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new SCH_EDIT_TOOL );
m_toolManager->RegisterTool( new SCH_EDIT_TABLE_TOOL );
m_toolManager->RegisterTool( new EE_INSPECTION_TOOL );
m_toolManager->RegisterTool( new SCH_DESIGN_BLOCK_CONTROL );
m_toolManager->RegisterTool( new SCH_EDITOR_CONTROL );
m_toolManager->RegisterTool( new SCH_FIND_REPLACE_TOOL );
m_toolManager->RegisterTool( new EE_POINT_EDITOR );
@ -577,6 +589,12 @@ void SCH_EDIT_FRAME::setupUIConditions()
return m_auimgr.GetPane( NetNavigatorPaneName() ).IsShown();
};
auto designBlockCond =
[ this ] (const SELECTION& aSel )
{
return m_auimgr.GetPane( DesignBlocksPaneName() ).IsShown();
};
auto undoCond =
[ this ] (const SELECTION& aSel )
{
@ -597,6 +615,7 @@ void SCH_EDIT_FRAME::setupUIConditions()
mgr->SetConditions( EE_ACTIONS::showHierarchy, CHECK( hierarchyNavigatorCond ) );
mgr->SetConditions( EE_ACTIONS::showNetNavigator, CHECK( netNavigatorCond ) );
mgr->SetConditions( ACTIONS::showProperties, CHECK( propertiesCond ) );
mgr->SetConditions( ACTIONS::showLibraryTree, CHECK( designBlockCond ) );
mgr->SetConditions( ACTIONS::toggleGrid, CHECK( cond.GridVisible() ) );
mgr->SetConditions( ACTIONS::toggleGridOverrides, CHECK( cond.GridOverrides() ) );
mgr->SetConditions( ACTIONS::toggleCursorStyle, CHECK( cond.FullscreenCursor() ) );
@ -745,12 +764,16 @@ void SCH_EDIT_FRAME::setupUIConditions()
mgr->SetConditions( EE_ACTIONS::toggleAnnotateAuto, CHECK( showAnnotateAutomaticallyCond ) );
mgr->SetConditions( ACTIONS::toggleBoundingBoxes, CHECK( cond.BoundingBoxes() ) );
mgr->SetConditions( EE_ACTIONS::saveSheetAsDesignBlock, ENABLE( hasElements ) );
mgr->SetConditions( EE_ACTIONS::saveSelectionAsDesignBlock, ENABLE( SELECTION_CONDITIONS::NotEmpty ) );
#define CURRENT_TOOL( action ) mgr->SetConditions( action, CHECK( cond.CurrentTool( action ) ) )
CURRENT_TOOL( ACTIONS::deleteTool );
CURRENT_TOOL( EE_ACTIONS::highlightNetTool );
CURRENT_TOOL( EE_ACTIONS::placeSymbol );
CURRENT_TOOL( EE_ACTIONS::placePower );
CURRENT_TOOL( EE_ACTIONS::placeDesignBlock );
CURRENT_TOOL( EE_ACTIONS::drawWire );
CURRENT_TOOL( EE_ACTIONS::drawBus );
CURRENT_TOOL( EE_ACTIONS::placeBusWireEntry );
@ -764,6 +787,7 @@ void SCH_EDIT_FRAME::setupUIConditions()
CURRENT_TOOL( EE_ACTIONS::drawSheet );
CURRENT_TOOL( EE_ACTIONS::placeSheetPin );
CURRENT_TOOL( EE_ACTIONS::syncSheetPins );
CURRENT_TOOL( EE_ACTIONS::drawSheetCopy );
CURRENT_TOOL( EE_ACTIONS::drawRectangle );
CURRENT_TOOL( EE_ACTIONS::drawCircle );
CURRENT_TOOL( EE_ACTIONS::drawArc );

View File

@ -56,6 +56,8 @@ class SCH_FIELD;
class SCH_JUNCTION;
class SCHEMATIC;
class SCH_COMMIT;
class DESIGN_BLOCK;
class DESIGN_BLOCK_PANE;
class DIALOG_BOOK_REPORTER;
class DIALOG_ERC;
class DIALOG_SYMBOL_FIELDS_TABLE;
@ -262,6 +264,11 @@ public:
*/
void OnFindDialogClose();
/**
* Design block panel options have changed and the panel needs to be refreshed.
*/
void UpdateDesignBlockOptions();
/**
* Break a single segment into two at the specified point.
* @param aCommit Transaction container used to record changes for undo/redo
@ -484,23 +491,6 @@ public:
wxString GetCurrentFileName() const override;
/**
* Import a KiCad schematic into the current sheet.
*
* @return True if the schematic was imported properly.
*/
bool AppendSchematic();
/**
* Add a sheet file into the current sheet and updates display
*
* @note Used in AppendSchematic() and SCH_EDIT_TOOL::ddAppendFile() (so it is public)
*
* @param aFullFileName Path and name of sheet
* @return True if the sheet was properly added
*/
bool AddSheetAndUpdateDisplay( const wxString aFullFileName );
/**
* Check if any of the screens has unsaved changes and asks the user whether to save or
* drop them.
@ -636,11 +626,16 @@ public:
* possible file recursion issues.
* @param aFileName is the file name to load. The file name is expected to have an absolute
* path.
*
* @param aSkipRecursionCheck is true to skip the recursion check. This is used when loading
* a schematic sheet that is not part of the current project. If we are placing
* sheet contents instead of a sheet, then we do not need to check for recursion.
* @param aSkipLibCheck is true to skip the new/duplicate lib check. This is always triggered when
* placing design blocks so it is not necessary to check for new/duplicate libs.
* @return True if the schematic was imported properly.
*/
bool LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurrentSheet,
const wxString& aFileName );
const wxString& aFileName, bool aSkipRecursionCheck = false,
bool aSkipLibCheck = false );
/**
* Removes a given junction and heals any wire segments under the junction
@ -747,6 +742,51 @@ public:
*/
bool CreateArchiveLibrary( const wxString& aFileName );
/**
* If a library name is given, creates a new design block library in the project folder
* with the given name. If no library name is given it prompts user for a library path,
* then creates a new design block library at that location.
* If library exists, user is warned about that, and is given a chance
* to abort the new creation, and in that case existing library is first deleted.
*
* @param aProposedName is the initial path and filename shown in the file chooser dialog.
* @return The newly created library path if library was successfully created, else
* wxEmptyString because user aborted or error.
*/
wxString CreateNewDesignBlockLibrary( const wxString& aLibName = wxEmptyString,
const wxString& aProposedName = wxEmptyString );
/**
* Add an existing library to either the global or project library table.
*
* @param aFileName the library to add; a file open dialog will be displayed if empty.
* @return true if successfully added.
*/
bool AddDesignBlockLibrary( const wxString& aFilename, DESIGN_BLOCK_LIB_TABLE* aTable );
void SaveSheetAsDesignBlock( const wxString& aLibraryName );
void SaveSelectionAsDesignBlock( const wxString& aLibraryName );
bool DeleteDesignBlockLibrary( const wxString& aLibName, bool aConfirm );
bool DeleteDesignBlockFromLibrary( const LIB_ID& aLibId, bool aConfirm );
/**
* Load design block from design block library table.
*
* @param aLibId is the design block library identifier to load.
* @param aUseCacheLib set to true to fall back to cache library if design block is not found in
* design block library table.
* @param aShowErrorMessage set to true to show any error messages.
* @return The design block found in the library or NULL if the design block was not found.
*/
DESIGN_BLOCK* GetDesignBlock( const LIB_ID& aLibId, bool aUseCacheLib = false,
bool aShowErrorMsg = false );
DESIGN_BLOCK_PANE* GetDesignBlockPane() const { return m_designBlocksPane; }
/**
* Plot or print the current sheet to the clipboard.
*/
@ -840,6 +880,8 @@ public:
void ToggleProperties() override;
void ToggleLibraryTree() override;
DIALOG_BOOK_REPORTER* GetSymbolDiffDialog();
DIALOG_ERC* GetErcDialog();
@ -934,6 +976,20 @@ protected:
void onPluginAvailabilityChanged( wxCommandEvent& aEvt );
#endif
/**
* Prompts a user to select global or project library tables
*
* @return Pointer to library table selected or nullptr if none selected/canceled
*/
DESIGN_BLOCK_LIB_TABLE* selectDesignBlockLibTable( bool aOptional = false );
/**
* Create a new library in the given table (presumed to be either the global or project
* library table).
*/
wxString createNewDesignBlockLibrary( const wxString& aLibName, const wxString& aProposedName,
DESIGN_BLOCK_LIB_TABLE* aTable );
private:
// Called when resizing the Hierarchy Navigator panel
void OnResizeHierarchyNavigator( wxSizeEvent& aEvent );
@ -946,7 +1002,6 @@ private:
void OnExit( wxCommandEvent& event );
void OnLoadFile( wxCommandEvent& event );
void OnAppendProject( wxCommandEvent& event );
void OnImportProject( wxCommandEvent& event );
void OnClearFileHistory( wxCommandEvent& aEvent );
@ -1048,6 +1103,10 @@ private:
std::vector<wxEvtHandler*> m_schematicChangeListeners;
std::vector<LIB_ID> m_designBlockHistoryList;
DESIGN_BLOCK_PANE* m_designBlocksPane;
#ifdef KICAD_IPC_API
std::unique_ptr<API_HANDLER_SCH> m_apiHandler;
#endif

View File

@ -2212,6 +2212,9 @@ wxString SCH_PAINTER::expandLibItemTextVars( const wxString& aSourceText,
std::function<bool( wxString* )> symbolResolver =
[&]( wxString* token ) -> bool
{
if( !m_schematic )
return false;
return aSymbolContext->ResolveTextVar( &m_schematic->CurrentSheet(), token );
};
@ -2244,7 +2247,7 @@ void SCH_PAINTER::draw( const SCH_SYMBOL* aSymbol, int aLayer )
// return;
}
int unit = aSymbol->GetUnitSelection( &m_schematic->CurrentSheet() );
int unit = m_schematic ? aSymbol->GetUnitSelection( &m_schematic->CurrentSheet() ) : 1;
int bodyStyle = aSymbol->GetBodyStyle();
// Use dummy symbol if the actual couldn't be found (or couldn't be locked).

View File

@ -164,7 +164,8 @@ void SCH_EDIT_FRAME::InitSheet( SCH_SHEET* aSheet, const wxString& aNewFilename
bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurrentSheet,
const wxString& aFileName )
const wxString& aFileName, bool aSkipRecursionCheck,
bool aSkipLibCheck )
{
wxASSERT( aSheet && aCurrentSheet );
@ -235,20 +236,18 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
// If the loaded schematic is in a different folder from the current project and
// it contains hierarchical sheets, the hierarchical sheet paths need to be updated.
if( fileName.GetPathWithSep() != Prj().GetProjectPath() && tmpSheet->CountSheets() )
//
// Additionally, we need to make all backing screens absolute paths be in the current project
// path not the source path.
if( fileName.GetPathWithSep() != Prj().GetProjectPath() )
{
SCH_SHEET_LIST loadedSheets( tmpSheet.get() );
for( const SCH_SHEET_PATH& sheetPath : loadedSheets )
{
// Skip the loaded sheet since the user already determined if the file path should
// be relative or absolute.
if( sheetPath.size() == 1 )
continue;
wxString lastSheetPath = Prj().GetProjectPath();
wxString lastSheetPath = fileName.GetPathWithSep();
for( unsigned i = 1; i < sheetPath.size(); i++ )
for( unsigned i = 0; i < sheetPath.size(); i++ )
{
SCH_SHEET* sheet = sheetPath.at( i );
wxCHECK2( sheet, continue );
@ -256,6 +255,14 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
SCH_SCREEN* screen = sheet->GetScreen();
wxCHECK2( screen, continue );
// Fix screen path to be based on the current project path.
// Basically, make an absolute screen path relative to the schematic file
// we started with, then make it absolute again using the current project path.
wxFileName screenFileName = screen->GetFileName();
screenFileName.MakeRelativeTo( fileName.GetPath() );
screenFileName.MakeAbsolute( Prj().GetProjectPath() );
screen->SetFileName( screenFileName.GetFullPath() );
// Use the screen file name which should always be absolute.
wxFileName loadedSheetFileName = screen->GetFileName();
wxCHECK2( loadedSheetFileName.IsAbsolute(), continue );
@ -268,7 +275,6 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
else
sheetFileName = loadedSheetFileName.GetFullPath();
sheetFileName.Replace( wxT( "\\" ), wxT( "/" ) );
sheet->SetFileName( sheetFileName );
lastSheetPath = loadedSheetFileName.GetPath();
}
@ -279,11 +285,11 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
SCH_SHEET_LIST schematicSheets = Schematic().BuildUnorderedSheetList();
// Make sure any new sheet changes do not cause any recursion issues.
if( CheckSheetForRecursion( tmpSheet.get(), aCurrentSheet )
|| checkForNoFullyDefinedLibIds( tmpSheet.get() ) )
{
if( !aSkipRecursionCheck && CheckSheetForRecursion( tmpSheet.get(), aCurrentSheet ) )
return false;
if( checkForNoFullyDefinedLibIds( tmpSheet.get() ) )
return false;
}
// Make a valiant attempt to warn the user of all possible scenarios where there could
// be broken symbol library links.
@ -372,7 +378,7 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
// If there are any new or duplicate libraries, check to see if it's possible that
// there could be any missing libraries that would cause broken symbol library links.
if( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() )
if( !aSkipLibCheck && ( !newLibNames.IsEmpty() || !duplicateLibNames.IsEmpty() ) )
{
if( !symLibTableFn.Exists() || !symLibTableFn.IsFileReadable() )
{
@ -381,8 +387,8 @@ bool SCH_EDIT_FRAME::LoadSheetFromFile( SCH_SHEET* aSheet, SCH_SHEET_PATH* aCurr
"incorrect symbol library references.\n\n"
"Do you wish to continue?" );
wxMessageDialog msgDlg4( this, msg, _( "Continue Load Schematic" ),
wxOK | wxCANCEL | wxCANCEL_DEFAULT |
wxCENTER | wxICON_QUESTION );
wxOK | wxCANCEL | wxCANCEL_DEFAULT | wxCENTER
| wxICON_QUESTION );
msgDlg4.SetOKCancelLabels( okButtonLabel, cancelButtonLabel );
if( msgDlg4.ShowModal() == wxID_CANCEL )

View File

@ -64,7 +64,7 @@ protected:
*/
SYMBOL_TREE_MODEL_ADAPTER( EDA_BASE_FRAME* aParent, LIB_TABLE* aLibs );
bool isSymbolModel() override { return true; }
PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::SYMBOL_LIB; }
private:
friend class SYMBOL_ASYNC_LOADER;

View File

@ -66,7 +66,7 @@ protected:
SYMBOL_TREE_SYNCHRONIZING_ADAPTER( SYMBOL_EDIT_FRAME* aParent,
SYMBOL_LIBRARY_MANAGER* aLibMgr );
bool isSymbolModel() override { return true; }
PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::SYMBOL_LIB; }
protected:
SYMBOL_EDIT_FRAME* m_frame;

View File

@ -37,6 +37,7 @@
#include <tool/action_toolbar.h>
#include <tools/ee_actions.h>
#include <tools/ee_selection_tool.h>
#include <widgets/design_block_pane.h>
#include <widgets/hierarchy_pane.h>
#include <widgets/wx_aui_utils.h>
#include <widgets/sch_properties_panel.h>
@ -355,3 +356,45 @@ void SCH_EDIT_FRAME::ToggleSchematicHierarchy()
m_auimgr.Update();
}
}
void SCH_EDIT_FRAME::ToggleLibraryTree()
{
EESCHEMA_SETTINGS* cfg = eeconfig();
wxCHECK( cfg, /* void */ );
wxAuiPaneInfo& db_library_pane = m_auimgr.GetPane( DesignBlocksPaneName() );
db_library_pane.Show( !db_library_pane.IsShown() );
if( db_library_pane.IsShown() )
{
if( db_library_pane.IsFloating() )
{
db_library_pane.FloatingSize( cfg->m_AuiPanels.design_blocks_panel_float_width,
cfg->m_AuiPanels.design_blocks_panel_float_height );
m_auimgr.Update();
}
else if( cfg->m_AuiPanels.design_blocks_panel_docked_width > 0 )
{
// SetAuiPaneSize also updates m_auimgr
SetAuiPaneSize( m_auimgr, db_library_pane,
cfg->m_AuiPanels.design_blocks_panel_docked_width, -1 );
}
}
else
{
if( db_library_pane.IsFloating() )
{
cfg->m_AuiPanels.design_blocks_panel_float_width = db_library_pane.floating_size.x;
cfg->m_AuiPanels.design_blocks_panel_float_height = db_library_pane.floating_size.y;
}
else
{
cfg->m_AuiPanels.design_blocks_panel_docked_width = m_designBlocksPane->GetSize().x;
}
m_auimgr.Update();
}
}

View File

@ -149,6 +149,28 @@ TOOL_ACTION EE_ACTIONS::syncSelection( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveSelection.SyncSelection" )
.Scope( AS_GLOBAL ) );
// SCH_DESIGN_BLOCK_CONTROL
TOOL_ACTION EE_ACTIONS::saveSheetAsDesignBlock( TOOL_ACTION_ARGS()
.Name( "eeschema.SchDesignBlockControl.saveSheetAsDesignBlock" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Save Sheet as Design Block..." ) )
.Tooltip( _( "Create a new design block from the current sheet" ) )
.Icon( BITMAPS::new_component ) );
TOOL_ACTION EE_ACTIONS::saveSelectionAsDesignBlock( TOOL_ACTION_ARGS()
.Name( "eeschema.SchDesignBlockControl.saveSelectionAsDesignBlock" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Save Selection as Design Block..." ) )
.Tooltip( _( "Create a new design block from the current selection" ) )
.Icon( BITMAPS::new_component ) );
TOOL_ACTION EE_ACTIONS::deleteDesignBlock( TOOL_ACTION_ARGS()
.Name( "eeschema.SchDesignBlockControl.saveDeleteDesignBlock" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Delete Design Block" ) )
.Tooltip( _( "Remove the selected design block from its library" ) )
.Icon( BITMAPS::trash ) );
// SYMBOL_EDITOR_CONTROL
//
TOOL_ACTION EE_ACTIONS::saveLibraryAs( TOOL_ACTION_ARGS()
@ -399,6 +421,17 @@ TOOL_ACTION EE_ACTIONS::placePower( TOOL_ACTION_ARGS()
.Flags( AF_ACTIVATE )
.Parameter<SCH_SYMBOL*>( nullptr ) );
TOOL_ACTION EE_ACTIONS::placeDesignBlock( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placeDesignBlock" )
.Scope( AS_GLOBAL )
.DefaultHotkey( MD_SHIFT + 'B' )
.FriendlyName( _( "Place Design Block" ) )
.Tooltip( _( "Add selected design block to current sheet" ) )
.Icon( BITMAPS::add_component )
.Flags( AF_ACTIVATE )
.Parameter<DESIGN_BLOCK*>( nullptr ) );
TOOL_ACTION EE_ACTIONS::placeNoConnect( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placeNoConnect" )
.Scope( AS_GLOBAL )
@ -464,6 +497,15 @@ TOOL_ACTION EE_ACTIONS::drawSheet( TOOL_ACTION_ARGS()
.Flags( AF_ACTIVATE )
.Parameter( SCH_SHEET_T ) );
TOOL_ACTION EE_ACTIONS::drawSheetCopy( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.drawSheetCopy" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Draw Sheet Copy" ) )
.Tooltip( _( "Copy sheet into project and draw on current sheet" ) )
.Icon( BITMAPS::add_hierarchical_subsheet )
.Flags( AF_ACTIVATE )
.Parameter<wxString*> ( nullptr ) );
TOOL_ACTION EE_ACTIONS::placeSheetPin( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placeSheetPin" )
.Scope( AS_GLOBAL )
@ -487,6 +529,15 @@ TOOL_ACTION EE_ACTIONS::syncAllSheetsPins( TOOL_ACTION_ARGS()
.Icon( BITMAPS::import_hierarchical_label )
.Flags( AF_ACTIVATE ) );
TOOL_ACTION EE_ACTIONS::importSheet( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.importSheet" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Import Sheet" ) )
.Tooltip( _( "Import sheet into project" ) )
.Icon( BITMAPS::add_hierarchical_subsheet )
.Flags( AF_ACTIVATE )
.Parameter<wxString*> ( nullptr ) );
TOOL_ACTION EE_ACTIONS::placeGlobalLabel( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveDrawing.placeGlobalLabel" )
.Scope( AS_GLOBAL )

View File

@ -78,6 +78,7 @@ public:
static TOOL_ACTION pickerTool;
static TOOL_ACTION placeSymbol;
static TOOL_ACTION placePower;
static TOOL_ACTION placeDesignBlock;
static TOOL_ACTION drawWire;
static TOOL_ACTION drawBus;
static TOOL_ACTION unfoldBus;
@ -89,7 +90,9 @@ public:
static TOOL_ACTION placeGlobalLabel;
static TOOL_ACTION placeHierLabel;
static TOOL_ACTION drawSheet;
static TOOL_ACTION drawSheetCopy;
static TOOL_ACTION placeSheetPin;
static TOOL_ACTION importSheet;
// Sync sheet pins for selected sheet symbol
static TOOL_ACTION syncSheetPins;
// Sync sheet pins for all sheet symbols
@ -197,6 +200,11 @@ public:
static TOOL_ACTION unsetDNP;
static TOOL_ACTION toggleDNP;
// Design Block management
static TOOL_ACTION saveSheetAsDesignBlock;
static TOOL_ACTION saveSelectionAsDesignBlock;
static TOOL_ACTION deleteDesignBlock;
// Library management
static TOOL_ACTION saveLibraryAs;
static TOOL_ACTION saveSymbolCopyAs;

View File

@ -0,0 +1,204 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-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 <tool/library_editor_control.h>
#include <wildcards_and_files_ext.h>
#include <bitmaps/bitmap_types.h>
#include <confirm.h>
#include <kidialog.h>
#include <gestfich.h> // To open with a text editor
#include <wx/filedlg.h>
#include <sch_design_block_control.h>
#include <design_block_pane.h>
#include <panel_design_block_chooser.h>
#include <ee_actions.h>
bool SCH_DESIGN_BLOCK_CONTROL::Init()
{
m_editFrame = getEditFrame<SCH_EDIT_FRAME>();
m_frame = m_editFrame;
m_selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
auto pinnedLib =
[this]( const SELECTION& aSel )
{
//
LIB_TREE_NODE* current = getCurrentTreeNode();
return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
&& current->m_Pinned;
};
auto unpinnedLib =
[this](const SELECTION& aSel )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
return current && current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
&& !current->m_Pinned;
};
auto isInLibrary =
[this](const SELECTION& aSel )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
return current
&& ( current->m_Type == LIB_TREE_NODE::TYPE::LIBRARY
|| current->m_Type == LIB_TREE_NODE::TYPE::ITEM );
};
auto isDesignBlock =
[this](const SELECTION& aSel )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
return current && current->m_Type == LIB_TREE_NODE::TYPE::ITEM;
};
CONDITIONAL_MENU& ctxMenu = m_menu->GetMenu();
ctxMenu.AddItem( EE_ACTIONS::placeDesignBlock, isDesignBlock, 1);
ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLib, 1 );
ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLib, 1 );
ctxMenu.AddSeparator( 1 );
ctxMenu.AddItem( ACTIONS::newLibrary, SELECTION_CONDITIONS::ShowAlways, 10 );
ctxMenu.AddItem( EE_ACTIONS::saveSheetAsDesignBlock, isInLibrary, 10 );
ctxMenu.AddItem( EE_ACTIONS::saveSelectionAsDesignBlock, isInLibrary, 10 );
ctxMenu.AddItem( EE_ACTIONS::deleteDesignBlock, isDesignBlock, 10 );
ctxMenu.AddSeparator( 400 );
ctxMenu.AddItem( ACTIONS::hideLibraryTree, SELECTION_CONDITIONS::ShowAlways, 400 );
return true;
}
int SCH_DESIGN_BLOCK_CONTROL::PinLibrary( const TOOL_EVENT& aEvent )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
if( current && !current->m_Pinned )
{
m_frame->Prj().PinLibrary( current->m_LibId.GetLibNickname(),
PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
current->m_Pinned = true;
getDesignBlockPane()->RefreshLibs();
}
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary( const TOOL_EVENT& aEvent )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
if( current && current->m_Pinned )
{
m_frame->Prj().UnpinLibrary( current->m_LibId.GetLibNickname(),
PROJECT::LIB_TYPE_T::DESIGN_BLOCK_LIB );
current->m_Pinned = false;
getDesignBlockPane()->RefreshLibs();
}
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::NewLibrary( const TOOL_EVENT& aEvent )
{
m_editFrame->CreateNewDesignBlockLibrary();
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
if( current )
m_editFrame->SaveSheetAsDesignBlock( current->m_LibId.GetLibNickname() );
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
if( current )
m_editFrame->SaveSelectionAsDesignBlock( current->m_LibId.GetLibNickname() );
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock( const TOOL_EVENT& aEvent )
{
LIB_TREE_NODE* current = getCurrentTreeNode();
if( current )
m_editFrame->DeleteDesignBlockFromLibrary( current->m_LibId, true );
return 0;
}
int SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree( const TOOL_EVENT& aEvent )
{
m_editFrame->ToggleLibraryTree();
return 0;
}
void SCH_DESIGN_BLOCK_CONTROL::setTransitions()
{
Go( &SCH_DESIGN_BLOCK_CONTROL::PinLibrary, ACTIONS::pinLibrary.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::UnpinLibrary, ACTIONS::unpinLibrary.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::NewLibrary, ACTIONS::newLibrary.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSheetAsDesignBlock, EE_ACTIONS::saveSheetAsDesignBlock.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::SaveSelectionAsDesignBlock, EE_ACTIONS::saveSelectionAsDesignBlock.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::DeleteDesignBlock, EE_ACTIONS::deleteDesignBlock.MakeEvent() );
Go( &SCH_DESIGN_BLOCK_CONTROL::HideLibraryTree, ACTIONS::hideLibraryTree.MakeEvent() );
}
LIB_ID SCH_DESIGN_BLOCK_CONTROL::getSelectedLibId()
{
m_editFrame->GetDesignBlockPane()->GetSelectedLibId();
return LIB_ID();
}
DESIGN_BLOCK_PANE* SCH_DESIGN_BLOCK_CONTROL::getDesignBlockPane()
{
return m_editFrame->GetDesignBlockPane();
}
LIB_TREE_NODE* SCH_DESIGN_BLOCK_CONTROL::getCurrentTreeNode()
{
LIB_TREE* libTree = getDesignBlockPane()->GetDesignBlockPanel()->GetLibTree();
return libTree ? libTree->GetCurrentTreeNode() : nullptr;
}

View File

@ -0,0 +1,66 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2019-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 SCH_DESIGN_BLOCK_CONTROL_H
#define SCH_DESIGN_BLOCK_CONTROL_H
#include <sch_base_frame.h>
#include <tools/ee_tool_base.h>
/**
* Handle schematic design block actions in the schematic editor.
*/
class SCH_DESIGN_BLOCK_CONTROL : public wxEvtHandler, public EE_TOOL_BASE<SCH_BASE_FRAME>
{
public:
SCH_DESIGN_BLOCK_CONTROL() : EE_TOOL_BASE<SCH_BASE_FRAME>( "eeschema.SchDesignBlockControl" ) {}
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
int PinLibrary( const TOOL_EVENT& aEvent );
int UnpinLibrary( const TOOL_EVENT& aEvent );
int NewLibrary( const TOOL_EVENT& aEvent );
int DeleteLibrary( const TOOL_EVENT& aEvent );
int SaveSheetAsDesignBlock( const TOOL_EVENT& aEvent );
int SaveSelectionAsDesignBlock( const TOOL_EVENT& aEvent );
int DeleteDesignBlock( const TOOL_EVENT& aEvent );
int HideLibraryTree( const TOOL_EVENT& aEvent );
private:
LIB_ID getSelectedLibId();
///< Set up handlers for various events.
void setTransitions() override;
DESIGN_BLOCK_PANE* getDesignBlockPane();
LIB_TREE_NODE* getCurrentTreeNode();
SCH_EDIT_FRAME* m_editFrame = nullptr;
};
#endif

View File

@ -34,9 +34,12 @@
#include <tools/ee_grid_helper.h>
#include <tools/rule_area_create_helper.h>
#include <gal/graphics_abstraction_layer.h>
#include <design_block_lib_table.h>
#include <ee_actions.h>
#include <sch_edit_frame.h>
#include <pgm_base.h>
#include <design_block.h>
#include <design_block_pane.h>
#include <eeschema_id.h>
#include <confirm.h>
#include <view/view_controls.h>
@ -541,6 +544,268 @@ int SCH_DRAWING_TOOLS::PlaceSymbol( const TOOL_EVENT& aEvent )
}
int SCH_DRAWING_TOOLS::ImportSheet( const TOOL_EVENT& aEvent )
{
bool placingDesignBlock = aEvent.IsAction( &EE_ACTIONS::placeDesignBlock );
DESIGN_BLOCK* designBlock =
placingDesignBlock && m_frame->GetDesignBlockPane()->GetSelectedLibId().IsValid()
? m_frame->GetDesignBlock( m_frame->GetDesignBlockPane()->GetSelectedLibId() )
: nullptr;
wxString* importSourceFile = !placingDesignBlock ? aEvent.Parameter<wxString*>() : nullptr;
wxString sheetFileName = wxEmptyString;
if( !placingDesignBlock )
{
if( importSourceFile != nullptr )
sheetFileName = *importSourceFile;
}
else if( designBlock )
sheetFileName = designBlock->GetSchematicFile();
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
SCHEMATIC_SETTINGS& schSettings = m_frame->Schematic().Settings();
SCH_SCREEN* screen = m_frame->GetScreen();
SCH_SHEET_PATH& sheetPath = m_frame->GetCurrentSheet();
KIGFX::VIEW_CONTROLS* controls = getViewControls();
EE_GRID_HELPER grid( m_toolMgr );
VECTOR2I cursorPos;
if( !cfg || !common_settings )
return 0;
if( m_inDrawingTool )
return 0;
auto setCursor =
[&]()
{
m_frame->GetCanvas()->SetCurrentCursor( designBlock ? KICURSOR::MOVING
: KICURSOR::COMPONENT );
};
auto placeSheetContents =
[&]()
{
SCH_COMMIT commit( m_toolMgr );
EE_SELECTION_TOOL* selectionTool = m_toolMgr->GetTool<EE_SELECTION_TOOL>();
EDA_ITEMS newItems;
bool keepAnnotations = cfg->m_DesignBlockChooserPanel.keep_annotations;
selectionTool->ClearSelection();
// Mark all existing items on the screen so we don't select them after appending
for( EDA_ITEM* item : screen->Items() )
item->SetFlags( SKIP_STRUCT );
if( !m_frame->LoadSheetFromFile( sheetPath.Last(), &sheetPath, sheetFileName, true,
placingDesignBlock ) )
return false;
m_frame->SetSheetNumberAndCount();
m_frame->SyncView();
m_frame->OnModify();
m_frame->HardRedraw(); // Full reinit of the current screen and the display.
// Select all new items
for( EDA_ITEM* item : screen->Items() )
{
if( !item->HasFlag( SKIP_STRUCT ) )
{
if( item->Type() == SCH_SYMBOL_T && !keepAnnotations )
static_cast<SCH_SYMBOL*>( item )->ClearAnnotation( &sheetPath, false );
if( item->Type() == SCH_LINE_T )
item->SetFlags( STARTPOINT | ENDPOINT );
commit.Added( item, screen );
newItems.emplace_back( item );
}
else
item->ClearFlags( SKIP_STRUCT );
}
selectionTool->AddItemsToSel( &newItems, true );
cursorPos = grid.Align( controls->GetMousePosition(),
grid.GetSelectionGrid( selectionTool->GetSelection() ) );
controls->ForceCursorPosition( true, cursorPos );
// Move everything to our current mouse position now
// that we have a selection to get a reference point
VECTOR2I anchorPos = selectionTool->GetSelection().GetReferencePoint();
VECTOR2I delta = cursorPos - anchorPos;
// Will all be SCH_ITEMs as these were pulled from the screen->Items()
for( EDA_ITEM* item : newItems )
static_cast<SCH_ITEM*>( item )->Move( delta );
if( !keepAnnotations )
{
EESCHEMA_SETTINGS::PANEL_ANNOTATE& annotate = cfg->m_AnnotatePanel;
if( annotate.automatic )
{
NULL_REPORTER reporter;
m_frame->AnnotateSymbols( &commit, ANNOTATE_SELECTION,
(ANNOTATE_ORDER_T) annotate.sort_order,
(ANNOTATE_ALGO_T) annotate.method, true /* recursive */,
schSettings.m_AnnotateStartNum, false, false, reporter );
}
// Annotation will clear selection, so we need to restore it
for( EDA_ITEM* item : newItems )
{
if( item->Type() == SCH_LINE_T )
item->SetFlags( STARTPOINT | ENDPOINT );
}
selectionTool->AddItemsToSel( &newItems, true );
}
// Start moving selection, cancel undoes the insertion
bool placed = m_toolMgr->RunSynchronousAction( EE_ACTIONS::move, &commit );
// Update our cursor position to the new location in case we're placing repeated copies
cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
if( placed )
commit.Push( placingDesignBlock ? _( "Add design block" )
: _( "Import Schematic Sheet Content..." ) );
else
commit.Revert();
m_frame->UpdateHierarchyNavigator();
return placed;
};
// Whether we are placing the sheet as a sheet, or as its contents, we need to get a filename
// if we weren't provided one
if( sheetFileName.IsEmpty() )
{
wxString path;
wxString file;
if (!placingDesignBlock) {
if( sheetFileName.IsEmpty() )
{
path = wxPathOnly( m_frame->Prj().GetProjectFullName() );
file = wxEmptyString;
}
else
{
path = wxPathOnly( sheetFileName );
file = wxFileName( sheetFileName ).GetFullName();
}
// Open file chooser dialog even if we have been provided a file so the user
// can select the options they want
wxFileDialog dlg( m_frame, _( "Choose Schematic" ), path, file,
FILEEXT::KiCadSchematicFileWildcard(),
wxFD_OPEN | wxFD_FILE_MUST_EXIST );
FILEDLG_IMPORT_SHEET_CONTENTS dlgHook( cfg );
dlg.SetCustomizeHook( dlgHook );
if( dlg.ShowModal() == wxID_CANCEL )
return 0;
sheetFileName = dlg.GetPath();
m_frame->UpdateDesignBlockOptions();
}
if( sheetFileName.IsEmpty() )
return 0;
}
// If we're placing sheet contents, we don't even want to run our tool loop, just add the items
// to the canvas and run the move tool
if( !cfg->m_DesignBlockChooserPanel.place_as_sheet )
{
while( placeSheetContents() && cfg->m_DesignBlockChooserPanel.repeated_placement )
;
m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
m_view->ClearPreview();
delete designBlock;
designBlock = nullptr;
return 0;
}
// We're placing a sheet as a sheet, we need to run a small tool loop to get the starting
// coordinate of the sheet drawing
m_frame->PushTool( aEvent );
Activate();
// Must be done after Activate() so that it gets set into the correct context
getViewControls()->ShowCursor( true );
// Set initial cursor
setCursor();
if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
{
m_toolMgr->PrimeTool( { 0, 0 } );
}
// Main loop: keep receiving events
while( TOOL_EVENT* evt = Wait() )
{
setCursor();
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
cursorPos = grid.Align( controls->GetMousePosition(), GRID_HELPER_GRIDS::GRID_CONNECTABLE );
controls->ForceCursorPosition( true, cursorPos );
// The tool hotkey is interpreted as a click when drawing
bool isSyntheticClick = designBlock && evt->IsActivate() && evt->HasPosition()
&& evt->Matches( aEvent );
if( evt->IsCancelInteractive() || ( designBlock && evt->IsAction( &ACTIONS::undo ) ) )
{
m_frame->GetInfoBar()->Dismiss();
break;
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) || isSyntheticClick )
{
wxString* tempFileName = new wxString( sheetFileName );
m_toolMgr->PostAction( EE_ACTIONS::drawSheetCopy, tempFileName );
break;
}
else if( evt->IsClick( BUT_RIGHT ) )
{
// Warp after context menu only if dragging...
if( !designBlock )
m_toolMgr->VetoContextMenuMouseWarp();
m_menu->ShowContextMenu( m_selectionTool->GetSelection() );
}
else if( evt->IsAction( &ACTIONS::duplicate )
|| evt->IsAction( &EE_ACTIONS::repeatDrawItem ) )
{
wxBell();
}
else
{
evt->SetPassEvent();
}
}
m_frame->PopTool( aEvent );
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
return 0;
}
int SCH_DRAWING_TOOLS::PlaceImage( const TOOL_EVENT& aEvent )
{
SCH_BITMAP* image = aEvent.Parameter<SCH_BITMAP*>();
@ -707,7 +972,7 @@ int SCH_DRAWING_TOOLS::PlaceImage( const TOOL_EVENT& aEvent )
if( !image || !image->ReadImageFile( fullFilename ) )
{
wxMessageBox( _( "Could not load image from '%s'." ), fullFilename );
wxMessageBox( wxString::Format( _( "Could not load image from '%s'." ), fullFilename ) );
delete image;
image = nullptr;
continue;
@ -2452,7 +2717,12 @@ int SCH_DRAWING_TOOLS::DrawTable( const TOOL_EVENT& aEvent )
int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
{
SCH_SHEET* sheet = nullptr;
bool isDrawSheetCopy = aEvent.IsAction( &EE_ACTIONS::drawSheetCopy );
wxString* filename = isDrawSheetCopy ? aEvent.Parameter<wxString*>() : nullptr;
SCH_SHEET* sheet = nullptr;
// Make sure we've been passed a filename if we're importing a sheet
wxCHECK( !isDrawSheetCopy || filename, 0 );
if( m_inDrawingTool )
return 0;
@ -2490,7 +2760,7 @@ int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
// Set initial cursor
setCursor();
if( aEvent.HasPosition() )
if( aEvent.HasPosition() && !isDrawSheetCopy )
m_toolMgr->PrimeTool( aEvent.Position() );
// Main loop: keep receiving events
@ -2575,16 +2845,57 @@ int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
m_toolMgr->RunAction( EE_ACTIONS::clearSelection );
sheet = new SCH_SHEET( m_frame->GetCurrentSheet().Last(), cursorPos );
if( isDrawSheetCopy )
{
if( !wxFileExists( *filename ) )
{
wxMessageBox( wxString::Format( _( "File '%s' does not exist." ), *filename ) );
m_frame->PopTool( aEvent );
break;
}
sheet = new SCH_SHEET( m_frame->GetCurrentSheet().Last(), cursorPos );
if( !m_frame->LoadSheetFromFile( sheet, &m_frame->GetCurrentSheet(), *filename ) )
{
wxMessageBox( wxString::Format( _( "Could not import sheet from '%s'." ), *filename ) );
delete sheet;
sheet = nullptr;
m_frame->PopTool( aEvent );
break;
}
wxFileName fn( *filename );
sheet->GetFields()[SHEETNAME].SetText( wxT( "Imported Sheet" ) );
sheet->GetFields()[SHEETFILENAME].SetText( fn.GetName() + wxT( "." )
+ FILEEXT::KiCadSchematicFileExtension );
}
else
{
sheet = new SCH_SHEET( m_frame->GetCurrentSheet().Last(), cursorPos );
sheet->SetScreen( nullptr );
sheet->GetFields()[SHEETNAME].SetText( wxT( "Untitled Sheet" ) );
sheet->GetFields()[SHEETFILENAME].SetText( wxT( "untitled." )
+ FILEEXT::KiCadSchematicFileExtension );
}
sheet->SetFlags( IS_NEW | IS_MOVING );
sheet->SetScreen( nullptr );
sheet->SetBorderWidth( schIUScale.MilsToIU( cfg->m_Drawing.default_line_thickness ) );
sheet->SetBorderColor( cfg->m_Drawing.default_sheet_border_color );
sheet->SetBackgroundColor( cfg->m_Drawing.default_sheet_background_color );
sheet->GetFields()[ SHEETNAME ].SetText( "Untitled Sheet" );
sheet->GetFields()[ SHEETFILENAME ].SetText( "untitled." + FILEEXT::KiCadSchematicFileExtension );
sizeSheet( sheet, cursorPos );
SCH_SHEET_LIST hierarchy = m_frame->Schematic().GetFullHierarchy();
SCH_SHEET_PATH instance = m_frame->GetCurrentSheet();
instance.push_back( sheet );
wxString pageNumber;
// Don't try to be too clever when assigning the next availabe page number. Just use
// the number of sheets plus one.
pageNumber.Printf( wxT( "%d" ), static_cast<int>( hierarchy.size() ) + 1 );
instance.SetPageNumber( pageNumber );
m_view->ClearPreview();
m_view->AddToPreview( sheet->Clone() );
}
@ -2603,9 +2914,12 @@ int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
sheet->AutoplaceFields( /* aScreen */ nullptr, /* aManual */ false );
SCH_COMMIT commit( m_toolMgr );
commit.Add( sheet, m_frame->GetScreen() );
commit.Push( "Draw Sheet" );
// Use the commit we were provided or make our own
SCH_COMMIT tempCommit = SCH_COMMIT( m_toolMgr );
SCH_COMMIT& c = evt->Commit() ? *( (SCH_COMMIT*) evt->Commit() ) : tempCommit;
c.Add( sheet, m_frame->GetScreen() );
c.Push( isDrawSheetCopy ? "Import Sheet Copy" : "Draw Sheet" );
SCH_SHEET_PATH newPath = m_frame->GetCurrentSheet();
newPath.push_back( sheet );
@ -2669,6 +2983,9 @@ int SCH_DRAWING_TOOLS::DrawSheet( const TOOL_EVENT& aEvent )
getViewControls()->CaptureCursor( false );
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
// We own the string if we're importing a sheet
delete filename;
return 0;
}
@ -2834,7 +3151,10 @@ void SCH_DRAWING_TOOLS::setTransitions()
Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeHierLabel.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeGlobalLabel.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::DrawSheet, EE_ACTIONS::drawSheet.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::DrawSheet, EE_ACTIONS::drawSheetCopy.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSheetPin.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::ImportSheet, EE_ACTIONS::placeDesignBlock.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::ImportSheet, EE_ACTIONS::importSheet.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::TwoClickPlace, EE_ACTIONS::placeSchematicText.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawRectangle.MakeEvent() );
Go( &SCH_DRAWING_TOOLS::DrawShape, EE_ACTIONS::drawCircle.MakeEvent() );

View File

@ -55,6 +55,7 @@ public:
int PlaceSymbol( const TOOL_EVENT& aEvent );
int SingleClickPlace( const TOOL_EVENT& aEvent );
int TwoClickPlace( const TOOL_EVENT& aEvent );
int ImportSheet( const TOOL_EVENT& aEvent );
int DrawShape( const TOOL_EVENT& aEvent );
int DrawRuleArea( const TOOL_EVENT& aEvent );
int DrawTable( const TOOL_EVENT& aEvent );
@ -83,6 +84,7 @@ private:
std::vector<PICKED_SYMBOL> m_symbolHistoryList;
std::vector<PICKED_SYMBOL> m_powerHistoryList;
std::vector<LIB_ID> m_designBlockHistoryList;
LABEL_FLAG_SHAPE m_lastSheetPinType;
LABEL_FLAG_SHAPE m_lastGlobalLabelShape;

View File

@ -2924,8 +2924,7 @@ int SCH_EDIT_TOOL::EditPageNumber( const TOOL_EVENT& aEvent )
int SCH_EDIT_TOOL::DdAppendFile( const TOOL_EVENT& aEvent )
{
wxString aFileName = *aEvent.Parameter<wxString*>();
return ( m_frame->AddSheetAndUpdateDisplay( aFileName ) ? 0 : 1 );
return m_toolMgr->RunAction( EE_ACTIONS::importSheet, aEvent.Parameter<wxString*>() );
}

View File

@ -2434,6 +2434,13 @@ int SCH_EDITOR_CONTROL::ToggleProperties( const TOOL_EVENT& aEvent )
}
int SCH_EDITOR_CONTROL::ToggleLibraryTree( const TOOL_EVENT& aEvent )
{
getEditFrame<SCH_EDIT_FRAME>()->ToggleLibraryTree();
return 0;
}
int SCH_EDITOR_CONTROL::ToggleHiddenPins( const TOOL_EVENT& aEvent )
{
EESCHEMA_SETTINGS* cfg = m_frame->eeconfig();
@ -2808,6 +2815,8 @@ void SCH_EDITOR_CONTROL::setTransitions()
Go( &SCH_EDITOR_CONTROL::ShowHierarchy, EE_ACTIONS::showHierarchy.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ShowNetNavigator, EE_ACTIONS::showNetNavigator.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ToggleProperties, ACTIONS::showProperties.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ToggleLibraryTree, ACTIONS::hideLibraryTree.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ToggleLibraryTree, ACTIONS::showLibraryTree.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ToggleHiddenPins, EE_ACTIONS::toggleHiddenPins.MakeEvent() );
Go( &SCH_EDITOR_CONTROL::ToggleHiddenFields, EE_ACTIONS::toggleHiddenFields.MakeEvent() );

View File

@ -130,6 +130,7 @@ public:
int ShowHierarchy( const TOOL_EVENT& aEvent );
int ShowNetNavigator( const TOOL_EVENT& aEvent );
int ToggleProperties( const TOOL_EVENT& aEvent );
int ToggleLibraryTree( const TOOL_EVENT& aEvent );
int ToggleHiddenPins( const TOOL_EVENT& aEvent );
int ToggleHiddenFields( const TOOL_EVENT& aEvent );

View File

@ -0,0 +1,157 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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 <design_block.h>
#include <widgets/design_block_pane.h>
#include <widgets/panel_design_block_chooser.h>
#include <eeschema_settings.h>
#include <kiface_base.h>
#include <sch_edit_frame.h>
#include <core/kicad_algo.h>
#include <template_fieldnames.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/sizer.h>
#include <confirm.h>
#include <wildcards_and_files_ext.h>
#include <ee_actions.h>
#include <tool/tool_manager.h>
static const wxString REPEATED_PLACEMENT = _( "Place repeated copies" );
static const wxString PLACE_AS_SHEET = _( "Place as sheet" );
static const wxString KEEP_ANNOTATIONS = _( "Keep annotations" );
DESIGN_BLOCK_PANE::DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
std::vector<LIB_ID>& aHistoryList ) :
WX_PANEL( aParent ), m_frame( aParent )
{
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
m_chooserPanel = new PANEL_DESIGN_BLOCK_CHOOSER( aParent, this, aHistoryList,
[aParent]()
{
aParent->GetToolManager()->RunAction(
EE_ACTIONS::placeDesignBlock );
} );
sizer->Add( m_chooserPanel, 1, wxEXPAND, 5 );
if( aPreselect && aPreselect->IsValid() )
m_chooserPanel->SetPreselect( *aPreselect );
SetName( wxT( "Design Blocks" ) );
wxBoxSizer* cbSizer = new wxBoxSizer( wxVERTICAL );
m_repeatedPlacement = new wxCheckBox( this, wxID_ANY, REPEATED_PLACEMENT );
m_repeatedPlacement->SetToolTip( _( "Place copies of the design block on subsequent clicks." ) );
m_placeAsSheet = new wxCheckBox( this, wxID_ANY, PLACE_AS_SHEET );
m_placeAsSheet->SetToolTip( _( "Place the design block as a new sheet." ) );
m_keepAnnotations = new wxCheckBox( this, wxID_ANY, KEEP_ANNOTATIONS );
m_keepAnnotations->SetToolTip( _( "Preserve reference designators in the source schematic. "
"Otherwise, clear then reannotate according to settings." ) );
UpdateCheckboxes();
// Set all checkbox handlers to the same function
m_repeatedPlacement->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
m_placeAsSheet->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
m_keepAnnotations->Bind( wxEVT_CHECKBOX, &DESIGN_BLOCK_PANE::OnCheckBox, this );
cbSizer->Add( m_repeatedPlacement, 0, wxLEFT, 5 );
cbSizer->Add( m_placeAsSheet, 0, wxLEFT, 5 );
cbSizer->Add( m_keepAnnotations, 0, wxLEFT, 5 );
sizer->Add( cbSizer, 0, wxEXPAND | wxLEFT, 5 );
SetSizer( sizer );
m_chooserPanel->FinishSetup();
Layout();
Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, m_chooserPanel );
}
void DESIGN_BLOCK_PANE::OnCheckBox( wxCommandEvent& aEvent )
{
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
{
cfg->m_DesignBlockChooserPanel.repeated_placement = m_repeatedPlacement->GetValue();
cfg->m_DesignBlockChooserPanel.place_as_sheet = m_placeAsSheet->GetValue();
cfg->m_DesignBlockChooserPanel.keep_annotations = m_keepAnnotations->GetValue();
}
}
void DESIGN_BLOCK_PANE::UpdateCheckboxes()
{
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
{
m_repeatedPlacement->SetValue( cfg->m_DesignBlockChooserPanel.repeated_placement );
m_placeAsSheet->SetValue( cfg->m_DesignBlockChooserPanel.place_as_sheet );
m_keepAnnotations->SetValue( cfg->m_DesignBlockChooserPanel.keep_annotations );
}
}
LIB_ID DESIGN_BLOCK_PANE::GetSelectedLibId( int* aUnit ) const
{
return m_chooserPanel->GetSelectedLibId( aUnit );
}
void DESIGN_BLOCK_PANE::SelectLibId( const LIB_ID& aLibId )
{
m_chooserPanel->SelectLibId( aLibId );
}
void DESIGN_BLOCK_PANE::RefreshLibs()
{
m_chooserPanel->RefreshLibs();
}
FILEDLG_IMPORT_SHEET_CONTENTS::FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings )
{
wxASSERT( aSettings );
m_settings = aSettings;
};
void FILEDLG_IMPORT_SHEET_CONTENTS::TransferDataFromCustomControls()
{
m_settings->m_DesignBlockChooserPanel.repeated_placement = m_cbRepeatedPlacement->GetValue();
m_settings->m_DesignBlockChooserPanel.place_as_sheet = m_cbPlaceAsSheet->GetValue();
m_settings->m_DesignBlockChooserPanel.keep_annotations = m_cbKeepAnnotations->GetValue();
}
void FILEDLG_IMPORT_SHEET_CONTENTS::AddCustomControls( wxFileDialogCustomize& customizer )
{
m_cbRepeatedPlacement = customizer.AddCheckBox( REPEATED_PLACEMENT );
m_cbRepeatedPlacement->SetValue( m_settings->m_DesignBlockChooserPanel.repeated_placement );
m_cbPlaceAsSheet = customizer.AddCheckBox( PLACE_AS_SHEET );
m_cbPlaceAsSheet->SetValue( m_settings->m_DesignBlockChooserPanel.place_as_sheet );
m_cbKeepAnnotations = customizer.AddCheckBox( KEEP_ANNOTATIONS );
m_cbKeepAnnotations->SetValue( m_settings->m_DesignBlockChooserPanel.keep_annotations );
}

View File

@ -0,0 +1,113 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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_PANE_H
#define DESIGN_BLOCK_PANE_H
#include <design_block_tree_model_adapter.h>
#include <widgets/html_window.h>
#include <widgets/wx_panel.h>
#include <wx/checkbox.h>
#include <wx/filedlgcustomize.h>
#include <eeschema_settings.h>
class SCH_EDIT_FRAME;
class PANEL_DESIGN_BLOCK_CHOOSER;
class DESIGN_BLOCK_PANE : public WX_PANEL
{
public:
/**
* Create dialog to choose design_block.
*
* @param aParent a SCH_BASE_FRAME parent window.
* @param aAllowFieldEdits if false, all functions that allow the user to edit fields
* (currently just footprint selection) will not be available.
* @param aShowFootprints if false, all footprint preview and selection features are
* disabled. This forces aAllowFieldEdits false too.
*/
DESIGN_BLOCK_PANE( SCH_EDIT_FRAME* aParent, const LIB_ID* aPreselect,
std::vector<LIB_ID>& aHistoryList );
/**
* To be called after this dialog returns from ShowModal().
*
* For multi-unit design_blocks, if the user selects the design_block itself rather than picking
* an individual unit, 0 will be returned in aUnit.
* Beware that this is an invalid unit number - this should be replaced with whatever
* default is desired (usually 1).
*
* @param aUnit if not NULL, the selected unit is filled in here.
* @return the #LIB_ID of the design_block that has been selected.
*/
LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const;
void SelectLibId( const LIB_ID& aLibId );
void RefreshLibs();
/* Handler for checkbox events */
void OnCheckBox( wxCommandEvent& aEvent );
void UpdateCheckboxes();
void OnSaveSheetAsDesignBlock( wxCommandEvent& aEvent );
void OnSaveSelectionAsDesignBlock( wxCommandEvent& aEvent );
void OnDeleteLibrary( wxCommandEvent& aEvent );
void OnDeleteDesignBlock( wxCommandEvent& aEvent );
PANEL_DESIGN_BLOCK_CHOOSER* GetDesignBlockPanel() const { return m_chooserPanel; }
protected:
PANEL_DESIGN_BLOCK_CHOOSER* m_chooserPanel;
wxCheckBox* m_repeatedPlacement;
wxCheckBox* m_placeAsSheet;
wxCheckBox* m_keepAnnotations;
SCH_EDIT_FRAME* m_frame;
};
// This is a helper class for the file dialog to allow the user to choose similar options
// as the design block chooser when importing a sheet.
class FILEDLG_IMPORT_SHEET_CONTENTS : public wxFileDialogCustomizeHook
{
public:
FILEDLG_IMPORT_SHEET_CONTENTS( EESCHEMA_SETTINGS* aSettings );
virtual void AddCustomControls( wxFileDialogCustomize& customizer ) override;
virtual void TransferDataFromCustomControls() override;
private:
EESCHEMA_SETTINGS* m_settings;
wxFileDialogCheckBox* m_cbRepeatedPlacement;
wxFileDialogCheckBox* m_cbPlaceAsSheet;
wxFileDialogCheckBox* m_cbKeepAnnotations;
wxDECLARE_NO_COPY_CLASS( FILEDLG_IMPORT_SHEET_CONTENTS );
};
#endif

View File

@ -0,0 +1,241 @@
/*
* 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 "design_block_preview_widget.h"
#include <schematic.h>
#include <sch_sheet.h>
#include <sch_view.h>
#include <sch_screen.h>
#include <gal/gal_display_options.h>
#include <gal/graphics_abstraction_layer.h>
#include <math/vector2wx.h>
#include <design_block_lib_table.h>
#include <design_block.h>
#include <sch_preview_panel.h>
#include <pgm_base.h>
#include <sch_painter.h>
#include <eda_draw_frame.h>
#include <project_sch.h>
#include <eeschema_settings.h>
#include <eeschema_helpers.h>
#include <settings/settings_manager.h>
#include <wx/log.h>
#include <wx/stattext.h>
DESIGN_BLOCK_PREVIEW_WIDGET::DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus,
EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType ) :
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 ),
m_preview( nullptr ),
m_status( nullptr ),
m_statusPanel( nullptr ),
m_statusSizer( nullptr ),
m_previewItem( nullptr )
{
auto common_settings = Pgm().GetCommonSettings();
auto app_settings = Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
m_galDisplayOptions.ReadConfig( *common_settings, app_settings->m_Window, this );
m_galDisplayOptions.m_forceDisplayCursor = false;
EDA_DRAW_PANEL_GAL::GAL_TYPE canvasType = aCanvasType;
// Allows only a CAIRO or OPENGL canvas:
if( canvasType != EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL
&& canvasType != EDA_DRAW_PANEL_GAL::GAL_FALLBACK )
{
canvasType = EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL;
}
m_preview = new SCH_PREVIEW_PANEL( this, wxID_ANY, wxDefaultPosition, wxSize( -1, -1 ),
m_galDisplayOptions, canvasType );
m_preview->SetStealsFocus( false );
m_preview->ShowScrollbars( wxSHOW_SB_NEVER, wxSHOW_SB_NEVER );
m_preview->GetGAL()->SetAxesEnabled( false );
// Do not display the grid: the look is not good for a small canvas area.
// But mainly, due to some strange bug I (JPC) was unable to fix, the grid creates
// strange artifacts on Windows when Eeschema is run from KiCad manager (but not in
// stand alone...).
m_preview->GetGAL()->SetGridVisibility( true );
// Early initialization of the canvas background color,
// before any OnPaint event is fired for the canvas using a wrong bg color
KIGFX::VIEW* view = m_preview->GetView();
auto settings = static_cast<SCH_RENDER_SETTINGS*>( view->GetPainter()->GetSettings() );
if( auto* theme = Pgm().GetSettingsManager().GetColorSettings( app_settings->m_ColorTheme ) )
settings->LoadColors( theme );
const COLOR4D& backgroundColor = settings->GetBackgroundColor();
const COLOR4D& foregroundColor = settings->GetCursorColor();
m_preview->GetGAL()->SetClearColor( backgroundColor );
settings->m_ShowPinsElectricalType = false;
settings->m_ShowPinNumbers = false;
settings->m_ShowHiddenPins = false;
settings->m_ShowHiddenFields = false;
m_outerSizer = new wxBoxSizer( wxVERTICAL );
if( aIncludeStatus )
{
m_statusPanel = new wxPanel( this );
m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
m_status = new wxStaticText( m_statusPanel, wxID_ANY, wxEmptyString );
m_status->SetForegroundColour( settings->GetLayerColor( LAYER_REFERENCEPART ).ToColour() );
m_statusSizer = new wxBoxSizer( wxVERTICAL );
m_statusSizer->Add( 0, 0, 1 ); // add a spacer
m_statusSizer->Add( m_status, 0, wxALIGN_CENTER );
m_statusSizer->Add( 0, 0, 1 ); // add a spacer
m_statusPanel->SetSizer( m_statusSizer );
// Give the status panel the same color scheme as the canvas so it isn't jarring when
// switched to.
m_statusPanel->SetBackgroundColour( backgroundColor.ToColour() );
m_statusPanel->SetForegroundColour( foregroundColor.ToColour() );
// Give the preview panel a small top border to align its top with the status panel,
// and give the status panel a small bottom border to align its bottom with the preview
// panel.
m_outerSizer->Add( m_preview, 1, wxTOP | wxEXPAND, 5 );
m_outerSizer->Add( m_statusPanel, 1, wxBOTTOM | wxEXPAND, 5 );
// Hide the status panel to start
m_statusPanel->Hide();
}
else
{
m_outerSizer->Add( m_preview, 1, wxEXPAND, 0 );
}
SetSizer( m_outerSizer );
Layout();
Connect( wxEVT_SIZE, wxSizeEventHandler( DESIGN_BLOCK_PREVIEW_WIDGET::onSize ), nullptr, this );
}
DESIGN_BLOCK_PREVIEW_WIDGET::~DESIGN_BLOCK_PREVIEW_WIDGET()
{
delete m_preview;
delete m_previewItem;
}
void DESIGN_BLOCK_PREVIEW_WIDGET::SetStatusText( wxString const& aText )
{
wxCHECK( m_statusPanel, /* void */ );
m_status->SetLabel( aText );
m_preview->Hide();
m_statusPanel->Show();
Layout();
}
void DESIGN_BLOCK_PREVIEW_WIDGET::onSize( wxSizeEvent& aEvent )
{
if( m_previewItem )
{
fitOnDrawArea();
m_preview->ForceRefresh();
}
aEvent.Skip();
}
void DESIGN_BLOCK_PREVIEW_WIDGET::fitOnDrawArea()
{
if( !m_previewItem )
return;
// set the view scale to fit the item on screen
KIGFX::VIEW* view = m_preview->GetView();
// Calculate the drawing area size, in internal units, for a scaling factor = 1.0
view->SetScale( 1.0 );
VECTOR2D clientSize = view->ToWorld( ToVECTOR2D( m_preview->GetClientSize() ), false );
// Calculate the draw scale to fit the drawing area
double scale = std::min( fabs( clientSize.x / m_itemBBox.GetWidth() ),
fabs( clientSize.y / m_itemBBox.GetHeight() ) );
// Above calculation will yield an exact fit; add a bit of whitespace around block
scale /= 1.2;
// Now fix the best scale
view->SetScale( scale );
view->SetCenter( m_itemBBox.Centre() );
}
void DESIGN_BLOCK_PREVIEW_WIDGET::DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock )
{
KIGFX::VIEW* view = m_preview->GetView();
if( m_previewItem )
{
view->Clear();
delete m_previewItem;
m_previewItem = nullptr;
}
if( aDesignBlock )
{
m_previewItem = EESCHEMA_HELPERS::LoadSchematic( aDesignBlock->GetSchematicFile(),
SCH_IO_MGR::SCH_KICAD, false, true );
BOX2I bBox;
if( m_previewItem )
{
for( EDA_ITEM* item : m_previewItem->CurrentSheet().LastScreen()->Items() )
{
view->Add( item );
if( item->Type() == SCH_FIELD_T )
{
if( !static_cast<const SCH_FIELD*>( item )->IsVisible() )
continue;
}
bBox.Merge( item->GetBoundingBox() );
}
}
m_itemBBox = bBox;
if( !m_preview->IsShownOnScreen() )
{
m_preview->Show();
if( m_statusPanel )
m_statusPanel->Hide();
Layout(); // Ensure panel size is up to date.
}
// Calculate the draw scale to fit the drawing area
fitOnDrawArea();
}
m_preview->ForceRefresh();
m_preview->Show();
}

View File

@ -0,0 +1,81 @@
/*
* 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/>.
*/
#ifndef DESIGN_BLOCK_PREVIEW_WIDGET_H
#define DESIGN_BLOCK_PREVIEW_WIDGET_H
#include <wx/panel.h>
#include <kiway.h>
#include <gal_display_options_common.h>
#include <class_draw_panel_gal.h>
class LIB_ID;
class DESIGN_BLOCK;
class SCHEMATIC;
class SCH_SHEET;
class wxStaticText;
class wxSizer;
class DESIGN_BLOCK_PREVIEW_WIDGET : public wxPanel
{
public:
/**
* Construct a symbol preview widget.
*
* @param aParent - parent window
* @param aCanvasType = the type of canvas (GAL_TYPE_OPENGL or GAL_TYPE_CAIRO only)
*/
DESIGN_BLOCK_PREVIEW_WIDGET( wxWindow* aParent, bool aIncludeStatus,
EDA_DRAW_PANEL_GAL::GAL_TYPE aCanvasType );
~DESIGN_BLOCK_PREVIEW_WIDGET() override;
/**
* Set the contents of the status label and display it.
*/
void SetStatusText( const wxString& aText );
/**
* Set the currently displayed symbol.
*/
void DisplayDesignBlock( DESIGN_BLOCK* aDesignBlock );
protected:
void onSize( wxSizeEvent& aEvent );
void fitOnDrawArea(); // set the view scale to fit the item on screen and center
GAL_DISPLAY_OPTIONS_IMPL m_galDisplayOptions;
EDA_DRAW_PANEL_GAL* m_preview;
wxStaticText* m_status;
wxPanel* m_statusPanel;
wxSizer* m_statusSizer;
wxSizer* m_outerSizer;
SCHEMATIC* m_previewItem;
/// The bounding box of the current item
BOX2I m_itemBBox;
};
#endif // DESIGN_BLOCK_PREVIEW_WIDGET_H

View File

@ -0,0 +1,431 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2016-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 <pgm_base.h>
#include <design_block.h>
#include <design_block_lib_table.h>
#include <panel_design_block_chooser.h>
#include <design_block_preview_widget.h>
#include <kiface_base.h>
#include <sch_edit_frame.h>
#include <project_sch.h>
#include <widgets/lib_tree.h>
#include <settings/settings_manager.h>
#include <project/project_file.h>
#include <eeschema_settings.h>
#include <dialogs/html_message_box.h>
#include <string_utils.h>
#include <wx/button.h>
#include <wx/clipbrd.h>
#include <wx/log.h>
#include <wx/panel.h>
#include <wx/sizer.h>
#include <wx/splitter.h>
#include <wx/timer.h>
#include <wx/wxhtml.h>
#include <wx/msgdlg.h>
#include <widgets/wx_progress_reporters.h>
wxString PANEL_DESIGN_BLOCK_CHOOSER::g_designBlockSearchString;
PANEL_DESIGN_BLOCK_CHOOSER::PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent,
std::vector<LIB_ID>& aHistoryList,
std::function<void()> aSelectHandler ) :
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize ),
m_vsplitter( nullptr ),
m_tree( nullptr ),
m_preview( nullptr ),
m_frame( aFrame ),
m_selectHandler( std::move( aSelectHandler ) ),
m_historyList( aHistoryList )
{
DESIGN_BLOCK_LIB_TABLE* libs = m_frame->Prj().DesignBlockLibs();
// Make sure settings are loaded before we start running multi-threaded design block loaders
Pgm().GetSettingsManager().GetAppSettings<EESCHEMA_SETTINGS>();
// Load design block files:
WX_PROGRESS_REPORTER* progressReporter =
new WX_PROGRESS_REPORTER( aParent, _( "Loading Design Block Libraries" ), 3 );
GDesignBlockList.ReadDesignBlockFiles( libs, nullptr, progressReporter );
// Force immediate deletion of the WX_PROGRESS_REPORTER. Do not use Destroy(), or use
// Destroy() followed by wxSafeYield() because on Windows, APP_PROGRESS_DIALOG and
// WX_PROGRESS_REPORTER have some side effects on the event loop manager. For instance, a
// subsequent call to ShowModal() or ShowQuasiModal() for a dialog following the use of a
// WX_PROGRESS_REPORTER results in incorrect modal or quasi modal behavior.
delete progressReporter;
if( GDesignBlockList.GetErrorCount() )
displayErrors( aFrame );
m_adapter = DESIGN_BLOCK_TREE_MODEL_ADAPTER::Create( m_frame, libs );
// -------------------------------------------------------------------------------------
// Construct the actual panel
//
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
m_vsplitter = new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxSP_LIVE_UPDATE | wxSP_NOBORDER | wxSP_3DSASH );
// Avoid the splitter window being assigned as the parent to additional windows.
m_vsplitter->SetExtraStyle( wxWS_EX_TRANSIENT );
wxPanel* treePanel = new wxPanel( m_vsplitter );
wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
treePanel->SetSizer( treeSizer );
wxPanel* detailsPanel = new wxPanel( m_vsplitter );
wxBoxSizer* detailsSizer = new wxBoxSizer( wxVERTICAL );
detailsPanel->SetSizer( detailsSizer );
m_preview = new DESIGN_BLOCK_PREVIEW_WIDGET( detailsPanel, false,
EDA_DRAW_PANEL_GAL::GAL_TYPE_OPENGL );
detailsSizer->Add( m_preview, 1, wxEXPAND, 5 );
detailsPanel->Layout();
detailsSizer->Fit( detailsPanel );
m_vsplitter->SetSashGravity( 0.5 );
m_vsplitter->SetMinimumPaneSize( 20 );
m_vsplitter->SplitHorizontally( treePanel, detailsPanel );
sizer->Add( m_vsplitter, 1, wxEXPAND, 5 );
m_tree = new LIB_TREE( treePanel, wxT( "design_blocks" ), libs, m_adapter,
LIB_TREE::FLAGS::ALL_WIDGETS, nullptr );
treeSizer->Add( m_tree, 1, wxEXPAND, 5 );
treePanel->Layout();
treeSizer->Fit( treePanel );
RefreshLibs();
m_adapter->FinishTreeInitialization();
m_tree->SetSearchString( g_designBlockSearchString );
m_dbl_click_timer = new wxTimer( this );
m_open_libs_timer = new wxTimer( this );
SetSizer( sizer );
Layout();
Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this,
m_dbl_click_timer->GetId() );
Bind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer, this,
m_open_libs_timer->GetId() );
Bind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this );
Bind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this );
// Open the user's previously opened libraries on timer expiration.
// This is done on a timer because we need a gross hack to keep GTK from garbling the
// display. Must be longer than the search debounce timer.
m_open_libs_timer->StartOnce( 300 );
}
PANEL_DESIGN_BLOCK_CHOOSER::~PANEL_DESIGN_BLOCK_CHOOSER()
{
Unbind( wxEVT_TIMER, &PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer, this );
Unbind( EVT_LIBITEM_SELECTED, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected, this );
Unbind( EVT_LIBITEM_CHOSEN, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen, this );
Unbind( wxEVT_CHAR_HOOK, &PANEL_DESIGN_BLOCK_CHOOSER::OnChar, this );
// Stop the timer during destruction early to avoid potential race conditions (that do happen)
m_dbl_click_timer->Stop();
m_open_libs_timer->Stop();
delete m_dbl_click_timer;
delete m_open_libs_timer;
g_designBlockSearchString = m_tree->GetSearchString();
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
{
// Save any changes to column widths, etc.
m_adapter->SaveSettings();
cfg->m_DesignBlockChooserPanel.width = GetParent()->GetSize().x;
cfg->m_DesignBlockChooserPanel.height = GetParent()->GetSize().y;
if( m_vsplitter )
cfg->m_DesignBlockChooserPanel.sash_pos_v = m_vsplitter->GetSashPosition();
cfg->m_DesignBlockChooserPanel.sort_mode = m_tree->GetSortMode();
}
}
void PANEL_DESIGN_BLOCK_CHOOSER::OnChar( wxKeyEvent& aEvent )
{
if( aEvent.GetKeyCode() == WXK_ESCAPE )
{
wxObject* eventSource = aEvent.GetEventObject();
if( wxTextCtrl* textCtrl = dynamic_cast<wxTextCtrl*>( eventSource ) )
{
// First escape cancels search string value
if( textCtrl->GetValue() == m_tree->GetSearchString()
&& !m_tree->GetSearchString().IsEmpty() )
{
m_tree->SetSearchString( wxEmptyString );
return;
}
}
}
else
{
aEvent.Skip();
}
}
void PANEL_DESIGN_BLOCK_CHOOSER::FinishSetup()
{
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
{
auto horizPixelsFromDU =
[&]( int x ) -> int
{
wxSize sz( x, 0 );
return GetParent()->ConvertDialogToPixels( sz ).x;
};
EESCHEMA_SETTINGS::PANEL_DESIGN_BLOCK_CHOOSER& panelCfg = cfg->m_DesignBlockChooserPanel;
int w = panelCfg.width > 40 ? panelCfg.width : horizPixelsFromDU( 440 );
int h = panelCfg.height > 40 ? panelCfg.height : horizPixelsFromDU( 340 );
GetParent()->SetSize( wxSize( w, h ) );
GetParent()->Layout();
// We specify the width of the right window (m_design_block_view_panel), because specify
// the width of the left window does not work as expected when SetSashGravity() is called
if( panelCfg.sash_pos_h < 0 )
panelCfg.sash_pos_h = horizPixelsFromDU( 220 );
if( panelCfg.sash_pos_v < 0 )
panelCfg.sash_pos_v = horizPixelsFromDU( 230 );
if( m_vsplitter )
m_vsplitter->SetSashPosition( panelCfg.sash_pos_v );
m_adapter->SetSortMode( (LIB_TREE_MODEL_ADAPTER::SORT_MODE) panelCfg.sort_mode );
}
}
void PANEL_DESIGN_BLOCK_CHOOSER::RefreshLibs( bool aProgress )
{
// Unselect before syncing to avoid null reference in the adapter
// if a selected item is removed during the sync
m_tree->Unselect();
DESIGN_BLOCK_TREE_MODEL_ADAPTER* adapter =
static_cast<DESIGN_BLOCK_TREE_MODEL_ADAPTER*>( m_adapter.get() );
// Clear all existing libraries then re-add
adapter->ClearLibraries();
// Read the libraries from disk if they've changed
DESIGN_BLOCK_LIB_TABLE* fpTable = m_frame->Prj().DesignBlockLibs();
// Sync FOOTPRINT_INFO list to the libraries on disk
if( aProgress )
{
WX_PROGRESS_REPORTER progressReporter( this, _( "Updating Design Block Libraries" ), 2 );
GDesignBlockList.ReadDesignBlockFiles( fpTable, nullptr, &progressReporter );
progressReporter.Show( false );
}
else
{
GDesignBlockList.ReadDesignBlockFiles( fpTable, nullptr, nullptr );
}
rebuildHistoryNode();
if( !m_historyList.empty() )
adapter->SetPreselectNode( m_historyList[0], 0 );
adapter->AddLibraries( m_frame );
m_tree->Regenerate( true );
}
void PANEL_DESIGN_BLOCK_CHOOSER::SetPreselect( const LIB_ID& aPreselect )
{
m_adapter->SetPreselectNode( aPreselect, 0 );
}
LIB_ID PANEL_DESIGN_BLOCK_CHOOSER::GetSelectedLibId( int* aUnit ) const
{
return m_tree->GetSelectedLibId( aUnit );
}
void PANEL_DESIGN_BLOCK_CHOOSER::SelectLibId( const LIB_ID& aLibId )
{
m_tree->CenterLibId( aLibId );
m_tree->SelectLibId( aLibId );
}
void PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer( wxTimerEvent& aEvent )
{
// Hack because of eaten MouseUp event. See PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen
// for the beginning of this spaghetti noodle.
wxMouseState state = wxGetMouseState();
if( state.LeftIsDown() )
{
// Mouse hasn't been raised yet, so fire the timer again. Otherwise the
// purpose of this timer is defeated.
m_dbl_click_timer->StartOnce( PANEL_DESIGN_BLOCK_CHOOSER::DBLCLICK_DELAY );
}
else
{
m_selectHandler();
addDesignBlockToHistory( m_tree->GetSelectedLibId() );
}
}
void PANEL_DESIGN_BLOCK_CHOOSER::onOpenLibsTimer( wxTimerEvent& aEvent )
{
if( EESCHEMA_SETTINGS* cfg = dynamic_cast<EESCHEMA_SETTINGS*>( Kiface().KifaceSettings() ) )
m_adapter->OpenLibs( cfg->m_LibTree.open_libs );
// Bind this now se we don't spam the event queue with EVT_LIBITEM_SELECTED events during
// the initial load.
Bind( EVT_LIBITEM_SELECTED, &PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected, this );
}
void PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockSelected( wxCommandEvent& aEvent )
{
if( GetSelectedLibId().IsValid() )
m_preview->DisplayDesignBlock( m_frame->GetDesignBlock( GetSelectedLibId() ) );
}
void PANEL_DESIGN_BLOCK_CHOOSER::onDesignBlockChosen( wxCommandEvent& aEvent )
{
if( m_tree->GetSelectedLibId().IsValid() )
{
// Got a selection. We can't just end the modal dialog here, because wx leaks some events
// back to the parent window (in particular, the MouseUp following a double click).
//
// NOW, here's where it gets really fun. wxTreeListCtrl eats MouseUp. This isn't really
// feasible to bypass without a fully custom wxDataViewCtrl implementation, and even then
// might not be fully possible (docs are vague). To get around this, we use a one-shot
// timer to schedule the dialog close.
//
// See PANEL_DESIGN_BLOCK_CHOOSER::onCloseTimer for the other end of this spaghetti noodle.
m_dbl_click_timer->StartOnce( PANEL_DESIGN_BLOCK_CHOOSER::DBLCLICK_DELAY );
}
}
void PANEL_DESIGN_BLOCK_CHOOSER::addDesignBlockToHistory( const LIB_ID& aLibId )
{
LIB_ID savedId = GetSelectedLibId();
m_tree->Unselect();
// Remove duplicates
for( int i = (int) m_historyList.size() - 1; i >= 0; --i )
{
if( m_historyList[i] == aLibId )
m_historyList.erase( m_historyList.begin() + i );
}
// Add the new name at the beginning of the history list
m_historyList.insert( m_historyList.begin(), aLibId );
// Remove extra names
while( m_historyList.size() >= 8 )
m_historyList.pop_back();
rebuildHistoryNode();
m_tree->Regenerate( true );
SelectLibId( savedId );
}
void PANEL_DESIGN_BLOCK_CHOOSER::rebuildHistoryNode()
{
wxString history = wxT( "-- " ) + _( "Recently Used" ) + wxT( " --" );
m_adapter->DoRemoveLibrary( history );
// Build the history list
std::vector<LIB_TREE_ITEM*> historyInfos;
for( const LIB_ID& lib : m_historyList )
{
LIB_TREE_ITEM* fp_info =
GDesignBlockList.GetDesignBlockInfo( lib.GetLibNickname(), lib.GetLibItemName() );
// this can be null, for example, if the design block has been deleted from a library.
if( fp_info != nullptr )
historyInfos.push_back( fp_info );
}
m_adapter->DoAddLibrary( history, wxEmptyString, historyInfos, false, true );
}
void PANEL_DESIGN_BLOCK_CHOOSER::displayErrors( wxTopLevelWindow* aWindow )
{
// @todo: go to a more HTML !<table>! ? centric output, possibly with recommendations
// for remedy of errors. Add numeric error codes to PARSE_ERROR, and switch on them for
// remedies, etc. Full access is provided to everything in every exception!
HTML_MESSAGE_BOX dlg( aWindow, _( "Load Error" ) );
dlg.MessageSet( _( "Errors were encountered loading footprints:" ) );
wxString msg;
while( std::unique_ptr<IO_ERROR> error = GDesignBlockList.PopError() )
{
wxString tmp = EscapeHTML( error->Problem() );
// Preserve new lines in error messages so queued errors don't run together.
tmp.Replace( wxS( "\n" ), wxS( "<BR>" ) );
msg += wxT( "<p>" ) + tmp + wxT( "</p>" );
}
dlg.AddHTML_Text( msg );
dlg.ShowModal();
}

View File

@ -0,0 +1,118 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2014-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 PANEL_DESIGN_BLOCK_CHOOSER_H
#define PANEL_DESIGN_BLOCK_CHOOSER_H
#include <widgets/lib_tree.h>
#include <design_block_tree_model_adapter.h>
#include <widgets/html_window.h>
class wxPanel;
class wxTimer;
class wxSplitterWindow;
class SCH_EDIT_FRAME;
class DESIGN_BLOCK_PREVIEW_WIDGET;
class PANEL_DESIGN_BLOCK_CHOOSER : public wxPanel
{
public:
/**
* Create dialog to choose design_block.
*
* @param aFrame the parent frame (usually a SCH_EDIT_FRAME or DESIGN_BLOCK_CHOOSER_FRAME)
* @param aParent the parent window (usually a DIALOG_SHIM or DESIGN_BLOCK_CHOOSER_FRAME)
* @param aAcceptHandler a handler to be called on double-click of a footprint
* @param aEscapeHandler a handler to be called on <ESC>
*/
PANEL_DESIGN_BLOCK_CHOOSER( SCH_EDIT_FRAME* aFrame, wxWindow* aParent,
std::vector<LIB_ID>& aHistoryList,
std::function<void()> aSelectHandler );
~PANEL_DESIGN_BLOCK_CHOOSER();
void OnChar( wxKeyEvent& aEvent );
void FinishSetup();
void SetPreselect( const LIB_ID& aPreselect );
void RefreshLibs( bool aProgress = false );
/**
* To be called after this dialog returns from ShowModal().
*
* For multi-unit design_blocks, if the user selects the design_block itself rather than picking
* an individual unit, 0 will be returned in aUnit.
* Beware that this is an invalid unit number - this should be replaced with whatever
* default is desired (usually 1).
*
* @param aUnit if not NULL, the selected unit is filled in here.
* @return the #LIB_ID of the design_block that has been selected.
*/
LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const;
void SelectLibId( const LIB_ID& aLibId );
LIB_TREE* GetLibTree() { return m_tree; }
protected:
static constexpr int DBLCLICK_DELAY = 100; // milliseconds
void OnDetailsCharHook( wxKeyEvent& aEvt );
void onCloseTimer( wxTimerEvent& aEvent );
void onOpenLibsTimer( wxTimerEvent& aEvent );
void onDesignBlockSelected( wxCommandEvent& aEvent );
/**
* Handle the selection of an item. This is called when either the search box or the tree
* receive an Enter, or the tree receives a double click.
* If the item selected is a category, it is expanded or collapsed; if it is a design_block, the
* design_block is picked.
*/
void onDesignBlockChosen( wxCommandEvent& aEvent );
void addDesignBlockToHistory( const LIB_ID& aLibId );
void rebuildHistoryNode();
void displayErrors( wxTopLevelWindow* aWindow );
static wxString g_designBlockSearchString;
wxTimer* m_dbl_click_timer;
wxTimer* m_open_libs_timer;
wxSplitterWindow* m_vsplitter;
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER> m_adapter;
LIB_TREE* m_tree;
DESIGN_BLOCK_PREVIEW_WIDGET* m_preview;
SCH_EDIT_FRAME* m_frame;
std::function<void()> m_selectHandler;
std::vector<LIB_ID> m_historyList;
};
#endif /* PANEL_DESIGN_BLOCK_CHOOSER_H */

View File

@ -466,6 +466,15 @@ public:
*/
int m_DisambiguationMenuDelay;
/**
* Enable the new Design Blocks feature
*
* Setting name: "EnableDesignBlocks"
* Valid values: true or false
* Default value: false
*/
bool m_EnableDesignBlocks;
/**
* Enable support for generators.
*

View File

@ -228,6 +228,7 @@ enum KICAD_T
*/
SYMBOL_LIB_TABLE_T,
FP_LIB_TABLE_T,
DESIGN_BLOCK_LIB_TABLE_T,
SYMBOL_LIBS_T,
SEARCH_STACK_T,
S3D_CACHE_T,
@ -516,6 +517,7 @@ constexpr bool IsMiscType( const KICAD_T aType )
case SYMBOL_LIB_TABLE_T:
case FP_LIB_TABLE_T:
case DESIGN_BLOCK_LIB_TABLE_T:
case SYMBOL_LIBS_T:
case SEARCH_STACK_T:
case S3D_CACHE_T:

View File

@ -0,0 +1,291 @@
/*
* 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
*/
#ifndef DESIGN_BLOCK_LIB_TABLE_H_
#define DESIGN_BLOCK_LIB_TABLE_H_
#include <kicommon.h>
#include <lib_table_base.h>
#include <design_block_io.h>
#include <design_block_info_impl.h>
class DESIGN_BLOCK;
class DESIGN_BLOCK_LIB_TABLE_GRID;
/**
* Hold a record identifying a library accessed by the appropriate design block library #PLUGIN
* object in the #DESIGN_BLOCK_LIB_TABLE.
*/
class KICOMMON_API DESIGN_BLOCK_LIB_TABLE_ROW : public LIB_TABLE_ROW
{
public:
DESIGN_BLOCK_LIB_TABLE_ROW( const wxString& aNick, const wxString& aURI, const wxString& aType,
const wxString& aOptions, const wxString& aDescr = wxEmptyString ) :
LIB_TABLE_ROW( aNick, aURI, aOptions, aDescr )
{
SetType( aType );
}
DESIGN_BLOCK_LIB_TABLE_ROW() : type( DESIGN_BLOCK_IO_MGR::KICAD_SEXP ) {}
bool operator==( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) const;
bool operator!=( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) const { return !( *this == aRow ); }
/**
* return the type of design block library table represented by this row.
*/
const wxString GetType() const override { return DESIGN_BLOCK_IO_MGR::ShowType( type ); }
/**
* Change the type represented by this row.
*/
void SetType( const wxString& aType ) override;
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T GetFileType() { return type; }
protected:
DESIGN_BLOCK_LIB_TABLE_ROW( const DESIGN_BLOCK_LIB_TABLE_ROW& aRow ) :
LIB_TABLE_ROW( aRow ), type( aRow.type )
{
}
private:
virtual LIB_TABLE_ROW* do_clone() const override
{
return new DESIGN_BLOCK_LIB_TABLE_ROW( *this );
}
void setPlugin( DESIGN_BLOCK_IO* aPlugin ) { plugin.reset( aPlugin ); }
friend class DESIGN_BLOCK_LIB_TABLE;
private:
IO_RELEASER<DESIGN_BLOCK_IO> plugin;
DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T type;
};
class KICOMMON_API DESIGN_BLOCK_LIB_TABLE : public LIB_TABLE
{
public:
PROJECT::ELEM ProjectElementType() override { return PROJECT::ELEM::DESIGN_BLOCK_LIB_TABLE; }
virtual void Parse( LIB_TABLE_LEXER* aLexer ) override;
virtual void Format( OUTPUTFORMATTER* aOutput, int aIndentLevel ) const override;
/**
* Build a design block library table by pre-pending this table fragment in front of
* @a aFallBackTable. Loading of this table fragment is done by using Parse().
*
* @param aFallBackTable is another DESIGN_BLOCK_LIB_TABLE which is searched only when a row
* is not found in this table. No ownership is taken of
* \a aFallBackTable.
*/
DESIGN_BLOCK_LIB_TABLE( DESIGN_BLOCK_LIB_TABLE* aFallBackTable = nullptr );
bool operator==( const DESIGN_BLOCK_LIB_TABLE& aFpTable ) const;
bool operator!=( const DESIGN_BLOCK_LIB_TABLE& r ) const { return !( *this == r ); }
/**
* Return an #DESIGN_BLOCK_LIB_TABLE_ROW if \a aNickName is found in this table or in any chained
* fall back table fragment.
*
* If \a aCheckIfEnabled is true, the library will be ignored even if it is disabled.
* Otherwise, the row found will be returned even if entry is disabled.
*
* The #PLUGIN is loaded and attached to the "plugin" field of the #DESIGN_BLOCK_LIB_TABLE_ROW if
* not already loaded.
*
* @param aNickName is the name of library nickname to find.
* @param aCheckIfEnabled is the flag to check if the library found is enabled.
* @return the library \a NickName if found.
* @throw IO_ERROR if \a aNickName cannot be found.
*/
const DESIGN_BLOCK_LIB_TABLE_ROW* FindRow( const wxString& aNickName,
bool aCheckIfEnabled = false );
/**
* Return a list of design block names contained within the library given by @a aNickname.
*
* @param aDesignBlockNames is the list to fill with the design block names found in \a aNickname
* @param aNickname is a locator for the "library", it is a "name" in LIB_TABLE_ROW.
* @param aBestEfforts if true, don't throw on errors.
*
* @throw IO_ERROR if the library cannot be found, or design block cannot be loaded.
*/
void DesignBlockEnumerate( wxArrayString& aDesignBlockNames, const wxString& aNickname,
bool aBestEfforts );
/**
* Generate a hashed timestamp representing the last-mod-times of the library indicated
* by \a aNickname, or all libraries if \a aNickname is NULL.
*/
long long GenerateTimestamp( const wxString* aNickname );
/**
* If possible, prefetches the specified library (e.g. performing downloads). Does not parse.
* Threadsafe.
*
* This is a no-op for libraries that cannot be prefetched.
*
* @param aNickname is a locator for the library; it is a name in LIB_TABLE_ROW.
*
* @throw IO_ERROR if there is an error prefetching the library.
*/
void PrefetchLib( const wxString& aNickname );
/**
* Load a design block having @a aDesignBlockName from the library given by @a aNickname.
*
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
* @param aDesignBlockName is the name of the design block to load.
* @param aKeepUUID = true to keep initial items UUID, false to set new UUID
* normally true if loaded in the design block editor, false
* if loaded in the board editor. Used only in kicad_plugin
* @return the design block if found caller owns it, else NULL if not found.
*
* @throw IO_ERROR if the library cannot be found or read. No exception
* is thrown in the case where aDesignBlockName cannot be found.
*/
DESIGN_BLOCK* DesignBlockLoad( const wxString& aNickname, const wxString& aDesignBlockName,
bool aKeepUUID = false );
/**
* Indicates whether or not the given design block already exists in the given library.
*/
bool DesignBlockExists( const wxString& aNickname, const wxString& aDesignBlockName );
/**
* A version of #DesignBlockLoad() for use after #DesignBlockEnumerate() for more efficient
* cache management.
*
* The return value is const to allow it to return a reference to a cached item.
*/
const DESIGN_BLOCK* GetEnumeratedDesignBlock( const wxString& aNickname,
const wxString& aDesignBlockName );
/**
* The set of return values from DesignBlockSave() below.
*/
enum SAVE_T
{
SAVE_OK,
SAVE_SKIPPED,
};
/**
* Write @a aDesignBlock to an existing library given by @a aNickname.
*
* If a design block by the same name already exists, it is replaced.
*
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
* @param aDesignBlock is what to store in the library. The caller continues to own the
* design block after this call.
* @param aOverwrite when true means overwrite any existing design block by the same name,
* else if false means skip the write and return SAVE_SKIPPED.
* @return #SAVE_OK or #SAVE_SKIPPED. If error saving, then #IO_ERROR is thrown.
*
* @throw IO_ERROR if there is a problem saving.
*/
SAVE_T DesignBlockSave( const wxString& aNickname, const DESIGN_BLOCK* aDesignBlock,
bool aOverwrite = true );
/**
* Delete the @a aDesignBlockName from the library given by @a aNickname.
*
* @param aNickname is a locator for the "library", it is a "name" in #LIB_TABLE_ROW.
* @param aDesignBlockName is the name of a design block to delete from the specified library.
*
* @throw IO_ERROR if there is a problem finding the design block or the library, or deleting it.
*/
void DesignBlockDelete( const wxString& aNickname, const wxString& aDesignBlockName );
/**
* Return true if the library given by @a aNickname is writable.
*
* Often system libraries are read only because of where they are installed.
*
* @throw IO_ERROR if no library at aLibraryPath exists.
*/
bool IsDesignBlockLibWritable( const wxString& aNickname );
void DesignBlockLibDelete( const wxString& aNickname );
void DesignBlockLibCreate( const wxString& aNickname );
/**
* Load a design block having @a aDesignBlockId with possibly an empty nickname.
*
* @param aDesignBlockId the [nickname] and name of the design block to load.
* @param aKeepUUID = true to keep initial items UUID, false to set new UUID
* normally true if loaded in the design block editor, false
* if loaded in the board editor
* used only in kicad_plugin
* @return the #DESIGN_BLOCK if found caller owns it, else NULL if not found.
*
* @throw IO_ERROR if the library cannot be found or read. No exception is
* thrown in the case where \a aDesignBlockName cannot be found.
* @throw PARSE_ERROR if @a aDesignBlockId is not parsed OK.
*/
DESIGN_BLOCK* DesignBlockLoadWithOptionalNickname( const LIB_ID& aDesignBlockId,
bool aKeepUUID = false );
/**
* Load the global design block library table into \a aTable.
*
* This probably should be move into the application object when KiCad is changed
* to a single process application. This is the least painful solution for the
* time being.
*
* @param aTable the #DESIGN_BLOCK_LIB_TABLE object to load.
* @return true if the global library table exists and is loaded properly.
* @throw IO_ERROR if an error occurs attempting to load the design block library
* table.
*/
static bool LoadGlobalTable( DESIGN_BLOCK_LIB_TABLE& aTable );
/**
* @return the platform specific global design block library path and file name.
*/
static wxString GetGlobalTableFileName();
/**
* Return the name of the environment variable used to hold the directory of
* locally installed "KiCad sponsored" system design block libraries.
*
*These can be either legacy or pretty format. The only thing special about this
* particular environment variable is that it is set automatically by KiCad on
* program start up, <b>if</b> it is not set already in the environment.
*/
static const wxString GlobalPathEnvVariableName();
private:
friend class DESIGN_BLOCK_LIB_TABLE_GRID;
};
KICOMMON_API extern DESIGN_BLOCK_LIB_TABLE GDesignBlockTable;
KICOMMON_API extern DESIGN_BLOCK_LIST_IMPL GDesignBlockList;
#endif // DESIGN_BLOCK_LIB_TABLE_H_

View File

@ -417,6 +417,8 @@ public:
static const wxString NetInspectorPanelName() { return wxS( "NetInspector" ); }
static const wxString DesignBlocksPaneName() { return wxS( "DesignBlocks" ); }
/**
* Fetch an item by KIID. Frame-type-specific implementation.
*/

View File

@ -62,7 +62,7 @@ enum FRAME_T
FRAME_CALC,
KIWAY_PLAYER_COUNT, // counts subset of FRAME_T's which are KIWAY_PLAYER derivatives
KIWAY_PLAYER_COUNT, // counts subset of FRAME_T's which are KIWAY_PLAYER derivatives
// C++ project manager is not a KIWAY_PLAYER
KICAD_MAIN_FRAME_T = KIWAY_PLAYER_COUNT,
@ -112,11 +112,12 @@ enum FRAME_T
// Library table dialogs are transient and are never returned
DIALOG_CONFIGUREPATHS,
DIALOG_DESIGN_BLOCK_LIBRARY_TABLE,
DIALOG_SCH_LIBRARY_TABLE,
DIALOG_PCB_LIBRARY_TABLE
};
//TEXT_EDITOR_FRAME_T,
//TEXT_EDITOR_FRAME_T,
#endif // FRAME_T_H_

View File

@ -71,8 +71,7 @@
enum main_id
{
ID_APPEND_PROJECT = wxID_HIGHEST,
ID_LOAD_FILE,
ID_LOAD_FILE = wxID_HIGHEST,
ID_NEW_BOARD,
ID_SAVE_BOARD,
ID_SAVE_BOARD_AS,

View File

@ -285,6 +285,16 @@ public:
*/
LIB_TREE_NODE_LIBRARY& AddLib( wxString const& aName, wxString const& aDesc );
/**
* Remove a library node from the root.
*/
void RemoveLib( wxString const& aName );
/**
* Clear the tree
*/
void Clear();
void UpdateScore( EDA_COMBINED_MATCHER* aMatcher, const wxString& aLib,
std::function<bool( LIB_TREE_NODE& aNode )>* aFilter ) override;
};

View File

@ -92,6 +92,8 @@
* - `HasDefaultCompare()` - whether sorted by default
*/
#include <project.h>
class APP_SETTINGS_BASE;
class TOOL_INTERACTIVE;
class EDA_BASE_FRAME;
@ -182,6 +184,13 @@ public:
const std::vector<LIB_TREE_ITEM*>& aItemList,
bool pinned, bool presorted );
/**
* Remove the library by name.
*
* @param aNodeName the name of the library to remove
*/
void DoRemoveLibrary( const wxString& aNodeName );
std::vector<wxString> GetAvailableColumns() const { return m_availableColumns; }
std::vector<wxString> GetShownColumns() const { return m_shownColumns; }
@ -390,7 +399,7 @@ protected:
unsigned int aCol,
wxDataViewItemAttr& aAttr ) const override;
virtual bool isSymbolModel() = 0;
virtual PROJECT::LIB_TYPE_T getLibType() = 0;
void resortTree();

View File

@ -67,6 +67,11 @@ public:
*/
static wxString GetDefaultUserFootprintsPath();
/**
* Gets the default path we point users to create projects
*/
static wxString GetDefaultUserDesignBlocksPath();
/**
* Gets the default path we point users to create projects
*/
@ -98,6 +103,11 @@ public:
*/
static wxString GetStockFootprintsPath();
/**
* Gets the stock (install) footprints path
*/
static wxString GetStockDesignBlocksPath();
/**
* Gets the stock (install) 3dmodels path
*/

View File

@ -42,6 +42,7 @@
/// default name for nameless projects
#define NAMELESS_PROJECT _( "untitled" )
class DESIGN_BLOCK_LIB_TABLE;
class FP_LIB_TABLE;
class SYMBOL_LIBS;
class SEARCH_STACK;
@ -75,6 +76,8 @@ public:
SYMBOL_LIB_TABLE,
SEARCH_STACK,
DESIGN_BLOCK_LIB_TABLE,
COUNT
};
@ -177,8 +180,22 @@ public:
*/
virtual const wxString SymbolLibTableName() const;
void PinLibrary( const wxString& aLibrary, bool isSymbolLibrary );
void UnpinLibrary( const wxString& aLibrary, bool isSymbolLibrary );
/**
* Return the path and file name of this projects design block library table.
*/
virtual const wxString DesignBlockLibTblName() const;
enum LIB_TYPE_T
{
SYMBOL_LIB,
FOOTPRINT_LIB,
DESIGN_BLOCK_LIB,
LIB_TYPE_COUNT
};
void PinLibrary( const wxString& aLibrary, enum LIB_TYPE_T aLibType );
void UnpinLibrary( const wxString& aLibrary, enum LIB_TYPE_T aLibType );
virtual PROJECT_FILE& GetProjectFile() const
{
@ -277,6 +294,11 @@ public:
*/
virtual FP_LIB_TABLE* PcbFootprintLibs( KIWAY& aKiway );
/**
* Return the table of design block libraries.
*/
virtual DESIGN_BLOCK_LIB_TABLE* DesignBlockLibs();
private:
friend class SETTINGS_MANAGER; // so that SM can set project path
friend class TEST_NETLISTS_FIXTURE; // TODO(JE) make this not required

View File

@ -123,6 +123,9 @@ public:
/// The list of pinned footprint libraries
std::vector<wxString> m_PinnedFootprintLibs;
/// The list of pinned design block libraries
std::vector<wxString> m_PinnedDesignBlockLibs;
std::map<wxString, wxString> m_TextVars;
/**

View File

@ -114,6 +114,7 @@ public:
bool remember_open_files;
std::vector<wxString> pinned_symbol_libs;
std::vector<wxString> pinned_fp_libs;
std::vector<wxString> pinned_design_block_libs;
};
struct SYSTEM

View File

@ -39,6 +39,8 @@ public:
std::vector<wxString> m_OpenProjects;
wxString m_lastDesignBlockLibDir;
std::vector<std::pair<wxString, wxString>> m_PcmRepositories;
wxString m_PcmLastDownloadDir;

View File

@ -217,6 +217,7 @@ public:
static TOOL_ACTION configurePaths;
static TOOL_ACTION showSymbolLibTable;
static TOOL_ACTION showFootprintLibTable;
static TOOL_ACTION showDesignBlockLibTable;
static TOOL_ACTION gettingStarted;
static TOOL_ACTION help;
static TOOL_ACTION about;

View File

@ -241,6 +241,13 @@ extern KICOMMON_API const wxChar* const traceGit;
*/
extern KICOMMON_API const wxChar* const traceEagleIo;
/*
* Flag to enable Design Block O debug tracing.
*
* Use "KICAD_EAGLE_IO" to enable.
*/
extern KICOMMON_API const wxChar* const traceDesignBlocks;
///@}
/**

View File

@ -176,6 +176,9 @@ public:
static const std::string Ipc2581FileExtension;
static const std::string WorkbookFileExtension;
static const std::string KiCadDesignBlockLibPathExtension;
static const std::string KiCadDesignBlockPathExtension;
static const std::string PngFileExtension;
static const std::string JpegFileExtension;
static const std::string TextFileExtension;
@ -246,6 +249,8 @@ public:
static wxString DocModulesFileName();
static wxString KiCadFootprintLibFileWildcard();
static wxString KiCadFootprintLibPathWildcard();
static wxString KiCadDesignBlockLibPathWildcard();
static wxString KiCadDesignBlockPathWildcard();
static wxString TextFileWildcard();
static wxString ModLegacyExportFileWildcard();
static wxString ErcFileWildcard();

View File

@ -9,6 +9,7 @@ add_subdirectory( pcm )
include_directories( BEFORE ${INC_BEFORE} )
include_directories(
${CMAKE_SOURCE_DIR}/common/dialogs
${CMAKE_SOURCE_DIR}/pcbnew
${CMAKE_SOURCE_DIR}/eeschema
${CMAKE_SOURCE_DIR}/kicad/pcm
@ -24,6 +25,8 @@ set( KICAD_SRCS
dialogs/dialog_template_selector.cpp
dialogs/panel_kicad_launcher_base.cpp
dialogs/panel_kicad_launcher.cpp
dialogs/panel_design_block_lib_table_base.cpp
dialogs/panel_design_block_lib_table.cpp
files-io.cpp
import_proj.cpp
import_project.cpp

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,106 @@
/*
* 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/>.
*/
#ifndef PANEL_DESIGN_BLOCK_LIB_TABLE_H
#define PANEL_DESIGN_BLOCK_LIB_TABLE_H
#include <dialogs/dialog_edit_library_tables.h>
#include <dialogs/panel_design_block_lib_table_base.h>
#include <widgets/wx_grid.h>
#include <design_block_io.h>
class DESIGN_BLOCK_LIB_TABLE;
class DESIGN_BLOCK_LIB_TABLE_GRID;
class PROJECT;
/**
* Dialog to show and edit symbol library tables.
*/
class PANEL_DESIGN_BLOCK_LIB_TABLE : public PANEL_DESIGN_BLOCK_LIB_TABLE_BASE
{
public:
PANEL_DESIGN_BLOCK_LIB_TABLE( DIALOG_EDIT_LIBRARY_TABLES* aParent, PROJECT* aProject,
DESIGN_BLOCK_LIB_TABLE* aGlobalTable,
const wxString& aGlobalTblPath,
DESIGN_BLOCK_LIB_TABLE* aProjectTable,
const wxString& aProjectTblPath,
const wxString& aProjectBasePath );
~PANEL_DESIGN_BLOCK_LIB_TABLE() override;
private:
bool TransferDataFromWindow() override;
/**
* Trim important fields, removes blank row entries, and checks for duplicates.
*
* @return bool - true if tables are OK, else false.
*/
bool verifyTables();
void OnUpdateUI( wxUpdateUIEvent& event ) override;
void appendRowHandler( wxCommandEvent& event ) override;
void browseLibrariesHandler( wxCommandEvent& event );
void deleteRowHandler( wxCommandEvent& event ) override;
void moveUpHandler( wxCommandEvent& event ) override;
void moveDownHandler( wxCommandEvent& event ) override;
void onMigrateLibraries( wxCommandEvent& event ) override;
void onSizeGrid( wxSizeEvent& event ) override;
void adjustPathSubsGridColumns( int aWidth );
/// Populate the readonly environment variable table with names and values
/// by examining all the full_uri columns.
void populateEnvironReadOnlyTable();
void populatePluginList();
DESIGN_BLOCK_LIB_TABLE_GRID* global_model() const
{
return (DESIGN_BLOCK_LIB_TABLE_GRID*) m_global_grid->GetTable();
}
DESIGN_BLOCK_LIB_TABLE_GRID* project_model() const
{
return m_project_grid ? (DESIGN_BLOCK_LIB_TABLE_GRID*) m_project_grid->GetTable() : nullptr;
}
DESIGN_BLOCK_LIB_TABLE_GRID* cur_model() const
{
return (DESIGN_BLOCK_LIB_TABLE_GRID*) m_cur_grid->GetTable();
}
// caller's tables are modified only on OK button and successful verification.
DESIGN_BLOCK_LIB_TABLE* m_globalTable;
DESIGN_BLOCK_LIB_TABLE* m_projectTable;
PROJECT* m_project;
wxString m_projectBasePath;
DIALOG_EDIT_LIBRARY_TABLES* m_parent;
WX_GRID* m_cur_grid; // changed based on tab choice
static size_t m_pageNdx; // Remember last notebook page selected during a session
//< Transient (unsaved) last browsed folder when adding a project level library.
wxString m_lastProjectLibDir;
std::map<DESIGN_BLOCK_IO_MGR::DESIGN_BLOCK_FILE_T, IO_BASE::IO_FILE_DESC>
m_supportedDesignBlockFiles;
};
#endif // PANEL_DESIGN_BLOCK_LIB_TABLE_H

View File

@ -0,0 +1,227 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "widgets/std_bitmap_button.h"
#include "widgets/wx_grid.h"
#include "panel_design_block_lib_table_base.h"
///////////////////////////////////////////////////////////////////////////
PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::PANEL_DESIGN_BLOCK_LIB_TABLE_BASE( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name ) : wxPanel( parent, id, pos, size, style, name )
{
wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL );
m_notebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_global_panel = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* m_global_sizer;
m_global_sizer = new wxBoxSizer( wxVERTICAL );
m_global_grid = new WX_GRID( m_global_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_global_grid->CreateGrid( 1, 7 );
m_global_grid->EnableEditing( true );
m_global_grid->EnableGridLines( true );
m_global_grid->EnableDragGridSize( false );
m_global_grid->SetMargins( 0, 0 );
// Columns
m_global_grid->SetColSize( 0, 48 );
m_global_grid->SetColSize( 1, 48 );
m_global_grid->SetColSize( 2, 100 );
m_global_grid->SetColSize( 3, 240 );
m_global_grid->SetColSize( 4, 100 );
m_global_grid->SetColSize( 5, 80 );
m_global_grid->SetColSize( 6, 240 );
m_global_grid->EnableDragColMove( false );
m_global_grid->EnableDragColSize( true );
m_global_grid->SetColLabelValue( 0, _("Active") );
m_global_grid->SetColLabelValue( 1, _("Visible") );
m_global_grid->SetColLabelValue( 2, _("Nickname") );
m_global_grid->SetColLabelValue( 3, _("Library Path") );
m_global_grid->SetColLabelValue( 4, _("Library Format") );
m_global_grid->SetColLabelValue( 5, _("Options") );
m_global_grid->SetColLabelValue( 6, _("Description") );
m_global_grid->SetColLabelSize( 22 );
m_global_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_global_grid->EnableDragRowSize( false );
m_global_grid->SetRowLabelSize( 0 );
m_global_grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_global_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
m_global_sizer->Add( m_global_grid, 1, wxALL|wxEXPAND, 5 );
m_global_panel->SetSizer( m_global_sizer );
m_global_panel->Layout();
m_global_sizer->Fit( m_global_panel );
m_notebook->AddPage( m_global_panel, _("Global Libraries"), true );
m_project_panel = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* m_project_sizer;
m_project_sizer = new wxBoxSizer( wxVERTICAL );
m_project_grid = new WX_GRID( m_project_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_project_grid->CreateGrid( 1, 7 );
m_project_grid->EnableEditing( true );
m_project_grid->EnableGridLines( true );
m_project_grid->EnableDragGridSize( false );
m_project_grid->SetMargins( 0, 0 );
// Columns
m_project_grid->SetColSize( 0, 48 );
m_project_grid->SetColSize( 1, 48 );
m_project_grid->SetColSize( 2, 100 );
m_project_grid->SetColSize( 3, 240 );
m_project_grid->SetColSize( 4, 100 );
m_project_grid->SetColSize( 5, 80 );
m_project_grid->SetColSize( 6, 240 );
m_project_grid->EnableDragColMove( false );
m_project_grid->EnableDragColSize( true );
m_project_grid->SetColLabelValue( 0, _("Active") );
m_project_grid->SetColLabelValue( 1, _("Visible") );
m_project_grid->SetColLabelValue( 2, _("Nickname") );
m_project_grid->SetColLabelValue( 3, _("Library Path") );
m_project_grid->SetColLabelValue( 4, _("LIbrary Format") );
m_project_grid->SetColLabelValue( 5, _("Options") );
m_project_grid->SetColLabelValue( 6, _("Description") );
m_project_grid->SetColLabelSize( 22 );
m_project_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_project_grid->EnableDragRowSize( false );
m_project_grid->SetRowLabelSize( 0 );
m_project_grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_project_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
m_project_sizer->Add( m_project_grid, 1, wxALL|wxEXPAND, 5 );
m_project_panel->SetSizer( m_project_sizer );
m_project_panel->Layout();
m_project_sizer->Fit( m_project_panel );
m_notebook->AddPage( m_project_panel, _("Project Specific Libraries"), false );
bMainSizer->Add( m_notebook, 1, wxEXPAND|wxRIGHT|wxLEFT, 5 );
wxBoxSizer* bButtonsSizer;
bButtonsSizer = new wxBoxSizer( wxHORIZONTAL );
m_append_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_append_button->SetToolTip( _("Add empty row to table") );
bButtonsSizer->Add( m_append_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
m_browseButton = new SPLIT_BUTTON( this, wxID_ANY, _( "Add Existing" ), wxDefaultPosition );
m_browseButton->SetToolTip( _("Add Existing") );
bButtonsSizer->Add( m_browseButton, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
m_move_up_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_move_up_button->SetToolTip( _("Move up") );
bButtonsSizer->Add( m_move_up_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
m_move_down_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_move_down_button->SetToolTip( _("Move down") );
bButtonsSizer->Add( m_move_down_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
bButtonsSizer->Add( 20, 0, 0, wxEXPAND, 5 );
m_delete_button = new STD_BITMAP_BUTTON( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( -1,-1 ), wxBU_AUTODRAW|0 );
m_delete_button->SetToolTip( _("Remove library from table") );
bButtonsSizer->Add( m_delete_button, 0, wxTOP|wxBOTTOM|wxRIGHT, 5 );
bButtonsSizer->Add( 20, 0, 1, wxEXPAND, 5 );
m_migrate_libs_button = new wxButton( this, wxID_ANY, _("Migrate Libraries"), wxDefaultPosition, wxDefaultSize, 0 );
bButtonsSizer->Add( m_migrate_libs_button, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5 );
bMainSizer->Add( bButtonsSizer, 0, wxEXPAND|wxALL, 8 );
wxStaticText* stPathsLabel;
stPathsLabel = new wxStaticText( this, wxID_ANY, _("Available path substitutions:"), wxDefaultPosition, wxDefaultSize, 0 );
stPathsLabel->Wrap( -1 );
bMainSizer->Add( stPathsLabel, 0, wxTOP|wxRIGHT|wxLEFT|wxEXPAND, 8 );
bMainSizer->Add( 0, 2, 0, wxEXPAND, 5 );
m_path_subs_grid = new WX_GRID( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
// Grid
m_path_subs_grid->CreateGrid( 1, 2 );
m_path_subs_grid->EnableEditing( false );
m_path_subs_grid->EnableGridLines( true );
m_path_subs_grid->EnableDragGridSize( false );
m_path_subs_grid->SetMargins( 0, 0 );
// Columns
m_path_subs_grid->SetColSize( 0, 150 );
m_path_subs_grid->SetColSize( 1, 500 );
m_path_subs_grid->AutoSizeColumns();
m_path_subs_grid->EnableDragColMove( false );
m_path_subs_grid->EnableDragColSize( true );
m_path_subs_grid->SetColLabelSize( 0 );
m_path_subs_grid->SetColLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Rows
m_path_subs_grid->EnableDragRowSize( true );
m_path_subs_grid->SetRowLabelSize( 0 );
m_path_subs_grid->SetRowLabelAlignment( wxALIGN_CENTER, wxALIGN_CENTER );
// Label Appearance
// Cell Defaults
m_path_subs_grid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
m_path_subs_grid->SetToolTip( _("This is a read-only table which shows pertinent environment variables.") );
bMainSizer->Add( m_path_subs_grid, 0, wxEXPAND|wxBOTTOM|wxRIGHT|wxLEFT, 5 );
this->SetSizer( bMainSizer );
this->Layout();
bMainSizer->Fit( this );
// Connect Events
this->Connect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::OnUpdateUI ) );
m_append_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::appendRowHandler ), NULL, this );
m_move_up_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::moveUpHandler ), NULL, this );
m_move_down_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::moveDownHandler ), NULL, this );
m_delete_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::deleteRowHandler ), NULL, this );
m_migrate_libs_button->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::onMigrateLibraries ), NULL, this );
m_path_subs_grid->Connect( wxEVT_SIZE, wxSizeEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::onSizeGrid ), NULL, this );
}
PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::~PANEL_DESIGN_BLOCK_LIB_TABLE_BASE()
{
// Disconnect Events
this->Disconnect( wxEVT_UPDATE_UI, wxUpdateUIEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::OnUpdateUI ) );
m_append_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::appendRowHandler ), NULL, this );
m_move_up_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::moveUpHandler ), NULL, this );
m_move_down_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::moveDownHandler ), NULL, this );
m_delete_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::deleteRowHandler ), NULL, this );
m_migrate_libs_button->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::onMigrateLibraries ), NULL, this );
m_path_subs_grid->Disconnect( wxEVT_SIZE, wxSizeEventHandler( PANEL_DESIGN_BLOCK_LIB_TABLE_BASE::onSizeGrid ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class STD_BITMAP_BUTTON;
class WX_GRID;
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/font.h>
#include <wx/grid.h>
#include <wx/gdicmn.h>
#include <wx/sizer.h>
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/notebook.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <widgets/split_button.h>
#include <wx/stattext.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class PANEL_DESIGN_BLOCK_LIB_TABLE_BASE
///////////////////////////////////////////////////////////////////////////////
class PANEL_DESIGN_BLOCK_LIB_TABLE_BASE : public wxPanel
{
private:
protected:
wxNotebook* m_notebook;
wxPanel* m_global_panel;
WX_GRID* m_global_grid;
wxPanel* m_project_panel;
WX_GRID* m_project_grid;
STD_BITMAP_BUTTON* m_append_button;
SPLIT_BUTTON* m_browseButton;
STD_BITMAP_BUTTON* m_move_up_button;
STD_BITMAP_BUTTON* m_move_down_button;
STD_BITMAP_BUTTON* m_delete_button;
wxButton* m_migrate_libs_button;
WX_GRID* m_path_subs_grid;
// Virtual event handlers, override them in your derived class
virtual void OnUpdateUI( wxUpdateUIEvent& event ) { event.Skip(); }
virtual void appendRowHandler( wxCommandEvent& event ) { event.Skip(); }
virtual void moveUpHandler( wxCommandEvent& event ) { event.Skip(); }
virtual void moveDownHandler( wxCommandEvent& event ) { event.Skip(); }
virtual void deleteRowHandler( wxCommandEvent& event ) { event.Skip(); }
virtual void onMigrateLibraries( wxCommandEvent& event ) { event.Skip(); }
virtual void onSizeGrid( wxSizeEvent& event ) { event.Skip(); }
public:
PANEL_DESIGN_BLOCK_LIB_TABLE_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxTAB_TRAVERSAL, const wxString& name = wxEmptyString );
~PANEL_DESIGN_BLOCK_LIB_TABLE_BASE();
};

View File

@ -47,6 +47,7 @@
#include <systemdirsappend.h>
#include <trace_helpers.h>
#include <wildcards_and_files_ext.h>
#include <confirm.h>
#include <git2.h>
#include <stdexcept>
@ -61,6 +62,7 @@
#include <api/api_server.h>
#endif
#include <design_block_lib_table.h>
// a dummy to quiet linking with EDA_BASE_FRAME::config();
#include <kiface_base.h>
@ -353,6 +355,27 @@ bool PGM_KICAD::OnPgmInit()
}
}
try
{
DESIGN_BLOCK_LIB_TABLE::LoadGlobalTable( GDesignBlockTable );
}
catch( const IO_ERROR& ioe )
{
// if we are here, a incorrect global design block library table was found.
// Incorrect global design block library table is not a fatal error:
// the user just has to edit the (partially) loaded table.
wxString msg = _( "An error occurred attempting to load the global design block library "
"table.\n"
"Please edit this global design block library table in Preferences "
"menu." );
DisplayErrorMessage( nullptr, msg, ioe.What() );
}
if( managerFrame->IsProjectActive() && GDesignBlockList.GetCount() == 0 )
GDesignBlockList.ReadCacheFromFile( Prj().GetProjectPath()
+ wxT( "design-block-info-cache" ) );
frame->Show( true );
frame->Raise();

View File

@ -210,6 +210,8 @@ void KICAD_MANAGER_FRAME::doReCreateMenuBar()
prefsMenu->Add( ACTIONS::configurePaths );
prefsMenu->Add( ACTIONS::showSymbolLibTable );
prefsMenu->Add( ACTIONS::showFootprintLibTable );
if( ADVANCED_CFG::GetCfg().m_EnableDesignBlocks )
prefsMenu->Add( ACTIONS::showDesignBlockLibTable );
prefsMenu->Add( ACTIONS::openPreferences );
prefsMenu->AppendSeparator();

View File

@ -28,6 +28,9 @@
#include <pgm_base.h>
#include <bin_mod.h>
class DESIGN_BLOCK_LIB_TABLE;
class DESIGN_BLOCK_LIST_IMPL;
/**
* PGM_KICAD
* extends PGM_BASE to bring in FileHistory() and PdfBrowser() which were moved from EDA_APP

View File

@ -22,6 +22,7 @@
#include <env_vars.h>
#include <executable_names.h>
#include <pgm_base.h>
#include <pgm_kicad.h>
#include <policy_keys.h>
#include <kiway.h>
#include <kicad_manager_frame.h>
@ -36,6 +37,7 @@
#include <tool/tool_event.h>
#include <tools/kicad_manager_actions.h>
#include <tools/kicad_manager_control.h>
#include <dialogs/panel_design_block_lib_table.h>
#include <dialogs/dialog_template_selector.h>
#include <dialogs/git/dialog_git_repository.h>
#include <git/git_clone_handler.h>
@ -43,6 +45,7 @@
#include <paths.h>
#include <wx/dir.h>
#include <wx/filedlg.h>
#include <design_block_lib_table.h>
#include "dialog_pcm.h"
#include "widgets/filedlg_new_project.h"
@ -887,6 +890,65 @@ int KICAD_MANAGER_CONTROL::Execute( const TOOL_EVENT& aEvent )
}
int KICAD_MANAGER_CONTROL::ShowDesignBlockLibTable( const TOOL_EVENT& aEvent )
{
// For some reason, after a click or a double click the bitmap button calling
// PCM keeps the focus althougt the focus was not set to this button.
// This hack force removing the focus from this button
m_frame->SetFocus();
wxSafeYield();
DESIGN_BLOCK_LIB_TABLE* globalTable = &GDesignBlockTable;
wxString globalTablePath = DESIGN_BLOCK_LIB_TABLE::GetGlobalTableFileName();
DESIGN_BLOCK_LIB_TABLE* projectTable = Prj().DesignBlockLibs();
wxString projectTablePath = Prj().DesignBlockLibTblName();
wxString msg;
DIALOG_EDIT_LIBRARY_TABLES dlg( m_frame, _( "Design Block Libraries" ) );
if( Prj().IsNullProject() )
projectTable = nullptr;
dlg.InstallPanel( new PANEL_DESIGN_BLOCK_LIB_TABLE( &dlg, &Prj(), globalTable, globalTablePath,
projectTable, projectTablePath,
Prj().GetProjectPath() ) );
if( dlg.ShowModal() == wxID_CANCEL )
return 0;
if( dlg.m_GlobalTableChanged )
{
try
{
globalTable->Save( globalTablePath );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error saving global library table:\n\n%s" ), ioe.What() );
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
}
}
if( projectTable && dlg.m_ProjectTableChanged )
{
try
{
projectTable->Save( projectTablePath );
}
catch( const IO_ERROR& ioe )
{
msg.Printf( _( "Error saving project-specific library table:\n\n%s" ), ioe.What() );
wxMessageBox( msg, _( "File Save Error" ), wxOK | wxICON_ERROR );
}
}
std::string payload = "";
Kiway.ExpressMail( FRAME_SCH, MAIL_RELOAD_LIB, payload );
Kiway.ExpressMail( FRAME_SCH_VIEWER, MAIL_RELOAD_LIB, payload );
return 0;
}
int KICAD_MANAGER_CONTROL::ShowPluginManager( const TOOL_EVENT& aEvent )
{
if( KIPLATFORM::POLICY::GetPolicyBool( POLICY_KEY_PCM )
@ -924,6 +986,7 @@ int KICAD_MANAGER_CONTROL::ShowPluginManager( const TOOL_EVENT& aEvent )
// Reset project tables
Prj().SetElem( PROJECT::ELEM::SYMBOL_LIB_TABLE, nullptr );
Prj().SetElem( PROJECT::ELEM::FPTBL, nullptr );
Prj().SetElem( PROJECT::ELEM::DESIGN_BLOCK_LIB_TABLE, nullptr );
KIWAY& kiway = m_frame->Kiway();
@ -979,5 +1042,7 @@ void KICAD_MANAGER_CONTROL::setTransitions()
Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherSch.MakeEvent() );
Go( &KICAD_MANAGER_CONTROL::Execute, KICAD_MANAGER_ACTIONS::editOtherPCB.MakeEvent() );
Go( &KICAD_MANAGER_CONTROL::ShowDesignBlockLibTable,
ACTIONS::showDesignBlockLibTable.MakeEvent() );
Go( &KICAD_MANAGER_CONTROL::ShowPluginManager, KICAD_MANAGER_ACTIONS::showPluginManager.MakeEvent() );
}

View File

@ -65,6 +65,7 @@ public:
int ShowPlayer( const TOOL_EVENT& aEvent );
int Execute( const TOOL_EVENT& aEvent );
int ShowDesignBlockLibTable( const TOOL_EVENT& aEvent );
int ShowPluginManager( const TOOL_EVENT& aEvent );
///< Set up handlers for various events.

View File

@ -51,7 +51,7 @@ protected:
std::vector<LIB_TREE_ITEM*> getFootprints( const wxString& aLibName );
bool isSymbolModel() override { return false; }
PROJECT::LIB_TYPE_T getLibType() override { return PROJECT::LIB_TYPE_T::FOOTPRINT_LIB; }
protected:
FP_LIB_TABLE* m_libs;

View File

@ -708,7 +708,7 @@ int DRAWING_TOOL::PlaceReferenceImage( const TOOL_EVENT& aEvent )
if( !image || !image->ReadImageFile( fullFilename ) )
{
wxMessageBox( _( "Could not load image from '%s'." ), fullFilename );
wxMessageBox( wxString::Format(_( "Could not load image from '%s'." ), fullFilename ) );
delete image;
image = nullptr;
continue;