Browse Source

ADDED: Multi-selection cut/copy/paste in symbol editor library tree

Fixes https://gitlab.com/kicad/code/kicad/-/issues/11505
7.0
Jon Evans 3 years ago
parent
commit
da9be1a812
  1. 29
      common/widgets/lib_tree.cpp
  2. 4
      eeschema/dialogs/dialog_choose_symbol.cpp
  3. 19
      eeschema/symbol_editor/symbol_edit_frame.cpp
  4. 9
      eeschema/symbol_editor/symbol_edit_frame.h
  5. 114
      eeschema/symbol_editor/symbol_editor.cpp
  6. 31
      eeschema/tools/symbol_editor_control.cpp
  7. 3
      eeschema/widgets/symbol_tree_pane.cpp
  8. 30
      include/widgets/lib_tree.h
  9. 2
      pcbnew/dialogs/dialog_choose_footprint.cpp

29
common/widgets/lib_tree.cpp

@ -34,7 +34,7 @@
LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, WIDGETS aWidgets,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, FLAGS aFlags,
HTML_WINDOW* aDetails ) :
wxPanel( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxWANTS_CHARS | wxTAB_TRAVERSAL | wxNO_BORDER ),
@ -44,7 +44,7 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
wxBoxSizer* sizer = new wxBoxSizer( wxVERTICAL );
// Search text control
if( aWidgets & SEARCH )
if( aFlags & SEARCH )
{
wxBoxSizer* search_sizer = new wxBoxSizer( wxHORIZONTAL );
@ -80,17 +80,17 @@ LIB_TREE::LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
}
// Tree control
m_tree_ctrl = new wxDataViewCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxDV_SINGLE );
int dvFlags = ( aFlags & MULTISELECT ) ? wxDV_MULTIPLE : wxDV_SINGLE;
m_tree_ctrl = new wxDataViewCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, dvFlags );
m_adapter->AttachTo( m_tree_ctrl );
if( aWidgets & DETAILS )
if( aFlags & DETAILS )
sizer->AddSpacer( 5 );
sizer->Add( m_tree_ctrl, 5, wxRIGHT | wxBOTTOM | wxEXPAND, 1 );
// Description panel
if( aWidgets & DETAILS )
if( aFlags & DETAILS )
{
if( !aDetails )
{
@ -171,6 +171,23 @@ LIB_ID LIB_TREE::GetSelectedLibId( int* aUnit ) const
}
int LIB_TREE::GetSelectedLibIds( std::vector<LIB_ID>& aSelection, std::vector<int>* aUnit ) const
{
wxDataViewItemArray selection;
int count = m_tree_ctrl->GetSelections( selection );
for( const wxDataViewItem& item : selection )
{
aSelection.emplace_back( m_adapter->GetAliasFor( item ) );
if( aUnit )
aUnit->emplace_back( m_adapter->GetUnitFor( item ) );
}
return count;
}
LIB_TREE_NODE* LIB_TREE::GetCurrentTreeNode() const
{
wxDataViewItem sel = m_tree_ctrl->GetSelection();

4
eeschema/dialogs/dialog_choose_symbol.cpp

@ -126,8 +126,8 @@ DIALOG_CHOOSE_SYMBOL::DIALOG_CHOOSE_SYMBOL( SCH_BASE_FRAME* aParent, const wxStr
wxBoxSizer* treeSizer = new wxBoxSizer( wxVERTICAL );
treePanel->SetSizer( treeSizer );
m_tree = new LIB_TREE( treePanel, Prj().SchSymbolLibTable(), aAdapter, LIB_TREE::WIDGETS::ALL,
m_details );
m_tree = new LIB_TREE( treePanel, Prj().SchSymbolLibTable(), aAdapter,
LIB_TREE::FLAGS::ALL_WIDGETS, m_details );
treeSizer->Add( m_tree, 1, wxEXPAND | wxALL, 5 );
treePanel->Layout();

19
eeschema/symbol_editor/symbol_edit_frame.cpp

@ -947,6 +947,17 @@ LIB_ID SYMBOL_EDIT_FRAME::GetTreeLIBID( int* aUnit ) const
}
int SYMBOL_EDIT_FRAME::GetTreeSelectionCount() const
{
return m_treePane->GetLibTree()->GetSelectionCount();
}
int SYMBOL_EDIT_FRAME::GetTreeLIBIDs( std::vector<LIB_ID>& aSelection ) const
{
return m_treePane->GetLibTree()->GetSelectedLibIds( aSelection );
}
LIB_SYMBOL* SYMBOL_EDIT_FRAME::getTargetSymbol() const
{
LIB_ID libId = GetTreeLIBID();
@ -975,6 +986,14 @@ LIB_ID SYMBOL_EDIT_FRAME::GetTargetLibId() const
}
std::vector<LIB_ID> SYMBOL_EDIT_FRAME::GetSelectedLibIds() const
{
std::vector<LIB_ID> ids;
GetTreeLIBIDs( ids );
return ids;
}
LIB_TREE_NODE* SYMBOL_EDIT_FRAME::GetCurrentTreeNode() const
{
return m_treePane->GetLibTree()->GetCurrentTreeNode();

9
eeschema/symbol_editor/symbol_edit_frame.h

@ -98,6 +98,10 @@ public:
*/
LIB_ID GetTreeLIBID( int* aUnit = nullptr ) const;
int GetTreeSelectionCount() const;
int GetTreeLIBIDs( std::vector<LIB_ID>& aSelection ) const;
/**
* Return the current symbol being edited or NULL if none selected.
*
@ -328,6 +332,11 @@ public:
*/
LIB_ID GetTargetLibId() const;
/**
* @return a list of selected items in the symbol tree
*/
std::vector<LIB_ID> GetSelectedLibIds() const;
void FocusOnLibId( const LIB_ID& aLibID );
/**

114
eeschema/symbol_editor/symbol_editor.cpp

@ -784,40 +784,47 @@ void SYMBOL_EDIT_FRAME::UpdateAfterSymbolProperties( wxString* aOldName )
void SYMBOL_EDIT_FRAME::DeleteSymbolFromLibrary()
{
LIB_ID libId = GetTargetLibId();
std::vector<LIB_ID> toDelete = GetSelectedLibIds();
if( m_libMgr->IsSymbolModified( libId.GetLibItemName(), libId.GetLibNickname() )
&& !IsOK( this, wxString::Format( _( "The symbol '%s' has been modified.\n"
"Do you want to remove it from the library?" ),
libId.GetUniStringLibItemName() ) ) )
{
return;
}
if( toDelete.empty() )
toDelete.emplace_back( GetTargetLibId() );
if( m_libMgr->HasDerivedSymbols( libId.GetLibItemName(), libId.GetLibNickname() ) )
for( LIB_ID& libId : toDelete )
{
wxString msg;
if( m_libMgr->IsSymbolModified( libId.GetLibItemName(), libId.GetLibNickname() )
&& !IsOK( this, wxString::Format( _( "The symbol '%s' has been modified.\n"
"Do you want to remove it from the library?" ),
libId.GetUniStringLibItemName() ) ) )
{
continue;
}
if( m_libMgr->HasDerivedSymbols( libId.GetLibItemName(), libId.GetLibNickname() ) )
{
wxString msg;
msg.Printf( _( "The symbol %s is used to derive other symbols.\n"
msg.Printf(
_( "The symbol %s is used to derive other symbols.\n"
"Deleting this symbol will delete all of the symbols derived from it.\n\n"
"Do you wish to delete this symbol and all of its derivatives?" ),
libId.GetLibItemName().wx_str() );
wxMessageDialog::ButtonLabel yesButtonLabel( _( "Delete Symbol" ) );
wxMessageDialog::ButtonLabel noButtonLabel( _( "Keep Symbol" ) );
wxMessageDialog::ButtonLabel yesButtonLabel( _( "Delete Symbol" ) );
wxMessageDialog::ButtonLabel noButtonLabel( _( "Keep Symbol" ) );
wxMessageDialog dlg( this, msg, _( "Warning" ),
wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
dlg.SetYesNoLabels( yesButtonLabel, noButtonLabel );
wxMessageDialog dlg( this, msg, _( "Warning" ),
wxYES_NO | wxYES_DEFAULT | wxICON_QUESTION | wxCENTER );
dlg.SetYesNoLabels( yesButtonLabel, noButtonLabel );
if( dlg.ShowModal() == wxID_NO )
return;
}
if( dlg.ShowModal() == wxID_NO )
continue;
}
if( IsCurrentSymbol( libId ) )
emptyScreen();
if( IsCurrentSymbol( libId ) )
emptyScreen();
m_libMgr->RemoveSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
m_libMgr->RemoveSymbol( libId.GetLibItemName(), libId.GetLibNickname() );
}
m_treePane->GetLibTree()->RefreshLibTree();
}
@ -825,17 +832,24 @@ void SYMBOL_EDIT_FRAME::DeleteSymbolFromLibrary()
void SYMBOL_EDIT_FRAME::CopySymbolToClipboard()
{
int dummyUnit;
LIB_ID libId = m_treePane->GetLibTree()->GetSelectedLibId( &dummyUnit );
LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(),
libId.GetLibNickname() );
std::vector<LIB_ID> symbols;
if( !symbol )
if( GetTreeLIBIDs( symbols ) == 0 )
return;
std::unique_ptr< LIB_SYMBOL> tmp = symbol->Flatten();
STRING_FORMATTER formatter;
SCH_SEXPR_PLUGIN::FormatLibSymbol( tmp.get(), formatter );
for( LIB_ID& libId : symbols )
{
LIB_SYMBOL* symbol = m_libMgr->GetBufferedSymbol( libId.GetLibItemName(),
libId.GetLibNickname() );
if( !symbol )
continue;
std::unique_ptr<LIB_SYMBOL> tmp = symbol->Flatten();
SCH_SEXPR_PLUGIN::FormatLibSymbol( tmp.get(), formatter );
}
wxLogNull doNotLog; // disable logging of failed clipboard actions
@ -863,6 +877,8 @@ void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
LIB_SYMBOL* srcSymbol = nullptr;
LIB_SYMBOL* newSymbol = nullptr;
std::vector<LIB_SYMBOL*> newSymbols;
if( aFromClipboard )
{
wxLogNull doNotLog; // disable logging of failed clipboard actions
@ -881,17 +897,26 @@ void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
clipboard->GetData( data );
wxString symbolSource = data.GetText();
STRING_LINE_READER reader( TO_UTF8( symbolSource ), "Clipboard" );
std::unique_ptr<STRING_LINE_READER> reader = std::make_unique<STRING_LINE_READER>( TO_UTF8( symbolSource ), "Clipboard" );
try
do
{
newSymbol = SCH_SEXPR_PLUGIN::ParseLibSymbol( reader );
}
catch( IO_ERROR& e )
{
wxLogMessage( "Can not paste: %s", e.Problem() );
return;
try
{
newSymbol = SCH_SEXPR_PLUGIN::ParseLibSymbol( *reader );
}
catch( IO_ERROR& e )
{
wxLogMessage( "Can not paste: %s", e.Problem() );
break;
}
if( newSymbol )
newSymbols.emplace_back( newSymbol );
reader.reset( new STRING_LINE_READER( *reader ) );
}
while( newSymbol );
}
else
{
@ -899,7 +924,7 @@ void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
wxCHECK( srcSymbol, /* void */ );
newSymbol = new LIB_SYMBOL( *srcSymbol );
newSymbols.emplace_back( new LIB_SYMBOL( *srcSymbol ) );
// Derive from same parent.
if( srcSymbol->IsAlias() )
@ -912,16 +937,19 @@ void SYMBOL_EDIT_FRAME::DuplicateSymbol( bool aFromClipboard )
}
}
if( !newSymbol )
if( newSymbols.empty() )
return;
ensureUniqueName( newSymbol, lib );
m_libMgr->UpdateSymbol( newSymbol, lib );
for( LIB_SYMBOL* symbol : newSymbols )
{
ensureUniqueName( symbol, lib );
m_libMgr->UpdateSymbol( symbol, lib );
LoadOneLibrarySymbolAux( newSymbol, lib, GetUnit(), GetConvert() );
LoadOneLibrarySymbolAux( symbol, lib, GetUnit(), GetConvert() );
}
SyncLibraries( false );
m_treePane->GetLibTree()->SelectLibId( LIB_ID( lib, newSymbol->GetName() ) );
m_treePane->GetLibTree()->SelectLibId( LIB_ID( lib, newSymbols[0]->GetName() ) );
delete newSymbol;
}

31
eeschema/tools/symbol_editor_control.cpp

@ -95,6 +95,11 @@ bool SYMBOL_EDITOR_CONTROL::Init()
LIB_ID sel = editFrame->GetTargetLibId();
return !sel.GetLibNickname().empty() && !sel.GetLibItemName().empty();
};
auto multiSelectedCondition =
[ editFrame ]( const SELECTION& aSel )
{
return editFrame->GetTreeSelectionCount() > 1;
};
ctxMenu.AddItem( ACTIONS::pinLibrary, unpinnedLibSelectedCondition );
ctxMenu.AddItem( ACTIONS::unpinLibrary, pinnedLibSelectedCondition );
@ -109,12 +114,12 @@ bool SYMBOL_EDITOR_CONTROL::Init()
ctxMenu.AddItem( ACTIONS::revert, symbolSelectedCondition || libInferredCondition );
ctxMenu.AddSeparator();
ctxMenu.AddItem( EE_ACTIONS::cutSymbol, symbolSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::copySymbol, symbolSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::cutSymbol, symbolSelectedCondition || multiSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::copySymbol, symbolSelectedCondition || multiSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::pasteSymbol, libInferredCondition );
ctxMenu.AddItem( EE_ACTIONS::duplicateSymbol, symbolSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::renameSymbol, symbolSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::deleteSymbol, symbolSelectedCondition );
ctxMenu.AddItem( EE_ACTIONS::deleteSymbol, symbolSelectedCondition || multiSelectedCondition );
ctxMenu.AddSeparator();
ctxMenu.AddItem( EE_ACTIONS::importSymbol, libInferredCondition );
@ -247,16 +252,24 @@ int SYMBOL_EDITOR_CONTROL::CutCopyDelete( const TOOL_EVENT& aEvt )
if( aEvt.IsAction( &EE_ACTIONS::cutSymbol ) || aEvt.IsAction( &EE_ACTIONS::deleteSymbol ) )
{
LIB_ID sel = editFrame->GetTreeLIBID();
const wxString& libName = sel.GetLibNickname();
wxString msg;
bool hasWritableLibs = false;
wxString msg;
if( editFrame->GetLibManager().IsLibraryReadOnly( libName ) )
for( LIB_ID& sel : editFrame->GetSelectedLibIds() )
{
msg.Printf( _( "Symbol library '%s' is not writable." ), libName );
const wxString& libName = sel.GetLibNickname();
if( editFrame->GetLibManager().IsLibraryReadOnly( libName ) )
msg.Printf( _( "Symbol library '%s' is not writable." ), libName );
else
hasWritableLibs = true;
}
if( !msg.IsEmpty() )
m_frame->ShowInfoBarError( msg );
if( !hasWritableLibs )
return 0;
}
editFrame->DeleteSymbolFromLibrary();
}

3
eeschema/widgets/symbol_tree_pane.cpp

@ -40,7 +40,8 @@ SYMBOL_TREE_PANE::SYMBOL_TREE_PANE( SYMBOL_EDIT_FRAME* aParent, SYMBOL_LIBRARY_M
// Create widgets
wxBoxSizer* boxSizer = new wxBoxSizer( wxVERTICAL );
m_tree = new LIB_TREE( this, &SYMBOL_LIB_TABLE::GetGlobalLibTable(), m_libMgr->GetAdapter(),
LIB_TREE::SEARCH );
static_cast<LIB_TREE::FLAGS>( LIB_TREE::SEARCH |
LIB_TREE::MULTISELECT ) );
boxSizer->Add( m_tree, 1, wxEXPAND, 5 );
SetSizer( boxSizer ); // should remove the previous sizer according to wxWidgets docs

30
include/widgets/lib_tree.h

@ -45,8 +45,15 @@ class LIB_TABLE;
class LIB_TREE : public wxPanel
{
public:
///< Flags to select extra widgets
enum WIDGETS { NONE = 0x00, SEARCH = 0x01, DETAILS = 0x02, ALL = 0xFF };
///< Flags to select extra widgets and options
enum FLAGS
{
NONE = 0x00,
SEARCH = 0x01,
DETAILS = 0x02,
ALL_WIDGETS = 0x0F,
MULTISELECT = 0x10
};
/**
* Construct a symbol tree.
@ -54,12 +61,12 @@ public:
* @param aParent parent window containing this tree widget
* @param aLibTable table containing libraries and items to display
* @param aAdapter a LIB_TREE_MODEL_ADAPTER instance to use
* @param aWidgets selection of sub-widgets to include
* @param aFlags selection of sub-widgets to include and other options
* @param aDetails if not null, a custom HTML_WINDOW to hold symbol details. If null this
* will be created inside the LIB_TREE.
*/
LIB_TREE( wxWindow* aParent, LIB_TABLE* aLibTable,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, WIDGETS aWidgets = ALL,
wxObjectDataPtr<LIB_TREE_MODEL_ADAPTER>& aAdapter, FLAGS aFlags = ALL_WIDGETS,
HTML_WINDOW* aDetails = nullptr );
~LIB_TREE() override;
@ -75,6 +82,21 @@ public:
*/
LIB_ID GetSelectedLibId( int* aUnit = nullptr ) const;
int GetSelectionCount() const
{
return m_tree_ctrl->GetSelectedItemsCount();
}
/**
* Retrieves a list of selections for trees that allow multi-selection
* @see GetSelectedLibId for details on how aUnit will be filled.
* @param aSelection will be filled with a list of selected LIB_IDs
* @param aUnit is an optional pointer to a list to fill with unit numbers
* @return the number of selected items
*/
int GetSelectedLibIds( std::vector<LIB_ID>& aSelection,
std::vector<int>* aUnit = nullptr ) const;
LIB_TREE_NODE* GetCurrentTreeNode() const;
/**

2
pcbnew/dialogs/dialog_choose_footprint.cpp

@ -82,7 +82,7 @@ DIALOG_CHOOSE_FOOTPRINT::DIALOG_CHOOSE_FOOTPRINT( PCB_BASE_FRAME* aParent,
sizer->Add( m_vsplitter, 1, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 5 );
m_tree = new LIB_TREE( m_hsplitter, Prj().PcbFootprintLibs(), aAdapter,
LIB_TREE::WIDGETS::ALL, details );
LIB_TREE::FLAGS::ALL_WIDGETS, details );
m_hsplitter->SetSashGravity( 0.8 );
m_hsplitter->SetMinimumPaneSize( 20 );

Loading…
Cancel
Save