/* * This program source code file is part of KiCad, a free EDA CAD application. * * Copyright (C) 2018-2024 KiCad Developers, see AUTHORS.txt for contributors. * * 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 2 * 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, you may find one here: * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * or you may search the http://www.gnu.org website for the version 2 license, * or you may write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ #include #include #include #include #include #include #include #include "grid_layer_box_helpers.h" #include #include enum { MYID_SELECT_FOOTPRINT = 991, // must be within GRID_TRICKS' enum range MYID_SHOW_DATASHEET }; wxArrayString g_menuOrientations; PCB_FIELDS_GRID_TABLE::PCB_FIELDS_GRID_TABLE( PCB_BASE_FRAME* aFrame, DIALOG_SHIM* aDialog ) : m_frame( aFrame ), m_dialog( aDialog ), m_fieldNameValidator( FIELD_NAME ), m_referenceValidator( REFERENCE_FIELD ), m_valueValidator( VALUE_FIELD ), m_urlValidator( FIELD_VALUE ), m_nonUrlValidator( FIELD_VALUE ) { // Build the column attributes. m_readOnlyAttr = new wxGridCellAttr; m_readOnlyAttr->SetReadOnly( true ); m_boolColAttr = new wxGridCellAttr; m_boolColAttr->SetRenderer( new wxGridCellBoolRenderer() ); m_boolColAttr->SetEditor( new wxGridCellBoolEditor() ); m_boolColAttr->SetAlignment( wxALIGN_CENTER, wxALIGN_CENTER ); if( g_menuOrientations.IsEmpty() ) { g_menuOrientations.push_back( "0" + EDA_UNIT_UTILS::GetText( EDA_UNITS::DEGREES ) ); g_menuOrientations.push_back( "90" + EDA_UNIT_UTILS::GetText( EDA_UNITS::DEGREES ) ); g_menuOrientations.push_back( "-90" + EDA_UNIT_UTILS::GetText( EDA_UNITS::DEGREES ) ); g_menuOrientations.push_back( "180" + EDA_UNIT_UTILS::GetText( EDA_UNITS::DEGREES ) ); } m_orientationColAttr = new wxGridCellAttr; m_orientationColAttr->SetEditor( new GRID_CELL_COMBOBOX( g_menuOrientations ) ); m_layerColAttr = new wxGridCellAttr; m_layerColAttr->SetRenderer( new GRID_CELL_LAYER_RENDERER( m_frame ) ); m_layerColAttr->SetEditor( new GRID_CELL_LAYER_SELECTOR( m_frame, {} ) ); m_referenceAttr = new wxGridCellAttr; GRID_CELL_TEXT_EDITOR* referenceEditor = new GRID_CELL_TEXT_EDITOR(); referenceEditor->SetValidator( m_referenceValidator ); m_referenceAttr->SetEditor( referenceEditor ); m_valueAttr = new wxGridCellAttr; GRID_CELL_TEXT_EDITOR* valueEditor = new GRID_CELL_TEXT_EDITOR(); valueEditor->SetValidator( m_valueValidator ); m_valueAttr->SetEditor( valueEditor ); m_footprintAttr = new wxGridCellAttr; if( !m_frame->IsType( FRAME_FOOTPRINT_EDITOR ) ) m_footprintAttr->SetReadOnly( true ); GRID_CELL_FPID_EDITOR* fpIdEditor = new GRID_CELL_FPID_EDITOR( m_dialog, "" ); fpIdEditor->SetValidator( m_nonUrlValidator ); m_footprintAttr->SetEditor( fpIdEditor ); m_urlAttr = new wxGridCellAttr; GRID_CELL_URL_EDITOR* urlEditor = new GRID_CELL_URL_EDITOR( m_dialog ); urlEditor->SetValidator( m_urlValidator ); m_urlAttr->SetEditor( urlEditor ); m_eval = std::make_unique( m_frame->GetUserUnits() ); m_frame->Bind( EDA_EVT_UNITS_CHANGED, &PCB_FIELDS_GRID_TABLE::onUnitsChanged, this ); } PCB_FIELDS_GRID_TABLE::~PCB_FIELDS_GRID_TABLE() { m_readOnlyAttr->DecRef(); m_boolColAttr->DecRef(); m_orientationColAttr->DecRef(); m_layerColAttr->DecRef(); m_referenceAttr->DecRef(); m_valueAttr->DecRef(); m_footprintAttr->DecRef(); m_urlAttr->DecRef(); m_frame->Unbind( EDA_EVT_UNITS_CHANGED, &PCB_FIELDS_GRID_TABLE::onUnitsChanged, this ); } void PCB_FIELDS_GRID_TABLE::onUnitsChanged( wxCommandEvent& aEvent ) { if( GetView() ) GetView()->ForceRefresh(); aEvent.Skip(); } wxString PCB_FIELDS_GRID_TABLE::GetColLabelValue( int aCol ) { switch( aCol ) { case PFC_NAME: return _( "Name" ); case PFC_VALUE: return _( "Value" ); case PFC_SHOWN: return _( "Show" ); case PFC_WIDTH: return _( "Width" ); case PFC_HEIGHT: return _( "Height" ); case PFC_THICKNESS: return _( "Thickness" ); case PFC_ITALIC: return _( "Italic" ); case PFC_LAYER: return _( "Layer" ); case PFC_ORIENTATION: return _( "Orientation" ); case PFC_UPRIGHT: return _( "Keep Upright" ); case PFC_XOFFSET: return _( "X Offset" ); case PFC_YOFFSET: return _( "Y Offset" ); case PFC_KNOCKOUT: return _( "Knockout" ); case PFC_MIRRORED: return _( "Mirrored" ); default: wxFAIL; return wxEmptyString; } } bool PCB_FIELDS_GRID_TABLE::CanGetValueAs( int aRow, int aCol, const wxString& aTypeName ) { switch( aCol ) { case PFC_NAME: case PFC_VALUE: case PFC_WIDTH: case PFC_HEIGHT: case PFC_THICKNESS: case PFC_ORIENTATION: case PFC_XOFFSET: case PFC_YOFFSET: return aTypeName == wxGRID_VALUE_STRING; case PFC_SHOWN: case PFC_ITALIC: case PFC_UPRIGHT: case PFC_KNOCKOUT: case PFC_MIRRORED: return aTypeName == wxGRID_VALUE_BOOL; case PFC_LAYER: return aTypeName == wxGRID_VALUE_NUMBER; default: wxFAIL; return false; } } bool PCB_FIELDS_GRID_TABLE::CanSetValueAs( int aRow, int aCol, const wxString& aTypeName ) { return CanGetValueAs( aRow, aCol, aTypeName ); } wxGridCellAttr* PCB_FIELDS_GRID_TABLE::GetAttr( int aRow, int aCol, wxGridCellAttr::wxAttrKind aKind ) { switch( aCol ) { case PFC_NAME: if( aRow < MANDATORY_FIELDS ) { m_readOnlyAttr->IncRef(); return enhanceAttr( m_readOnlyAttr, aRow, aCol, aKind ); } return enhanceAttr( nullptr, aRow, aCol, aKind ); case PFC_VALUE: if( aRow == REFERENCE_FIELD ) { m_referenceAttr->IncRef(); return enhanceAttr( m_referenceAttr, aRow, aCol, aKind ); } else if( aRow == VALUE_FIELD ) { m_valueAttr->IncRef(); return enhanceAttr( m_valueAttr, aRow, aCol, aKind ); } else if( aRow == FOOTPRINT_FIELD ) { m_footprintAttr->IncRef(); return enhanceAttr( m_footprintAttr, aRow, aCol, aKind ); } else if( aRow == DATASHEET_FIELD ) { m_urlAttr->IncRef(); return enhanceAttr( m_urlAttr, aRow, aCol, aKind ); } return enhanceAttr( nullptr, aRow, aCol, aKind ); case PFC_WIDTH: case PFC_HEIGHT: case PFC_THICKNESS: case PFC_XOFFSET: case PFC_YOFFSET: return enhanceAttr( nullptr, aRow, aCol, aKind ); case PFC_SHOWN: case PFC_ITALIC: case PFC_UPRIGHT: case PFC_KNOCKOUT: case PFC_MIRRORED: m_boolColAttr->IncRef(); return enhanceAttr( m_boolColAttr, aRow, aCol, aKind ); case PFC_LAYER: m_layerColAttr->IncRef(); return enhanceAttr( m_layerColAttr, aRow, aCol, aKind ); case PFC_ORIENTATION: m_orientationColAttr->IncRef(); return enhanceAttr( m_orientationColAttr, aRow, aCol, aKind ); default: wxFAIL; return enhanceAttr( nullptr, aRow, aCol, aKind ); } } wxString PCB_FIELDS_GRID_TABLE::GetValue( int aRow, int aCol ) { wxGrid* grid = GetView(); const PCB_FIELD& field = this->at( (size_t) aRow ); if( grid->GetGridCursorRow() == aRow && grid->GetGridCursorCol() == aCol && grid->IsCellEditControlShown() ) { auto it = m_evalOriginal.find( { aRow, aCol } ); if( it != m_evalOriginal.end() ) return it->second; } switch( aCol ) { case PFC_NAME: return field.GetName(); case PFC_VALUE: return field.GetText(); case PFC_WIDTH: return m_frame->StringFromValue( field.GetTextWidth(), true ); case PFC_HEIGHT: return m_frame->StringFromValue( field.GetTextHeight(), true ); case PFC_THICKNESS: return m_frame->StringFromValue( field.GetTextThickness(), true ); case PFC_LAYER: return field.GetLayerName(); case PFC_ORIENTATION: { EDA_ANGLE angle = field.GetTextAngle() - field.GetParentFootprint()->GetOrientation(); return m_frame->StringFromValue( angle, true ); } case PFC_XOFFSET: return m_frame->StringFromValue( field.GetFPRelativePosition().x, true ); case PFC_YOFFSET: return m_frame->StringFromValue( field.GetFPRelativePosition().y, true ); default: // we can't assert here because wxWidgets sometimes calls this without checking // the column type when trying to see if there's an overflow return wxT( "bad wxWidgets!" ); } } bool PCB_FIELDS_GRID_TABLE::GetValueAsBool( int aRow, int aCol ) { PCB_FIELD& field = this->at( (size_t) aRow ); switch( aCol ) { case PFC_SHOWN: return field.IsVisible(); case PFC_ITALIC: return field.IsItalic(); case PFC_UPRIGHT: return field.IsKeepUpright(); case PFC_KNOCKOUT: return field.IsKnockout(); case PFC_MIRRORED: return field.IsMirrored(); default: wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) ); return false; } } long PCB_FIELDS_GRID_TABLE::GetValueAsLong( int aRow, int aCol ) { PCB_FIELD& field = this->at( (size_t) aRow ); switch( aCol ) { case PFC_LAYER: return field.GetLayer(); default: wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a long value" ), aCol ) ); return 0; } } void PCB_FIELDS_GRID_TABLE::SetValue( int aRow, int aCol, const wxString &aValue ) { PCB_FIELD& field = this->at( (size_t) aRow ); VECTOR2I pos; wxString value = aValue; switch( aCol ) { case PFC_WIDTH: case PFC_HEIGHT: case PFC_THICKNESS: case PFC_XOFFSET: case PFC_YOFFSET: m_eval->SetDefaultUnits( m_frame->GetUserUnits() ); if( m_eval->Process( value ) ) { m_evalOriginal[ { aRow, aCol } ] = value; value = m_eval->Result(); } break; default: break; } switch( aCol ) { case PFC_NAME: field.SetName( value ); break; case PFC_VALUE: field.SetText( value ); break; case PFC_WIDTH: field.SetTextWidth( m_frame->ValueFromString( value ) ); break; case PFC_HEIGHT: field.SetTextHeight( m_frame->ValueFromString( value ) ); break; case PFC_THICKNESS: field.SetTextThickness( m_frame->ValueFromString( value ) ); break; case PFC_ORIENTATION: field.SetTextAngle( m_frame->AngleValueFromString( value ) + field.GetParentFootprint()->GetOrientation() ); break; case PFC_XOFFSET: case PFC_YOFFSET: pos = field.GetFPRelativePosition(); if( aCol == PFC_XOFFSET ) pos.x = m_frame->ValueFromString( value ); else pos.y = m_frame->ValueFromString( value ); field.SetFPRelativePosition( pos ); break; default: wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a string value" ), aCol ) ); break; } GetView()->Refresh(); } void PCB_FIELDS_GRID_TABLE::SetValueAsBool( int aRow, int aCol, bool aValue ) { PCB_FIELD& field = this->at( (size_t) aRow ); switch( aCol ) { case PFC_SHOWN: field.SetVisible( aValue ); break; case PFC_ITALIC: field.SetItalic( aValue ); break; case PFC_UPRIGHT: field.SetKeepUpright( aValue ); break; case PFC_KNOCKOUT: field.SetIsKnockout( aValue ); break; case PFC_MIRRORED: field.SetMirrored( aValue ); break; default: wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a bool value" ), aCol ) ); break; } } void PCB_FIELDS_GRID_TABLE::SetValueAsLong( int aRow, int aCol, long aValue ) { PCB_FIELD& field = this->at( (size_t) aRow ); switch( aCol ) { case PFC_LAYER: field.SetLayer( ToLAYER_ID( (int) aValue ) ); field.SetMirrored( IsBackLayer( field.GetLayer() ) ); break; default: wxFAIL_MSG( wxString::Format( wxT( "column %d doesn't hold a long value" ), aCol ) ); break; } }