2023-06-20 21:57:20 -04:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KICAD, a free EDA CAD application.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2020 CERN
|
2025-01-01 13:30:11 -08:00
|
|
|
* Copyright The KiCad Developers, see AUTHORS.txt for contributors.
|
2023-06-20 21:57:20 -04:00
|
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
|
|
*
|
|
|
|
* 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 "sch_properties_panel.h"
|
|
|
|
|
2023-11-01 00:38:43 +00:00
|
|
|
#include <font/fontconfig.h>
|
|
|
|
#include <pgm_base.h>
|
2023-06-20 21:57:20 -04:00
|
|
|
#include <connection_graph.h>
|
|
|
|
#include <properties/pg_editors.h>
|
|
|
|
#include <properties/pg_properties.h>
|
|
|
|
#include <properties/property_mgr.h>
|
|
|
|
#include <sch_commit.h>
|
|
|
|
#include <sch_edit_frame.h>
|
2024-05-16 09:15:40 -07:00
|
|
|
#include <symbol_edit_frame.h>
|
|
|
|
#include <symbol_viewer_frame.h>
|
2023-06-20 21:57:20 -04:00
|
|
|
#include <schematic.h>
|
2025-09-02 18:29:20 -07:00
|
|
|
#include <sch_symbol.h>
|
|
|
|
#include <sch_field.h>
|
2025-09-09 07:10:53 -07:00
|
|
|
#include <template_fieldnames.h>
|
2023-06-20 21:57:20 -04:00
|
|
|
#include <settings/color_settings.h>
|
|
|
|
#include <string_utils.h>
|
|
|
|
#include <tool/tool_manager.h>
|
2025-03-12 14:41:25 +00:00
|
|
|
#include <tools/sch_selection_tool.h>
|
2025-09-02 18:29:20 -07:00
|
|
|
#include <set>
|
2023-06-20 21:57:20 -04:00
|
|
|
|
2025-09-02 18:29:20 -07:00
|
|
|
static const wxString MISSING_FIELD_SENTINEL = wxS( "\uE000" );
|
|
|
|
|
|
|
|
class SCH_SYMBOL_FIELD_PROPERTY : public PROPERTY_BASE
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
SCH_SYMBOL_FIELD_PROPERTY( const wxString& aName ) :
|
|
|
|
PROPERTY_BASE( aName ),
|
|
|
|
m_name( aName )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t OwnerHash() const override { return TYPE_HASH( SCH_SYMBOL ); }
|
|
|
|
size_t BaseHash() const override { return TYPE_HASH( SCH_SYMBOL ); }
|
|
|
|
size_t TypeHash() const override { return TYPE_HASH( wxString ); }
|
|
|
|
|
|
|
|
bool Writeable( INSPECTABLE* aObject ) const override
|
|
|
|
{
|
|
|
|
return PROPERTY_BASE::Writeable( aObject );
|
|
|
|
}
|
|
|
|
|
|
|
|
void setter( void* obj, wxAny& v ) override
|
|
|
|
{
|
|
|
|
wxString value;
|
|
|
|
|
|
|
|
if( !v.GetAs( &value ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
SCH_SYMBOL* symbol = reinterpret_cast<SCH_SYMBOL*>( obj );
|
|
|
|
SCH_FIELD* field = symbol->GetField( m_name );
|
|
|
|
|
|
|
|
if( !field )
|
|
|
|
{
|
|
|
|
SCH_FIELD newField( symbol, FIELD_T::USER, m_name );
|
|
|
|
newField.SetText( value );
|
|
|
|
symbol->AddField( newField );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
field->SetText( value );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
wxAny getter( const void* obj ) const override
|
|
|
|
{
|
|
|
|
const SCH_SYMBOL* symbol = reinterpret_cast<const SCH_SYMBOL*>( obj );
|
|
|
|
const SCH_FIELD* field = symbol->GetField( m_name );
|
|
|
|
|
|
|
|
if( field )
|
|
|
|
return wxAny( field->GetText() );
|
|
|
|
else
|
|
|
|
return wxAny( MISSING_FIELD_SENTINEL );
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
wxString m_name;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::set<wxString> SCH_PROPERTIES_PANEL::m_currentFieldNames;
|
2023-06-20 21:57:20 -04:00
|
|
|
|
|
|
|
SCH_PROPERTIES_PANEL::SCH_PROPERTIES_PANEL( wxWindow* aParent, SCH_BASE_FRAME* aFrame ) :
|
|
|
|
PROPERTIES_PANEL( aParent, aFrame ),
|
|
|
|
m_frame( aFrame ),
|
|
|
|
m_propMgr( PROPERTY_MANAGER::Instance() )
|
|
|
|
{
|
|
|
|
m_propMgr.Rebuild();
|
|
|
|
bool found = false;
|
|
|
|
|
|
|
|
wxASSERT( wxPGGlobalVars );
|
|
|
|
|
|
|
|
wxString editorKey = PG_UNIT_EDITOR::BuildEditorName( m_frame );
|
|
|
|
|
|
|
|
auto it = wxPGGlobalVars->m_mapEditorClasses.find( editorKey );
|
|
|
|
|
|
|
|
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
|
|
|
{
|
|
|
|
m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( it->second );
|
|
|
|
m_unitEditorInstance->UpdateFrame( m_frame );
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( !found )
|
|
|
|
{
|
|
|
|
PG_UNIT_EDITOR* new_editor = new PG_UNIT_EDITOR( m_frame );
|
|
|
|
m_unitEditorInstance = static_cast<PG_UNIT_EDITOR*>( wxPropertyGrid::RegisterEditorClass( new_editor ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_CHECKBOX_EDITOR::EDITOR_NAME );
|
|
|
|
|
|
|
|
if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
|
|
|
|
{
|
|
|
|
PG_CHECKBOX_EDITOR* cbEditor = new PG_CHECKBOX_EDITOR();
|
|
|
|
m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( wxPropertyGrid::RegisterEditorClass( cbEditor ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_checkboxEditorInstance = static_cast<PG_CHECKBOX_EDITOR*>( it->second );
|
|
|
|
}
|
2023-06-28 23:54:27 -04:00
|
|
|
|
|
|
|
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_COLOR_EDITOR::EDITOR_NAME );
|
|
|
|
|
|
|
|
if( it == wxPGGlobalVars->m_mapEditorClasses.end() )
|
|
|
|
{
|
|
|
|
PG_COLOR_EDITOR* colorEditor = new PG_COLOR_EDITOR();
|
|
|
|
m_colorEditorInstance = static_cast<PG_COLOR_EDITOR*>( wxPropertyGrid::RegisterEditorClass( colorEditor ) );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_colorEditorInstance = static_cast<PG_COLOR_EDITOR*>( it->second );
|
|
|
|
}
|
2025-09-09 07:10:53 -07:00
|
|
|
|
|
|
|
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
|
|
|
|
|
|
|
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
|
|
|
{
|
|
|
|
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( it->second );
|
|
|
|
m_fpEditorInstance->UpdateFrame( m_frame );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PG_FPID_EDITOR* fpEditor = new PG_FPID_EDITOR( m_frame );
|
|
|
|
m_fpEditorInstance = static_cast<PG_FPID_EDITOR*>( wxPropertyGrid::RegisterEditorClass( fpEditor ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
it = wxPGGlobalVars->m_mapEditorClasses.find( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
|
|
|
|
|
|
|
if( it != wxPGGlobalVars->m_mapEditorClasses.end() )
|
|
|
|
{
|
|
|
|
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( it->second );
|
|
|
|
m_urlEditorInstance->UpdateFrame( m_frame );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PG_URL_EDITOR* urlEditor = new PG_URL_EDITOR( m_frame );
|
|
|
|
m_urlEditorInstance = static_cast<PG_URL_EDITOR*>( wxPropertyGrid::RegisterEditorClass( urlEditor ) );
|
|
|
|
}
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
SCH_PROPERTIES_PANEL::~SCH_PROPERTIES_PANEL()
|
|
|
|
{
|
|
|
|
m_unitEditorInstance->UpdateFrame( nullptr );
|
2025-09-09 07:10:53 -07:00
|
|
|
m_fpEditorInstance->UpdateFrame( nullptr );
|
|
|
|
m_urlEditorInstance->UpdateFrame( nullptr );
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SCH_PROPERTIES_PANEL::UpdateData()
|
|
|
|
{
|
2025-03-12 14:41:25 +00:00
|
|
|
SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
|
|
|
|
const SELECTION& selection = selectionTool->GetSelection();
|
2023-06-20 21:57:20 -04:00
|
|
|
|
|
|
|
// Will actually just be updatePropertyValues() if selection hasn't changed
|
|
|
|
rebuildProperties( selection );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SCH_PROPERTIES_PANEL::AfterCommit()
|
|
|
|
{
|
2025-03-12 14:41:25 +00:00
|
|
|
SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
|
|
|
|
const SELECTION& selection = selectionTool->GetSelection();
|
2023-06-20 21:57:20 -04:00
|
|
|
|
|
|
|
rebuildProperties( selection );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2025-09-02 18:29:20 -07:00
|
|
|
void SCH_PROPERTIES_PANEL::rebuildProperties( const SELECTION& aSelection )
|
|
|
|
{
|
|
|
|
m_currentFieldNames.clear();
|
|
|
|
|
|
|
|
for( EDA_ITEM* item : aSelection )
|
|
|
|
{
|
|
|
|
if( item->Type() != SCH_SYMBOL_T )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
SCH_SYMBOL* symbol = static_cast<SCH_SYMBOL*>( item );
|
|
|
|
|
|
|
|
for( const SCH_FIELD& field : symbol->GetFields() )
|
|
|
|
{
|
|
|
|
if( field.IsPrivate() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
m_currentFieldNames.insert( field.GetCanonicalName() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const wxString groupFields = _HKI( "Fields" );
|
|
|
|
|
|
|
|
for( const wxString& name : m_currentFieldNames )
|
|
|
|
{
|
|
|
|
if( !m_propMgr.GetProperty( TYPE_HASH( SCH_SYMBOL ), name ) )
|
|
|
|
{
|
|
|
|
m_propMgr.AddProperty( new SCH_SYMBOL_FIELD_PROPERTY( name ), groupFields )
|
|
|
|
.SetAvailableFunc( [name]( INSPECTABLE* )
|
|
|
|
{
|
|
|
|
return SCH_PROPERTIES_PANEL::m_currentFieldNames.count( name );
|
|
|
|
} );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PROPERTIES_PANEL::rebuildProperties( aSelection );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-06-20 21:57:20 -04:00
|
|
|
wxPGProperty* SCH_PROPERTIES_PANEL::createPGProperty( const PROPERTY_BASE* aProperty ) const
|
|
|
|
{
|
2023-06-29 22:53:06 -04:00
|
|
|
wxPGProperty* prop = PGPropertyFactory( aProperty, m_frame );
|
|
|
|
|
|
|
|
if( auto colorProp = dynamic_cast<PGPROPERTY_COLOR4D*>( prop ) )
|
|
|
|
{
|
|
|
|
COLOR4D bg = m_frame->GetColorSettings()->GetColor( LAYER_SCHEMATIC_BACKGROUND );
|
|
|
|
colorProp->SetBackgroundColor( bg );
|
|
|
|
}
|
|
|
|
|
2025-09-09 07:10:53 -07:00
|
|
|
if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::FOOTPRINT ) )
|
|
|
|
prop->SetEditor( PG_FPID_EDITOR::BuildEditorName( m_frame ) );
|
|
|
|
else if( aProperty->Name() == GetCanonicalFieldName( FIELD_T::DATASHEET ) )
|
|
|
|
prop->SetEditor( PG_URL_EDITOR::BuildEditorName( m_frame ) );
|
|
|
|
|
2023-06-29 22:53:06 -04:00
|
|
|
return prop;
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PROPERTY_BASE* SCH_PROPERTIES_PANEL::getPropertyFromEvent( const wxPropertyGridEvent& aEvent ) const
|
|
|
|
{
|
2025-03-12 14:41:25 +00:00
|
|
|
SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
|
|
|
|
const SELECTION& selection = selectionTool->GetSelection();
|
|
|
|
SCH_ITEM* firstItem = static_cast<SCH_ITEM*>( selection.Front() );
|
2023-06-20 21:57:20 -04:00
|
|
|
|
|
|
|
wxCHECK_MSG( firstItem, nullptr,
|
|
|
|
wxT( "getPropertyFromEvent for a property with nothing selected!") );
|
|
|
|
|
|
|
|
PROPERTY_BASE* property = m_propMgr.GetProperty( TYPE_HASH( *firstItem ),
|
|
|
|
aEvent.GetPropertyName() );
|
|
|
|
wxCHECK_MSG( property, nullptr,
|
|
|
|
wxT( "getPropertyFromEvent for a property not found on the selected item!" ) );
|
|
|
|
|
|
|
|
return property;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SCH_PROPERTIES_PANEL::valueChanging( wxPropertyGridEvent& aEvent )
|
|
|
|
{
|
2025-04-01 15:26:56 +01:00
|
|
|
if( m_SuppressGridChangeEvents )
|
|
|
|
return;
|
|
|
|
|
2025-03-12 14:41:25 +00:00
|
|
|
SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
|
|
|
|
const SELECTION& selection = selectionTool->GetSelection();
|
2025-07-11 18:13:27 +01:00
|
|
|
EDA_ITEM* frontItem = selection.Front();
|
2023-06-20 21:57:20 -04:00
|
|
|
|
2025-07-11 18:13:27 +01:00
|
|
|
if( !frontItem )
|
|
|
|
return;
|
2023-06-20 21:57:20 -04:00
|
|
|
|
2025-07-11 18:13:27 +01:00
|
|
|
if( PROPERTY_BASE* property = getPropertyFromEvent( aEvent ) )
|
2023-06-20 21:57:20 -04:00
|
|
|
{
|
2025-07-11 18:13:27 +01:00
|
|
|
wxVariant newValue = aEvent.GetPropertyValue();
|
|
|
|
|
|
|
|
if( VALIDATOR_RESULT validationFailure = property->Validate( newValue.GetAny(), frontItem ) )
|
|
|
|
{
|
|
|
|
wxString errorMsg = wxString::Format( wxS( "%s: %s" ), wxGetTranslation( property->Name() ),
|
|
|
|
validationFailure->get()->Format( m_frame ) );
|
|
|
|
m_frame->ShowInfoBarError( errorMsg );
|
|
|
|
aEvent.Veto();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aEvent.Skip();
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SCH_PROPERTIES_PANEL::valueChanged( wxPropertyGridEvent& aEvent )
|
|
|
|
{
|
2025-04-01 15:26:56 +01:00
|
|
|
if( m_SuppressGridChangeEvents )
|
|
|
|
return;
|
|
|
|
|
2025-03-12 14:41:25 +00:00
|
|
|
SCH_SELECTION_TOOL* selectionTool = m_frame->GetToolManager()->GetTool<SCH_SELECTION_TOOL>();
|
|
|
|
const SELECTION& selection = selectionTool->GetSelection();
|
2025-08-15 10:21:03 -07:00
|
|
|
|
|
|
|
wxCHECK( getPropertyFromEvent( aEvent ), /* void */ );
|
2023-06-20 21:57:20 -04:00
|
|
|
|
2025-03-12 14:41:25 +00:00
|
|
|
wxVariant newValue = aEvent.GetPropertyValue();
|
|
|
|
SCH_COMMIT changes( m_frame );
|
2023-06-20 21:57:20 -04:00
|
|
|
SCH_SCREEN* screen = m_frame->GetScreen();
|
|
|
|
|
2023-11-28 21:57:41 -05:00
|
|
|
PROPERTY_COMMIT_HANDLER handler( &changes );
|
|
|
|
|
2023-06-20 21:57:20 -04:00
|
|
|
for( EDA_ITEM* edaItem : selection )
|
|
|
|
{
|
2025-06-12 11:20:33 +01:00
|
|
|
if( !edaItem->IsSCH_ITEM() )
|
|
|
|
continue;
|
|
|
|
|
2023-06-20 21:57:20 -04:00
|
|
|
SCH_ITEM* item = static_cast<SCH_ITEM*>( edaItem );
|
2025-08-15 10:21:03 -07:00
|
|
|
PROPERTY_BASE* property = m_propMgr.GetProperty( TYPE_HASH( *item ),
|
|
|
|
aEvent.GetPropertyName() );
|
|
|
|
wxCHECK( property, /* void */ );
|
2025-07-03 20:53:12 -06:00
|
|
|
|
|
|
|
if( item->Type() == SCH_TABLECELL_T )
|
|
|
|
changes.Modify( item->GetParent(), screen, RECURSE_MODE::NO_RECURSE );
|
|
|
|
else
|
|
|
|
changes.Modify( item, screen, RECURSE_MODE::NO_RECURSE );
|
|
|
|
|
2023-06-20 21:57:20 -04:00
|
|
|
item->Set( property, newValue );
|
2024-07-20 16:01:50 +01:00
|
|
|
|
|
|
|
if( SCH_SYMBOL* symbol = dynamic_cast<SCH_SYMBOL*>( item ) )
|
|
|
|
symbol->SyncOtherUnits( symbol->Schematic()->CurrentSheet(), changes, property );
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
|
2024-02-03 13:43:41 +00:00
|
|
|
changes.Push( _( "Edit Properties" ) );
|
2023-06-20 21:57:20 -04:00
|
|
|
m_frame->Refresh();
|
|
|
|
|
|
|
|
// Perform grid updates as necessary based on value change
|
|
|
|
AfterCommit();
|
2024-04-23 17:01:49 -04:00
|
|
|
|
|
|
|
aEvent.Skip();
|
2023-06-20 21:57:20 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-03 10:57:26 -05:00
|
|
|
void SCH_PROPERTIES_PANEL::OnLanguageChanged( wxCommandEvent& aEvent )
|
2023-12-22 18:34:26 -05:00
|
|
|
{
|
2024-03-03 10:57:26 -05:00
|
|
|
PROPERTIES_PANEL::OnLanguageChanged( aEvent );
|
2024-04-23 17:01:49 -04:00
|
|
|
|
|
|
|
aEvent.Skip();
|
2023-12-22 18:34:26 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|