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; }
86 wxBoxSizer* mainSizer =
new wxBoxSizer( wxVERTICAL );
88#if !wxCHECK_VERSION( 3, 3, 0 )
92 wxPGInitResourceModule();
99 wxPGEditor_TextCtrl =
nullptr;
100 wxPGEditor_Choice =
nullptr;
101 wxPGEditor_ComboBox =
nullptr;
102 wxPGEditor_TextCtrlAndButton =
nullptr;
103 wxPGEditor_CheckBox =
nullptr;
104 wxPGEditor_ChoiceAndButton =
nullptr;
105 wxPGEditor_SpinCtrl =
nullptr;
106 wxPGEditor_DatePickerCtrl =
nullptr;
109 if( !
Pgm().m_PropertyGridInitialized )
116 m_caption =
new wxStaticText(
this, wxID_ANY,
_(
"No objects selected" ) );
117 mainSizer->Add(
m_caption, 0, wxALL | wxEXPAND, 5 );
120 m_grid->SetUnspecifiedValueAppearance( wxPGCell( wxT(
"<...>" ) ) );
122#if wxCHECK_VERSION( 3, 3, 0 )
123 m_grid->SetValidationFailureBehavior( wxPGVFBFlags::StayInProperty );
125 m_grid->SetValidationFailureBehavior( wxPG_VFB_STAY_IN_PROPERTY );
128#if wxCHECK_VERSION( 3, 3, 0 )
129 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_RETURN );
130 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_NUMPAD_ENTER );
131 m_grid->AddActionTrigger( wxPGKeyboardAction::NextProperty, WXK_DOWN );
132 m_grid->AddActionTrigger( wxPGKeyboardAction::PrevProperty, WXK_UP );
133 m_grid->AddActionTrigger( wxPGKeyboardAction::Edit, WXK_SPACE );
135 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_RETURN );
136 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_NUMPAD_ENTER );
137 m_grid->AddActionTrigger( wxPG_ACTION_NEXT_PROPERTY, WXK_DOWN );
138 m_grid->AddActionTrigger( wxPG_ACTION_PREV_PROPERTY, WXK_UP );
139 m_grid->AddActionTrigger( wxPG_ACTION_EDIT, WXK_SPACE );
142 m_grid->DedicateKey( WXK_RETURN );
143 m_grid->DedicateKey( WXK_NUMPAD_ENTER );
144 m_grid->DedicateKey( WXK_DOWN );
145 m_grid->DedicateKey( WXK_UP );
146 mainSizer->Add(
m_grid, 1, wxEXPAND, 5 );
148 m_grid->SetCellDisabledTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
152 m_grid->SetCaptionTextColour( wxSystemSettings::GetColour( wxSYS_COLOUR_CAPTIONTEXT ) );
157 SetSizer( mainSizer );
164 m_grid->MakeColumnEditable( 0 );
166 Bind( wxEVT_PG_ITEM_EXPANDED,
167 [&]( wxPropertyGridEvent& )
172 Bind( wxEVT_PG_ITEM_COLLAPSED,
173 [&]( wxPropertyGridEvent& )
187 Bind( wxEVT_PG_COL_END_DRAG,
188 [&]( wxPropertyGridEvent& )
195 [&]( wxSizeEvent& aEvent )
208 [
this]( wxTimerEvent& )
224 if(
m_grid->IsEditorFocused() )
225 m_grid->CommitChangesFromEditor();
239 m_panel->m_SuppressGridChangeEvents++;
245 m_panel->m_SuppressGridChangeEvents--;
269 if(
m_grid->IsEditorFocused() )
270 m_grid->CommitChangesFromEditor();
276 if( aSelection.
Empty() )
278 m_caption->SetLabel(
_(
"No objects selected" ) );
283 else if( aSelection.
Size() == 1 )
289 m_caption->SetLabel( wxString::Format(
_(
"%d objects selected" ), aSelection.
Size() ) );
293 std::set<TYPE_ID> types;
298 wxCHECK( !types.empty(), );
301 std::map<wxString, PROPERTY_BASE*> commonProps;
302 const std::vector<PROPERTY_BASE*>& allProperties = propMgr.
GetProperties( *types.begin() );
305 commonProps.emplace( property->Name(), property );
307 std::map<wxString, int> displayOrder;
309 displayOrder.emplace( entry.first->Name(), entry.second );
312 std::set<wxString> groups( groupDisplayOrder.begin(), groupDisplayOrder.end() );
315 for(
auto itType = std::next( types.begin() ); itType != types.end(); ++itType )
321 if( !groups.count(
group ) )
323 groupDisplayOrder.emplace_back(
group );
324 groups.insert(
group );
328 for(
auto it = commonProps.begin(); it != commonProps.end(); )
333 if( it->second->IgnoreValue() )
340 it = commonProps.erase( it );
345 std::vector<const INSPECTABLE*> inspectables( aSelection.
begin(), aSelection.
end() );
349 if( commonProps.contains( prop->Name() ) )
352 commonProps.emplace( prop->Name(), prop );
354 auto maxOrderIt = std::ranges::max_element( displayOrder,
355 [](
const auto& aL,
const auto& aR )
357 return aL.second < aR.second;
359 int nextOrder = maxOrderIt == displayOrder.end() ? 0 : maxOrderIt->second + 1;
360 displayOrder.emplace( prop->Name(), nextOrder );
362 if(
const wxString& dynGroup = prop->Group(); !dynGroup.IsEmpty() && !groups.contains( dynGroup ) )
364 groupDisplayOrder.emplace_back( dynGroup );
365 groups.insert( dynGroup );
371 if( !groups.contains(
_HKI(
"Custom Properties" ) ) )
373 groupDisplayOrder.emplace_back(
_HKI(
"Custom Properties" ) );
374 groups.insert(
_HKI(
"Custom Properties" ) );
381 if( !entry.forceCategory || groups.contains( entry.groupKey ) )
384 if( entry.enableFunc && !entry.enableFunc() )
387 groupDisplayOrder.emplace_back( entry.groupKey );
388 groups.insert( entry.groupKey );
397 std::set<wxString> availableProps;
400 for(
auto& [
name, property] : commonProps )
402 if( property->IsHiddenFromPropertiesManager() )
405 if( isLibraryEditor && property->IsHiddenFromLibraryEditors() )
408 if( isDesignEditor && property->IsHiddenFromDesignEditors() )
416 availableProps.insert(
name );
419 bool writeable =
true;
420 std::set<wxString> existingProps;
422 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
424 wxPGProperty* pgProp = it.GetProperty();
425 wxString
name = pgProp->GetName();
429 existingProps.insert(
name );
431 if( !availableProps.count(
name ) )
439 if( choices.GetCount() > 0 )
440 pgProp->SetChoices( choices );
442 pgProp->SetValue( commonVal );
443 pgProp->ChangeFlag( wxPG_PROP_READONLY, !writeable );
446 if( !writeable && pgProp ==
m_grid->GetSelection() &&
m_grid->IsEditorFocused() )
447 m_grid->CommitChangesFromEditor();
450 if( !existingProps.empty() && existingProps == availableProps )
456 std::map<wxPGProperty*, int> pgPropOrders;
457 std::map<wxString, std::vector<wxPGProperty*>> pgPropGroups;
459 for(
const wxString&
name : availableProps )
471 if( choices.GetCount() )
472 pgProp->SetChoices( choices );
474 pgProp->SetValue( commonVal );
475 pgProp->ChangeFlag( wxPG_PROP_READONLY, !writeable );
478 wxASSERT( displayOrder.count(
name ) );
479 pgPropOrders[pgProp] = displayOrder[
name];
480 pgPropGroups[
property->Group()].emplace_back( pgProp );
484 const wxString unspecifiedGroupCaption =
_(
"Basic Properties" );
486 for(
const wxString& groupName : groupDisplayOrder )
488 if( !pgPropGroups.contains( groupName )
492 return aButton.groupKey == groupName
493 && ( aButton.forceCategory || groupName == _HKI(
"Custom Properties" ) )
494 && ( !aButton.enableFunc || aButton.enableFunc() );
500 std::vector<wxPGProperty*> properties;
502 if( pgPropGroups.contains( groupName ) )
503 properties = pgPropGroups[groupName];
505 wxString groupCaption = wxGetTranslation( groupName );
507 auto groupItem =
new wxPropertyCategory( groupName.IsEmpty() ? unspecifiedGroupCaption
510 m_grid->Append( groupItem );
512 std::sort( properties.begin(), properties.end(),
513 [&]( wxPGProperty*& aFirst, wxPGProperty*& aSecond )
515 return pgPropOrders[aFirst] < pgPropOrders[aSecond];
518 for( wxPGProperty* property : properties )
519 m_grid->Append( property );
529 const wxAny&
any = aItem->
Get( aProperty );
530 bool converted =
false;
536 converted =
any.GetAs<
int>( &tmp );
539 aValue = wxVariant( tmp );
543 converted =
any.GetAs( &aValue );
547 wxString propName = aProperty->
Name();
548 propName.Replace(
' ',
'_' );
549 wxFAIL_MSG( wxString::Format( wxS(
"Could not convert wxAny to wxVariant for %s::%s" ),
559 wxVariant& aValue,
bool& aWritable, wxPGChoices& aChoices )
562 bool different =
false;
577 if( property->IsHiddenFromPropertiesManager() )
580 if( property->IgnoreValue() )
583 wxPGChoices choices =
property->
GetChoices( item );
592 wxArrayString labels = choices.GetLabels();
593 wxArrayInt values = choices.GetValuesForStrings( labels );
595 if( labels != aChoices.GetLabels() || values != aChoices.GetValuesForStrings( labels ) )
608 if( !different && !aValue.IsNull() && value != aValue )
613 else if( !different )
631 if( aEvent.IsShown() )
640 wxPGProperty* pgProp = aEvent.GetProperty();
655 wxPGProperty* pgProp = aEvent.GetProperty();
660 const wxString oldName = pgProp->GetBaseName();
662 wxTextCtrl* labelEditor =
m_grid->GetLabelEditor();
666 newName = labelEditor->GetValue();
692 [
this, oldName, newName]()
701 if( newName == oldName )
709 [
this, oldName, originalLabel, newName]()
711 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
713 wxPGProperty* p = it.GetProperty();
715 if( p->GetBaseName() == oldName && p->GetLabel() == newName )
717 p->SetLabel( originalLabel );
728 [
this, oldName, newName]()
737 wxPGProperty* pgProp = aEvent.GetProperty();
744 wxMenu* menu =
new wxMenu;
753 const wxPoint pos = ScreenToClient( wxGetMousePosition() );
754 CallAfter( [
this, menu, pos]()
756 PopupMenu( menu, pos );
764 if( !
m_grid->GetLabelEditor() )
770 const wxString newName =
m_grid->GetLabelEditor()->GetValue();
774 m_grid->EndLabelEdit(
true );
777 if( pendingKey.IsEmpty() )
782 else if( newName != pendingKey )
789 for( wxPropertyGridIterator it =
m_grid->GetIterator(); !it.AtEnd(); it.Next() )
791 wxPGProperty* p = it.GetProperty();
793 if( !p->IsCategory() && p->GetBaseName() == aKey )
795 m_grid->SelectProperty( p );
796 m_grid->BeginLabelEdit( 0 );
798 if( aStartBlank &&
m_grid->GetLabelEditor() )
799 m_grid->GetLabelEditor()->SetValue( wxEmptyString );
811 if( aEvent.GetKeyCode() == WXK_TAB
812 && ( aEvent.GetModifiers() == wxMOD_NONE || aEvent.GetModifiers() == wxMOD_SHIFT ) )
827 const bool forward = !aEvent.ShiftDown();
830 []( wxPGProperty* aProp )
832 return aProp && !aProp->IsCategory() && aProp->IsVisible() && aProp->IsEnabled()
833 && !aProp->HasFlag( wxPG_PROP_READONLY ) && aProp->GetEditorClass();
837 [&]( wxPropertyGridIterator aIt )
839 while( !aIt.AtEnd() )
841 if( isTabStop( aIt.GetProperty() ) )
842 return aIt.GetProperty();
850 return static_cast<wxPGProperty*
>( nullptr );
853 wxPGProperty* current =
m_grid->GetSelectedProperty();
854 wxPGProperty* target =
nullptr;
858 wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE, current );
865 target = findTarget( it );
871 wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE,
872 forward ? wxTOP : wxBOTTOM );
873 target = findTarget( it );
877 m_grid->SelectProperty( target,
true );
884 if( aEvent.GetKeyCode() ==
'C' && aEvent.GetModifiers() == wxMOD_CONTROL )
887 if( !
m_grid->IsEditorFocused() )
889 if( wxPGProperty* prop =
m_grid->GetSelectedProperty() )
891 if( wxTheClipboard->Open() )
893 wxTheClipboard->SetData(
new wxTextDataObject( prop->GetValueAsString() ) );
894 wxTheClipboard->Close();
901 if( aEvent.GetKeyCode() == WXK_SPACE )
903 if( wxPGProperty* prop =
m_grid->GetSelectedProperty() )
905 if( prop->GetValueType() == wxT(
"bool" ) )
907 m_grid->SetPropertyValue( prop, !prop->GetValue().GetBool() );
913 if( aEvent.GetKeyCode() == WXK_RETURN || aEvent.GetKeyCode() == WXK_NUMPAD_ENTER
914 || aEvent.GetKeyCode() == WXK_DOWN || aEvent.GetKeyCode() == WXK_UP )
927 if( wxPGProperty* prop =
m_grid->GetSelectedProperty() )
928 m_grid->SelectProperty( prop,
true );
953 std::function<
void()> aAction, std::function<
bool()> aEnableFunc,
954 bool aForceCategory )
958 button->SetToolTip( aTooltip );
961 button->Bind( wxEVT_BUTTON,
962 [
this, button]( wxCommandEvent& )
966 if( entry.button == button )
975 .groupKey = aGroupKey,
976 .action = std::move( aAction ),
977 .enableFunc = std::move( aEnableFunc ),
978 .forceCategory = aForceCategory } );
984 const wxString caption = wxGetTranslation( aGroupKey );
986 for( wxPropertyGridIterator it =
m_grid->GetIterator( wxPG_ITERATE_VISIBLE ); !it.AtEnd(); it.Next() )
988 if( wxPGProperty* pgProp = it.GetProperty(); pgProp->IsCategory() && pgProp->GetLabel() == caption )
1005 entry.button->Hide();
1011 const int rightEdgeBase =
m_grid->GetClientSize().x -
m_grid->FromDIP( 4 );
1012 int nextRightEdge = rightEdgeBase;
1013 wxString currentRow;
1014 bool haveCurrentRow =
false;
1018 if(
bool sameRow = haveCurrentRow && currentRow == entry.groupKey; !sameRow )
1020 currentRow = entry.groupKey;
1021 haveCurrentRow =
true;
1022 nextRightEdge = rightEdgeBase;
1026 bool visible =
false;
1028 int rowHeight =
m_grid->GetRowHeight();
1030 if( category && ( !entry.enableFunc || entry.enableFunc() ) )
1032 rowY =
m_grid->CalcScrolledPosition( wxPoint( 0, category->GetY() ) ).y;
1033 visible = ( rowY + rowHeight > 0 ) && ( rowY < m_grid->GetClientSize().y );
1038 entry.button->Hide();
1042 const wxSize btnSize = entry.button->GetSize();
1043 const int btnY = rowY + ( rowHeight - btnSize.y ) / 2;
1044 const int btnX = nextRightEdge - btnSize.x;
1046 if( !entry.button->IsShown() )
1047 entry.button->Show();
1049 entry.button->SetPosition( wxPoint( std::max( 0, btnX ), std::max( 0, btnY ) ) );
1050 entry.button->Raise();
1052 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
True while a wxPropertyGrid event (e.g. right-click) is being processed.
void ScrollWindow(int aDx, int aDy, const wxRect *aRect=nullptr) override
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...
wxTimer m_deferredRebuildTimer
Not CallAfter(): on macOS the pending-event queue is drained from inside tool events,...
SELECTION m_deferredSelection
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
Find the caption row for the given untranslated group name, if present.
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()
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
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
std::vector< PROPERTY_BASE * > GetCommonDynamicProperties(const std::vector< const INSPECTABLE * > &aObjects) const
Return the dynamic properties of the first object whose names every other object also has,...
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
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
size_t TYPE_ID
Unique type identifier.
std::vector< FAB_LAYER_COLOR > dummy