44#include <wx/stc/stc.h>
63 m_propertyGrid->SetDefaultCellAlignment( wxALIGN_LEFT, wxALIGN_CENTER );
68 [
this]( wxKeyEvent& aEvent )
73 [
this]( wxStyledTextEvent& aEvent )
93 SetMinSize( wxSize( 350, 400 ) );
126 if( IsShown() &&
m_notebook->GetSelection() == 0 )
133 if( aValue.CheckType<
int>() )
134 return wxVariant( aValue.As<
int>() );
135 else if( aValue.CheckType<
long>() )
136 return wxVariant( aValue.As<
long>() );
137 else if( aValue.CheckType<
long long>() )
139 return wxVariant(
static_cast<double>( aValue.As<
long long>() ) );
140 else if( aValue.CheckType<
double>() )
141 return wxVariant( aValue.As<
double>() );
142 else if( aValue.CheckType<
bool>() )
143 return wxVariant( aValue.As<
bool>() );
144 else if( aValue.CheckType<wxString>() )
145 return wxVariant( aValue.As<wxString>() );
149 if( aValue.GetAs( &variant ) )
154 if( aValue.GetAs( &strVal ) )
155 return wxVariant( strVal );
166 const wxString& propName = aProperty->
Name();
167 wxString variantName;
172 if( !variantName.IsEmpty() )
174 if( propName ==
_HKI(
"Do not Populate" ) )
176 else if( propName ==
_HKI(
"Exclude From Bill of Materials" ) )
178 else if( propName ==
_HKI(
"Exclude From Simulation" ) )
180 else if( propName ==
_HKI(
"Exclude From Position Files" ) )
185 wxAny anyValue = aItem->
Get( aProperty );
187 if( anyValue.IsNull() )
196wxVariant getFootprintFieldValue(
FOOTPRINT* aFootprint,
const wxString& aFieldName )
203 wxString variantName;
208 if( !variantName.IsEmpty() )
217 return wxVariant( field->
GetText() );
221std::set<wxString> getCommonFootprintFieldNames(
const PCB_SELECTION& aSelection )
223 std::set<wxString> commonFieldNames;
224 bool firstFootprint =
true;
232 std::set<wxString> fieldNames;
242 commonFieldNames = std::move( fieldNames );
243 firstFootprint =
false;
247 std::erase_if( commonFieldNames,
248 [&](
const wxString& aName )
250 return !fieldNames.count( aName );
255 return commonFieldNames;
261 if( aValue.IsNull() )
262 return wxEmptyString;
265 return aValue.GetString();
267 wxVariant valueCopy = aValue;
268 wxPGProperty* pgProperty =
nullptr;
274 const wxPGChoices& canonicalLayers = aProperty->
Choices();
275 wxArrayString boardLayerNames;
276 wxArrayInt boardLayerIDs;
278 for(
int ii = 0; ii < (int) canonicalLayers.GetCount(); ++ii )
280 int layer = canonicalLayers.GetValue( ii );
283 boardLayerIDs.push_back( layer );
287 layerProp->SetLabel( wxGetTranslation( aProperty->
Name() ) );
288 layerProp->SetName( aProperty->
Name() );
289 layerProp->SetHelpString( wxGetTranslation( aProperty->
Name() ) );
290 layerProp->SetClientData(
const_cast<PROPERTY_BASE*
>( aProperty ) );
291 pgProperty = layerProp;
300 wxString formatted = pgProperty->ValueToString( valueCopy, 0 );
303 if( !formatted.IsEmpty() )
307 return aValue.GetString();
311wxString normalizeFormattedValueForExpression(
PROPERTY_BASE* aProperty,
const wxString& aValue )
313 wxString normalized = aValue;
320 normalized.Replace( wxT(
" " ), wxEmptyString );
321 normalized.Replace( wxT(
"mils" ), wxT(
"mil" ) );
326 normalized.Replace( wxT(
"°" ), wxT(
"deg" ) );
327 normalized.Replace( wxT(
" " ), wxEmptyString );
337bool isExprIdentChar( wxChar aCh )
339 return wxIsalnum( aCh ) || aCh == wxT(
'_' );
343std::set<wxString> getQueryableFootprintFieldNames(
const std::vector<PROPERTY_ROW_DATA>& aRows )
345 std::set<wxString> fieldNames = {
_HKI(
"Reference" ),
_HKI(
"Value" ),
_HKI(
"Datasheet" ),
346 _HKI(
"Description" ) };
350 if( row.property ==
nullptr )
351 fieldNames.insert( row.propertyName );
358bool matchAliasAt(
const wxString& aExpression,
size_t aPos,
const wxString& aAlias )
360 if( aPos + aAlias.length() > aExpression.length() )
363 if( aExpression.Mid( aPos, aAlias.length() ) != aAlias )
366 if( aPos > 0 && isExprIdentChar( aExpression[aPos - 1] ) )
369 size_t end = aPos + aAlias.length();
371 if(
end < aExpression.length() && isExprIdentChar( aExpression[
end] ) )
378wxString normalizeQueryFieldAliases(
const wxString& aExpression,
const std::vector<PROPERTY_ROW_DATA>& aRows )
386 std::vector<FIELD_ALIAS> aliases;
388 for(
const wxString& fieldName : getQueryableFootprintFieldNames( aRows ) )
390 wxString exprName = fieldName;
391 exprName.Replace( wxT(
" " ), wxT(
"_" ) );
393 wxString escapedFieldName = fieldName;
394 escapedFieldName.Replace( wxT(
"'" ), wxT(
"\\'" ) );
397 { wxT(
"A." ) + exprName, wxString::Format( wxT(
"A.getField('%s')" ), escapedFieldName ) } );
400 std::sort( aliases.begin(), aliases.end(),
401 [](
const FIELD_ALIAS& aLhs,
const FIELD_ALIAS& aRhs )
403 return aLhs.from.length() > aRhs.from.length();
407 bool inString =
false;
409 for(
size_t i = 0; i < aExpression.length(); )
411 wxChar ch = aExpression[i];
413 if( ch == wxT(
'\'' ) && ( i == 0 || aExpression[i - 1] != wxT(
'\\' ) ) )
415 inString = !inString;
423 bool replaced =
false;
425 for(
const FIELD_ALIAS& alias : aliases )
427 if( matchAliasAt( aExpression, i, alias.from ) )
429 normalized += alias.to;
430 i += alias.from.length();
448bool queryUsesUnsupportedPairwiseSyntax(
const wxString& aExpression )
450 bool inString =
false;
452 for(
size_t i = 0; i < aExpression.length(); ++i )
454 wxChar ch = aExpression[i];
456 if( ch == wxT(
'\'' ) && ( i == 0 || aExpression[i - 1] != wxT(
'\\' ) ) )
458 inString = !inString;
462 if( !inString && i + 2 <= aExpression.length() && aExpression.Mid( i, 2 ) == wxT(
"B." )
463 && ( i == 0 || !isExprIdentChar( aExpression[i - 1] ) ) )
473bool isVisibleInFindByProperties(
const wxString& aName,
const PROPERTY_BASE* aProperty )
494 if( selection.Empty() )
503 std::map<wxString, PROPERTY_MATCH_MODE> savedModes;
508 savedModes[row.propertyName] = row.matchMode;
514 if( selection.Size() == 1 )
515 m_statusLabel->SetLabel( selection.Front()->GetFriendlyName() );
517 m_statusLabel->SetLabel( wxString::Format(
_(
"%d objects selected" ), selection.Size() ) );
524 std::map<wxString, PROPERTY_BASE*> commonProps;
528 commonProps.emplace( prop->Name(), prop );
532 for(
auto propIt = commonProps.begin(); propIt != commonProps.end(); )
535 propIt = commonProps.erase( propIt );
541 for(
auto& [
name, property] : commonProps )
543 if( !isVisibleInFindByProperties(
name, property ) )
546 bool available =
true;
567 bool different =
false;
603 row.
displayValue = different ? wxString( wxT(
"<...>" ) )
609 for(
const wxString& fieldName : getCommonFootprintFieldNames( selection ) )
611 if( commonProps.count( fieldName ) )
621 bool different =
false;
622 bool available =
true;
626 wxVariant value = getFootprintFieldValue(
static_cast<FOOTPRINT*
>( item ), fieldName );
650 row.
displayValue = different ? wxString( wxT(
"<...>" ) )
670 m_propertyGrid->SetCellTextColour( i, 2, wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
674 wxString choices[] = {
_(
"Ignored" ),
_(
"Matching" ),
_(
"Different" ) };
675 m_propertyGrid->SetCellEditor( i, 2,
new wxGridCellChoiceEditor( 3, choices ) );
676 m_propertyGrid->SetCellRenderer( i, 2,
new wxGridCellStringRenderer() );
688 int col1Width = totalWidth - col0Width - col2Width;
693 bool anyActive =
false;
713 if( aEvent.GetCol() == 2 && aEvent.GetRow() >= 0 && aEvent.GetRow() < (
int)
m_propertyRows.size()
716 m_propertyGrid->SetGridCursor( aEvent.GetRow(), aEvent.GetCol() );
728 int row = aEvent.GetRow();
729 int col = aEvent.GetCol();
740 if( val ==
_(
"Matching" ) )
742 else if( val ==
_(
"Different" ) )
749 bool anyActive =
false;
769 int col1Width = totalWidth - col0Width - col2Width;
806 std::vector<BOARD_ITEM*> items;
812 items.push_back( track );
816 items.push_back( fp );
818 for(
PAD*
pad : fp->Pads() )
819 items.push_back(
pad );
821 for(
PCB_FIELD* field : fp->GetFields() )
822 items.push_back( field );
825 items.push_back( gi );
827 for(
ZONE* zone : fp->Zones() )
828 items.push_back( zone );
832 items.push_back( item );
835 items.push_back( zone );
838 items.push_back(
group );
861 if( row.property ==
nullptr )
866 itemValue = getFootprintFieldValue(
static_cast<FOOTPRINT*
>( aItem ), row.propertyName );
881 if( itemValue.IsNull() )
884 bool valuesEqual = ( itemValue == row.rawValue );
904 if( !aMatchList.empty() )
907 if(
m_zoomToFit->IsChecked() && !aMatchList.empty() )
910 m_frame->GetCanvas()->ForceRefresh();
912 aStatusLabel->SetLabel( wxString::Format(
_(
"%zu items matched" ), aMatchList.size() ) );
924 matchList.push_back( item );
933 wxString expression =
m_queryEditor->GetText().Trim().Trim(
false );
935 if( expression.IsEmpty() )
938 wxString normalizedExpression = normalizeQueryFieldAliases( expression,
m_propertyRows );
940 if( queryUsesUnsupportedPairwiseSyntax( normalizedExpression ) )
942 wxString error =
_(
"B. expressions are not supported." );
945 wxMessageBox( error,
_(
"Expression Error" ), wxOK | wxICON_ERROR,
this );
956 [&](
const wxString& aMessage,
int aOffset )
958 errors += aMessage + wxT(
"\n" );
961 bool ok = compiler.
Compile( normalizedExpression.ToUTF8().data(), &ucode, &preflightContext );
966 wxMessageBox( errors,
_(
"Expression Error" ), wxOK | wxICON_ERROR,
this );
977 bool matched =
false;
978 LSET itemLayers = item->GetLayerSet();
995 matchList.push_back( item );
1004 wxString
result = aPropName;
1005 result.Replace( wxT(
" " ), wxT(
"_" ) );
1012 if( aValue.GetType() == wxT(
"bool" ) )
1013 return aValue.GetBool() ? wxT(
"true" ) : wxT(
"false" );
1017 wxString val = formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue );
1019 val.Replace( wxT(
"'" ), wxT(
"\\'" ) );
1021 return wxString::Format( wxT(
"'%s'" ), val );
1033 return normalizeFormattedValueForExpression( aProp,
1034 formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue ) );
1040 if( aValue.GetType() == wxT(
"double" ) || aValue.GetType() == wxT(
"long" ) )
1041 return aValue.GetString();
1043 wxString val = aProp ? formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue ) : aValue.GetString();
1045 val.Replace( wxT(
"'" ), wxT(
"\\'" ) );
1047 return wxString::Format( wxT(
"'%s'" ), val );
1053 wxArrayString conditions;
1065 if( row.property ==
nullptr )
1067 lhs = wxString::Format( wxT(
"A.getField(%s)" ),
1077 if( value.IsEmpty() )
1080 aSkippedRows->Add( wxGetTranslation( row.propertyName ) );
1087 conditions.Add( wxString::Format( wxT(
"%s %s %s" ), lhs, op, value ) );
1092 for(
size_t i = 0; i < conditions.size(); i++ )
1106 wxArrayString skipped;
1109 if( !expr.IsEmpty() || !skipped.empty() )
1114 if( skipped.empty() )
1116 else if( skipped.size() == 1 )
1117 m_queryStatusLabel->SetLabel( wxString::Format(
_(
"No value to match: %s" ), skipped[0] ) );
1119 m_queryStatusLabel->SetLabel( wxString::Format(
_(
"%zu rows have no value to match" ), skipped.size() ) );
1126 wxString expression =
m_queryEditor->GetText().Trim().Trim(
false );
1128 if( expression.IsEmpty() )
1134 wxString normalizedExpression = normalizeQueryFieldAliases( expression,
m_propertyRows );
1136 if( queryUsesUnsupportedPairwiseSyntax( normalizedExpression ) )
1148 [&](
const wxString& aMessage,
int aOffset )
1150 errors += aMessage + wxT(
"\n" );
1153 bool ok = compiler.
Compile( normalizedExpression.ToUTF8().data(), &ucode, &preflightContext );
1166 if( sel != wxNOT_FOUND )
1184 if( aQuery.IsEmpty() )
1191 queries.erase( std::remove( queries.begin(), queries.end(), aQuery ), queries.end() );
1194 queries.insert( queries.begin(), aQuery );
1197 if( queries.size() > 10 )
1198 queries.resize( 10 );
1213 if( event.GetSelection() == 0 )
1227 wxString prev2 =
m_queryEditor->GetTextRange( pos - 2, pos );
1229 if( prev2 == wxT(
"A." ) )
1232 wxArrayString tokens;
1233 std::set<wxString> seen;
1244 wxString
name = prop->Name();
1246 if( !isVisibleInFindByProperties(
name, prop ) )
1249 name.Replace( wxT(
" " ), wxT(
"_" ) );
1251 if( seen.insert(
name ).second )
1256 for(
const wxString& fieldName : getQueryableFootprintFieldNames(
m_propertyRows ) )
1258 wxString exprName = fieldName;
1259 exprName.Replace( wxT(
" " ), wxT(
"_" ) );
1261 if( seen.insert( exprName ).second )
1262 tokens.Add( exprName );
static TOOL_ACTION zoomFitSelection
static TOOL_ACTION selectionClear
Clear the current selection.
static TOOL_ACTION selectItems
Select a list of items (specified as the event parameter)
A base class for any item which can be embedded within the BOARD container class, and therefore insta...
virtual const BOARD * GetBoard() const
Return the BOARD in which this BOARD_ITEM resides, or NULL if none.
const wxString GetLayerName(PCB_LAYER_ID aLayer) const
Return the name of a aLayer.
wxString GetCurrentVariant() const
wxStaticText * m_queryStatusLabel
DIALOG_FIND_BY_PROPERTIES_BASE(wxWindow *parent, wxWindowID id=wxID_ANY, const wxString &title=_("Find by Properties"), const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
wxButton * m_createQueryBtn
wxButton * m_selectMatchingBtn
wxCheckBox * m_deselectNonMatching
wxComboBox * m_recentQueries
wxStaticText * m_statusLabel
wxStyledTextCtrl * m_queryEditor
bool Show(bool show=true) override
DIALOG_FIND_BY_PROPERTIES(PCB_EDIT_FRAME *aParent)
void selectMatchingFromQuery()
void onSelectMatchingClick(wxCommandEvent &event) override
void applyMatchResults(EDA_ITEMS &aMatchList, wxStaticText *aStatusLabel)
wxString generateExpressionFromProperties(wxArrayString *aSkippedRows=nullptr)
std::vector< PROPERTY_ROW_DATA > m_propertyRows
void onGridCellChanged(wxGridEvent &aEvent)
void saveRecentQuery(const wxString &aQuery)
~DIALOG_FIND_BY_PROPERTIES() override
wxString formatValueForExpression(PROPERTY_BASE *aProp, const wxVariant &aValue)
wxVariant getVariantAwareValue(EDA_ITEM *aItem, PROPERTY_BASE *aProperty)
void onNotebookPageChanged(wxNotebookEvent &event) override
std::set< size_t > m_selectedTypes
void OnBoardChanged(wxCommandEvent &event)
void onCheckSyntaxClick(wxCommandEvent &event) override
static wxVariant anyToVariant(const wxAny &aValue)
bool itemMatchesPropertyCriteria(BOARD_ITEM *aItem)
void OnSelectionChanged()
void updateMatchModeCell(int aRow)
void onScintillaCharAdded(wxStyledTextEvent &aEvent)
static wxString propNameToExprField(const wxString &aPropName)
void onRecentQuerySelected(wxCommandEvent &event) override
void rebuildPropertyGrid()
void OnCloseButtonClick(wxCommandEvent &event) override
SCINTILLA_TRICKS * m_scintillaTricks
void onGridCellClick(wxGridEvent &aEvent)
std::vector< BOARD_ITEM * > collectAllBoardItems()
void selectMatchingFromProperties()
void onGridSizeChanged(wxSizeEvent &aEvent)
void onCreateQueryClick(wxCommandEvent &event) override
bool Show(bool show) override
A base class for most all the KiCad significant classes used in schematics and boards.
KICAD_T Type() const
Returns the type of object.
virtual const wxString & GetText() const
Return the string associated with the text object.
wxAny Get(PROPERTY_BASE *aProperty) const
void SetErrorCallback(std::function< void(const wxString &aMessage, int aOffset)> aCallback)
bool Compile(const wxString &aString, UCODE *aCode, CONTEXT *aPreflightContext)
VALUE * Run(CONTEXT *ctx)
LSET is a set of PCB_LAYER_IDs.
LSEQ Seq(const LSEQ &aSequence) const
Return an LSEQ from the union of this LSET and a desired sequence.
void SetItems(BOARD_ITEM *a, BOARD_ITEM *b=nullptr)
The main frame for Pcbnew.
wxString GetUntranslatedName() const
Get the untranslated field name for storage, variable look-up, etc.
A set of BOARD_ITEMs (i.e., without duplicates).
The backing store for a PROJECT, in JSON format.
std::vector< wxString > m_FindByPropertiesQueries
Recent queries for Find by Properties dialog.
bool IsHiddenFromPropertiesManager() const
virtual size_t TypeHash() const =0
Return type-id of the property type.
bool IsHiddenFromDesignEditors() const
PROPERTY_DISPLAY Display() const
virtual bool HasChoices() const
Return true if this PROPERTY has a limited set of possible values.
const wxString & Name() const
virtual const wxPGChoices & Choices() const
Return a limited set of possible values (e.g.
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.
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 ...
Add cut/copy/paste, dark theme, autocomplete and brace highlighting to a wxStyleTextCtrl instance.
Handle a list of polygons defining a copper zone.
PCB_LAYER_ID
A quick note on layer IDs:
PCB_LAYER_ID ToLAYER_ID(int aLayer)
Class to handle a set of BOARD_ITEMs.
wxPGProperty * PGPropertyFactory(const PROPERTY_BASE *aProperty, EDA_DRAW_FRAME *aFrame)
Customized abstract wxPGProperty class to handle coordinate/size units.
@ PT_DEGREE
Angle expressed in degrees.
@ PT_COORD
Coordinate expressed in distance units (mm/inch)
@ PT_DECIDEGREE
Angle expressed in decidegrees.
@ PT_SIZE
Size expressed in distance units (mm/inch)
@ PT_TIME
Time expressed in ps.
size_t TYPE_ID
Unique type identifier.
std::vector< EDA_ITEM * > EDA_ITEMS
std::vector< FAB_LAYER_COLOR > dummy
PROPERTY_MATCH_MODE matchMode
wxString result
Test unit parsing edge cases and error handling.
@ PCB_FOOTPRINT_T
class FOOTPRINT, a footprint