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 )
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 )
347 std::set<wxString> fieldNames = {
_HKI(
"Value" ),
_HKI(
"Datasheet" ),
_HKI(
"Description" ) };
351 if( row.property ==
nullptr )
352 fieldNames.insert( row.propertyName );
359bool matchAliasAt(
const wxString& aExpression,
size_t aPos,
const wxString& aAlias )
361 if( aPos + aAlias.length() > aExpression.length() )
364 if( aExpression.Mid( aPos, aAlias.length() ) != aAlias )
367 if( aPos > 0 && isExprIdentChar( aExpression[aPos - 1] ) )
370 size_t end = aPos + aAlias.length();
372 if(
end < aExpression.length() && isExprIdentChar( aExpression[
end] ) )
390 std::vector<FIELD_ALIAS> aliases;
392 for(
const wxString& fieldName : getQueryableFootprintFieldNames( aRows ) )
394 wxString exprName = fieldName;
395 exprName.Replace( wxT(
" " ), wxT(
"_" ) );
397 wxString escapedFieldName = fieldName;
398 escapedFieldName.Replace( wxT(
"'" ), wxT(
"\\'" ) );
401 { wxT(
"A." ) + exprName, wxString::Format( wxT(
"A.getField('%s')" ), escapedFieldName ) } );
404 std::sort( aliases.begin(), aliases.end(),
405 [](
const FIELD_ALIAS& aLhs,
const FIELD_ALIAS& aRhs )
407 return aLhs.from.length() > aRhs.from.length();
411 bool inString =
false;
413 for(
size_t i = 0; i < aExpression.length(); )
415 wxChar ch = aExpression[i];
417 if( ch == wxT(
'\'' ) && ( i == 0 || aExpression[i - 1] != wxT(
'\\' ) ) )
419 inString = !inString;
427 bool replaced =
false;
429 for(
const FIELD_ALIAS& alias : aliases )
431 if( matchAliasAt( aExpression, i, alias.from ) )
434 i += alias.from.length();
455bool queryUsesUnsupportedPairwiseSyntax(
const wxString& aExpression )
457 bool inString =
false;
459 for(
size_t i = 0; i < aExpression.length(); ++i )
461 wxChar ch = aExpression[i];
463 if( ch == wxT(
'\'' ) && ( i == 0 || aExpression[i - 1] != wxT(
'\\' ) ) )
465 inString = !inString;
469 if( !inString && i + 2 <= aExpression.length() && aExpression.Mid( i, 2 ) == wxT(
"B." )
470 && ( i == 0 || !isExprIdentChar( aExpression[i - 1] ) ) )
480bool isVisibleInFindByProperties(
const wxString& aName,
const PROPERTY_BASE* aProperty )
501 if( selection.Empty() )
510 std::map<wxString, PROPERTY_MATCH_MODE> savedModes;
515 savedModes[row.propertyName] = row.matchMode;
521 if( selection.Size() == 1 )
522 m_statusLabel->SetLabel( selection.Front()->GetFriendlyName() );
524 m_statusLabel->SetLabel( wxString::Format(
_(
"%d objects selected" ), selection.Size() ) );
531 std::map<wxString, PROPERTY_BASE*> commonProps;
535 commonProps.emplace( prop->Name(), prop );
539 for(
auto propIt = commonProps.begin(); propIt != commonProps.end(); )
542 propIt = commonProps.erase( propIt );
548 for(
auto& [
name, property] : commonProps )
550 if( !isVisibleInFindByProperties(
name, property ) )
553 bool available =
true;
574 bool different =
false;
610 row.
displayValue = different ? wxString( wxT(
"<...>" ) )
616 for(
const wxString& fieldName : getCommonFootprintFieldNames( selection ) )
618 if( commonProps.count( fieldName ) )
628 bool different =
false;
629 bool available =
true;
633 wxVariant value = getFootprintFieldValue(
static_cast<FOOTPRINT*
>( item ), fieldName );
657 row.
displayValue = different ? wxString( wxT(
"<...>" ) )
677 m_propertyGrid->SetCellTextColour( i, 2, wxSystemSettings::GetColour( wxSYS_COLOUR_GRAYTEXT ) );
681 wxString choices[] = {
_(
"Ignored" ),
_(
"Matching" ),
_(
"Different" ) };
682 m_propertyGrid->SetCellEditor( i, 2,
new wxGridCellChoiceEditor( 3, choices ) );
683 m_propertyGrid->SetCellRenderer( i, 2,
new wxGridCellStringRenderer() );
695 int col1Width = totalWidth - col0Width - col2Width;
700 bool anyActive =
false;
720 if( aEvent.GetCol() == 2 && aEvent.GetRow() >= 0 && aEvent.GetRow() < (
int)
m_propertyRows.size()
723 m_propertyGrid->SetGridCursor( aEvent.GetRow(), aEvent.GetCol() );
735 int row = aEvent.GetRow();
736 int col = aEvent.GetCol();
747 if( val ==
_(
"Matching" ) )
749 else if( val ==
_(
"Different" ) )
756 bool anyActive =
false;
776 int col1Width = totalWidth - col0Width - col2Width;
813 std::vector<BOARD_ITEM*> items;
819 items.push_back( track );
823 items.push_back( fp );
825 for(
PAD*
pad : fp->Pads() )
826 items.push_back(
pad );
828 for(
PCB_FIELD* field : fp->GetFields() )
829 items.push_back( field );
832 items.push_back( gi );
834 for(
ZONE* zone : fp->Zones() )
835 items.push_back( zone );
839 items.push_back( item );
842 items.push_back( zone );
845 items.push_back(
group );
868 if( row.property ==
nullptr )
873 itemValue = getFootprintFieldValue(
static_cast<FOOTPRINT*
>( aItem ), row.propertyName );
888 if( itemValue.IsNull() )
891 bool valuesEqual = ( itemValue == row.rawValue );
911 if( !aMatchList.empty() )
914 if(
m_zoomToFit->IsChecked() && !aMatchList.empty() )
917 m_frame->GetCanvas()->ForceRefresh();
919 aStatusLabel->SetLabel( wxString::Format(
_(
"%zu items matched" ), aMatchList.size() ) );
931 matchList.push_back( item );
940 wxString expression =
m_queryEditor->GetText().Trim().Trim(
false );
942 if( expression.IsEmpty() )
947 if( queryUsesUnsupportedPairwiseSyntax( normalizedExpression ) )
949 wxString error =
_(
"B. expressions are not supported." );
952 wxMessageBox( error,
_(
"Expression Error" ), wxOK | wxICON_ERROR,
this );
963 [&](
const wxString& aMessage,
int aOffset )
965 errors += aMessage + wxT(
"\n" );
968 bool ok = compiler.
Compile( normalizedExpression.ToUTF8().data(), &ucode, &preflightContext );
973 wxMessageBox( errors,
_(
"Expression Error" ), wxOK | wxICON_ERROR,
this );
984 bool matched =
false;
985 LSET itemLayers = item->GetLayerSet();
1002 matchList.push_back( item );
1011 wxString
result = aPropName;
1012 result.Replace( wxT(
" " ), wxT(
"_" ) );
1019 if( aValue.GetType() == wxT(
"bool" ) )
1020 return aValue.GetBool() ? wxT(
"true" ) : wxT(
"false" );
1024 wxString val = formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue );
1026 val.Replace( wxT(
"'" ), wxT(
"\\'" ) );
1028 return wxString::Format( wxT(
"'%s'" ), val );
1040 return normalizeFormattedValueForExpression( aProp,
1041 formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue ) );
1047 if( aValue.GetType() == wxT(
"double" ) || aValue.GetType() == wxT(
"long" ) )
1048 return aValue.GetString();
1050 wxString val = aProp ? formatFindByPropertiesDisplayValue(
m_frame, aProp, aValue ) : aValue.GetString();
1052 val.Replace( wxT(
"'" ), wxT(
"\\'" ) );
1054 return wxString::Format( wxT(
"'%s'" ), val );
1060 wxArrayString conditions;
1072 if( row.property ==
nullptr )
1074 lhs = wxString::Format( wxT(
"A.getField(%s)" ),
1084 if( value.IsEmpty() )
1087 aSkippedRows->Add( wxGetTranslation( row.propertyName ) );
1094 conditions.Add( wxString::Format( wxT(
"%s %s %s" ), lhs, op, value ) );
1099 for(
size_t i = 0; i < conditions.size(); i++ )
1113 wxArrayString skipped;
1116 if( !expr.IsEmpty() || !skipped.empty() )
1121 if( skipped.empty() )
1123 else if( skipped.size() == 1 )
1124 m_queryStatusLabel->SetLabel( wxString::Format(
_(
"No value to match: %s" ), skipped[0] ) );
1126 m_queryStatusLabel->SetLabel( wxString::Format(
_(
"%zu rows have no value to match" ), skipped.size() ) );
1133 wxString expression =
m_queryEditor->GetText().Trim().Trim(
false );
1135 if( expression.IsEmpty() )
1143 if( queryUsesUnsupportedPairwiseSyntax( normalizedExpression ) )
1155 [&](
const wxString& aMessage,
int aOffset )
1157 errors += aMessage + wxT(
"\n" );
1160 bool ok = compiler.
Compile( normalizedExpression.ToUTF8().data(), &ucode, &preflightContext );
1173 if( sel != wxNOT_FOUND )
1191 if( aQuery.IsEmpty() )
1198 queries.erase( std::remove( queries.begin(), queries.end(), aQuery ), queries.end() );
1201 queries.insert( queries.begin(), aQuery );
1204 if( queries.size() > 10 )
1205 queries.resize( 10 );
1220 if( event.GetSelection() == 0 )
1234 wxString prev2 =
m_queryEditor->GetTextRange( pos - 2, pos );
1236 if( prev2 == wxT(
"A." ) )
1239 wxArrayString tokens;
1240 std::set<wxString> seen;
1251 wxString
name = prop->Name();
1253 if( !isVisibleInFindByProperties(
name, prop ) )
1256 name.Replace( wxT(
" " ), wxT(
"_" ) );
1258 if( seen.insert(
name ).second )
1263 for(
const wxString& fieldName : getQueryableFootprintFieldNames(
m_propertyRows ) )
1265 wxString exprName = fieldName;
1266 exprName.Replace( wxT(
" " ), wxT(
"_" ) );
1268 if( seen.insert( exprName ).second )
1269 tokens.Add( exprName );
static std::optional< VECTOR2D > normalized(const VECTOR2D &aVec)
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).
A wxEnumProperty that displays a color next to the enum value.
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.
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.
wxString normalizeQueryFieldAliases(const wxString &aExpression, const std::vector< PROPERTY_ROW_DATA > &aRows)
wxString normalizeQueryFieldAliases(const wxString &aExpression, const std::vector< PROPERTY_ROW_DATA > &aRows)
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)
#define TYPE_HASH(x)
Macro to generate unique identifier for a type.
@ 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