39#include <wx/clipbrd.h>
40#include <wx/dataobj.h>
41#include <wx/settings.h>
42#include <wx/stattext.h>
43#include <wx/propgrid/advprops.h>
49#if !wxCHECK_VERSION( 3, 3, 0 )
58 wxPropertyGrid( aParent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxPG_DEFAULT_STYLE | wxPG_TOOLTIPS )
62 void ScrollWindow(
int aDx,
int aDy,
const wxRect* aRect =
nullptr )
override
64 wxPropertyGrid::ScrollWindow( aDx, aDy, aRect );
67 panel->positionCategoryButtons();
74 wxStatusBar* GetStatusBar()
override {
return nullptr; }
85 wxBoxSizer* mainSizer =
new wxBoxSizer( wxVERTICAL );
87#if !wxCHECK_VERSION( 3, 3, 0 )
91 wxPGInitResourceModule();
98 wxPGEditor_TextCtrl =
nullptr;
99 wxPGEditor_Choice =
nullptr;
100 wxPGEditor_ComboBox =
nullptr;
101 wxPGEditor_TextCtrlAndButton =
nullptr;
102 wxPGEditor_CheckBox =
nullptr;
103 wxPGEditor_ChoiceAndButton =
nullptr;
104 wxPGEditor_SpinCtrl =
nullptr;
105 wxPGEditor_DatePickerCtrl =
nullptr;
108 if( !
Pgm().m_PropertyGridInitialized )
115 m_caption =
new wxStaticText(
this, wxID_ANY,
_(
"No objects selected" ) );
116 mainSizer->Add(
m_caption, 0, wxALL | wxEXPAND, 5 );
119 m_grid->SetUnspecifiedValueAppearance( wxPGCell( wxT(
"<...>" ) ) );
121#if wxCHECK_VERSION( 3, 3, 0 )
122 m_grid->SetValidationFailureBehavior( wxPGVFBFlags::MarkCell );
124 m_grid->SetValidationFailureBehavior( wxPG_VFB_MARK_CELL );
127#if wxCHECK_VERSION( 3, 3, 0 )
128 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_RETURN );
129 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_NUMPAD_ENTER );
130 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_DOWN );
131 m_grid->AddActionTrigger( wxPGKeyboardAction::PrevProperty, WXK_UP );
132 m_grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_SPACE );
134 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );
135 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_NUMPAD_ENTER );
136 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
137 m_grid->AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_UP );
138 m_grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_SPACE );
141 m_grid->DedicateKey( WXK_RETURN );
142 m_grid->DedicateKey( WXK_NUMPAD_ENTER );
143 m_grid->DedicateKey( WXK_DOWN );
144 m_grid->DedicateKey( WXK_UP );
145 mainSizer->Add(
m_grid, 1, wxEXPAND, 5 );
147 m_grid->SetCellDisabledTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
151 m_grid->SetCaptionTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_CAPTIONTEXT ) );
156 SetSizer( mainSizer );
163 m_grid->MakeColumnEditable( 0 );
165 Bind( wxEVT_PG_ITEM_EXPANDED,
166 [&]( wxPropertyGridEvent& )
171 Bind( wxEVT_PG_ITEM_COLLAPSED,
172 [&]( wxPropertyGridEvent& )
186 Bind( wxEVT_PG_COL_END_DRAG,
187 [&]( wxPropertyGridEvent& )
194 [&]( wxSizeEvent& aEvent )
216 if(
m_grid->IsEditorFocused() )
217 m_grid->CommitChangesFromEditor();
231 m_panel->m_SuppressGridChangeEvents++;
237 m_panel->m_SuppressGridChangeEvents--;
253 CallAfter( [
this, aSelection]()
263 if(
m_grid->IsEditorFocused() )
264 m_grid->CommitChangesFromEditor();
270 if( aSelection.
Empty() )
272 m_caption->SetLabel(
_(
"No objects selected" ) );
277 else if( aSelection.
Size() == 1 )
283 m_caption->SetLabel( wxString::Format(
_(
"%d objects selected" ), aSelection.
Size() ) );
287 std::set<TYPE_ID> types;
292 wxCHECK( !types.empty(), );
295 std::map<wxString, PROPERTY_BASE*> commonProps;
296 const std::vector<PROPERTY_BASE*>& allProperties = propMgr.
GetProperties( *types.begin() );
299 commonProps.emplace( property->Name(), property );
301 std::map<wxString, int> displayOrder;
303 displayOrder.emplace( entry.first->Name(), entry.second );
306 std::set<wxString> groups( groupDisplayOrder.begin(), groupDisplayOrder.end() );
309 for(
auto itType = std::next( types.begin() ); itType != types.end(); ++itType )
315 if( !groups.count(
group ) )
317 groupDisplayOrder.emplace_back(
group );
318 groups.insert(
group );
322 for(
auto it = commonProps.begin(); it != commonProps.end(); )
327 if( it->second->IgnoreValue() )
334 it = commonProps.erase( it );
339 for(
const EDA_ITEM* item : aSelection )
343 bool commonToAll =
true;
345 for(
const EDA_ITEM* other : aSelection )
347 std::vector<PROPERTY_BASE*> otherProps = other->GetDynamicProperties();
349 if( std::ranges::none_of( otherProps,
352 return p->
Name() == prop->Name();
360 if( !commonToAll || commonProps.contains( prop->Name() ) )
363 commonProps.emplace( prop->Name(), prop );
365 auto maxOrderIt = std::ranges::max_element( displayOrder,
366 [](
const auto& aL,
const auto& aR )
368 return aL.second < aR.second;
370 int nextOrder = maxOrderIt == displayOrder.end() ? 0 : maxOrderIt->second + 1;
371 displayOrder.emplace( prop->Name(), nextOrder );
373 if(
const wxString& dynGroup = prop->Group(); !dynGroup.IsEmpty() && !groups.contains( dynGroup ) )
375 groupDisplayOrder.emplace_back( dynGroup );
376 groups.insert( dynGroup );
383 if( !groups.contains(
_HKI(
"Custom Properties" ) ) )
385 groupDisplayOrder.emplace_back(
_HKI(
"Custom Properties" ) );
386 groups.insert(
_HKI(
"Custom Properties" ) );
393 if( !entry.forceCategory || groups.contains( entry.groupKey ) )
396 if( entry.enableFunc && !entry.enableFunc() )
399 groupDisplayOrder.emplace_back( entry.groupKey );
400 groups.insert( entry.groupKey );
409 std::set<wxString> availableProps;
412 for(
auto& [
name, property] : commonProps )
414 if( property->IsHiddenFromPropertiesManager() )
417 if( isLibraryEditor && property->IsHiddenFromLibraryEditors() )
420 if( isDesignEditor && property->IsHiddenFromDesignEditors() )
428 availableProps.insert(
name );
431 bool writeable =
true;
432 std::set<wxString> existingProps;
434 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
436 wxPGProperty* pgProp = it.GetProperty();
437 wxString
name = pgProp->GetName();
441 existingProps.insert(
name );
443 if( !availableProps.count(
name ) )
451 if( choices.GetCount() > 0 )
452 pgProp->SetChoices( choices );
454 pgProp->SetValue( commonVal );
455 pgProp->ChangeFlag( wxPG_PROP_READONLY, !writeable );
458 if( !existingProps.empty() && existingProps == availableProps )
464 std::map<wxPGProperty*, int> pgPropOrders;
465 std::map<wxString, std::vector<wxPGProperty*>> pgPropGroups;
467 for(
const wxString&
name : availableProps )
479 if( choices.GetCount() )
480 pgProp->SetChoices( choices );
482 pgProp->SetValue( commonVal );
483 pgProp->ChangeFlag( wxPG_PROP_READONLY, !writeable );
486 wxASSERT( displayOrder.count(
name ) );
487 pgPropOrders[pgProp] = displayOrder[
name];
488 pgPropGroups[
property->Group()].emplace_back( pgProp );
492 const wxString unspecifiedGroupCaption =
_(
"Basic Properties" );
494 for(
const wxString& groupName : groupDisplayOrder )
496 if( groupName !=
_HKI(
"Custom Properties" ) && !pgPropGroups.contains( groupName ) )
499 std::vector<wxPGProperty*> properties;
501 if( pgPropGroups.contains( groupName ) )
502 properties = pgPropGroups[groupName];
504 wxString groupCaption = wxGetTranslation( groupName );
506 auto groupItem =
new wxPropertyCategory( groupName.IsEmpty() ? unspecifiedGroupCaption
509 m_grid->Append( groupItem );
511 std::sort( properties.begin(), properties.end(),
512 [&]( wxPGProperty*& aFirst, wxPGProperty*& aSecond )
514 return pgPropOrders[aFirst] < pgPropOrders[aSecond];
517 for( wxPGProperty* property : properties )
518 m_grid->Append( property );
528 const wxAny&
any = aItem->
Get( aProperty );
529 bool converted =
false;
535 converted =
any.GetAs<
int>( &tmp );
538 aValue = wxVariant( tmp );
542 converted =
any.GetAs( &aValue );
546 wxString propName = aProperty->
Name();
547 propName.Replace(
' ',
'_' );
548 wxFAIL_MSG( wxString::Format( wxS(
"Could not convert wxAny to wxVariant for %s::%s" ),
558 wxVariant& aValue,
bool& aWritable, wxPGChoices& aChoices )
561 bool different =
false;
576 if( property->IsHiddenFromPropertiesManager() )
579 wxPGChoices choices =
property->
GetChoices( item );
588 wxArrayString labels = choices.GetLabels();
589 wxArrayInt values = choices.GetValuesForStrings( labels );
591 if( labels != aChoices.GetLabels() || values != aChoices.GetValuesForStrings( labels ) )
603 if( property->IgnoreValue() )
607 if( !different && !aValue.IsNull() && value != aValue )
612 else if( !different )
630 if( aEvent.IsShown() )
639 wxPGProperty* pgProp = aEvent.GetProperty();
654 wxPGProperty* pgProp = aEvent.GetProperty();
659 const wxString oldName = pgProp->GetBaseName();
661 wxTextCtrl* labelEditor =
m_grid->GetLabelEditor();
665 newName = labelEditor->GetValue();
691 [
this, oldName, newName]()
700 if( newName == oldName )
708 [
this, oldName, originalLabel, newName]()
710 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
712 wxPGProperty* p = it.GetProperty();
714 if( p->GetBaseName() == oldName && p->GetLabel() == newName )
716 p->SetLabel( originalLabel );
727 [
this, oldName, newName]()
736 wxPGProperty* pgProp = aEvent.GetProperty();
743 wxMenu* menu =
new wxMenu;
752 const wxPoint pos = ScreenToClient( wxGetMousePosition() );
753 CallAfter( [
this, menu, pos]()
755 PopupMenu( menu, pos );
763 if( !
m_grid->GetLabelEditor() )
769 const wxString newName =
m_grid->GetLabelEditor()->GetValue();
773 m_grid->EndLabelEdit(
true );
776 if( pendingKey.IsEmpty() )
781 else if( newName != pendingKey )
788 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
790 wxPGProperty* p = it.GetProperty();
792 if( !p->IsCategory() && p->GetBaseName() == aKey )
794 m_grid->SelectProperty( p );
795 m_grid->BeginLabelEdit( 0 );
797 if( aStartBlank &&
m_grid->GetLabelEditor() )
798 m_grid->GetLabelEditor()->SetValue( wxEmptyString );
810 if( aEvent.GetKeyCode() == WXK_TAB
811 && ( aEvent.GetModifiers() == wxMOD_NONE || aEvent.GetModifiers() == wxMOD_SHIFT ) )
817 m_grid->CommitChangesFromEditor();
819 const bool forward = !aEvent.ShiftDown();
822 []( wxPGProperty* aProp )
824 return aProp && !aProp->IsCategory() && aProp->IsVisible() && aProp->IsEnabled()
825 && !aProp->HasFlag( wxPG_PROP_READONLY ) && aProp->GetEditorClass();
829 [&]( wxPropertyGridIterator aIt )
831 while( !aIt.AtEnd() )
833 if( isTabStop( aIt.GetProperty() ) )
834 return aIt.GetProperty();
842 return static_cast<wxPGProperty*
>( nullptr );
845 wxPGProperty* current =
m_grid->GetSelectedProperty();
846 wxPGProperty* target =
nullptr;
850 wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE, current );
857 target = findTarget( it );
863 wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE,
864 forward ? wxTOP : wxBOTTOM );
865 target = findTarget( it );
869 m_grid->SelectProperty( target,
true );
876 if( aEvent.GetKeyCode() ==
'C' && aEvent.GetModifiers() == wxMOD_CONTROL )
878 if( wxPGProperty* prop =
m_grid->GetSelectedProperty() )
880 if( wxTheClipboard->Open() )
882 wxTheClipboard->SetData(
new wxTextDataObject( prop->GetValueAsString() ) );
883 wxTheClipboard->Close();
889 if( aEvent.GetKeyCode() == WXK_SPACE )
891 if( wxPGProperty* prop =
m_grid->GetSelectedProperty() )
893 if( prop->GetValueType() == wxT(
"bool" ) )
895 m_grid->SetPropertyValue( prop, !prop->GetValue().GetBool() );
901 if( aEvent.GetKeyCode() == WXK_RETURN || aEvent.GetKeyCode() == WXK_NUMPAD_ENTER
902 || aEvent.GetKeyCode() == WXK_DOWN || aEvent.GetKeyCode() == WXK_UP )
904 m_grid->CommitChangesFromEditor();
908 m_grid->SelectProperty(
m_grid->GetSelectedProperty(),
true );
933 std::function<
void()> aAction, std::function<
bool()> aEnableFunc,
934 bool aForceCategory )
938 button->SetToolTip( aTooltip );
941 button->Bind( wxEVT_BUTTON,
942 [
this, button]( wxCommandEvent& )
946 if( entry.button == button )
955 .groupKey = aGroupKey,
956 .action = std::move( aAction ),
957 .enableFunc = std::move( aEnableFunc ),
958 .forceCategory = aForceCategory } );
964 const wxString caption = wxGetTranslation( aGroupKey );
966 for( wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE ); !it.AtEnd(); it.Next() )
968 if( wxPGProperty* pgProp = it.GetProperty(); pgProp->IsCategory() && pgProp->GetLabel() == caption )
985 entry.button->Hide();
991 const int rightEdgeBase =
m_grid->GetClientSize().x -
m_grid->FromDIP( 4 );
992 int nextRightEdge = rightEdgeBase;
994 bool haveCurrentRow =
false;
998 if(
bool sameRow = haveCurrentRow && currentRow == entry.groupKey; !sameRow )
1000 currentRow = entry.groupKey;
1001 haveCurrentRow =
true;
1002 nextRightEdge = rightEdgeBase;
1006 bool visible =
false;
1008 int rowHeight =
m_grid->GetRowHeight();
1010 if( category && ( !entry.enableFunc || entry.enableFunc() ) )
1012 rowY =
m_grid->CalcScrolledPosition( wxPoint( 0, category->GetY() ) ).y;
1013 visible = ( rowY + rowHeight > 0 ) && ( rowY < m_grid->GetClientSize().y );
1018 entry.button->Hide();
1022 const wxSize btnSize = entry.button->GetSize();
1023 const int btnY = rowY + ( rowHeight - btnSize.y ) / 2;
1024 const int btnX = nextRightEdge - btnSize.x;
1026 if( !entry.button->IsShown() )
1027 entry.button->Show();
1029 entry.button->SetPosition( wxPoint( std::max( 0, btnX ), std::max( 0, btnY ) ) );
1030 entry.button->Raise();
1032 nextRightEdge = btnX -
m_grid->FromDIP( 2 );
wxBitmapBundle KiBitmapBundle(BITMAPS aBitmap, int aMinHeight)
BITMAPS
A list of all bitmap identifiers.
The base frame for deriving all KiCad main window classes.
A base class for most all the KiCad significant classes used in schematics and boards.
virtual wxString GetFriendlyName() const
wxAny Get(PROPERTY_BASE *aProperty) const
virtual wxString GetClass() const =0
Return the class name.
bool m_PropertyGridInitialized
Enhanced renderer to work around some limitations in wxWidgets 3.0 capabilities.
bool IsProcessingWxPGEvent() const
void ScrollWindow(int aDx, int aDy, const wxRect *aRect=nullptr) override
True while a wxPropertyGrid event (e.g. right-click) is being processed.
PROPERTIES_PANEL_GRID(wxWindow *aParent)
wxString m_contextMenuPropertyName
virtual ~PROPERTIES_PANEL()
float m_splitter_key_proportion
Proportion of the grid column splitter that is used for the key column (0.0 - 1.0)
virtual bool isKeyEditable(const wxPGProperty *aPGProp) const
virtual void onKeyRenamed(const wxString &aOldName, const wxString &aNewName)
PROPERTIES_PANEL(wxWindow *aParent, EDA_BASE_FRAME *aFrame)
virtual void valueChanging(wxPropertyGridEvent &aEvent)
wxString m_editingOriginalLabel
virtual void onLabelEditEnding(wxPropertyGridEvent &aEvent)
void RecalculateSplitterPos()
virtual bool buildContextMenu(wxMenu &aMenu, wxPGProperty *aPGProp)
void beginLabelEdit(const wxString &aKey, bool aStartBlank=false)
virtual bool isKeyNameInUse(const wxString &aName) const
bool m_resolvingPendingKey
True while settling a pending label edit synchronously (see settlePendingLabelEdit).
void SetSplitterProportion(float aProportion)
void onRightClick(wxPropertyGridEvent &aEvent)
void settlePendingLabelEdit()
Synchronously ends any in-progress label edit; potentially canceling a newly added row.
std::vector< PROPERTY_BASE * > m_displayed
int m_SuppressGridChangeEvents
virtual void OnLanguageChanged(wxCommandEvent &aEvent)
virtual void UpdateData()=0
friend class PROPERTIES_PANEL_GRID
virtual void onLabelEditBegin(wxPropertyGridEvent &aEvent)
virtual void valueChanged(wxPropertyGridEvent &aEvent)
virtual void onNewItemLeftBlank(const wxString &aKey)
bool extractValueAndWritability(const SELECTION &aSelection, const wxString &aPropName, wxVariant &aValue, bool &aWritable, wxPGChoices &aChoices)
Processes a selection and determines whether the given property should be available or not and what t...
virtual bool getItemValue(EDA_ITEM *aItem, PROPERTY_BASE *aProperty, wxVariant &aValue)
Utility to fetch a property value and convert to wxVariant Precondition: aItem is known to have prope...
wxPGProperty * categoryForGroup(const wxString &aGroupKey) const
virtual void rebuildProperties(const SELECTION &aSelection)
Generates the property grid for a given selection of items.
void onCharHook(wxKeyEvent &aEvent)
virtual wxPGProperty * createPGProperty(const PROPERTY_BASE *aProperty) const =0
void addCategoryButton(const wxString &aGroupKey, const wxString &aTooltip, BITMAPS aBitmap, std::function< void()> aAction, std::function< bool()> aEnableFunc=nullptr, bool aForceCategory=false)
Registers an overlay button on a category caption row.
void onShow(wxShowEvent &aEvent)
void hideCategoryButtons()
Hides all overlay buttons (used when no selection or a rebuild is deferred).
std::vector< CATEGORY_BUTTON > m_categoryButtons
void positionCategoryButtons()
Find the caption row for the given untranslated group name, if present.
void updateCategoryButtons()
Updates overlay button visibility/positions; called after rebuilds and on scroll.
wxString m_pendingNewKey
Key of a freshly-added blank field/custom property awaiting a name from the user.
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
wxPGChoices GetChoices(INSPECTABLE *aObject) const
const wxString & Name() const
Provide class metadata.Helper macro to map type hashes to names.
const std::vector< PROPERTY_BASE * > & GetProperties(TYPE_ID aType) const
Return all properties for a specific type.
bool IsWriteableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the property ...
static PROPERTY_MANAGER & Instance()
PROPERTY_BASE * GetProperty(TYPE_ID aType, const wxString &aProperty) const
Return a property for a specific type.
bool IsAvailableFor(TYPE_ID aItemClass, PROPERTY_BASE *aProp, INSPECTABLE *aItem)
Checks overriden availability and original availability of a property, returns false if the property ...
const std::map< PROPERTY_BASE *, int > & GetDisplayOrder(TYPE_ID aType) const
const std::vector< wxString > & GetGroupDisplayOrder(TYPE_ID aType) const
int Size() const
Returns the number of selected parts.
bool Empty() const
Checks if there is anything selected.
SUPPRESS_GRID_CHANGED_EVENTS(PROPERTIES_PANEL *aPanel)
~SUPPRESS_GRID_CHANGED_EVENTS()
PROPERTIES_PANEL * m_panel
A type-safe container of any type.
Base window classes and related definitions.
@ FRAME_SCH_SYMBOL_EDITOR
Some functions to handle hotkeys in KiCad.
KICOMMON_API wxFont GetDockedPaneFont(wxWindow *aWindow)
PGM_BASE & Pgm()
The global program "get" accessor.
APIIMPORT wxPGGlobalVarsClass * wxPGGlobalVars
size_t TYPE_ID
Unique type identifier.
std::vector< FAB_LAYER_COLOR > dummy