Compare commits

...

12 Commits

Author SHA1 Message Date
Roberto Fernandez Bautista
c8e061ab64 Merge branch 'altium_custom_pads' into 'master'
Draft: Fix Altium custom pads import

See merge request kicad/code/kicad!2254
2025-09-12 15:13:31 +00:00
Seth Hillbrand
56ad08cdd8 Prevent backannotation from changing variables
Updating to PCB always resolves text variables from the schematic.  This
means that back annotation will always start from resolved variables if
they exist in the schematic.  So to avoid this, we prevent overwriting
any text variables that exist in the schematic.

This does prevent the user from updating a text variable in a pcbnew
field and backannotating this variable change into the schematic where
the schematic had a text variable already.  Text variables are forward
annotation only until we add an additional property to each field to
include the unresolved text

Fixes https://gitlab.com/kicad/code/kicad/issues/21724
2025-09-12 07:24:39 -07:00
Seth Hillbrand
accbee3c6e Move git stuff to kicommon to fix build
And do other cool stuff
2025-09-12 07:17:42 -07:00
Seth Hillbrand
29025882fc Fix typo in pad table 2025-09-12 06:55:31 -07:00
Seth Hillbrand
c64f99c57a ADDED: VCSHASH and VCSSHORTHASH
Right now, git hashes only resolved by the variables ${VCSHASH} or
${VCSSHORTHASH}
2025-09-12 06:00:27 -07:00
Seth Hillbrand
5952c2fb9a ADDED: Pad edit table
Allows editing common pad properties in table format

Fixes https://gitlab.com/kicad/code/kicad/issues/10789
2025-09-12 05:39:25 -07:00
Jeff Young
0b6de964fd Fix initialization order. 2025-09-12 13:32:17 +01:00
Jeff Young
976278e5a6 ADDED: extra info for shape, pin and field differences.
Fixes https://gitlab.com/kicad/code/kicad/-/issues/21647
2025-09-12 13:32:17 +01:00
Roberto Fernandez Bautista
8d8bd7253f Windows CI: custom vcpkg triplet to ensure shareable binary cache 2025-09-12 11:18:50 +00:00
Seth Hillbrand
5dc6d43f43 Add via tenting/plugging representation to 3d-viewer
Fixes https://gitlab.com/kicad/code/kicad/issues/21704
2025-09-12 01:23:41 -07:00
jean-pierre charras
98dd5a68eb Fix a compil issue on gcc/msys2
gcc does not like implicit conversion from wxString to char*
2025-09-12 08:20:49 +02:00
Roberto Fernandez Bautista
10cc6f353e Fix Altium custom pads import
- Add missing parsing to AREGION6 properties
- Link imported regions to pads (replace pad shape)
2025-05-26 21:13:06 +02:00
54 changed files with 1332 additions and 155 deletions

View File

@ -9,7 +9,6 @@ win64_build:
image: registry.gitlab.com/kicad/kicad-ci/windows-build-image/ltsc2022-msvc:latest
variables:
VCPKG_BINARY_SOURCES: 'nuget,gitlab,readwrite'
VCPKG_DISABLE_COMPILER_TRACKING: '1'
# Switch the compressor to fastzip and reduce the compression level
FF_USE_FASTZIP: "true"
CACHE_COMPRESSION_LEVEL: "fast"
@ -37,6 +36,8 @@ win64_build:
-DKICAD_BUILD_PNS_DEBUG_TOOL=ON `
-DKICAD_USE_3DCONNEXION=ON `
-DVCPKG_BUILD_TYPE=debug `
-DVCPKG_INSTALL_OPTIONS="--x-abi-tools-use-exact-versions" `
-DVCPKG_OVERLAY_TRIPLETS="$Env:CI_PROJECT_DIR/tools/custom_vcpkg_triplets" `
../../
- cmake --build . 2>&1 | tee compilation_log.txt
- cd ../../

View File

@ -26,6 +26,7 @@
#include "render_3d_opengl.h"
#include <board.h>
#include <footprint.h>
#include <pcb_track.h>
#include "../../3d_math.h"
#include "convert_basic_shapes_to_polygon.h"
#include <lset.h>
@ -726,6 +727,54 @@ void RENDER_3D_OPENGL::generateCylinder( const SFVEC2F& aCenter, float aInnerRad
}
void RENDER_3D_OPENGL::generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop )
{
const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
{
float a0 = delta * i;
float a1 = delta * ( i + 1 );
const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
aCenter.y + sinf( a0 ) * aRadius, aZ );
const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
aCenter.y + sinf( a1 ) * aRadius, aZ );
const SFVEC3F c( aCenter.x, aCenter.y, aZ );
if( aTop )
aDstLayer->m_layer_top_triangles->AddTriangle( p1, p0, c );
else
aDstLayer->m_layer_bot_triangles->AddTriangle( p0, p1, c );
}
}
void RENDER_3D_OPENGL::generateDimple( const SFVEC2F& aCenter, float aRadius, float aZ,
float aDepth, unsigned int aNr_sides_per_circle,
TRIANGLE_DISPLAY_LIST* aDstLayer, bool aTop )
{
const float delta = 2.0f * glm::pi<float>() / (float) aNr_sides_per_circle;
const SFVEC3F c( aCenter.x, aCenter.y, aTop ? aZ - aDepth : aZ + aDepth );
for( unsigned int i = 0; i < aNr_sides_per_circle; ++i )
{
float a0 = delta * i;
float a1 = delta * ( i + 1 );
const SFVEC3F p0( aCenter.x + cosf( a0 ) * aRadius,
aCenter.y + sinf( a0 ) * aRadius, aZ );
const SFVEC3F p1( aCenter.x + cosf( a1 ) * aRadius,
aCenter.y + sinf( a1 ) * aRadius, aZ );
if( aTop )
aDstLayer->m_layer_top_triangles->AddTriangle( p0, p1, c );
else
aDstLayer->m_layer_bot_triangles->AddTriangle( p1, p0, c );
}
}
void RENDER_3D_OPENGL::generateViasAndPads()
{
if( !m_boardAdapter.GetBoard() )
@ -882,6 +931,63 @@ void RENDER_3D_OPENGL::generateViasAndPads()
delete layerTriangles;
}
}
TRIANGLE_DISPLAY_LIST* frontCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
TRIANGLE_DISPLAY_LIST* backCover = new TRIANGLE_DISPLAY_LIST( m_boardAdapter.GetViaCount() );
for( const PCB_TRACK* track : m_boardAdapter.GetBoard()->Tracks() )
{
if( track->Type() != PCB_VIA_T )
continue;
const PCB_VIA* via = static_cast<const PCB_VIA*>( track );
const float holediameter = via->GetDrillValue() * m_boardAdapter.BiuTo3dUnits();
const float hole_radius = holediameter / 2.0f + 2.0 * platingThickness3d;
const SFVEC2F center( via->GetStart().x * m_boardAdapter.BiuTo3dUnits(),
-via->GetStart().y * m_boardAdapter.BiuTo3dUnits() );
unsigned int seg = m_boardAdapter.GetCircleSegmentCount( via->GetDrillValue() );
PCB_LAYER_ID top_layer, bottom_layer;
via->LayerPair( &top_layer, &bottom_layer );
float ztop, zbot, dummy;
getLayerZPos( top_layer, ztop, dummy );
getLayerZPos( bottom_layer, dummy, zbot );
bool frontCovering = via->GetFrontCoveringMode() == COVERING_MODE::COVERED || via->IsTented( F_Mask );
bool backCovering = via->GetBackCoveringMode() == COVERING_MODE::COVERED || via->IsTented( B_Mask );
bool frontPlugged = via->GetFrontPluggingMode() == PLUGGING_MODE::PLUGGED;
bool backPlugged = via->GetBackPluggingMode() == PLUGGING_MODE::PLUGGED;
bool filled = via->GetFillingMode() == FILLING_MODE::FILLED
|| via->GetCappingMode() == CAPPING_MODE::CAPPED;
const float depth = hole_radius * 0.3f;
if( frontCovering )
{
if( filled || !frontPlugged )
generateDisk( center, hole_radius, ztop, seg, frontCover, true );
else
generateDimple( center, hole_radius, ztop, depth, seg, frontCover, true );
}
if( backCovering )
{
if( filled || !backPlugged )
generateDisk( center, hole_radius, zbot, seg, backCover, false );
else
generateDimple( center, hole_radius, zbot, depth, seg, backCover, false );
}
}
if( frontCover->m_layer_top_triangles->GetVertexSize() > 0 )
m_viaFrontCover = new OPENGL_RENDER_LIST( *frontCover, 0, 0.0f, 0.0f );
if( backCover->m_layer_bot_triangles->GetVertexSize() > 0 )
m_viaBackCover = new OPENGL_RENDER_LIST( *backCover, 0, 0.0f, 0.0f );
delete frontCover;
delete backCover;
}

View File

@ -72,6 +72,8 @@ RENDER_3D_OPENGL::RENDER_3D_OPENGL( EDA_3D_CANVAS* aCanvas, BOARD_ADAPTER& aAdap
m_outerViaThroughHoles = nullptr;
m_microviaHoles = nullptr;
m_padHoles = nullptr;
m_viaFrontCover = nullptr;
m_viaBackCover = nullptr;
m_circleTexture = 0;
m_grid = 0;
@ -947,6 +949,8 @@ void RENDER_3D_OPENGL::freeAllLists()
DELETE_AND_FREE( m_microviaHoles )
DELETE_AND_FREE( m_padHoles )
DELETE_AND_FREE( m_viaFrontCover )
DELETE_AND_FREE( m_viaBackCover )
}
@ -968,6 +972,17 @@ void RENDER_3D_OPENGL::renderSolderMaskLayer( PCB_LAYER_ID aLayerID, float aZPos
setLayerMaterial( aLayerID );
m_board->SetItIsTransparent( true );
m_board->DrawCulled( aShowThickness, solder_mask, via_holes );
if( aLayerID == F_Mask && m_viaFrontCover )
{
m_viaFrontCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
m_viaFrontCover->DrawTop();
}
else if( aLayerID == B_Mask && m_viaBackCover )
{
m_viaBackCover->ApplyScalePosition( aZPos, 4 * m_boardAdapter.GetNonCopperLayerThickness() );
m_viaBackCover->DrawBot();
}
}
}

View File

@ -123,6 +123,14 @@ private:
float aZtop, float aZbot, unsigned int aNr_sides_per_circle,
TRIANGLE_DISPLAY_LIST* aDstLayer );
void generateDisk( const SFVEC2F& aCenter, float aRadius, float aZ,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop );
void generateDimple( const SFVEC2F& aCenter, float aRadius, float aZ, float aDepth,
unsigned int aNr_sides_per_circle, TRIANGLE_DISPLAY_LIST* aDstLayer,
bool aTop );
void generateViasAndPads();
/**
@ -236,6 +244,8 @@ private:
OPENGL_RENDER_LIST* m_microviaHoles;
OPENGL_RENDER_LIST* m_padHoles;
OPENGL_RENDER_LIST* m_viaFrontCover;
OPENGL_RENDER_LIST* m_viaBackCover;
// Caches
std::map<wxString, MODEL_3D*> m_3dModelMap;

View File

@ -6,7 +6,12 @@
},
{
"environment": "vcpkg",
"VcPkgDir": "D:/vcpkg/"
"VcPkgDir": "D:/vcpkg/",
"VCPKG_BINARY_SOURCES": "nuget,kicad-gitlab,read"
},
{
"environment": "swig",
"SwigExePath": "D:/swigwin-4.1.1/swig.exe"
}
],
"configurations": [
@ -14,7 +19,7 @@
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg" ],
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg", "swig"],
"buildRoot": "${env.BuildDir}\\${name}",
"installRoot": "${env.InstallDir}\\${name}",
"cmakeCommandArgs": "",
@ -30,6 +35,21 @@
"name": "KICAD_WIN32_DPI_AWARE",
"value": "ON",
"type": "BOOL"
},
{
"name": "SWIG_EXECUTABLE",
"value": "${env.SwigExePath}",
"type": "STRING"
},
{
"name": "VCPKG_OVERLAY_TRIPLETS",
"value": "${workspaceRoot}/tools/custom_vcpkg_triplets",
"type": "STRING"
},
{
"name": "VCPKG_INSTALL_OPTIONS",
"value": "--x-abi-tools-use-exact-versions",
"type": "STRING"
}
],
"cmakeToolchain": "${env.VcPkgDir}/scripts/buildsystems/vcpkg.cmake"
@ -38,7 +58,7 @@
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "RelWithDebInfo",
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg" ],
"inheritEnvironments": [ "msvc_x64_x64", "vcpkg", "swig"],
"buildRoot": "${env.BuildDir}\\${name}",
"installRoot": "${env.InstallDir}\\${name}",
"cmakeCommandArgs": "",
@ -54,6 +74,21 @@
"name": "KICAD_WIN32_DPI_AWARE",
"value": "ON",
"type": "BOOL"
},
{
"name": "SWIG_EXECUTABLE",
"value": "${env.SwigExePath}",
"type": "STRING"
},
{
"name": "VCPKG_OVERLAY_TRIPLETS",
"value": "${workspaceRoot}/tools/custom_vcpkg_triplets",
"type": "STRING"
},
{
"name": "VCPKG_INSTALL_OPTIONS",
"value": "--x-abi-tools-use-exact-versions",
"type": "STRING"
}
],
"cmakeToolchain": "${env.VcPkgDir}/scripts/buildsystems/vcpkg.cmake"

View File

@ -67,6 +67,30 @@ set( KICOMMON_SRCS
# Gal
gal/color4d.cpp
gal/opengl/gl_context_mgr.cpp
# Git
git/git_add_to_index_handler.cpp
git/git_branch_handler.cpp
git/git_clone_handler.cpp
git/git_commit_handler.cpp
git/git_config_handler.cpp
git/git_compare_handler.cpp
git/git_init_handler.cpp
git/project_git_utils.cpp
git/git_pull_handler.cpp
git/git_push_handler.cpp
git/git_remove_from_index_handler.cpp
git/git_remove_vcs_handler.cpp
git/git_resolve_conflict_handler.cpp
git/git_revert_handler.cpp
git/git_status_handler.cpp
git/git_switch_branch_handler.cpp
git/git_sync_handler.cpp
git/kicad_git_common.cpp
git/kicad_git_errors.cpp
git/git_backend.cpp
git/libgit_backend.cpp
# Jobs
jobs/job.cpp
jobs/job_dispatcher.cpp
@ -616,26 +640,6 @@ set( COMMON_IMPORT_GFX_SRCS
import_gfx/svg_import_plugin.cpp
)
set( COMMON_GIT_SRCS
git/git_add_to_index_handler.cpp
git/git_branch_handler.cpp
git/git_clone_handler.cpp
git/git_commit_handler.cpp
git/git_config_handler.cpp
git/git_init_handler.cpp
git/git_pull_handler.cpp
git/git_push_handler.cpp
git/git_remove_from_index_handler.cpp
git/git_resolve_conflict_handler.cpp
git/git_revert_handler.cpp
git/git_status_handler.cpp
git/git_sync_handler.cpp
git/project_git_utils.cpp
git/kicad_git_common.cpp
git/kicad_git_errors.cpp
git/git_backend.cpp
git/libgit_backend.cpp
)
set( COMMON_SRCS
${LIB_KICAD_SRCS}
@ -648,7 +652,6 @@ set( COMMON_SRCS
${COMMON_IO_SRCS}
${FONT_SRCS}
${COMMON_IMPORT_GFX_SRCS}
${COMMON_GIT_SRCS}
${COMMON_TRANSLINE_CALCULATION_SRCS}
base_screen.cpp
bin_mod.cpp

View File

@ -25,12 +25,13 @@
#define GIT_ADD_TO_INDEX_HANDLER_H_
#include <git/kicad_git_common.h>
#include <import_export.h>
#include <vector>
#include <wx/string.h>
class LIBGIT_BACKEND;
class GIT_ADD_TO_INDEX_HANDLER : public KIGIT_COMMON
class APIEXPORT GIT_ADD_TO_INDEX_HANDLER : public KIGIT_COMMON
{
public:
GIT_ADD_TO_INDEX_HANDLER( git_repository* aRepository );

View File

@ -25,6 +25,7 @@
#define GIT_BACKEND_H_
#include <map>
#include <import_export.h>
#include <set>
#include <vector>
#include <wx/string.h>
@ -56,7 +57,7 @@ enum class CommitResult
Cancelled
};
class GIT_BACKEND
class APIEXPORT GIT_BACKEND
{
public:
virtual ~GIT_BACKEND() = default;
@ -121,7 +122,7 @@ public:
virtual void PerformRemoveFromIndex( GIT_REMOVE_FROM_INDEX_HANDLER* aHandler ) = 0;
};
GIT_BACKEND* GetGitBackend();
void SetGitBackend( GIT_BACKEND* aBackend );
APIEXPORT GIT_BACKEND* GetGitBackend();
APIEXPORT void SetGitBackend( GIT_BACKEND* aBackend );
#endif

View File

@ -25,6 +25,7 @@
#define GIT_BRANCH_HANDLER_H
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <wx/string.h>
#include <vector>
@ -36,7 +37,7 @@ enum class BranchResult
Error
};
class GIT_BRANCH_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_BRANCH_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_BRANCH_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -25,10 +25,11 @@
#define GIT_CLONE_HANDLER_H_
#include "kicad_git_common.h"
#include <import_export.h>
#include "git_repo_mixin.h"
#include "git_progress.h"
class GIT_CLONE_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_CLONE_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_CLONE_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -27,6 +27,7 @@
// Define a class to handle git commit operations
#include <git/kicad_git_common.h>
#include <import_export.h>
#include "git_backend.h"
#include <string>
@ -35,7 +36,7 @@
class LIBGIT_BACKEND;
class GIT_COMMIT_HANDLER : public KIGIT_COMMON
class APIEXPORT GIT_COMMIT_HANDLER : public KIGIT_COMMON
{
public:
GIT_COMMIT_HANDLER( git_repository* aRepo );

View File

@ -25,6 +25,7 @@
#define GIT_CONFIG_HANDLER_H
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <wx/string.h>
struct GitUserConfig
@ -35,7 +36,7 @@ struct GitUserConfig
bool hasEmail = false;
};
class GIT_CONFIG_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_CONFIG_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_CONFIG_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -25,6 +25,7 @@
#define GIT_INIT_HANDLER_H
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <wx/string.h>
enum class InitResult
@ -43,7 +44,7 @@ struct RemoteConfig
KIGIT_COMMON::GIT_CONN_TYPE connType;
};
class GIT_INIT_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_INIT_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_INIT_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -25,10 +25,11 @@
#define GIT_PROGRESS_H_
#include <widgets/wx_progress_reporters.h>
#include <import_export.h>
#include <memory>
class GIT_PROGRESS
class APIEXPORT GIT_PROGRESS
{
public:
GIT_PROGRESS() :

View File

@ -25,6 +25,7 @@
#define _GIT_PULL_HANDLER_H_
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <vector>
#include <string>
@ -51,7 +52,7 @@ enum class PullResult : int
class LIBGIT_BACKEND;
class GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_PULL_HANDLER : public KIGIT_REPO_MIXIN
{
public:
friend class LIBGIT_BACKEND;

View File

@ -26,6 +26,7 @@
#include <git/git_progress.h>
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <git/kicad_git_errors.h>
#include <wx/string.h>
@ -37,7 +38,7 @@ enum class PushResult
Error
};
class GIT_PUSH_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_PUSH_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_PUSH_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -25,12 +25,13 @@
#define GIT_REMOVE_FROM_INDEX_HANDLER_H_
#include <git/kicad_git_common.h>
#include <import_export.h>
#include <vector>
#include <wx/string.h>
class LIBGIT_BACKEND;
class GIT_REMOVE_FROM_INDEX_HANDLER : public KIGIT_COMMON
class APIEXPORT GIT_REMOVE_FROM_INDEX_HANDLER : public KIGIT_COMMON
{
public:
GIT_REMOVE_FROM_INDEX_HANDLER( git_repository* aRepository );

View File

@ -17,8 +17,9 @@
#include "kicad_git_common.h"
#include "kicad_git_errors.h"
#include "git_progress.h"
#include <import_export.h>
class KIGIT_REPO_MIXIN: public KIGIT_ERRORS, public GIT_PROGRESS
class APIEXPORT KIGIT_REPO_MIXIN: public KIGIT_ERRORS, public GIT_PROGRESS
{
public:
KIGIT_REPO_MIXIN( KIGIT_COMMON* aCommon ) : m_common( aCommon )

View File

@ -25,10 +25,11 @@
#define GIT_RESOLVE_CONFLICT_HANDLER_H
#include <git2.h>
#include <import_export.h>
class wxString;
class GIT_RESOLVE_CONFLICT_HANDLER
class APIEXPORT GIT_RESOLVE_CONFLICT_HANDLER
{
public:
GIT_RESOLVE_CONFLICT_HANDLER( git_repository* aRepository );

View File

@ -25,6 +25,7 @@
#define GIT_REVERT_HANDLER_H_
#include <git2.h>
#include <import_export.h>
#include <vector>
#include <wx/string.h>
// TEMPORARY HACKFIX INCLUDE FOR STD::VECTOR EXPORT OUT OF KICOMMON ON WINDOWS
@ -32,7 +33,7 @@
class LIBGIT_BACKEND;
class GIT_REVERT_HANDLER
class APIEXPORT GIT_REVERT_HANDLER
{
public:
GIT_REVERT_HANDLER( git_repository* aRepository );

View File

@ -25,6 +25,7 @@
#define GIT_STATUS_HANDLER_H
#include <git/git_repo_mixin.h>
#include <import_export.h>
#include <wx/string.h>
#include <map>
#include <set>
@ -38,7 +39,7 @@ struct FileStatus
unsigned int gitStatus; // Raw git status flags
};
class GIT_STATUS_HANDLER : public KIGIT_REPO_MIXIN
class APIEXPORT GIT_STATUS_HANDLER : public KIGIT_REPO_MIXIN
{
public:
GIT_STATUS_HANDLER( KIGIT_COMMON* aCommon );

View File

@ -25,10 +25,11 @@
#define GIT_SYNC_HANDLER_H_
#include <git2.h>
#include <import_export.h>
class wxString;
class GIT_SYNC_HANDLER
class APIEXPORT GIT_SYNC_HANDLER
{
public:
GIT_SYNC_HANDLER( git_repository* aRepository );

View File

@ -25,11 +25,12 @@
#include <istream>
#include <string>
#include <git2.h>
#include <import_export.h>
#include <richio.h>
class BLOB_BUFFER_STREAM : public std::streambuf
class APIEXPORT BLOB_BUFFER_STREAM : public std::streambuf
{
public:
BLOB_BUFFER_STREAM( git_blob* aBlob )
@ -57,7 +58,7 @@ public:
};
// Build a class that implements LINE_READER for git_blobs
class BLOB_READER : public LINE_READER
class APIEXPORT BLOB_READER : public LINE_READER
{
public:
BLOB_READER( git_blob* aBlob ) : m_blob( aBlob )

View File

@ -25,6 +25,7 @@
#define _GIT_COMMON_H_
#include <git/kicad_git_errors.h>
#include <import_export.h>
#include <git2.h>
#include <atomic>
@ -35,7 +36,7 @@
class LIBGIT_BACKEND;
class KIGIT_COMMON
class APIEXPORT KIGIT_COMMON
{
public:
@ -193,19 +194,19 @@ private:
static const unsigned KIGIT_CREDENTIAL_SSH_AGENT = 1 << sizeof( m_testedTypes - 1 );
};
extern "C" int progress_cb( const char* str, int len, void* data );
extern "C" void clone_progress_cb( const char* str, size_t len, size_t total, void* data );
extern "C" int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload );
extern "C" int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
void* aPayload );
extern "C" int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal,
size_t aBytes, void* aPayload );
extern "C" int push_update_reference_cb( const char* aRefname, const char* aStatus,
void* aPayload );
extern "C" APIEXPORT int progress_cb( const char* str, int len, void* data );
extern "C" APIEXPORT void clone_progress_cb( const char* str, size_t len, size_t total, void* data );
extern "C" APIEXPORT int transfer_progress_cb( const git_transfer_progress* aStats, void* aPayload );
extern "C" APIEXPORT int update_cb( const char* aRefname, const git_oid* aFirst, const git_oid* aSecond,
void* aPayload );
extern "C" APIEXPORT int push_transfer_progress_cb( unsigned int aCurrent, unsigned int aTotal,
size_t aBytes, void* aPayload );
extern "C" APIEXPORT int push_update_reference_cb( const char* aRefname, const char* aStatus,
void* aPayload );
extern "C" int fetchhead_foreach_cb( const char*, const char*,
const git_oid* aOID, unsigned int aIsMerge, void* aPayload );
extern "C" int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
unsigned int aAllowedTypes, void* aPayload );
extern "C" APIEXPORT int fetchhead_foreach_cb( const char*, const char*,
const git_oid* aOID, unsigned int aIsMerge, void* aPayload );
extern "C" APIEXPORT int credentials_cb( git_cred** aOut, const char* aUrl, const char* aUsername,
unsigned int aAllowedTypes, void* aPayload );
#endif // _GIT_COMMON_H_

View File

@ -28,7 +28,7 @@
#include <wx/translation.h>
class KIGIT_ERRORS
class APIEXPORT KIGIT_ERRORS
{
public:

View File

@ -25,11 +25,12 @@
#define LIBGIT_BACKEND_H_
#include "git_backend.h"
#include <import_export.h>
// Forward declarations to avoid exposing libgit2 headers
struct git_annotated_commit;
class LIBGIT_BACKEND : public GIT_BACKEND
class APIEXPORT LIBGIT_BACKEND : public GIT_BACKEND
{
public:
void Init() override;

View File

@ -24,6 +24,9 @@
#include "project_git_utils.h"
#include "git_backend.h"
#include <wx/string.h>
#include <string_utils.h>
namespace KIGIT
{
@ -43,4 +46,34 @@ bool PROJECT_GIT_UTILS::RemoveVCS( git_repository*& aRepo, const wxString& aProj
return GetGitBackend()->RemoveVCS( aRepo, aProjectPath, aRemoveGitDir, aErrors );
}
wxString PROJECT_GIT_UTILS::GetCurrentHash( const wxString& aProjectFile, bool aShort )
{
wxString result = wxT( "no hash" );
git_repository* repo = PROJECT_GIT_UTILS::GetRepositoryForFile( TO_UTF8( aProjectFile ) );
if( repo )
{
git_reference* head = nullptr;
if( git_repository_head( &head, repo ) == 0 )
{
const git_oid* oid = git_reference_target( head );
if( oid )
{
char buf[41];
size_t len = aShort ? 8 : 41;
git_oid_tostr( buf, len, oid );
result = wxString::FromUTF8( buf );
}
git_reference_free( head );
}
git_repository_free( repo );
}
return result;
}
} // namespace KIGIT

View File

@ -26,12 +26,13 @@
#include <git2.h>
#include <wx/string.h>
#include <import_export.h>
namespace KIGIT
{
/** Utility class with helper functions for project level git operations. */
class PROJECT_GIT_UTILS
class APIEXPORT PROJECT_GIT_UTILS
{
public:
/**
@ -51,6 +52,16 @@ public:
*/
static int CreateBranch( git_repository* aRepo, const wxString& aBranchName );
/**
* Return the current HEAD commit hash for the repository containing aProjectFile.
*
* @param aProjectFile Absolute path to any file within the repository (typically the
* project file path).
* @param aShort If true, return the short (8 char) hash, otherwise full hash.
* @return wxString containing the hash or "no hash" if unavailable.
*/
static wxString GetCurrentHash( const wxString& aProjectFile, bool aShort );
/**
* Remove version control from a directory by freeing the repository and
* optionally removing the .git directory.

View File

@ -46,11 +46,19 @@ int32_t ALTIUM_PROPS_UTILS::ConvertToKicadUnit( const double aValue )
}
std::optional<int> ALTIUM_PROPS_UTILS::ReadOptInt( const std::map<wxString, wxString>& aProps,
const wxString& aKey )
{
const std::map<wxString, wxString>::const_iterator& value = aProps.find( aKey );
return value == aProps.end() ? std::optional<int>{} : wxAtoi( value->second );
}
int ALTIUM_PROPS_UTILS::ReadInt( const std::map<wxString, wxString>& aProps, const wxString& aKey,
int aDefault )
{
const std::map<wxString, wxString>::const_iterator& value = aProps.find( aKey );
return value == aProps.end() ? aDefault : wxAtoi( value->second );
std::optional<int> opt = ReadOptInt(aProps, aKey);
return opt.has_value() ? opt.value() : aDefault;
}

View File

@ -27,6 +27,7 @@
#include <stdint.h>
#include <map>
#include <optional>
#include <wx/string.h>
@ -36,6 +37,9 @@ class ALTIUM_PROPS_UTILS
public:
static int32_t ConvertToKicadUnit( const double aValue );
static std::optional<int> ReadOptInt( const std::map<wxString, wxString>& aProps,
const wxString& aKey );
static int ReadInt( const std::map<wxString, wxString>& aProps, const wxString& aKey,
int aDefault );

View File

@ -36,6 +36,8 @@
#include <kiway.h>
#include <lockfile.h>
#include <macros.h>
#include <git/project_git_utils.h>
#include <git2.h>
#include <project.h>
#include <project/project_file.h>
#include <trace_helpers.h>
@ -45,6 +47,7 @@
#include <title_block.h>
PROJECT::PROJECT() :
m_readOnly( false ),
m_textVarsTicker( 0 ),
@ -85,6 +88,16 @@ bool PROJECT::TextVarResolver( wxString* aToken ) const
*aToken = TITLE_BLOCK::GetCurrentDate();
return true;
}
else if( aToken->IsSameAs( wxT( "VCSHASH" ) ) )
{
*aToken = KIGIT::PROJECT_GIT_UTILS::GetCurrentHash( GetProjectFullName(), false );
return true;
}
else if( aToken->IsSameAs( wxT( "VCSSHORTHASH" ) ) )
{
*aToken = KIGIT::PROJECT_GIT_UTILS::GetCurrentHash( GetProjectFullName(), true );
return true;
}
else if( GetTextVars().count( *aToken ) > 0 )
{
*aToken = GetTextVars().at( *aToken );

View File

@ -152,10 +152,10 @@ const wxAuiPaneInfo& defaultDesignBlocksPaneInfo( wxWindow* aWindow )
EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
APP_SETTINGS_BASE( "eeschema", eeschemaSchemaVersion ),
m_Appearance(),
m_AutoplaceFields(),
m_Drawing(),
m_FindReplaceExtra(),
m_Input(),
m_AutoplaceFields(),
m_Selection(),
m_PageSettings(),
m_AnnotatePanel(),
m_BomPanel(),
@ -163,8 +163,9 @@ EESCHEMA_SETTINGS::EESCHEMA_SETTINGS() :
m_LibViewPanel(),
m_NetlistPanel(),
m_SymChooserPanel(),
m_FindReplaceExtra(),
m_ERCDialog(),
m_ImportGraphics(),
m_Selection(),
m_Simulator(),
m_RescueNeverShow( false )
{

View File

@ -1542,7 +1542,7 @@ std::vector<LIB_SYMBOL_UNIT> LIB_SYMBOL::GetUnitDrawItems()
#define REPORT( msg ) { if( aReporter ) aReporter->Report( msg ); }
#define ITEM_DESC( item ) ( item )->GetItemDescription( &unitsProvider, true )
#define ITEM_DESC( item ) ( item )->GetItemDescription( &unitsProvider, false )
int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aReporter ) const
{
@ -1586,35 +1586,34 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
return retv;
}
// Make sure shapes and pins are sorted. No need with fields as those are
// matched by id/name.
// Make sure shapes are sorted. No need with fields or pins as those are matched by id/name and number.
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> aShapes;
std::set<const SCH_ITEM*> aFields;
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> aPins;
std::set<const SCH_FIELD*> aFields;
std::set<const SCH_PIN*> aPins;
for( auto it = m_drawings.begin(); it != m_drawings.end(); ++it )
{
if( it->Type() == SCH_SHAPE_T )
aShapes.insert( &(*it) );
else if( it->Type() == SCH_FIELD_T )
aFields.insert( &(*it) );
aFields.insert( static_cast<const SCH_FIELD*>( &(*it) ) );
else if( it->Type() == SCH_PIN_T )
aPins.insert( &(*it) );
aPins.insert( static_cast<const SCH_PIN*>( &(*it) ) );
}
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> bShapes;
std::set<const SCH_ITEM*> bFields;
std::set<const SCH_ITEM*, SCH_ITEM::cmp_items> bPins;
std::set<const SCH_FIELD*> bFields;
std::set<const SCH_PIN*> bPins;
for( auto it = aRhs.m_drawings.begin(); it != aRhs.m_drawings.end(); ++it )
{
if( it->Type() == SCH_SHAPE_T )
bShapes.insert( &(*it) );
else if( it->Type() == SCH_FIELD_T )
bFields.insert( &(*it) );
bFields.insert( static_cast<const SCH_FIELD*>( &(*it) ) );
else if( it->Type() == SCH_PIN_T )
bPins.insert( &(*it) );
bPins.insert( static_cast<const SCH_PIN*>( &(*it) ) );
}
if( int tmp = static_cast<int>( aShapes.size() - bShapes.size() ) )
@ -1632,7 +1631,9 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
if( int tmp2 = (*aIt)->compare( *(*bIt), aCompareFlags ) )
{
retv = tmp2;
REPORT( wxString::Format( _( "%s differs." ), ITEM_DESC( *aIt ) ) );
REPORT( wxString::Format( _( "Graphic item differs: %s; %s." ),
ITEM_DESC( *aIt ),
ITEM_DESC( *bIt ) ) );
if( !aReporter )
return retv;
@ -1640,46 +1641,48 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
}
}
if( int tmp = static_cast<int>( aPins.size() - bPins.size() ) )
for( const SCH_PIN* aPin : aPins )
{
retv = tmp;
REPORT( _( "Pin count differs." ) );
const SCH_PIN* bPin = aRhs.GetPin( aPin->GetNumber(), aPin->GetUnit(), aPin->GetBodyStyle() );
if( !aReporter )
return retv;
}
else
{
for( const SCH_ITEM* aPinItem : aPins )
if( !bPin )
{
const SCH_PIN* aPin = static_cast<const SCH_PIN*>( aPinItem );
const SCH_PIN* bPin = aRhs.GetPin( aPin->GetNumber(), aPin->GetUnit(),
aPin->GetBodyStyle() );
retv = 1;
REPORT( wxString::Format( _( "Extra pin in schematic symbol: %s." ), ITEM_DESC( aPin ) ) );
if( !bPin )
{
retv = 1;
REPORT( wxString::Format( _( "Pin %s not found." ), aPin->GetNumber() ) );
if( !aReporter )
return retv;
}
else if( int tmp = aPin->SCH_ITEM::compare( *bPin, aCompareFlags ) )
{
retv = tmp;
REPORT( wxString::Format( _( "Pin %s differs: %s; %s" ),
aPin->GetNumber(),
ITEM_DESC( aPin ),
ITEM_DESC( bPin ) ) );
if( !aReporter )
return retv;
}
else if( int tmp2 = aPinItem->compare( *bPin, aCompareFlags ) )
{
retv = tmp2;
REPORT( wxString::Format( _( "Pin %s differs." ), aPin->GetNumber() ) );
if( !aReporter )
return retv;
}
if( !aReporter )
return retv;
}
}
for( const SCH_ITEM* aFieldItem : aFields )
for( const SCH_PIN* bPin : bPins )
{
const SCH_PIN* aPin = aRhs.GetPin( bPin->GetNumber(), bPin->GetUnit(), bPin->GetBodyStyle() );
if( !aPin )
{
retv = 1;
REPORT( wxString::Format( _( "Missing pin in schematic symbol: %s." ), ITEM_DESC( bPin ) ) );
if( !aReporter )
return retv;
}
}
for( const SCH_FIELD* aField : aFields )
{
const SCH_FIELD* aField = static_cast<const SCH_FIELD*>( aFieldItem );
const SCH_FIELD* bField = nullptr;
int tmp = 0;
if( aField->IsMandatory() )
bField = aRhs.GetField( aField->GetId() );
@ -1687,33 +1690,49 @@ int LIB_SYMBOL::Compare( const LIB_SYMBOL& aRhs, int aCompareFlags, REPORTER* aR
bField = aRhs.GetField( aField->GetName() );
if( !bField )
tmp = 1;
else
tmp = aFieldItem->compare( *bField, aCompareFlags );
{
retv = 1;
REPORT( wxString::Format( _( "Extra field in schematic symbol: %s." ), ITEM_DESC( aField ) ) );
if( tmp )
if( !aReporter )
return retv;
}
else if( int tmp = aField->SCH_ITEM::compare( *bField, aCompareFlags ) )
{
retv = tmp;
REPORT( wxString::Format( _( "%s field differs." ), aField->GetName( false ) ) );
REPORT( wxString::Format( _( "Field '%s' differs: %s; %s." ),
aField->GetName( false ),
ITEM_DESC( aField ),
ITEM_DESC( bField ) ) );
if( !aReporter )
return retv;
}
}
if( int tmp = static_cast<int>( aFields.size() - bFields.size() ) )
for( const SCH_FIELD* bField : bFields )
{
retv = tmp;
REPORT( _( "Field count differs." ) );
const SCH_FIELD* aField = nullptr;
if( !aReporter )
return retv;
if( bField->IsMandatory() )
aField = aRhs.GetField( bField->GetId() );
else
aField = aRhs.GetField( bField->GetName() );
if( !aField )
{
retv = 1;
REPORT( wxString::Format( _( "Missing field in schematic symbol: %s." ), ITEM_DESC( bField ) ) );
if( !aReporter )
return retv;
}
}
if( int tmp = static_cast<int>( m_fpFilters.GetCount() - aRhs.m_fpFilters.GetCount() ) )
{
retv = tmp;
REPORT( _( "Footprint filters differs." ) );
REPORT( _( "Footprint filter count differs." ) );
if( !aReporter )
return retv;

View File

@ -974,9 +974,19 @@ void SCH_FIELD::CalcEdit( const VECTOR2I& aPosition )
wxString SCH_FIELD::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
{
return wxString::Format( _( "Field %s '%s'" ),
UnescapeString( GetName() ),
aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
wxString content = aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() );
if( content.IsEmpty() )
{
return wxString::Format( _( "Field %s (empty)" ),
UnescapeString( GetName() ) );
}
else
{
return wxString::Format( _( "Field %s '%s'" ),
UnescapeString( GetName() ),
content );
}
}

View File

@ -1937,10 +1937,20 @@ wxString SCH_DIRECTIVE_LABEL::GetItemDescription( UNITS_PROVIDER* aUnitsProvider
}
else
{
return wxString::Format( _( "Directive Label [%s %s]" ),
UnescapeString( m_fields[0].GetName() ),
aFull ? m_fields[0].GetShownText( false )
: KIUI::EllipsizeMenuText( m_fields[0].GetText() ) );
const SCH_FIELD& firstField = m_fields[0];
wxString content = aFull ? firstField.GetShownText( false ) : KIUI::EllipsizeMenuText( firstField.GetText() );
if( content.IsEmpty() )
{
return wxString::Format( _( "Directive Label [%s (empty)]" ),
UnescapeString( m_fields[0].GetName() ) );
}
else
{
return wxString::Format( _( "Directive Label [%s %s]" ),
UnescapeString( m_fields[0].GetName() ),
content );
}
}
}

View File

@ -1169,7 +1169,7 @@ wxString SCH_SHEET::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFu
{
const SCH_FIELD* sheetnameField = GetField( FIELD_T::SHEET_NAME );
return wxString::Format( _( "Hierarchical Sheet %s" ),
return wxString::Format( _( "Hierarchical Sheet '%s'" ),
aFull ? sheetnameField->GetShownText( false )
: KIUI::EllipsizeMenuText( sheetnameField->GetText() ) );
}

View File

@ -328,7 +328,7 @@ void SCH_SHEET_PIN::GetEndPoints( std::vector<DANGLING_END_ITEM>& aItemList )
wxString SCH_SHEET_PIN::GetItemDescription( UNITS_PROVIDER* aUnitsProvider, bool aFull ) const
{
return wxString::Format( _( "Hierarchical Sheet Pin %s" ),
return wxString::Format( _( "Hierarchical Sheet Pin '%s'" ),
aFull ? GetShownText( false ) : KIUI::EllipsizeMenuText( GetText() ) );
}

View File

@ -384,7 +384,8 @@ void BACK_ANNOTATE::applyChangelist()
if( !m_dryRun )
commit.Modify( symbol, screen, RECURSE_MODE::NO_RECURSE );
if( m_processReferences && ref.GetRef() != fpData.m_ref && !skip )
if( m_processReferences && ref.GetRef() != fpData.m_ref && !skip
&& !symbol->GetField( FIELD_T::REFERENCE )->HasTextVars() )
{
++m_changesCount;
msg.Printf( _( "Change %s reference designator to '%s'." ),
@ -397,7 +398,8 @@ void BACK_ANNOTATE::applyChangelist()
m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
}
if( m_processFootprints && oldFootprint != fpData.m_footprint && !skip )
if( m_processFootprints && oldFootprint != fpData.m_footprint && !skip
&& !symbol->GetField( FIELD_T::FOOTPRINT )->HasTextVars() )
{
++m_changesCount;
msg.Printf( _( "Change %s footprint assignment from '%s' to '%s'." ),
@ -411,7 +413,8 @@ void BACK_ANNOTATE::applyChangelist()
m_reporter.ReportHead( msg, RPT_SEVERITY_ACTION );
}
if( m_processValues && oldValue != fpData.m_value && !skip )
if( m_processValues && oldValue != fpData.m_value && !skip
&& !symbol->GetField( FIELD_T::VALUE )->HasTextVars() )
{
++m_changesCount;
msg.Printf( _( "Change %s value from '%s' to '%s'." ),
@ -502,6 +505,7 @@ void BACK_ANNOTATE::applyChangelist()
// with all the variables resolved. The footprints field value gets the symbol's
// resolved value when the PCB is updated from the schematic.
if( symField
&& !symField->HasTextVars()
&& symField->GetShownText( &ref.GetSheetPath(), false ) != fpFieldValue )
{
m_changesCount++;

View File

@ -120,6 +120,7 @@ set( PCBNEW_DIALOGS
dialogs/dialog_outset_items_base.cpp
dialogs/dialog_pad_properties.cpp
dialogs/dialog_pad_properties_base.cpp
dialogs/dialog_fp_edit_pad_table.cpp
dialogs/dialog_plot.cpp
dialogs/dialog_plot_base.cpp
dialogs/dialog_pns_diff_pair_dimensions.cpp

View File

@ -0,0 +1,689 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright The 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 "dialog_fp_edit_pad_table.h"
#include <wx/button.h>
#include <wx/sizer.h>
#include <wx/dcclient.h>
#include <pcb_shape.h>
#include <widgets/wx_grid.h>
#include <widgets/grid_text_helpers.h>
#include <widgets/grid_combobox.h>
#include <base_units.h>
#include <units_provider.h>
#include <board.h>
#include <footprint.h>
#include <footprint_edit_frame.h>
// Helper to map shape string to PAD_SHAPE
static PAD_SHAPE ShapeFromString( const wxString& shape )
{
if( shape == _( "Oval" ) ) return PAD_SHAPE::OVAL;
if( shape == _( "Rectangle" ) ) return PAD_SHAPE::RECTANGLE;
if( shape == _( "Trapezoid" ) ) return PAD_SHAPE::TRAPEZOID;
if( shape == _( "Rounded rectangle" ) ) return PAD_SHAPE::ROUNDRECT;
if( shape == _( "Chamfered rectangle" ) ) return PAD_SHAPE::CHAMFERED_RECT;
if( shape == _( "Custom shape" ) ) return PAD_SHAPE::CUSTOM;
return PAD_SHAPE::CIRCLE;
}
DIALOG_FP_EDIT_PAD_TABLE::DIALOG_FP_EDIT_PAD_TABLE( PCB_BASE_FRAME* aParent, FOOTPRINT* aFootprint ) :
DIALOG_SHIM( (wxWindow*)aParent, wxID_ANY, _( "Pad Table" ), wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER ),
m_grid( nullptr ),
m_footprint( aFootprint ),
m_unitsProvider( std::make_unique<UNITS_PROVIDER>( pcbIUScale, GetUserUnits() ) )
{
wxBoxSizer* topSizer = new wxBoxSizer( wxVERTICAL );
m_grid = new WX_GRID( this, wxID_ANY );
m_grid->CreateGrid( 0, 11 );
m_grid->SetColLabelValue( COL_NUMBER, _( "Number" ) );
m_grid->SetColLabelValue( COL_TYPE, _( "Type" ) );
m_grid->SetColLabelValue( COL_SHAPE, _( "Shape" ) );
m_grid->SetColLabelValue( COL_POS_X, _( "X Position" ) );
m_grid->SetColLabelValue( COL_POS_Y, _( "Y Position" ) );
m_grid->SetColLabelValue( COL_SIZE_X, _( "Size X" ) );
m_grid->SetColLabelValue( COL_SIZE_Y, _( "Size Y" ) );
m_grid->SetColLabelValue( COL_DRILL_X, _( "Drill X" ) );
m_grid->SetColLabelValue( COL_DRILL_Y, _( "Drill Y" ) );
m_grid->SetColLabelValue( COL_P2D_LENGTH, _( "Pad->Die Length" ) );
m_grid->SetColLabelValue( COL_P2D_DELAY, _( "Pad->Die Delay" ) );
m_grid->EnableEditing( true );
wxGridCellAttr* attr;
// Type column editor (attribute)
attr = new wxGridCellAttr;
{
wxArrayString typeNames;
typeNames.push_back( _( "Through-hole" ) ); // PTH
typeNames.push_back( _( "SMD" ) ); // SMD
typeNames.push_back( _( "Connector" ) ); // CONN SMD? (use CONN?)
typeNames.push_back( _( "NPTH" ) ); // NPTH
typeNames.push_back( _( "Aperture" ) ); // inferred copper-less
attr->SetEditor( new GRID_CELL_COMBOBOX( typeNames ) );
}
m_grid->SetColAttr( COL_TYPE, attr );
attr = new wxGridCellAttr;
wxArrayString shapeNames;
shapeNames.push_back( _( "Circle" ) );
shapeNames.push_back( _( "Oval" ) );
shapeNames.push_back( _( "Rectangle" ) );
shapeNames.push_back( _( "Trapezoid" ) );
shapeNames.push_back( _( "Rounded rectangle" ) );
shapeNames.push_back( _( "Chamfered rectangle" ) );
shapeNames.push_back( _( "Custom shape" ) );
attr->SetEditor( new GRID_CELL_COMBOBOX( shapeNames ) );
m_grid->SetColAttr( COL_SHAPE, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_POS_X, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_POS_Y, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_SIZE_X, attr );
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_SIZE_Y, attr );
// Drill X
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_DRILL_X, attr );
// Drill Y
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_DRILL_Y, attr );
// Pad->Die Length
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_P2D_LENGTH, attr );
// Pad->Die Delay
attr = new wxGridCellAttr;
attr->SetEditor( new GRID_CELL_TEXT_EDITOR() );
m_grid->SetColAttr( COL_P2D_DELAY, attr );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_POS_Y );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_SIZE_Y );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_X );
m_grid->SetUnitsProvider( m_unitsProvider.get(), COL_DRILL_Y );
m_grid->SetAutoEvalCols( { COL_POS_X, COL_POS_Y, COL_SIZE_X, COL_SIZE_Y, COL_DRILL_X, COL_DRILL_Y } );
topSizer->Add( m_grid, 1, wxEXPAND | wxALL, 5 );
wxStdDialogButtonSizer* buttons = new wxStdDialogButtonSizer();
buttons->AddButton( new wxButton( this, wxID_OK ) );
buttons->AddButton( new wxButton( this, wxID_CANCEL ) );
buttons->Realize();
topSizer->Add( buttons, 0, wxALIGN_RIGHT | wxALL, 5 );
SetSizerAndFit( topSizer );
CaptureOriginalPadState();
Populate();
// Bind cell change handlers for real-time updates
m_grid->Bind( wxEVT_GRID_CELL_CHANGED, &DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged, this );
m_grid->Bind( wxEVT_GRID_SELECT_CELL, &DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell, this );
// Listen for cancel
Bind(
wxEVT_BUTTON,
[this]( wxCommandEvent& aEvt )
{
m_cancelled = true;
aEvt.Skip();
},
wxID_CANCEL );
finishDialogSettings();
}
DIALOG_FP_EDIT_PAD_TABLE::~DIALOG_FP_EDIT_PAD_TABLE()
{
if( m_cancelled )
RestoreOriginalPadState();
}
void DIALOG_FP_EDIT_PAD_TABLE::Populate()
{
if( !m_footprint )
return;
int row = 0;
for( PAD* pad : m_footprint->Pads() )
{
m_grid->AppendRows( 1 );
m_grid->SetCellValue( row, COL_NUMBER, pad->GetNumber() );
// Pad attribute to string
wxString attrStr;
switch( pad->GetAttribute() )
{
case PAD_ATTRIB::PTH: attrStr = _( "Through-hole" ); break;
case PAD_ATTRIB::SMD: attrStr = _( "SMD" ); break;
case PAD_ATTRIB::CONN: attrStr = _( "Connector" ); break;
case PAD_ATTRIB::NPTH: attrStr = _( "NPTH" ); break;
default: attrStr = _( "Through-hole" ); break;
}
VECTOR2I size = pad->GetSize( PADSTACK::ALL_LAYERS );
if( pad->IsAperturePad() )
attrStr = _( "Aperture" );
m_grid->SetCellValue( row, COL_TYPE, attrStr );
m_grid->SetCellValue( row, COL_SHAPE, pad->ShowPadShape( PADSTACK::ALL_LAYERS ) );
m_grid->SetCellValue( row, COL_POS_X, m_unitsProvider->StringFromValue( pad->GetPosition().x, true ) );
m_grid->SetCellValue( row, COL_POS_Y, m_unitsProvider->StringFromValue( pad->GetPosition().y, true ) );
m_grid->SetCellValue( row, COL_SIZE_X, m_unitsProvider->StringFromValue( size.x, true ) );
m_grid->SetCellValue( row, COL_SIZE_Y, m_unitsProvider->StringFromValue( size.y, true ) );
// Drill values (only meaningful for PTH or NPTH). Leave empty otherwise.
if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
{
VECTOR2I drill = pad->GetDrillSize();
if( drill.x > 0 )
m_grid->SetCellValue( row, COL_DRILL_X, m_unitsProvider->StringFromValue( drill.x, true ) );
if( drill.y > 0 )
m_grid->SetCellValue( row, COL_DRILL_Y, m_unitsProvider->StringFromValue( drill.y, true ) );
}
else
{
// For non-PTH pads, drill columns are not applicable.
m_grid->SetReadOnly( row, COL_DRILL_X, true );
m_grid->SetReadOnly( row, COL_DRILL_Y, true );
}
// Pad to die metrics
if( pad->GetPadToDieLength() )
m_grid->SetCellValue( row, COL_P2D_LENGTH, m_unitsProvider->StringFromValue( pad->GetPadToDieLength(), true ) );
if( pad->GetPadToDieDelay() )
m_grid->SetCellValue( row, COL_P2D_DELAY, wxString::Format( "%d", pad->GetPadToDieDelay() ) );
row++;
}
// Hide row labels per requirements
m_grid->HideRowLabels();
// Auto size the data columns first to get reasonable initial widths
m_grid->AutoSizeColumns();
// Ensure the Shape column (index 1) is wide enough for the longest translated
// shape text plus the dropdown arrow / padding. We compute a max text width
// using a device context and add a platform neutral padding.
{
wxClientDC dc( m_grid );
dc.SetFont( m_grid->GetFont() );
wxArrayString shapeNames;
shapeNames.push_back( _( "Circle" ) );
shapeNames.push_back( _( "Oval" ) );
shapeNames.push_back( _( "Rectangle" ) );
shapeNames.push_back( _( "Trapezoid" ) );
shapeNames.push_back( _( "Rounded rectangle" ) );
shapeNames.push_back( _( "Chamfered rectangle" ) );
shapeNames.push_back( _( "Custom shape" ) );
int maxWidth = 0;
for( const wxString& str : shapeNames )
{
int w, h;
dc.GetTextExtent( str, &w, &h );
maxWidth = std::max( maxWidth, w );
}
// Add padding for internal cell margins + dropdown control.
int padding = FromDIP( 30 ); // heuristic: 2*margin + arrow button
m_grid->SetColSize( COL_SHAPE, maxWidth + padding );
}
// Record initial proportions for proportional resizing later.
InitColumnProportions();
Bind( wxEVT_SIZE, &DIALOG_FP_EDIT_PAD_TABLE::OnSize, this );
// Run an initial proportional resize using current client size so columns
// respect proportions immediately.
wxSizeEvent sizeEvt( GetSize(), GetId() );
CallAfter(
[this, sizeEvt]
{
wxSizeEvent evt( sizeEvt );
this->OnSize( evt );
} );
// If pads exist, select the first row to show initial highlight
if( m_grid->GetNumberRows() > 0 )
{
m_grid->SetGridCursor( 0, 0 );
// Construct event with required parameters (id, type, obj, row, col,...)
wxGridEvent ev( m_grid->GetId(), wxEVT_GRID_SELECT_CELL, m_grid, 0, 0, -1, -1, true );
OnSelectCell( ev );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::CaptureOriginalPadState()
{
m_originalPads.clear();
if( !m_footprint )
return;
for( PAD* pad : m_footprint->Pads() )
{
PAD_SNAPSHOT snap;
snap.number = pad->GetNumber();
snap.shape = pad->GetShape( PADSTACK::ALL_LAYERS );
snap.position = pad->GetPosition();
snap.size = pad->GetSize( PADSTACK::ALL_LAYERS );
snap.attribute = pad->GetAttribute();
snap.drillSize = pad->GetDrillSize();
snap.padToDieLength= pad->GetPadToDieLength();
snap.padToDieDelay = pad->GetPadToDieDelay();
m_originalPads.push_back( snap );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::RestoreOriginalPadState()
{
if( !m_footprint )
return;
size_t idx = 0;
PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
for( PAD* pad : m_footprint->Pads() )
{
if( idx >= m_originalPads.size() )
break;
const PAD_SNAPSHOT& snap = m_originalPads[idx++];
pad->SetNumber( snap.number );
pad->SetShape( PADSTACK::ALL_LAYERS, snap.shape );
pad->SetAttribute( snap.attribute );
pad->SetPosition( snap.position );
pad->SetSize( PADSTACK::ALL_LAYERS, snap.size );
pad->SetDrillSize( snap.drillSize );
pad->SetPadToDieLength( snap.padToDieLength );
pad->SetPadToDieDelay( snap.padToDieDelay );
pad->ClearBrightened();
if( canvas )
canvas->GetView()->Update( pad, KIGFX::REPAINT );
}
if( canvas )
{
canvas->GetView()->MarkTargetDirty( KIGFX::TARGET_OVERLAY );
canvas->ForceRefresh();
}
}
bool DIALOG_FP_EDIT_PAD_TABLE::TransferDataFromWindow()
{
if( !m_grid->CommitPendingChanges() )
return false;
if( !m_footprint )
return true;
int row = 0;
for( PAD* pad : m_footprint->Pads() )
{
pad->SetNumber( m_grid->GetCellValue( row, COL_NUMBER ) );
wxString typeStr = m_grid->GetCellValue( row, COL_TYPE );
if( typeStr == _( "Through-hole" ) )
pad->SetAttribute( PAD_ATTRIB::PTH );
else if( typeStr == _( "SMD" ) )
pad->SetAttribute( PAD_ATTRIB::SMD );
else if( typeStr == _( "Connector" ) )
pad->SetAttribute( PAD_ATTRIB::CONN );
else if( typeStr == _( "NPTH" ) )
pad->SetAttribute( PAD_ATTRIB::NPTH );
// Aperture derived by copper-less layers; do not overwrite attribute here.
wxString shape = m_grid->GetCellValue( row, COL_SHAPE );
PAD_SHAPE newShape = ShapeFromString( shape );
pad->SetShape( PADSTACK::ALL_LAYERS, newShape );
VECTOR2I pos;
pos.x = m_grid->GetUnitValue( row, COL_POS_X );
pos.y = m_grid->GetUnitValue( row, COL_POS_Y );
pad->SetPosition( pos );
VECTOR2I size;
size.x = m_grid->GetUnitValue( row, COL_SIZE_X );
size.y = m_grid->GetUnitValue( row, COL_SIZE_Y );
pad->SetSize( PADSTACK::ALL_LAYERS, size );
// Drill sizes (only if attribute allows)
if( pad->GetAttribute() == PAD_ATTRIB::PTH || pad->GetAttribute() == PAD_ATTRIB::NPTH )
{
int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
if( dx > 0 || dy > 0 )
{
if( dx <= 0 ) dx = dy;
if( dy <= 0 ) dy = dx;
pad->SetDrillSize( { dx, dy } );
}
}
// Pad->Die
wxString delayStr = m_grid->GetCellValue( row, COL_P2D_DELAY );
wxString lenStr = m_grid->GetCellValue( row, COL_P2D_LENGTH );
if( !lenStr.IsEmpty() )
pad->SetPadToDieLength( m_grid->GetUnitValue( row, COL_P2D_LENGTH ) );
else
pad->SetPadToDieLength( 0 );
if( !delayStr.IsEmpty() )
{
long delayVal;
if( delayStr.ToLong( &delayVal ) )
pad->SetPadToDieDelay( (int) delayVal );
else
pad->SetPadToDieDelay( 0 );
}
else
pad->SetPadToDieDelay( 0 );
row++;
}
return true;
}
void DIALOG_FP_EDIT_PAD_TABLE::InitColumnProportions()
{
m_colProportions.clear();
m_minColWidths.clear();
if( !m_grid )
return;
// Only consider the actual data columns (all of them since row labels are hidden)
int cols = m_grid->GetNumberCols();
int total = 0;
std::vector<int> widths;
widths.reserve( cols );
for( int c = 0; c < cols; ++c )
{
int w = m_grid->GetColSize( c );
widths.push_back( w );
total += w;
}
if( total <= 0 )
return;
for( int w : widths )
{
m_colProportions.push_back( (double) w / (double) total );
m_minColWidths.push_back( w );
}
}
void DIALOG_FP_EDIT_PAD_TABLE::OnSize( wxSizeEvent& aEvent )
{
if( m_colProportions.empty() )
{
aEvent.Skip();
return;
}
// Compute available total width for columns and resize keeping proportions.
int cols = m_grid->GetNumberCols();
int available = 0;
for( int c = 0; c < cols; ++c )
available += m_grid->GetColSize( c );
// Use client size of grid minus scrollbar estimate to better distribute.
int clientW = m_grid->GetClientSize().x;
if( clientW > 0 )
available = clientW; // prefer actual client width
int used = 0;
for( int c = 0; c < cols; ++c )
{
int target = (int) std::round( m_colProportions[c] * available );
target = std::max( target, m_minColWidths[c] );
// Defer last column to absorb rounding diff.
if( c == cols - 1 )
target = std::max( available - used, m_minColWidths[c] );
m_grid->SetColSize( c, target );
used += target;
}
aEvent.Skip();
}
void DIALOG_FP_EDIT_PAD_TABLE::OnCellChanged( wxGridEvent& aEvent )
{
int row = aEvent.GetRow();
int col = aEvent.GetCol();
if( !m_footprint )
return;
// row -> pad mapping is current order of Pads() iteration; rebuild each time (pads count small)
int idx = 0;
PAD* target = nullptr;
for( PAD* pad : m_footprint->Pads() )
{
if( idx == row ) { target = pad; break; }
++idx;
}
if( !target )
return;
bool needCanvasRefresh = false;
switch( col )
{
case COL_NUMBER:
target->SetNumber( m_grid->GetCellValue( row, col ) );
break;
case COL_TYPE:
{
wxString typeStr = m_grid->GetCellValue( row, col );
PAD_ATTRIB newAttr = target->GetAttribute();
if( typeStr == _( "Through-hole" ) )
newAttr = PAD_ATTRIB::PTH;
else if( typeStr == _( "SMD" ) )
newAttr = PAD_ATTRIB::SMD;
else if( typeStr == _( "Connector" ) )
newAttr = PAD_ATTRIB::CONN;
else if( typeStr == _( "NPTH" ) )
newAttr = PAD_ATTRIB::NPTH;
if( newAttr != target->GetAttribute() )
{
target->SetAttribute( newAttr );
// Toggle drill columns read-only state dynamically.
bool drillsEditable = ( newAttr == PAD_ATTRIB::PTH || newAttr == PAD_ATTRIB::NPTH );
m_grid->SetReadOnly( row, COL_DRILL_X, !drillsEditable );
m_grid->SetReadOnly( row, COL_DRILL_Y, !drillsEditable );
needCanvasRefresh = true;
}
break;
}
case COL_SHAPE:
target->SetShape( PADSTACK::ALL_LAYERS, ShapeFromString( m_grid->GetCellValue( row, col ) ) );
needCanvasRefresh = true;
break;
case COL_POS_X:
case COL_POS_Y:
{
VECTOR2I pos = target->GetPosition();
if( col == COL_POS_X )
pos.x = m_grid->GetUnitValue( row, col );
else
pos.y = m_grid->GetUnitValue( row, col );
target->SetPosition( pos );
needCanvasRefresh = true;
break;
}
case COL_SIZE_X:
case COL_SIZE_Y:
{
VECTOR2I size = target->GetSize( PADSTACK::ALL_LAYERS );
if( col == COL_SIZE_X )
size.x = m_grid->GetUnitValue( row, col );
else
size.y = m_grid->GetUnitValue( row, col );
target->SetSize( PADSTACK::ALL_LAYERS, size );
needCanvasRefresh = true;
break;
}
case COL_DRILL_X:
case COL_DRILL_Y:
{
if( target->GetAttribute() == PAD_ATTRIB::PTH || target->GetAttribute() == PAD_ATTRIB::NPTH )
{
int dx = m_grid->GetUnitValue( row, COL_DRILL_X );
int dy = m_grid->GetUnitValue( row, COL_DRILL_Y );
if( dx > 0 || dy > 0 )
{
if( dx <= 0 ) dx = dy;
if( dy <= 0 ) dy = dx;
target->SetDrillSize( { dx, dy } );
needCanvasRefresh = true;
}
}
break;
}
case COL_P2D_LENGTH:
if( !m_grid->GetCellValue( row, col ).IsEmpty() )
target->SetPadToDieLength( m_grid->GetUnitValue( row, col ) );
break;
case COL_P2D_DELAY:
{
wxString d = m_grid->GetCellValue( row, col );
long val;
if( d.ToLong( &val ) )
target->SetPadToDieDelay( (int) val );
break;
}
default:
break;
}
// Request redraw (simple approach)
target->SetDirty();
if( needCanvasRefresh )
{
if( PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() ) )
base->GetCanvas()->ForceRefresh();
}
}
void DIALOG_FP_EDIT_PAD_TABLE::OnSelectCell( wxGridEvent& aEvent )
{
int row = aEvent.GetRow();
if( !m_footprint )
return;
PCB_BASE_FRAME* base = dynamic_cast<PCB_BASE_FRAME*>( GetParent() );
PCB_DRAW_PANEL_GAL* canvas = base ? base->GetCanvas() : nullptr;
// Clear existing pad selections
for( PAD* pad : m_footprint->Pads() )
{
if( pad->IsBrightened() )
{
pad->ClearBrightened();
if( canvas )
canvas->GetView()->Update( pad, KIGFX::REPAINT );
}
}
int idx = 0;
for( PAD* pad : m_footprint->Pads() )
{
if( idx == row )
{
pad->SetBrightened();
if( canvas )
{
canvas->GetView()->Update( pad, KIGFX::REPAINT );
canvas->ForceRefresh();
}
break;
}
++idx;
}
}

View File

@ -0,0 +1,59 @@
#pragma once
#include "dialog_shim.h"
#include <memory>
#include <vector>
#include <pad.h>
class PCB_BASE_FRAME;
class FOOTPRINT;
class WX_GRID;
class UNITS_PROVIDER;
class DIALOG_FP_EDIT_PAD_TABLE : public DIALOG_SHIM
{
public:
DIALOG_FP_EDIT_PAD_TABLE( PCB_BASE_FRAME* aParent, FOOTPRINT* aFootprint );
~DIALOG_FP_EDIT_PAD_TABLE() override;
bool TransferDataFromWindow() override;
private:
void Populate();
void OnSize( wxSizeEvent& aEvent );
// Proportional resize support
std::vector<double> m_colProportions; // relative widths captured after init
std::vector<int> m_minColWidths; // initial (minimum) widths
void InitColumnProportions();
struct PAD_SNAPSHOT
{
wxString number;
PAD_SHAPE shape;
VECTOR2I position;
VECTOR2I size;
PAD_ATTRIB attribute;
VECTOR2I drillSize;
int padToDieLength = 0;
int padToDieDelay = 0;
};
std::vector<PAD_SNAPSHOT> m_originalPads; // original pad data for cancel rollback
bool m_cancelled = false; // set if user hit cancel
void CaptureOriginalPadState();
void RestoreOriginalPadState();
void OnCellChanged( wxGridEvent& aEvent );
void OnSelectCell( wxGridEvent& aEvent );
// Column indices (after adding Type column)
enum COLS { COL_NUMBER = 0, COL_TYPE, COL_SHAPE, COL_POS_X, COL_POS_Y, COL_SIZE_X, COL_SIZE_Y,
COL_DRILL_X, COL_DRILL_Y, COL_P2D_LENGTH, COL_P2D_DELAY };
WX_GRID* m_grid;
FOOTPRINT* m_footprint;
std::unique_ptr<UNITS_PROVIDER> m_unitsProvider;
};

View File

@ -542,7 +542,8 @@ void DRC_ENGINE::compileRules()
}
if( error_semaphore.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) )
THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name, rule->m_Condition->GetExpression(), 0, 0 );
THROW_PARSE_ERROR( wxT( "Parse error" ), rule->m_Name,
TO_UTF8( rule->m_Condition->GetExpression() ), 0, 0 );
for( const DRC_CONSTRAINT& constraint : rule->m_Constraints )
{

View File

@ -1421,6 +1421,7 @@ void FOOTPRINT_EDIT_FRAME::setupUIConditions()
mgr->SetConditions( PCB_ACTIONS::placeImportedGraphics, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::footprintProperties, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::padTable, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::editTextAndGraphics, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::checkFootprint, ENABLE( haveFootprintCond ) );
mgr->SetConditions( PCB_ACTIONS::repairFootprint, ENABLE( haveFootprintCond ) );

View File

@ -119,6 +119,7 @@ void FOOTPRINT_EDIT_FRAME::doReCreateMenuBar()
editMenu->AppendSeparator();
editMenu->Add( PCB_ACTIONS::editTextAndGraphics );
editMenu->Add( PCB_ACTIONS::padTable );
editMenu->Add( PCB_ACTIONS::defaultPadProperties );
editMenu->Add( PCB_ACTIONS::enumeratePads );
editMenu->Add( ACTIONS::gridOrigin );

View File

@ -1192,6 +1192,8 @@ AREGION6::AREGION6( ALTIUM_BINARY_PARSER& aReader, bool aExtendedVertices )
int pkind = ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KIND" ), 0 );
bool is_cutout = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISBOARDCUTOUT" ), false );
v7layer = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "V7_LAYER" ), wxEmptyString );
name = ALTIUM_PROPS_UTILS::ReadString( properties, wxT( "NAME" ), wxEmptyString );
is_shapebased = ALTIUM_PROPS_UTILS::ReadBool( properties, wxT( "ISSHAPEBASED" ), false );
keepoutrestrictions = static_cast<uint8_t>(
ALTIUM_PROPS_UTILS::ReadInt( properties, wxT( "KEEPOUTRESTRIC" ), 0x1F ) );
@ -1201,6 +1203,25 @@ AREGION6::AREGION6( ALTIUM_BINARY_PARSER& aReader, bool aExtendedVertices )
subpolyindex = static_cast<uint16_t>(
ALTIUM_PROPS_UTILS::ReadInt( properties, "SUBPOLYINDEX", ALTIUM_POLYGON_NONE ) );
unionindex = ALTIUM_PROPS_UTILS::ReadOptInt( properties, "UNIONINDEX" );
padindex = ALTIUM_PROPS_UTILS::ReadOptInt( properties, "PADINDEX" );
arc_resolution = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, "ARCRESOLUTION", "0.1mil");
cavity_height = ALTIUM_PROPS_UTILS::ReadKicadUnit( properties, "CAVITYHEIGHT", "0mil");
// Note RFB:
// Sometimes the format could have vertices in the properties region, e.g. like below. In these
// cases a property "MAINCONTOURVERTEXCOUNT" will also be present containing the number of
// vertices. For now, I decided it is not worth it to parse it since the same data is present in
// the binary stream in all samples I have seen.
// KIND0=0
// VX0=-27mil
// VY0=-15mil
// CX0=0mil
// CY0=0mil
// SA0= 0.00000000000000E+0000
// EA0= 0.00000000000000E+0000
// R0=0mil
switch( pkind )
{
case 0:

View File

@ -562,8 +562,16 @@ struct AREGION6
uint8_t keepoutrestrictions;
uint16_t holecount;
wxString name;
wxString v7layer; ///< duplicate data? seems identical to 'layer' in the binary stream
std::optional<int> unionindex; ///< Unclear what unionindex refers to
std::optional<int> padindex;
ALTIUM_REGION_KIND kind; // I assume this means if normal or keepout?
std::optional<int32_t> cavity_height; ///< Unclear what cavity height is
std::optional<int32_t> arc_resolution;
std::vector<ALTIUM_VERTICE> outline;
std::vector<std::vector<ALTIUM_VERTICE>> holes;

View File

@ -76,6 +76,29 @@ bool IsAltiumLayerAPlane( ALTIUM_LAYER aLayer )
return aLayer >= ALTIUM_LAYER::INTERNAL_PLANE_1 && aLayer <= ALTIUM_LAYER::INTERNAL_PLANE_16;
}
PAD* ALTIUM_PCB::HelperGetPad( int aPadIndex ) const
{
if( !m_padIndexMap.contains( aPadIndex ) )
{
THROW_IO_ERROR( wxString::Format( wxT( "Component creator tries to access pad id "
"%d, which does not exist" ),
aPadIndex ) );
}
BOARD_CONNECTED_ITEM* padBoardItem = m_padIndexMap.at( aPadIndex );
if( padBoardItem->Type() != PCB_PAD_T )
{
// Todo: graceful handling?
THROW_IO_ERROR( wxString::Format( wxT( "Pad %d is not on a copper layer, unexpected" ),
aPadIndex ) );
}
return static_cast<PAD*>( padBoardItem );
}
FOOTPRINT* ALTIUM_PCB::HelperGetFootprint( uint16_t aComponent ) const
{
if( aComponent == ALTIUM_COMPONENT_NONE || m_components.size() <= aComponent )
@ -793,7 +816,7 @@ FOOTPRINT* ALTIUM_PCB::ParseFootprint( ALTIUM_PCB_COMPOUND_FILE& altiumLibFile,
case ALTIUM_RECORD::PAD:
{
APAD6 pad( parser );
ConvertPads6ToFootprintItem( footprint.get(), pad );
ConvertPads6ToFootprintItem( footprint.get(), pad, primitiveIndex );
break;
}
case ALTIUM_RECORD::VIA:
@ -2685,6 +2708,11 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItem( FOOTPRINT* aFoot
void ALTIUM_PCB::ConvertShapeBasedRegions6ToBoardItemOnLayer( const AREGION6& aElem,
PCB_LAYER_ID aLayer )
{
wxASSERT_MSG( !aElem.padindex.has_value(),
wxT( "ConvertShapeBasedRegions6ToBoardItemOnLayer was called for a Region with a "
"pad index! ConvertShapeBasedRegions6ToFootprintItemOnLayer should be "
"called instead" ) );
SHAPE_LINE_CHAIN linechain;
HelperShapeLineChainFromAltiumVertices( linechain, aElem.outline );
@ -2758,9 +2786,24 @@ void ALTIUM_PCB::ConvertShapeBasedRegions6ToFootprintItemOnLayer( FOOTPRINT*
polySet.AddHole( hole_linechain );
}
if( aLayer == F_Cu || aLayer == B_Cu )
if( aElem.padindex.has_value() && LSET::AllCuMask().Contains( aLayer ) )
{
// Replace shape on layer of existing pad
int actualPadindex=aElem.padindex.value() - 1; // altium seems to index starting at 1?
PAD* pad = HelperGetPad( actualPadindex ); // throws if pad doesn't exist
pad->SetShape( aLayer, PAD_SHAPE::CUSTOM );
polySet.Move( -pad->GetPosition() );
polySet.Rotate( -pad->Padstack().GetOrientation() );
pad->AddPrimitivePoly( aLayer, polySet, 0, true );
// Ignore extended primitive information from the region - this appears to mean nothing
// for regions used in pads. It should have already been set correctly when the pad was
// loaded
}
else if( aLayer == F_Cu || aLayer == B_Cu )
{
// TODO(JE) padstacks -- not sure what should happen here yet
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
LSET padLayers;
@ -3158,6 +3201,8 @@ void ALTIUM_PCB::ParsePads6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumPcbF
ALTIUM_BINARY_PARSER reader( aAltiumPcbFile, aEntry );
int padindex = 0;
while( reader.GetRemainingBytes() >= 4 /* TODO: use Header section of file */ )
{
checkpoint();
@ -3165,12 +3210,12 @@ void ALTIUM_PCB::ParsePads6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumPcbF
if( elem.component == ALTIUM_COMPONENT_NONE )
{
ConvertPads6ToBoardItem( elem );
ConvertPads6ToBoardItem( elem, padindex++ );
}
else
{
FOOTPRINT* footprint = HelperGetFootprint( elem.component );
ConvertPads6ToFootprintItem( footprint, elem );
ConvertPads6ToFootprintItem( footprint, elem, padindex++ );
}
}
@ -3179,13 +3224,13 @@ void ALTIUM_PCB::ParsePads6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumPcbF
}
void ALTIUM_PCB::ConvertPads6ToBoardItem( const APAD6& aElem )
void ALTIUM_PCB::ConvertPads6ToBoardItem( const APAD6& aElem, const int aPadIndex )
{
// It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
&& aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
{
ConvertPads6ToBoardItemOnNonCopper( aElem );
ConvertPads6ToBoardItemOnNonCopper( aElem, aPadIndex );
}
else
{
@ -3193,7 +3238,7 @@ void ALTIUM_PCB::ConvertPads6ToBoardItem( const APAD6& aElem )
std::unique_ptr<FOOTPRINT> footprint = std::make_unique<FOOTPRINT>( m_board );
footprint->SetPosition( aElem.position );
ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem );
ConvertPads6ToFootprintItemOnCopper( footprint.get(), aElem, aPadIndex );
m_board->Add( footprint.release(), ADD_MODE::APPEND );
}
@ -3274,22 +3319,24 @@ void ALTIUM_PCB::ConvertVias6ToFootprintItem( FOOTPRINT* aFootprint, const AVIA6
}
void ALTIUM_PCB::ConvertPads6ToFootprintItem( FOOTPRINT* aFootprint, const APAD6& aElem )
void ALTIUM_PCB::ConvertPads6ToFootprintItem( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex )
{
// It is possible to place altium pads on non-copper layers -> we need to interpolate them using drawings!
if( !IsAltiumLayerCopper( aElem.layer ) && !IsAltiumLayerAPlane( aElem.layer )
&& aElem.layer != ALTIUM_LAYER::MULTI_LAYER )
{
ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem );
ConvertPads6ToFootprintItemOnNonCopper( aFootprint, aElem, aPadIndex );
}
else
{
ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem );
ConvertPads6ToFootprintItemOnCopper( aFootprint, aElem, aPadIndex );
}
}
void ALTIUM_PCB::ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, const APAD6& aElem )
void ALTIUM_PCB::ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex )
{
std::unique_ptr<PAD> pad = std::make_unique<PAD>( aFootprint );
@ -3617,11 +3664,13 @@ void ALTIUM_PCB::ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, con
if( aElem.is_tent_bottom )
pad->SetLayerSet( pad->GetLayerSet().reset( B_Mask ) );
aFootprint->Add( pad.release(), ADD_MODE::APPEND );
PAD* pad_ptr = pad.release();
m_padIndexMap.emplace( aPadIndex, pad_ptr );
aFootprint->Add( pad_ptr, ADD_MODE::APPEND );
}
void ALTIUM_PCB::ConvertPads6ToBoardItemOnNonCopper( const APAD6& aElem )
void ALTIUM_PCB::ConvertPads6ToBoardItemOnNonCopper( const APAD6& aElem, const int aPadIndex )
{
PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
@ -3643,11 +3692,14 @@ void ALTIUM_PCB::ConvertPads6ToBoardItemOnNonCopper( const APAD6& aElem )
HelperParsePad6NonCopper( aElem, klayer, pad.get() );
m_board->Add( pad.release(), ADD_MODE::APPEND );
PCB_SHAPE* pad_ptr = pad.release();
m_padIndexMap.emplace( aPadIndex, pad_ptr );
m_board->Add( pad_ptr, ADD_MODE::APPEND );
}
void ALTIUM_PCB::ConvertPads6ToFootprintItemOnNonCopper( FOOTPRINT* aFootprint, const APAD6& aElem )
void ALTIUM_PCB::ConvertPads6ToFootprintItemOnNonCopper( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex )
{
PCB_LAYER_ID klayer = GetKicadLayer( aElem.layer );
@ -3689,7 +3741,9 @@ void ALTIUM_PCB::ConvertPads6ToFootprintItemOnNonCopper( FOOTPRINT* aFootprint,
HelperParsePad6NonCopper( aElem, klayer, pad.get() );
aFootprint->Add( pad.release(), ADD_MODE::APPEND );
PCB_SHAPE* pad_ptr = pad.release();
m_padIndexMap.emplace( aPadIndex, pad_ptr );
aFootprint->Add( pad_ptr, ADD_MODE::APPEND );
}

View File

@ -85,6 +85,8 @@ enum class ALTIUM_PCB_DIR
class BOARD;
class BOARD_CONNECTED_ITEM;
class PAD;
class FP_SHAPE;
class PCB_SHAPE;
class PCB_TEXTBOX;
@ -182,11 +184,14 @@ private:
const ACOMPONENTBODY6& aElem );
void ParsePads6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ConvertPads6ToBoardItem( const APAD6& aElem );
void ConvertPads6ToFootprintItem( FOOTPRINT* aFootprint, const APAD6& aElem );
void ConvertPads6ToBoardItemOnNonCopper( const APAD6& aElem );
void ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, const APAD6& aElem );
void ConvertPads6ToFootprintItemOnNonCopper( FOOTPRINT* aFootprint, const APAD6& aElem );
void ConvertPads6ToBoardItem( const APAD6& aElem, const int aPadIndex );
void ConvertPads6ToFootprintItem( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex );
void ConvertPads6ToBoardItemOnNonCopper( const APAD6& aElem, const int aPadIndex );
void ConvertPads6ToFootprintItemOnCopper( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex );
void ConvertPads6ToFootprintItemOnNonCopper( FOOTPRINT* aFootprint, const APAD6& aElem,
const int aPadIndex );
void ParseVias6Data( const ALTIUM_PCB_COMPOUND_FILE& aAltiumPcbFile,
const CFB::COMPOUND_FILE_ENTRY* aEntry );
void ConvertVias6ToFootprintItem( FOOTPRINT* aFootprint, const AVIA6& aElem );
@ -261,6 +266,7 @@ private:
const ALTIUM_LAYER aAltiumLayer );
FOOTPRINT* HelperGetFootprint( uint16_t aComponent ) const;
PAD* HelperGetPad( int aPadIndex ) const;
void remapUnsureLayers( std::vector<ABOARD6_LAYER_STACKUP>& aStackup );
@ -277,6 +283,7 @@ private:
std::map<ALTIUM_RULE_KIND, std::vector<ARULE6>> m_rules;
std::map<ALTIUM_RECORD, std::multimap<int, const AEXTENDED_PRIMITIVE_INFORMATION>>
m_extendedPrimitiveInformationMaps;
std::map<int, BOARD_CONNECTED_ITEM*> m_padIndexMap;
std::map<ALTIUM_LAYER, ZONE*> m_outer_plane;

View File

@ -166,6 +166,7 @@ std::optional<TOOLBAR_CONFIGURATION> FOOTPRINT_EDIT_TOOLBAR_SETTINGS::DefaultToo
config.AppendSeparator()
.AppendAction( PCB_ACTIONS::footprintProperties )
.AppendAction( PCB_ACTIONS::padTable )
.AppendAction( PCB_ACTIONS::defaultPadProperties )
.AppendAction( ACTIONS::showDatasheet )
.AppendAction( PCB_ACTIONS::checkFootprint );

View File

@ -37,6 +37,7 @@
#include <pcbnew_settings.h>
#include <board_commit.h>
#include <dialogs/dialog_push_pad_properties.h>
#include <dialogs/dialog_fp_edit_pad_table.h>
#include <tools/pcb_actions.h>
#include <tools/pcb_grid_helper.h>
#include <tools/pcb_selection_tool.h>
@ -109,6 +110,7 @@ bool PAD_TOOL::Init()
if( m_isFootprintEditor )
{
menu.AddItem( PCB_ACTIONS::padTable, SELECTION_CONDITIONS::ShowAlways, 400 );
menu.AddItem( PCB_ACTIONS::enumeratePads, SELECTION_CONDITIONS::ShowAlways, 400 );
menu.AddItem( PCB_ACTIONS::recombinePad, recombineCondition, 400 );
menu.AddItem( PCB_ACTIONS::explodePad, explodeCondition, 400 );
@ -911,6 +913,22 @@ std::vector<PCB_SHAPE*> PAD_TOOL::RecombinePad( PAD* aPad, bool aIsDryRun )
return aPad->Recombine( aIsDryRun, maxError );
}
int PAD_TOOL::PadTable( const TOOL_EVENT& aEvent )
{
if( !m_isFootprintEditor || InPadEditMode() )
return 0;
FOOTPRINT* footprint = board()->GetFirstFootprint();
if( !footprint )
return 0;
DIALOG_FP_EDIT_PAD_TABLE dlg( frame(), footprint );
dlg.ShowQuasiModal();
return 0;
}
void PAD_TOOL::setTransitions()
{
@ -920,6 +938,7 @@ void PAD_TOOL::setTransitions()
Go( &PAD_TOOL::PlacePad, PCB_ACTIONS::placePad.MakeEvent() );
Go( &PAD_TOOL::EnumeratePads, PCB_ACTIONS::enumeratePads.MakeEvent() );
Go( &PAD_TOOL::PadTable, PCB_ACTIONS::padTable.MakeEvent() );
Go( &PAD_TOOL::EditPad, PCB_ACTIONS::explodePad.MakeEvent() );
Go( &PAD_TOOL::EditPad, PCB_ACTIONS::recombinePad.MakeEvent() );

View File

@ -46,6 +46,8 @@ public:
*/
int EnumeratePads( const TOOL_EVENT& aEvent );
int PadTable( const TOOL_EVENT& aEvent );
/**
* Place a pad in footprint editor.
*/

View File

@ -899,6 +899,13 @@ TOOL_ACTION PCB_ACTIONS::footprintProperties( TOOL_ACTION_ARGS()
.FriendlyName( _( "Footprint Properties..." ) )
.Icon( BITMAPS::module_options ) );
TOOL_ACTION PCB_ACTIONS::padTable( TOOL_ACTION_ARGS()
.Name( "pcbnew.ModuleEditor.padTable" )
.Scope( AS_GLOBAL )
.FriendlyName( _( "Pad Table..." ) )
.Tooltip( _( "Displays pad table for bulk editing of pads" ) )
.Icon( BITMAPS::pin_table ) );
TOOL_ACTION PCB_ACTIONS::checkFootprint( TOOL_ACTION_ARGS()
.Name( "pcbnew.ModuleEditor.checkFootprint" )
.Scope( AS_GLOBAL )

View File

@ -488,6 +488,7 @@ public:
static TOOL_ACTION footprintProperties;
static TOOL_ACTION defaultPadProperties;
static TOOL_ACTION padTable;
static TOOL_ACTION checkFootprint;

View File

@ -0,0 +1,3 @@
include("${VCPKG_ROOT_DIR}/triplets/x64-windows.cmake")
set(VCPKG_DISABLE_COMPILER_TRACKING ON)