2024-08-30 16:03:06 -07:00
/*
* This program source code file is part of KiCad , a free EDA CAD application .
*
* Copyright ( C ) 2025 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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
2025-09-06 00:05:27 +01:00
# include <dialog_lib_fields_table.h>
2024-08-30 16:03:06 -07:00
# include <bitmaps.h>
# include <common.h>
# include <confirm.h>
# include <eda_doc.h>
# include <lib_fields_data_model.h>
# include <grid_tricks.h>
# include <kiface_base.h>
# include <kiplatform/ui.h>
# include <kiway_player.h>
2025-09-04 06:59:02 -07:00
# include <string_utils.h>
2024-08-30 16:03:06 -07:00
# include <symbol_editor/lib_symbol_library_manager.h>
# include <project_sch.h>
# include <symbol_edit_frame.h>
# include <symbol_editor/symbol_editor_settings.h>
# include <widgets/grid_checkbox.h>
2025-09-04 06:59:02 -07:00
# include <widgets/grid_icon_text_helpers.h>
2024-08-30 16:03:06 -07:00
# include <widgets/grid_text_button_helpers.h>
# include <widgets/std_bitmap_button.h>
# include <tools/sch_actions.h>
# include <tool/tool_manager.h>
# include <trace_helpers.h>
2025-09-06 00:05:27 +01:00
# include <fields_data_model.h>
2024-08-30 16:03:06 -07:00
# include <dialog_lib_new_symbol.h>
# include <wx/arrstr.h>
# include <wx/msgdlg.h>
# include <wx/srchctrl.h>
# include <wx/textdlg.h>
using DIALOG_NEW_SYMBOL = DIALOG_LIB_NEW_SYMBOL ;
# ifdef __WXMAC__
# define COLUMN_MARGIN 3
# else
# define COLUMN_MARGIN 15
# endif
2025-09-06 00:05:27 +01:00
class VIEW_CONTROLS_GRID_TRICKS : public GRID_TRICKS
{
public :
VIEW_CONTROLS_GRID_TRICKS ( WX_GRID * aGrid ) :
GRID_TRICKS ( aGrid )
{ }
protected :
void doPopupSelection ( wxCommandEvent & event ) override
{
if ( event . GetId ( ) > = GRIDTRICKS_FIRST_SHOWHIDE )
m_grid - > PostSizeEvent ( ) ;
GRID_TRICKS : : doPopupSelection ( event ) ;
}
} ;
2024-08-30 16:03:06 -07:00
enum
{
MYID_SELECT_FOOTPRINT = GRIDTRICKS_FIRST_CLIENT_ID ,
MYID_SHOW_DATASHEET ,
MYID_REVERT_ROW ,
MYID_CLEAR_CELL ,
MYID_CREATE_DERIVED_SYMBOL
} ;
2025-09-06 00:05:27 +01:00
2024-08-30 16:03:06 -07:00
class LIB_FIELDS_EDITOR_GRID_TRICKS : public GRID_TRICKS
{
public :
2025-09-06 00:05:27 +01:00
LIB_FIELDS_EDITOR_GRID_TRICKS ( DIALOG_LIB_FIELDS_TABLE * aParent , WX_GRID * aGrid ,
VIEW_CONTROLS_GRID_DATA_MODEL * aViewFieldsData ,
2024-08-30 16:03:06 -07:00
LIB_FIELDS_EDITOR_GRID_DATA_MODEL * aDataModel ) :
GRID_TRICKS ( aGrid ) ,
m_dlg ( aParent ) ,
2025-09-06 00:05:27 +01:00
m_viewControlsDataModel ( aViewFieldsData ) ,
2024-08-30 16:03:06 -07:00
m_dataModel ( aDataModel )
{ }
protected :
void showPopupMenu ( wxMenu & menu , wxGridEvent & aEvent ) override
{
wxMenuItem * revertMenu = menu . Append ( MYID_REVERT_ROW , _ ( " Revert symbol " ) , _ ( " Revert the symbol to its last saved state " ) , wxITEM_NORMAL ) ;
wxMenuItem * clearMenu = menu . Append ( MYID_CLEAR_CELL , _ ( " Clear cell " ) , _ ( " Clear the cell value " ) , wxITEM_NORMAL ) ;
menu . AppendSeparator ( ) ;
wxMenuItem * createDerivedSymbolMenu = menu . Append ( MYID_CREATE_DERIVED_SYMBOL , _ ( " Create Derived Symbol " ) , _ ( " Create a new symbol derived from the selected one " ) , wxITEM_NORMAL ) ;
// Get global mouse position and convert to grid client coords
wxPoint mousePos = wxGetMousePosition ( ) ;
wxPoint gridPt = m_grid - > ScreenToClient ( mousePos ) ;
// Offset by grid header (column label) height so header area doesn't map to a row.
int headerHeight = m_grid - > GetColLabelSize ( ) ;
gridPt . y - = headerHeight ;
if ( gridPt . y < 0 )
gridPt . y = 0 ;
int row = m_grid - > YToRow ( gridPt . y ) ;
int col = m_grid - > XToCol ( gridPt . x ) ;
m_grid - > SetGridCursor ( row , col ) ;
revertMenu - > Enable ( m_dataModel - > IsCellEdited ( row , col ) ) ;
clearMenu - > Enable ( ! m_dataModel - > IsCellClear ( row , col ) ) ;
createDerivedSymbolMenu - > Enable ( m_dataModel - > IsRowSingleSymbol ( row ) ) ;
if ( m_dataModel - > GetColFieldName ( col ) = = GetCanonicalFieldName ( FIELD_T : : FOOTPRINT ) )
{
2025-09-06 00:05:27 +01:00
menu . Append ( MYID_SELECT_FOOTPRINT , _ ( " Select Footprint... " ) , _ ( " Browse for footprint " ) ) ;
2024-08-30 16:03:06 -07:00
menu . AppendSeparator ( ) ;
}
else if ( m_dataModel - > GetColFieldName ( col ) = = GetCanonicalFieldName ( FIELD_T : : DATASHEET ) )
{
2025-09-06 00:05:27 +01:00
menu . Append ( MYID_SHOW_DATASHEET , _ ( " Show Datasheet " ) , _ ( " Show datasheet in browser " ) ) ;
2024-08-30 16:03:06 -07:00
menu . AppendSeparator ( ) ;
}
GRID_TRICKS : : showPopupMenu ( menu , aEvent ) ;
}
void doPopupSelection ( wxCommandEvent & event ) override
{
int row = m_grid - > GetGridCursorRow ( ) ;
int col = m_grid - > GetGridCursorCol ( ) ;
if ( event . GetId ( ) = = MYID_REVERT_ROW )
{
if ( m_grid - > CommitPendingChanges ( false ) )
m_dataModel - > RevertRow ( row ) ;
if ( m_dataModel - > IsEdited ( ) )
m_dlg - > OnModify ( ) ;
else
m_dlg - > ClearModify ( ) ;
m_grid - > ForceRefresh ( ) ;
}
else if ( event . GetId ( ) = = MYID_CLEAR_CELL )
{
if ( m_grid - > CommitPendingChanges ( false ) )
m_dataModel - > ClearCell ( row , col ) ;
if ( m_dataModel - > IsEdited ( ) )
m_dlg - > OnModify ( ) ;
else
m_dlg - > ClearModify ( ) ;
m_grid - > ForceRefresh ( ) ;
}
else if ( event . GetId ( ) = = MYID_CREATE_DERIVED_SYMBOL )
{
const LIB_SYMBOL * parentSymbol = m_dataModel - > GetSymbolForRow ( row ) ;
wxArrayString symbolNames ;
m_dataModel - > GetSymbolNames ( symbolNames ) ;
2025-09-06 00:05:27 +01:00
auto validator =
[ & ] ( wxString newName ) - > bool
{
return symbolNames . Index ( newName ) = = wxNOT_FOUND ;
} ;
2024-08-30 16:03:06 -07:00
EDA_DRAW_FRAME * frame = dynamic_cast < EDA_DRAW_FRAME * > ( m_dlg - > GetParent ( ) ) ;
DIALOG_NEW_SYMBOL dlg ( frame , symbolNames , parentSymbol - > GetName ( ) , validator ) ;
if ( dlg . ShowModal ( ) ! = wxID_OK )
return ;
wxString derivedName = dlg . GetName ( ) ;
m_dataModel - > CreateDerivedSymbolImmediate ( row , col , derivedName ) ;
if ( m_dataModel - > IsEdited ( ) )
m_dlg - > OnModify ( ) ;
m_grid - > ForceRefresh ( ) ;
}
else if ( event . GetId ( ) = = MYID_SELECT_FOOTPRINT )
{
// pick a footprint using the footprint picker.
wxString fpid = m_grid - > GetCellValue ( row , col ) ;
2025-09-06 00:05:27 +01:00
if ( KIWAY_PLAYER * frame = m_dlg - > Kiway ( ) . Player ( FRAME_FOOTPRINT_CHOOSER , true , m_dlg ) )
2024-08-30 16:03:06 -07:00
{
if ( frame - > ShowModal ( & fpid , m_dlg ) )
m_grid - > SetCellValue ( row , col , fpid ) ;
frame - > Destroy ( ) ;
}
}
else if ( event . GetId ( ) = = MYID_SHOW_DATASHEET )
{
wxString datasheet_uri = m_grid - > GetCellValue ( row , col ) ;
2025-09-06 00:05:27 +01:00
GetAssociatedDocument ( m_dlg , datasheet_uri , & m_dlg - > Prj ( ) , PROJECT_SCH : : SchSearchS ( & m_dlg - > Prj ( ) ) ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
else if ( event . GetId ( ) > = GRIDTRICKS_FIRST_SHOWHIDE )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
if ( ! m_grid - > CommitPendingChanges ( false ) )
return ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
// Pop-up column order is the order of the shown fields, not the viewControls order
col = event . GetId ( ) - GRIDTRICKS_FIRST_SHOWHIDE ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
bool show = ! m_dataModel - > GetShowColumn ( col ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_dlg - > ShowHideColumn ( col , show ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
wxString fieldName = m_dataModel - > GetColFieldName ( col ) ;
for ( row = 0 ; row < m_viewControlsDataModel - > GetNumberRows ( ) ; row + + )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
if ( m_viewControlsDataModel - > GetCanonicalFieldName ( row ) = = fieldName )
m_viewControlsDataModel - > SetValueAsBool ( row , SHOW_FIELD_COLUMN , show ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
if ( m_viewControlsDataModel - > GetView ( ) )
m_viewControlsDataModel - > GetView ( ) - > ForceRefresh ( ) ;
}
else
{
GRID_TRICKS : : doPopupSelection ( event ) ;
2024-08-30 16:03:06 -07:00
}
}
2025-09-06 00:05:27 +01:00
DIALOG_LIB_FIELDS_TABLE * m_dlg ;
VIEW_CONTROLS_GRID_DATA_MODEL * m_viewControlsDataModel ;
2024-08-30 16:03:06 -07:00
LIB_FIELDS_EDITOR_GRID_DATA_MODEL * m_dataModel ;
} ;
2025-09-06 00:05:27 +01:00
DIALOG_LIB_FIELDS_TABLE : : DIALOG_LIB_FIELDS_TABLE ( SYMBOL_EDIT_FRAME * parent , DIALOG_LIB_FIELDS_TABLE : : SCOPE aScope ) :
DIALOG_LIB_FIELDS_TABLE_BASE ( parent , wxID_ANY ) ,
m_parent ( parent ) ,
m_scope ( aScope ) ,
m_viewControlsDataModel ( nullptr ) ,
m_dataModel ( nullptr )
2024-08-30 16:03:06 -07:00
{
m_bRefresh - > SetBitmap ( KiBitmapBundle ( BITMAPS : : small_refresh ) ) ;
m_addFieldButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : small_plus ) ) ;
m_removeFieldButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : small_trash ) ) ;
m_renameFieldButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : small_edit ) ) ;
2025-09-06 19:06:36 +01:00
m_sidebarButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : left ) ) ;
2025-09-06 00:05:27 +01:00
m_viewControlsDataModel = new VIEW_CONTROLS_GRID_DATA_MODEL ( false ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_viewControlsGrid - > UseNativeColHeader ( true ) ;
m_viewControlsGrid - > SetTable ( m_viewControlsDataModel , true ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
// must be done after SetTable(), which appears to re-set it
m_viewControlsGrid - > SetSelectionMode ( wxGrid : : wxGridSelectCells ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
// add Cut, Copy, and Paste to wxGrid
m_viewControlsGrid - > PushEventHandler ( new VIEW_CONTROLS_GRID_TRICKS ( m_viewControlsGrid ) ) ;
2024-08-30 16:03:06 -07:00
wxGridCellAttr * attr = new wxGridCellAttr ;
2025-09-06 00:05:27 +01:00
attr - > SetReadOnly ( true ) ;
m_viewControlsDataModel - > SetColAttr ( attr , DISPLAY_NAME_COLUMN ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
attr = new wxGridCellAttr ;
attr - > SetRenderer ( new wxGridCellBoolRenderer ( ) ) ;
attr - > SetReadOnly ( ) ; // not really; we delegate interactivity to GRID_TRICKS
attr - > SetAlignment ( wxALIGN_CENTER , wxALIGN_CENTER ) ;
m_viewControlsDataModel - > SetColAttr ( attr , SHOW_FIELD_COLUMN ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
attr = new wxGridCellAttr ;
attr - > SetRenderer ( new wxGridCellBoolRenderer ( ) ) ;
attr - > SetReadOnly ( ) ; // not really; we delegate interactivity to GRID_TRICKS
attr - > SetAlignment ( wxALIGN_CENTER , wxALIGN_CENTER ) ;
m_viewControlsDataModel - > SetColAttr ( attr , GROUP_BY_COLUMN ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
// Compress the view controls grid. (We want it to look different from the fields grid.)
m_viewControlsGrid - > SetDefaultRowSize ( m_viewControlsGrid - > GetDefaultRowSize ( ) - FromDIP ( 4 ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_filter - > SetDescriptiveText ( _ ( " Filter " ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_dataModel = new LIB_FIELDS_EDITOR_GRID_DATA_MODEL ( ) ;
2024-08-30 16:03:06 -07:00
m_grid - > UseNativeColHeader ( true ) ;
m_grid - > SetTable ( m_dataModel , true ) ;
// must be done after SetTable(), which appears to re-set it
m_grid - > SetSelectionMode ( wxGrid : : wxGridSelectCells ) ;
// add Cut, Copy, and Paste to wxGrid
2025-09-06 00:05:27 +01:00
m_grid - > PushEventHandler ( new LIB_FIELDS_EDITOR_GRID_TRICKS ( this , m_grid , m_viewControlsDataModel ,
m_dataModel ) ) ;
2024-08-30 16:03:06 -07:00
SetInitialFocus ( m_grid ) ;
m_grid - > ClearSelection ( ) ;
SetupStandardButtons ( ) ;
finishDialogSettings ( ) ;
SetSize ( wxSize ( horizPixelsFromDU ( 600 ) , vertPixelsFromDU ( 300 ) ) ) ;
2025-09-06 00:05:27 +01:00
SYMBOL_EDITOR_SETTINGS : : PANEL_LIB_FIELDS_TABLE & cfg = m_parent - > libeditconfig ( ) - > m_LibFieldEditor ;
m_viewControlsGrid - > ShowHideColumns ( cfg . view_controls_visible_columns ) ;
2025-09-07 18:26:48 +02:00
// Ensure at least one column is visible otherwise we cannot add columns
// because there is no area to right click to get the menu managing the show/hide columns
wxString visible_column = m_viewControlsGrid - > GetShownColumnsAsString ( ) ; ;
if ( visible_column . IsEmpty ( ) )
{
visible_column = wxT ( " 0 " ) ;
m_viewControlsGrid - > ShowHideColumns ( visible_column ) ;
}
2025-09-06 00:05:27 +01:00
CallAfter ( [ this , cfg ] ( )
{
2025-09-06 19:06:36 +01:00
if ( cfg . sidebar_collapsed )
{
m_splitterMainWindow - > Unsplit ( m_leftPanel ) ;
m_sidebarButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : right ) ) ;
}
else
{
m_splitterMainWindow - > SetSashPosition ( cfg . sash_pos ) ;
}
2025-09-06 00:05:27 +01:00
} ) ;
2024-08-30 16:03:06 -07:00
Center ( ) ;
// Connect Events
2025-09-06 00:05:27 +01:00
m_grid - > Bind ( wxEVT_GRID_COL_SORT , & DIALOG_LIB_FIELDS_TABLE : : OnColSort , this ) ;
m_grid - > Bind ( wxEVT_GRID_COL_MOVE , & DIALOG_LIB_FIELDS_TABLE : : OnColMove , this ) ;
m_grid - > Bind ( wxEVT_GRID_CELL_LEFT_CLICK , & DIALOG_LIB_FIELDS_TABLE : : OnTableCellClick , this ) ;
m_viewControlsGrid - > Bind ( wxEVT_GRID_CELL_CHANGED , & DIALOG_LIB_FIELDS_TABLE : : OnViewControlsCellChanged , this ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
DIALOG_LIB_FIELDS_TABLE : : ~ DIALOG_LIB_FIELDS_TABLE ( )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
SYMBOL_EDITOR_SETTINGS : : PANEL_LIB_FIELDS_TABLE & cfg = m_parent - > libeditconfig ( ) - > m_LibFieldEditor ;
cfg . view_controls_visible_columns = m_viewControlsGrid - > GetShownColumnsAsString ( ) ;
2025-09-06 19:06:36 +01:00
cfg . sidebar_collapsed = ( m_splitterMainWindow - > GetSashPosition ( ) = = 0 ) ;
if ( ! cfg . sidebar_collapsed )
cfg . sash_pos = m_splitterMainWindow - > GetSashPosition ( ) ;
2025-09-06 00:05:27 +01:00
for ( int i = 0 ; i < m_grid - > GetNumberCols ( ) ; i + + )
{
if ( m_grid - > IsColShown ( i ) )
{
std : : string fieldName ( m_dataModel - > GetColFieldName ( i ) . ToUTF8 ( ) ) ;
cfg . field_widths [ fieldName ] = m_grid - > GetColSize ( i ) ;
}
}
2024-08-30 16:03:06 -07:00
// Disconnect Events
2025-09-06 00:05:27 +01:00
m_grid - > Unbind ( wxEVT_GRID_COL_SORT , & DIALOG_LIB_FIELDS_TABLE : : OnColSort , this ) ;
m_grid - > Unbind ( wxEVT_GRID_COL_MOVE , & DIALOG_LIB_FIELDS_TABLE : : OnColMove , this ) ;
m_grid - > Unbind ( wxEVT_GRID_CELL_LEFT_CLICK , & DIALOG_LIB_FIELDS_TABLE : : OnTableCellClick , this ) ;
m_viewControlsGrid - > Unbind ( wxEVT_GRID_CELL_CHANGED , & DIALOG_LIB_FIELDS_TABLE : : OnViewControlsCellChanged , this ) ;
2024-08-30 16:03:06 -07:00
// Delete the GRID_TRICKS.
2025-09-06 00:05:27 +01:00
m_viewControlsGrid - > PopEventHandler ( true ) ;
2024-08-30 16:03:06 -07:00
m_grid - > PopEventHandler ( true ) ;
2025-09-06 00:05:27 +01:00
// we gave ownership of m_viewControlsDataModel & m_dataModel to the wxGrids...
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : SetupColumnProperties ( int aCol )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
wxGridCellAttr * attr = new wxGridCellAttr ;
attr - > SetReadOnly ( false ) ;
// Set some column types to specific editors
if ( m_dataModel - > GetColFieldName ( aCol ) = = GetCanonicalFieldName ( FIELD_T : : FOOTPRINT ) )
{
// Create symbol netlist for footprint picker
wxString symbolNetlist ;
if ( ! m_symbolsList . empty ( ) )
{
// Use the first symbol's netlist (all symbols in lib should have similar pin structure)
LIB_SYMBOL * symbol = m_symbolsList [ 0 ] ;
wxArrayString pins ;
2025-09-04 17:32:04 -07:00
for ( SCH_PIN * pin : symbol - > GetGraphicalPins ( 0 /* all units */ , 1 /* single bodyStyle */ ) )
2025-09-06 00:05:27 +01:00
pins . push_back ( pin - > GetNumber ( ) + ' ' + pin - > GetShownName ( ) ) ;
if ( ! pins . IsEmpty ( ) )
symbolNetlist < < EscapeString ( wxJoin ( pins , ' \t ' ) , CTX_LINE ) ;
symbolNetlist < < wxS ( " \r " ) ;
wxArrayString fpFilters = symbol - > GetFPFilters ( ) ;
if ( ! fpFilters . IsEmpty ( ) )
symbolNetlist < < EscapeString ( wxJoin ( fpFilters , ' ' ) , CTX_LINE ) ;
symbolNetlist < < wxS ( " \r " ) ;
}
attr - > SetEditor ( new GRID_CELL_FPID_EDITOR ( this , symbolNetlist ) ) ;
m_dataModel - > SetColAttr ( attr , aCol ) ;
}
else if ( m_dataModel - > GetColFieldName ( aCol ) = = GetCanonicalFieldName ( FIELD_T : : DATASHEET ) )
{
// set datasheet column viewer button
attr - > SetEditor ( new GRID_CELL_URL_EDITOR ( this , PROJECT_SCH : : SchSearchS ( & Prj ( ) ) ) ) ;
m_dataModel - > SetColAttr ( attr , aCol ) ;
}
else if ( m_dataModel - > ColIsCheck ( aCol ) )
{
attr - > SetAlignment ( wxALIGN_CENTER , wxALIGN_CENTER ) ;
attr - > SetRenderer ( new GRID_CELL_CHECKBOX_RENDERER ( ) ) ;
m_dataModel - > SetColAttr ( attr , aCol ) ;
}
else if ( IsGeneratedField ( m_dataModel - > GetColFieldName ( aCol ) ) )
{
attr - > SetReadOnly ( ) ;
m_dataModel - > SetColAttr ( attr , aCol ) ;
}
else
{
attr - > SetEditor ( m_grid - > GetDefaultEditor ( ) ) ;
m_dataModel - > SetColAttr ( attr , aCol ) ;
}
}
void DIALOG_LIB_FIELDS_TABLE : : SetupAllColumnProperties ( )
{
SYMBOL_EDITOR_SETTINGS * cfg = m_parent - > GetSettings ( ) ;
wxSize defaultDlgSize = ConvertDialogToPixels ( wxSize ( 600 , 300 ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
// Restore column sorting order and widths
m_grid - > AutoSizeColumns ( false ) ;
int sortCol = 0 ;
bool sortAscending = true ;
// Find the VALUE column for initial sorting
int valueCol = m_dataModel - > GetFieldNameCol ( GetCanonicalFieldName ( FIELD_T : : VALUE ) ) ;
// Set initial sort to VALUE field (ascending) if no previous sort preference exists
if ( m_dataModel - > GetSortCol ( ) = = 0 & & valueCol ! = - 1 )
{
sortCol = valueCol ;
sortAscending = true ;
m_dataModel - > SetSorting ( sortCol , sortAscending ) ;
}
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
for ( int col = 0 ; col < m_grid - > GetNumberCols ( ) ; + + col )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
SetupColumnProperties ( col ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( col = = m_dataModel - > GetSortCol ( ) )
{
sortCol = col ;
sortAscending = m_dataModel - > GetSortAsc ( ) ;
}
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
// sync m_grid's column visibilities to Show checkboxes in m_viewControlsGrid
for ( int i = 0 ; i < m_viewControlsDataModel - > GetNumberRows ( ) ; + + i )
{
int col = m_dataModel - > GetFieldNameCol ( m_viewControlsDataModel - > GetCanonicalFieldName ( i ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( col = = - 1 )
continue ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
bool show = m_viewControlsDataModel - > GetValueAsBool ( i , SHOW_FIELD_COLUMN ) ;
m_dataModel - > SetShowColumn ( col , show ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( show )
{
m_grid - > ShowCol ( col ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
std : : string key ( m_dataModel - > GetColFieldName ( col ) . ToUTF8 ( ) ) ;
if ( cfg - > m_LibFieldEditor . field_widths . count ( key )
& & ( cfg - > m_LibFieldEditor . field_widths . at ( key ) > 0 ) )
{
m_grid - > SetColSize ( col , cfg - > m_LibFieldEditor . field_widths . at ( key ) ) ;
}
else
{
int textWidth = m_dataModel - > GetDataWidth ( col ) + COLUMN_MARGIN ;
int maxWidth = defaultDlgSize . x / 3 ;
m_grid - > SetColSize ( col , std : : clamp ( textWidth , 100 , maxWidth ) ) ;
}
}
else
{
m_grid - > HideCol ( col ) ;
}
}
m_dataModel - > SetSorting ( sortCol , sortAscending ) ;
m_grid - > SetSortingColumn ( sortCol , sortAscending ) ;
}
bool DIALOG_LIB_FIELDS_TABLE : : TransferDataToWindow ( )
{
switch ( m_scope )
{
case SCOPE : : SCOPE_LIBRARY : m_choiceScope - > SetSelection ( 0 ) ; break ;
case SCOPE : : SCOPE_RELATED_SYMBOLS : m_choiceScope - > SetSelection ( 1 ) ; break ;
}
setScope ( m_scope ) ;
return true ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : loadSymbols ( const wxArrayString & aSymbolNames )
2024-08-30 16:03:06 -07:00
{
// Clear any existing data
m_symbolsList . clear ( ) ;
2025-09-06 00:05:27 +01:00
wxString libName = m_parent - > GetTreeLIBID ( ) . GetLibNickname ( ) ;
if ( aSymbolNames . IsEmpty ( ) )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
if ( m_scope = = SCOPE_RELATED_SYMBOLS )
wxMessageBox ( wxString : : Format ( _ ( " No related symbols found in library '%s'. " ) , libName ) ) ;
else
wxMessageBox ( wxString : : Format ( _ ( " No symbols found in library '%s'. " ) , libName ) ) ;
return ;
}
2024-08-30 16:03:06 -07:00
2025-09-07 13:49:15 +01:00
// Load each symbol from the library manager and add it to our list
for ( const wxString & symbolName : aSymbolNames )
2025-09-06 00:05:27 +01:00
{
2025-09-07 13:49:15 +01:00
LIB_SYMBOL * canvasSymbol = m_parent - > GetCurSymbol ( ) ;
2024-08-30 16:03:06 -07:00
2025-09-07 13:49:15 +01:00
if ( canvasSymbol & & canvasSymbol - > GetLibraryName ( ) = = libName & & canvasSymbol - > GetName ( ) = = symbolName )
{
m_symbolsList . push_back ( canvasSymbol ) ;
}
else
{
2024-08-30 16:03:06 -07:00
try
{
2025-09-07 13:49:15 +01:00
if ( LIB_SYMBOL * symbol = m_parent - > GetLibManager ( ) . GetSymbol ( symbolName , libName ) )
2024-08-30 16:03:06 -07:00
m_symbolsList . push_back ( symbol ) ;
}
catch ( const IO_ERROR & ioe )
{
// Log the error and continue
2025-09-06 00:05:27 +01:00
wxLogWarning ( wxString : : Format ( _ ( " Error loading symbol '%s': %s " ) , symbolName , ioe . What ( ) ) ) ;
2024-08-30 16:03:06 -07:00
}
}
}
if ( m_symbolsList . empty ( ) )
{
2025-09-06 00:05:27 +01:00
if ( m_scope = = SCOPE_RELATED_SYMBOLS )
wxMessageBox ( _ ( " No related symbols could be loaded from the library. " ) ) ;
else
wxMessageBox ( _ ( " No symbols could be loaded from the library. " ) ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
m_dataModel - > SetSymbols ( m_symbolsList ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
bool DIALOG_LIB_FIELDS_TABLE : : TransferDataFromWindow ( )
2024-08-30 16:03:06 -07:00
{
if ( ! m_grid - > CommitPendingChanges ( ) )
return false ;
if ( ! wxDialog : : TransferDataFromWindow ( ) )
return false ;
2025-09-07 13:49:15 +01:00
bool updateCanvas = false ;
2024-08-30 16:03:06 -07:00
m_dataModel - > ApplyData (
2025-09-07 13:49:15 +01:00
[ & ] ( LIB_SYMBOL * symbol )
2024-08-30 16:03:06 -07:00
{
2025-09-07 13:49:15 +01:00
m_parent - > GetLibManager ( ) . UpdateSymbol ( symbol , symbol - > GetLibNickname ( ) ) ;
if ( m_parent - > GetCurSymbol ( ) = = symbol )
updateCanvas = true ;
2024-08-30 16:03:06 -07:00
} ,
[ & ] ( )
{
// Handle newly created derived symbols
auto createdSymbols = m_dataModel - > GetAndClearCreatedDerivedSymbols ( ) ;
wxLogTrace ( traceLibFieldTable , " Post-apply handler: found %zu created derived symbols " ,
createdSymbols . size ( ) ) ;
for ( const auto & [ symbol , libraryName ] : createdSymbols )
{
if ( ! libraryName . IsEmpty ( ) )
{
wxLogTrace ( traceLibFieldTable , " Updating symbol '%s' (UUID: %s) in library '%s' " ,
symbol - > GetName ( ) , symbol - > m_Uuid . AsString ( ) , libraryName ) ;
// Update the symbol in the library manager to properly register it
m_parent - > GetLibManager ( ) . UpdateSymbol ( symbol , libraryName ) ;
}
}
// Sync libraries and refresh tree if there were changes
if ( ! createdSymbols . empty ( ) )
{
wxLogTrace ( traceLibFieldTable , " Syncing libraries due to %zu new symbols " , createdSymbols . size ( ) ) ;
// Store references to the created symbols before sync
std : : vector < LIB_SYMBOL * > symbolsToPreserve ;
for ( const auto & [ symbol , libraryName ] : createdSymbols )
{
symbolsToPreserve . push_back ( symbol ) ;
}
// Synchronize libraries to update the tree view
m_parent - > SyncLibraries ( false ) ;
// Ensure created symbols are still in the symbol list after sync
for ( LIB_SYMBOL * symbol : symbolsToPreserve )
{
bool found = false ;
for ( LIB_SYMBOL * existingSymbol : m_symbolsList )
{
if ( existingSymbol - > m_Uuid = = symbol - > m_Uuid )
{
found = true ;
break ;
}
}
if ( ! found )
{
wxLogTrace ( traceLibFieldTable , " Re-adding symbol '%s' to list after sync " , symbol - > GetName ( ) ) ;
m_symbolsList . push_back ( symbol ) ;
}
}
}
wxLogTrace ( traceLibFieldTable , " Dialog symbol list size after processing: %zu " , m_symbolsList . size ( ) ) ;
} ) ;
ClearModify ( ) ;
wxLogTrace ( traceLibFieldTable , " About to rebuild grid rows to include new symbols " ) ;
RegroupSymbols ( ) ;
wxLogTrace ( traceLibFieldTable , " Grid rebuild completed " ) ;
2025-09-07 13:49:15 +01:00
m_parent - > RefreshLibraryTree ( ) ;
if ( updateCanvas )
{
m_parent - > OnModify ( ) ;
m_parent - > HardRedraw ( ) ;
}
2024-08-30 16:03:06 -07:00
return true ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnAddField ( wxCommandEvent & event )
{
wxTextEntryDialog dlg ( this , _ ( " New field name: " ) , _ ( " Add Field " ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( dlg . ShowModal ( ) ! = wxID_OK )
return ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
wxString fieldName = dlg . GetValue ( ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( fieldName . IsEmpty ( ) )
{
DisplayError ( this , _ ( " Field must have a name. " ) ) ;
return ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
for ( int i = 0 ; i < m_dataModel - > GetNumberCols ( ) ; + + i )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
if ( fieldName = = m_dataModel - > GetColFieldName ( i ) )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
DisplayError ( this , wxString : : Format ( _ ( " Field name '%s' already in use. " ) , fieldName ) ) ;
return ;
2024-08-30 16:03:06 -07:00
}
}
2025-09-06 00:05:27 +01:00
AddField ( fieldName , GetGeneratedFieldDisplayName ( fieldName ) , true , false , true ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
SetupColumnProperties ( m_dataModel - > GetColsCount ( ) - 1 ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
OnModify ( ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnRemoveField ( wxCommandEvent & event )
{
m_viewControlsGrid - > OnDeleteRows (
[ & ] ( int row )
{
return IsOK ( this , wxString : : Format ( _ ( " Are you sure you want to remove the field '%s'? " ) ,
m_viewControlsDataModel - > GetValue ( row , DISPLAY_NAME_COLUMN ) ) ) ;
} ,
[ & ] ( int row )
{
wxString fieldName = m_viewControlsDataModel - > GetCanonicalFieldName ( row ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
RemoveField ( fieldName ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_viewControlsDataModel - > DeleteRow ( row ) ;
OnModify ( ) ;
} ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnRenameField ( wxCommandEvent & event )
{
wxArrayInt selectedRows = m_viewControlsGrid - > GetSelectedRows ( ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( selectedRows . empty ( ) & & m_viewControlsGrid - > GetGridCursorRow ( ) > = 0 )
selectedRows . push_back ( m_viewControlsGrid - > GetGridCursorRow ( ) ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( selectedRows . empty ( ) )
2024-08-30 16:03:06 -07:00
return ;
2025-09-06 00:05:27 +01:00
int row = selectedRows [ 0 ] ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
wxString fieldName = m_viewControlsDataModel - > GetCanonicalFieldName ( row ) ;
2024-08-30 16:03:06 -07:00
int col = m_dataModel - > GetFieldNameCol ( fieldName ) ;
wxCHECK_RET ( col ! = - 1 , wxS ( " Existing field name missing from data model " ) ) ;
wxTextEntryDialog dlg ( this , _ ( " New field name: " ) , _ ( " Rename Field " ) , fieldName ) ;
if ( dlg . ShowModal ( ) ! = wxID_OK )
return ;
wxString newFieldName = dlg . GetValue ( ) ;
if ( newFieldName = = fieldName )
return ;
if ( m_dataModel - > GetFieldNameCol ( newFieldName ) ! = - 1 )
{
DisplayError ( this , wxString : : Format ( _ ( " Field name '%s' already exists. " ) , newFieldName ) ) ;
return ;
}
RenameField ( fieldName , newFieldName ) ;
2025-09-06 00:05:27 +01:00
m_viewControlsDataModel - > SetCanonicalFieldName ( row , newFieldName ) ;
m_viewControlsDataModel - > SetValue ( row , DISPLAY_NAME_COLUMN , newFieldName ) ;
m_viewControlsDataModel - > SetValue ( row , LABEL_COLUMN , GetGeneratedFieldDisplayName ( newFieldName ) ) ;
2024-08-30 16:03:06 -07:00
SetupColumnProperties ( col ) ;
OnModify ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnFilterText ( wxCommandEvent & event )
{
m_dataModel - > SetFilter ( m_filter - > GetValue ( ) ) ;
RegroupSymbols ( ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnFilterMouseMoved ( wxMouseEvent & aEvent )
2024-08-30 16:03:06 -07:00
{
wxPoint pos = aEvent . GetPosition ( ) ;
wxRect ctrlRect = m_filter - > GetScreenRect ( ) ;
int buttonWidth = ctrlRect . GetHeight ( ) ; // Presume buttons are square
// TODO: restore cursor when mouse leaves the filter field (or is it a MSW bug?)
if ( m_filter - > IsSearchButtonVisible ( ) & & pos . x < buttonWidth )
SetCursor ( wxCURSOR_ARROW ) ;
else if ( m_filter - > IsCancelButtonVisible ( ) & & pos . x > ctrlRect . GetWidth ( ) - buttonWidth )
SetCursor ( wxCURSOR_ARROW ) ;
else
SetCursor ( wxCURSOR_IBEAM ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : setScope ( SCOPE aScope )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
LIB_SYMBOL_LIBRARY_MANAGER & libMgr = m_parent - > GetLibManager ( ) ;
wxString targetLib = m_parent - > GetTargetLibId ( ) . GetLibNickname ( ) ;
wxString targetSymbol = m_parent - > GetTargetLibId ( ) . GetLibItemName ( ) ;
wxArrayString symbolNames ;
SetTitle ( wxString : : Format ( _ ( " Library Fields Table (%s) " ) , targetLib ) ) ;
m_scope = aScope ;
if ( m_scope = = SCOPE : : SCOPE_RELATED_SYMBOLS )
{
const LIB_SYMBOL * symbol = libMgr . GetBufferedSymbol ( targetSymbol , targetLib ) ;
LIB_SYMBOL_SPTR root = symbol ? symbol - > GetRootSymbol ( ) : nullptr ;
if ( root )
{
symbolNames . Add ( root - > GetName ( ) ) ;
// Now we have the root symbol, collect all its derived symbols
libMgr . GetDerivedSymbolNames ( root - > GetName ( ) , targetLib , symbolNames ) ;
}
}
else
{
// Get all symbol names from the library manager
libMgr . GetSymbolNames ( targetLib , symbolNames ) ;
}
// Get all symbols from the library
loadSymbols ( symbolNames ) ;
// Update the field list and refresh the grid
UpdateFieldList ( ) ;
SetupAllColumnProperties ( ) ;
2024-08-30 16:03:06 -07:00
RegroupSymbols ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnScope ( wxCommandEvent & aEvent )
{
switch ( aEvent . GetSelection ( ) )
{
case 0 : setScope ( SCOPE : : SCOPE_LIBRARY ) ; break ;
case 1 : setScope ( SCOPE : : SCOPE_RELATED_SYMBOLS ) ; break ;
}
}
void DIALOG_LIB_FIELDS_TABLE : : OnRegroupSymbols ( wxCommandEvent & event )
2024-08-30 16:03:06 -07:00
{
RegroupSymbols ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnTableValueChanged ( wxGridEvent & event )
2024-08-30 16:03:06 -07:00
{
m_grid - > ForceRefresh ( ) ;
OnModify ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnTableCellClick ( wxGridEvent & event )
2024-08-30 16:03:06 -07:00
{
wxString cellValue = m_grid - > GetCellValue ( event . GetRow ( ) , event . GetCol ( ) ) ;
if ( cellValue . StartsWith ( wxS ( " > " ) ) | | cellValue . StartsWith ( wxS ( " v " ) ) )
{
m_grid - > ClearSelection ( ) ;
m_dataModel - > ExpandCollapseRow ( event . GetRow ( ) ) ;
m_grid - > SetGridCursor ( event . GetRow ( ) , event . GetCol ( ) ) ;
// Don't call event.Skip() - we've handled this event
}
else
{
event . Skip ( ) ; // Let normal cell editing proceed
}
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnTableItemContextMenu ( wxGridEvent & event )
{
if ( m_grid )
2024-08-30 16:03:06 -07:00
{
// Get global mouse position and convert to grid client coords
wxPoint mousePos = wxGetMousePosition ( ) ;
wxPoint gridPt = m_grid - > ScreenToClient ( mousePos ) ;
2025-09-06 00:05:27 +01:00
int row = m_grid - > YToRow ( gridPt . y ) ;
int col = m_grid - > XToCol ( gridPt . x ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( row ! = - 1 & & col ! = - 1 )
m_grid - > SetGridCursor ( row , col ) ;
}
2024-08-30 16:03:06 -07:00
event . Skip ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnTableColSize ( wxGridSizeEvent & aEvent )
{
2024-08-30 16:03:06 -07:00
aEvent . Skip ( ) ;
m_grid - > ForceRefresh ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnSizeViewControlsGrid ( wxSizeEvent & event )
{
const wxString & showColLabel = m_viewControlsGrid - > GetColLabelValue ( SHOW_FIELD_COLUMN ) ;
const wxString & groupByColLabel = m_viewControlsGrid - > GetColLabelValue ( GROUP_BY_COLUMN ) ;
int showColWidth = KIUI : : GetTextSize ( showColLabel , m_viewControlsGrid ) . x + COLUMN_MARGIN ;
int groupByColWidth = KIUI : : GetTextSize ( groupByColLabel , m_viewControlsGrid ) . x + COLUMN_MARGIN ;
int remainingWidth = m_viewControlsGrid - > GetSize ( ) . GetX ( ) - showColWidth - groupByColWidth ;
m_viewControlsGrid - > SetColSize ( showColWidth , SHOW_FIELD_COLUMN ) ;
m_viewControlsGrid - > SetColSize ( groupByColWidth , GROUP_BY_COLUMN ) ;
if ( m_viewControlsGrid - > IsColShown ( DISPLAY_NAME_COLUMN ) & & m_viewControlsGrid - > IsColShown ( LABEL_COLUMN ) )
{
m_viewControlsGrid - > SetColSize ( DISPLAY_NAME_COLUMN , std : : max ( remainingWidth / 2 , 60 ) ) ;
m_viewControlsGrid - > SetColSize ( LABEL_COLUMN , std : : max ( remainingWidth - ( remainingWidth / 2 ) , 60 ) ) ;
}
else if ( m_viewControlsGrid - > IsColShown ( DISPLAY_NAME_COLUMN ) )
{
m_viewControlsGrid - > SetColSize ( DISPLAY_NAME_COLUMN , std : : max ( remainingWidth , 60 ) ) ;
}
else if ( m_viewControlsGrid - > IsColShown ( LABEL_COLUMN ) )
{
m_viewControlsGrid - > SetColSize ( LABEL_COLUMN , std : : max ( remainingWidth , 60 ) ) ;
}
event . Skip ( ) ;
}
2025-09-06 19:06:36 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnSidebarToggle ( wxCommandEvent & event )
{
SYMBOL_EDITOR_SETTINGS : : PANEL_LIB_FIELDS_TABLE & cfg = m_parent - > libeditconfig ( ) - > m_LibFieldEditor ;
if ( cfg . sidebar_collapsed )
{
cfg . sidebar_collapsed = false ;
m_splitterMainWindow - > SplitVertically ( m_leftPanel , m_rightPanel , cfg . sash_pos ) ;
m_sidebarButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : left ) ) ;
}
else
{
cfg . sash_pos = m_splitterMainWindow - > GetSashPosition ( ) ;
cfg . sidebar_collapsed = true ;
m_splitterMainWindow - > Unsplit ( m_leftPanel ) ;
m_sidebarButton - > SetBitmap ( KiBitmapBundle ( BITMAPS : : right ) ) ;
}
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnApply ( wxCommandEvent & event )
{
TransferDataFromWindow ( ) ;
}
void DIALOG_LIB_FIELDS_TABLE : : OnCancel ( wxCommandEvent & event )
2024-08-30 16:03:06 -07:00
{
2025-09-03 15:54:29 -07:00
m_grid - > CommitPendingChanges ( true ) ;
if ( m_dataModel - > IsEdited ( ) )
{
if ( ! HandleUnsavedChanges ( this , _ ( " Save changes? " ) ,
[ & ] ( ) - > bool
{
return TransferDataFromWindow ( ) ;
} ) )
return ;
}
2024-08-30 16:03:06 -07:00
EndModal ( wxID_CANCEL ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnOk ( wxCommandEvent & event )
2024-08-30 16:03:06 -07:00
{
if ( TransferDataFromWindow ( ) )
EndModal ( wxID_OK ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnClose ( wxCloseEvent & aEvent )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
m_grid - > CommitPendingChanges ( true ) ;
if ( m_dataModel - > IsEdited ( ) )
{
if ( ! HandleUnsavedChanges ( this , _ ( " Save changes? " ) ,
[ & ] ( ) - > bool
{
return TransferDataFromWindow ( ) ;
} ) )
{
aEvent . Veto ( ) ;
return ;
}
}
aEvent . Skip ( ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : UpdateFieldList ( )
2024-08-30 16:03:06 -07:00
{
auto addMandatoryField =
2025-09-06 00:05:27 +01:00
[ & ] ( FIELD_T fieldId , bool show , bool groupBy )
{
AddField ( GetCanonicalFieldName ( fieldId ) ,
GetDefaultFieldName ( fieldId , DO_TRANSLATE ) , show , groupBy ) ;
} ;
2024-08-30 16:03:06 -07:00
// Add mandatory fields first show groupBy
addMandatoryField ( FIELD_T : : REFERENCE , false , false ) ;
addMandatoryField ( FIELD_T : : VALUE , true , false ) ;
addMandatoryField ( FIELD_T : : FOOTPRINT , true , false ) ;
addMandatoryField ( FIELD_T : : DATASHEET , true , false ) ;
addMandatoryField ( FIELD_T : : DESCRIPTION , false , false ) ;
AddField ( wxS ( " Keywords " ) , _ ( " Keywords " ) , true , false ) ;
// Add attribute fields as checkboxes show groupBy user checkbox
AddField ( wxS ( " ${EXCLUDE_FROM_BOM} " ) , _ ( " Exclude From BOM " ) , true , false , false , true ) ;
AddField ( wxS ( " ${EXCLUDE_FROM_SIM} " ) , _ ( " Exclude From Simulation " ) , true , false , false , true ) ;
AddField ( wxS ( " ${EXCLUDE_FROM_BOARD} " ) , _ ( " Exclude From Board " ) , true , false , false , true ) ;
AddField ( wxS ( " Power " ) , _ ( " Power Symbol " ) , true , false , false , true ) ;
AddField ( wxS ( " LocalPower " ) , _ ( " Local Power Symbol " ) , true , false , false , true ) ;
// User fields next
std : : set < wxString > userFieldNames ;
for ( LIB_SYMBOL * symbol : m_symbolsList )
{
std : : vector < SCH_FIELD * > fields ;
symbol - > GetFields ( fields ) ;
for ( SCH_FIELD * field : fields )
{
if ( ! field - > IsMandatory ( ) & & ! field - > IsPrivate ( ) )
userFieldNames . insert ( field - > GetName ( ) ) ;
}
}
for ( const wxString & fieldName : userFieldNames )
AddField ( fieldName , GetGeneratedFieldDisplayName ( fieldName ) , true , false ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : AddField ( const wxString & aFieldName , const wxString & aLabelValue , bool show ,
bool groupBy , bool addedByUser , bool aIsCheckbox )
2024-08-30 16:03:06 -07:00
{
// Users can add fields with variable names that match the special names in the grid,
// e.g. ${QUANTITY} so make sure we don't add them twice
2025-09-06 00:05:27 +01:00
for ( int row = 0 ; row < m_viewControlsDataModel - > GetNumberRows ( ) ; row + + )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
if ( m_viewControlsDataModel - > GetCanonicalFieldName ( row ) = = aFieldName )
2024-08-30 16:03:06 -07:00
return ;
}
m_dataModel - > AddColumn ( aFieldName , aLabelValue , addedByUser , aIsCheckbox ) ;
wxGridTableMessage msg ( m_dataModel , wxGRIDTABLE_NOTIFY_COLS_APPENDED , 1 ) ;
m_grid - > ProcessTableMessage ( msg ) ;
2025-09-06 00:05:27 +01:00
m_viewControlsGrid - > OnAddRow (
[ & ] ( ) - > std : : pair < int , int >
{
m_viewControlsDataModel - > AppendRow ( aFieldName , aLabelValue , show , groupBy ) ;
return { m_viewControlsDataModel - > GetNumberRows ( ) - 1 , - 1 } ;
} ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : RemoveField ( const wxString & fieldName )
2024-08-30 16:03:06 -07:00
{
int col = m_dataModel - > GetFieldNameCol ( fieldName ) ;
wxCHECK_RET ( col ! = - 1 , wxS ( " Field name not found " ) ) ;
m_dataModel - > RemoveColumn ( col ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : RenameField ( const wxString & oldName , const wxString & newName )
2024-08-30 16:03:06 -07:00
{
int col = m_dataModel - > GetFieldNameCol ( oldName ) ;
wxCHECK_RET ( col ! = - 1 , wxS ( " Existing field name missing from data model " ) ) ;
m_dataModel - > RenameColumn ( col , newName ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : RegroupSymbols ( )
2024-08-30 16:03:06 -07:00
{
m_dataModel - > RebuildRows ( ) ;
m_grid - > ForceRefresh ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnColSort ( wxGridEvent & aEvent )
2024-08-30 16:03:06 -07:00
{
int sortCol = aEvent . GetCol ( ) ;
std : : string key ( m_dataModel - > GetColFieldName ( sortCol ) . ToUTF8 ( ) ) ;
bool ascending ;
// This is bonkers, but wxWidgets doesn't tell us ascending/descending in the event, and
// if we ask it will give us pre-event info.
if ( m_grid - > IsSortingBy ( sortCol ) )
{
// same column; invert ascending
ascending = ! m_grid - > IsSortOrderAscending ( ) ;
}
else
{
// different column; start with ascending
ascending = true ;
}
m_dataModel - > SetSorting ( sortCol , ascending ) ;
RegroupSymbols ( ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnColMove ( wxGridEvent & aEvent )
2024-08-30 16:03:06 -07:00
{
int origPos = aEvent . GetCol ( ) ;
// Save column widths since the setup function uses the saved config values
SYMBOL_EDITOR_SETTINGS * cfg = m_parent - > GetSettings ( ) ;
for ( int i = 0 ; i < m_grid - > GetNumberCols ( ) ; i + + )
{
if ( m_grid - > IsColShown ( i ) )
{
std : : string fieldName ( m_dataModel - > GetColFieldName ( i ) . ToUTF8 ( ) ) ;
cfg - > m_LibFieldEditor . field_widths [ fieldName ] = m_grid - > GetColSize ( i ) ;
}
}
CallAfter (
[ origPos , this ] ( )
{
int newPos = m_grid - > GetColPos ( origPos ) ;
# ifdef __WXMAC__
if ( newPos < origPos )
newPos + = 1 ;
# endif
m_dataModel - > MoveColumn ( origPos , newPos ) ;
// "Unmove" the column since we've moved the column internally
m_grid - > ResetColPos ( ) ;
// We need to reset all the column attr's to the correct column order
SetupAllColumnProperties ( ) ;
m_grid - > ForceRefresh ( ) ;
} ) ;
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : ShowHideColumn ( int aCol , bool aShow )
{
if ( aShow )
m_grid - > ShowCol ( aCol ) ;
else
m_grid - > HideCol ( aCol ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_dataModel - > SetShowColumn ( aCol , aShow ) ;
2024-08-30 16:03:06 -07:00
m_grid - > ForceRefresh ( ) ;
2025-09-06 00:05:27 +01:00
OnModify ( ) ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
void DIALOG_LIB_FIELDS_TABLE : : OnViewControlsCellChanged ( wxGridEvent & aEvent )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
int row = aEvent . GetRow ( ) ;
2025-09-04 06:59:02 -07:00
2025-09-06 00:05:27 +01:00
wxCHECK ( row < m_viewControlsGrid - > GetNumberRows ( ) , /* void */ ) ;
2025-09-04 06:59:02 -07:00
2025-09-06 00:05:27 +01:00
switch ( aEvent . GetCol ( ) )
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
case DISPLAY_NAME_COLUMN :
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
wxString label = m_viewControlsDataModel - > GetValue ( row , DISPLAY_NAME_COLUMN ) ;
wxString fieldName = m_viewControlsDataModel - > GetCanonicalFieldName ( row ) ;
int dataCol = m_dataModel - > GetFieldNameCol ( fieldName ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( dataCol ! = - 1 )
{
m_dataModel - > SetColLabelValue ( dataCol , label ) ;
m_grid - > SetColLabelValue ( dataCol , label ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_grid - > ForceRefresh ( ) ;
OnModify ( ) ;
}
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
break ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
case SHOW_FIELD_COLUMN :
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
wxString fieldName = m_viewControlsDataModel - > GetCanonicalFieldName ( row ) ;
bool value = m_viewControlsDataModel - > GetValueAsBool ( row , SHOW_FIELD_COLUMN ) ;
int dataCol = m_dataModel - > GetFieldNameCol ( fieldName ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
if ( dataCol ! = - 1 )
ShowHideColumn ( dataCol , value ) ;
break ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
case GROUP_BY_COLUMN :
2024-08-30 16:03:06 -07:00
{
2025-09-06 00:05:27 +01:00
wxString fieldName = m_viewControlsDataModel - > GetCanonicalFieldName ( row ) ;
bool value = m_viewControlsDataModel - > GetValueAsBool ( row , GROUP_BY_COLUMN ) ;
int dataCol = m_dataModel - > GetFieldNameCol ( fieldName ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_dataModel - > SetGroupColumn ( dataCol , value ) ;
m_dataModel - > RebuildRows ( ) ;
2024-08-30 16:03:06 -07:00
2025-09-06 00:05:27 +01:00
m_grid - > ForceRefresh ( ) ;
OnModify ( ) ;
break ;
2024-08-30 16:03:06 -07:00
}
2025-09-06 00:05:27 +01:00
default :
break ;
}
2024-08-30 16:03:06 -07:00
}
2025-09-04 06:59:02 -07:00
2025-09-06 00:05:27 +01:00