ADDED: PCB tables.

This commit is contained in:
Jeff Young 2024-01-15 17:29:55 +00:00
parent 48f983826d
commit e445249720
74 changed files with 11148 additions and 4194 deletions

View File

@ -42,6 +42,8 @@
#include <pcb_track.h>
#include <pcb_base_frame.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_shape.h>
#include <pcb_dimension.h>
#include <zone.h>
@ -376,6 +378,9 @@ private:
void addShape( const PCB_TEXTBOX* aTextBox, CONTAINER_2D_BASE* aContainer,
const BOARD_ITEM* aOwner );
void addTable( const PCB_TABLE* aTable, CONTAINER_2D_BASE* aContainer,
const BOARD_ITEM* aOwner );
void addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aDstContainer,
PCB_LAYER_ID aLayerId );

View File

@ -38,6 +38,7 @@
#include <pad.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <board_design_settings.h>
#include <pcb_painter.h> // for PCB_RENDER_SETTINGS
#include <zone.h>
@ -225,12 +226,17 @@ void BOARD_ADAPTER::addFootprintShapes( const FOOTPRINT* aFootprint, CONTAINER_2
PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( item );
if( textbox->GetLayer() == aLayerId )
{
if( textbox->IsBorderEnabled() )
addShape( textbox, aContainer, aFootprint );
addShape( textbox, aContainer, aFootprint );
addText( textbox, aContainer, aFootprint );
}
break;
}
case PCB_TABLE_T:
{
PCB_TABLE* table = static_cast<PCB_TABLE*>( item );
if( table->GetLayer() == aLayerId )
addTable( table, aContainer, aFootprint );
break;
}
@ -726,7 +732,9 @@ void BOARD_ADAPTER::addShape( const PCB_TEXTBOX* aTextBox, CONTAINER_2D_BASE* aC
// So for polygon, we use PCB_SHAPE::TransformShapeToPolygon
if( aTextBox->GetShape() == SHAPE_T::RECTANGLE )
{
addShape( static_cast<const PCB_SHAPE*>( aTextBox ), aContainer, aOwner );
}
else
{
SHAPE_POLY_SET polyList;
@ -739,6 +747,17 @@ void BOARD_ADAPTER::addShape( const PCB_TEXTBOX* aTextBox, CONTAINER_2D_BASE* aC
}
void BOARD_ADAPTER::addTable( const PCB_TABLE* aTable, CONTAINER_2D_BASE* aContainer,
const BOARD_ITEM* aOwner )
{
// JEY TODO: tables
// add borders
for( PCB_TABLECELL* cell : aTable->GetCells() )
addText( cell, aContainer, aOwner );
}
void BOARD_ADAPTER::addSolidAreasShapes( const ZONE* aZone, CONTAINER_2D_BASE* aContainer,
PCB_LAYER_ID aLayerId )
{

View File

@ -40,6 +40,7 @@
#include <pad.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_shape.h>
#include <zone.h>
#include <convert_basic_shapes_to_polygon.h>
@ -622,6 +623,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
break;
case PCB_TABLE_T:
addTable( static_cast<PCB_TABLE*>( item ), layerContainer, item );
break;
case PCB_DIM_ALIGNED_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_RADIAL_T:
@ -686,6 +691,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
break;
}
case PCB_TABLE_T:
// JEY TODO: tables
break;
default:
wxLogTrace( m_logTrace, wxT( "createLayers: item type: %d not implemented" ),
item->Type() );
@ -847,6 +856,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
addShape( static_cast<PCB_TEXTBOX*>( item ), layerContainer, item );
break;
case PCB_TABLE_T:
// JEY TODO: tables
break;
case PCB_DIM_ALIGNED_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_RADIAL_T:
@ -942,6 +955,10 @@ void BOARD_ADAPTER::createLayers( REPORTER* aStatusReporter )
break;
}
case PCB_TABLE_T:
// JEY TODO: tables
break;
default:
break;
}

View File

@ -680,6 +680,8 @@ set( PCB_COMMON_SRCS
${CMAKE_SOURCE_DIR}/pcbnew/pcb_target.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_reference_image.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_field.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_table.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_tablecell.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_text.cpp
${CMAKE_SOURCE_DIR}/pcbnew/pcb_textbox.cpp
${CMAKE_SOURCE_DIR}/pcbnew/project_pcb.cpp

View File

@ -349,6 +349,8 @@ static struct EDA_ITEM_DESC
.Map( PCB_FIELD_T, _HKI( "Text" ) )
.Map( PCB_TEXT_T, _HKI( "Text" ) )
.Map( PCB_TEXTBOX_T, _HKI( "Text Box" ) )
.Map( PCB_TABLE_T, _HKI( "Table" ) )
.Map( PCB_TABLECELL_T, _HKI( "Table Cell" ) )
.Map( PCB_TRACE_T, _HKI( "Track" ) )
.Map( PCB_ARC_T, _HKI( "Track" ) )
.Map( PCB_VIA_T, _HKI( "Via" ) )

View File

@ -2015,8 +2015,8 @@ static struct EDA_SHAPE_DESC
&EDA_SHAPE::SetWidth, &EDA_SHAPE::GetWidth, PROPERTY_DISPLAY::PT_SIZE ) );
void ( EDA_SHAPE::*lineStyleSetter )( LINE_STYLE ) = &EDA_SHAPE::SetLineStyle;
propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>(
_HKI( "Line Style" ), lineStyleSetter, &EDA_SHAPE::GetLineStyle ) );
propMgr.AddProperty( new PROPERTY_ENUM<EDA_SHAPE, LINE_STYLE>( _HKI( "Line Style" ),
lineStyleSetter, &EDA_SHAPE::GetLineStyle ) );
propMgr.AddProperty( new PROPERTY<EDA_SHAPE, COLOR4D>( _HKI( "Line Color" ),
&EDA_SHAPE::SetLineColor, &EDA_SHAPE::GetLineColor ) )

View File

@ -296,6 +296,10 @@ size_t hash_fp_item( const EDA_ITEM* aItem, int aFlags )
}
break;
case PCB_TABLE_T:
// JEY TODO: tables
break;
default:
wxASSERT_MSG( false, "Unhandled type in function hash_fp_item() (exporter_gencad.cpp)" );
}

View File

@ -59,6 +59,7 @@ bottom
bottom_left
bottom_right
castellated_pads
cells
center
chamfer
chamfer_ratio
@ -66,6 +67,9 @@ circle
clearance
clearance_min
color
cols
column_count
column_widths
comment
company
connect
@ -108,6 +112,7 @@ exclude_from_pos_files
exclude_from_bom
extension_height
extension_offset
external
fab_layers_line_width
fab_layers_text_dims
face
@ -158,6 +163,7 @@ hatch_smoothing_level
hatch_smoothing_value
hatch_border_algorithm
hatch_min_hole_area
header
height
hide
hole_to_hole_min
@ -269,9 +275,12 @@ right
rotate
roundrect
roundrect_rratio
rows
row_heights
scale
segment
segment_width
separators
setup
sheetfile
sheetname
@ -286,6 +295,7 @@ solder_paste_margin
solder_paste_margin_ratio
solder_paste_ratio
solid
span
stackup
start
status
@ -293,6 +303,8 @@ stroke
style
suffix
suppress_zeroes
table
table_cell
tags
target
title

View File

@ -298,6 +298,83 @@ TOOL_ACTION ACTIONS::rightJustify( TOOL_ACTION_ARGS()
.Tooltip( _( "Right-justify fields and text items" ) )
.Icon( BITMAPS::text_align_right ) );
TOOL_ACTION ACTIONS::selectColumns( TOOL_ACTION_ARGS()
.Name( "common.InteractiveSelection.SelectColumns" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Column(s)" ) )
.Tooltip( _( "Select complete column(s) containing the current selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::selectRows( TOOL_ACTION_ARGS()
.Name( "common.InteractiveSelection.Rows" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Row(s)" ) )
.Tooltip( _( "Select complete row(s) containing the current selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::selectTable( TOOL_ACTION_ARGS()
.Name( "common.InteractiveSelection.SelectTable" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Table" ) )
.Tooltip( _( "Select parent table of selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::addRowAbove( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.addRowAbove" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Row Above" ) )
.Tooltip( _( "Insert a new table row above the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::addRowBelow( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.addRowBelow" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Row Below" ) )
.Tooltip( _( "Insert a new table row below the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::addColBefore( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.addColBefore" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Column Before" ) )
.Tooltip( _( "Insert a new table column before the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::addColAfter( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.addColAfter" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Column After" ) )
.Tooltip( _( "Insert a new table column after the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::deleteRows( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.deleteRows" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Delete Row(s)" ) )
.Tooltip( _( "Delete rows containing the currently selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::deleteColumns( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.deleteColumns" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Delete Column(s)" ) )
.Tooltip( _( "Delete columns containing the currently selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::mergeCells( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.mergeCells" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Merge Cells" ) )
.Tooltip( _( "Turn selected table cells into a single cell" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::unmergeCells( TOOL_ACTION_ARGS()
.Name( "common.TableEditor.unmergeCell" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Unmerge Cells" ) )
.Tooltip( _( "Turn merged table cells back into separate cells." ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION ACTIONS::activatePointEditor( TOOL_ACTION_ARGS()
.Name( "common.Control.activatePointEditor" )
.Scope( AS_GLOBAL ) );
@ -765,7 +842,7 @@ TOOL_ACTION ACTIONS::updatePreferences( TOOL_ACTION_ARGS()
.Name( "common.Control.updatePreferences" )
.Scope( AS_GLOBAL ) );
TOOL_ACTION ACTIONS::selectColumns( TOOL_ACTION_ARGS()
TOOL_ACTION ACTIONS::selectLibTreeColumns( TOOL_ACTION_ARGS()
.Name( "common.Control.selectColumns" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Columns..." ) ) );

View File

@ -931,7 +931,7 @@ void LIB_TREE::onHeaderContextMenu( wxDataViewEvent& aEvent )
ACTION_MENU menu( true, nullptr );
menu.Add( ACTIONS::selectColumns );
menu.Add( ACTIONS::selectLibTreeColumns );
if( GetPopupMenuSelectionFromUser( menu ) != wxID_NONE )
{

View File

@ -393,7 +393,19 @@ bool DIALOG_TABLECELL_PROPERTIES::TransferDataFromWindow()
m_table->SetSeparatorsStroke( stroke );
}
m_cell->SetText( m_textCtrl->GetValue() );
wxString txt = m_textCtrl->GetValue();
#ifdef __WXMAC__
// On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting.
// Replace it now.
txt.Replace( "\r", "\n" );
#elif defined( __WINDOWS__ )
// On Windows, a new line is coded as \r\n. We use only \n in kicad files and in
// drawing routines so strip the \r char.
txt.Replace( "\r", "" );
#endif
m_cell->SetText( txt );
if( m_fontCtrl->HaveFontSelection() )
{

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
@ -76,7 +76,7 @@ DIALOG_TABLECELL_PROPERTIES_BASE::DIALOG_TABLECELL_PROPERTIES_BASE( wxWindow* pa
wxBoxSizer* bSizer16;
bSizer16 = new wxBoxSizer( wxVERTICAL );
m_textEntrySizer = new wxGridBagSizer( 4, 3 );
m_textEntrySizer = new wxGridBagSizer( 3, 3 );
m_textEntrySizer->SetFlexibleDirection( wxBOTH );
m_textEntrySizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_textEntrySizer->SetEmptyCellSize( wxSize( 0,2 ) );

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 3.10.1-0-g8feb16b)
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!

View File

@ -4226,6 +4226,10 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseSchTextBoxContent( SCH_TEXTBOX* aTextBox )
cell->SetColSpan( parseInt( "column span" ) );
cell->SetRowSpan( parseInt( "row span" ) );
}
else
{
Expecting( "at, size, stroke, fill, effects or uuid" );
}
NeedRIGHT();
break;
@ -4252,7 +4256,10 @@ void SCH_IO_KICAD_SEXPR_PARSER::parseSchTextBoxContent( SCH_TEXTBOX* aTextBox )
break;
default:
Expecting( "at, size, stroke, fill, effects or uuid" );
if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( aTextBox ) )
Expecting( "at, size, stroke, fill, effects, span or uuid" );
else
Expecting( "at, size, stroke, fill, effects or uuid" );
}
}

View File

@ -301,7 +301,7 @@ public:
* This is a pure virtual function. Derived classes must implement this.
*/
virtual double Similarity( const SCH_ITEM& aItem ) const = 0;
virtual bool operator==( const SCH_ITEM& aItem ) const = 0;
virtual bool operator==( const SCH_ITEM& aOtherItem ) const = 0;
/**
* Print a schematic item.

View File

@ -2418,11 +2418,8 @@ void SCH_PAINTER::draw( const SCH_TEXTBOX* aTextBox, int aLayer )
void SCH_PAINTER::draw( const SCH_TABLE* aTable, int aLayer )
{
const_cast<SCH_TABLE*>( aTable )->RunOnChildren(
[&]( SCH_ITEM* aChild )
{
draw( static_cast<SCH_TEXTBOX*>( aChild ), aLayer );
} );
for( SCH_TABLECELL* cell : aTable->GetCells() )
draw( static_cast<SCH_TEXTBOX*>( cell ), aLayer );
if( aLayer == LAYER_SELECTION_SHADOWS )
return;

View File

@ -66,10 +66,7 @@ SCH_TABLE::SCH_TABLE( const SCH_TABLE& aTable ) :
m_rowHeights = aTable.m_rowHeights;
for( SCH_TABLECELL* src : aTable.m_cells )
m_cells.push_back( new SCH_TABLECELL( *src ) );
for( SCH_TABLECELL* dest : m_cells )
dest->SetParent( this );
AddCell( new SCH_TABLECELL( *src ) );
}
@ -139,6 +136,8 @@ VECTOR2I SCH_TABLE::GetEnd() const
void SCH_TABLE::Normalize()
{
// JEY TODO: pukes on rotated tables...
int y = GetPosition().y;
for( int row = 0; row < GetRowCount(); ++row )
@ -151,20 +150,29 @@ void SCH_TABLE::Normalize()
int colWidth = m_colWidths[ col ];
SCH_TABLECELL* cell = GetCell( row, col );
cell->SetPosition( VECTOR2I( x, y ) );
cell->SetEnd( cell->GetStart() + VECTOR2I( colWidth, rowHeight ) );
VECTOR2I pos( x, y );
if( cell->GetPosition() != pos )
{
cell->SetPosition( pos );
cell->ClearRenderCache();
}
VECTOR2I end = cell->GetStart() + VECTOR2I( colWidth, rowHeight );
if( cell->GetColSpan() > 1 || cell->GetRowSpan() > 1 )
{
VECTOR2I extraSize;
for( int ii = col + 1; ii < col + cell->GetColSpan(); ++ii )
extraSize.x += m_colWidths[ii];
end.x += m_colWidths[ii];
for( int ii = row + 1; ii < row + cell->GetRowSpan(); ++ii )
extraSize.y += m_rowHeights[ii];
end.y += m_rowHeights[ii];
}
cell->SetEnd( cell->GetEnd() + extraSize );
if( cell->GetEnd() != end )
{
cell->SetEnd( end );
cell->ClearRenderCache();
}
x += colWidth;
@ -198,6 +206,8 @@ void SCH_TABLE::Rotate( const VECTOR2I& aCenter )
{
for( SCH_TABLECELL* cell : m_cells )
cell->Rotate( aCenter );
Normalize();
}
@ -599,36 +609,48 @@ static struct SCH_TABLE_DESC
&EDA_SHAPE::SetStartY, &EDA_SHAPE::GetStartY, PROPERTY_DISPLAY::PT_COORD,
ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
const wxString tableProps = _( "Table Properties" );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, bool>( _HKI( "External Border" ),
&SCH_TABLE::SetStrokeExternal, &SCH_TABLE::StrokeExternal ) );
&SCH_TABLE::SetStrokeExternal, &SCH_TABLE::StrokeExternal ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, bool>( _HKI( "Header Border" ),
&SCH_TABLE::SetStrokeHeader, &SCH_TABLE::StrokeHeader ) );
&SCH_TABLE::SetStrokeHeader, &SCH_TABLE::StrokeHeader ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, int>( _HKI( "Border Width" ),
&SCH_TABLE::SetBorderWidth, &SCH_TABLE::GetBorderWidth,
PROPERTY_DISPLAY::PT_SIZE ) );
PROPERTY_DISPLAY::PT_SIZE ),
tableProps );
propMgr.AddProperty( new PROPERTY_ENUM<SCH_TABLE, LINE_STYLE>( _HKI( "Border Style" ),
&SCH_TABLE::SetBorderStyle, &SCH_TABLE::GetBorderStyle ) );
&SCH_TABLE::SetBorderStyle, &SCH_TABLE::GetBorderStyle ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, COLOR4D>( _HKI( "Border Color" ),
&SCH_TABLE::SetBorderColor, &SCH_TABLE::GetBorderColor ) );
&SCH_TABLE::SetBorderColor, &SCH_TABLE::GetBorderColor ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, bool>( _HKI( "Row Separators" ),
&SCH_TABLE::SetStrokeRows, &SCH_TABLE::StrokeRows ) );
&SCH_TABLE::SetStrokeRows, &SCH_TABLE::StrokeRows ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, bool>( _HKI( "Cell Separators" ),
&SCH_TABLE::SetStrokeColumns, &SCH_TABLE::StrokeColumns ) );
&SCH_TABLE::SetStrokeColumns, &SCH_TABLE::StrokeColumns ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, int>( _HKI( "Separators Width" ),
&SCH_TABLE::SetSeparatorsWidth, &SCH_TABLE::GetSeparatorsWidth,
PROPERTY_DISPLAY::PT_SIZE ) );
PROPERTY_DISPLAY::PT_SIZE ),
tableProps );
propMgr.AddProperty( new PROPERTY_ENUM<SCH_TABLE, LINE_STYLE>( _HKI( "Separators Style" ),
&SCH_TABLE::SetSeparatorsStyle, &SCH_TABLE::GetSeparatorsStyle ) );
&SCH_TABLE::SetSeparatorsStyle, &SCH_TABLE::GetSeparatorsStyle ),
tableProps );
propMgr.AddProperty( new PROPERTY<SCH_TABLE, COLOR4D>( _HKI( "Separators Color" ),
&SCH_TABLE::SetSeparatorsColor, &SCH_TABLE::GetSeparatorsColor ) );
&SCH_TABLE::SetSeparatorsColor, &SCH_TABLE::GetSeparatorsColor ),
tableProps );
}
} _SCH_TABLE_DESC;

View File

@ -135,6 +135,41 @@ void SCH_TABLECELL::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PAN
}
double SCH_TABLECELL::Similarity( const SCH_ITEM& aOtherItem ) const
{
if( aOtherItem.Type() != Type() )
return 0.0;
const SCH_TABLECELL& other = static_cast<const SCH_TABLECELL&>( aOtherItem );
double similarity = 1.0;
if( m_colSpan != other.m_colSpan )
similarity *= 0.9;
if( m_rowSpan != other.m_rowSpan )
similarity *= 0.9;
similarity *= SCH_TEXTBOX::Similarity( other );
return similarity;
}
bool SCH_TABLECELL::operator==( const SCH_ITEM& aOtherItem ) const
{
if( aOtherItem.Type() != Type() )
return false;
const SCH_TABLECELL& other = static_cast<const SCH_TABLECELL&>( aOtherItem );
return m_colSpan == other.m_colSpan
&& m_rowSpan == other.m_rowSpan
&& SCH_TEXTBOX::operator==( other );
}
static struct SCH_TABLECELL_DESC
{
SCH_TABLECELL_DESC()

View File

@ -71,6 +71,10 @@ public:
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
double Similarity( const SCH_ITEM& aOther ) const override;
bool operator==( const SCH_ITEM& aOther ) const override;
protected:
int m_colSpan;
int m_rowSpan;

View File

@ -145,83 +145,6 @@ TOOL_ACTION EE_ACTIONS::syncSelection( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveSelection.SyncSelection" )
.Scope( AS_GLOBAL ) );
TOOL_ACTION EE_ACTIONS::selectColumns( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveSelection.SelectColumns" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Column(s)" ) )
.Tooltip( _( "Select complete column(s) containing the current selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::selectRows( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveSelection.Rows" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Row(s)" ) )
.Tooltip( _( "Select complete row(s) containing the current selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::selectTable( TOOL_ACTION_ARGS()
.Name( "eeschema.InteractiveSelection.SelectTable" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Select Table" ) )
.Tooltip( _( "Select parent table of selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::addRowAbove( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.addRowAbove" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Row Above" ) )
.Tooltip( _( "Insert a new table row above the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::addRowBelow( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.addRowBelow" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Row Below" ) )
.Tooltip( _( "Insert a new table row below the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::addColumnBefore( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.addColumnBefore" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Column Before" ) )
.Tooltip( _( "Insert a new table column before the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::addColumnAfter( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.addColumnAfter" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Column After" ) )
.Tooltip( _( "Insert a new table column after the selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::deleteRows( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.deleteRows" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Delete Row(s)" ) )
.Tooltip( _( "Delete rows containing the currently selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::deleteColumns( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.deleteColumns" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Delete Column(s)" ) )
.Tooltip( _( "Delete columns containing the currently selected cell(s)" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::mergeCells( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.mergeCells" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Merge Cells" ) )
.Tooltip( _( "Turn selected table cells into a single cell" ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
TOOL_ACTION EE_ACTIONS::unmergeCells( TOOL_ACTION_ARGS()
.Name( "eeschema.TableEditor.unmergeCell" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Unmerge Cells" ) )
.Tooltip( _( "Turn merged table cells back into separate cells." ) )
.Icon( BITMAPS::spreadsheet ) ); // JEY TODO: need icon
// SYMBOL_EDITOR_CONTROL
//
TOOL_ACTION EE_ACTIONS::saveLibraryAs( TOOL_ACTION_ARGS()

View File

@ -69,19 +69,6 @@ public:
/// Selection synchronization (PCB -> SCH)
static TOOL_ACTION syncSelection;
// Tables
static TOOL_ACTION selectRows;
static TOOL_ACTION selectColumns;
static TOOL_ACTION selectTable;
static TOOL_ACTION addRowAbove;
static TOOL_ACTION addRowBelow;
static TOOL_ACTION addColumnBefore;
static TOOL_ACTION addColumnAfter;
static TOOL_ACTION deleteRows;
static TOOL_ACTION deleteColumns;
static TOOL_ACTION mergeCells;
static TOOL_ACTION unmergeCells;
// Locking
static TOOL_ACTION toggleLock;
static TOOL_ACTION lock;

View File

@ -280,9 +280,9 @@ bool EE_SELECTION_TOOL::Init()
menu.AddItem( EE_ACTIONS::clearHighlight, haveHighlight && EE_CONDITIONS::Idle, 1 );
menu.AddSeparator( haveHighlight && EE_CONDITIONS::Idle, 1 );
menu.AddItem( EE_ACTIONS::selectColumns, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddItem( EE_ACTIONS::selectRows, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddItem( EE_ACTIONS::selectTable, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddItem( ACTIONS::selectColumns, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddItem( ACTIONS::selectRows, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddItem( ACTIONS::selectTable, tableCellSelection && EE_CONDITIONS::Idle, 2 );
menu.AddSeparator( 100 );
menu.AddItem( EE_ACTIONS::drawWire, schEditCondition && EE_CONDITIONS::Empty, 100 );
@ -2545,9 +2545,9 @@ void EE_SELECTION_TOOL::setTransitions()
Go( &EE_SELECTION_TOOL::Main, EE_ACTIONS::selectionActivate.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectNode, EE_ACTIONS::selectNode.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectConnection, EE_ACTIONS::selectConnection.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectColumns, EE_ACTIONS::selectColumns.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectRows, EE_ACTIONS::selectRows.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectTable, EE_ACTIONS::selectTable.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectColumns, ACTIONS::selectColumns.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectRows, ACTIONS::selectRows.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectTable, ACTIONS::selectTable.MakeEvent() );
Go( &EE_SELECTION_TOOL::ClearSelection, EE_ACTIONS::clearSelection.MakeEvent() );

View File

@ -2165,13 +2165,14 @@ int SCH_DRAWING_TOOLS::DrawTable( const TOOL_EVENT& aEvent )
else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
{
VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
int fontSize = schematic->Settings().m_DefaultTextSize;
VECTOR2I origin( table->GetPosition() );
VECTOR2I requestedSize( cursorPos - origin );
int colCount = std::max( 1, requestedSize.x / ( gridSize.x * 24 ) );
int rowCount = std::max( 1, requestedSize.y / ( gridSize.y * 8 ) );
int colCount = std::max( 1, requestedSize.x / ( fontSize * 15 ) );
int rowCount = std::max( 1, requestedSize.y / ( fontSize * 2 ) );
VECTOR2I cellSize( std::max( gridSize.x * 4, requestedSize.x / colCount ),
VECTOR2I cellSize( std::max( gridSize.x * 5, requestedSize.x / colCount ),
std::max( gridSize.y * 2, requestedSize.y / rowCount ) );
cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;

View File

@ -21,16 +21,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <kiway.h>
#include <tools/sch_edit_tool.h>
#include <tools/ee_selection_tool.h>
#include <ee_actions.h>
#include <string_utils.h>
#include <sch_table.h>
#include <sch_tablecell.h>
#include <sch_commit.h>
#include <pgm_base.h>
#include <core/kicad_algo.h>
#include <tools/sch_edit_table_tool.h>
@ -44,78 +35,13 @@ bool SCH_EDIT_TABLE_TOOL::Init()
{
EE_TOOL_BASE::Init();
auto tableCellSelection = EE_CONDITIONS::MoreThan( 0 )
&& EE_CONDITIONS::OnlyTypes( { SCH_TABLECELL_T } );
auto tableCellBlockSelection =
[&]( const SELECTION& sel )
{
if( sel.CountType( SCH_TABLECELL_T ) < 2 )
return false;
int colMin = std::numeric_limits<int>::max();
int colMax = 0;
int rowMin = std::numeric_limits<int>::max();
int rowMax = 0;
int selectedArea = 0;
for( EDA_ITEM* item : sel )
{
wxCHECK2( item->Type() == SCH_TABLECELL_T, continue );
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
colMin = std::min( colMin, cell->GetColumn() );
colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
rowMin = std::min( rowMin, cell->GetRow() );
rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
selectedArea += cell->GetColSpan() * cell->GetRowSpan();
}
return selectedArea == ( colMax - colMin ) * ( rowMax - rowMin );
};
auto mergedCellsSelection =
[&]( const SELECTION& sel )
{
for( EDA_ITEM* item : sel )
{
if( SCH_TABLECELL* cell = dynamic_cast<SCH_TABLECELL*>( item ) )
{
if( cell->GetColSpan() > 1 || cell->GetRowSpan() > 1 )
return true;
}
}
return false;
};
//
// Add editing actions to the selection tool menu
//
CONDITIONAL_MENU& selToolMenu = m_selectionTool->GetToolMenu().GetMenu();
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( EE_ACTIONS::addRowAbove, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( EE_ACTIONS::addRowBelow, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( EE_ACTIONS::addColumnBefore, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( EE_ACTIONS::addColumnAfter, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( EE_ACTIONS::deleteRows, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( EE_ACTIONS::deleteColumns, tableCellSelection && EE_CONDITIONS::Idle, 100 );
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( EE_ACTIONS::mergeCells, tableCellSelection && tableCellBlockSelection, 100 );
selToolMenu.AddItem( EE_ACTIONS::unmergeCells, tableCellSelection && mergedCellsSelection, 100 );
selToolMenu.AddSeparator( 100 );
addMenus( m_selectionTool->GetToolMenu().GetMenu() );
return true;
}
SCH_TABLECELL* copyCell( SCH_TABLECELL* aSource )
SCH_TABLECELL* SCH_EDIT_TABLE_TOOL::copyCell( SCH_TABLECELL* aSource )
{
SCH_TABLECELL* cell = new SCH_TABLECELL();
@ -127,415 +53,23 @@ SCH_TABLECELL* copyCell( SCH_TABLECELL* aSource )
}
int SCH_EDIT_TABLE_TOOL::AddRowAbove( const TOOL_EVENT& aEvent )
const SELECTION& SCH_EDIT_TABLE_TOOL::getTableCellSelection()
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
SCH_TABLECELL* topmost = nullptr;
for( EDA_ITEM* item : selection )
{
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
if( !topmost || cell->GetRow() < topmost->GetRow() )
topmost = cell;
}
if( !topmost )
return 0;
int row = topmost->GetRow();
SCH_TABLE* table = static_cast<SCH_TABLE*>( topmost->GetParent() );
SCH_COMMIT commit( m_toolMgr );
// Make a copy of the source row before things start moving around
std::vector<SCH_TABLECELL*> sources;
sources.reserve( table->GetColCount() );
for( int col = 0; col < table->GetColCount(); ++col )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, m_frame->GetScreen() );
for( int col = 0; col < table->GetColCount(); ++col )
{
SCH_TABLECELL* cell = copyCell( sources[col] );
table->InsertCell( row * table->GetColCount(), cell );
}
table->Normalize();
commit.Push( _( "Add Row Above" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::AddRowBelow( const TOOL_EVENT& aEvent )
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
SCH_TABLECELL* bottommost = nullptr;
if( selection.Empty() )
return 0;
for( EDA_ITEM* item : selection )
{
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
if( !bottommost || cell->GetRow() > bottommost->GetRow() )
bottommost = cell;
}
if( !bottommost )
return 0;
int row = bottommost->GetRow();
SCH_TABLE* table = static_cast<SCH_TABLE*>( bottommost->GetParent() );
SCH_COMMIT commit( m_toolMgr );
// Make a copy of the source row before things start moving around
std::vector<SCH_TABLECELL*> sources;
sources.reserve( table->GetColCount() );
for( int col = 0; col < table->GetColCount(); ++col )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, m_frame->GetScreen() );
for( int col = 0; col < table->GetColCount(); ++col )
{
SCH_TABLECELL* cell = copyCell( sources[col] );
table->InsertCell( ( row + 1 ) * table->GetColCount(), cell );
}
table->Normalize();
commit.Push( _( "Add Row Below" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::AddColumnBefore( const TOOL_EVENT& aEvent )
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
SCH_TABLECELL* leftmost = nullptr;
for( EDA_ITEM* item : selection )
{
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
if( !leftmost || cell->GetColumn() < leftmost->GetColumn() )
leftmost = cell;
}
if( !leftmost )
return 0;
int col = leftmost->GetColumn();
SCH_TABLE* table = static_cast<SCH_TABLE*>( leftmost->GetParent() );
int rowCount = table->GetRowCount();
SCH_COMMIT commit( m_toolMgr );
// Make a copy of the source column before things start moving around
std::vector<SCH_TABLECELL*> sources;
sources.reserve( rowCount );
for( int row = 0; row < rowCount; ++row )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, m_frame->GetScreen() );
table->SetColCount( table->GetColCount() + 1 );
for( int row = 0; row < rowCount; ++row )
{
SCH_TABLECELL* cell = copyCell( sources[row] );
table->InsertCell( row * table->GetColCount() + col, cell );
}
table->Normalize();
commit.Push( _( "Add Column Before" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::AddColumnAfter( const TOOL_EVENT& aEvent )
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
SCH_TABLECELL* rightmost = nullptr;
for( EDA_ITEM* item : selection )
{
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
if( !rightmost || cell->GetColumn() > rightmost->GetColumn() )
rightmost = cell;
}
if( !rightmost )
return 0;
int col = rightmost->GetColumn();
SCH_TABLE* table = static_cast<SCH_TABLE*>( rightmost->GetParent() );
int rowCount = table->GetRowCount();
SCH_COMMIT commit( m_toolMgr );
// Make a copy of the source column before things start moving around
std::vector<SCH_TABLECELL*> sources;
sources.reserve( rowCount );
for( int row = 0; row < rowCount; ++row )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, m_frame->GetScreen() );
table->SetColCount( table->GetColCount() + 1 );
for( int row = 0; row < rowCount; ++row )
{
SCH_TABLECELL* cell = copyCell( sources[row] );
table->InsertCell( row * table->GetColCount() + col + 1, cell );
}
table->Normalize();
commit.Push( _( "Add Column After" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::DeleteColumns( const TOOL_EVENT& aEvent )
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
if( selection.Empty() )
return 0;
SCH_TABLE* table = static_cast<SCH_TABLE*>( selection[0]->GetParent() );
int deleted = 0;
for( int col = 0; col < table->GetColCount(); ++col )
{
bool deleteColumn = false;
for( int row = 0; row < table->GetRowCount(); ++row )
{
if( table->GetCell( row, col )->IsSelected() )
{
deleteColumn = true;
break;
}
}
if( deleteColumn )
{
for( int row = 0; row < table->GetRowCount(); ++row )
table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
deleted++;
}
}
SCH_COMMIT commit( m_toolMgr );
if( deleted == table->GetColCount() )
{
commit.Remove( table, m_frame->GetScreen() );
}
else
{
commit.Modify( table, m_frame->GetScreen() );
table->DeleteMarkedCells();
table->SetColCount( table->GetColCount() - deleted );
table->Normalize();
}
if( deleted > 1 )
commit.Push( _( "Delete Columns" ) );
else
commit.Push( _( "Delete Column" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::DeleteRows( const TOOL_EVENT& aEvent )
{
EE_SELECTION& selection = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
if( selection.Empty() )
return 0;
SCH_TABLE* table = static_cast<SCH_TABLE*>( selection[0]->GetParent() );
int deleted = 0;
for( int row = 0; row < table->GetRowCount(); ++row )
{
bool deleteRow = false;
for( int col = 0; col < table->GetColCount(); ++col )
{
if( table->GetCell( row, col )->IsSelected() )
{
deleteRow = true;
break;
}
}
if( deleteRow )
{
for( int col = 0; col < table->GetColCount(); ++col )
table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
deleted++;
}
}
SCH_COMMIT commit( m_toolMgr );
if( deleted == table->GetRowCount() )
{
commit.Remove( table, m_frame->GetScreen() );
}
else
{
commit.Modify( table, m_frame->GetScreen() );
table->DeleteMarkedCells();
table->Normalize();
}
if( deleted > 1 )
commit.Push( _( "Delete Rows" ) );
else
commit.Push( _( "Delete Row" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::MergeCells( const TOOL_EVENT& aEvent )
{
EE_SELECTION& sel = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
if( sel.Empty() )
return 0;
int colMin = std::numeric_limits<int>::max();
int colMax = 0;
int rowMin = std::numeric_limits<int>::max();
int rowMax = 0;
SCH_COMMIT commit( m_toolMgr );
SCH_TABLE* table = static_cast<SCH_TABLE*>( sel[0]->GetParent() );
for( EDA_ITEM* item : sel )
{
wxCHECK2( item->Type() == SCH_TABLECELL_T, continue );
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
colMin = std::min( colMin, cell->GetColumn() );
colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
rowMin = std::min( rowMin, cell->GetRow() );
rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
}
wxString content;
VECTOR2I extents;
for( int row = rowMin; row < rowMax; ++row )
{
extents.y += table->GetRowHeight( row );
extents.x = 0;
for( int col = colMin; col < colMax; ++col )
{
extents.x += table->GetColWidth( col );
SCH_TABLECELL* cell = table->GetCell( row, col );
if( !cell->GetText().IsEmpty() )
{
if( !content.IsEmpty() )
content += "\n";
content += cell->GetText();
}
commit.Modify( cell, m_frame->GetScreen() );
cell->SetColSpan( 0 );
cell->SetRowSpan( 0 );
cell->SetText( wxEmptyString );
}
}
SCH_TABLECELL* topLeft = table->GetCell( rowMin, colMin );
topLeft->SetColSpan( colMax - colMin );
topLeft->SetRowSpan( rowMax - rowMin );
topLeft->SetText( content );
topLeft->SetEnd( topLeft->GetStart() + extents );
table->Normalize();
commit.Push( _( "Merge Cells" ) );
return 0;
}
int SCH_EDIT_TABLE_TOOL::UnmergeCells( const TOOL_EVENT& aEvent )
{
EE_SELECTION& sel = m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
if( sel.Empty() )
return 0;
SCH_COMMIT commit( m_toolMgr );
SCH_TABLE* table = static_cast<SCH_TABLE*>( sel[0]->GetParent() );
for( EDA_ITEM* item : sel )
{
wxCHECK2( item->Type() == SCH_TABLECELL_T, continue );
SCH_TABLECELL* cell = static_cast<SCH_TABLECELL*>( item );
int rowSpan = cell->GetRowSpan();
int colSpan = cell->GetColSpan();
for( int row = cell->GetRow(); row < cell->GetRow() + rowSpan; ++row )
{
for( int col = cell->GetColumn(); col < cell->GetColumn() + colSpan; ++col )
{
SCH_TABLECELL* target = table->GetCell( row, col );
commit.Modify( target, m_frame->GetScreen() );
target->SetColSpan( 1 );
target->SetRowSpan( 1 );
VECTOR2I extents( table->GetColWidth( col ), table->GetRowHeight( row ) );
target->SetEnd( target->GetStart() + extents );
}
}
}
table->Normalize();
commit.Push( _( "Unmerge Cells" ) );
return 0;
return m_selectionTool->RequestSelection( { SCH_TABLECELL_T } );
}
void SCH_EDIT_TABLE_TOOL::setTransitions()
{
Go( &SCH_EDIT_TABLE_TOOL::AddRowAbove, EE_ACTIONS::addRowAbove.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddRowBelow, EE_ACTIONS::addRowBelow.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddRowAbove, ACTIONS::addRowAbove.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddRowBelow, ACTIONS::addRowBelow.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddColumnBefore, EE_ACTIONS::addColumnBefore.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddColumnAfter, EE_ACTIONS::addColumnAfter.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddColumnBefore, ACTIONS::addColBefore.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::AddColumnAfter, ACTIONS::addColAfter.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::DeleteRows, EE_ACTIONS::deleteRows.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::DeleteColumns, EE_ACTIONS::deleteColumns.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::DeleteRows, ACTIONS::deleteRows.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::DeleteColumns, ACTIONS::deleteColumns.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::MergeCells, EE_ACTIONS::mergeCells.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::UnmergeCells, EE_ACTIONS::unmergeCells.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::MergeCells, ACTIONS::mergeCells.MakeEvent() );
Go( &SCH_EDIT_TABLE_TOOL::UnmergeCells, ACTIONS::unmergeCells.MakeEvent() );
}

View File

@ -21,16 +21,21 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef SCH_TABLE_TOOL_H
#define SCH_TABLE_TOOL_H
#ifndef SCH_EDIT_TABLE_TOOL_H
#define SCH_EDIT_TABLE_TOOL_H
#include <tools/ee_tool_base.h>
#include <tool/edit_table_tool_base.h>
#include <sch_table.h>
#include <sch_tablecell.h>
#include <sch_commit.h>
class SCH_EDIT_FRAME;
class SCH_EDIT_TABLE_TOOL : public EE_TOOL_BASE<SCH_EDIT_FRAME>
class SCH_EDIT_TABLE_TOOL : public EE_TOOL_BASE<SCH_EDIT_FRAME>,
public EDIT_TABLE_TOOL_BASE<SCH_TABLE, SCH_TABLECELL, SCH_COMMIT>
{
public:
SCH_EDIT_TABLE_TOOL();
@ -39,19 +44,28 @@ public:
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
int AddRowAbove( const TOOL_EVENT& aEvent );
int AddRowBelow( const TOOL_EVENT& aEvent );
int AddColumnBefore( const TOOL_EVENT& aEvent );
int AddColumnAfter( const TOOL_EVENT& aEvent );
int DeleteRows( const TOOL_EVENT& aEvent );
int DeleteColumns( const TOOL_EVENT& aEvent );
int AddRowAbove( const TOOL_EVENT& aEvent ) { return doAddRowAbove( aEvent ); }
int AddRowBelow( const TOOL_EVENT& aEvent ) { return doAddRowBelow( aEvent ); }
int AddColumnBefore( const TOOL_EVENT& aEvent ) { return doAddColumnBefore( aEvent ); }
int AddColumnAfter( const TOOL_EVENT& aEvent ) { return doAddColumnAfter( aEvent ); }
int DeleteRows( const TOOL_EVENT& aEvent ) { return doDeleteRows( aEvent ); }
int DeleteColumns( const TOOL_EVENT& aEvent ) { return doDeleteColumns( aEvent ); }
int MergeCells( const TOOL_EVENT& aEvent );
int UnmergeCells( const TOOL_EVENT& aEvent );
int MergeCells( const TOOL_EVENT& aEvent ) { return doMergeCells( aEvent ); }
int UnmergeCells( const TOOL_EVENT& aEvent ) { return doUnmergeCells( aEvent ); }
private:
///< Set up handlers for various events.
void setTransitions() override;
private:
TOOL_MANAGER* getToolMgr() override { return m_toolMgr; }
BASE_SCREEN* getScreen() override { return m_frame->GetScreen(); }
const SELECTION& getTableCellSelection() override;
void clearSelection() override { m_toolMgr->RunAction( EE_ACTIONS::clearSelection ); };
SCH_TABLECELL* copyCell( SCH_TABLECELL* aSource ) override;
};
#endif //SCH_TABLE_TOOL_H
#endif //SCH_EDIT_TABLE_TOOL_H

View File

@ -1427,11 +1427,13 @@ int SCH_EDIT_TOOL::DoDelete( const TOOL_EVENT& aEvent )
}
else if( sch_item->Type() == SCH_FIELD_T )
{
// Hide field
commit.Modify( item, m_frame->GetScreen() );
static_cast<SCH_FIELD*>( sch_item )->SetVisible( false );
}
else if( sch_item->Type() == SCH_TABLECELL_T )
{
// Clear contents of table cell
commit.Modify( item, m_frame->GetScreen() );
static_cast<SCH_TABLECELL*>( sch_item )->SetText( wxEmptyString );
}

View File

@ -91,6 +91,8 @@ enum KICAD_T
PCB_GENERATOR_T, ///< class PCB_GENERATOR, generator on a layer
PCB_TEXT_T, ///< class PCB_TEXT, text on a layer
PCB_TEXTBOX_T, ///< class PCB_TEXTBOX, wrapped text on a layer
PCB_TABLE_T, ///< class PCB_TABLE, table of PCB_TABLECELLs
PCB_TABLECELL_T, ///< class PCB_TABLECELL, PCB_TEXTBOX for use in tables
PCB_TRACE_T, ///< class PCB_TRACK, a track segment (segment on a copper layer)
PCB_VIA_T, ///< class PCB_VIA, a via (like a track segment on a copper layer)
PCB_ARC_T, ///< class PCB_ARC, an arc track segment on a copper layer
@ -442,6 +444,8 @@ constexpr bool IsPcbnewType( const KICAD_T aType )
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
case PCB_TRACE_T:
case PCB_VIA_T:
case PCB_ARC_T:

View File

@ -78,6 +78,19 @@ public:
static TOOL_ACTION centerJustify;
static TOOL_ACTION rightJustify;
// Tables
static TOOL_ACTION selectRows;
static TOOL_ACTION selectColumns;
static TOOL_ACTION selectTable;
static TOOL_ACTION addRowAbove;
static TOOL_ACTION addRowBelow;
static TOOL_ACTION addColBefore;
static TOOL_ACTION addColAfter;
static TOOL_ACTION deleteRows;
static TOOL_ACTION deleteColumns;
static TOOL_ACTION mergeCells;
static TOOL_ACTION unmergeCells;
// Find and Replace
static TOOL_ACTION showSearch;
static TOOL_ACTION find;
@ -185,7 +198,7 @@ public:
static TOOL_ACTION activatePointEditor;
static TOOL_ACTION cycleArcEditMode;
static TOOL_ACTION updatePreferences;
static TOOL_ACTION selectColumns;
static TOOL_ACTION selectLibTreeColumns;
// Suite
static TOOL_ACTION openPreferences;

View File

@ -0,0 +1,555 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023-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
*/
#ifndef EDIT_TABLE_TOOL_BASE_H
#define EDIT_TABLE_TOOL_BASE_H
#include <tool/tool_base.h>
#include <tool/selection.h>
#include <tool/actions.h>
#include <wx/translation.h>
class BASE_SCREEN;
/**
* SCH_TABLE_EDIT_TOOL and PCB_TABLE_EDIT_TOOL share most of their algorithms, which are
* implemented here.
*/
template<typename T_TABLE, typename T_TABLECELL, typename T_COMMIT>
class EDIT_TABLE_TOOL_BASE
{
protected:
void addMenus( CONDITIONAL_MENU& selToolMenu )
{
auto cellSelection = SELECTION_CONDITIONS::MoreThan( 0 )
&& SELECTION_CONDITIONS::OnlyTypes( { SCH_TABLECELL_T,
PCB_TABLECELL_T } );
auto cellBlockSelection =
[&]( const SELECTION& sel )
{
if( sel.Size() < 2 )
return false;
int colMin = std::numeric_limits<int>::max();
int colMax = 0;
int rowMin = std::numeric_limits<int>::max();
int rowMax = 0;
int selectedArea = 0;
for( EDA_ITEM* item : sel )
{
if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
{
colMin = std::min( colMin, cell->GetColumn() );
colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
rowMin = std::min( rowMin, cell->GetRow() );
rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
selectedArea += cell->GetColSpan() * cell->GetRowSpan();
}
}
return selectedArea == ( colMax - colMin ) * ( rowMax - rowMin );
};
auto mergedCellsSelection =
[&]( const SELECTION& sel )
{
for( EDA_ITEM* item : sel )
{
if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
{
if( cell->GetColSpan() > 1 || cell->GetRowSpan() > 1 )
return true;
}
}
return false;
};
//
// Add editing actions to the selection tool menu
//
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( ACTIONS::addRowAbove, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( ACTIONS::addRowBelow, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( ACTIONS::addColBefore, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( ACTIONS::addColAfter, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( ACTIONS::deleteRows, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddItem( ACTIONS::deleteColumns, cellSelection && SELECTION_CONDITIONS::Idle, 100 );
selToolMenu.AddSeparator( 100 );
selToolMenu.AddItem( ACTIONS::mergeCells, cellSelection && cellBlockSelection, 100 );
selToolMenu.AddItem( ACTIONS::unmergeCells, cellSelection && mergedCellsSelection, 100 );
selToolMenu.AddSeparator( 100 );
}
int doAddRowAbove( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
T_TABLECELL* topmost = nullptr;
for( EDA_ITEM* item : selection )
{
T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
if( !topmost || cell->GetRow() < topmost->GetRow() )
topmost = cell;
}
if( !topmost )
return 0;
int row = topmost->GetRow();
T_TABLE* table = static_cast<T_TABLE*>( topmost->GetParent() );
T_COMMIT commit( getToolMgr() );
// Make a copy of the source row before things start moving around
std::vector<T_TABLECELL*> sources;
sources.reserve( table->GetColCount() );
for( int col = 0; col < table->GetColCount(); ++col )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, getScreen() );
for( int col = 0; col < table->GetColCount(); ++col )
{
T_TABLECELL* cell = copyCell( sources[col] );
table->InsertCell( row * table->GetColCount(), cell );
}
for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
table->Normalize();
commit.Push( _( "Add Row Above" ) );
return 0;
}
int doAddRowBelow( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
T_TABLECELL* bottommost = nullptr;
if( selection.Empty() )
return 0;
for( EDA_ITEM* item : selection )
{
T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
if( !bottommost || cell->GetRow() > bottommost->GetRow() )
bottommost = cell;
}
if( !bottommost )
return 0;
int row = bottommost->GetRow();
T_TABLE* table = static_cast<T_TABLE*>( bottommost->GetParent() );
T_COMMIT commit( getToolMgr() );
// Make a copy of the source row before things start moving around
std::vector<T_TABLECELL*> sources;
sources.reserve( table->GetColCount() );
for( int col = 0; col < table->GetColCount(); ++col )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, getScreen() );
for( int col = 0; col < table->GetColCount(); ++col )
{
T_TABLECELL* cell = copyCell( sources[col] );
table->InsertCell( ( row + 1 ) * table->GetColCount(), cell );
}
for( int afterRow = table->GetRowCount() - 1; afterRow > row; afterRow-- )
table->SetRowHeight( afterRow, table->GetRowHeight( afterRow - 1 ) );
table->Normalize();
commit.Push( _( "Add Row Below" ) );
return 0;
}
int doAddColumnBefore( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
T_TABLECELL* leftmost = nullptr;
for( EDA_ITEM* item : selection )
{
T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
if( !leftmost || cell->GetColumn() < leftmost->GetColumn() )
leftmost = cell;
}
if( !leftmost )
return 0;
int col = leftmost->GetColumn();
T_TABLE* table = static_cast<T_TABLE*>( leftmost->GetParent() );
int rowCount = table->GetRowCount();
T_COMMIT commit( getToolMgr() );
// Make a copy of the source column before things start moving around
std::vector<T_TABLECELL*> sources;
sources.reserve( rowCount );
for( int row = 0; row < rowCount; ++row )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, getScreen() );
table->SetColCount( table->GetColCount() + 1 );
for( int row = 0; row < rowCount; ++row )
{
T_TABLECELL* cell = copyCell( sources[row] );
table->InsertCell( row * table->GetColCount() + col, cell );
}
for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
table->Normalize();
commit.Push( _( "Add Column Before" ) );
return 0;
}
int doAddColumnAfter( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
T_TABLECELL* rightmost = nullptr;
for( EDA_ITEM* item : selection )
{
T_TABLECELL* cell = static_cast<T_TABLECELL*>( item );
if( !rightmost || cell->GetColumn() > rightmost->GetColumn() )
rightmost = cell;
}
if( !rightmost )
return 0;
int col = rightmost->GetColumn();
T_TABLE* table = static_cast<T_TABLE*>( rightmost->GetParent() );
int rowCount = table->GetRowCount();
T_COMMIT commit( getToolMgr() );
// Make a copy of the source column before things start moving around
std::vector<T_TABLECELL*> sources;
sources.reserve( rowCount );
for( int row = 0; row < rowCount; ++row )
sources.push_back( table->GetCell( row, col ) );
commit.Modify( table, getScreen() );
table->SetColCount( table->GetColCount() + 1 );
for( int row = 0; row < rowCount; ++row )
{
T_TABLECELL* cell = copyCell( sources[row] );
table->InsertCell( row * table->GetColCount() + col + 1, cell );
}
for( int afterCol = table->GetColCount() - 1; afterCol > col; afterCol-- )
table->SetColWidth( afterCol, table->GetColWidth( afterCol - 1 ) );
table->Normalize();
commit.Push( _( "Add Column After" ) );
return 0;
}
int doDeleteRows( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
if( selection.Empty() )
return 0;
T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
std::vector<int> deleted;
for( int row = 0; row < table->GetRowCount(); ++row )
{
bool deleteRow = false;
for( int col = 0; col < table->GetColCount(); ++col )
{
if( table->GetCell( row, col )->IsSelected() )
{
deleteRow = true;
break;
}
}
if( deleteRow )
{
for( int col = 0; col < table->GetColCount(); ++col )
table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
deleted.push_back( row );
}
}
T_COMMIT commit( getToolMgr() );
if( deleted.size() == (unsigned) table->GetRowCount() )
{
commit.Remove( table );
}
else
{
commit.Modify( table, getScreen() );
clearSelection();
table->DeleteMarkedCells();
for( int row = 0; row < table->GetRowCount(); ++row )
{
int offset = 0;
for( int deletedRow : deleted )
{
if( deletedRow >= row )
offset++;
}
table->SetRowHeight( row, table->GetRowHeight( row + offset ) );
}
table->Normalize();
}
if( deleted.size() > 1 )
commit.Push( _( "Delete Rows" ) );
else
commit.Push( _( "Delete Row" ) );
return 0;
}
int doDeleteColumns( const TOOL_EVENT& aEvent )
{
const SELECTION& selection = getTableCellSelection();
if( selection.Empty() )
return 0;
T_TABLE* table = static_cast<T_TABLE*>( selection[0]->GetParent() );
std::vector<int> deleted;
for( int col = 0; col < table->GetColCount(); ++col )
{
bool deleteColumn = false;
for( int row = 0; row < table->GetRowCount(); ++row )
{
if( table->GetCell( row, col )->IsSelected() )
{
deleteColumn = true;
break;
}
}
if( deleteColumn )
{
for( int row = 0; row < table->GetRowCount(); ++row )
table->GetCell( row, col )->SetFlags( STRUCT_DELETED );
deleted.push_back( col );
}
}
T_COMMIT commit( getToolMgr() );
if( deleted.size() == (unsigned) table->GetColCount() )
{
commit.Remove( table );
}
else
{
commit.Modify( table, getScreen() );
clearSelection();
table->DeleteMarkedCells();
table->SetColCount( table->GetColCount() - deleted.size() );
for( int col = 0; col < table->GetColCount(); ++col )
{
int offset = 0;
for( int deletedCol : deleted )
{
if( deletedCol >= col )
offset++;
}
table->SetColWidth( col, table->GetColWidth( col + offset ) );
}
table->Normalize();
}
if( deleted.size() > 1 )
commit.Push( _( "Delete Columns" ) );
else
commit.Push( _( "Delete Column" ) );
return 0;
}
int doMergeCells( const TOOL_EVENT& aEvent )
{
const SELECTION& sel = getTableCellSelection();
if( sel.Empty() )
return 0;
int colMin = std::numeric_limits<int>::max();
int colMax = 0;
int rowMin = std::numeric_limits<int>::max();
int rowMax = 0;
T_COMMIT commit( getToolMgr() );
T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
for( EDA_ITEM* item : sel )
{
if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
{
colMin = std::min( colMin, cell->GetColumn() );
colMax = std::max( colMax, cell->GetColumn() + cell->GetColSpan() );
rowMin = std::min( rowMin, cell->GetRow() );
rowMax = std::max( rowMax, cell->GetRow() + cell->GetRowSpan() );
}
}
wxString content;
VECTOR2I extents;
for( int row = rowMin; row < rowMax; ++row )
{
extents.y += table->GetRowHeight( row );
extents.x = 0;
for( int col = colMin; col < colMax; ++col )
{
extents.x += table->GetColWidth( col );
T_TABLECELL* cell = table->GetCell( row, col );
if( !cell->GetText().IsEmpty() )
{
if( !content.IsEmpty() )
content += "\n";
content += cell->GetText();
}
commit.Modify( cell, getScreen() );
cell->SetColSpan( 0 );
cell->SetRowSpan( 0 );
cell->SetText( wxEmptyString );
}
}
T_TABLECELL* topLeft = table->GetCell( rowMin, colMin );
topLeft->SetColSpan( colMax - colMin );
topLeft->SetRowSpan( rowMax - rowMin );
topLeft->SetText( content );
topLeft->SetEnd( topLeft->GetStart() + extents );
table->Normalize();
commit.Push( _( "Merge Cells" ) );
return 0;
}
int doUnmergeCells( const TOOL_EVENT& aEvent )
{
const SELECTION& sel = getTableCellSelection();
if( sel.Empty() )
return 0;
T_COMMIT commit( getToolMgr() );
T_TABLE* table = static_cast<T_TABLE*>( sel[0]->GetParent() );
for( EDA_ITEM* item : sel )
{
if( T_TABLECELL* cell = dynamic_cast<T_TABLECELL*>( item ) )
{
int rowSpan = cell->GetRowSpan();
int colSpan = cell->GetColSpan();
for( int row = cell->GetRow(); row < cell->GetRow() + rowSpan; ++row )
{
for( int col = cell->GetColumn(); col < cell->GetColumn() + colSpan; ++col )
{
T_TABLECELL* target = table->GetCell( row, col );
commit.Modify( target, getScreen() );
target->SetColSpan( 1 );
target->SetRowSpan( 1 );
VECTOR2I extents( table->GetColWidth( col ), table->GetRowHeight( row ) );
target->SetEnd( target->GetStart() + extents );
}
}
}
}
table->Normalize();
commit.Push( _( "Unmerge Cells" ) );
return 0;
}
virtual TOOL_MANAGER* getToolMgr() = 0;
virtual BASE_SCREEN* getScreen() = 0;
virtual const SELECTION& getTableCellSelection() = 0;
virtual void clearSelection() = 0;
virtual T_TABLECELL* copyCell( T_TABLECELL* aSource ) = 0;
};
#endif //EDIT_TABLE_TOOL_BASE_H

View File

@ -132,6 +132,8 @@ set( PCBNEW_DIALOGS
dialogs/dialog_print_pcbnew.cpp
dialogs/dialog_swap_layers.cpp
dialogs/dialog_swap_layers_base.cpp
dialogs/dialog_tablecell_properties.cpp
dialogs/dialog_tablecell_properties_base.cpp
dialogs/dialog_target_properties.cpp
dialogs/dialog_target_properties_base.cpp
dialogs/dialog_text_properties.cpp
@ -361,6 +363,7 @@ set( PCBNEW_CLASS_SRCS
tools/drc_tool.cpp
tools/edit_tool.cpp
tools/edit_tool_move_fct.cpp
tools/pcb_edit_table_tool.cpp
tools/global_edit_tool.cpp
tools/group_tool.cpp
tools/footprint_editor_control.cpp

View File

@ -142,6 +142,7 @@ void ARRAY_CREATOR::Invoke()
case PCB_GENERATOR_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TRACE_T:
case PCB_ARC_T:
case PCB_VIA_T:

View File

@ -47,6 +47,7 @@
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_dimension.h>
#include <pgm_base.h>
#include <pcbnew_settings.h>
@ -916,6 +917,7 @@ void BOARD::Add( BOARD_ITEM* aBoardItem, ADD_MODE aMode, bool aSkipConnectivity
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TARGET_T:
if( aMode == ADD_MODE::APPEND || aMode == ADD_MODE::BULK_APPEND )
m_drawings.push_back( aBoardItem );
@ -1019,6 +1021,7 @@ void BOARD::Remove( BOARD_ITEM* aBoardItem, REMOVE_MODE aRemoveMode )
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TARGET_T:
alg::delete_matching( m_drawings, aBoardItem );
break;
@ -1170,6 +1173,15 @@ BOARD_ITEM* BOARD::GetItem( const KIID& aID ) const
for( BOARD_ITEM* drawing : Drawings() )
{
if( drawing->Type() == PCB_TABLE_T )
{
for( PCB_TABLECELL* cell : static_cast<PCB_TABLE*>( drawing )->GetCells() )
{
if( cell->m_Uuid == aID )
return drawing;
}
}
if( drawing->m_Uuid == aID )
return drawing;
}
@ -1505,6 +1517,8 @@ INSPECT_RESULT BOARD::Visit( INSPECTOR inspector, void* testData,
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
case PCB_DIM_ALIGNED_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_RADIAL_T:
@ -2524,6 +2538,13 @@ bool BOARD::cmp_drawings::operator()( const BOARD_ITEM* aFirst,
return textbox->PCB_SHAPE::Compare( other ) && textbox->EDA_TEXT::Compare( other );
}
else if( aFirst->Type() == PCB_TABLE_T )
{
const PCB_TABLE* table = static_cast<const PCB_TABLE*>( aFirst );
const PCB_TABLE* other = static_cast<const PCB_TABLE*>( aSecond );
return PCB_TABLE::Compare( table, other );
}
return aFirst->m_Uuid < aSecond->m_Uuid;
}
@ -2594,14 +2615,22 @@ void BOARD::ConvertBrdLayerToPolygonalContours( PCB_LAYER_ID aLayer,
{
const PCB_TEXTBOX* textbox = static_cast<const PCB_TEXTBOX*>( item );
// plot border
// border
textbox->PCB_SHAPE::TransformShapeToPolygon( aOutlines, aLayer, 0, maxError,
ERROR_INSIDE );
// plot text
// text
textbox->TransformTextToPolySet( aOutlines, 0, maxError, ERROR_INSIDE );
break;
}
case PCB_TABLE_T:
{
const PCB_TABLE* table = static_cast<const PCB_TABLE*>( item );
table->TransformShapeToPolygon( aOutlines, aLayer, 0, maxError, ERROR_INSIDE );
break;
}
case PCB_DIM_ALIGNED_T:
case PCB_DIM_CENTER_T:
case PCB_DIM_RADIAL_T:

View File

@ -338,7 +338,8 @@ void BOARD_COMMIT::Push( const wxString& aMessage, int aCommitFlags )
case PCB_SHAPE_T: // a shape (normally not on copper layers)
case PCB_REFERENCE_IMAGE_T: // a bitmap on an associated layer
case PCB_GENERATOR_T: // a generator on a layer
case PCB_TEXTBOX_T: // a wrapped text on a layer
case PCB_TEXTBOX_T: // a line-wrapped (and optionally bordered) text item
case PCB_TABLE_T: // rows and columns of tablecells
case PCB_TRACE_T: // a track segment (segment on a copper layer)
case PCB_ARC_T: // an arced track segment (segment on a copper layer)
case PCB_VIA_T: // a via (like track segment on a copper layer)

View File

@ -42,6 +42,8 @@ const std::vector<KICAD_T> GENERAL_COLLECTOR::AllBoardItems = {
PCB_TEXT_T, // in m_drawings
PCB_REFERENCE_IMAGE_T, // in m_drawings
PCB_TEXTBOX_T, // in m_drawings
PCB_TABLE_T, // in m_drawings
PCB_TABLECELL_T, // in tables
PCB_SHAPE_T, // in m_drawings
PCB_DIM_ALIGNED_T, // in m_drawings
PCB_DIM_CENTER_T, // in m_drawings
@ -66,6 +68,7 @@ const std::vector<KICAD_T> GENERAL_COLLECTOR::BoardLevelItems = {
PCB_REFERENCE_IMAGE_T,
PCB_TEXT_T,
PCB_TEXTBOX_T,
PCB_TABLE_T,
PCB_SHAPE_T,
PCB_DIM_ALIGNED_T,
PCB_DIM_ORTHOGONAL_T,
@ -101,6 +104,8 @@ const std::vector<KICAD_T> GENERAL_COLLECTOR::FootprintItems = {
PCB_FIELD_T,
PCB_TEXT_T,
PCB_TEXTBOX_T,
PCB_TABLE_T,
PCB_TABLECELL_T,
PCB_SHAPE_T,
PCB_DIM_ALIGNED_T,
PCB_DIM_ORTHOGONAL_T,
@ -194,6 +199,8 @@ INSPECT_RESULT GENERAL_COLLECTOR::Inspect( EDA_ITEM* aTestItem, void* aTestData
case PCB_TEXTBOX_T:
case PCB_SHAPE_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
boardItem = static_cast<BOARD_ITEM*>( aTestItem );
break;

View File

@ -0,0 +1,477 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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 <widgets/bitmap_button.h>
#include <widgets/font_choice.h>
#include <confirm.h>
#include <board_commit.h>
#include <board_design_settings.h>
#include <board.h>
#include <footprint.h>
#include <pcb_textbox.h>
#include <pcb_tablecell.h>
#include <pcb_table.h>
#include <project.h>
#include <pcb_edit_frame.h>
#include <pcb_layer_box_selector.h>
#include <tool/tool_manager.h>
#include <tools/pcb_actions.h>
#include <scintilla_tricks.h>
#include "dialog_tablecell_properties.h"
class TABLECELL_SCINTILLA_TRICKS : public SCINTILLA_TRICKS
{
public:
TABLECELL_SCINTILLA_TRICKS( wxStyledTextCtrl* aScintilla,
std::function<void( wxKeyEvent& )> onAcceptHandler,
std::function<void()> onNextHandler ) :
SCINTILLA_TRICKS( aScintilla, wxT( "{}" ), false, std::move( onAcceptHandler ) ),
m_onNextHandler( std::move( onNextHandler ) )
{ }
protected:
void onCharHook( wxKeyEvent& aEvent ) override
{
if( aEvent.GetKeyCode() == WXK_TAB && aEvent.AltDown() && !aEvent.ControlDown() )
m_onNextHandler();
else
SCINTILLA_TRICKS::onCharHook( aEvent );
}
private:
std::function<void()> m_onNextHandler;
};
DIALOG_TABLECELL_PROPERTIES::DIALOG_TABLECELL_PROPERTIES( PCB_BASE_EDIT_FRAME* aFrame,
PCB_TABLECELL* aCell ) :
DIALOG_TABLECELL_PROPERTIES_BASE( aFrame ),
m_frame( aFrame ),
m_table( nullptr ),
m_cell( aCell ),
m_borderWidth( aFrame, m_borderWidthLabel, m_borderWidthCtrl, m_borderWidthUnits ),
m_separatorsWidth( aFrame, m_separatorsWidthLabel, m_separatorsWidthCtrl, m_separatorsWidthUnits ),
m_textHeight( aFrame, m_SizeYLabel, m_SizeYCtrl, m_SizeYUnits ),
m_textWidth( aFrame, m_SizeXLabel, m_SizeXCtrl, m_SizeXUnits ),
m_textThickness( aFrame, m_ThicknessLabel, m_ThicknessCtrl, m_ThicknessUnits ),
m_scintillaTricks( nullptr )
{
m_table = static_cast<PCB_TABLE*>( m_cell->GetParent() );
#ifdef _WIN32
// Without this setting, on Windows, some esoteric unicode chars create display issue
// in a wxStyledTextCtrl.
// for SetTechnology() info, see https://www.scintilla.org/ScintillaDoc.html#SCI_SETTECHNOLOGY
m_textCtrl->SetTechnology(wxSTC_TECHNOLOGY_DIRECTWRITE);
#endif
m_scintillaTricks = new TABLECELL_SCINTILLA_TRICKS( m_textCtrl,
// onAccept handler
[this]( wxKeyEvent& aEvent )
{
wxPostEvent( this, wxCommandEvent( wxEVT_COMMAND_BUTTON_CLICKED, wxID_OK ) );
},
// onNext handler
[this]()
{
wxCommandEvent dummy;
OnApply( dummy );
} );
// A hack which causes Scintilla to auto-size the text editor canvas
// See: https://github.com/jacobslusser/ScintillaNET/issues/216
m_textCtrl->SetScrollWidth( 1 );
m_textCtrl->SetScrollWidthTracking( true );
SetInitialFocus( m_textCtrl );
if( m_table->GetParentFootprint() )
{
// Do not allow locking items in the footprint editor
m_cbLocked->Show( false );
}
// Configure the layers list selector. Note that footprints are built outside the current
// board and so we may need to show all layers if the text is on an unactivated layer.
if( !m_frame->GetBoard()->IsLayerEnabled( m_table->GetLayer() ) )
m_LayerSelectionCtrl->ShowNonActivatedLayers( true );
m_LayerSelectionCtrl->SetLayersHotkeys( false );
m_LayerSelectionCtrl->SetBoardFrame( m_frame );
m_LayerSelectionCtrl->Resync();
for( const auto& [lineStyle, lineStyleDesc] : lineTypeNames )
{
m_borderStyleCombo->Append( lineStyleDesc.name, KiBitmap( lineStyleDesc.bitmap ) );
m_separatorsStyleCombo->Append( lineStyleDesc.name, KiBitmap( lineStyleDesc.bitmap ) );
}
m_borderStyleCombo->Append( DEFAULT_STYLE );
m_separatorsStyleCombo->Append( DEFAULT_STYLE );
m_separator1->SetIsSeparator();
m_bold->SetIsCheckButton();
m_bold->SetBitmap( KiBitmapBundle( BITMAPS::text_bold ) );
m_italic->SetIsCheckButton();
m_italic->SetBitmap( KiBitmapBundle( BITMAPS::text_italic ) );
m_separator2->SetIsSeparator();
m_hAlignLeft->SetIsRadioButton();
m_hAlignLeft->SetBitmap( KiBitmapBundle( BITMAPS::text_align_left ) );
m_hAlignCenter->SetIsRadioButton();
m_hAlignCenter->SetBitmap( KiBitmapBundle( BITMAPS::text_align_center ) );
m_hAlignRight->SetIsRadioButton();
m_hAlignRight->SetBitmap( KiBitmapBundle( BITMAPS::text_align_right ) );
m_separator3->SetIsSeparator();
m_vAlignTop->SetIsRadioButton();
m_vAlignTop->SetBitmap( KiBitmapBundle( BITMAPS::text_valign_top ) );
m_vAlignCenter->SetIsRadioButton();
m_vAlignCenter->SetBitmap( KiBitmapBundle( BITMAPS::text_valign_center ) );
m_vAlignBottom->SetIsRadioButton();
m_vAlignBottom->SetBitmap( KiBitmapBundle( BITMAPS::text_valign_bottom ) );
m_separator4->SetIsSeparator();
m_hotkeyHint->SetFont( KIUI::GetInfoFont( this ) );
m_hotkeyHint->SetLabel( wxString::Format( wxT( "(%s+%s)" ),
KeyNameFromKeyCode( WXK_ALT ),
KeyNameFromKeyCode( WXK_TAB ) ) );
SetupStandardButtons();
Layout();
m_hAlignLeft->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onHAlignButton, this );
m_hAlignCenter->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onHAlignButton, this );
m_hAlignRight->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onHAlignButton, this );
m_vAlignTop->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onVAlignButton, this );
m_vAlignCenter->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onVAlignButton, this );
m_vAlignBottom->Bind( wxEVT_BUTTON, &DIALOG_TABLECELL_PROPERTIES::onVAlignButton, this );
// Now all widgets have the size fixed, call FinishDialogSettings
finishDialogSettings();
}
DIALOG_TABLECELL_PROPERTIES::~DIALOG_TABLECELL_PROPERTIES()
{
delete m_scintillaTricks;
}
bool DIALOG_TABLECELL_PROPERTIES::TransferDataToWindow()
{
if( !wxDialog::TransferDataToWindow() )
return false;
m_LayerSelectionCtrl->SetLayerSelection( m_table->GetLayer() );
m_cbLocked->SetValue( m_table->IsLocked() );
m_borderCheckbox->SetValue( m_table->StrokeExternal() );
m_headerBorder->SetValue( m_table->StrokeHeader() );
if( m_table->GetBorderStroke().GetWidth() >= 0 )
m_borderWidth.SetValue( m_table->GetBorderStroke().GetWidth() );
int style = static_cast<int>( m_table->GetBorderStroke().GetLineStyle() );
if( style == -1 )
m_borderStyleCombo->SetStringSelection( DEFAULT_STYLE );
else if( style < (int) lineTypeNames.size() )
m_borderStyleCombo->SetSelection( style );
else
wxFAIL_MSG( "Line type not found in the type lookup map" );
m_borderWidth.Enable( m_table->StrokeExternal() || m_table->StrokeHeader() );
m_borderStyleLabel->Enable( m_table->StrokeExternal() || m_table->StrokeHeader() );
m_borderStyleCombo->Enable( m_table->StrokeExternal() || m_table->StrokeHeader() );
bool rows = m_table->StrokeRows() && m_table->GetSeparatorsStroke().GetWidth() >= 0;
bool cols = m_table->StrokeColumns() && m_table->GetSeparatorsStroke().GetWidth() >= 0;
m_rowSeparators->SetValue( rows );
m_colSeparators->SetValue( cols );
if( m_table->GetSeparatorsStroke().GetWidth() >= 0 )
m_separatorsWidth.SetValue( m_table->GetSeparatorsStroke().GetWidth() );
style = static_cast<int>( m_table->GetSeparatorsStroke().GetLineStyle() );
if( style == -1 )
m_separatorsStyleCombo->SetStringSelection( DEFAULT_STYLE );
else if( style < (int) lineTypeNames.size() )
m_separatorsStyleCombo->SetSelection( style );
else
wxFAIL_MSG( "Line type not found in the type lookup map" );
m_separatorsWidth.Enable( rows || cols );
m_separatorsStyleLabel->Enable( rows || cols );
m_separatorsStyleCombo->Enable( rows || cols );
m_textCtrl->SetValue( m_cell->GetText() );
m_fontCtrl->SetFontSelection( m_cell->GetFont() );
m_textWidth.SetValue( m_cell->GetTextWidth() );
m_textHeight.SetValue( m_cell->GetTextHeight() );
m_textThickness.SetValue( m_cell->GetTextThickness() );
m_bold->Check( m_cell->IsBold() );
m_italic->Check( m_cell->IsItalic() );
switch( m_cell->GetHorizJustify() )
{
case GR_TEXT_H_ALIGN_LEFT: m_hAlignLeft->Check(); break;
case GR_TEXT_H_ALIGN_CENTER: m_hAlignCenter->Check(); break;
case GR_TEXT_H_ALIGN_RIGHT: m_hAlignRight->Check(); break;
}
switch( m_cell->GetVertJustify() )
{
case GR_TEXT_V_ALIGN_TOP: m_vAlignTop->Check(); break;
case GR_TEXT_V_ALIGN_CENTER: m_vAlignCenter->Check(); break;
case GR_TEXT_V_ALIGN_BOTTOM: m_vAlignBottom->Check(); break;
}
return true;
}
void DIALOG_TABLECELL_PROPERTIES::onHAlignButton( wxCommandEvent& aEvent )
{
for( BITMAP_BUTTON* btn : { m_hAlignLeft, m_hAlignCenter, m_hAlignRight } )
{
if( btn->IsChecked() && btn != aEvent.GetEventObject() )
btn->Check( false );
}
}
void DIALOG_TABLECELL_PROPERTIES::onVAlignButton( wxCommandEvent& aEvent )
{
for( BITMAP_BUTTON* btn : { m_vAlignTop, m_vAlignCenter, m_vAlignBottom } )
{
if( btn->IsChecked() && btn != aEvent.GetEventObject() )
btn->Check( false );
}
}
void DIALOG_TABLECELL_PROPERTIES::onBorderChecked( wxCommandEvent& aEvent )
{
BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
PCB_LAYER_ID currentLayer = ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() );
int defaultLineThickness = bds.GetLineThickness( currentLayer );
bool border = m_borderCheckbox->GetValue();
if( border && m_borderWidth.GetValue() < 0 )
m_borderWidth.SetValue( defaultLineThickness );
m_borderWidth.Enable( border );
m_borderStyleLabel->Enable( border );
m_borderStyleCombo->Enable( border );
bool row = m_rowSeparators->GetValue();
bool col = m_colSeparators->GetValue();
if( ( row || col ) && m_separatorsWidth.GetValue() < 0 )
m_separatorsWidth.SetValue( defaultLineThickness );
m_separatorsWidth.Enable( row || col );
m_separatorsStyleLabel->Enable( row || col );
m_separatorsStyleCombo->Enable( row || col );
}
void DIALOG_TABLECELL_PROPERTIES::OnCharHook( wxKeyEvent& aEvt )
{
if( aEvt.GetKeyCode() == WXK_TAB && aEvt.AltDown() && !aEvt.ControlDown() )
{
wxCommandEvent dummy;
OnApply( dummy );
}
else
{
DIALOG_SHIM::OnCharHook( aEvt );
}
}
void DIALOG_TABLECELL_PROPERTIES::OnApply( wxCommandEvent& aEvent )
{
TransferDataFromWindow();
for( size_t ii = 0; ii < m_table->GetCells().size(); ++ii )
{
if( m_table->GetCells()[ii] == m_cell )
{
ii++;
if( ii >= m_table->GetCells().size() )
ii = 0;
m_cell = m_table->GetCells()[ii];
m_frame->GetToolManager()->RunAction( PCB_ACTIONS::selectionClear );
m_frame->GetToolManager()->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, m_cell );
break;
}
}
TransferDataToWindow();
m_textCtrl->SelectAll();
}
bool DIALOG_TABLECELL_PROPERTIES::TransferDataFromWindow()
{
if( !wxDialog::TransferDataFromWindow() )
return false;
BOARD_COMMIT commit( m_frame );
commit.Modify( m_table );
// If no other command in progress, prepare undo command
// (for a command in progress, will be made later, at the completion of command)
bool pushCommit = ( m_table->GetEditFlags() == 0 );
// Set IN_EDIT flag to force undo/redo/abort proper operation and avoid new calls to
// SaveCopyInUndoList for the same text if is moved, and then rotated, edited, etc....
if( !pushCommit )
m_table->SetFlags( IN_EDIT );
m_table->SetLayer( ToLAYER_ID( m_LayerSelectionCtrl->GetLayerSelection() ) );
m_table->SetLocked( m_cbLocked->GetValue() );
m_table->SetStrokeExternal( m_borderCheckbox->GetValue() );
m_table->SetStrokeHeader( m_headerBorder->GetValue() );
{
STROKE_PARAMS stroke = m_table->GetBorderStroke();
if( m_borderCheckbox->GetValue() )
stroke.SetWidth( std::max( 0, m_borderWidth.GetIntValue() ) );
else
stroke.SetWidth( -1 );
auto it = lineTypeNames.begin();
std::advance( it, m_borderStyleCombo->GetSelection() );
if( it == lineTypeNames.end() )
stroke.SetLineStyle( LINE_STYLE::DEFAULT );
else
stroke.SetLineStyle( it->first );
m_table->SetBorderStroke( stroke );
}
m_table->SetStrokeRows( m_rowSeparators->GetValue() );
m_table->SetStrokeColumns( m_colSeparators->GetValue() );
{
STROKE_PARAMS stroke = m_table->GetSeparatorsStroke();
if( m_rowSeparators->GetValue() || m_colSeparators->GetValue() )
stroke.SetWidth( std::max( 0, m_separatorsWidth.GetIntValue() ) );
else
stroke.SetWidth( -1 );
auto it = lineTypeNames.begin();
std::advance( it, m_separatorsStyleCombo->GetSelection() );
if( it == lineTypeNames.end() )
stroke.SetLineStyle( LINE_STYLE::DEFAULT );
else
stroke.SetLineStyle( it->first );
m_table->SetSeparatorsStroke( stroke );
}
wxString txt = m_textCtrl->GetValue();
#ifdef __WXMAC__
// On macOS CTRL+Enter produces '\r' instead of '\n' regardless of EOL setting.
// Replace it now.
txt.Replace( "\r", "\n" );
#elif defined( __WINDOWS__ )
// On Windows, a new line is coded as \r\n. We use only \n in kicad files and in
// drawing routines so strip the \r char.
txt.Replace( "\r", "" );
#endif
m_cell->SetText( txt );
if( m_fontCtrl->HaveFontSelection() )
{
m_cell->SetFont( m_fontCtrl->GetFontSelection( m_bold->IsChecked(),
m_italic->IsChecked() ) );
}
m_cell->SetTextWidth( m_textWidth.GetIntValue() );
m_cell->SetTextHeight( m_textHeight.GetIntValue() );
m_cell->SetTextThickness( m_textThickness.GetIntValue() );
if( m_bold->IsChecked() != m_cell->IsBold() )
{
if( m_bold->IsChecked() )
{
m_cell->SetBold( true );
m_cell->SetTextThickness( GetPenSizeForBold( m_cell->GetTextWidth() ) );
}
else
{
m_cell->SetBold( false );
m_cell->SetTextThickness( 0 ); // Use default pen width
}
}
if( m_hAlignRight->IsChecked() )
m_cell->SetHorizJustify( GR_TEXT_H_ALIGN_RIGHT );
else if( m_hAlignCenter->IsChecked() )
m_cell->SetHorizJustify( GR_TEXT_H_ALIGN_CENTER );
else
m_cell->SetHorizJustify( GR_TEXT_H_ALIGN_LEFT );
if( m_vAlignBottom->IsChecked() )
m_cell->SetVertJustify( GR_TEXT_V_ALIGN_BOTTOM );
else if( m_vAlignCenter->IsChecked() )
m_cell->SetVertJustify( GR_TEXT_V_ALIGN_CENTER );
else
m_cell->SetVertJustify( GR_TEXT_V_ALIGN_TOP );
if( !commit.Empty() )
commit.Push( _( "Edit Table Cell" ), SKIP_CONNECTIVITY );
return true;
}
void PCB_BASE_EDIT_FRAME::ShowTableCellPropertiesDialog( PCB_TABLECELL* aTableCell )
{
DIALOG_TABLECELL_PROPERTIES dlg( this, aTableCell );
// QuasiModal required for Scintilla auto-complete
dlg.ShowQuasiModal();
}

View File

@ -0,0 +1,72 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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
*/
#ifndef DIALOG_TABLECELL_PROPERTIES_H
#define DIALOG_TABLECELL_PROPERTIES_H
#include <widgets/unit_binder.h>
#include <wx/valnum.h>
#include "dialog_tablecell_properties_base.h"
class PCB_BASE_EDIT_FRAME;
class PCB_TABLE;
class PCB_TABLECELL;
class SCINTILLA_TRICKS;
class DIALOG_TABLECELL_PROPERTIES : public DIALOG_TABLECELL_PROPERTIES_BASE
{
public:
DIALOG_TABLECELL_PROPERTIES( PCB_BASE_EDIT_FRAME* aParentFrame, PCB_TABLECELL* aCell );
~DIALOG_TABLECELL_PROPERTIES();
protected:
void OnCharHook( wxKeyEvent& aEvt ) override;
private:
bool TransferDataToWindow() override;
bool TransferDataFromWindow() override;
void onHAlignButton( wxCommandEvent &aEvent );
void onVAlignButton( wxCommandEvent &aEvent );
void onBorderChecked( wxCommandEvent& aEvent ) override;
void OnApply( wxCommandEvent& aEvent ) override;
private:
PCB_BASE_EDIT_FRAME* m_frame;
PCB_TABLE* m_table;
PCB_TABLECELL* m_cell;
UNIT_BINDER m_borderWidth;
UNIT_BINDER m_separatorsWidth;
UNIT_BINDER m_textHeight;
UNIT_BINDER m_textWidth;
UNIT_BINDER m_textThickness;
SCINTILLA_TRICKS* m_scintillaTricks;
};
#endif //DIALOG_TABLECELL_PROPERTIES_H

View File

@ -0,0 +1,382 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#include "pcb_layer_box_selector.h"
#include "widgets/bitmap_button.h"
#include "widgets/font_choice.h"
#include "widgets/wx_infobar.h"
#include "dialog_tablecell_properties_base.h"
///////////////////////////////////////////////////////////////////////////
DIALOG_TABLECELL_PROPERTIES_BASE::DIALOG_TABLECELL_PROPERTIES_BASE( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : DIALOG_SHIM( parent, id, title, pos, size, style )
{
this->SetSizeHints( wxDefaultSize, wxDefaultSize );
wxBoxSizer* bMainSizer;
bMainSizer = new wxBoxSizer( wxVERTICAL );
m_infoBar = new WX_INFOBAR( this );
m_infoBar->SetShowHideEffects( wxSHOW_EFFECT_NONE, wxSHOW_EFFECT_NONE );
m_infoBar->SetEffectDuration( 500 );
m_infoBar->Hide();
bMainSizer->Add( m_infoBar, 0, wxEXPAND|wxBOTTOM, 5 );
wxBoxSizer* bColumns;
bColumns = new wxBoxSizer( wxHORIZONTAL );
wxBoxSizer* bCellContentMargins;
bCellContentMargins = new wxBoxSizer( wxVERTICAL );
m_textCtrl = new wxStyledTextCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SUNKEN, wxEmptyString );
m_textCtrl->SetUseTabs( true );
m_textCtrl->SetTabWidth( 4 );
m_textCtrl->SetIndent( 4 );
m_textCtrl->SetTabIndents( false );
m_textCtrl->SetBackSpaceUnIndents( false );
m_textCtrl->SetViewEOL( false );
m_textCtrl->SetViewWhiteSpace( false );
m_textCtrl->SetMarginWidth( 2, 0 );
m_textCtrl->SetIndentationGuides( false );
m_textCtrl->SetReadOnly( false );
m_textCtrl->SetMarginWidth( 1, 0 );
m_textCtrl->SetMarginWidth( 0, 0 );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDER, wxSTC_MARK_BOXPLUS );
m_textCtrl->MarkerSetBackground( wxSTC_MARKNUM_FOLDER, wxColour( wxT("BLACK") ) );
m_textCtrl->MarkerSetForeground( wxSTC_MARKNUM_FOLDER, wxColour( wxT("WHITE") ) );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDEROPEN, wxSTC_MARK_BOXMINUS );
m_textCtrl->MarkerSetBackground( wxSTC_MARKNUM_FOLDEROPEN, wxColour( wxT("BLACK") ) );
m_textCtrl->MarkerSetForeground( wxSTC_MARKNUM_FOLDEROPEN, wxColour( wxT("WHITE") ) );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDERSUB, wxSTC_MARK_EMPTY );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDEREND, wxSTC_MARK_BOXPLUS );
m_textCtrl->MarkerSetBackground( wxSTC_MARKNUM_FOLDEREND, wxColour( wxT("BLACK") ) );
m_textCtrl->MarkerSetForeground( wxSTC_MARKNUM_FOLDEREND, wxColour( wxT("WHITE") ) );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDEROPENMID, wxSTC_MARK_BOXMINUS );
m_textCtrl->MarkerSetBackground( wxSTC_MARKNUM_FOLDEROPENMID, wxColour( wxT("BLACK") ) );
m_textCtrl->MarkerSetForeground( wxSTC_MARKNUM_FOLDEROPENMID, wxColour( wxT("WHITE") ) );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDERMIDTAIL, wxSTC_MARK_EMPTY );
m_textCtrl->MarkerDefine( wxSTC_MARKNUM_FOLDERTAIL, wxSTC_MARK_EMPTY );
m_textCtrl->SetSelBackground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHT ) );
m_textCtrl->SetSelForeground( true, wxSystemSettings::GetColour( wxSYS_COLOUR_HIGHLIGHTTEXT ) );
m_textCtrl->SetMinSize( wxSize( 500,-1 ) );
bCellContentMargins->Add( m_textCtrl, 1, wxEXPAND|wxTOP|wxBOTTOM, 1 );
bColumns->Add( bCellContentMargins, 1, wxEXPAND|wxTOP, 6 );
m_notebook = new wxNotebook( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0 );
m_tablePage = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer16;
bSizer16 = new wxBoxSizer( wxVERTICAL );
m_textEntrySizer = new wxGridBagSizer( 3, 3 );
m_textEntrySizer->SetFlexibleDirection( wxBOTH );
m_textEntrySizer->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
m_textEntrySizer->SetEmptyCellSize( wxSize( 0,2 ) );
m_layerLabel = new wxStaticText( m_tablePage, wxID_ANY, _("Layer:"), wxDefaultPosition, wxDefaultSize, 0 );
m_layerLabel->Wrap( -1 );
m_textEntrySizer->Add( m_layerLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
m_LayerSelectionCtrl = new PCB_LAYER_BOX_SELECTOR( m_tablePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, 0 );
m_LayerSelectionCtrl->SetMinSize( wxSize( 175,-1 ) );
m_textEntrySizer->Add( m_LayerSelectionCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_cbLocked = new wxCheckBox( m_tablePage, wxID_ANY, _("Locked"), wxDefaultPosition, wxDefaultSize, 0 );
m_textEntrySizer->Add( m_cbLocked, wxGBPosition( 1, 0 ), wxGBSpan( 1, 3 ), wxBOTTOM, 20 );
m_borderCheckbox = new wxCheckBox( m_tablePage, wxID_ANY, _("External border"), wxDefaultPosition, wxDefaultSize, 0 );
m_textEntrySizer->Add( m_borderCheckbox, wxGBPosition( 2, 0 ), wxGBSpan( 1, 2 ), wxBOTTOM, 2 );
m_headerBorder = new wxCheckBox( m_tablePage, wxID_ANY, _("Header border"), wxDefaultPosition, wxDefaultSize, 0 );
m_textEntrySizer->Add( m_headerBorder, wxGBPosition( 2, 2 ), wxGBSpan( 1, 1 ), wxLEFT, 20 );
m_borderWidthLabel = new wxStaticText( m_tablePage, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_borderWidthLabel->Wrap( -1 );
m_textEntrySizer->Add( m_borderWidthLabel, wxGBPosition( 4, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
wxBoxSizer* bSizer7;
bSizer7 = new wxBoxSizer( wxHORIZONTAL );
m_borderWidthCtrl = new wxTextCtrl( m_tablePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer7->Add( m_borderWidthCtrl, 0, wxEXPAND, 5 );
m_borderWidthUnits = new wxStaticText( m_tablePage, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_borderWidthUnits->Wrap( -1 );
bSizer7->Add( m_borderWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 3 );
m_textEntrySizer->Add( bSizer7, wxGBPosition( 4, 1 ), wxGBSpan( 1, 2 ), wxEXPAND, 5 );
m_borderStyleLabel = new wxStaticText( m_tablePage, wxID_ANY, _("Style:"), wxDefaultPosition, wxDefaultSize, 0 );
m_borderStyleLabel->Wrap( -1 );
m_textEntrySizer->Add( m_borderStyleLabel, wxGBPosition( 5, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
m_borderStyleCombo = new wxBitmapComboBox( m_tablePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
m_borderStyleCombo->SetMinSize( wxSize( 200,-1 ) );
m_textEntrySizer->Add( m_borderStyleCombo, wxGBPosition( 5, 1 ), wxGBSpan( 1, 2 ), wxEXPAND, 5 );
m_textEntrySizer->Add( 0, 15, wxGBPosition( 6, 0 ), wxGBSpan( 1, 1 ), wxEXPAND, 5 );
m_rowSeparators = new wxCheckBox( m_tablePage, wxID_ANY, _("Row lines"), wxDefaultPosition, wxDefaultSize, 0 );
m_textEntrySizer->Add( m_rowSeparators, wxGBPosition( 7, 0 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxRIGHT, 15 );
m_colSeparators = new wxCheckBox( m_tablePage, wxID_ANY, _("Column lines"), wxDefaultPosition, wxDefaultSize, 0 );
m_textEntrySizer->Add( m_colSeparators, wxGBPosition( 7, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT, 20 );
m_separatorsWidthLabel = new wxStaticText( m_tablePage, wxID_ANY, _("Width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_separatorsWidthLabel->Wrap( -1 );
m_textEntrySizer->Add( m_separatorsWidthLabel, wxGBPosition( 9, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
wxBoxSizer* bSizer71;
bSizer71 = new wxBoxSizer( wxHORIZONTAL );
m_separatorsWidthCtrl = new wxTextCtrl( m_tablePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize( -1,-1 ), 0 );
bSizer71->Add( m_separatorsWidthCtrl, 0, wxEXPAND, 5 );
m_separatorsWidthUnits = new wxStaticText( m_tablePage, wxID_ANY, _("mm"), wxDefaultPosition, wxDefaultSize, 0 );
m_separatorsWidthUnits->Wrap( -1 );
bSizer71->Add( m_separatorsWidthUnits, 0, wxALIGN_CENTER_VERTICAL|wxLEFT, 3 );
m_textEntrySizer->Add( bSizer71, wxGBPosition( 9, 1 ), wxGBSpan( 1, 2 ), wxEXPAND, 5 );
m_separatorsStyleLabel = new wxStaticText( m_tablePage, wxID_ANY, _("Style:"), wxDefaultPosition, wxDefaultSize, 0 );
m_separatorsStyleLabel->Wrap( -1 );
m_textEntrySizer->Add( m_separatorsStyleLabel, wxGBPosition( 10, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
m_separatorsStyleCombo = new wxBitmapComboBox( m_tablePage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0, NULL, wxCB_READONLY );
m_separatorsStyleCombo->SetMinSize( wxSize( 200,-1 ) );
m_textEntrySizer->Add( m_separatorsStyleCombo, wxGBPosition( 10, 1 ), wxGBSpan( 1, 2 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_textEntrySizer->AddGrowableCol( 1 );
bSizer16->Add( m_textEntrySizer, 1, wxEXPAND|wxALL, 5 );
m_tablePage->SetSizer( bSizer16 );
m_tablePage->Layout();
bSizer16->Fit( m_tablePage );
m_notebook->AddPage( m_tablePage, _("Table"), false );
m_cellPage = new wxPanel( m_notebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL );
wxBoxSizer* bSizer13;
bSizer13 = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bMargins;
bMargins = new wxBoxSizer( wxVERTICAL );
wxBoxSizer* bSizeCtrlSizer;
bSizeCtrlSizer = new wxBoxSizer( wxHORIZONTAL );
m_separator1 = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_separator1->Enable( false );
bSizeCtrlSizer->Add( m_separator1, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_bold = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_bold->SetToolTip( _("Bold") );
bSizeCtrlSizer->Add( m_bold, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_italic = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_italic->SetToolTip( _("Italic") );
bSizeCtrlSizer->Add( m_italic, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_separator2 = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_separator2->Enable( false );
bSizeCtrlSizer->Add( m_separator2, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_hAlignLeft = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_hAlignLeft->SetToolTip( _("Align left") );
bSizeCtrlSizer->Add( m_hAlignLeft, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_hAlignCenter = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_hAlignCenter->SetToolTip( _("Align horizontal center") );
bSizeCtrlSizer->Add( m_hAlignCenter, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_hAlignRight = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_hAlignRight->SetToolTip( _("Align right") );
bSizeCtrlSizer->Add( m_hAlignRight, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_separator3 = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_separator3->Enable( false );
bSizeCtrlSizer->Add( m_separator3, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_vAlignTop = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_vAlignTop->SetToolTip( _("Align top") );
bSizeCtrlSizer->Add( m_vAlignTop, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_vAlignCenter = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_vAlignCenter->SetToolTip( _("Align vertical center") );
bSizeCtrlSizer->Add( m_vAlignCenter, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_vAlignBottom = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_vAlignBottom->SetToolTip( _("Align bottom") );
bSizeCtrlSizer->Add( m_vAlignBottom, 0, wxALIGN_CENTER_VERTICAL, 5 );
m_separator4 = new BITMAP_BUTTON( m_cellPage, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 21,21 ), wxBU_AUTODRAW|wxBORDER_NONE );
m_separator4->Enable( false );
bSizeCtrlSizer->Add( m_separator4, 0, wxALIGN_CENTER_VERTICAL, 5 );
bMargins->Add( bSizeCtrlSizer, 0, wxBOTTOM, 10 );
wxGridBagSizer* gbSizer2;
gbSizer2 = new wxGridBagSizer( 4, 5 );
gbSizer2->SetFlexibleDirection( wxBOTH );
gbSizer2->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
gbSizer2->SetEmptyCellSize( wxSize( -1,5 ) );
m_fontLabel = new wxStaticText( m_cellPage, wxID_ANY, _("Font:"), wxDefaultPosition, wxDefaultSize, 0 );
m_fontLabel->Wrap( -1 );
gbSizer2->Add( m_fontLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxLEFT, 1 );
wxString m_fontCtrlChoices[] = { _("Default Font"), _("KiCad Font") };
int m_fontCtrlNChoices = sizeof( m_fontCtrlChoices ) / sizeof( wxString );
m_fontCtrl = new FONT_CHOICE( m_cellPage, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_fontCtrlNChoices, m_fontCtrlChoices, 0 );
m_fontCtrl->SetSelection( 0 );
gbSizer2->Add( m_fontCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 3 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
gbSizer2->AddGrowableCol( 1 );
bMargins->Add( gbSizer2, 0, wxEXPAND|wxBOTTOM, 5 );
wxGridBagSizer* gbSizer1;
gbSizer1 = new wxGridBagSizer( 3, 5 );
gbSizer1->SetFlexibleDirection( wxBOTH );
gbSizer1->SetNonFlexibleGrowMode( wxFLEX_GROWMODE_SPECIFIED );
gbSizer1->SetEmptyCellSize( wxSize( -1,8 ) );
m_SizeXLabel = new wxStaticText( m_cellPage, wxID_ANY, _("Text width:"), wxDefaultPosition, wxDefaultSize, 0 );
m_SizeXLabel->Wrap( -1 );
m_SizeXLabel->SetToolTip( _("Text width") );
gbSizer1->Add( m_SizeXLabel, wxGBPosition( 0, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 4 );
m_SizeXCtrl = new wxTextCtrl( m_cellPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
gbSizer1->Add( m_SizeXCtrl, wxGBPosition( 0, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_SizeXUnits = new wxStaticText( m_cellPage, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_SizeXUnits->Wrap( -1 );
gbSizer1->Add( m_SizeXUnits, wxGBPosition( 0, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
m_SizeYLabel = new wxStaticText( m_cellPage, wxID_ANY, _("Text height:"), wxDefaultPosition, wxDefaultSize, 0 );
m_SizeYLabel->Wrap( -1 );
m_SizeYLabel->SetToolTip( _("Text height") );
gbSizer1->Add( m_SizeYLabel, wxGBPosition( 1, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 4 );
m_SizeYCtrl = new wxTextCtrl( m_cellPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
gbSizer1->Add( m_SizeYCtrl, wxGBPosition( 1, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_SizeYUnits = new wxStaticText( m_cellPage, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_SizeYUnits->Wrap( -1 );
gbSizer1->Add( m_SizeYUnits, wxGBPosition( 1, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
m_ThicknessLabel = new wxStaticText( m_cellPage, wxID_ANY, _("Thickness:"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThicknessLabel->Wrap( -1 );
m_ThicknessLabel->SetToolTip( _("Text thickness") );
gbSizer1->Add( m_ThicknessLabel, wxGBPosition( 2, 0 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 4 );
m_ThicknessCtrl = new wxTextCtrl( m_cellPage, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_PROCESS_ENTER );
gbSizer1->Add( m_ThicknessCtrl, wxGBPosition( 2, 1 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL|wxEXPAND, 5 );
m_ThicknessUnits = new wxStaticText( m_cellPage, wxID_ANY, _("unit"), wxDefaultPosition, wxDefaultSize, 0 );
m_ThicknessUnits->Wrap( -1 );
gbSizer1->Add( m_ThicknessUnits, wxGBPosition( 2, 2 ), wxGBSpan( 1, 1 ), wxALIGN_CENTER_VERTICAL, 5 );
bMargins->Add( gbSizer1, 1, wxEXPAND|wxTOP, 5 );
bSizer13->Add( bMargins, 1, wxEXPAND|wxALL, 5 );
m_cellPage->SetSizer( bSizer13 );
m_cellPage->Layout();
bSizer13->Fit( m_cellPage );
m_notebook->AddPage( m_cellPage, _("Cell"), true );
bColumns->Add( m_notebook, 0, wxEXPAND|wxBOTTOM|wxLEFT, 10 );
bMainSizer->Add( bColumns, 1, wxEXPAND|wxRIGHT|wxLEFT, 10 );
wxBoxSizer* bButtons;
bButtons = new wxBoxSizer( wxHORIZONTAL );
bButtons->Add( 0, 0, 1, wxEXPAND, 5 );
m_applyButton = new wxButton( this, wxID_ANY, _("Apply && Go to Next Cell"), wxDefaultPosition, wxDefaultSize, 0 );
bButtons->Add( m_applyButton, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 5 );
m_hotkeyHint = new wxStaticText( this, wxID_ANY, _("(Option+Tab)"), wxDefaultPosition, wxDefaultSize, 0 );
m_hotkeyHint->Wrap( -1 );
bButtons->Add( m_hotkeyHint, 0, wxALIGN_CENTER_VERTICAL|wxRIGHT, 10 );
m_sdbSizer1 = new wxStdDialogButtonSizer();
m_sdbSizer1OK = new wxButton( this, wxID_OK );
m_sdbSizer1->AddButton( m_sdbSizer1OK );
m_sdbSizer1Cancel = new wxButton( this, wxID_CANCEL );
m_sdbSizer1->AddButton( m_sdbSizer1Cancel );
m_sdbSizer1->Realize();
bButtons->Add( m_sdbSizer1, 0, wxEXPAND|wxALL, 5 );
bMainSizer->Add( bButtons, 0, wxEXPAND, 5 );
this->SetSizer( bMainSizer );
this->Layout();
bMainSizer->Fit( this );
// Connect Events
m_textCtrl->Connect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onMultiLineTCLostFocus ), NULL, this );
m_borderCheckbox->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_rowSeparators->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_colSeparators->Connect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_SizeXCtrl->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnOkClick ), NULL, this );
m_SizeYCtrl->Connect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnOkClick ), NULL, this );
m_ThicknessCtrl->Connect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onThickness ), NULL, this );
m_applyButton->Connect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnApply ), NULL, this );
}
DIALOG_TABLECELL_PROPERTIES_BASE::~DIALOG_TABLECELL_PROPERTIES_BASE()
{
// Disconnect Events
m_textCtrl->Disconnect( wxEVT_KILL_FOCUS, wxFocusEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onMultiLineTCLostFocus ), NULL, this );
m_borderCheckbox->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_rowSeparators->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_colSeparators->Disconnect( wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onBorderChecked ), NULL, this );
m_SizeXCtrl->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnOkClick ), NULL, this );
m_SizeYCtrl->Disconnect( wxEVT_COMMAND_TEXT_ENTER, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnOkClick ), NULL, this );
m_ThicknessCtrl->Disconnect( wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::onThickness ), NULL, this );
m_applyButton->Disconnect( wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler( DIALOG_TABLECELL_PROPERTIES_BASE::OnApply ), NULL, this );
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,119 @@
///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.0.0-0-g0efcecf)
// http://www.wxformbuilder.org/
//
// PLEASE DO *NOT* EDIT THIS FILE!
///////////////////////////////////////////////////////////////////////////
#pragma once
#include <wx/artprov.h>
#include <wx/xrc/xmlres.h>
#include <wx/intl.h>
class BITMAP_BUTTON;
class FONT_CHOICE;
class PCB_LAYER_BOX_SELECTOR;
class WX_INFOBAR;
#include "dialog_shim.h"
#include <wx/infobar.h>
#include <wx/gdicmn.h>
#include <wx/font.h>
#include <wx/colour.h>
#include <wx/settings.h>
#include <wx/string.h>
#include <wx/stc/stc.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/bmpcbox.h>
#include <wx/checkbox.h>
#include <wx/textctrl.h>
#include <wx/gbsizer.h>
#include <wx/panel.h>
#include <wx/bitmap.h>
#include <wx/image.h>
#include <wx/icon.h>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/notebook.h>
#include <wx/dialog.h>
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
/// Class DIALOG_TABLECELL_PROPERTIES_BASE
///////////////////////////////////////////////////////////////////////////////
class DIALOG_TABLECELL_PROPERTIES_BASE : public DIALOG_SHIM
{
private:
protected:
WX_INFOBAR* m_infoBar;
wxStyledTextCtrl* m_textCtrl;
wxNotebook* m_notebook;
wxPanel* m_tablePage;
wxGridBagSizer* m_textEntrySizer;
wxStaticText* m_layerLabel;
PCB_LAYER_BOX_SELECTOR* m_LayerSelectionCtrl;
wxCheckBox* m_cbLocked;
wxCheckBox* m_borderCheckbox;
wxCheckBox* m_headerBorder;
wxStaticText* m_borderWidthLabel;
wxTextCtrl* m_borderWidthCtrl;
wxStaticText* m_borderWidthUnits;
wxStaticText* m_borderStyleLabel;
wxBitmapComboBox* m_borderStyleCombo;
wxCheckBox* m_rowSeparators;
wxCheckBox* m_colSeparators;
wxStaticText* m_separatorsWidthLabel;
wxTextCtrl* m_separatorsWidthCtrl;
wxStaticText* m_separatorsWidthUnits;
wxStaticText* m_separatorsStyleLabel;
wxBitmapComboBox* m_separatorsStyleCombo;
wxPanel* m_cellPage;
BITMAP_BUTTON* m_separator1;
BITMAP_BUTTON* m_bold;
BITMAP_BUTTON* m_italic;
BITMAP_BUTTON* m_separator2;
BITMAP_BUTTON* m_hAlignLeft;
BITMAP_BUTTON* m_hAlignCenter;
BITMAP_BUTTON* m_hAlignRight;
BITMAP_BUTTON* m_separator3;
BITMAP_BUTTON* m_vAlignTop;
BITMAP_BUTTON* m_vAlignCenter;
BITMAP_BUTTON* m_vAlignBottom;
BITMAP_BUTTON* m_separator4;
wxStaticText* m_fontLabel;
FONT_CHOICE* m_fontCtrl;
wxStaticText* m_SizeXLabel;
wxTextCtrl* m_SizeXCtrl;
wxStaticText* m_SizeXUnits;
wxStaticText* m_SizeYLabel;
wxTextCtrl* m_SizeYCtrl;
wxStaticText* m_SizeYUnits;
wxStaticText* m_ThicknessLabel;
wxTextCtrl* m_ThicknessCtrl;
wxStaticText* m_ThicknessUnits;
wxButton* m_applyButton;
wxStaticText* m_hotkeyHint;
wxStdDialogButtonSizer* m_sdbSizer1;
wxButton* m_sdbSizer1OK;
wxButton* m_sdbSizer1Cancel;
// Virtual event handlers, override them in your derived class
virtual void onMultiLineTCLostFocus( wxFocusEvent& event ) { event.Skip(); }
virtual void onBorderChecked( wxCommandEvent& event ) { event.Skip(); }
virtual void OnOkClick( wxCommandEvent& event ) { event.Skip(); }
virtual void onThickness( wxCommandEvent& event ) { event.Skip(); }
virtual void OnApply( wxCommandEvent& event ) { event.Skip(); }
public:
DIALOG_TABLECELL_PROPERTIES_BASE( wxWindow* parent, wxWindowID id = wxID_ANY, const wxString& title = _("Table Cell Properties"), const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxSize( -1,-1 ), long style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER );
~DIALOG_TABLECELL_PROPERTIES_BASE();
};

View File

@ -1135,6 +1135,7 @@ DRC_CONSTRAINT DRC_ENGINE::EvalRules( DRC_CONSTRAINT_T aConstraintType, const BO
case PCB_FIELD_T: mask = DRC_DISALLOW_TEXTS; break;
case PCB_TEXT_T: mask = DRC_DISALLOW_TEXTS; break;
case PCB_TEXTBOX_T: mask = DRC_DISALLOW_TEXTS; break;
case PCB_TABLE_T: mask = DRC_DISALLOW_TEXTS; break;
case PCB_ZONE_T:
// Treat teardrop areas as tracks for DRC purposes

View File

@ -38,6 +38,7 @@
#include <pcb_target.h>
#include <pcb_dimension.h>
#include <pcb_textbox.h>
#include <pcb_tablecell.h>
#include <pcb_shape.h>
#include <dialog_drc.h>
#include <connectivity/connectivity_data.h>
@ -136,6 +137,10 @@ void PCB_EDIT_FRAME::OnEditItemRequest( BOARD_ITEM* aItem )
ShowTextBoxPropertiesDialog( static_cast<PCB_TEXTBOX*>( aItem ) );
break;
case PCB_TABLECELL_T:
ShowTableCellPropertiesDialog( static_cast<PCB_TABLECELL*>( aItem ) );
break;
case PCB_PAD_T:
ShowPadPropertiesDialog( static_cast<PAD*>( aItem ) );
break;

View File

@ -23,6 +23,7 @@
#include "tools/convert_tool.h"
#include "tools/drawing_tool.h"
#include "tools/edit_tool.h"
#include "tools/pcb_edit_table_tool.h"
#include "tools/footprint_editor_control.h"
#include "tools/pad_tool.h"
#include "tools/pcb_actions.h"
@ -1119,6 +1120,7 @@ void FOOTPRINT_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new PCB_SELECTION_TOOL );
m_toolManager->RegisterTool( new ZOOM_TOOL );
m_toolManager->RegisterTool( new EDIT_TOOL );
m_toolManager->RegisterTool( new PCB_EDIT_TABLE_TOOL );
m_toolManager->RegisterTool( new PAD_TOOL );
m_toolManager->RegisterTool( new DRAWING_TOOL );
m_toolManager->RegisterTool( new PCB_POINT_EDITOR );

View File

@ -312,6 +312,7 @@ void PCB_EDIT_FRAME::doReCreateMenuBar()
placeMenu->Add( PCB_ACTIONS::placeReferenceImage );
placeMenu->Add( PCB_ACTIONS::placeText );
placeMenu->Add( PCB_ACTIONS::drawTextBox );
placeMenu->Add( PCB_ACTIONS::drawTable );
placeMenu->AppendSeparator();
ACTION_MENU* dimensionSubmenu = new ACTION_MENU( false, selTool );

View File

@ -33,6 +33,7 @@ class APPEARANCE_CONTROLS;
class BOARD_ITEM_CONTAINER;
class PANEL_SELECTION_FILTER;
class PCB_TEXTBOX;
class PCB_TABLECELL;
class PCB_TEXT;
class PCB_SHAPE;
@ -178,6 +179,7 @@ public:
void ShowReferenceImagePropertiesDialog( BOARD_ITEM* aBitmap );
void ShowTextPropertiesDialog( PCB_TEXT* aText );
int ShowTextBoxPropertiesDialog( PCB_TEXTBOX* aTextBox );
void ShowTableCellPropertiesDialog( PCB_TABLECELL* aTableCell );
void ShowGraphicItemPropertiesDialog( PCB_SHAPE* aShape );
///< @copydoc EDA_DRAW_FRAME::UseGalCanvas()

View File

@ -68,6 +68,7 @@
#include <tools/pcb_picker_tool.h>
#include <tools/pcb_point_editor.h>
#include <tools/edit_tool.h>
#include <tools/pcb_edit_table_tool.h>
#include <tools/group_tool.h>
#include <tools/generator_tool.h>
#include <tools/drc_tool.h>
@ -650,6 +651,7 @@ void PCB_EDIT_FRAME::setupTools()
m_toolManager->RegisterTool( new PCB_PICKER_TOOL );
m_toolManager->RegisterTool( new ROUTER_TOOL );
m_toolManager->RegisterTool( new EDIT_TOOL );
m_toolManager->RegisterTool( new PCB_EDIT_TABLE_TOOL );
m_toolManager->RegisterTool( new GLOBAL_EDIT_TOOL );
m_toolManager->RegisterTool( new PAD_TOOL );
m_toolManager->RegisterTool( new DRAWING_TOOL );
@ -968,6 +970,7 @@ void PCB_EDIT_FRAME::setupUIConditions()
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeReferenceImage );
CURRENT_EDIT_TOOL( PCB_ACTIONS::placeText );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTextBox );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawTable );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawAlignedDimension );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawOrthogonalDimension );
CURRENT_EDIT_TOOL( PCB_ACTIONS::drawCenterDimension );

View File

@ -57,6 +57,7 @@ bool PCB_GROUP::IsGroupableType( KICAD_T aType )
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_GROUP_T:
case PCB_GENERATOR_T:
case PCB_TRACE_T:

View File

@ -43,6 +43,8 @@
#include <pcb_target.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_tablecell.h>
#include <pcb_table.h>
#include <pcb_track.h>
#include <zone.h>
#include <pcbnew_settings.h>
@ -406,6 +408,10 @@ void PCB_IO_KICAD_SEXPR::Format( const BOARD_ITEM* aItem, int aNestLevel ) const
format( static_cast<const PCB_TEXTBOX*>( aItem ), aNestLevel );
break;
case PCB_TABLE_T:
format( static_cast<const PCB_TABLE*>( aItem ), aNestLevel );
break;
case PCB_GROUP_T:
format( static_cast<const PCB_GROUP*>( aItem ), aNestLevel );
break;
@ -1448,7 +1454,7 @@ void PCB_IO_KICAD_SEXPR::format( const PAD* aPad, int aNestLevel ) const
switch( aPad->GetShape() )
{
case PAD_SHAPE::CIRCLE: shape = "circle"; break;
case PAD_SHAPE::RECTANGLE: shape = "rect"; break;
case PAD_SHAPE::RECTANGLE: shape = "rect"; break;
case PAD_SHAPE::OVAL: shape = "oval"; break;
case PAD_SHAPE::TRAPEZOID: shape = "trapezoid"; break;
case PAD_SHAPE::CHAMFERED_RECT:
@ -1887,7 +1893,9 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) c
FOOTPRINT* parentFP = aTextBox->GetParentFootprint();
m_out->Print( aNestLevel, "(%s %s\n",
parentFP ? "fp_text_box" : "gr_text_box",
aTextBox->Type() == PCB_TABLECELL_T ? "table_cell"
: parentFP ? "fp_text_box"
: "gr_text_box",
m_out->Quotew( aTextBox->GetText() ).c_str() );
if( aTextBox->IsLocked() )
@ -1911,6 +1919,9 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) c
UNIMPLEMENTED_FOR( aTextBox->SHAPE_T_asString() );
}
if( const PCB_TABLECELL* cell = dynamic_cast<const PCB_TABLECELL*>( aTextBox ) )
m_out->Print( 0, " (span %d %d)", cell->GetColSpan(), cell->GetRowSpan() );
EDA_ANGLE angle = aTextBox->GetTextAngle();
if( parentFP )
@ -1930,9 +1941,12 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) c
// PCB_TEXTBOXes are never hidden, so always omit "hide" attribute
aTextBox->EDA_TEXT::Format( m_out, aNestLevel, m_ctl | CTL_OMIT_HIDE );
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "border", aTextBox->IsBorderEnabled() );
if( aTextBox->Type() != PCB_TABLECELL_T )
{
KICAD_FORMAT::FormatBool( m_out, aNestLevel + 1, "border", aTextBox->IsBorderEnabled() );
aTextBox->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
aTextBox->GetStroke().Format( m_out, pcbIUScale, aNestLevel + 1 );
}
if( aTextBox->GetFont() && aTextBox->GetFont()->IsOutline() )
formatRenderCache( aTextBox, aNestLevel + 1 );
@ -1941,6 +1955,66 @@ void PCB_IO_KICAD_SEXPR::format( const PCB_TEXTBOX* aTextBox, int aNestLevel ) c
}
void PCB_IO_KICAD_SEXPR::format( const PCB_TABLE* aTable, int aNestLevel ) const
{
wxCHECK_RET( aTable != nullptr && m_out != nullptr, "" );
m_out->Print( aNestLevel, "(table (column_count %d)\n",
aTable->GetColCount() );
if( aTable->IsLocked() )
KICAD_FORMAT::FormatBool( m_out, aNestLevel, "locked", aTable->IsLocked() );
formatLayer( aTable->GetLayer() );
m_out->Print( aNestLevel + 1, "(border (external %s) (header %s)",
aTable->StrokeExternal() ? "yes" : "no",
aTable->StrokeHeader() ? "yes" : "no" );
if( aTable->StrokeExternal() || aTable->StrokeHeader() )
{
m_out->Print( 0, " " );
aTable->GetBorderStroke().Format( m_out, pcbIUScale, 0 );
}
m_out->Print( 0, ")\n" );
m_out->Print( aNestLevel + 1, "(separators (rows %s) (cols %s)",
aTable->StrokeRows() ? "yes" : "no",
aTable->StrokeColumns() ? "yes" : "no" );
if( aTable->StrokeRows() || aTable->StrokeColumns() )
{
m_out->Print( 0, " " );
aTable->GetSeparatorsStroke().Format( m_out, pcbIUScale, 0 );
}
m_out->Print( 0, ")\n" ); // Close `separators` token.
m_out->Print( aNestLevel + 1, "(column_widths" );
for( int col = 0; col < aTable->GetColCount(); ++col )
m_out->Print( 0, " %s", formatInternalUnits( aTable->GetColWidth( col ) ).c_str() );
m_out->Print( 0, ")\n" );
m_out->Print( aNestLevel + 1, "(row_heights" );
for( int row = 0; row < aTable->GetRowCount(); ++row )
m_out->Print( 0, " %s", formatInternalUnits( aTable->GetRowHeight( row ) ).c_str() );
m_out->Print( 0, ")\n" );
m_out->Print( aNestLevel + 1, "(cells\n" );
for( PCB_TABLECELL* cell : aTable->GetCells() )
format( static_cast<PCB_TEXTBOX*>( cell ), aNestLevel + 2 );
m_out->Print( aNestLevel + 1, ")\n" ); // Close `cells` token.
m_out->Print( aNestLevel, ")\n" ); // Close `table` token.
}
void PCB_IO_KICAD_SEXPR::format( const PCB_GROUP* aGroup, int aNestLevel ) const
{
// Don't write empty groups

View File

@ -52,6 +52,7 @@ class PCB_TRACK;
class ZONE;
class PCB_TEXT;
class PCB_TEXTBOX;
class PCB_TABLE;
class EDA_TEXT;
class SHAPE_LINE_CHAIN;
class TEARDROP_PARAMETERS;
@ -148,9 +149,10 @@ class PCB_IO_KICAD_SEXPR; // forward decl
//#define SEXPR_BOARD_FILE_VERSION 20231014 // V8 file format normalization
//#define SEXPR_BOARD_FILE_VERSION 20231212 // Reference image locking/UUIDs, footprint boolean format
//#define SEXPR_BOARD_FILE_VERSION 20231231 // Use 'uuid' rather than 'id' for generators and groups
//#define SEXPR_BOARD_FILE_VERSION 20240108 // Convert teardrop parameters to explicit bools
//#define SEXPR_BOARD_FILE_VERSION 20240108 // Convert teardrop parameters to explicit bools
//----------------- Start of 9.0 development -----------------
#define SEXPR_BOARD_FILE_VERSION 20240201 // Use nullable properties for overrides
//define SEXPR_BOARD_FILE_VERSION 20240201 // Use nullable properties for overrides
#define SEXPR_BOARD_FILE_VERSION 20240202 // Tables
#define BOARD_FILE_HOST_VERSION 20200825 ///< Earlier files than this include the host tag
#define LEGACY_ARC_FORMATTING 20210925 ///< These were the last to use old arc formatting
@ -159,7 +161,7 @@ class PCB_IO_KICAD_SEXPR; // forward decl
#define FIRST_NORMALIZED_VERISON 20230924 ///< Earlier files did not have normalized bools
#define CTL_OMIT_PAD_NETS (1 << 1) ///< Omit pads net names (useless in library)
#define CTL_OMIT_UUIDS (1 << 2) ///< Omit component unique ids (useless in library)
#define CTL_OMIT_UUIDS (1 << 2) ///< Omit component unique ids (useless in library)
#define CTL_OMIT_INITIAL_COMMENTS (1 << 3) ///< omit FOOTPRINT initial comments
#define CTL_OMIT_PATH (1 << 4) ///< Omit component sheet time stamp (useless in library)
#define CTL_OMIT_AT (1 << 5) ///< Omit position and rotation. (always saved
@ -425,6 +427,8 @@ private:
void format( const PCB_TEXT* aText, int aNestLevel = 0 ) const;
void format( const PCB_TEXTBOX* aTextBox, int aNestLevel = 0 ) const;
void format( const PCB_TABLE* aTable, int aNestLevel = 0 ) const;
void format( const PCB_GENERATOR* aGenerator, int aNestLevel = 0 ) const;
void format( const PCB_TRACK* aTrack, int aNestLevel = 0 ) const;

View File

@ -45,6 +45,7 @@
#include <pcb_target.h>
#include <pcb_track.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pad.h>
#include <generators_mgr.h>
#include <zone.h>
@ -1006,6 +1007,12 @@ BOARD* PCB_IO_KICAD_SEXPR_PARSER::parseBOARD_unchecked()
bulkAddedItems.push_back( item );
break;
case T_table:
item = parsePCB_TABLE( m_board );
m_board->Add( item, ADD_MODE::BULK_APPEND, true );
bulkAddedItems.push_back( item );
break;
case T_dimension:
item = parseDIMENSION( m_board );
m_board->Add( item, ADD_MODE::BULK_APPEND, true );
@ -3333,19 +3340,40 @@ PCB_TEXTBOX* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TEXTBOX( BOARD_ITEM* aParent )
std::unique_ptr<PCB_TEXTBOX> textbox = std::make_unique<PCB_TEXTBOX>( aParent );
parseTextBoxContent( textbox.get() );
return textbox.release();
}
PCB_TABLECELL* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TABLECELL( BOARD_ITEM* aParent )
{
wxCHECK_MSG( CurTok() == T_table_cell, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table cell." ) );
std::unique_ptr<PCB_TABLECELL> cell = std::make_unique<PCB_TABLECELL>( aParent );
parseTextBoxContent( cell.get() );
return cell.release();
}
void PCB_IO_KICAD_SEXPR_PARSER::parseTextBoxContent( PCB_TEXTBOX* aTextBox )
{
STROKE_PARAMS stroke( -1, LINE_STYLE::SOLID );
T token = NextTok();
if( token == T_locked )
{
textbox->SetLocked( true );
aTextBox->SetLocked( true );
token = NextTok();
}
if( !IsSymbol( token ) && (int) token != DSN_NUMBER )
Expecting( "text value" );
textbox->SetText( FromUTF8() );
aTextBox->SetText( FromUTF8() );
NeedLEFT();
token = NextTok();
@ -3354,7 +3382,7 @@ PCB_TEXTBOX* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TEXTBOX( BOARD_ITEM* aParent )
{
int x = parseBoardUnits( "X coordinate" );
int y = parseBoardUnits( "Y coordinate" );
textbox->SetStart( VECTOR2I( x, y ) );
aTextBox->SetStart( VECTOR2I( x, y ) );
NeedRIGHT();
NeedLEFT();
@ -3365,17 +3393,17 @@ PCB_TEXTBOX* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TEXTBOX( BOARD_ITEM* aParent )
x = parseBoardUnits( "X coordinate" );
y = parseBoardUnits( "Y coordinate" );
textbox->SetEnd( VECTOR2I( x, y ) );
aTextBox->SetEnd( VECTOR2I( x, y ) );
NeedRIGHT();
}
else if( token == T_pts )
{
textbox->SetShape( SHAPE_T::POLY );
textbox->GetPolyShape().RemoveAllContours();
textbox->GetPolyShape().NewOutline();
aTextBox->SetShape( SHAPE_T::POLY );
aTextBox->GetPolyShape().RemoveAllContours();
aTextBox->GetPolyShape().NewOutline();
while( (token = NextTok() ) != T_RIGHT )
parseOutlinePoints( textbox->GetPolyShape().Outline( 0 ) );
parseOutlinePoints( aTextBox->GetPolyShape().Outline( 0 ) );
}
else
{
@ -3394,7 +3422,7 @@ PCB_TEXTBOX* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TEXTBOX( BOARD_ITEM* aParent )
case T_angle:
// Set the angle of the text only, the coordinates of the box (a polygon) are
// already at the right position, and must not be rotated
textbox->EDA_TEXT::SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
aTextBox->EDA_TEXT::SetTextAngle( EDA_ANGLE( parseDouble( "text box angle" ), DEGREES_T ) );
NeedRIGHT();
break;
@ -3409,48 +3437,224 @@ PCB_TEXTBOX* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TEXTBOX( BOARD_ITEM* aParent )
}
case T_border:
textbox->SetBorderEnabled( parseBool() );
aTextBox->SetBorderEnabled( parseBool() );
NeedRIGHT();
break;
case T_layer:
textbox->SetLayer( parseBoardItemLayer() );
aTextBox->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_span:
if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
{
cell->SetColSpan( parseInt( "column span" ) );
cell->SetRowSpan( parseInt( "row span" ) );
}
else
{
Expecting( "angle, width, layer, effects, render_cache, uuid or tstamp" );
}
NeedRIGHT();
break;
case T_tstamp:
case T_uuid:
NextTok();
const_cast<KIID&>( textbox->m_Uuid ) = CurStrToKIID();
const_cast<KIID&>( aTextBox->m_Uuid ) = CurStrToKIID();
NeedRIGHT();
break;
case T_effects:
parseEDA_TEXT( static_cast<EDA_TEXT*>( textbox.get() ) );
parseEDA_TEXT( static_cast<EDA_TEXT*>( aTextBox ) );
break;
case T_render_cache:
parseRenderCache( static_cast<EDA_TEXT*>( textbox.get() ) );
parseRenderCache( static_cast<EDA_TEXT*>( aTextBox ) );
break;
default:
Expecting( "angle, width, layer, effects, render_cache, uuid or tstamp" );
if( PCB_TABLECELL* cell = dynamic_cast<PCB_TABLECELL*>( aTextBox ) )
Expecting( "angle, width, layer, effects, span, render_cache, uuid or tstamp" );
else
Expecting( "angle, width, layer, effects, render_cache, uuid or tstamp" );
}
}
textbox->SetStroke( stroke );
aTextBox->SetStroke( stroke );
if( m_requiredVersion < 20230825 ) // compat, we move to an explicit flag
textbox->SetBorderEnabled( stroke.GetWidth() >= 0 );
aTextBox->SetBorderEnabled( stroke.GetWidth() >= 0 );
if( FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aParent ) )
if( FOOTPRINT* parentFP = dynamic_cast<FOOTPRINT*>( aTextBox->GetParent() ) )
{
textbox->Rotate( { 0, 0 }, parentFP->GetOrientation() );
textbox->Move( parentFP->GetPosition() );
aTextBox->Rotate( { 0, 0 }, parentFP->GetOrientation() );
aTextBox->Move( parentFP->GetPosition() );
}
}
PCB_TABLE* PCB_IO_KICAD_SEXPR_PARSER::parsePCB_TABLE( BOARD_ITEM* aParent )
{
wxCHECK_MSG( CurTok() == T_table, nullptr,
wxT( "Cannot parse " ) + GetTokenString( CurTok() ) + wxT( " as a table." ) );
T token;
STROKE_PARAMS borderStroke( -1, LINE_STYLE::SOLID );
STROKE_PARAMS separatorsStroke( -1, LINE_STYLE::SOLID );
std::unique_ptr<PCB_TABLE> table = std::make_unique<PCB_TABLE>( aParent, -1 );
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token == T_locked )
{
table->SetLocked( true );
token = NextTok();
}
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_column_count:
table->SetColCount( parseInt( "column count" ) );
NeedRIGHT();
break;
case T_layer:
table->SetLayer( parseBoardItemLayer() );
NeedRIGHT();
break;
case T_column_widths:
{
int col = 0;
while( ( token = NextTok() ) != T_RIGHT )
table->SetColWidth( col++, parseBoardUnits() );
break;
}
case T_row_heights:
{
int row = 0;
while( ( token = NextTok() ) != T_RIGHT )
table->SetRowHeight( row++, parseBoardUnits() );
break;
}
case T_cells:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
if( token != T_table_cell )
Expecting( "table_cell" );
table->AddCell( parsePCB_TABLECELL( table.get() ) );
}
break;
case T_border:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_external:
table->SetStrokeExternal( parseBool() );
NeedRIGHT();
break;
case T_header:
table->SetStrokeHeader( parseBool() );
NeedRIGHT();
break;
case T_stroke:
{
STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
strokeParser.SyncLineReaderWith( *this );
strokeParser.ParseStroke( borderStroke );
SyncLineReaderWith( strokeParser );
table->SetBorderStroke( borderStroke );
break;
}
default:
Expecting( "external, header or stroke" );
break;
}
}
break;
case T_separators:
for( token = NextTok(); token != T_RIGHT; token = NextTok() )
{
if( token != T_LEFT )
Expecting( T_LEFT );
token = NextTok();
switch( token )
{
case T_rows:
table->SetStrokeRows( parseBool() );
NeedRIGHT();
break;
case T_cols:
table->SetStrokeColumns( parseBool() );
NeedRIGHT();
break;
case T_stroke:
{
STROKE_PARAMS_PARSER strokeParser( reader, pcbIUScale.IU_PER_MM );
strokeParser.SyncLineReaderWith( *this );
strokeParser.ParseStroke( separatorsStroke );
SyncLineReaderWith( strokeParser );
table->SetSeparatorsStroke( separatorsStroke );
break;
}
default:
Expecting( "rows, cols, or stroke" );
break;
}
}
break;
default:
Expecting( "columns, layer, col_widths, row_heights, border, separators, header or "
"cells" );
}
}
return textbox.release();
return table.release();
}
@ -4324,6 +4528,13 @@ FOOTPRINT* PCB_IO_KICAD_SEXPR_PARSER::parseFOOTPRINT_unchecked( wxArrayString* a
break;
}
case T_table:
{
PCB_TABLE* table = parsePCB_TABLE( footprint.get() );
footprint->Add( table, ADD_MODE::APPEND, true );
break;
}
case T_fp_arc:
case T_fp_circle:
case T_fp_curve:

View File

@ -52,9 +52,10 @@ class PCB_DIMENSION_BASE;
class PCB_SHAPE;
class PCB_REFERENCE_IMAGE;
class EDA_TEXT;
class FP_SHAPE;
class PCB_TEXT;
class PCB_TRACK;
class PCB_TABLE;
class PCB_TABLECELL;
class FOOTPRINT;
class PCB_GROUP;
class PCB_TARGET;
@ -207,11 +208,15 @@ private:
void parseTEARDROP_PARAMETERS( TEARDROP_PARAMETERS* tdParams );
void parseTextBoxContent( PCB_TEXTBOX* aTextBox );
PCB_SHAPE* parsePCB_SHAPE( BOARD_ITEM* aParent );
PCB_TEXT* parsePCB_TEXT( BOARD_ITEM* aParent );
void parsePCB_TEXT_effects( PCB_TEXT* aText );
PCB_REFERENCE_IMAGE* parsePCB_REFERENCE_IMAGE( BOARD_ITEM* aParent );
PCB_TEXTBOX* parsePCB_TEXTBOX( BOARD_ITEM* aParent );
PCB_TABLECELL* parsePCB_TABLECELL( BOARD_ITEM* aParent );
PCB_TABLE* parsePCB_TABLE( BOARD_ITEM* aParent );
PCB_DIMENSION_BASE* parseDIMENSION( BOARD_ITEM* aParent );
// Parse a footprint, but do not replace PARSE_ERROR with FUTURE_FORMAT_ERROR automatically.

View File

@ -37,6 +37,8 @@
#include <pcb_reference_image.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_tablecell.h>
#include <pcb_marker.h>
#include <pcb_dimension.h>
#include <pcb_target.h>
@ -613,6 +615,10 @@ bool PCB_PAINTER::Draw( const VIEW_ITEM* aItem, int aLayer )
draw( static_cast<const PCB_TEXTBOX*>( item ), aLayer );
break;
case PCB_TABLE_T:
draw( static_cast<const PCB_TABLE*>( item ), aLayer );
break;
case PCB_FOOTPRINT_T:
draw( static_cast<const FOOTPRINT*>( item ), aLayer );
break;
@ -2132,10 +2138,18 @@ void PCB_PAINTER::draw( const PCB_TEXT* aText, int aLayer )
void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
{
const COLOR4D& color = m_pcbSettings.GetColor( aTextBox, aLayer );
int thickness = getLineThickness( aTextBox->GetWidth() );
LINE_STYLE lineStyle = aTextBox->GetStroke().GetLineStyle();
wxString resolvedText( aTextBox->GetShownText( true ) );
if( aTextBox->Type() == PCB_TABLECELL_T )
{
const PCB_TABLECELL* cell = static_cast<const PCB_TABLECELL*>( aTextBox );
if( cell->GetColSpan() == 0 || cell->GetRowSpan() == 0 )
return;
}
COLOR4D color = m_pcbSettings.GetColor( aTextBox, aLayer );
int thickness = getLineThickness( aTextBox->GetWidth() );
LINE_STYLE lineStyle = aTextBox->GetStroke().GetLineStyle();
wxString resolvedText( aTextBox->GetShownText( true ) );
KIFONT::FONT* font = aTextBox->GetFont();
@ -2171,6 +2185,13 @@ void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
m_gal->DrawPolygon( dpts );
}
if( aTextBox->Type() == PCB_TABLECELL_T )
{
// Selection for tables is done with a background wash, so pass in nullptr to GetColor()
// so we just get the "normal" (un-selected/un-brightened) color for the borders.
color = m_pcbSettings.GetColor( nullptr, aLayer );
}
m_gal->SetFillColor( color );
m_gal->SetStrokeColor( color );
m_gal->SetIsFill( true );
@ -2224,14 +2245,14 @@ void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
// so the text drawn on LAYER_LOCKED_ITEM_SHADOW with a thick width is disabled
// If enabled, the thick text position must be offsetted to be exactly on the
// initial text, which is not easy, depending on its rotation and justification.
#if 0
#if 0
const COLOR4D sh_color = m_pcbSettings.GetColor( aTextBox, aLayer );
m_gal->SetFillColor( sh_color );
m_gal->SetStrokeColor( sh_color );
attrs.m_StrokeWidth += m_lockedShadowMargin;
#else
#else
return;
#endif
#endif
}
std::vector<std::unique_ptr<KIFONT::GLYPH>>* cache = nullptr;
@ -2251,6 +2272,146 @@ void PCB_PAINTER::draw( const PCB_TEXTBOX* aTextBox, int aLayer )
}
void PCB_PAINTER::draw( const PCB_TABLE* aTable, int aLayer )
{
for( PCB_TABLECELL* cell : aTable->GetCells() )
draw( static_cast<PCB_TEXTBOX*>( cell ), aLayer );
VECTOR2I pos = aTable->GetPosition();
VECTOR2I end = aTable->GetEnd();
// Selection for tables is done with a background wash, so pass in nullptr to GetColor()
// so we just get the "normal" (un-selected/un-brightened) color for the borders.
COLOR4D color = m_pcbSettings.GetColor( nullptr, aLayer );
int lineWidth;
LINE_STYLE lineStyle;
auto setupStroke =
[&]( const STROKE_PARAMS& stroke )
{
lineWidth = getLineThickness( stroke.GetWidth() );
lineStyle = stroke.GetLineStyle();
m_gal->SetIsFill( false );
m_gal->SetIsStroke( true );
m_gal->SetStrokeColor( color );
m_gal->SetLineWidth( lineWidth );
};
auto strokeShape =
[&]( const SHAPE& shape )
{
STROKE_PARAMS::Stroke( &shape, lineStyle, lineWidth, &m_pcbSettings,
[&]( const VECTOR2I& a, const VECTOR2I& b )
{
// DrawLine has problem with 0 length lines so enforce minimum
if( a == b )
m_gal->DrawLine( a+1, b );
else
m_gal->DrawLine( a, b );
} );
};
auto strokeLine =
[&]( const VECTOR2I& ptA, const VECTOR2I& ptB )
{
if( lineStyle <= LINE_STYLE::FIRST_TYPE )
{
m_gal->DrawLine( ptA, ptB );
}
else
{
SHAPE_SEGMENT seg( ptA, ptB );
strokeShape( seg );
}
};
auto strokeRect =
[&]( const VECTOR2I& ptA, const VECTOR2I& ptB )
{
if( lineStyle <= LINE_STYLE::FIRST_TYPE )
{
m_gal->DrawRectangle( ptA, ptB );
}
else
{
SHAPE_RECT rect( BOX2I( ptA, ptB - ptA ) );
strokeShape( rect );
}
};
if( aTable->GetSeparatorsStroke().GetWidth() >= 0 )
{
setupStroke( aTable->GetSeparatorsStroke() );
if( aTable->StrokeColumns() )
{
for( int col = 0; col < aTable->GetColCount() - 1; ++col )
{
for( int row = 0; row < aTable->GetRowCount(); ++row )
{
PCB_TABLECELL* cell = aTable->GetCell( row, col );
if( cell->GetColSpan() > 0 && cell->GetRowSpan() > 0 )
{
strokeLine( VECTOR2I( cell->GetEndX(), cell->GetStartY() ),
VECTOR2I( cell->GetEndX(), cell->GetEndY() ) );
}
}
}
}
if( aTable->StrokeRows() )
{
for( int row = 0; row < aTable->GetRowCount() - 1; ++row )
{
for( int col = 0; col < aTable->GetColCount(); ++col )
{
PCB_TABLECELL* cell = aTable->GetCell( row, 0 );
if( cell->GetColSpan() > 0 && cell->GetRowSpan() > 0 )
{
strokeLine( VECTOR2I( cell->GetStartX(), cell->GetEndY() ),
VECTOR2I( cell->GetEndX(), cell->GetEndY() ) );
}
}
}
}
}
if( aTable->GetBorderStroke().GetWidth() >= 0 )
{
setupStroke( aTable->GetBorderStroke() );
if( aTable->StrokeHeader() )
{
PCB_TABLECELL* cell = aTable->GetCell( 0, 0 );
strokeLine( VECTOR2I( pos.x, cell->GetEndY() ), VECTOR2I( end.x, cell->GetEndY() ) );
}
if( aTable->StrokeExternal() )
strokeRect( pos, end );
}
// Highlight selected tablecells with a background wash.
for( PCB_TABLECELL* cell : aTable->GetCells() )
{
if( cell->IsSelected() )
{
std::vector<VECTOR2I> corners = cell->GetCorners();
std::deque<VECTOR2D> pts;
pts.insert( pts.end(), corners.begin(), corners.end() );
m_gal->SetFillColor( color.WithAlpha( 0.5 ) );
m_gal->SetIsFill( true );
m_gal->SetIsStroke( false );
m_gal->DrawPolygon( pts );
}
}
}
void PCB_PAINTER::draw( const FOOTPRINT* aFootprint, int aLayer )
{
if( aLayer == LAYER_ANCHOR )

View File

@ -52,6 +52,7 @@ class ZONE;
class PCB_REFERENCE_IMAGE;
class PCB_TEXT;
class PCB_TEXTBOX;
class PCB_TABLE;
class PCB_DIMENSION_BASE;
class PCB_TARGET;
class PCB_MARKER;
@ -185,6 +186,7 @@ protected:
void draw( const PCB_REFERENCE_IMAGE* aBitmap, int aLayer );
void draw( const PCB_TEXT* aText, int aLayer );
void draw( const PCB_TEXTBOX* aText, int aLayer );
void draw( const PCB_TABLE* aTable, int aLayer );
void draw( const FOOTPRINT* aFootprint, int aLayer );
void draw( const PCB_GROUP* aGroup, int aLayer );
void draw( const ZONE* aZone, int aLayer );

450
pcbnew/pcb_table.cpp Normal file
View File

@ -0,0 +1,450 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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 <pcb_edit_frame.h>
#include <pcb_table.h>
PCB_TABLE::PCB_TABLE( BOARD_ITEM* aParent, int aLineWidth ) :
BOARD_ITEM_CONTAINER( aParent, PCB_TABLE_T ),
m_strokeExternal( true ),
m_strokeHeader( true ),
m_borderStroke( aLineWidth, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
m_strokeRows( true ),
m_strokeColumns( true ),
m_separatorsStroke( aLineWidth, LINE_STYLE::DEFAULT, COLOR4D::UNSPECIFIED ),
m_colCount( 0 )
{
}
PCB_TABLE::PCB_TABLE( const PCB_TABLE& aTable ) :
BOARD_ITEM_CONTAINER( aTable )
{
m_strokeExternal = aTable.m_strokeExternal;
m_strokeHeader = aTable.m_strokeHeader;
m_borderStroke = aTable.m_borderStroke;
m_strokeRows = aTable.m_strokeRows;
m_strokeColumns = aTable.m_strokeColumns;
m_separatorsStroke = aTable.m_separatorsStroke;
m_colCount = aTable.m_colCount;
m_colWidths = aTable.m_colWidths;
m_rowHeights = aTable.m_rowHeights;
for( PCB_TABLECELL* src : aTable.m_cells )
AddCell( new PCB_TABLECELL( *src ) );
}
PCB_TABLE::~PCB_TABLE()
{
// We own our cells; delete them
for( PCB_TABLECELL* cell : m_cells )
delete cell;
}
void PCB_TABLE::swapData( BOARD_ITEM* aImage )
{
wxCHECK_RET( aImage != nullptr && aImage->Type() == PCB_TABLE_T,
wxT( "Cannot swap data with invalid table." ) );
PCB_TABLE* table = static_cast<PCB_TABLE*>( aImage );
std::swap( m_strokeExternal, table->m_strokeExternal );
std::swap( m_strokeHeader, table->m_strokeHeader );
std::swap( m_borderStroke, table->m_borderStroke );
std::swap( m_strokeRows, table->m_strokeRows );
std::swap( m_strokeColumns, table->m_strokeColumns );
std::swap( m_separatorsStroke, table->m_separatorsStroke );
std::swap( m_colCount, table->m_colCount );
std::swap( m_colWidths, table->m_colWidths );
std::swap( m_rowHeights, table->m_rowHeights );
std::swap( m_cells, table->m_cells );
for( PCB_TABLECELL* cell : m_cells )
cell->SetParent( this );
for( PCB_TABLECELL* cell : table->m_cells )
cell->SetParent( table );
}
void PCB_TABLE::SetPosition( const VECTOR2I& aPos )
{
Move( aPos - GetPosition() );
}
VECTOR2I PCB_TABLE::GetPosition() const
{
return m_cells[0]->GetPosition();
}
VECTOR2I PCB_TABLE::GetEnd() const
{
VECTOR2I tableSize;
for( int ii = 0; ii < GetColCount(); ++ii )
tableSize.x += GetColWidth( ii );
for( int ii = 0; ii < GetRowCount(); ++ii )
tableSize.y += GetRowHeight( ii );
return GetPosition() + tableSize;
}
void PCB_TABLE::Normalize()
{
// JEY TODO: pukes on rotated tables...
int y = GetPosition().y;
for( int row = 0; row < GetRowCount(); ++row )
{
int x = GetPosition().x;
int rowHeight = m_rowHeights[ row ];
for( int col = 0; col < GetColCount(); ++col )
{
int colWidth = m_colWidths[ col ];
PCB_TABLECELL* cell = GetCell( row, col );
VECTOR2I pos( x, y );
if( cell->GetPosition() != pos )
{
cell->SetPosition( pos );
cell->ClearRenderCache();
}
VECTOR2I end = cell->GetStart() + VECTOR2I( colWidth, rowHeight );
if( cell->GetColSpan() > 1 || cell->GetRowSpan() > 1 )
{
for( int ii = col + 1; ii < col + cell->GetColSpan(); ++ii )
end.x += m_colWidths[ii];
for( int ii = row + 1; ii < row + cell->GetRowSpan(); ++ii )
end.y += m_rowHeights[ii];
}
if( cell->GetEnd() != end )
{
cell->SetEnd( end );
cell->ClearRenderCache();
}
x += colWidth;
}
y += rowHeight;
}
}
void PCB_TABLE::Move( const VECTOR2I& aMoveVector )
{
for( PCB_TABLECELL* cell : m_cells )
cell->Move( aMoveVector );
}
void PCB_TABLE::Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle )
{
for( PCB_TABLECELL* cell : m_cells )
cell->Rotate( aRotCentre, aAngle );
Normalize();
}
void PCB_TABLE::Flip( const VECTOR2I& aCentre, bool aFlipLeftRight )
{
for( PCB_TABLECELL* cell : m_cells )
cell->Flip( aCentre, aFlipLeftRight );
}
void PCB_TABLE::RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction ) const
{
for( PCB_TABLECELL* cell : m_cells )
aFunction( cell );
}
const BOX2I PCB_TABLE::GetBoundingBox() const
{
// Note: a table with no cells is not allowed
BOX2I bbox = m_cells[0]->GetBoundingBox();
bbox.Merge( m_cells[ m_cells.size() - 1 ]->GetBoundingBox() );
return bbox;
}
void PCB_TABLE::TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer,
int aClearance, int aMaxError, ERROR_LOC aErrorLoc,
bool aIgnoreLineWidth ) const
{
for( PCB_TABLECELL* cell : m_cells )
cell->TransformShapeToPolygon( aBuffer, aLayer, aClearance, aMaxError, aErrorLoc, false );
}
INSPECT_RESULT PCB_TABLE::Visit( INSPECTOR aInspector, void* aTestData,
const std::vector<KICAD_T>& aScanTypes )
{
for( KICAD_T scanType : aScanTypes )
{
if( scanType == PCB_TABLE_T )
{
if( INSPECT_RESULT::QUIT == aInspector( this, aTestData ) )
return INSPECT_RESULT::QUIT;
}
if( scanType == PCB_TABLECELL_T )
{
for( PCB_TABLECELL* cell : m_cells )
{
if( INSPECT_RESULT::QUIT == aInspector( cell, (void*) this ) )
return INSPECT_RESULT::QUIT;
}
}
}
return INSPECT_RESULT::CONTINUE;
}
wxString PCB_TABLE::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
{
return wxString::Format( _( "%d Column Table" ), m_colCount );
}
BITMAPS PCB_TABLE::GetMenuImage() const
{
return BITMAPS::spreadsheet; // JEY TODO
}
bool PCB_TABLE::HitTest( const VECTOR2I& aPosition, int aAccuracy ) const
{
BOX2I rect = GetBoundingBox();
rect.Inflate( aAccuracy );
return rect.Contains( aPosition );
}
bool PCB_TABLE::HitTest( const BOX2I& aRect, bool aContained, int aAccuracy ) const
{
BOX2I rect = aRect;
rect.Inflate( aAccuracy );
if( aContained )
return rect.Contains( GetBoundingBox() );
return rect.Intersects( GetBoundingBox() );
}
void PCB_TABLE::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
// Don't use GetShownText() here; we want to show the user the variable references
aList.emplace_back( _( "Table" ), wxString::Format( _( "%d Columns" ), m_colCount ) );
}
int PCB_TABLE::Compare( const PCB_TABLE* aTable, const PCB_TABLE* aOther )
{
int diff;
if( ( diff = (int) aTable->GetCells().size() - (int) aOther->GetCells().size() ) != 0 )
return diff;
if( ( diff = aTable->GetColCount() - aOther->GetColCount() ) != 0 )
return diff;
for( int col = 0; col < aTable->GetColCount(); ++col )
{
if( ( diff = aTable->GetColWidth( col ) - aOther->GetColWidth( col ) ) != 0 )
return diff;
}
for( int row = 0; row < aTable->GetRowCount(); ++row )
{
if( ( diff = aTable->GetRowHeight( row ) - aOther->GetRowHeight( row ) ) != 0 )
return diff;
}
for( int row = 0; row < aTable->GetRowCount(); ++row )
{
for( int col = 0; col < aTable->GetColCount(); ++col )
{
PCB_TABLECELL* cell = aTable->GetCell( row, col );
PCB_TABLECELL* other = aOther->GetCell( row, col );
if( ( diff = cell->PCB_SHAPE::Compare( other ) ) != 0 )
return diff;
if( ( diff = cell->EDA_TEXT::Compare( other ) ) != 0 )
return diff;
}
}
return 0;
}
bool PCB_TABLE::operator==( const BOARD_ITEM& aOther ) const
{
if( Type() != aOther.Type() )
return false;
const PCB_TABLE& other = static_cast<const PCB_TABLE&>( aOther );
if( m_cells.size() != other.m_cells.size() )
return false;
if( m_colWidths != other.m_colWidths )
return false;
if( m_rowHeights != other.m_rowHeights )
return false;
for( int ii = 0; ii < (int) m_cells.size(); ++ii )
{
if( !( *m_cells[ii] == *other.m_cells[ii] ) )
return false;
}
return true;
}
double PCB_TABLE::Similarity( const BOARD_ITEM& aOther ) const
{
if( aOther.Type() != Type() )
return 0.0;
const PCB_TABLE& other = static_cast<const PCB_TABLE&>( aOther );
if( m_cells.size() != other.m_cells.size() )
return 0.1;
double similarity = 1.0;
for( int ii = 0; ii < (int) m_cells.size(); ++ii )
similarity *= m_cells[ii]->Similarity( *other.m_cells[ii] );
return similarity;
}
static struct PCB_TABLE_DESC
{
PCB_TABLE_DESC()
{
ENUM_MAP<LINE_STYLE>& plotDashTypeEnum = ENUM_MAP<LINE_STYLE>::Instance();
if( plotDashTypeEnum.Choices().GetCount() == 0 )
{
plotDashTypeEnum.Map( LINE_STYLE::DEFAULT, _HKI( "Default" ) )
.Map( LINE_STYLE::SOLID, _HKI( "Solid" ) )
.Map( LINE_STYLE::DASH, _HKI( "Dashed" ) )
.Map( LINE_STYLE::DOT, _HKI( "Dotted" ) )
.Map( LINE_STYLE::DASHDOT, _HKI( "Dash-Dot" ) )
.Map( LINE_STYLE::DASHDOTDOT, _HKI( "Dash-Dot-Dot" ) );
}
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_TABLE );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLE, BOARD_ITEM> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLE, BOARD_ITEM_CONTAINER> );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLE ), TYPE_HASH( BOARD_ITEM ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLE ), TYPE_HASH( BOARD_ITEM_CONTAINER ) );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, int>( _HKI( "Start X" ),
&PCB_TABLE::SetPositionX, &PCB_TABLE::GetPositionX, PROPERTY_DISPLAY::PT_COORD,
ORIGIN_TRANSFORMS::ABS_X_COORD ) );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, int>( _HKI( "Start Y" ),
&PCB_TABLE::SetPositionY, &PCB_TABLE::GetPositionY, PROPERTY_DISPLAY::PT_COORD,
ORIGIN_TRANSFORMS::ABS_Y_COORD ) );
const wxString tableProps = _( "Table Properties" );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, bool>( _HKI( "External Border" ),
&PCB_TABLE::SetStrokeExternal, &PCB_TABLE::StrokeExternal ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, bool>( _HKI( "Header Border" ),
&PCB_TABLE::SetStrokeHeader, &PCB_TABLE::StrokeHeader ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, int>( _HKI( "Border Width" ),
&PCB_TABLE::SetBorderWidth, &PCB_TABLE::GetBorderWidth,
PROPERTY_DISPLAY::PT_SIZE ),
tableProps );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_TABLE, LINE_STYLE>( _HKI( "Border Style" ),
&PCB_TABLE::SetBorderStyle, &PCB_TABLE::GetBorderStyle ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, COLOR4D>( _HKI( "Border Color" ),
&PCB_TABLE::SetBorderColor, &PCB_TABLE::GetBorderColor ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, bool>( _HKI( "Row Separators" ),
&PCB_TABLE::SetStrokeRows, &PCB_TABLE::StrokeRows ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, bool>( _HKI( "Cell Separators" ),
&PCB_TABLE::SetStrokeColumns, &PCB_TABLE::StrokeColumns ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, int>( _HKI( "Separators Width" ),
&PCB_TABLE::SetSeparatorsWidth, &PCB_TABLE::GetSeparatorsWidth,
PROPERTY_DISPLAY::PT_SIZE ),
tableProps );
propMgr.AddProperty( new PROPERTY_ENUM<PCB_TABLE, LINE_STYLE>( _HKI( "Separators Style" ),
&PCB_TABLE::SetSeparatorsStyle, &PCB_TABLE::GetSeparatorsStyle ),
tableProps );
propMgr.AddProperty( new PROPERTY<PCB_TABLE, COLOR4D>( _HKI( "Separators Color" ),
&PCB_TABLE::SetSeparatorsColor, &PCB_TABLE::GetSeparatorsColor ),
tableProps );
}
} _PCB_TABLE_DESC;

250
pcbnew/pcb_table.h Normal file
View File

@ -0,0 +1,250 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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
*/
#ifndef PCB_TABLE_H
#define PCB_TABLE_H
#include <pcb_tablecell.h>
#include <board_item.h>
class PCB_TABLE : public BOARD_ITEM_CONTAINER
{
public:
PCB_TABLE( BOARD_ITEM* aParent, int aLineWidth );
PCB_TABLE( const PCB_TABLE& aTable );
~PCB_TABLE();
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && PCB_TABLE_T == aItem->Type();
}
virtual wxString GetClass() const override
{
return wxT( "PCB_TABLE" );
}
void SetStrokeExternal( bool aDoStroke ) { m_strokeExternal = aDoStroke; }
bool StrokeExternal() const { return m_strokeExternal; }
void SetStrokeHeader( bool aDoStroke ) { m_strokeHeader = aDoStroke; }
bool StrokeHeader() const { return m_strokeHeader; }
void SetBorderStroke( const STROKE_PARAMS& aParams ) { m_borderStroke = aParams; }
const STROKE_PARAMS& GetBorderStroke() const { return m_borderStroke; }
void SetBorderWidth( int aWidth ) { m_borderStroke.SetWidth( aWidth ); }
int GetBorderWidth() const { return m_borderStroke.GetWidth(); }
void SetBorderStyle( const LINE_STYLE aStyle ) { m_borderStroke.SetLineStyle( aStyle ); }
LINE_STYLE GetBorderStyle() const { return m_borderStroke.GetLineStyle(); }
void SetBorderColor( const COLOR4D& aColor ) { m_borderStroke.SetColor( aColor ); }
COLOR4D GetBorderColor() const { return m_borderStroke.GetColor(); }
void SetSeparatorsStroke( const STROKE_PARAMS& aParams ) { m_separatorsStroke = aParams; }
const STROKE_PARAMS& GetSeparatorsStroke() const { return m_separatorsStroke; }
void SetSeparatorsWidth( int aWidth ) { m_separatorsStroke.SetWidth( aWidth ); }
int GetSeparatorsWidth() const { return m_separatorsStroke.GetWidth(); }
void SetSeparatorsStyle( const LINE_STYLE aStyle ) { m_separatorsStroke.SetLineStyle( aStyle ); }
LINE_STYLE GetSeparatorsStyle() const { return m_separatorsStroke.GetLineStyle(); }
void SetSeparatorsColor( const COLOR4D& aColor ) { m_separatorsStroke.SetColor( aColor ); }
COLOR4D GetSeparatorsColor() const { return m_separatorsStroke.GetColor(); }
void SetStrokeColumns( bool aDoStroke ) { m_strokeColumns = aDoStroke; }
bool StrokeColumns() const { return m_strokeColumns; }
void SetStrokeRows( bool aDoStroke ) { m_strokeRows = aDoStroke; }
bool StrokeRows() const { return m_strokeRows; }
void RunOnChildren( const std::function<void( BOARD_ITEM* )>& aFunction ) const override;
void SetPosition( const VECTOR2I& aPos ) override;
VECTOR2I GetPosition() const override;
VECTOR2I GetEnd() const;
// For property manager:
void SetPositionX( int x ) { SetPosition( VECTOR2I( x, GetPosition().y ) ); }
void SetPositionY( int y ) { SetPosition( VECTOR2I( GetPosition().x, y ) ); }
int GetPositionX() const { return GetPosition().x; }
int GetPositionY() const { return GetPosition().y; }
void SetColCount( int aCount ) { m_colCount = aCount; }
int GetColCount() const { return m_colCount; }
int GetRowCount() const
{
return m_cells.size() / m_colCount;
}
void SetColWidth( int aCol, int aWidth ) { m_colWidths[aCol] = aWidth; }
int GetColWidth( int aCol ) const
{
if( m_colWidths.count( aCol ) )
return m_colWidths.at( aCol );
return 0;
}
void SetRowHeight( int aRow, int aHeight ) { m_rowHeights[aRow] = aHeight; }
int GetRowHeight( int aRow ) const
{
if( m_rowHeights.count( aRow ) )
return m_rowHeights.at( aRow );
return 0;
}
PCB_TABLECELL* GetCell( int aRow, int aCol ) const
{
int idx = aRow * m_colCount + aCol;
if( idx < (int) m_cells.size() )
return m_cells[ idx ];
else
return nullptr;
}
std::vector<PCB_TABLECELL*> GetCells() const
{
return m_cells;
}
void AddCell( PCB_TABLECELL* aCell )
{
m_cells.push_back( aCell );
aCell->SetLayer( GetLayer() );
aCell->SetParent( this );
}
void InsertCell( int aIdx, PCB_TABLECELL* aCell )
{
m_cells.insert( m_cells.begin() + aIdx, aCell );
aCell->SetLayer( GetLayer() );
aCell->SetParent( this );
}
void ClearCells()
{
for( PCB_TABLECELL* cell : m_cells )
delete cell;
m_cells.clear();
}
void DeleteMarkedCells()
{
alg::delete_if( m_cells,
[]( PCB_TABLECELL* cell )
{
return ( cell->GetFlags() & STRUCT_DELETED ) > 0;
} );
}
void Add( BOARD_ITEM* aItem, ADD_MODE aMode = ADD_MODE::INSERT,
bool aSkipConnectivity = false ) override
{
wxFAIL_MSG( wxT( "Use AddCell()/InsertCell() instead." ) );
}
void Remove( BOARD_ITEM* aItem, REMOVE_MODE aMode = REMOVE_MODE::NORMAL ) override
{
wxFAIL_MSG( wxT( "Use DeleteMarkedCells() instead." ) );
}
void Normalize() override;
void Move( const VECTOR2I& aMoveVector ) override;
void Rotate( const VECTOR2I& aRotCentre, const EDA_ANGLE& aAngle ) override;
void Flip( const VECTOR2I& aCentre, bool aFlipLeftRight ) override;
const BOX2I GetBoundingBox() const override;
void TransformShapeToPolygon( SHAPE_POLY_SET& aBuffer, PCB_LAYER_ID aLayer, int aClearance,
int aMaxError, ERROR_LOC aErrorLoc,
bool aIgnoreLineWidth = false ) const override;
INSPECT_RESULT Visit( INSPECTOR inspector, void* testData,
const std::vector<KICAD_T>& aScanTypes ) override;
bool Matches( const EDA_SEARCH_DATA& aSearchData, void* aAuxData ) const override
{
// Symbols are searchable via the child field and pin item text.
return false;
}
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const override;
BITMAPS GetMenuImage() const override;
bool HitTest( const VECTOR2I& aPosition, int aAccuracy = 0 ) const override;
bool HitTest( const BOX2I& aRect, bool aContained, int aAccuracy = 0 ) const override;
EDA_ITEM* Clone() const override
{
return new PCB_TABLE( *this );
}
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
double Similarity( const BOARD_ITEM& aOther ) const override;
bool operator==( const BOARD_ITEM& aOther ) const override;
static int Compare( const PCB_TABLE* aTable, const PCB_TABLE* aOther );
#if defined(DEBUG)
void Show( int nestLevel, std::ostream& os ) const override { ShowDummy( os ); }
#endif
protected:
virtual void swapData( BOARD_ITEM* aImage ) override;
protected:
bool m_strokeExternal;
bool m_strokeHeader;
STROKE_PARAMS m_borderStroke;
bool m_strokeRows;
bool m_strokeColumns;
STROKE_PARAMS m_separatorsStroke;
int m_colCount;
std::map<int, int> m_colWidths;
std::map<int, int> m_rowHeights;
std::vector<PCB_TABLECELL*> m_cells;
};
#endif /* PCB_TABLE_H */

192
pcbnew/pcb_tablecell.cpp Normal file
View File

@ -0,0 +1,192 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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 <pcb_edit_frame.h>
#include <font/font.h>
#include <widgets/msgpanel.h>
#include <string_utils.h>
#include <pcb_table.h>
#include <pcb_tablecell.h>
PCB_TABLECELL::PCB_TABLECELL( BOARD_ITEM* aParent ) :
PCB_TEXTBOX( aParent, PCB_TABLECELL_T ),
m_colSpan( 1 ),
m_rowSpan( 1 )
{
}
void PCB_TABLECELL::swapData( BOARD_ITEM* aImage )
{
wxASSERT( aImage->Type() == PCB_TABLECELL_T );
std::swap( *((PCB_TABLECELL*) this), *((PCB_TABLECELL*) aImage) );
}
wxString PCB_TABLECELL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const
{
return wxString::Format( _( "Table Cell %s" ), GetAddr() );
}
int PCB_TABLECELL::GetRow() const
{
const PCB_TABLE* table = static_cast<const PCB_TABLE*>( GetParent() );
for( int row = 0; row < table->GetRowCount(); ++row )
{
for( int col = 0; col < table->GetColCount(); ++col )
{
if( table->GetCell( row, col ) == this )
return row;
}
}
return -1;
}
int PCB_TABLECELL::GetColumn() const
{
const PCB_TABLE* table = static_cast<const PCB_TABLE*>( GetParent() );
for( int row = 0; row < table->GetRowCount(); ++row )
{
for( int col = 0; col < table->GetColCount(); ++col )
{
if( table->GetCell( row, col ) == this )
return col;
}
}
return -1;
}
wxString PCB_TABLECELL::GetAddr() const
{
return wxString::Format( wxT( "%c%d" ),
'A' + GetColumn() % 26,
GetRow() + 1 );
}
void PCB_TABLECELL::GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList )
{
aList.emplace_back( _( "Table Cell" ), GetAddr() );
// Don't use GetShownText() here; we want to show the user the variable references
aList.emplace_back( _( "Text" ), KIUI::EllipsizeStatusText( aFrame, GetText() ) );
if( aFrame->GetName() == PCB_EDIT_FRAME_NAME && IsLocked() )
aList.emplace_back( _( "Status" ), _( "Locked" ) );
aList.emplace_back( _( "Layer" ), GetLayerName() );
aList.emplace_back( _( "Mirror" ), IsMirrored() ? _( "Yes" ) : _( "No" ) );
aList.emplace_back( _( "Cell Width" ),
aFrame->MessageTextFromValue( std::abs( GetEnd().x - GetStart().x ) ) );
aList.emplace_back( _( "Cell Height" ),
aFrame->MessageTextFromValue( std::abs( GetEnd().y - GetStart().y ) ) );
aList.emplace_back( _( "Font" ), GetFont() ? GetFont()->GetName() : _( "Default" ) );
aList.emplace_back( _( "Text Thickness" ), aFrame->MessageTextFromValue( GetTextThickness() ) );
aList.emplace_back( _( "Text Width" ), aFrame->MessageTextFromValue( GetTextWidth() ) );
aList.emplace_back( _( "Text Height" ), aFrame->MessageTextFromValue( GetTextHeight() ) );
}
double PCB_TABLECELL::Similarity( const BOARD_ITEM& aBoardItem ) const
{
if( aBoardItem.Type() != Type() )
return 0.0;
const PCB_TABLECELL& other = static_cast<const PCB_TABLECELL&>( aBoardItem );
double similarity = 1.0;
if( m_colSpan != other.m_colSpan )
similarity *= 0.9;
if( m_rowSpan != other.m_rowSpan )
similarity *= 0.9;
similarity *= PCB_TEXTBOX::Similarity( other );
return similarity;
}
bool PCB_TABLECELL::operator==( const BOARD_ITEM& aBoardItem ) const
{
if( aBoardItem.Type() != Type() )
return false;
const PCB_TABLECELL& other = static_cast<const PCB_TABLECELL&>( aBoardItem );
return m_colSpan == other.m_colSpan
&& m_rowSpan == other.m_rowSpan
&& PCB_TEXTBOX::operator==( other );
}
static struct PCB_TABLECELL_DESC
{
PCB_TABLECELL_DESC()
{
PROPERTY_MANAGER& propMgr = PROPERTY_MANAGER::Instance();
REGISTER_TYPE( PCB_TABLECELL );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLECELL, PCB_TEXTBOX> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLECELL, PCB_SHAPE> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLECELL, EDA_SHAPE> );
propMgr.AddTypeCast( new TYPE_CAST<PCB_TABLECELL, EDA_TEXT> );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_TEXTBOX ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_SHAPE ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ) );
propMgr.InheritsAfter( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_TEXTBOX ), _HKI( "Border" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_TEXTBOX ), _HKI( "Border Style" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( PCB_TEXTBOX ), _HKI( "Border Width" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Start X" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Start Y" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "End X" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "End Y" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Shape" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Width" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_SHAPE ), _HKI( "Line Style" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Visible" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Width" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Height" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Thickness" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Orientation" ) );
propMgr.Mask( TYPE_HASH( PCB_TABLECELL ), TYPE_HASH( EDA_TEXT ), _HKI( "Hyperlink" ) );
}
} _PCB_TABLECELL_DESC;

85
pcbnew/pcb_tablecell.h Normal file
View File

@ -0,0 +1,85 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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
*/
#ifndef PCB_TABLECELL_H
#define PCB_TABLECELL_H
#include <pcb_textbox.h>
class PCB_TABLECELL : public PCB_TEXTBOX
{
public:
PCB_TABLECELL( BOARD_ITEM* parent );
static inline bool ClassOf( const EDA_ITEM* aItem )
{
return aItem && PCB_TABLECELL_T == aItem->Type();
}
wxString GetClass() const override
{
return wxT( "PCB_TABLECELL" );
}
virtual wxString GetFriendlyName() const override
{
return _( "Table Cell" );
}
EDA_ITEM* Clone() const override
{
return new PCB_TABLECELL( *this );
}
int GetRow() const;
int GetColumn() const;
// @return the spreadsheet nomenclature for the cell (ie: B3 for 2nd column, 3rd row)
wxString GetAddr() const;
int GetColSpan() const { return m_colSpan; }
void SetColSpan( int aSpan ) { m_colSpan = aSpan; }
int GetRowSpan() const { return m_rowSpan; }
void SetRowSpan( int aSpan ) { m_rowSpan = aSpan; }
wxString GetItemDescription( UNITS_PROVIDER* aUnitsProvider ) const override;
void GetMsgPanelInfo( EDA_DRAW_FRAME* aFrame, std::vector<MSG_PANEL_ITEM>& aList ) override;
double Similarity( const BOARD_ITEM& aBoardItem ) const override;
bool operator==( const BOARD_ITEM& aBoardItem ) const override;
protected:
virtual void swapData( BOARD_ITEM* aImage ) override;
protected:
int m_colSpan;
int m_rowSpan;
};
#endif /* PCB_TABLECELL_H */

View File

@ -38,8 +38,8 @@
#include <core/ignore.h>
PCB_TEXTBOX::PCB_TEXTBOX( BOARD_ITEM* parent ) :
PCB_SHAPE( parent, PCB_TEXTBOX_T, SHAPE_T::RECTANGLE ),
PCB_TEXTBOX::PCB_TEXTBOX( BOARD_ITEM* aParent, KICAD_T aType ) :
PCB_SHAPE( aParent, aType, SHAPE_T::RECTANGLE ),
EDA_TEXT( pcbIUScale ),
m_borderEnabled( true )
{
@ -485,7 +485,7 @@ EDA_ITEM* PCB_TEXTBOX::Clone() const
void PCB_TEXTBOX::swapData( BOARD_ITEM* aImage )
{
assert( aImage->Type() == PCB_TEXTBOX_T );
wxASSERT( aImage->Type() == PCB_TEXTBOX_T );
std::swap( *((PCB_TEXTBOX*) this), *((PCB_TEXTBOX*) aImage) );
}

View File

@ -36,7 +36,7 @@ class MSG_PANEL_ITEM;
class PCB_TEXTBOX : public PCB_SHAPE, public EDA_TEXT
{
public:
PCB_TEXTBOX( BOARD_ITEM* parent );
PCB_TEXTBOX( BOARD_ITEM* aParent, KICAD_T aType = PCB_TEXTBOX_T );
// Do not create a copy constructor & operator=.
// The ones generated by the compiler are adequate.

View File

@ -76,11 +76,18 @@ void PCB_VIEW::Update( const KIGFX::VIEW_ITEM* aItem, int aUpdateFlags ) const
{
if( const BOARD_ITEM* boardItem = dynamic_cast<const BOARD_ITEM*>( aItem ) )
{
boardItem->RunOnChildren(
[this, aUpdateFlags]( BOARD_ITEM* child )
{
VIEW::Update( child, aUpdateFlags );
} );
if( boardItem->Type() == PCB_TABLECELL_T )
{
VIEW::Update( boardItem->GetParent() );
}
else
{
boardItem->RunOnChildren(
[this, aUpdateFlags]( BOARD_ITEM* child )
{
VIEW::Update( child, aUpdateFlags );
} );
}
}
VIEW::Update( aItem, aUpdateFlags );

View File

@ -187,6 +187,7 @@ void FOOTPRINT_EDIT_FRAME::ReCreateVToolbar()
m_drawToolBar->Add( PCB_ACTIONS::placeReferenceImage, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTable, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->AddGroup( dimensionGroup, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( ACTIONS::deleteTool, ACTION_TOOLBAR::TOGGLE );

View File

@ -441,6 +441,7 @@ void PCB_EDIT_FRAME::ReCreateVToolbar()
m_drawToolBar->Add( PCB_ACTIONS::placeReferenceImage, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::placeText, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTextBox, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( PCB_ACTIONS::drawTable, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->AddGroup( dimensionGroup, ACTION_TOOLBAR::TOGGLE );
m_drawToolBar->Add( ACTIONS::deleteTool, ACTION_TOOLBAR::TOGGLE );

View File

@ -66,6 +66,8 @@
#include <pcb_reference_image.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_tablecell.h>
#include <pcb_dimension.h>
#include <pcbnew_id.h>
#include <preview_items/arc_assistant.h>
@ -562,6 +564,7 @@ int DRAWING_TOOL::PlaceReferenceImage( const TOOL_EVENT& aEvent )
}
m_frame->PushTool( aEvent );
auto setCursor =
[&]()
{
@ -796,6 +799,15 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
SCOPED_DRAW_MODE scopedDrawMode( m_mode, MODE::TEXT );
PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
auto setCursor =
[&]()
{
if( text )
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
else
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
};
auto cleanup =
[&]()
{
@ -808,15 +820,6 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
text = nullptr;
};
auto setCursor =
[&]()
{
if( text )
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
else
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::TEXT );
};
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
m_frame->PushTool( aEvent );
@ -1026,6 +1029,218 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
}
int DRAWING_TOOL::DrawTable( const TOOL_EVENT& aEvent )
{
if( m_inDrawingTool )
return 0;
REENTRANCY_GUARD guard( &m_inDrawingTool );
COMMON_SETTINGS* common_settings = Pgm().GetCommonSettings();
PCB_TABLE* table = nullptr;
const BOARD_DESIGN_SETTINGS& bds = m_frame->GetDesignSettings();
BOARD_COMMIT commit( m_frame );
PCB_GRID_HELPER grid( m_toolMgr, m_frame->GetMagneticItemsSettings() );
// We might be running as the same shape in another co-routine. Make sure that one
// gets whacked.
m_toolMgr->DeactivateTool();
auto setCursor =
[&]()
{
if( table )
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::MOVING );
else
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::PENCIL );
};
auto cleanup =
[&] ()
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
m_controls->ForceCursorPosition( false );
m_controls->ShowCursor( true );
m_controls->SetAutoPan( false );
m_controls->CaptureCursor( false );
delete table;
table = nullptr;
};
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
m_frame->PushTool( aEvent );
Activate();
// Must be done after Activate() so that it gets set into the correct context
getViewControls()->ShowCursor( true );
m_controls->ForceCursorPosition( false );
// Set initial cursor
setCursor();
if( aEvent.HasPosition() )
m_toolMgr->PrimeTool( aEvent.Position() );
else if( common_settings->m_Input.immediate_actions && !aEvent.IsReactivate() )
m_toolMgr->PrimeTool( { 0, 0 } );
// Main loop: keep receiving events
while( TOOL_EVENT* evt = Wait() )
{
setCursor();
grid.SetSnap( !evt->Modifier( MD_SHIFT ) );
grid.SetUseGrid( getView()->GetGAL()->GetGridSnapping() && !evt->DisableGridSnapping() );
VECTOR2I cursorPos =
GetClampedCoords( grid.BestSnapAnchor( m_controls->GetMousePosition(),
m_frame->GetActiveLayer(), GRID_TEXT ),
COORDS_PADDING );
m_controls->ForceCursorPosition( true, cursorPos );
if( evt->IsCancelInteractive() || ( table && evt->IsAction( &ACTIONS::undo ) ) )
{
if( table )
{
cleanup();
}
else
{
m_frame->PopTool( aEvent );
break;
}
}
else if( evt->IsActivate() )
{
if( table )
cleanup();
if( evt->IsMoveTool() )
{
// leave ourselves on the stack so we come back after the move
break;
}
else
{
m_frame->PopTool( aEvent );
break;
}
}
else if( evt->IsClick( BUT_RIGHT ) )
{
// Warp after context menu only if dragging...
if( !table )
m_toolMgr->VetoContextMenuMouseWarp();
m_menu.ShowContextMenu( selection() );
}
else if( evt->IsClick( BUT_LEFT ) )
{
if( !table )
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
PCB_LAYER_ID layer = m_frame->GetActiveLayer();
table = new PCB_TABLE( m_frame->GetModel(), bds.GetLineThickness( layer ) );
table->SetColCount( 1 );
table->AddCell( new PCB_TABLECELL( table ) );
table->SetLayer( layer );
table->SetPosition( cursorPos );
if( !m_view->IsLayerVisible( layer ) )
{
m_frame->GetAppearancePanel()->SetLayerVisible( layer, true );
m_frame->GetCanvas()->Refresh();
}
m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, table );
m_view->Update( &selection() );
// update the cursor so it looks correct before another event
setCursor();
}
else
{
table->ClearFlags();
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
table->Normalize();
commit.Add( table, m_frame->GetScreen() );
commit.Push( _( "Draw Table" ) );
m_toolMgr->RunAction<EDA_ITEM*>( PCB_ACTIONS::selectItem, table );
table = nullptr;
m_toolMgr->PostAction( ACTIONS::activatePointEditor );
}
}
else if( table && ( evt->IsAction( &ACTIONS::refreshPreview ) || evt->IsMotion() ) )
{
VECTOR2I fontSize = bds.GetTextSize( table->GetLayer() );
VECTOR2I gridSize = grid.GetGridSize( grid.GetItemGrid( table ) );
VECTOR2I origin( table->GetPosition() );
VECTOR2I requestedSize( cursorPos - origin );
int colCount = std::max( 1, requestedSize.x / ( fontSize.x * 15 ) );
int rowCount = std::max( 1, requestedSize.y / ( fontSize.y * 3 ) );
VECTOR2I cellSize( std::max( fontSize.x * 5, requestedSize.x / colCount ),
std::max( fontSize.y * 3, requestedSize.y / rowCount ) );
cellSize.x = KiROUND( (double) cellSize.x / gridSize.x ) * gridSize.x;
cellSize.y = KiROUND( (double) cellSize.y / gridSize.y ) * gridSize.y;
table->ClearCells();
table->SetColCount( colCount );
for( int col = 0; col < colCount; ++col )
table->SetColWidth( col, cellSize.x );
for( int row = 0; row < rowCount; ++row )
{
table->SetRowHeight( row, cellSize.y );
for( int col = 0; col < colCount; ++col )
{
PCB_TABLECELL* cell = new PCB_TABLECELL( table );
cell->SetPosition( origin + VECTOR2I( col * cellSize.x, row * cellSize.y ) );
cell->SetEnd( cell->GetPosition() + cellSize );
table->AddCell( cell );
}
}
selection().SetReferencePoint( cursorPos );
m_view->Update( &selection() );
m_frame->SetMsgPanel( table );
}
else if( table && evt->IsAction( &PCB_ACTIONS::properties ) )
{
frame()->OnEditItemRequest( table );
m_view->Update( &selection() );
frame()->SetMsgPanel( table );
}
else if( table && ( ZONE_FILLER_TOOL::IsZoneFillAction( evt )
|| evt->IsAction( &ACTIONS::redo ) ) )
{
wxBell();
}
else
{
evt->SetPassEvent();
}
// Enable autopanning and cursor capture only when there is a shape being drawn
getViewControls()->SetAutoPan( table != nullptr );
getViewControls()->CaptureCursor( table != nullptr );
}
getViewControls()->SetAutoPan( false );
getViewControls()->CaptureCursor( false );
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
return 0;
}
void DRAWING_TOOL::constrainDimension( PCB_DIMENSION_BASE* aDim )
{
const VECTOR2I lineVector{ aDim->GetEnd() - aDim->GetStart() };
@ -3457,6 +3672,7 @@ void DRAWING_TOOL::setTransitions()
Go( &DRAWING_TOOL::DrawVia, PCB_ACTIONS::drawVia.MakeEvent() );
Go( &DRAWING_TOOL::PlaceReferenceImage, PCB_ACTIONS::placeReferenceImage.MakeEvent() );
Go( &DRAWING_TOOL::PlaceText, PCB_ACTIONS::placeText.MakeEvent() );
Go( &DRAWING_TOOL::DrawTable, PCB_ACTIONS::drawTable.MakeEvent() );
Go( &DRAWING_TOOL::DrawRectangle, PCB_ACTIONS::drawTextBox.MakeEvent() );
Go( &DRAWING_TOOL::PlaceImportedGraphics, PCB_ACTIONS::placeImportedGraphics.MakeEvent() );
Go( &DRAWING_TOOL::SetAnchor, PCB_ACTIONS::setAnchor.MakeEvent() );

View File

@ -157,6 +157,11 @@ public:
*/
int PlaceText( const TOOL_EVENT& aEvent );
/*
* Start interactively drawing a table (rows & columns of TEXTBOXes).
*/
int DrawTable( const TOOL_EVENT& aEvent );
/**
* Start interactively drawing a dimension.
*

View File

@ -36,6 +36,7 @@
#include <pcb_target.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_tablecell.h>
#include <pcb_generator.h>
#include <collectors.h>
#include <pcb_edit_frame.h>
@ -1733,6 +1734,7 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
{
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForTableCells( aCollector );
},
// Prompt user regarding locked items if in board editor and in free-pad-mode (if
// we're not in free-pad mode we delay this until the second RequestSelection()).
@ -1758,6 +1760,7 @@ int EDIT_TOOL::Rotate( const TOOL_EVENT& aEvent )
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForFreePads( aCollector );
sTool->FilterCollectorForTableCells( aCollector );
},
true /* prompt user regarding locked items */ );
}
@ -1997,6 +2000,10 @@ int EDIT_TOOL::Mirror( const TOOL_EVENT& aEvent )
static_cast<PCB_TEXTBOX*>( item )->Mirror( mirrorPoint, mirrorAroundXaxis );
break;
case PCB_TABLE_T:
// JEY TODO: tables
break;
case PCB_PAD_T:
if( mirrorLeftRight )
mirrorPadX( *static_cast<PAD*>( item ), mirrorPoint );
@ -2129,6 +2136,7 @@ int EDIT_TOOL::Flip( const TOOL_EVENT& aEvent )
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForFreePads( aCollector );
sTool->FilterCollectorForTableCells( aCollector );
},
!m_dragging /* prompt user regarding locked items */ );
@ -2215,6 +2223,7 @@ void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
case PCB_TEXT_T:
case PCB_SHAPE_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_REFERENCE_IMAGE_T:
case PCB_DIMENSION_T:
case PCB_DIM_ALIGNED_T:
@ -2225,6 +2234,12 @@ void EDIT_TOOL::DeleteItems( const PCB_SELECTION& aItems, bool aIsCut )
commit.Remove( board_item );
break;
case PCB_TABLECELL_T:
// Clear contents of table cell
commit.Modify( board_item );
static_cast<PCB_TABLECELL*>( board_item )->SetText( wxEmptyString );
break;
case PCB_GROUP_T:
board_item->RunOnDescendants(
[&commit]( BOARD_ITEM* aItem )
@ -2415,6 +2430,7 @@ int EDIT_TOOL::MoveExact( const TOOL_EVENT& aEvent )
{
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForTableCells( aCollector );
},
true /* prompt user regarding locked items */ );
@ -2509,6 +2525,7 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
sTool->FilterCollectorForFreePads( aCollector, true );
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForTableCells( aCollector );
} );
if( selection.Empty() )
@ -2599,6 +2616,10 @@ int EDIT_TOOL::Duplicate( const TOOL_EVENT& aEvent )
commit.Add( dupe_item );
break;
case PCB_TABLE_T:
// JEY TODO: tables
break;
case PCB_GENERATOR_T:
case PCB_GROUP_T:
dupe_item = static_cast<PCB_GROUP*>( orig_item )->DeepDuplicate();

View File

@ -299,6 +299,7 @@ bool EDIT_TOOL::doMoveSelection( const TOOL_EVENT& aEvent, BOARD_COMMIT* aCommit
sTool->FilterCollectorForMarkers( aCollector );
sTool->FilterCollectorForHierarchy( aCollector, true );
sTool->FilterCollectorForFreePads( aCollector );
sTool->FilterCollectorForTableCells( aCollector );
},
true /* prompt user regarding locked items */ );

View File

@ -180,10 +180,18 @@ TOOL_ACTION PCB_ACTIONS::drawTextBox( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveDrawing.textbox" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Text Box" ) )
.Tooltip( _( "Add a wrapped text item" ) )
.Tooltip( _( "Add a line-wrapped text item" ) )
.Icon( BITMAPS::add_textbox )
.Flags( AF_ACTIVATE ) );
TOOL_ACTION PCB_ACTIONS::drawTable( TOOL_ACTION_ARGS()
.Name( "pcbnew.InteractiveDrawing.drawTable" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Add Table" ) )
.Tooltip( _( "Draw table" ) )
.Icon( BITMAPS::spreadsheet ) // JEY TODO
.Flags( AF_ACTIVATE ) );
TOOL_ACTION PCB_ACTIONS::spacingIncrease( TOOL_ACTION_ARGS()
.Name( "pcbnew.lengthTuner.SpacingIncrease" )
.Scope( AS_GLOBAL )

View File

@ -200,6 +200,7 @@ public:
static TOOL_ACTION placeReferenceImage;
static TOOL_ACTION placeText;
static TOOL_ACTION drawTextBox;
static TOOL_ACTION drawTable;
static TOOL_ACTION spacingIncrease;
static TOOL_ACTION spacingDecrease;
static TOOL_ACTION amplIncrease;

View File

@ -0,0 +1,94 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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 <tool/tool_manager.h>
#include <tools/pcb_selection_tool.h>
#include <tools/pcb_actions.h>
#include <collectors.h>
#include <tools/pcb_edit_table_tool.h>
PCB_EDIT_TABLE_TOOL::PCB_EDIT_TABLE_TOOL() :
PCB_TOOL_BASE( "pcbnew.TableEditor" )
{
}
bool PCB_EDIT_TABLE_TOOL::Init()
{
PCB_TOOL_BASE::Init();
addMenus( m_toolMgr->GetTool<PCB_SELECTION_TOOL>()->GetToolMenu().GetMenu() );
return true;
}
PCB_TABLECELL* PCB_EDIT_TABLE_TOOL::copyCell( PCB_TABLECELL* aSource )
{
PCB_TABLECELL* cell = new PCB_TABLECELL( aSource->GetParent() );
cell->SetEnd( aSource->GetEnd() - aSource->GetStart() );
return cell;
}
const SELECTION& PCB_EDIT_TABLE_TOOL::getTableCellSelection()
{
PCB_SELECTION_TOOL* selTool = m_toolMgr->GetTool<PCB_SELECTION_TOOL>();
return selTool->RequestSelection(
[]( const VECTOR2I& aPt, GENERAL_COLLECTOR& aCollector, PCB_SELECTION_TOOL* sTool )
{
// Iterate from the back so we don't have to worry about removals.
for( int i = aCollector.GetCount() - 1; i >= 0; --i )
{
if( !dynamic_cast<PCB_TABLECELL*>( aCollector[ i ] ) )
aCollector.Remove( aCollector[ i ] );
}
},
false /* prompt user regarding locked items */ );
}
void PCB_EDIT_TABLE_TOOL::clearSelection()
{
m_toolMgr->RunAction( PCB_ACTIONS::selectionClear );
};
void PCB_EDIT_TABLE_TOOL::setTransitions()
{
Go( &PCB_EDIT_TABLE_TOOL::AddRowAbove, ACTIONS::addRowAbove.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::AddRowBelow, ACTIONS::addRowBelow.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::AddColumnBefore, ACTIONS::addColBefore.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::AddColumnAfter, ACTIONS::addColAfter.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::DeleteRows, ACTIONS::deleteRows.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::DeleteColumns, ACTIONS::deleteColumns.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::MergeCells, ACTIONS::mergeCells.MakeEvent() );
Go( &PCB_EDIT_TABLE_TOOL::UnmergeCells, ACTIONS::unmergeCells.MakeEvent() );
}

View File

@ -0,0 +1,68 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 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
*/
#ifndef PCB_EDIT_TABLE_TOOL_H
#define PCB_EDIT_TABLE_TOOL_H
#include <tools/pcb_tool_base.h>
#include <tool/edit_table_tool_base.h>
#include <pcb_table.h>
#include <pcb_tablecell.h>
#include <board_commit.h>
class PCB_EDIT_TABLE_TOOL : public PCB_TOOL_BASE,
public EDIT_TABLE_TOOL_BASE<PCB_TABLE, PCB_TABLECELL, BOARD_COMMIT>
{
public:
PCB_EDIT_TABLE_TOOL();
~PCB_EDIT_TABLE_TOOL() override { }
/// @copydoc TOOL_INTERACTIVE::Init()
bool Init() override;
int AddRowAbove( const TOOL_EVENT& aEvent ) { return doAddRowAbove( aEvent ); }
int AddRowBelow( const TOOL_EVENT& aEvent ) { return doAddRowBelow( aEvent ); }
int AddColumnBefore( const TOOL_EVENT& aEvent ) { return doAddColumnBefore( aEvent ); }
int AddColumnAfter( const TOOL_EVENT& aEvent ) { return doAddColumnAfter( aEvent ); }
int DeleteRows( const TOOL_EVENT& aEvent ) { return doDeleteRows( aEvent ); }
int DeleteColumns( const TOOL_EVENT& aEvent ) { return doDeleteColumns( aEvent ); }
int MergeCells( const TOOL_EVENT& aEvent ) { return doMergeCells( aEvent ); }
int UnmergeCells( const TOOL_EVENT& aEvent ) { return doUnmergeCells( aEvent ); }
private:
///< Set up handlers for various events.
void setTransitions() override;
private:
TOOL_MANAGER* getToolMgr() override { return m_toolMgr; }
BASE_SCREEN* getScreen() override { return nullptr; }
const SELECTION& getTableCellSelection() override;
void clearSelection() override;
PCB_TABLECELL* copyCell( PCB_TABLECELL* aSource ) override;
};
#endif //PCB_EDIT_TABLE_TOOL_H

View File

@ -46,6 +46,8 @@ using namespace std::placeholders;
#include <pcb_generator.h>
#include <pcb_dimension.h>
#include <pcb_textbox.h>
#include <pcb_tablecell.h>
#include <pcb_table.h>
#include <pad.h>
#include <zone.h>
#include <footprint.h>
@ -74,6 +76,12 @@ enum RECT_LINES
};
enum TABLECELL_POINTS
{
COL_WIDTH, ROW_HEIGHT
};
enum ARC_POINTS
{
ARC_START, ARC_MID, ARC_END, ARC_CENTER
@ -283,6 +291,14 @@ std::shared_ptr<EDIT_POINTS> PCB_POINT_EDITOR::makePoints( EDA_ITEM* aItem )
break;
}
case PCB_TABLECELL_T:
{
PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( aItem );
points->AddPoint( cell->GetEnd() - VECTOR2I( 0, cell->GetRectangleHeight() / 2 ) );
points->AddPoint( cell->GetEnd() - VECTOR2I( cell->GetRectangleWidth() / 2, 0 ) );
break;
}
case PCB_PAD_T:
{
const PAD* pad = static_cast<const PAD*>( aItem );
@ -557,9 +573,16 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genStartEdit, &commit,
static_cast<PCB_GENERATOR*>( item ) );
}
else if( item->Type() == PCB_TABLECELL_T )
{
PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
commit.Modify( table );
}
else
{
commit.StageItems( selection, CHT_MODIFY );
commit.Modify( item );
}
getViewControls()->ForceCursorPosition( false );
@ -672,9 +695,13 @@ int PCB_POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
m_toolMgr->RunSynchronousAction( PCB_ACTIONS::genPushEdit, &commit,
static_cast<PCB_GENERATOR*>( item ) );
}
else if( item->Type() == PCB_TABLECELL_T )
{
commit.Push( _( "Resize Table Cells" ) );
}
else
{
commit.Push( _( "Drag Corner" ) );
commit.Push( _( "Move Point" ) );
}
inDrag = false;
@ -1403,6 +1430,40 @@ void PCB_POINT_EDITOR::updateItem( BOARD_COMMIT* aCommit )
break;
}
case PCB_TABLECELL_T:
{
PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
PCB_TABLE* table = static_cast<PCB_TABLE*>( cell->GetParent() );
if( isModified( m_editPoints->Point( COL_WIDTH ) ) )
{
cell->SetEnd( VECTOR2I( m_editPoints->Point( 0 ).GetX(), cell->GetEndY() ) );
int colWidth = cell->GetRectangleWidth();
for( int ii = 0; ii < cell->GetColSpan() - 1; ++ii )
colWidth -= table->GetColWidth( cell->GetColumn() + ii );
table->SetColWidth( cell->GetColumn() + cell->GetColSpan() - 1, colWidth );
table->Normalize();
}
else if( isModified( m_editPoints->Point( ROW_HEIGHT ) ) )
{
cell->SetEnd( VECTOR2I( cell->GetEndX(), m_editPoints->Point( 1 ).GetY() ) );
int rowHeight = cell->GetRectangleHeight();
for( int ii = 0; ii < cell->GetRowSpan() - 1; ++ii )
rowHeight -= table->GetRowHeight( cell->GetRow() + ii );
table->SetRowHeight( cell->GetRow() + cell->GetRowSpan() - 1, rowHeight );
table->Normalize();
}
getView()->Update( table );
break;
}
case PCB_PAD_T:
{
PAD* pad = static_cast<PAD*>( item );
@ -1933,6 +1994,17 @@ void PCB_POINT_EDITOR::updatePoints()
break;
}
case PCB_TABLECELL_T:
{
PCB_TABLECELL* cell = static_cast<PCB_TABLECELL*>( item );
m_editPoints->Point( 0 ).SetPosition( cell->GetEndX(),
cell->GetEndY() - cell->GetRectangleHeight() / 2 );
m_editPoints->Point( 1 ).SetPosition( cell->GetEndX() - cell->GetRectangleWidth() / 2,
cell->GetEndY() );
break;
}
case PCB_PAD_T:
{
const PAD* pad = static_cast<const PAD*>( item );

View File

@ -45,6 +45,8 @@ using namespace std::placeholders;
#include <pcb_shape.h>
#include <pcb_text.h>
#include <pcb_textbox.h>
#include <pcb_table.h>
#include <pcb_tablecell.h>
#include <pcb_marker.h>
#include <pcb_generator.h>
#include <zone.h>
@ -399,7 +401,16 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
m_frame->FocusOnItem( nullptr );
m_toolMgr->ProcessEvent( EVENTS::InhibitSelectionEditing );
if( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
GENERAL_COLLECTORS_GUIDE guide = getCollectorsGuide();
GENERAL_COLLECTOR collector;
collector.Collect( board(), { PCB_TABLECELL_T }, evt->DragOrigin(), guide );
if( collector.GetCount() )
{
selectTableCells( static_cast<PCB_TABLE*>( collector[0]->GetParent() ) );
}
else if( hasModifier() || dragAction == MOUSE_DRAG_ACTION::SELECT )
{
selectMultiple();
}
@ -888,6 +899,112 @@ const TOOL_ACTION* allowedActions[] = { &ACTIONS::panUp, &ACTIONS::panD
&ACTIONS::zoomFitObjects, nullptr };
bool PCB_SELECTION_TOOL::selectTableCells( PCB_TABLE* aTable )
{
bool cancelled = false; // Was the tool canceled while it was running?
m_multiple = true; // Multiple selection mode is active
for( PCB_TABLECELL* cell : aTable->GetCells() )
{
if( cell->IsSelected() )
cell->SetFlags( CANDIDATE );
else
cell->ClearFlags( CANDIDATE );
}
auto wasSelected =
[]( EDA_ITEM* aItem )
{
return ( aItem->GetFlags() & CANDIDATE ) > 0;
};
while( TOOL_EVENT* evt = Wait() )
{
if( evt->IsCancelInteractive() || evt->IsActivate() )
{
cancelled = true;
break;
}
else if( evt->IsDrag( BUT_LEFT ) )
{
getViewControls()->SetAutoPan( true );
BOX2I selectionRect( evt->DragOrigin(), evt->Position() - evt->DragOrigin() );
selectionRect.Normalize();
for( PCB_TABLECELL* cell : aTable->GetCells() )
{
bool doSelect = false;
if( cell->HitTest( selectionRect, false ) )
{
if( m_subtractive )
doSelect = false;
else if( m_exclusive_or )
doSelect = !wasSelected( cell );
else
doSelect = true;
}
else if( wasSelected( cell ) )
{
doSelect = m_additive || m_subtractive || m_exclusive_or;
}
if( doSelect && !cell->IsSelected() )
select( cell );
else if( !doSelect && cell->IsSelected() )
unselect( cell );
}
}
else if( evt->IsMouseUp( BUT_LEFT ) )
{
m_selection.SetIsHover( false );
bool anyAdded = false;
bool anySubtracted = false;
for( PCB_TABLECELL* cell : aTable->GetCells() )
{
if( cell->IsSelected() && !wasSelected( cell ) )
anyAdded = true;
else if( wasSelected( cell ) && !cell->IsSelected() )
anySubtracted = true;
}
// Inform other potentially interested tools
if( anyAdded )
m_toolMgr->ProcessEvent( EVENTS::SelectedEvent );
if( anySubtracted )
m_toolMgr->ProcessEvent( EVENTS::UnselectedEvent );
break; // Stop waiting for events
}
else
{
// Allow some actions for navigation
for( int i = 0; allowedActions[i]; ++i )
{
if( evt->IsAction( allowedActions[i] ) )
{
evt->SetPassEvent();
break;
}
}
}
}
getViewControls()->SetAutoPan( false );
m_multiple = false; // Multiple selection mode is inactive
if( !cancelled )
m_selection.ClearReferencePoint();
return cancelled;
}
bool PCB_SELECTION_TOOL::selectMultiple()
{
bool cancelled = false; // Was the tool canceled while it was running?
@ -2322,6 +2439,8 @@ static bool itemIsIncludedByFilter( const BOARD_ITEM& aItem, const BOARD& aBoard
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
return aFilterOptions.includePcbTexts;
default:
@ -2486,6 +2605,8 @@ bool PCB_SELECTION_TOOL::itemPassesFilter( BOARD_ITEM* aItem, bool aMultiSelect
case PCB_FIELD_T:
case PCB_TEXT_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
if( !m_filter.text )
return false;
@ -2800,6 +2921,8 @@ bool PCB_SELECTION_TOOL::Selectable( const BOARD_ITEM* aItem, bool checkVisibili
case PCB_SHAPE_T:
case PCB_TEXTBOX_T:
case PCB_TABLE_T:
case PCB_TABLECELL_T:
if( m_isFootprintEditor )
{
if( !view()->IsLayerVisible( aItem->GetLayer() ) )
@ -3042,6 +3165,7 @@ int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aIt
}
case PCB_TEXTBOX_T:
case PCB_TABLECELL_T:
{
PCB_TEXTBOX* textbox = static_cast<PCB_TEXTBOX*>( aItem );
@ -3052,6 +3176,20 @@ int PCB_SELECTION_TOOL::hitTestDistance( const VECTOR2I& aWhere, BOARD_ITEM* aIt
break;
}
case PCB_TABLE_T:
{
PCB_TABLE* table = static_cast<PCB_TABLE*>( aItem );
for( PCB_TABLECELL* cell : table->GetCells() )
{
// Add a bit of slop to text-shapes
if( cell->GetEffectiveTextShape()->Collide( loc, aMaxDistance, &distance ) )
distance = std::clamp( distance - ( aMaxDistance / 2 ), 0, distance );
}
break;
}
case PCB_ZONE_T:
{
ZONE* zone = static_cast<ZONE*>( aItem );
@ -3286,7 +3424,9 @@ void PCB_SELECTION_TOOL::GuessSelectionCandidates( GENERAL_COLLECTOR& aCollector
{
static const LSET silkLayers( 2, B_SilkS, F_SilkS );
static const LSET courtyardLayers( 2, B_CrtYd, F_CrtYd );
static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T, PCB_TEXT_T, PCB_TEXTBOX_T,
static std::vector<KICAD_T> singleLayerSilkTypes = { PCB_FIELD_T,
PCB_TEXT_T, PCB_TEXTBOX_T,
PCB_TABLE_T, PCB_TABLECELL_T,
PCB_SHAPE_T };
if( ADVANCED_CFG::GetCfg().m_PcbSelectionVisibilityRatio != 1.0 )
@ -3540,6 +3680,29 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
}
void PCB_SELECTION_TOOL::FilterCollectorForTableCells( GENERAL_COLLECTOR& aCollector ) const
{
std::set<BOARD_ITEM*> to_add;
// Iterate from the back so we don't have to worry about removals.
for( int i = (int) aCollector.GetCount() - 1; i >= 0; --i )
{
BOARD_ITEM* item = aCollector[i];
if( item->Type() == PCB_TABLECELL_T )
{
if( !aCollector.HasItem( item->GetParent() ) )
to_add.insert( item->GetParent() );
aCollector.Remove( item );
}
}
for( BOARD_ITEM* item : to_add )
aCollector.Append( item );
}
void PCB_SELECTION_TOOL::FilterCollectorForFreePads( GENERAL_COLLECTOR& aCollector,
bool aForcePromotion ) const
{

View File

@ -42,6 +42,7 @@
class PCB_BASE_FRAME;
class BOARD_ITEM;
class GENERAL_COLLECTOR;
class PCB_TABLE;
namespace KIGFX
{
@ -205,6 +206,11 @@ public:
void FilterCollectorForFreePads( GENERAL_COLLECTOR& aCollector,
bool aForcePromotion = false ) const;
/**
* Promote any table cell selections to the whole table.
*/
void FilterCollectorForTableCells( GENERAL_COLLECTOR& aCollector ) const;
/**
* Drop any PCB_MARKERs from the collector.
*/
@ -293,6 +299,8 @@ private:
*/
bool selectMultiple();
bool selectTableCells( PCB_TABLE* aTable );
/**
* Handle disambiguation actions including displaying the menu.
*/

View File

@ -837,6 +837,10 @@ void ZONE_FILLER::addKnockout( BOARD_ITEM* aItem, PCB_LAYER_ID aLayer, int aGap,
break;
}
case PCB_TABLE_T:
// JEY TODO: tables
break;
case PCB_SHAPE_T:
case PCB_TARGET_T:
aItem->TransformShapeToPolygon( aHoles, aLayer, aGap, m_maxError, ERROR_OUTSIDE,